Proxmark3 community

Research, development and trades concerning the powerful Proxmark3 device.

Remember; sharing is caring. Bring something back to the community.


"Learn the tools of the trade the hard way." +Fravia

You are not logged in.

Announcement

Time changes and with it the technology
Proxmark3 @ discord

Users of this forum, please be aware that information stored on this site is not private.

#1 2013-10-06 17:22:33

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Adding Ultralight Support to PM3

Hi,

I've managed to add some simple functions to read/write ultralight cards. 
I admit its not fancy as I basically copied the mifare functions and stripped out the authentication and crypto calls.
I think the WRITE_COMPATIBILITY command I've tried to implement is a bit broken as I get a x01 response????
However, I moved onto the WRITE command, and got it to work! smile

I have implemented 3 new commands
*urdbl - ultralight read block
*urdcard - ultralight readcard
*uwrbl - ultralight write block

proxmark3> hf mf urdbl 15
--block no:0f          
#db# READ BLOCK FINISHED                 
isOk:01 data:53 10 fa 23           
proxmark3> hf mf uwrbl 15 5310fa22
--block no:0f          
--data: 53 10 fa 22           
#db# Data command: a2                 
#db# Data R: 0f 53 10 fa 22 fd 23                 
#db# WRITE BLOCK FINISHED                 
isOk:01          
proxmark3> hf mf urdbl 15
--block no:0f          
#db# READ BLOCK FINISHED                 
isOk:01 data:53 10 fa 22           
proxmark3> hf mf uwrbl 15 5310fa23
--block no:0f          
--data: 53 10 fa 23           
#db# Data command: a2                 
#db# Data R: 0f 53 10 fa 23 74 32                 
#db# WRITE BLOCK FINISHED                 
isOk:01          
proxmark3> hf mf urdbl 15
--block no:0f          
#db# READ BLOCK FINISHED                 
isOk:01 data:53 10 fa 23           
proxmark3> hf mf uwrbl 15 5310fa22
--block no:0f          
--data: 53 10 fa 22           
#db# Data command: a2                 
#db# Data R: 0f 53 10 fa 22 fd 23                 
#db# WRITE BLOCK FINISHED                 
isOk:01   
proxmark3> hf mf urdbl 15
--block no:0f          
#db# READ BLOCK FINISHED                 
isOk:01 data:53 10 fa 22     

UPDATE urdcard (with block-lock indicator)

proxmark3> hf mf urdcard
Attempting to Read Ultralight...           
#db# READ CARD FINISHED                 
isOk:01          
Block 00:04 34 ba 02            
Block 01:f1 dd 25 80            
Block 02:89 48 00 80            
Block 03:00 00 00 00  [0]          
Block 04:ff ff ff ff  [0]          
Block 05:00 00 00 00  [0]          
Block 06:00 00 00 00  [0]          
Block 07:00 00 00 00  [0]          
Block 08:00 00 00 00  [0]          
Block 09:00 00 00 00  [0]          
Block 0a:00 00 00 00  [0]          
Block 0b:00 00 00 00  [0]          
Block 0c:45 5d 9d cc  [0]          
Block 0d:5b 7e d2 f3  [0]          
Block 0e:78 93 a8 cc  [0]          
Block 0f:53 10 fa 22  [1]  

Last edited by midnitesnake (2013-10-20 09:35:36)

Offline

#2 2013-10-08 15:47:46

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

Sorry for double post, but is anyone interested in the patches once Ive cleaned up my code a bit?

Offline

#3 2013-10-08 17:29:40

holiman
Contributor
Registered: 2013-05-03
Posts: 566

Re: Adding Ultralight Support to PM3

Sure, submit a link here (or the patch directly, if it is small enough). When submitting the patch, please make it as a diff against a *recent* (preferrably latest) svn-version so it can be applied cleanly.

Offline

#4 2013-10-08 18:29:04

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

Hopefully Ive done this right "diff -rupN proxmark3 proxmark3-dev/ > ultralight.patch"

I updated to the latest r807 to generate the patch, I've also tried to clean all the cruft from the patch like binary versions differ (from all the compiling and testing Ive been doing).  So all that should be left is the code I've added / changed.

683 lines

diff -rupN proxmark3/armsrc/appmain.c proxmark3-dev/armsrc/appmain.c
--- proxmark3/armsrc/appmain.c	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/armsrc/appmain.c	2013-10-08 18:09:53.000000000 +0100
@@ -782,12 +782,24 @@ void UsbPacketReceived(uint8_t *packet, 
 		case CMD_MIFARE_READBL:
 			MifareReadBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
+		case CMD_MIFAREU_READBL:
+			MifareUReadBlock(c->arg[0],c->d.asBytes);
+			break;
+		case CMD_MIFAREU_READCARD:
+			MifareUReadCard(c->arg[0],c->d.asBytes);
+                        break;
 		case CMD_MIFARE_READSC:
 			MifareReadSector(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
 		case CMD_MIFARE_WRITEBL:
 			MifareWriteBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
+		case CMD_MIFAREU_WRITEBL_COMPAT:
+			MifareUWriteBlock(c->arg[0], c->d.asBytes);
+                        break;
+		case CMD_MIFAREU_WRITEBL:
+                        MifareUWriteBlock_Special(c->arg[0], c->d.asBytes);
+                        break;
 		case CMD_MIFARE_NESTED:
 			MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
diff -rupN proxmark3/armsrc/apps.h proxmark3-dev/armsrc/apps.h
--- proxmark3/armsrc/apps.h	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/armsrc/apps.h	2013-10-08 18:09:53.000000000 +0100
@@ -158,8 +158,12 @@ void EPA_PACE_Collect_Nonce(UsbCommand *
 void ReaderMifare(bool first_try);
 int32_t dist_nt(uint32_t nt1, uint32_t nt2);
 void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *data);
+void MifareUReadBlock(uint8_t arg0,uint8_t *datain);
+void MifareUReadCard(uint8_t arg0,uint8_t *datain);
 void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
 void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
+void MifareUWriteBlock(uint8_t arg0,uint8_t *datain);
+void MifareUWriteBlock_Special(uint8_t arg0,uint8_t *datain);
 void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
 void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
 void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
diff -rupN proxmark3/armsrc/mifarecmd.c proxmark3-dev/armsrc/mifarecmd.c
--- proxmark3/armsrc/mifarecmd.c	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/armsrc/mifarecmd.c	2013-10-08 18:09:53.000000000 +0100
@@ -94,6 +94,60 @@ void MifareReadBlock(uint8_t arg0, uint8
 
 }
 
+void MifareUReadBlock(uint8_t arg0,uint8_t *datain)
+{
+    // params
+	uint8_t blockNo = arg0;
+	
+	// variables
+	byte_t isOK = 0;
+	byte_t dataoutbuf[16];
+	uint8_t uid[10];
+	uint32_t cuid;
+    
+	// clear trace
+	iso14a_clear_trace();
+	iso14443a_setup();
+    
+	LED_A_ON();
+	LED_B_OFF();
+	LED_C_OFF();
+    
+	while (true) {
+		if(!iso14443a_select_card(uid, NULL, &cuid)) {
+            if (MF_DBGLEVEL >= 1)	Dbprintf("Can't select card");
+			break;
+		};
+        
+		if(mifare_ultra_readblock(cuid, blockNo, dataoutbuf)) {
+            if (MF_DBGLEVEL >= 1)	Dbprintf("Read block error");
+			break;
+		};
+        
+		if(mifare_ultra_halt(cuid)) {
+            if (MF_DBGLEVEL >= 1)	Dbprintf("Halt error");
+			break;
+		};
+		
+		isOK = 1;
+		break;
+	}
+	
+	if (MF_DBGLEVEL >= 2)	DbpString("READ BLOCK FINISHED");
+    
+	// add trace trailer
+	memset(uid, 0x44, 4);
+	LogTrace(uid, 4, 0, 0, TRUE);
+	LED_B_ON();
+        cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16);
+	LED_B_OFF();
+    
+    
+    // Thats it...
+	FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+	LEDsoff();
+}
+
 //-----------------------------------------------------------------------------
 // Select, Authenticaate, Read an MIFARE tag. 
 // read sector (data = 4 x 16 bytes = 64 bytes)
@@ -191,6 +245,66 @@ void MifareReadSector(uint8_t arg0, uint
 
 }
 
+void MifareUReadCard(uint8_t arg0, uint8_t *datain)
+{
+  // params
+        uint8_t sectorNo = arg0;
+        
+        // variables
+        byte_t isOK = 0;
+        byte_t dataoutbuf[16 * 4];
+        uint8_t uid[10];
+        uint32_t cuid;
+
+        // clear trace
+        iso14a_clear_trace();
+//      iso14a_set_tracing(false);
+
+        iso14443a_setup();
+
+        LED_A_ON();
+        LED_B_OFF();
+        LED_C_OFF();
+
+        while (true) {
+                if(!iso14443a_select_card(uid, NULL, &cuid)) {
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Can't select card");
+                        break;
+                };
+		for(int sec=0;sec<16;sec++){
+                    if(mifare_ultra_readblock(cuid, sectorNo * 4 + sec, dataoutbuf + 4 * sec)) {
+                    if (MF_DBGLEVEL >= 1)   Dbprintf("Read block %d error",sec);
+                        break;
+                    };
+                }
+                if(mifare_ultra_halt(cuid)) {
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Halt error");
+                        break;
+                };
+
+                isOK = 1;
+                break;
+        }
+        
+        if (MF_DBGLEVEL >= 2) DbpString("READ CARD FINISHED");
+
+        // add trace trailer
+        memset(uid, 0x44, 4);
+        LogTrace(uid, 4, 0, 0, TRUE);
+        
+        LED_B_ON();
+  cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,64);
+  //cmd_send(CMD_ACK,isOK,0,0,dataoutbuf+32, 32);
+        LED_B_OFF();
+
+        // Thats it...
+        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+        LEDsoff();
+//  iso14a_set_tracing(TRUE);
+
+}
+
+
 //-----------------------------------------------------------------------------
 // Select, Authenticaate, Read an MIFARE tag. 
 // read block
