From d2f487af9cc29122d86ca4f349ba825aaf98748c Mon Sep 17 00:00:00 2001 From: "martin.holst@gmail.com" Date: Fri, 31 Jan 2014 21:17:34 +0000 Subject: [PATCH] Various improvements on the Mifare1kSimulation. Fixed issue with tracebuffer not being cleared, fixed issue with 'static' errors, added modes for doing reader-only attack, added interactive mode, added possibility to set UID from cmdline, either 7-byte or 4-byte. See http://www.proxmark.org/forum/viewtopic.php?id=1529 and http://www.proxmark.org/forum/viewtopic.php?id=1649&p=3 for some more background and discussion --- armsrc/iso14443a.c | 442 ++++++++++++++++++++++---------------- client/cmdhfmf.c | 512 +++++++++++++++++++++++++-------------------- include/usb_cmd.h | 9 + 3 files changed, 554 insertions(+), 409 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 3966d77a..aa97e6e9 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1926,7 +1926,7 @@ void ReaderMifare(bool first_try) static uint8_t mf_nr_ar3; uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); - traceLen = 0; + iso14a_clear_trace(); tracing = false; byte_t nt_diff = 0; @@ -1974,7 +1974,7 @@ void ReaderMifare(bool first_try) LED_B_OFF(); LED_C_OFF(); - + for(uint16_t i = 0; TRUE; i++) { WDT_HIT(); @@ -2113,18 +2113,22 @@ void ReaderMifare(bool first_try) tracing = TRUE; } -//----------------------------------------------------------------------------- -// MIFARE 1K simulate. -// -//----------------------------------------------------------------------------- -void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +/** + *MIFARE 1K simulate. + * + *@param flags : + * FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK + * 4B_FLAG_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that + * 7B_FLAG_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that + * FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later + *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is inifite + */ +void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) { int cardSTATE = MFEMUL_NOFIELD; int _7BUID = 0; int vHf = 0; // in mV - //int nextCycleTimeout = 0; int res; -// uint32_t timer = 0; uint32_t selTimer = 0; uint32_t authTimer = 0; uint32_t par = 0; @@ -2132,60 +2136,83 @@ void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) uint8_t cardWRBL = 0; uint8_t cardAUTHSC = 0; uint8_t cardAUTHKEY = 0xff; // no authentication - //uint32_t cardRn = 0; uint32_t cardRr = 0; uint32_t cuid = 0; - uint32_t rn_enc = 0; + //uint32_t rn_enc = 0; uint32_t ans = 0; uint32_t cardINTREG = 0; uint8_t cardINTBLOCK = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - + uint32_t numReads = 0;//Counts numer of times reader read a block uint8_t* receivedCmd = eml_get_bigbufptr_recbuf(); uint8_t *response = eml_get_bigbufptr_sendbuf(); - static uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k 4BUID + uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k 4BUID + uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; + uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; // !!! + uint8_t rSAK[] = {0x08, 0xb6, 0xdd}; + uint8_t rSAK1[] = {0x04, 0xda, 0x17}; - static uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - static uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; // !!! - - static uint8_t rSAK[] = {0x08, 0xb6, 0xdd}; - static uint8_t rSAK1[] = {0x04, 0xda, 0x17}; + uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; + uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; - static uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; -// static uint8_t rAUTH_NT[] = {0x1a, 0xac, 0xff, 0x4f}; - static uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; + //Here, we collect UID,NT,AR,NR,UID2,NT2,AR2,NR2 + // This can be used in a reader-only attack. + // (it can also be retrieved via 'hf 14a list', but hey... + uint32_t ar_nr_responses[] = {0,0,0,0,0,0,0,0}; + uint8_t ar_nr_collected = 0; // clear trace - traceLen = 0; + iso14a_clear_trace(); + tracing = true; // Authenticate response - nonce uint32_t nonce = bytes_to_num(rAUTH_NT, 4); - - // get UID from emul memory - emlGetMemBt(receivedCmd, 7, 1); - _7BUID = !(receivedCmd[0] == 0x00); - if (!_7BUID) { // ---------- 4BUID - rATQA[0] = 0x04; - emlGetMemBt(rUIDBCC1, 0, 4); + //-- Determine the UID + // Can be set from emulator memory, incoming data + // and can be 7 or 4 bytes long + if(flags & FLAG_4B_UID_IN_DATA) + { + // 4B uid comes from data-portion of packet + memcpy(rUIDBCC1,datain,4); rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - } else { // ---------- 7BUID - rATQA[0] = 0x44; + }else if(flags & FLAG_7B_UID_IN_DATA) + { + // 7B uid comes from data-portion of packet + memcpy(&rUIDBCC1[1],datain,3); + memcpy(rUIDBCC2, datain+3, 4); + _7BUID = true; + } + else + { + // get UID from emul memory + emlGetMemBt(receivedCmd, 7, 1); + _7BUID = !(receivedCmd[0] == 0x00); + if (!_7BUID) { // ---------- 4BUID + emlGetMemBt(rUIDBCC1, 0, 4); + } else { // ---------- 7BUID + emlGetMemBt(&rUIDBCC1[1], 0, 3); + emlGetMemBt(rUIDBCC2, 3, 4); + } + } + /* + * Regardless of what method was used to set the UID, set fifth byte and modify + * the ATQA for 4 or 7-byte UID + */ + + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + if(_7BUID) + { + rATQA[0] = 0x44; rUIDBCC1[0] = 0x88; - emlGetMemBt(&rUIDBCC1[1], 0, 3); - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - emlGetMemBt(rUIDBCC2, 3, 4); rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; } -// -------------------------------------- test area - -// -------------------------------------- END test area // start mkseconds counter StartCountUS(); @@ -2193,19 +2220,23 @@ void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); SpinDelay(200); - if (MF_DBGLEVEL >= 1) Dbprintf("Started. 7buid=%d", _7BUID); + if (MF_DBGLEVEL >= 1) { + if (!_7BUID) { + Dbprintf("4B UID: %02x%02x%02x%02x",rUIDBCC1[0] , rUIDBCC1[1] , rUIDBCC1[2] , rUIDBCC1[3]); + }else + { + Dbprintf("7B UID: (%02x)%02x%02x%02x%02x%02x%02x%02x",rUIDBCC1[0] , rUIDBCC1[1] , rUIDBCC1[2] , rUIDBCC1[3],rUIDBCC2[0],rUIDBCC2[1] ,rUIDBCC2[2] , rUIDBCC2[3]); + } + } // calibrate mkseconds counter GetDeltaCountUS(); - while (true) { + bool finished = false; + while (!BUTTON_PRESS() && !finished) { WDT_HIT(); - if(BUTTON_PRESS()) { - break; - } - // find reader field // Vref = 3300mV, and an 10:1 voltage divider on the input // can measure voltages up to 33000 mV @@ -2216,56 +2247,54 @@ void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_A_ON(); } } + if(cardSTATE == MFEMUL_NOFIELD) continue; - if (cardSTATE != MFEMUL_NOFIELD) { - res = EmGetCmd(receivedCmd, &len, RECV_CMD_SIZE); // (+ nextCycleTimeout) - if (res == 2) { - cardSTATE = MFEMUL_NOFIELD; - LEDsoff(); - continue; - } - if(res) break; - } - - //nextCycleTimeout = 0; - -// if (len) Dbprintf("len:%d cmd: %02x %02x %02x %02x", len, receivedCmd[0], receivedCmd[1], receivedCmd[2], receivedCmd[3]); - - if (len != 4 && cardSTATE != MFEMUL_NOFIELD) { // len != 4 <---- speed up the code 4 authentication - // REQ or WUP request in ANY state and WUP in HALTED state - if (len == 1 && ((receivedCmd[0] == 0x26 && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == 0x52)) { - selTimer = GetTickCount(); - EmSendCmdEx(rATQA, sizeof(rATQA), (receivedCmd[0] == 0x52)); - cardSTATE = MFEMUL_SELECT1; - - // init crypto block - LED_B_OFF(); - LED_C_OFF(); - crypto1_destroy(pcs); - cardAUTHKEY = 0xff; - } + //Now, get data + + res = EmGetCmd(receivedCmd, &len, RECV_CMD_SIZE); // (+ nextCycleTimeout) + if (res == 2) { //Field is off! + cardSTATE = MFEMUL_NOFIELD; + LEDsoff(); + continue; + }else if(res == 1) break;//return value 1 means button press + + + // REQ or WUP request in ANY state and WUP in HALTED state + if (len == 1 && ((receivedCmd[0] == 0x26 && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == 0x52)) { + selTimer = GetTickCount(); + EmSendCmdEx(rATQA, sizeof(rATQA), (receivedCmd[0] == 0x52)); + cardSTATE = MFEMUL_SELECT1; + + // init crypto block + LED_B_OFF(); + LED_C_OFF(); + crypto1_destroy(pcs); + cardAUTHKEY = 0xff; + continue; } - + switch (cardSTATE) { - case MFEMUL_NOFIELD:{ - break; - } - case MFEMUL_HALTED:{ - break; - } + case MFEMUL_NOFIELD: + case MFEMUL_HALTED: case MFEMUL_IDLE:{ break; } case MFEMUL_SELECT1:{ // select all if (len == 2 && (receivedCmd[0] == 0x93 && receivedCmd[1] == 0x20)) { + if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL received"); EmSendCmd(rUIDBCC1, sizeof(rUIDBCC1)); break; } + if (MF_DBGLEVEL >= 4 && len == 9 && receivedCmd[0] == 0x93 && receivedCmd[1] == 0x70 ) + { + Dbprintf("SELECT %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); + } // select card if (len == 9 && (receivedCmd[0] == 0x93 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC1, 4) == 0)) { + if (!_7BUID) EmSendCmd(rSAK, sizeof(rSAK)); else @@ -2285,6 +2314,51 @@ void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) break; } + case MFEMUL_AUTH1:{ + if( len != 8) + { + cardSTATE_TO_IDLE(); + break; + } + uint32_t ar = bytes_to_num(receivedCmd, 4); + uint32_t nr= bytes_to_num(&receivedCmd[4], 4); + + //Collect AR/NR + if(ar_nr_collected < 2){ + if(ar_nr_responses[ar_nr_collected*4+2] != ar) + {// Avoid duplicates + ar_nr_collected++; + ar_nr_responses[ar_nr_collected*4] = cuid; + ar_nr_responses[ar_nr_collected*4+1] = nonce; + ar_nr_responses[ar_nr_collected*4+2] = ar; + ar_nr_responses[ar_nr_collected*4+3] = nr; + } + } + + // --- crypto + crypto1_word(pcs, ar , 1); + cardRr = nr ^ crypto1_word(pcs, 0, 0); + + // test if auth OK + if (cardRr != prng_successor(nonce, 64)){ + if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED. cardRr=%08x, succ=%08x",cardRr, prng_successor(nonce, 64)); + //Shouldn't we respond anything here? + // Right now, we don't nack or anything, which causes the + // reader to do a WUPA after a while. /Martin + cardSTATE_TO_IDLE(); + break; + } + + ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); + + num_to_bytes(ans, 4, rAUTH_AT); + // --- crypto + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); + LED_C_ON(); + cardSTATE = MFEMUL_WORK; + if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED. sector=%d, key=%d time=%d", cardAUTHSC, cardAUTHKEY, GetTickCount() - authTimer); + break; + } case MFEMUL_SELECT2:{ if (!len) break; @@ -2308,86 +2382,44 @@ void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) // i guess there is a command). go into the work state. if (len != 4) break; cardSTATE = MFEMUL_WORK; - goto lbWORK; - } - case MFEMUL_AUTH1:{ - if (len == 8) { - // --- crypto - rn_enc = bytes_to_num(receivedCmd, 4); - crypto1_word(pcs, rn_enc , 1); - cardRr = bytes_to_num(&receivedCmd[4], 4) ^ crypto1_word(pcs, 0, 0); - // test if auth OK - if (cardRr != prng_successor(nonce, 64)){ - if (MF_DBGLEVEL >= 4) Dbprintf("AUTH FAILED. cardRr=%08x, succ=%08x", cardRr, prng_successor(nonce, 64)); - cardSTATE_TO_IDLE(); - break; - } - ans = prng_successor(nonce, 96) ^ crypto1_word(pcs, 0, 0); - num_to_bytes(ans, 4, rAUTH_AT); - // --- crypto - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - cardSTATE = MFEMUL_AUTH2; - } else { - cardSTATE_TO_IDLE(); - } - if (cardSTATE != MFEMUL_AUTH2) break; - } - case MFEMUL_AUTH2:{ - LED_C_ON(); - cardSTATE = MFEMUL_WORK; - if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED. sec=%d, key=%d time=%d", cardAUTHSC, cardAUTHKEY, GetTickCount() - authTimer); - break; + //goto lbWORK; + //intentional fall-through to the next case-stmt } case MFEMUL_WORK:{ -lbWORK: if (len == 0) break; - - if (cardAUTHKEY == 0xff) { - // first authentication - if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) { - authTimer = GetTickCount(); - - cardAUTHSC = receivedCmd[1] / 4; // received block num - cardAUTHKEY = receivedCmd[0] - 0x60; - - // --- crypto - crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); - num_to_bytes(nonce, 4, rAUTH_AT); - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - // --- crypto - -// last working revision -// EmSendCmd14443aRaw(resp1, resp1Len, 0); -// LogTrace(NULL, 0, GetDeltaCountUS(), 0, true); + if (len == 0) break; - cardSTATE = MFEMUL_AUTH1; - //nextCycleTimeout = 10; - break; - } - } else { + bool encrypted_data = (cardAUTHKEY != 0xFF) ; + + if(encrypted_data) + { // decrypt seqence mf_crypto1_decrypt(pcs, receivedCmd, len); - - // nested authentication - if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) { - authTimer = GetTickCount(); + } - cardAUTHSC = receivedCmd[1] / 4; // received block num - cardAUTHKEY = receivedCmd[0] - 0x60; + if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) { + authTimer = GetTickCount(); + cardAUTHSC = receivedCmd[1] / 4; // received block num + cardAUTHKEY = receivedCmd[0] - 0x60; + crypto1_destroy(pcs);//Added by martin + crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - // --- crypto - crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); - num_to_bytes(ans, 4, rAUTH_AT); - EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - // --- crypto + if (!encrypted_data) { // first authentication + if (MF_DBGLEVEL >= 2) Dbprintf("Reader authenticating for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); - cardSTATE = MFEMUL_AUTH1; - //nextCycleTimeout = 10; - break; + crypto1_word(pcs, cuid ^ nonce, 0);//Update crypto state + num_to_bytes(nonce, 4, rAUTH_AT); // Send nonce + } + else{ // nested authentication + if (MF_DBGLEVEL >= 2) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); + ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); + num_to_bytes(ans, 4, rAUTH_AT); } + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); + //Dbprintf("Sending rAUTH %02x%02x%02x%02x", rAUTH_AT[0],rAUTH_AT[1],rAUTH_AT[2],rAUTH_AT[3]); + cardSTATE = MFEMUL_AUTH1; + break; } - + // rule 13 of 7.5.3. in ISO 14443-4. chaining shall be continued // BUT... ACK --> NACK if (len == 1 && receivedCmd[0] == CARD_ACK) { @@ -2401,39 +2433,63 @@ lbWORK: if (len == 0) break; break; } - // read block - if (len == 4 && receivedCmd[0] == 0x30) { - if (receivedCmd[1] >= 16 * 4 || receivedCmd[1] / 4 != cardAUTHSC) { + if(len != 4) break; + + if(receivedCmd[0] == 0x30 // read block + || receivedCmd[0] == 0xA0 // write block + || receivedCmd[0] == 0xC0 + || receivedCmd[0] == 0xC1 + || receivedCmd[0] == 0xC2 // inc dec restore + || receivedCmd[0] == 0xB0) // transfer + { + if (receivedCmd[1] >= 16 * 4) + { + + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); + break; + } + + if (receivedCmd[1] / 4 != cardAUTHSC) + { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd[0],receivedCmd[1],cardAUTHSC); break; } + } + // read block + if (receivedCmd[0] == 0x30) { + if (MF_DBGLEVEL >= 2) { + Dbprintf("Reader reading block %d (0x%02x)",receivedCmd[1],receivedCmd[1]); + } emlGetMem(response, receivedCmd[1], 1); AppendCrc14443a(response, 16); mf_crypto1_encrypt(pcs, response, 18, &par); EmSendCmdPar(response, 18, par); + numReads++; + if(exitAfterNReads > 0 && numReads == exitAfterNReads) + { + Dbprintf("%d reads done, exiting", numReads); + finished = true; + } break; } - // write block - if (len == 4 && receivedCmd[0] == 0xA0) { - if (receivedCmd[1] >= 16 * 4 || receivedCmd[1] / 4 != cardAUTHSC) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } + if (receivedCmd[0] == 0xA0) { + if (MF_DBGLEVEL >= 2) Dbprintf("RECV 0xA0 write block %d (%02x)",receivedCmd[1],receivedCmd[1]); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); //nextCycleTimeout = 50; cardSTATE = MFEMUL_WRITEBL2; cardWRBL = receivedCmd[1]; break; - } - - // works with cardINTREG - + } // increment, decrement, restore - if (len == 4 && (receivedCmd[0] == 0xC0 || receivedCmd[0] == 0xC1 || receivedCmd[0] == 0xC2)) { - if (receivedCmd[1] >= 16 * 4 || - receivedCmd[1] / 4 != cardAUTHSC || - emlCheckValBl(receivedCmd[1])) { + if (receivedCmd[0] == 0xC0 || receivedCmd[0] == 0xC1 || receivedCmd[0] == 0xC2) { + if (MF_DBGLEVEL >= 2) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); + + if (emlCheckValBl(receivedCmd[1])) { + if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); break; } @@ -2445,17 +2501,13 @@ lbWORK: if (len == 0) break; if (receivedCmd[0] == 0xC2) cardSTATE = MFEMUL_INTREG_REST; cardWRBL = receivedCmd[1]; - + break; } - // transfer - if (len == 4 && receivedCmd[0] == 0xB0) { - if (receivedCmd[1] >= 16 * 4 || receivedCmd[1] / 4 != cardAUTHSC) { - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - break; - } + if (receivedCmd[0] == 0xB0) { + if (MF_DBGLEVEL >= 2) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd[1])) EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); @@ -2466,20 +2518,23 @@ lbWORK: if (len == 0) break; } // halt - if (len == 4 && (receivedCmd[0] == 0x50 && receivedCmd[1] == 0x00)) { + if (receivedCmd[0] == 0x50 && receivedCmd[1] == 0x00) { LED_B_OFF(); LED_C_OFF(); cardSTATE = MFEMUL_HALTED; if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED. Selected time: %d ms", GetTickCount() - selTimer); break; } - - // command not allowed - if (len == 4) { + // RATS + if (receivedCmd[0] == 0xe0) {//RATS EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); break; } + // command not allowed + if (MF_DBGLEVEL >= 4) Dbprintf("Received command not allowed, nacking"); + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + // case break break; } @@ -2541,10 +2596,43 @@ lbWORK: if (len == 0) break; // add trace trailer memset(rAUTH_NT, 0x44, 4); LogTrace(rAUTH_NT, 4, 0, 0, TRUE); - + if(flags & FLAG_INTERACTIVE)// Interactive mode flag, means we need to send ACK + { + //May just aswell send the collected ar_nr in the response aswell + cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,0,0,&ar_nr_responses,ar_nr_collected*4*4); + } + if(flags & FLAG_NR_AR_ATTACK) + { + if(ar_nr_collected > 1) + { + Dbprintf("Collected two pairs of AR/NR which can be used to extract keys from reader:"); + Dbprintf("../tools/mfcrack32 %08x %08x %08x %08x", + ar_nr_responses[0], // UID + ar_nr_responses[1], //NT + ar_nr_responses[2], //AR1 + ar_nr_responses[3], //NR1 + ar_nr_responses[6], //AR2 + ar_nr_responses[7] //NR2 + ); + }else + { + Dbprintf("Failed to obtain two AR/NR pairs!"); + if(ar_nr_collected >0) + { + Dbprintf("Only got these: UID=%08d, nonce=%08d, AR1=%08d, NR1=%08d", + ar_nr_responses[0], // UID + ar_nr_responses[1], //NT + ar_nr_responses[2], //AR1 + ar_nr_responses[3] //NR1 + ); + } + } + } if (MF_DBGLEVEL >= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, traceLen); } + + //----------------------------------------------------------------------------- // MIFARE sniffer. // diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 86fbe8df..637ef4a1 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -137,86 +137,86 @@ int CmdHF14AMfWrBl(const char *Cmd) PrintAndLog("Command execute timeout"); } - 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 "); - 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; + 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 "); + 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}; @@ -259,135 +259,135 @@ int CmdHF14AMfRdBl(const char *Cmd) PrintAndLog("Command execute timeout"); } - return 0; -} - -int CmdHF14AMfURdBl(const char *Cmd) -{ - uint8_t blockNo = 0; - - if (strlen(Cmd)<1) { - PrintAndLog("Usage: hf mf urdbl "); - 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; + return 0; +} + +int CmdHF14AMfURdBl(const char *Cmd) +{ + uint8_t blockNo = 0; + + if (strlen(Cmd)<1) { + PrintAndLog("Usage: hf mf urdbl "); + 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}; @@ -1144,24 +1144,68 @@ int CmdHF14AMfChk(const char *Cmd) int CmdHF14AMf1kSim(const char *Cmd) { - uint8_t uid[4] = {0, 0, 0, 0}; - + uint8_t uid[7] = {0, 0, 0, 0,0,0,0}; + uint8_t exitAfterNReads = 0; + uint8_t flags = 0; + if (param_getchar(Cmd, 0) == 'h') { - PrintAndLog("Usage: hf mf sim "); + PrintAndLog("Usage: hf mf sim u n i x"); + PrintAndLog(" u (Optional) UID. If not specified, the UID from emulator memory will be used"); + PrintAndLog(" n (Optional) Automatically exit simulation after blocks have been read by reader. 0 = infinite"); + PrintAndLog(" i (Optional) Interactive, means that console will not be returned until simulation finishes or is aborted"); + PrintAndLog(" x (Optional) Crack, performs the 'reader attack', nr/ar attack against a legitimate reader, fishes out the key(s)"); PrintAndLog(" sample: hf mf sim 0a0a0a0a "); return 0; - } - - if (param_getchar(Cmd, 0) && param_gethex(Cmd, 0, uid, 8)) { - PrintAndLog("UID must include 8 HEX symbols"); - return 1; } - PrintAndLog(" uid:%s ", sprint_hex(uid, 4)); - - UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {0, 0, 0}}; - memcpy(c.d.asBytes, uid, 4); + uint8_t pnr = 0; + if (param_getchar(Cmd, pnr) == 'u') { + if(param_gethex(Cmd, pnr+1, uid, 8) == 0) + { + flags |=FLAG_4B_UID_IN_DATA; // UID from packet + }else if(param_gethex(Cmd,pnr+1,uid,14) == 0) + { + flags |= FLAG_7B_UID_IN_DATA;// UID from packet + }else + { + PrintAndLog("UID, if specified, must include 8 or 14 HEX symbols"); + return 1; + } + pnr +=2; + } + if (param_getchar(Cmd, pnr) == 'n') { + exitAfterNReads = param_get8(Cmd,pnr+1); + pnr += 2; + } + if (param_getchar(Cmd, pnr) == 'i' ) { + //Using a flag to signal interactiveness, least significant bit + flags |= FLAG_INTERACTIVE; + pnr++; + } + + if (param_getchar(Cmd, pnr) == 'x' ) { + //Using a flag to signal interactiveness, least significant bit + flags |= FLAG_NR_AR_ATTACK; + } + PrintAndLog(" uid:%s, numreads:%d, flags:%d (0x%02x) ", + flags & FLAG_4B_UID_IN_DATA ? sprint_hex(uid,4): + flags & FLAG_7B_UID_IN_DATA ? sprint_hex(uid,7): "N/A" + , exitAfterNReads, flags,flags); + + + UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads,0}}; + memcpy(c.d.asBytes, uid, sizeof(uid)); SendCommand(&c); + if(flags & 1) + { + UsbCommand resp; + PrintAndLog("Press pm3-button to abort simulation"); + while(! WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + //We're waiting only 1.5 s at a time, otherwise we get the + // annoying message about "Waiting for a response... " + } + } + return 0; } @@ -1291,7 +1335,11 @@ int CmdHF14AMfELoad(const char *Cmd) while(!feof(f)){ memset(buf, 0, sizeof(buf)); if (fgets(buf, sizeof(buf), f) == NULL) { - PrintAndLog("File reading error."); + if(blockNum == 16 * 4) + { + break; + } + PrintAndLog("File reading error."); return 2; } @@ -1319,7 +1367,7 @@ int CmdHF14AMfELoad(const char *Cmd) PrintAndLog("File content error. There must be 64 blocks"); return 4; } - PrintAndLog("Loaded from file: %s", filename); + PrintAndLog("Loaded %d blocks from file: %s", blockNum, filename); return 0; } @@ -1864,15 +1912,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"}, - {"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"}, + {"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 - "}, diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 2673dcac..a107e392 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -151,6 +151,15 @@ typedef struct { #define CMD_UNKNOWN 0xFFFF + +//Mifare simulation flags +#define FLAG_INTERACTIVE 0x01 +#define FLAG_4B_UID_IN_DATA 0x02 +#define FLAG_7B_UID_IN_DATA 0x04 +#define FLAG_NR_AR_ATTACK 0x08 + + + // CMD_DEVICE_INFO response packet has flags in arg[0], flag definitions: /* Whether a bootloader that understands the common_area is present */ #define DEVICE_INFO_FLAG_BOOTROM_PRESENT (1<<0) -- 2.39.2