From 981bd4292e1f6d898453479c7bad86480d0c959f Mon Sep 17 00:00:00 2001
From: "roel@libnfc.org" <roel@libnfc.org@ef4ab9da-24cd-11de-8aaa-f3a34680c41f>
Date: Fri, 11 Oct 2013 08:43:23 +0000
Subject: [PATCH] integrated MIFARE ultralight features, contributed by
 'midnitesnake'

---
 armsrc/appmain.c    |  12 ++
 armsrc/apps.h       |   4 +
 armsrc/iso14443a.c  |   2 +-
 armsrc/mifarecmd.c  | 275 +++++++++++++++++++++++++++++++++++++++++---
 armsrc/mifareutil.c | 181 +++++++++++++++++++++++++----
 armsrc/mifareutil.h |  41 ++++---
 client/cmdhfmf.c    | 236 ++++++++++++++++++++++++++++++++++---
 client/cmdhfmf.h    |  23 ++--
 client/proxmark3.c  |   1 +
 include/usb_cmd.h   |   4 +
 10 files changed, 690 insertions(+), 89 deletions(-)

diff --git a/armsrc/appmain.c b/armsrc/appmain.c
index b5a85deb..3f309520 100644
--- a/armsrc/appmain.c
+++ b/armsrc/appmain.c
@@ -782,12 +782,24 @@ void UsbPacketReceived(uint8_t *packet, int len)
 		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 --git a/armsrc/apps.h b/armsrc/apps.h
index c0faaca2..4e99b1aa 100644
--- a/armsrc/apps.h
+++ b/armsrc/apps.h
@@ -158,8 +158,12 @@ void EPA_PACE_Collect_Nonce(UsbCommand * c);
 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 --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c
index b3316ef0..63cc32ae 100644
--- a/armsrc/iso14443a.c
+++ b/armsrc/iso14443a.c
@@ -1604,7 +1604,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, int maxLen, int
 
 void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par, uint32_t *timing)
 {
- 
+
   CodeIso14443aBitsAsReaderPar(frame,bits,par);
   
   // Select the card
diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c
index 91ae9054..cbad7a90 100644
--- a/armsrc/mifarecmd.c
+++ b/armsrc/mifarecmd.c
@@ -91,12 +91,66 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
 	FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
 	LEDsoff();
 //  iso14a_set_tracing(TRUE);
-
-}
-
-//-----------------------------------------------------------------------------
-// Select, Authenticaate, Read an MIFARE tag. 
-// read sector (data = 4 x 16 bytes = 64 bytes)
+
+}
+
+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)
 //-----------------------------------------------------------------------------
 void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
 {
@@ -188,12 +242,72 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
 	FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
 	LEDsoff();
 //  iso14a_set_tracing(TRUE);
-
-}
-
-//-----------------------------------------------------------------------------
-// Select, Authenticaate, Read an MIFARE tag. 
-// read block
+
+}
+
+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
 //-----------------------------------------------------------------------------
 void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
 {
@@ -270,12 +384,137 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain)
 	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))) & \
+
+}
+
+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))) & \
 	(oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \
 	(oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0;
 }
diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c
index 4f4e978c..b847043b 100644
--- a/armsrc/mifareutil.c
+++ b/armsrc/mifareutil.c
@@ -79,12 +79,38 @@ uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data) {
 // send commands
 int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t *timing)
 {
-	return mifare_sendcmd_shortex(pcs, crypted, cmd, data, answer, NULL, 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)
-{
-	uint8_t dcmd[4], ecmd[4];
+	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];
 	uint32_t pos, par, res;
 
 	dcmd[0] = cmd;
@@ -253,12 +279,43 @@ int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blo
 	}
 	
 	memcpy(blockData, receivedAnswer, 16);
-	return 0;
-}
-
-int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) 
-{
-	// variables
+	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
 	int len, i;	
 	uint32_t pos;
 	uint32_t par = 0;
@@ -300,12 +357,71 @@ int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t bl
 		return 2;
 	}
 	
-	return 0;
-}
-
-int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) 
-{
-	// variables
+	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
 	int len;	
 	
 	// Mifare HALT
@@ -317,12 +433,29 @@ int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid)
 		return 1;
 	}
 
-	return 0;
-}
-
-// work with emulator memory
-void emlSetMem(uint8_t *data, int blockNum, int blocksCount) {
-	uint8_t* emCARD = eml_get_bigbufptr_cardmem();
+	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();
 	
 	memcpy(emCARD + blockNum * 16, data, blocksCount * 16);
 }
diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h
index d170f3c6..ad637ea0 100644
--- a/armsrc/mifareutil.h
+++ b/armsrc/mifareutil.h
@@ -52,21 +52,26 @@ extern int MF_DBGLEVEL;
 
 #define cardSTATE_TO_IDLE() cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF();
 
-//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_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, \
+//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, \
 												uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested);
-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_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData);
-int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); 
-
-// crypto functions
-void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len);
+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);
 void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, int len, uint32_t *par);
 uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data);
 
@@ -82,7 +87,7 @@ void emlGetMem(uint8_t *data, int blockNum, int blocksCount);
 void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount);
 uint64_t emlGetKey(int sectorNum, int keyType);
 int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum);
-int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum);
-int emlCheckValBl(int blockNum);
-
-#endif
\ No newline at end of file
+int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum);
+int emlCheckValBl(int blockNum);
+
+#endif
diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c
index a0e67160..74382886 100644
--- a/client/cmdhfmf.c
+++ b/client/cmdhfmf.c
@@ -137,12 +137,86 @@ int CmdHF14AMfWrBl(const char *Cmd)
 		PrintAndLog("Command execute timeout");
 	}
 
-	return 0;
-}
-
-int CmdHF14AMfRdBl(const char *Cmd)
-{
-	uint8_t blockNo = 0;
+	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;
 	uint8_t keyType = 0;
 	uint8_t key[6] = {0, 0, 0, 0, 0, 0};
 	
@@ -185,12 +259,135 @@ int CmdHF14AMfRdBl(const char *Cmd)
 		PrintAndLog("Command execute timeout");
 	}
 
-  return 0;
-}
-
-int CmdHF14AMfRdSc(const char *Cmd)
-{
-	int i;
+  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;
 	uint8_t sectorNo = 0;
 	uint8_t keyType = 0;
 	uint8_t key[6] = {0, 0, 0, 0, 0, 0};
@@ -1667,12 +1864,15 @@ int CmdHF14AMfSniff(const char *Cmd){
 
 static command_t CommandTable[] =
 {
-  {"help",		CmdHelp,						1, "This help"},
-  {"dbg",			CmdHF14AMfDbg,			0, "Set default debug mode"},
-  {"rdbl",		CmdHF14AMfRdBl,			0, "Read MIFARE classic 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"},
+  {"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"},
   {"wrbl",		CmdHF14AMfWrBl,			0, "Write MIFARE classic block"},
   {"chk",			CmdHF14AMfChk,			0, "Test block keys"},
   {"mifare",	CmdHF14AMifare,			0, "Read parity error messages. param - <used card nonce>"},
diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h
index 16dcccfb..62e856ad 100644
--- a/client/cmdhfmf.h
+++ b/client/cmdhfmf.h
@@ -26,16 +26,19 @@
 #include "mifarehost.h"
 
 int CmdHFMF(const char *Cmd);
-
-int CmdHF14AMfDbg(const char* cmd);
-int CmdHF14AMfRdBl(const char* cmd);
-int CmdHF14AMfRdSc(const char* cmd);
-int CmdHF14AMfDump(const char* cmd);
-int CmdHF14AMfRestore(const char* cmd);
-int CmdHF14AMfWrBl(const char* cmd);
-int CmdHF14AMfChk(const char* cmd);
-int CmdHF14AMifare(const char* cmd);
-int CmdHF14AMfNested(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);
 int CmdHF14AMfSniff(const char* cmd);
 int CmdHF14AMf1kSim(const char* cmd);
 int CmdHF14AMfEClear(const char* cmd);
diff --git a/client/proxmark3.c b/client/proxmark3.c
index 2fefc890..102e03f7 100644
--- a/client/proxmark3.c
+++ b/client/proxmark3.c
@@ -168,6 +168,7 @@ static void *main_loop(void *targ) {
 			
 			if (cmd[0] != 0x00) {
 				if (strncmp(cmd, "quit", 4) == 0) {
+					exit(0);
 					break;
 				}
 				
diff --git a/include/usb_cmd.h b/include/usb_cmd.h
index 8726ec67..2673dcac 100644
--- a/include/usb_cmd.h
+++ b/include/usb_cmd.h
@@ -139,8 +139,12 @@ typedef struct {
 #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
-- 
2.39.5