@@ -273,6 +387,131 @@ void MifareWriteBlock(uint8_t arg0, uint
 
 }
 
+void MifareUWriteBlock(uint8_t arg0, uint8_t *datain)
+{
+        // params
+        uint8_t blockNo = arg0;
+        byte_t blockdata[16];
+
+        memset(blockdata,'\0',16);
+        memcpy(blockdata, datain,16);
+        
+        // variables
+        byte_t isOK = 0;
+        uint8_t uid[10];
+        uint32_t cuid;
+
+        // clear trace
+        iso14a_clear_trace();
+	//  iso14a_set_tracing(false);
+
+        iso14443a_setup();
+
+        LED_A_ON();
+        LED_B_OFF();
+        LED_C_OFF();
+
+        while (true) {
+                if(!iso14443a_select_card(uid, NULL, &cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Can't select card");
+                        break;
+                };
+
+                if(mifare_ultra_writeblock(cuid, blockNo, blockdata)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Write block error");
+                        break;
+                };
+
+                if(mifare_ultra_halt(cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Halt error");
+                        break;
+                };
+                
+                isOK = 1;
+                break;
+        }
+        
+        if (MF_DBGLEVEL >= 2)   DbpString("WRITE BLOCK FINISHED");
+
+        // add trace trailer
+        memset(uid, 0x44, 4);
+        LogTrace(uid, 4, 0, 0, TRUE);
+
+        LED_B_ON();
+  	cmd_send(CMD_ACK,isOK,0,0,0,0);
+//      UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
+        LED_B_OFF();
+
+
+        // Thats it...
+        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+        LEDsoff();
+//  iso14a_set_tracing(TRUE);
+
+}
+
+void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain)
+{
+        // params
+        uint8_t blockNo = arg0;
+        byte_t blockdata[4];
+        
+	memcpy(blockdata, datain,4);
+
+        // variables
+        byte_t isOK = 0;
+        uint8_t uid[10];
+        uint32_t cuid;
+
+        // clear trace
+        iso14a_clear_trace();
+        //  iso14a_set_tracing(false);
+
+        iso14443a_setup();
+
+        LED_A_ON();
+        LED_B_OFF();
+        LED_C_OFF();
+
+        while (true) {
+                        if(!iso14443a_select_card(uid, NULL, &cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Can't select card");
+                        break;
+                };
+
+                if(mifare_ultra_special_writeblock(cuid, blockNo, blockdata)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Write block error");
+                        break;
+                };
+
+                if(mifare_ultra_halt(cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Halt error");
+                        break;
+                };
+
+                isOK = 1;
+                break;
+        }
+
+        if (MF_DBGLEVEL >= 2)   DbpString("WRITE BLOCK FINISHED");
+
+        // add trace trailer
+	memset(uid, 0x44, 4);
+        LogTrace(uid, 4, 0, 0, TRUE);
+
+       LED_B_ON();
+        cmd_send(CMD_ACK,isOK,0,0,0,0);
+//      UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
+        LED_B_OFF();
+
+
+        // Thats it...
+        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+        LEDsoff();
+//  iso14a_set_tracing(TRUE);
+
+}
+
 // Return 1 if the nonce is invalid else return 0
 int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) {
 	return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \
diff -rupN proxmark3/armsrc/mifareutil.c proxmark3-dev/armsrc/mifareutil.c
--- proxmark3/armsrc/mifareutil.c	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/armsrc/mifareutil.c	2013-10-08 18:09:53.000000000 +0100
@@ -82,6 +82,32 @@ int mifare_sendcmd_short(struct Crypto1S
 	return mifare_sendcmd_shortex(pcs, crypted, cmd, data, answer, NULL, timing);
 }
 
+int mifare_sendcmd_short_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *timing)
+{
+        uint8_t dcmd[8];//, ecmd[4];
+        //uint32_t par=0;
+
+        dcmd[0] = cmd;
+        dcmd[1] = data[0];
+	dcmd[2] = data[1];
+	dcmd[3] = data[2];
+	dcmd[4] = data[3];
+	dcmd[5] = data[4];
+	AppendCrc14443a(dcmd, 6);
+	//Dbprintf("Data command: %02x", dcmd[0]);
+	//Dbprintf("Data R: %02x %02x %02x %02x %02x %02x %02x", dcmd[1],dcmd[2],dcmd[3],dcmd[4],dcmd[5],dcmd[6],dcmd[7]);
+
+        //memcpy(ecmd, dcmd, sizeof(dcmd));
+	ReaderTransmit(dcmd, sizeof(dcmd), NULL);
+	int len = ReaderReceive(answer);
+	if(!len)
+	{
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Authentication failed. Card timeout.");
+                return 2;
+        }
+	return len;
+}
+
 int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr, uint32_t *timing)
 {
 	uint8_t dcmd[4], ecmd[4];
@@ -256,6 +282,37 @@ int mifare_classic_readblock(struct Cryp
 	return 0;
 }
 
+int mifare_ultra_readblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData)
+{
+	// variables
+	int len;
+	uint8_t	bt[2];
+	
+	uint8_t* receivedAnswer = mifare_get_bigbufptr();
+	
+	// command MIFARE_CLASSIC_READBLOCK
+	len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer,NULL);
+	if (len == 1) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
+		return 1;
+	}
+	if (len != 18) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("Cmd Error: card timeout. len: %x", len);
+		return 2;
+	}
+    
+	memcpy(bt, receivedAnswer + 16, 2);
+	AppendCrc14443a(receivedAnswer, 16);
+	if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("Cmd CRC response error.");
+		return 3;
+	}
+	
+	memcpy(blockData, receivedAnswer, 14);
+	return 0;
+}
+
+
 int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) 
 {
 	// variables
@@ -303,6 +360,65 @@ int mifare_classic_writeblock(struct Cry
 	return 0;
 }
 
+int mifare_ultra_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData) 
+{
+        // variables
+        int len;     
+        uint32_t par = 0;
+        
+        uint8_t d_block[18];
+        uint8_t* receivedAnswer = mifare_get_bigbufptr();
+        
+        // command MIFARE_CLASSIC_WRITEBLOCK
+        len = mifare_sendcmd_short(NULL, 1, 0xA0, blockNo, receivedAnswer,NULL);
+
+        if ((len != 1) || (receivedAnswer[0] != 0x0A)) {   //  0x0a - ACK
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]);  
+                return 1;
+        }
+
+	memset(d_block,'\0',18);
+	memcpy(d_block, blockData, 16);
+        AppendCrc14443a(d_block, 16);
+
+	ReaderTransmitPar(d_block, sizeof(d_block), par, NULL);
+
+        // Receive the response
+        len = ReaderReceive(receivedAnswer);    
+
+	if ((len != 1) || (receivedAnswer[0] != 0x0A)) {   //  0x0a - ACK
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len);
+                return 2;
+        }        
+
+        return 0;
+} 
+
+int mifare_ultra_special_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData)
+{
+        // variables
+        int len;
+        //uint32_t par = 0;
+
+        uint8_t d_block[8];
+        uint8_t* receivedAnswer = mifare_get_bigbufptr();
+
+        // command MIFARE_CLASSIC_WRITEBLOCK
+	memset(d_block,'\0',8);
+	d_block[0]= blockNo;
+	memcpy(d_block+1,blockData,4);
+	AppendCrc14443a(d_block, 6);
+
+	//i know the data send here is correct
+        len = mifare_sendcmd_short_special(NULL, 1, 0xA2, d_block, receivedAnswer,NULL);
+
+        if (receivedAnswer[0] != 0x0A) {   //  0x0a - ACK
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0],len);
+                return 1;
+        }
+        return 0;
+}
+
 int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) 
 {
 	// variables
@@ -320,6 +436,23 @@ int mifare_classic_halt(struct Crypto1St
 	return 0;
 }
 
+int mifare_ultra_halt(uint32_t uid)
+{
+	// variables
+	int len;
+	
+	// Mifare HALT
+	uint8_t* receivedAnswer = mifare_get_bigbufptr();
+    
+	len = mifare_sendcmd_short(NULL, 1, 0x50, 0x00, receivedAnswer, NULL);
+	if (len != 0) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("halt error. response len: %x", len);
+		return 1;
+	}
+    
+	return 0;
+}
+
 // work with emulator memory
 void emlSetMem(uint8_t *data, int blockNum, int blocksCount) {
 	uint8_t* emCARD = eml_get_bigbufptr_cardmem();
diff -rupN proxmark3/armsrc/mifareutil.h proxmark3-dev/armsrc/mifareutil.h
--- proxmark3/armsrc/mifareutil.h	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/armsrc/mifareutil.h	2013-10-08 18:09:53.000000000 +0100
@@ -55,6 +55,7 @@ extern int MF_DBGLEVEL;
 //functions
 uint8_t* mifare_get_bigbufptr(void);
 int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t *timing);
+int mifare_sendcmd_short_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t *data, uint8_t* amswer, uint8_t *timing);
 int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr, uint32_t *timing);
 
 int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, \
@@ -62,8 +63,12 @@ int mifare_classic_auth(struct Crypto1St
 int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, \
 													uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested, uint32_t * ntptr, uint32_t *timing);
 int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); 
+int mifare_ultra_readblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData);
 int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData);
+int mifare_ultra_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData);
+int mifare_ultra_special_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData);
 int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); 
+int mifare_ultra_halt(uint32_t uid);
 
 // crypto functions
 void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len);
@@ -85,4 +90,4 @@ int emlGetValBl(uint32_t *blReg, uint8_t
 int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum);
 int emlCheckValBl(int blockNum);
 
-#endif
\ No newline at end of file
+#endif
diff -rupN proxmark3/client/cmdhfmf.c proxmark3-dev/client/cmdhfmf.c
--- proxmark3/client/cmdhfmf.c	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/client/cmdhfmf.c	2013-10-08 18:09:53.000000000 +0100
@@ -140,6 +140,77 @@ int CmdHF14AMfWrBl(const char *Cmd)
 	return 0;
 }
 
+int CmdHF14AMfUWrBl(const char *Cmd)
+{
+        uint8_t blockNo = 0;
+        uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+	UsbCommand resp;
+        
+        if (strlen(Cmd)<3) {
+                PrintAndLog("Usage:  hf mf uwrbl    <block number> <block data (8 hex symbols)>");
+                PrintAndLog("        sample: hf mf uwrbl 0 01020304");
+                return 0;
+        }       
+
+        blockNo = param_get8(Cmd, 0);
+        if (param_gethex(Cmd, 1, bldata, 8)) {
+                PrintAndLog("Block data must include 8 HEX symbols");
+                return 1;
+        }
+
+	switch(blockNo)
+	{
+	case 0:
+		PrintAndLog("Access Denied");
+		break;
+	case 1:
+		PrintAndLog("Access Denied");
+		break;
+	case 2:
+		PrintAndLog("--specialblock no:%02x", blockNo);
+                PrintAndLog("--data: %s", sprint_hex(bldata, 4));
+                UsbCommand c = {CMD_MIFAREU_WRITEBL, {blockNo}};
+                memcpy(c.d.asBytes, bldata, 4);
+                SendCommand(&c);
+
+                if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                        uint8_t isOK  = resp.arg[0] & 0xff;
+                        PrintAndLog("isOk:%02x", isOK);
+                } else {
+                        PrintAndLog("Command execute timeout");
+                }
+	case 3:
+	        PrintAndLog("--specialblock no:%02x", blockNo);
+                PrintAndLog("--data: %s", sprint_hex(bldata, 4));
+                UsbCommand d = {CMD_MIFAREU_WRITEBL, {blockNo}};
+                memcpy(d.d.asBytes,bldata, 4);
+                SendCommand(&d);
+
+                if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                        uint8_t isOK  = resp.arg[0] & 0xff;
+                        PrintAndLog("isOk:%02x", isOK);
+                } else {
+                        PrintAndLog("Command execute timeout");
+                }
+	default: 
+        	PrintAndLog("--block no:%02x", blockNo);
+        	PrintAndLog("--data: %s", sprint_hex(bldata, 4));        	
+  		//UsbCommand e = {CMD_MIFAREU_WRITEBL_COMPAT, {blockNo}};
+        	//memcpy(e.d.asBytes,bldata, 16);
+  		UsbCommand e = {CMD_MIFAREU_WRITEBL, {blockNo}};
+                memcpy(e.d.asBytes,bldata, 4);
+		SendCommand(&e);
+
+        	if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+        	        uint8_t isOK  = resp.arg[0] & 0xff;
+                	PrintAndLog("isOk:%02x", isOK);
+        	} else {
+                	PrintAndLog("Command execute timeout");
+        	}
+	}
+        return 0;
+}
+
 int CmdHF14AMfRdBl(const char *Cmd)
 {
 	uint8_t blockNo = 0;
@@ -188,6 +259,72 @@ int CmdHF14AMfRdBl(const char *Cmd)
   return 0;
 }
 
+int CmdHF14AMfURdBl(const char *Cmd)
+{
+        uint8_t blockNo = 0;
+
+        if (strlen(Cmd)<1) {
+                PrintAndLog("Usage:  hf mf urdbl    <block number>");
+                PrintAndLog("        sample: hf mf urdbl 0");
+                return 0;
+        }       
+        
+        blockNo = param_get8(Cmd, 0);
+        PrintAndLog("--block no:%02x", blockNo);
+        
+  UsbCommand c = {CMD_MIFAREU_READBL, {blockNo}};
+  SendCommand(&c);
+
+        UsbCommand resp;
+        if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                uint8_t                isOK  = resp.arg[0] & 0xff;
+                uint8_t              * data  = resp.d.asBytes;
+
+                if (isOK)
+                        PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4));
+                else
+                        PrintAndLog("isOk:%02x", isOK);
+        } else {
+                PrintAndLog("Command execute timeout");
+        }
+
+  return 0;
+}
+
+int CmdHF14AMfURdCard(const char *Cmd)
+{
+        int i;
+        uint8_t sectorNo = 0;
+        
+        uint8_t isOK  = 0;
+        uint8_t * data  = NULL;
+
+        if (sectorNo > 15) {
+                PrintAndLog("Sector number must be less than 16");
+                return 1;
+        }
+        PrintAndLog("Attempting to Read Ultralight... ");
+        
+  UsbCommand c = {CMD_MIFAREU_READCARD, {sectorNo}};
+  SendCommand(&c);
+        PrintAndLog(" ");
+
+        UsbCommand resp;
+        if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                isOK  = resp.arg[0] & 0xff;
+                data  = resp.d.asBytes;
+
+                PrintAndLog("isOk:%02x", isOK);
+                if (isOK) 
+                        for (i = 0; i < 16; i++) {
+                                PrintAndLog("Block %02x:%s", i,sprint_hex(data + i * 4, 4));
+                        }
+        } else {
+                PrintAndLog("Command1 execute timeout");
+        }
+  return 0;
+}
+
 int CmdHF14AMfRdSc(const char *Cmd)
 {
 	int i;
@@ -1670,6 +1807,9 @@ static command_t CommandTable[] =
   {"help",		CmdHelp,						1, "This help"},
   {"dbg",			CmdHF14AMfDbg,			0, "Set default debug mode"},
   {"rdbl",		CmdHF14AMfRdBl,			0, "Read MIFARE classic block"},
+  {"urdbl",              CmdHF14AMfURdBl,                 0, "Read MIFARE Ultralight block"},
+  {"urdcard",           CmdHF14AMfURdCard,               0,"Read MIFARE Ultralight Card"},
+  {"uwrbl",		CmdHF14AMfUWrBl,		0,"Write MIFARE Ultralight block"},
   {"rdsc",		CmdHF14AMfRdSc,			0, "Read MIFARE classic sector"},
   {"dump",		CmdHF14AMfDump,			0, "Dump MIFARE classic tag to binary file"},
   {"restore",	CmdHF14AMfRestore,	0, "Restore MIFARE classic binary file to BLANK tag"},
diff -rupN proxmark3/client/cmdhfmf.h proxmark3-dev/client/cmdhfmf.h
--- proxmark3/client/cmdhfmf.h	2013-10-08 18:09:44.000000000 +0100
+++ proxmark3-dev/client/cmdhfmf.h	2013-10-08 18:09:53.000000000 +0100
@@ -29,10 +29,13 @@ int CmdHFMF(const char *Cmd);
 
 int CmdHF14AMfDbg(const char* cmd);
 int CmdHF14AMfRdBl(const char* cmd);
+int CmdHF14AMfURdBl(const char* cmd);
 int CmdHF14AMfRdSc(const char* cmd);
+int CmdHF14SMfURdCard(const char* cmd);
 int CmdHF14AMfDump(const char* cmd);
 int CmdHF14AMfRestore(const char* cmd);
 int CmdHF14AMfWrBl(const char* cmd);
+int CmdHF14AMfUWrBl(const char* cmd);
 int CmdHF14AMfChk(const char* cmd);
 int CmdHF14AMifare(const char* cmd);
 int CmdHF14AMfNested(const char* cmd);

Offline

#5 2013-10-09 10:47:40

rule
Member
Registered: 2008-05-21
Posts: 417

Re: Adding Ultralight Support to PM3

First of all, thanks for your great contribution!

However, your patch however does not work on the current SVN repository (also tried on -r807 for that matter).

Trying to apply the patch to revision 807

$patch -p1 < ultralight.patch

It gives me the following output:

patching file armsrc/appmain.c
patching file armsrc/apps.h
patching file armsrc/mifarecmd.c
Hunk #1 FAILED at 94.
Hunk #2 FAILED at 245.
Hunk #3 FAILED at 387.
3 out of 3 hunks FAILED -- saving rejects to file armsrc/mifarecmd.c.rej
patching file armsrc/mifareutil.c
Hunk #1 FAILED at 82.
Hunk #2 FAILED at 282.
Hunk #3 FAILED at 360.
Hunk #4 FAILED at 436.
4 out of 4 hunks FAILED -- saving rejects to file armsrc/mifareutil.c.rej
patching file armsrc/mifareutil.h
Hunk #1 FAILED at 55.
Hunk #2 FAILED at 63.
Hunk #3 FAILED at 90.
3 out of 3 hunks FAILED -- saving rejects to file armsrc/mifareutil.h.rej
patching file client/cmdhfmf.c
Hunk #1 FAILED at 140.
Hunk #2 FAILED at 259.
Hunk #3 FAILED at 1807.
3 out of 3 hunks FAILED -- saving rejects to file client/cmdhfmf.c.rej
patching file client/cmdhfmf.h
Hunk #1 FAILED at 29.
1 out of 1 hunk FAILED -- saving rejects to file client/cmdhfmf.h.rej

I could give you access to the SVN repository if you want to contribute more code in the future,
otherwise you maybe want to try to use the "svn diff" command.

Offline

#6 2013-10-09 14:47:50

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

Learn something new everyday, feel like such an idiot.  Im not sure how much code I'll have to contribute in the near future, I'll keep offering patches if I have anything useful.

This time with "svn diff"

Update (10/10/13) Small bug fix, and added indicator for block-lock 1=active 0=inactive on urdcard:

Index: armsrc/appmain.c
===================================================================
--- armsrc/appmain.c	(revision 810)
+++ armsrc/appmain.c	(working copy)
@@ -782,6 +782,12 @@
 		case CMD_MIFARE_READBL:
 			MifareReadBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
+		case CMD_MIFAREU_READBL:
+			MifareUReadBlock(c->arg[0],c->d.asBytes);
+			break;
+		case CMD_MIFAREU_READCARD:
+			MifareUReadCard(c->arg[0],c->d.asBytes);
+                        break;
 		case CMD_MIFARE_READSC:
 			MifareReadSector(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
@@ -788,6 +794,12 @@
 		case CMD_MIFARE_WRITEBL:
 			MifareWriteBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
+		case CMD_MIFAREU_WRITEBL_COMPAT:
+			MifareUWriteBlock(c->arg[0], c->d.asBytes);
+                        break;
+		case CMD_MIFAREU_WRITEBL:
+                        MifareUWriteBlock_Special(c->arg[0], c->d.asBytes);
+                        break;
 		case CMD_MIFARE_NESTED:
 			MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
 			break;
Index: armsrc/apps.h
===================================================================
--- armsrc/apps.h	(revision 810)
+++ armsrc/apps.h	(working copy)
@@ -158,8 +158,12 @@
 void ReaderMifare(bool first_try);
 int32_t dist_nt(uint32_t nt1, uint32_t nt2);
 void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *data);
+void MifareUReadBlock(uint8_t arg0,uint8_t *datain);
+void MifareUReadCard(uint8_t arg0,uint8_t *datain);
 void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
 void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
+void MifareUWriteBlock(uint8_t arg0,uint8_t *datain);
+void MifareUWriteBlock_Special(uint8_t arg0,uint8_t *datain);
 void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
 void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
 void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
Index: armsrc/iso14443a.c
===================================================================
--- armsrc/iso14443a.c	(revision 810)
+++ armsrc/iso14443a.c	(working copy)
@@ -1604,7 +1604,7 @@
 
 void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par, uint32_t *timing)
 {
- 
+
   CodeIso14443aBitsAsReaderPar(frame,bits,par);
   
   // Select the card
Index: armsrc/mifarecmd.c
===================================================================
--- armsrc/mifarecmd.c	(revision 810)
+++ armsrc/mifarecmd.c	(working copy)
@@ -94,6 +94,60 @@
 
 }
 
+void MifareUReadBlock(uint8_t arg0,uint8_t *datain)
+{
+    // params
+	uint8_t blockNo = arg0;
+	
+	// variables
+	byte_t isOK = 0;
+	byte_t dataoutbuf[16];
+	uint8_t uid[10];
+	uint32_t cuid;
+    
+	// clear trace
+	iso14a_clear_trace();
+	iso14443a_setup();
+    
+	LED_A_ON();
+	LED_B_OFF();
+	LED_C_OFF();
+    
+	while (true) {
+		if(!iso14443a_select_card(uid, NULL, &cuid)) {
+            if (MF_DBGLEVEL >= 1)	Dbprintf("Can't select card");
+			break;
+		};
+        
+		if(mifare_ultra_readblock(cuid, blockNo, dataoutbuf)) {
+            if (MF_DBGLEVEL >= 1)	Dbprintf("Read block error");
+			break;
+		};
+        
+		if(mifare_ultra_halt(cuid)) {
+            if (MF_DBGLEVEL >= 1)	Dbprintf("Halt error");
+			break;
+		};
+		
+		isOK = 1;
+		break;
+	}
+	
+	if (MF_DBGLEVEL >= 2)	DbpString("READ BLOCK FINISHED");
+    
+	// add trace trailer
+	memset(uid, 0x44, 4);
+	LogTrace(uid, 4, 0, 0, TRUE);
+	LED_B_ON();
+        cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16);
+	LED_B_OFF();
+    
+    
+    // Thats it...
+	FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+	LEDsoff();
+}
+
 //-----------------------------------------------------------------------------
 // Select, Authenticaate, Read an MIFARE tag. 
 // read sector (data = 4 x 16 bytes = 64 bytes)
@@ -191,6 +245,66 @@
 
 }
 
+void MifareUReadCard(uint8_t arg0, uint8_t *datain)
+{
+  // params
+        uint8_t sectorNo = arg0;
+        
+        // variables
+        byte_t isOK = 0;
+        byte_t dataoutbuf[16 * 4];
+        uint8_t uid[10];
+        uint32_t cuid;
+
+        // clear trace
+        iso14a_clear_trace();
+//      iso14a_set_tracing(false);
+
+        iso14443a_setup();
+
+        LED_A_ON();
+        LED_B_OFF();
+        LED_C_OFF();
+
+        while (true) {
+                if(!iso14443a_select_card(uid, NULL, &cuid)) {
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Can't select card");
+                        break;
+                };
+		for(int sec=0;sec<16;sec++){
+                    if(mifare_ultra_readblock(cuid, sectorNo * 4 + sec, dataoutbuf + 4 * sec)) {
+                    if (MF_DBGLEVEL >= 1)   Dbprintf("Read block %d error",sec);
+                        break;
+                    };
+                }
+                if(mifare_ultra_halt(cuid)) {
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Halt error");
+                        break;
+                };
+
+                isOK = 1;
+                break;
+        }
+        
+        if (MF_DBGLEVEL >= 2) DbpString("READ CARD FINISHED");
+
+        // add trace trailer
+        memset(uid, 0x44, 4);
+        LogTrace(uid, 4, 0, 0, TRUE);
+        
+        LED_B_ON();
+  cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,64);
+  //cmd_send(CMD_ACK,isOK,0,0,dataoutbuf+32, 32);
+        LED_B_OFF();
+
+        // Thats it...
+        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+        LEDsoff();
+//  iso14a_set_tracing(TRUE);
+
+}
+
+
 //-----------------------------------------------------------------------------
 // Select, Authenticaate, Read an MIFARE tag. 
 // read block
@@ -273,6 +387,131 @@
 
 }
 
+void MifareUWriteBlock(uint8_t arg0, uint8_t *datain)
+{
+        // params
+        uint8_t blockNo = arg0;
+        byte_t blockdata[16];
+
+        memset(blockdata,'\0',16);
+        memcpy(blockdata, datain,16);
+        
+        // variables
+        byte_t isOK = 0;
+        uint8_t uid[10];
+        uint32_t cuid;
+
+        // clear trace
+        iso14a_clear_trace();
+	//  iso14a_set_tracing(false);
+
+        iso14443a_setup();
+
+        LED_A_ON();
+        LED_B_OFF();
+        LED_C_OFF();
+
+        while (true) {
+                if(!iso14443a_select_card(uid, NULL, &cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Can't select card");
+                        break;
+                };
+
+                if(mifare_ultra_writeblock(cuid, blockNo, blockdata)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Write block error");
+                        break;
+                };
+
+                if(mifare_ultra_halt(cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Halt error");
+                        break;
+                };
+                
+                isOK = 1;
+                break;
+        }
+        
+        if (MF_DBGLEVEL >= 2)   DbpString("WRITE BLOCK FINISHED");
+
+        // add trace trailer
+        memset(uid, 0x44, 4);
+        LogTrace(uid, 4, 0, 0, TRUE);
+
+        LED_B_ON();
+  	cmd_send(CMD_ACK,isOK,0,0,0,0);
+//      UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
+        LED_B_OFF();
+
+
+        // Thats it...
+        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+        LEDsoff();
+//  iso14a_set_tracing(TRUE);
+
+}
+
+void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain)
+{
+        // params
+        uint8_t blockNo = arg0;
+        byte_t blockdata[4];
+        
+	memcpy(blockdata, datain,4);
+
+        // variables
+        byte_t isOK = 0;
+        uint8_t uid[10];
+        uint32_t cuid;
+
+        // clear trace
+        iso14a_clear_trace();
+        //  iso14a_set_tracing(false);
+
+        iso14443a_setup();
+
+        LED_A_ON();
+        LED_B_OFF();
+        LED_C_OFF();
+
+        while (true) {
+                        if(!iso14443a_select_card(uid, NULL, &cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Can't select card");
+                        break;
+                };
+
+                if(mifare_ultra_special_writeblock(cuid, blockNo, blockdata)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Write block error");
+                        break;
+                };
+
+                if(mifare_ultra_halt(cuid)) {
+                        if (MF_DBGLEVEL >= 1)   Dbprintf("Halt error");
+                        break;
+                };
+
+                isOK = 1;
+                break;
+        }
+
+        if (MF_DBGLEVEL >= 2)   DbpString("WRITE BLOCK FINISHED");
+
+        // add trace trailer
+	memset(uid, 0x44, 4);
+        LogTrace(uid, 4, 0, 0, TRUE);
+
+       LED_B_ON();
+        cmd_send(CMD_ACK,isOK,0,0,0,0);
+//      UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand));
+        LED_B_OFF();
+
+
+        // Thats it...
+        FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+        LEDsoff();
+//  iso14a_set_tracing(TRUE);
+
+}
+
 // Return 1 if the nonce is invalid else return 0
 int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) {
 	return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \
Index: armsrc/mifareutil.c
===================================================================
--- armsrc/mifareutil.c	(revision 810)
+++ armsrc/mifareutil.c	(working copy)
@@ -82,6 +82,32 @@
 	return mifare_sendcmd_shortex(pcs, crypted, cmd, data, answer, NULL, timing);
 }
 
+int mifare_sendcmd_short_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *timing)
+{
+        uint8_t dcmd[8];//, ecmd[4];
+        //uint32_t par=0;
+
+        dcmd[0] = cmd;
+        dcmd[1] = data[0];
+	dcmd[2] = data[1];
+	dcmd[3] = data[2];
+	dcmd[4] = data[3];
+	dcmd[5] = data[4];
+	AppendCrc14443a(dcmd, 6);
+	//Dbprintf("Data command: %02x", dcmd[0]);
+	//Dbprintf("Data R: %02x %02x %02x %02x %02x %02x %02x", dcmd[1],dcmd[2],dcmd[3],dcmd[4],dcmd[5],dcmd[6],dcmd[7]);
+
+        //memcpy(ecmd, dcmd, sizeof(dcmd));
+	ReaderTransmit(dcmd, sizeof(dcmd), NULL);
+	int len = ReaderReceive(answer);
+	if(!len)
+	{
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Authentication failed. Card timeout.");
+                return 2;
+        }
+	return len;
+}
+
 int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr, uint32_t *timing)
 {
 	uint8_t dcmd[4], ecmd[4];
@@ -256,6 +282,37 @@
 	return 0;
 }
 
+int mifare_ultra_readblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData)
+{
+	// variables
+	int len;
+	uint8_t	bt[2];
+	
+	uint8_t* receivedAnswer = mifare_get_bigbufptr();
+	
+	// command MIFARE_CLASSIC_READBLOCK
+	len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer,NULL);
+	if (len == 1) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
+		return 1;
+	}
+	if (len != 18) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("Cmd Error: card timeout. len: %x", len);
+		return 2;
+	}
+    
+	memcpy(bt, receivedAnswer + 16, 2);
+	AppendCrc14443a(receivedAnswer, 16);
+	if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("Cmd CRC response error.");
+		return 3;
+	}
+	
+	memcpy(blockData, receivedAnswer, 14);
+	return 0;
+}
+
+
 int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) 
 {
 	// variables
@@ -303,6 +360,65 @@
 	return 0;
 }
 
+int mifare_ultra_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData) 
+{
+        // variables
+        int len;     
+        uint32_t par = 0;
+        
+        uint8_t d_block[18];
+        uint8_t* receivedAnswer = mifare_get_bigbufptr();
+        
+        // command MIFARE_CLASSIC_WRITEBLOCK
+        len = mifare_sendcmd_short(NULL, 1, 0xA0, blockNo, receivedAnswer,NULL);
+
+        if ((len != 1) || (receivedAnswer[0] != 0x0A)) {   //  0x0a - ACK
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]);  
+                return 1;
+        }
+
+	memset(d_block,'\0',18);
+	memcpy(d_block, blockData, 16);
+        AppendCrc14443a(d_block, 16);
+
+	ReaderTransmitPar(d_block, sizeof(d_block), par, NULL);
+
+        // Receive the response
+        len = ReaderReceive(receivedAnswer);    
+
+	if ((len != 1) || (receivedAnswer[0] != 0x0A)) {   //  0x0a - ACK
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len);
+                return 2;
+        }        
+
+        return 0;
+} 
+
+int mifare_ultra_special_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData)
+{
+        // variables
+        int len;
+        //uint32_t par = 0;
+
+        uint8_t d_block[8];
+        uint8_t* receivedAnswer = mifare_get_bigbufptr();
+
+        // command MIFARE_CLASSIC_WRITEBLOCK
+	memset(d_block,'\0',8);
+	d_block[0]= blockNo;
+	memcpy(d_block+1,blockData,4);
+	AppendCrc14443a(d_block, 6);
+
+	//i know the data send here is correct
+        len = mifare_sendcmd_short_special(NULL, 1, 0xA2, d_block, receivedAnswer,NULL);
+
+        if (receivedAnswer[0] != 0x0A) {   //  0x0a - ACK
+                if (MF_DBGLEVEL >= 1)   Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0],len);
+                return 1;
+        }
+        return 0;
+}
+
 int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) 
 {
 	// variables
@@ -320,6 +436,23 @@
 	return 0;
 }
 
+int mifare_ultra_halt(uint32_t uid)
+{
+	// variables
+	int len;
+	
+	// Mifare HALT
+	uint8_t* receivedAnswer = mifare_get_bigbufptr();
+    
+	len = mifare_sendcmd_short(NULL, 1, 0x50, 0x00, receivedAnswer, NULL);
+	if (len != 0) {
+		if (MF_DBGLEVEL >= 1)	Dbprintf("halt error. response len: %x", len);
+		return 1;
+	}
+    
+	return 0;
+}
+
 // work with emulator memory
 void emlSetMem(uint8_t *data, int blockNum, int blocksCount) {
 	uint8_t* emCARD = eml_get_bigbufptr_cardmem();
Index: armsrc/mifareutil.h
===================================================================
--- armsrc/mifareutil.h	(revision 810)
+++ armsrc/mifareutil.h	(working copy)
@@ -55,6 +55,7 @@
 //functions
 uint8_t* mifare_get_bigbufptr(void);
 int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t *timing);
+int mifare_sendcmd_short_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t *data, uint8_t* amswer, uint8_t *timing);
 int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr, uint32_t *timing);
 
 int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, \
@@ -62,8 +63,12 @@
 int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, \
 													uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested, uint32_t * ntptr, uint32_t *timing);
 int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); 
+int mifare_ultra_readblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData);
 int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData);
+int mifare_ultra_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData);
+int mifare_ultra_special_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData);
 int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); 
+int mifare_ultra_halt(uint32_t uid);
 
 // crypto functions
 void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len);
@@ -85,4 +90,4 @@
 int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum);
 int emlCheckValBl(int blockNum);
 
-#endif
\ No newline at end of file
+#endif
Index: client/Makefile
===================================================================
--- client/Makefile	(revision 810)
+++ client/Makefile	(working copy)
@@ -24,8 +24,8 @@
 MOC = $(QTDIR)/bin/moc
 LUAPLATFORM = mingw
 else ifeq ($(platform),Darwin)
-CXXFLAGS = -I/Library/Frameworks/QtGui.framework/Versions/Current/Headers -I/Library/Frameworks/QtCore.framework/Versions/Current/Headers
-QTLDLIBS = -framework QtGui -framework QtCore
+CXXFLAGS = -I/opt/local/Library/Frameworks/QtGui.framework/Versions/Current/Headers -I/opt/local/Library/Frameworks/QtCore.framework/Versions/Current/Headers
+QTLDLIBS = -lQtCore -lQtGui
 MOC = moc 
 LUAPLATFORM = macosx
 else
Index: client/cmdhfmf.c
===================================================================
--- client/cmdhfmf.c	(revision 810)
+++ client/cmdhfmf.c	(working copy)
@@ -140,6 +140,80 @@
 	return 0;
 }
 
+int CmdHF14AMfUWrBl(const char *Cmd)
+{
+        uint8_t blockNo = 0;
+        uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+	UsbCommand resp;
+        
+        if (strlen(Cmd)<3) {
+                PrintAndLog("Usage:  hf mf uwrbl    <block number> <block data (8 hex symbols)>");
+                PrintAndLog("        sample: hf mf uwrbl 0 01020304");
+                return 0;
+        }       
+
+        blockNo = param_get8(Cmd, 0);
+        if (param_gethex(Cmd, 1, bldata, 8)) {
+                PrintAndLog("Block data must include 8 HEX symbols");
+                return 1;
+        }
+
+	switch(blockNo)
+	{
+	case 0:
+		PrintAndLog("Access Denied");
+		break;
+	case 1:
+		PrintAndLog("Access Denied");
+		break;
+	case 2:
+		PrintAndLog("--specialblock no:%02x", blockNo);
+                PrintAndLog("--data: %s", sprint_hex(bldata, 4));
+                UsbCommand c = {CMD_MIFAREU_WRITEBL, {blockNo}};
+                memcpy(c.d.asBytes, bldata, 4);
+                SendCommand(&c);
+
+                if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                        uint8_t isOK  = resp.arg[0] & 0xff;
+                        PrintAndLog("isOk:%02x", isOK);
+                } else {
+                        PrintAndLog("Command execute timeout");
+                }
+		break;
+	case 3:
+	        PrintAndLog("--specialblock no:%02x", blockNo);
+                PrintAndLog("--data: %s", sprint_hex(bldata, 4));
+                UsbCommand d = {CMD_MIFAREU_WRITEBL, {blockNo}};
+                memcpy(d.d.asBytes,bldata, 4);
+                SendCommand(&d);
+
+                if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                        uint8_t isOK  = resp.arg[0] & 0xff;
+                        PrintAndLog("isOk:%02x", isOK);
+                } else {
+                        PrintAndLog("Command execute timeout");
+                }
+		break;
+	default: 
+        	PrintAndLog("--block no:%02x", blockNo);
+        	PrintAndLog("--data: %s", sprint_hex(bldata, 4));        	
+  		//UsbCommand e = {CMD_MIFAREU_WRITEBL_COMPAT, {blockNo}};
+        	//memcpy(e.d.asBytes,bldata, 16);
+  		UsbCommand e = {CMD_MIFAREU_WRITEBL, {blockNo}};
+                memcpy(e.d.asBytes,bldata, 4);
+		SendCommand(&e);
+
+        	if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+        	        uint8_t isOK  = resp.arg[0] & 0xff;
+                	PrintAndLog("isOk:%02x", isOK);
+        	} else {
+                	PrintAndLog("Command execute timeout");
+        	}
+		break;
+	}
+        return 0;
+}
+
 int CmdHF14AMfRdBl(const char *Cmd)
 {
 	uint8_t blockNo = 0;
@@ -188,6 +262,129 @@
   return 0;
 }
 
+int CmdHF14AMfURdBl(const char *Cmd)
+{
+        uint8_t blockNo = 0;
+
+        if (strlen(Cmd)<1) {
+                PrintAndLog("Usage:  hf mf urdbl    <block number>");
+                PrintAndLog("        sample: hf mf urdbl 0");
+                return 0;
+        }       
+        
+        blockNo = param_get8(Cmd, 0);
+        PrintAndLog("--block no:%02x", blockNo);
+        
+  UsbCommand c = {CMD_MIFAREU_READBL, {blockNo}};
+  SendCommand(&c);
+
+        UsbCommand resp;
+        if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                uint8_t                isOK  = resp.arg[0] & 0xff;
+                uint8_t              * data  = resp.d.asBytes;
+
+                if (isOK)
+                        PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4));
+                else
+                        PrintAndLog("isOk:%02x", isOK);
+        } else {
+                PrintAndLog("Command execute timeout");
+        }
+
+  return 0;
+}
+
+int CmdHF14AMfURdCard(const char *Cmd)
+{
+        int i;
+        uint8_t sectorNo = 0;
+	uint8_t *lockbytes_t=NULL;
+	uint8_t lockbytes[2]={0,0};
+	bool bit[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+        
+        uint8_t isOK  = 0;
+        uint8_t * data  = NULL;
+
+        if (sectorNo > 15) {
+                PrintAndLog("Sector number must be less than 16");
+                return 1;
+        }
+        PrintAndLog("Attempting to Read Ultralight... ");
+        
+  	UsbCommand c = {CMD_MIFAREU_READCARD, {sectorNo}};
+  	SendCommand(&c);
+
+        UsbCommand resp;
+        if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
+                isOK  = resp.arg[0] & 0xff;
+                data  = resp.d.asBytes;
+
+                PrintAndLog("isOk:%02x", isOK);
+                if (isOK) 
+                        for (i = 0; i < 16; i++) {
+				switch(i){
+				  case 2:
+					//process lock bytes
+					lockbytes_t=data+(i*4);
+					lockbytes[0]=lockbytes_t[2];
+					lockbytes[1]=lockbytes_t[3];
+					for(int j=0; j<16; j++){
+						bit[j]=lockbytes[j/8] & ( 1 <<(7-j%8));
+					}
+					//PrintAndLog("LB %02x %02x", lockbytes[0],lockbytes[1]);
+					//PrintAndLog("LB2b %02x %02x %02x %02x %02x %02x %02x %02x",bit[8],bit[9],bit[10],bit[11],bit[12],bit[13],bit[14],bit[15]);		
+					PrintAndLog("Block %02x:%s ", i,sprint_hex(data + i * 4, 4));
+					break;
+				  case 3: 
+					PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[4]);
+					break;
+				  case 4:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[3]);
+					break;
+				  case 5:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[2]);
+					break;
+				  case 6:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[1]);
+					break;
+				  case 7:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[0]);
+					break;
+				  case 8:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[15]);
+					break;
+				  case 9:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[14]);
+					break;
+				  case 10:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[13]);
+					break;
+				  case 11:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[12]);
+					break;
+				  case 12:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[11]);
+					break;
+				  case 13:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[10]);
+					break;
+				  case 14:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[9]);
+					break;
+				  case 15:
+                                        PrintAndLog("Block %02x:%s [%d]", i,sprint_hex(data + i * 4, 4),bit[8]);
+					break;
+				  default:
+					PrintAndLog("Block %02x:%s ", i,sprint_hex(data + i * 4, 4));
+					break;
+				}
+                        }
+        } else {
+                PrintAndLog("Command1 execute timeout");
+        }
+  return 0;
+}
+
 int CmdHF14AMfRdSc(const char *Cmd)
 {
 	int i;
@@ -1670,6 +1867,9 @@
   {"help",		CmdHelp,						1, "This help"},
   {"dbg",			CmdHF14AMfDbg,			0, "Set default debug mode"},
   {"rdbl",		CmdHF14AMfRdBl,			0, "Read MIFARE classic block"},
+  {"urdbl",              CmdHF14AMfURdBl,                 0, "Read MIFARE Ultralight block"},
+  {"urdcard",           CmdHF14AMfURdCard,               0,"Read MIFARE Ultralight Card"},
+  {"uwrbl",		CmdHF14AMfUWrBl,		0,"Write MIFARE Ultralight block"},
   {"rdsc",		CmdHF14AMfRdSc,			0, "Read MIFARE classic sector"},
   {"dump",		CmdHF14AMfDump,			0, "Dump MIFARE classic tag to binary file"},
   {"restore",	CmdHF14AMfRestore,	0, "Restore MIFARE classic binary file to BLANK tag"},
Index: client/cmdhfmf.h
===================================================================
--- client/cmdhfmf.h	(revision 810)
+++ client/cmdhfmf.h	(working copy)
@@ -29,10 +29,13 @@
 
 int CmdHF14AMfDbg(const char* cmd);
 int CmdHF14AMfRdBl(const char* cmd);
+int CmdHF14AMfURdBl(const char* cmd);
 int CmdHF14AMfRdSc(const char* cmd);
+int CmdHF14SMfURdCard(const char* cmd);
 int CmdHF14AMfDump(const char* cmd);
 int CmdHF14AMfRestore(const char* cmd);
 int CmdHF14AMfWrBl(const char* cmd);
+int CmdHF14AMfUWrBl(const char* cmd);
 int CmdHF14AMfChk(const char* cmd);
 int CmdHF14AMifare(const char* cmd);
 int CmdHF14AMfNested(const char* cmd);
Index: client/proxmark3.c
===================================================================
--- client/proxmark3.c	(revision 810)
+++ client/proxmark3.c	(working copy)
@@ -168,6 +168,7 @@
 			
 			if (cmd[0] != 0x00) {
 				if (strncmp(cmd, "quit", 4) == 0) {
+					exit(0);
 					break;
 				}
 				
Index: include/usb_cmd.h
===================================================================
--- include/usb_cmd.h	(revision 810)
+++ include/usb_cmd.h	(working copy)
@@ -139,8 +139,12 @@
 #define CMD_MIFARE_NESTED                                                 0x0612
 
 #define CMD_MIFARE_READBL                                                 0x0620
+#define CMD_MIFAREU_READBL						  0x0720
 #define CMD_MIFARE_READSC                                                 0x0621
+#define CMD_MIFAREU_READCARD						  0x0721
 #define CMD_MIFARE_WRITEBL                                                0x0622
+#define CMD_MIFAREU_WRITEBL_COMPAT					  0x0722
+#define CMD_MIFAREU_WRITEBL						  0x0723
 #define CMD_MIFARE_CHKKEYS                                                0x0623
 
 #define CMD_MIFARE_SNIFFER                                                0x0630

Last edited by midnitesnake (2013-10-20 09:36:23)

Offline

#7 2013-10-10 17:32:33

jonor
Contributor
Registered: 2009-09-17
Posts: 97

Re: Adding Ultralight Support to PM3

rule wrote:

It gives me the following output:

I think the problem is the End Of Line, in proxmark's source some files has eol dos and some file has eol unix. When you copy the patch from web and wrote in a new file you have the same eol for all files, but when you try to patch files with different eol from your OS your got a FAIL.
For example
armsrc/appmain.c, armsrc/apps.h are unix eol so rule you got ok (I think you develope on Linux), all other files are eol dos.

after conversion

dos2unix -o armsrc/mifarecmd.c
dos2unix -o armsrc/mifareutil.c
dos2unix -o armsrc/mifareutil.h
dos2unix -o client/cmdhfmf.c
dos2unix -o client/cmdhfmf.h
dos2unix -o client/proxmark3.c
dos2unix -o include/usb_cmd.h

I got this with the same patch:

patching file armsrc/appmain.c
patching file armsrc/apps.h
patching file armsrc/mifarecmd.c
patching file armsrc/mifareutil.c
patching file armsrc/mifareutil.h
patching file client/cmdhfmf.c
patching file client/cmdhfmf.h
patching file client/proxmark3.c
patching file include/usb_cmd.h

Offline

#8 2013-10-10 17:38:30

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

I am currently developing on OSX.  Would explain the problem with EOL.

Offline

#9 2013-10-10 20:54:47

jonor
Contributor
Registered: 2009-09-17
Posts: 97

Re: Adding Ultralight Support to PM3

OSX use another EOL different from unix and dos.
But the issue is not in original OS but in destination where the patch is applied.
Without dos2unix when I tried to apply your patch I got the same errors reported by rule, my OS is Linux.
But I think if you tried to copy and paste from your browser your patch and try to apply it, you got FAILED, because the original file has a different EOL of your OS.
With my first patch I have the same issue, so I uploaded it to external host as file, and so on for all other patches.

Last edited by jonor (2013-10-10 20:56:23)

Offline

#10 2013-10-11 09:55:22

rule
Member
Registered: 2008-05-21
Posts: 417

Re: Adding Ultralight Support to PM3

Hey Guys,

Don't know about the new line issues. But 'svn diff' / 'svn patch' works like a charm:

svn patch ultralight.patch

Integrates it correctly

U         armsrc/appmain.c
U         armsrc/apps.h
U         armsrc/iso14443a.c
U         armsrc/mifarecmd.c
U         armsrc/mifareutil.c
U         armsrc/mifareutil.h
U         client/Makefile
U         client/cmdhfmf.c
U         client/cmdhfmf.h
U         client/proxmark3.c
U         include/usb_cmd.h

However, I did not commit the changes made in 'client/Makefile'. Please make sure that Makefile changes are really necessary and working for everyone. On my OSX 10.8.5 it did not compile with your settings. Did you installed QT through a deployment system like MacPorts? If I remember correctly, you will get it installed in '/Library' with linkable frameworks if you use the installer from the QT-Project website.

If you can figure out a more generic way to make it compile on all configurations, I would be happy to commit it to the repository.

Thanks for you contribution, keep up the good work!

  Roel

Offline

#11 2013-10-11 10:49:57

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

Sorry, the Makefile is the patch I created was inorder for MacPorts QT libraries to integrate into the project and work correctly for me.

Last edited by midnitesnake (2013-10-11 10:50:59)

Offline

#12 2013-10-12 09:38:11

asper
Contributor
Registered: 2008-08-24
Posts: 1,409

Re: Adding Ultralight Support to PM3

Problem: the commands seem to be related to a Mifare Ultralight [MF0ICU1] but why an "authenticate" command (use of key A or B) is provided to write ? Ultralight C [MF0ICU2] has keys but not Ultralight.

EDIT: my fault, I wrongly read source code. Sorry, all is fine ! Well so also Ultralight C support won't be difficult to be implemented !

Last edited by asper (2013-10-12 09:40:09)

Offline

#13 2013-10-12 13:53:10

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

Unfortunately, I haven't got an Ultralight C to practise on. 

Need to do more work on it like dump/restore to/from file, and to see if I can trick the proxmark into simulating a card?

Offline

#14 2014-03-24 19:21:52

midnitesnake
Contributor
Registered: 2012-05-11
Posts: 151

Re: Adding Ultralight Support to PM3

Now added support for Chinese UID Changeable cards. See github repo.

UPDATE: Ive been getting questions about how it works.

hf mf uwrbl  <block> <data> [w]

to write to a normal block:

hf mf uwrbl 0f ffffffff

when trying to write to  block 0 or 1 :

 hf mf uwrbl 0 ffffffff
 Access Denied!

You have to use the w flag to denote that the card is a UID changeable e.g.

 hf mf uwrbl 0 ffffffff w
--specialblock no:00          
--data: ff ff ff ff                         
#db# WRITE BLOCK FINISHED                 
isOk:00   

Last edited by midnitesnake (2014-03-25 19:47:11)

Offline

#15 2014-03-28 06:54:07

iceman
Administrator
Registered: 2013-04-25
Posts: 9,497
Website

Re: Adding Ultralight Support to PM3

Midnitesnake,  I just ordered 10 pieces of Ultralight C,  supposed to by magic ones. I will try out the functionality when I get them.

Offline

#16 2014-03-28 10:39:03

asper
Contributor
Registered: 2008-08-24
Posts: 1,409

Re: Adding Ultralight Support to PM3

All commands should be the same (ultralight commands); only AUTHENTICATE command is added to ultraligh C and it SHOULD be [60] (not tested).

If you are able to sniff an authentication transaction between Ultralight C and a reader (es. SL500F) please post it here ! wink

EDIT: ultralight C shouldn't be changeable UID... only ultralight (without c) are supposed to be with changeable uid... can you provide a link where you bought them ?

Last edited by asper (2014-03-28 10:51:32)

Offline

#17 2014-04-07 21:38:28

iceman
Administrator
Registered: 2013-04-25
Posts: 9,497
Website

Re: Adding Ultralight Support to PM3

Funny, I got my order today.   Plenty of cards to play with now.

I can confirm that my ultralight cards are indeed magic ones. Works with Midnitesnake's commands like a charm. However when it comes to the Ultralight C cards, I must confess that I'm in predikament.  I can't seem to see the difference between a normal ultralight card and a ultralight c card with the current implementation.   I must have missed something.

Offline

Board footer

Powered by FluxBB