From a25d5c1cdebbd9a0840620e9307ecc8a254c2315 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 31 Oct 2014 09:26:35 +0100 Subject: [PATCH 01/16] test: hf 15 sim.. --- armsrc/iso15693.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 884ed976..42cb0ab8 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -837,26 +837,27 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // Now the VICC>VCD responses when we are simulating a tag static void BuildInventoryResponse( uint8_t *uid) { - uint8_t cmd[12]; + uint8_t cmd[13]; uint16_t crc; // one sub-carrier, inventory, 1 slot, fast rate // AFI is at bit 5 (1<<4) when doing an INVENTORY - cmd[0] = 0; //(1 << 2) | (1 << 5) | (1 << 1); - cmd[1] = 0; + cmd[0] = 0x0d; // COM LEN? Data 8 + 4 //(1 << 2) | (1 << 5) | (1 << 1); + cmd[1] = 0; // com_Adr + cmd[2] = 0; // status 00 = success // 64-bit UID - cmd[2] = uid[7]; //0x32; - cmd[3] = uid[6]; //0x4b; - cmd[4] = uid[5]; //0x03; - cmd[5] = uid[4]; //0x01; - cmd[6] = uid[3]; //0x00; - cmd[7] = uid[2]; //0x10; - cmd[8] = uid[1]; //0x05; - cmd[9] = uid[0]; //0xe0; + cmd[3] = uid[7]; //0x32; + cmd[4] = uid[6]; //0x4b; + cmd[5] = uid[5]; //0x03; + cmd[6] = uid[4]; //0x01; + cmd[7] = uid[3]; //0x00; + cmd[8] = uid[2]; //0x10; + cmd[9] = uid[1]; //0x05; + cmd[10] = uid[0]; //0xe0; //Now the CRC crc = Crc(cmd, 10); - cmd[10] = crc & 0xff; - cmd[11] = crc >> 8; + cmd[11] = crc & 0xff; + cmd[12] = crc >> 8; CodeIso15693AsReader(cmd, sizeof(cmd)); } -- 2.39.5 From c15d2bdc9b25da89931c767902639fa7e8c9b764 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 3 Nov 2014 13:49:19 +0100 Subject: [PATCH 02/16] ADD: added identification for Mifare TNP3xxx tags. ADD: MD5-lua functionality ADD: AES 128 decrypt lua functionality ADD: test luc script for reading TNP3xxx tags CHG: testing some changes for "hf 14b sim" / "lf em4x 410xsim" --- armsrc/iso15693.c | 57 ++--- armsrc/lfops.c | 59 +++-- client/cmddata.c | 9 +- client/cmdhf14a.c | 1 + client/cmdlfem4x.c | 275 ++++++++++++++++++++- client/cmdlfem4x.h | 1 + client/graph.c | 3 + client/lualibs/htmlskel.lua | 1 + client/lualibs/md5.lua | 384 +++++++++++++++++++++++++++++ client/lualibs/mf_default_keys.lua | 8 +- client/lualibs/read14a.lua | 1 + client/scripting.c | 44 +++- client/scripts/mifare_autopwn.lua | 2 + client/scripts/tnp3.lua | 189 ++++++++++++++ include/at91sam7s512.h | 2 +- 15 files changed, 971 insertions(+), 65 deletions(-) create mode 100644 client/lualibs/md5.lua create mode 100644 client/scripts/tnp3.lua diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 42cb0ab8..11a49902 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -463,28 +463,15 @@ static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int AT91C_BASE_SSC->SSC_THR = 0x43; } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; + int8_t b = (int8_t)AT91C_BASE_SSC->SSC_RHR; // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates, so every other sample is I, // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. - if(getNext) { - int8_t r; - - if(b < 0) { - r = -b; - } else { - r = b; - } - if(prev < 0) { - r -= prev; - } else { - r += prev; - } + if (getNext) { - dest[c++] = (uint8_t)r; + dest[c++] = abs(b) + abs(prev); if(c >= 20000) { break; @@ -837,27 +824,27 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // Now the VICC>VCD responses when we are simulating a tag static void BuildInventoryResponse( uint8_t *uid) { - uint8_t cmd[13]; + uint8_t cmd[12]; uint16_t crc; // one sub-carrier, inventory, 1 slot, fast rate // AFI is at bit 5 (1<<4) when doing an INVENTORY - cmd[0] = 0x0d; // COM LEN? Data 8 + 4 //(1 << 2) | (1 << 5) | (1 << 1); - cmd[1] = 0; // com_Adr - cmd[2] = 0; // status 00 = success + //(1 << 2) | (1 << 5) | (1 << 1); + cmd[0] = 0; // + cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported // 64-bit UID - cmd[3] = uid[7]; //0x32; - cmd[4] = uid[6]; //0x4b; - cmd[5] = uid[5]; //0x03; - cmd[6] = uid[4]; //0x01; - cmd[7] = uid[3]; //0x00; - cmd[8] = uid[2]; //0x10; - cmd[9] = uid[1]; //0x05; - cmd[10] = uid[0]; //0xe0; + cmd[2] = uid[7]; //0x32; + cmd[3] = uid[6]; //0x4b; + cmd[4] = uid[5]; //0x03; + cmd[5] = uid[4]; //0x01; + cmd[6] = uid[3]; //0x00; + cmd[7] = uid[2]; //0x10; + cmd[8] = uid[1]; //0x05; + cmd[9] = uid[0]; //0xe0; //Now the CRC crc = Crc(cmd, 10); - cmd[11] = crc & 0xff; - cmd[12] = crc >> 8; + cmd[10] = crc & 0xff; + cmd[11] = crc >> 8; CodeIso15693AsReader(cmd, sizeof(cmd)); } @@ -1124,9 +1111,6 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) memset(buf, 0x00, 100); - // Inventory response - BuildInventoryResponse(uid); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1149,6 +1133,9 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) { // Build a suitable reponse to the reader INVENTORY cocmmand // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. + + BuildInventoryResponse(uid); + TransmitTo15693Reader(ToSend, ToSendMax, &tsamples, &wait); } @@ -1156,6 +1143,10 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); + Dbprintf("Simulationg uid: %x %x %x %x %x %x %x %x", + uid[0], uid[1], uid[2], uid[3], + uid[4], uid[5], uid[6], uid[7]); + LED_A_OFF(); LED_B_OFF(); LED_C_OFF(); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index c80caf77..dc5efe68 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -456,21 +456,30 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - + //FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); + + // Connect the A/D to the peak-detected low-frequency path. + //SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + // Configure output and enable pin that is connected to the FPGA (for modulating) AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; // (PIO_PER) PIO Enable Register , + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; // (PIO_OER) Output Enable Register + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; // (PIO_ODR) Output Disable Register // Give it a bit of time for the resonant antenna to settle. - SpinDelay(30); + SpinDelay(150); + + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low + + while(!BUTTON_PRESS()) { + WDT_HIT(); - for(;;) { - - while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { + // PIO_PDSR = Pin Data Status Register + // GPIO_SSC_CLK = SSC Transmit Clock + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { // wait for ssp_clk to go high if(BUTTON_PRESS()) { DbpString("Stopped at 0"); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off @@ -479,12 +488,21 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) WDT_HIT(); } - if ( buff[i] ) - OPEN_COIL(); - else - SHORT_COIL(); - - while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { + // PIO_CODR = Clear Output Data Register + // PIO_SODR = Set Output Data Register + //#define LOW(x) AT91C_BASE_PIOA->PIO_CODR = (x) + //#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR = (x) + + if ( buff[i] > 0 ){ + HIGH(GPIO_SSC_DOUT); + //FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + //FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); + } else { + LOW(GPIO_SSC_DOUT); + //FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + } + + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { // wait for ssp_clk to go low if(BUTTON_PRESS()) { DbpString("Stopped at 1"); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off @@ -492,18 +510,23 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) } WDT_HIT(); } - + + //SpinDelayUs(512); + ++i; if(i == period) { i = 0; if (gap) { // turn of modulation - SHORT_COIL(); + LOW(GPIO_SSC_DOUT); // wait SpinDelay(gap); } } } + DbpString("Stopped"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + return; } #define DEBUG_FRAME_CONTENTS 1 diff --git a/client/cmddata.c b/client/cmddata.c index b01b45ba..1df3486d 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -21,6 +21,7 @@ #include "cmdmain.h" #include "cmddata.h" + static int CmdHelp(const char *Cmd); int CmdAmp(const char *Cmd) @@ -670,7 +671,9 @@ int CmdManchesterDemod(const char *Cmd) // At this stage, we now have a bitstream of "01" ("1") or "10" ("0"), parse it into final decoded bitstream // Actually, we overwrite BitStream with the new decoded bitstream, we just need to be careful // to stop output at the final bitidx2 value, not bitidx - for (i = 0; i < bitidx; i += 2) { + + //http://www.proxmark.org/forum/viewtopic.php?id=403 + for (i = 1; i < bitidx; i += 2) { if ((BitStream[i] == 0) && (BitStream[i+1] == 1)) { BitStream[bit2idx++] = 1 ^ invert; } else if ((BitStream[i] == 1) && (BitStream[i+1] == 0)) { @@ -713,7 +716,7 @@ int CmdManchesterDemod(const char *Cmd) BitStream[i+14], BitStream[i+15]); } - return 0; + return bit2idx; } /* Modulate our data into manchester */ @@ -884,7 +887,7 @@ static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, {"amp", CmdAmp, 1, "Amplify peaks"}, - {"askdemod", Cmdaskdemod, 1, "<0 or 1> -- Attempt to demodulate simple ASK tags"}, + {"askdemod", Cmdaskdemod, 1, "<0|1> -- Attempt to demodulate simple ASK tags"}, {"autocorr", CmdAutoCorr, 1, " -- Autocorrelation over window"}, {"bitsamples", CmdBitsamples, 0, "Get raw samples as bitstring"}, {"bitstream", CmdBitstream, 1, "[clock rate] -- Convert waveform into a bitstream"}, diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index bd19cee4..0f2b5222 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -202,6 +202,7 @@ int CmdHF14AReader(const char *Cmd) switch (card->sak) { case 0x00: PrintAndLog("TYPE : NXP MIFARE Ultralight | Ultralight C"); break; + case 0x01: PrintAndLog("TYPE : NXP TNP3xxx Activision Game Appliance"); break; case 0x04: PrintAndLog("TYPE : NXP MIFARE (various !DESFire !DESFire EV1)"); break; case 0x08: PrintAndLog("TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1"); break; case 0x09: PrintAndLog("TYPE : NXP MIFARE Mini 0.3k"); break; diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index 67013b2e..07f909ac 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -127,7 +127,7 @@ retest: PrintAndLog("Thought we had a valid tag but failed at word %d (i=%d)", rows + 1, i); /* Start back rows * 5 + 9 header bits, -1 to not start at same place */ - i -= 9 + (5 * rows) -2; + i -= 9 + (5 * rows) -5; rows = header = 0; } @@ -220,11 +220,11 @@ int CmdEM410xSim(const char *Cmd) int clock = 64; /* clear our graph */ - ClearGraph(1); + ClearGraph(0); /* write it out a few times */ - for (h = 0; h < 4; h++) - { + //for (h = 0; h < 4; h++) + //{ /* write 9 start bits */ for (i = 0; i < 9; i++) AppendGraph(0, clock, 1); @@ -262,10 +262,10 @@ int CmdEM410xSim(const char *Cmd) /* stop bit */ AppendGraph(0, clock, 0); - } + //} /* modulate that biatch */ - CmdManchesterMod("64"); + //CmdManchesterMod("64"); /* booyah! */ RepaintGraphWindow(); @@ -295,7 +295,7 @@ int CmdEM410xWatch(const char *Cmd) } CmdLFRead(read_h ? "h" : ""); - CmdSamples("16000"); + CmdSamples("6000"); } while ( !CmdEM410xRead("") @@ -654,8 +654,10 @@ int CmdWriteWordPWD(const char *Cmd) static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, + {"410xread", CmdEM410xRead, 1, "[clock rate] -- Extract ID from EM410x tag"}, {"410xsim", CmdEM410xSim, 0, " -- Simulate EM410x tag"}, + {"replay", MWRem4xReplay, 0, "Watches for tag and simulates manchester encoded em4x tag"}, {"410xwatch", CmdEM410xWatch, 0, "['h'] -- Watches for EM410x 125/134 kHz tags (option 'h' for 134)"}, {"410xspoof", CmdEM410xWatchnSpoof, 0, "['h'] --- Watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" }, {"410xwrite", CmdEM410xWrite, 1, " <'0' T5555> <'1' T55x7> [clock rate] -- Write EM410x UID to T5555(Q5) or T55x7 tag, optionally setting clock rate"}, @@ -667,6 +669,265 @@ static command_t CommandTable[] = {NULL, NULL, 0, NULL} }; + +//Confirms the parity of a bitstream as well as obtaining the data (TagID) from within the appropriate memory space. +//Arguments: +// Pointer to a string containing the desired bitsream +// Pointer to a string that will receive the decoded tag ID +// Length of the bitsream pointed at in the first argument, char* _strBitStream +//Retuns: +//1 Parity confirmed +//0 Parity not confirmed +int ConfirmEm410xTagParity( char* _strBitStream, char* pID, int LengthOfBitstream ) +{ + int i = 0; + int rows = 0; + int Parity[4] = {0x00}; + char ID[11] = {0x00}; + int k = 0; + int BitStream[70] = {0x00}; + int counter = 0; + //prepare variables + for ( i = 0; i <= LengthOfBitstream; i++) + { + if (_strBitStream[i] == '1') + { + k =1; + memcpy(&BitStream[i], &k,4); + } + else if (_strBitStream[i] == '0') + { + k = 0; + memcpy(&BitStream[i], &k,4); + } + } + while ( counter < 2 ) + { + //set/reset variables and counters + memset(ID,0x00,sizeof(ID)); + memset(Parity,0x00,sizeof(Parity)); + rows = 0; + for ( i = 9; i <= LengthOfBitstream; i++) + { + if ( rows < 10 ) + { + if ((BitStream[i] ^ BitStream[i+1] ^ BitStream[i+2] ^ BitStream[i+3]) == BitStream[i+4]) + { + sprintf(ID+rows, "%x", (8 * BitStream[i]) + (4 * BitStream[i+1]) + (2 * BitStream[i+2]) + (1 * BitStream[i+3])); + rows++; + /* Keep parity info and move four bits ahead*/ + Parity[0] ^= BitStream[i]; + Parity[1] ^= BitStream[i+1]; + Parity[2] ^= BitStream[i+2]; + Parity[3] ^= BitStream[i+3]; + i += 4; + } + } + if ( rows == 10 ) + { + if ( BitStream[i] == Parity[0] && BitStream[i+1] == Parity[1] && + BitStream[i+2] == Parity[2] && BitStream[i+3] == Parity[3] && + BitStream[i+4] == 0) + { + memcpy(pID,ID,strlen(ID)); + return 1; + } + } + } + printf("[PARITY ->]Failed. Flipping Bits, and rechecking parity for bitstream:\n[PARITY ->]"); + for (k = 0; k < LengthOfBitstream; k++) + { + BitStream[k] ^= 1; + printf("%i", BitStream[k]); + } + puts(" "); + counter++; + } + return 0; +} +//Reads and demodulates an em410x RFID tag. It further allows slight modification to the decoded bitstream +//Once a suitable bitstream has been identified, and if needed, modified, it is replayed. Allowing emulation of the +//"stolen" rfid tag. +//No meaningful returns or arguments. +int MWRem4xReplay(const char* Cmd) +{ + // //header traces + // static char ArrayTraceZero[] = { '0','0','0','0','0','0','0','0','0' }; + // static char ArrayTraceOne[] = { '1','1','1','1','1','1','1','1','1' }; + // //local string variables + // char strClockRate[10] = {0x00}; + // char strAnswer[4] = {0x00}; + // char strTempBufferMini[2] = {0x00}; + // //our outbound bit-stream + // char strSimulateBitStream[65] = {0x00}; + // //integers + // int iClockRate = 0; + // int needle = 0; + // int j = 0; + // int iFirstHeaderOffset = 0x00000000; + // int numManchesterDemodBits=0; + // //boolean values + // bool bInverted = false; + // //pointers to strings. memory will be allocated. + // char* pstrInvertBitStream = 0x00000000; + // char* pTempBuffer = 0x00000000; + // char* pID = 0x00000000; + // char* strBitStreamBuffer = 0x00000000; + + + // puts("###################################"); + // puts("#### Em4x Replay ##"); + // puts("#### R.A.M. June 2013 ##"); + // puts("###################################"); + // //initialize + // CmdLFRead(""); + // //Collect ourselves 10,000 samples + // CmdSamples("10000"); + // puts("[->]preforming ASK demodulation\n"); + // //demodulate ask + // Cmdaskdemod("0"); + // iClockRate = DetectClock(0); + // sprintf(strClockRate, "%i\n",iClockRate); + // printf("[->]Detected ClockRate: %s\n", strClockRate); + + // //If detected clock rate is something completely unreasonable, dont go ahead + // if ( iClockRate < 0xFFFE ) + // { + // pTempBuffer = (char*)malloc(MAX_GRAPH_TRACE_LEN); + // if (pTempBuffer == 0x00000000) + // return 0; + // memset(pTempBuffer,0x00,MAX_GRAPH_TRACE_LEN); + // //Preform manchester de-modulation and display in a single line. + // numManchesterDemodBits = CmdManchesterDemod( strClockRate ); + // //note: numManchesterDemodBits is set above in CmdManchesterDemod() + // if ( numManchesterDemodBits == 0 ) + // return 0; + // strBitStreamBuffer = malloc(numManchesterDemodBits+1); + // if ( strBitStreamBuffer == 0x00000000 ) + // return 0; + // memset(strBitStreamBuffer, 0x00, (numManchesterDemodBits+1)); + // //fill strBitStreamBuffer with demodulated, string formatted bits. + // for ( j = 0; j <= numManchesterDemodBits; j++ ) + // { + // sprintf(strTempBufferMini, "%i",BitStream[j]); + // strcat(strBitStreamBuffer,strTempBufferMini); + // } + // printf("[->]Demodulated Bitstream: \n%s\n", strBitStreamBuffer); + // //Reset counter and select most probable bit stream + // j = 0; + // while ( j < numManchesterDemodBits ) + // { + // memset(strSimulateBitStream,0x00,64); + // //search for header of nine (9) 0's : 000000000 or nine (9) 1's : 1111 1111 1 + // if ( ( strncmp(strBitStreamBuffer+j, ArrayTraceZero, sizeof(ArrayTraceZero)) == 0 ) || + // ( strncmp(strBitStreamBuffer+j, ArrayTraceOne, sizeof(ArrayTraceOne)) == 0 ) ) + // { + // iFirstHeaderOffset = j; + // memcpy(strSimulateBitStream, strBitStreamBuffer+j,64); + // printf("[->]Offset of Header"); + // if ( strncmp(strBitStreamBuffer+iFirstHeaderOffset, "0", 1) == 0 ) + // printf("'%s'", ArrayTraceZero ); + // else + // printf("'%s'", ArrayTraceOne ); + // printf(": %i\nHighlighted string : %s\n",iFirstHeaderOffset,strSimulateBitStream); + // //allow us to escape loop or choose another frame + // puts("[<-]Are we happy with this sample? [Y]es/[N]o"); + // gets(strAnswer); + // if ( ( strncmp(strAnswer,"y",1) == 0 ) || ( strncmp(strAnswer,"Y",1) == 0 ) ) + // { + // j = numManchesterDemodBits+1; + // break; + // } + // } + // j++; + // } + // } + // else return 0; + + // //Do we want the buffer inverted? + // memset(strAnswer, 0x00, sizeof(strAnswer)); + // printf("[<-]Do you wish to invert the highlighted bitstream? [Y]es/[N]o\n"); + // gets(strAnswer); + // if ( ( strncmp("y", strAnswer,1) == 0 ) || ( strncmp("Y", strAnswer, 1 ) == 0 ) ) + // { + // //allocate heap memory + // pstrInvertBitStream = (char*)malloc(numManchesterDemodBits); + // if ( pstrInvertBitStream != 0x00000000 ) + // { + // memset(pstrInvertBitStream,0x00,numManchesterDemodBits); + // bInverted = true; + // //Invert Bitstream + // for ( needle = 0; needle <= numManchesterDemodBits; needle++ ) + // { + // if (strSimulateBitStream[needle] == '0') + // strcat(pstrInvertBitStream,"1"); + // else if (strSimulateBitStream[needle] == '1') + // strcat(pstrInvertBitStream,"0"); + // } + // printf("[->]Inverted bitstream: %s\n", pstrInvertBitStream); + // } + // } + // //Confirm parity of selected string + // pID = (char*)malloc(11); + // if (pID != 0x00000000) + // { + // memset(pID, 0x00, 11); + // if (ConfirmEm410xTagParity(strSimulateBitStream,pID, 64) == 1) + // { + // printf("[->]Parity confirmed for selected bitstream!\n"); + // printf("[->]Tag ID was detected as: [hex]:%s\n",pID ); + // } + // else + // printf("[->]Parity check failed for the selected bitstream!\n"); + // } + + // //Spoof + // memset(strAnswer, 0x00, sizeof(strAnswer)); + // printf("[<-]Do you wish to continue with the EM4x simulation? [Y]es/[N]o\n"); + // gets(strAnswer); + // if ( ( strncmp(strAnswer,"y",1) == 0 ) || ( strncmp(strAnswer,"Y",1) == 0 ) ) + // { + // strcat(pTempBuffer, strClockRate); + // strcat(pTempBuffer, " "); + // if (bInverted == true) + // strcat(pTempBuffer,pstrInvertBitStream); + // if (bInverted == false) + // strcat(pTempBuffer,strSimulateBitStream); + // //inform the user + // puts("[->]Starting simulation now: \n"); + // //Simulate tag with prepared buffer. + // CmdLFSimManchester(pTempBuffer); + // } + // else if ( ( strcmp("n", strAnswer) == 0 ) || ( strcmp("N", strAnswer ) == 0 ) ) + // printf("[->]Exiting procedure now...\n"); + // else + // printf("[->]Erroneous selection\nExiting procedure now....\n"); + + // //Clean up -- Exit function + // //clear memory, then release pointer. + // if ( pstrInvertBitStream != 0x00000000 ) + // { + // memset(pstrInvertBitStream,0x00,numManchesterDemodBits); + // free(pstrInvertBitStream); + // } + // if ( pTempBuffer != 0x00000000 ) + // { + // memset(pTempBuffer,0x00,MAX_GRAPH_TRACE_LEN); + // free(pTempBuffer); + // } + // if ( pID != 0x00000000 ) + // { + // memset(pID,0x00,11); + // free(pID); + // } + // if ( strBitStreamBuffer != 0x00000000 ) + // { + // memset(strBitStreamBuffer,0x00,numManchesterDemodBits); + // free(strBitStreamBuffer); + // } + return 0; +} + int CmdLFEM4X(const char *Cmd) { CmdsParse(CommandTable, Cmd); diff --git a/client/cmdlfem4x.h b/client/cmdlfem4x.h index a209e8f9..587dbf7f 100644 --- a/client/cmdlfem4x.h +++ b/client/cmdlfem4x.h @@ -22,5 +22,6 @@ int CmdReadWord(const char *Cmd); int CmdReadWordPWD(const char *Cmd); int CmdWriteWord(const char *Cmd); int CmdWriteWordPWD(const char *Cmd); +int MWRem4xReplay(const char* Cmd); #endif diff --git a/client/graph.c b/client/graph.c index 4e8cb89c..7b45f3f2 100644 --- a/client/graph.c +++ b/client/graph.c @@ -89,6 +89,9 @@ int DetectClock(int peak) case 1: clock--; break; case 2: clock -= 2; break; } + if ( clock < 32) + clock = 32; + printf("- adjusted it to %d \n", clock); return clock; } diff --git a/client/lualibs/htmlskel.lua b/client/lualibs/htmlskel.lua index a52abdef..b468eb2d 100644 --- a/client/lualibs/htmlskel.lua +++ b/client/lualibs/htmlskel.lua @@ -55,6 +55,7 @@ local skel_1 = [[ return "UNKNOWN" } + add("04,,,Mifare TNP3xxx Activision 1K,0f01,01"); add("04,,,Mifare Mini,0004,09"); add("04,,,Mifare Classic 1k/Mifare Plus(4 byte UID) 2K SL1,0004,08"); add("04,,,Mifare Plus (4 byte UID) 2K SL2,0004,10"); diff --git a/client/lualibs/md5.lua b/client/lualibs/md5.lua new file mode 100644 index 00000000..2390f957 --- /dev/null +++ b/client/lualibs/md5.lua @@ -0,0 +1,384 @@ +local md5 = { + _VERSION = "md5.lua 0.5.0", + _DESCRIPTION = "MD5 computation in Lua (5.1)", + _URL = "https://github.com/kikito/md5.lua", + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ]] +} + +-- bit lib implementions + +local floor, abs, max = math.floor, math.abs, math.max +local char, byte, format, rep, sub = + string.char, string.byte, string.format, string.rep, string.sub + +local function check_int(n) + -- checking not float + if(n - floor(n) > 0) then + error("trying to use bitwise operation on non-integer!") + end +end + +local function tbl2number(tbl) + local n = #tbl + + local rslt = 0 + local power = 1 + for i = 1, n do + rslt = rslt + tbl[i]*power + power = power*2 + end + + return rslt +end + +local function expand(tbl_m, tbl_n) + local big = {} + local small = {} + if(#tbl_m > #tbl_n) then + big = tbl_m + small = tbl_n + else + big = tbl_n + small = tbl_m + end + -- expand small + for i = #small + 1, #big do + small[i] = 0 + end + +end + +local to_bits -- needs to be declared before bit_not + +local function bit_not(n) + local tbl = to_bits(n) + local size = max(#tbl, 32) + for i = 1, size do + if(tbl[i] == 1) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + return tbl2number(tbl) +end + +-- defined as local above +to_bits = function (n) + check_int(n) + if(n < 0) then + -- negative + return to_bits(bit_not(abs(n)) + 1) + end + -- to bits table + local tbl = {} + local cnt = 1 + while (n > 0) do + local last = math.fmod(n,2) + if(last == 1) then + tbl[cnt] = 1 + else + tbl[cnt] = 0 + end + n = (n-last)/2 + cnt = cnt + 1 + end + + return tbl +end + +local function bit_or(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = max(#tbl_m, #tbl_n) + for i = 1, rslt do + if(tbl_m[i]== 0 and tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl2number(tbl) +end + +local function bit_and(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = max(#tbl_m, #tbl_n) + for i = 1, rslt do + if(tbl_m[i]== 0 or tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl2number(tbl) +end + +local function bit_xor(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + local rslt = max(#tbl_m, #tbl_n) + for i = 1, rslt do + if(tbl_m[i] ~= tbl_n[i]) then + tbl[i] = 1 + else + tbl[i] = 0 + end + end + + return tbl2number(tbl) +end + +local function bit_rshift(n, bits) + check_int(n) + + local high_bit = 0 + if(n < 0) then + -- negative + n = bit_not(abs(n)) + 1 + high_bit = 2147483648 -- 0x80000000 + end + + for i=1, bits do + n = n/2 + n = bit_or(floor(n), high_bit) + end + return floor(n) +end + +local function bit_lshift(n, bits) + check_int(n) + + if(n < 0) then + -- negative + n = bit_not(abs(n)) + 1 + end + + for i=1, bits do + n = n*2 + end + return bit_and(n, 4294967295) -- 0xFFFFFFFF +end + +-- convert little-endian 32-bit int to a 4-char string +local function lei2str(i) + local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end + return f(0)..f(8)..f(16)..f(24) +end + +-- convert raw string to big-endian int +local function str2bei(s) + local v=0 + for i=1, #s do + v = v * 256 + byte(s, i) + end + return v +end + +-- convert raw string to little-endian int +local function str2lei(s) + local v=0 + for i = #s,1,-1 do + v = v*256 + byte(s, i) + end + return v +end + +-- cut up a string in little-endian ints of given size +local function cut_le_str(s,...) + local o, r = 1, {} + local args = {...} + for i=1, #args do + table.insert(r, str2lei(sub(s, o, o + args[i] - 1))) + o = o + args[i] + end + return r +end + +local swap = function (w) return str2bei(lei2str(w)) end + +local function hex2binaryaux(hexval) + return char(tonumber(hexval, 16)) +end + +local function hex2binary(hex) + local result, _ = hex:gsub('..', hex2binaryaux) + return result +end + +-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh) +-- 10/02/2001 jcw@equi4.com + +local FF = 0xffffffff +local CONSTS = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 +} + +local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end +local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end +local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end +local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end +local z=function (f,a,b,c,d,x,s,ac) + a=bit_and(a+f(b,c,d)+x+ac,FF) + -- be *very* careful that left shift does not cause rounding! + return bit_or(bit_lshift(bit_and(a,bit_rshift(FF,s)),s),bit_rshift(a,32-s))+b +end + +local function transform(A,B,C,D,X) + local a,b,c,d=A,B,C,D + local t=CONSTS + + a=z(f,a,b,c,d,X[ 0], 7,t[ 1]) + d=z(f,d,a,b,c,X[ 1],12,t[ 2]) + c=z(f,c,d,a,b,X[ 2],17,t[ 3]) + b=z(f,b,c,d,a,X[ 3],22,t[ 4]) + a=z(f,a,b,c,d,X[ 4], 7,t[ 5]) + d=z(f,d,a,b,c,X[ 5],12,t[ 6]) + c=z(f,c,d,a,b,X[ 6],17,t[ 7]) + b=z(f,b,c,d,a,X[ 7],22,t[ 8]) + a=z(f,a,b,c,d,X[ 8], 7,t[ 9]) + d=z(f,d,a,b,c,X[ 9],12,t[10]) + c=z(f,c,d,a,b,X[10],17,t[11]) + b=z(f,b,c,d,a,X[11],22,t[12]) + a=z(f,a,b,c,d,X[12], 7,t[13]) + d=z(f,d,a,b,c,X[13],12,t[14]) + c=z(f,c,d,a,b,X[14],17,t[15]) + b=z(f,b,c,d,a,X[15],22,t[16]) + + a=z(g,a,b,c,d,X[ 1], 5,t[17]) + d=z(g,d,a,b,c,X[ 6], 9,t[18]) + c=z(g,c,d,a,b,X[11],14,t[19]) + b=z(g,b,c,d,a,X[ 0],20,t[20]) + a=z(g,a,b,c,d,X[ 5], 5,t[21]) + d=z(g,d,a,b,c,X[10], 9,t[22]) + c=z(g,c,d,a,b,X[15],14,t[23]) + b=z(g,b,c,d,a,X[ 4],20,t[24]) + a=z(g,a,b,c,d,X[ 9], 5,t[25]) + d=z(g,d,a,b,c,X[14], 9,t[26]) + c=z(g,c,d,a,b,X[ 3],14,t[27]) + b=z(g,b,c,d,a,X[ 8],20,t[28]) + a=z(g,a,b,c,d,X[13], 5,t[29]) + d=z(g,d,a,b,c,X[ 2], 9,t[30]) + c=z(g,c,d,a,b,X[ 7],14,t[31]) + b=z(g,b,c,d,a,X[12],20,t[32]) + + a=z(h,a,b,c,d,X[ 5], 4,t[33]) + d=z(h,d,a,b,c,X[ 8],11,t[34]) + c=z(h,c,d,a,b,X[11],16,t[35]) + b=z(h,b,c,d,a,X[14],23,t[36]) + a=z(h,a,b,c,d,X[ 1], 4,t[37]) + d=z(h,d,a,b,c,X[ 4],11,t[38]) + c=z(h,c,d,a,b,X[ 7],16,t[39]) + b=z(h,b,c,d,a,X[10],23,t[40]) + a=z(h,a,b,c,d,X[13], 4,t[41]) + d=z(h,d,a,b,c,X[ 0],11,t[42]) + c=z(h,c,d,a,b,X[ 3],16,t[43]) + b=z(h,b,c,d,a,X[ 6],23,t[44]) + a=z(h,a,b,c,d,X[ 9], 4,t[45]) + d=z(h,d,a,b,c,X[12],11,t[46]) + c=z(h,c,d,a,b,X[15],16,t[47]) + b=z(h,b,c,d,a,X[ 2],23,t[48]) + + a=z(i,a,b,c,d,X[ 0], 6,t[49]) + d=z(i,d,a,b,c,X[ 7],10,t[50]) + c=z(i,c,d,a,b,X[14],15,t[51]) + b=z(i,b,c,d,a,X[ 5],21,t[52]) + a=z(i,a,b,c,d,X[12], 6,t[53]) + d=z(i,d,a,b,c,X[ 3],10,t[54]) + c=z(i,c,d,a,b,X[10],15,t[55]) + b=z(i,b,c,d,a,X[ 1],21,t[56]) + a=z(i,a,b,c,d,X[ 8], 6,t[57]) + d=z(i,d,a,b,c,X[15],10,t[58]) + c=z(i,c,d,a,b,X[ 6],15,t[59]) + b=z(i,b,c,d,a,X[13],21,t[60]) + a=z(i,a,b,c,d,X[ 4], 6,t[61]) + d=z(i,d,a,b,c,X[11],10,t[62]) + c=z(i,c,d,a,b,X[ 2],15,t[63]) + b=z(i,b,c,d,a,X[ 9],21,t[64]) + + return A+a,B+b,C+c,D+d +end + +---------------------------------------------------------------- + +function md5.sumhexa(s) + local msgLen = #s + local padLen = 56 - msgLen % 64 + + if msgLen % 64 > 56 then padLen = padLen + 64 end + + if padLen == 0 then padLen = 64 end + + s = s .. char(128) .. rep(char(0),padLen-1) .. lei2str(8*msgLen) .. lei2str(0) + + assert(#s % 64 == 0) + + local t = CONSTS + local a,b,c,d = t[65],t[66],t[67],t[68] + + for i=1,#s,64 do + local X = cut_le_str(sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4) + assert(#X == 16) + X[0] = table.remove(X,1) -- zero based! + a,b,c,d = transform(a,b,c,d,X) + end + + return format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d)) +end + +function md5.sum(s) + return hex2binary(md5.sumhexa(s)) +end + +return md5 diff --git a/client/lualibs/mf_default_keys.lua b/client/lualibs/mf_default_keys.lua index 4859ff0c..b9b414d8 100644 --- a/client/lualibs/mf_default_keys.lua +++ b/client/lualibs/mf_default_keys.lua @@ -141,7 +141,13 @@ local _keys = { '200000000000', 'a00000000000', 'b00000000000', - } + + --[[ + Should be for Mifare TNP3xxx tags A KEY. + --]] + '4b0b20107ccb', + +} --- -- The keys above have just been pasted in, for completeness sake. They contain duplicates. diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index 24021a1d..10e7c2d4 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -25,6 +25,7 @@ local ISO14A_COMMAND = { local ISO14443a_TYPES = {} ISO14443a_TYPES[0x00] = "NXP MIFARE Ultralight | Ultralight C" +ISO14443a_TYPES[0x01] = "NXP MIFARE TNP3xxx Activision Game Appliance" ISO14443a_TYPES[0x04] = "NXP MIFARE (various !DESFire !DESFire EV1)" ISO14443a_TYPES[0x08] = "NXP MIFARE CLASSIC 1k | Plus 2k" ISO14443a_TYPES[0x09] = "NXP MIFARE Mini 0.3k" diff --git a/client/scripting.c b/client/scripting.c index 963bb64c..fd065a04 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -18,6 +18,7 @@ #include "util.h" #include "nonce2key/nonce2key.h" #include "../common/iso15693tools.h" +#include /** * The following params expected: * UsbCommand c @@ -224,6 +225,44 @@ static int l_iso15693_crc(lua_State *L) return 1; } +/* + Simple AES 128 cbc hook up to OpenSSL. + params: key, input +*/ +static int l_aes(lua_State *L) +{ + //Check number of arguments + int i; + size_t size; + const char *p_key = luaL_checklstring(L, 1, &size); + if(size != 32) return returnToLuaWithError(L,"Wrong size of key, got %d bytes, expected 32", (int) size); + + const char *p_encTxt = luaL_checklstring(L, 2, &size); + + unsigned char indata[AES_BLOCK_SIZE] = {0x00}; + unsigned char outdata[AES_BLOCK_SIZE] = {0x00}; + unsigned char aes_key[AES_BLOCK_SIZE] = {0x00}; + unsigned char iv[AES_BLOCK_SIZE] = {0x00}; + + // convert key to bytearray + for (i = 0; i < 32; i += 2) { + sscanf(&p_encTxt[i], "%02x", (unsigned int *)&indata[i / 2]); + } + + // convert input to bytearray + for (i = 0; i < 32; i += 2) { + sscanf(&p_key[i], "%02x", (unsigned int *)&aes_key[i / 2]); + } + + AES_KEY key; + AES_set_decrypt_key(aes_key, 128, &key); + AES_cbc_encrypt(indata, outdata, sizeof(indata), &key, iv, AES_DECRYPT); + + //Push decrypted array as a string + lua_pushlstring(L,(const char *)&outdata, sizeof(outdata)); + return 1;// return 1 to signal one return value +} + /** * @brief Sets the lua path to include "./lualibs/?.lua", in order for a script to be * able to do "require('foobar')" if foobar.lua is within lualibs folder. @@ -259,8 +298,9 @@ int set_pm3_libraries(lua_State *L) {"foobar", l_foobar}, {"ukbhit", l_ukbhit}, {"clearCommandBuffer", l_clearCommandBuffer}, - {"console", l_CmdConsole}, - {"iso15693_crc", l_iso15693_crc}, + {"console", l_CmdConsole}, + {"iso15693_crc", l_iso15693_crc}, + {"aes", l_aes}, {NULL, NULL} }; diff --git a/client/scripts/mifare_autopwn.lua b/client/scripts/mifare_autopwn.lua index 8d0d358f..eb98ffbf 100644 --- a/client/scripts/mifare_autopwn.lua +++ b/client/scripts/mifare_autopwn.lua @@ -133,6 +133,8 @@ function nested(key,sak) typ = 0 elseif 0x10 == sak then-- "NXP MIFARE Plus 2k" typ = 2 + elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K" + typ = 1 else print("I don't know how many sectors there are on this type of card, defaulting to 16") end diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua new file mode 100644 index 00000000..3eb5af1c --- /dev/null +++ b/client/scripts/tnp3.lua @@ -0,0 +1,189 @@ +local cmds = require('commands') +local getopt = require('getopt') +local bin = require('bin') +local lib14a = require('read14a') +local utils = require('utils') +local md5 = require('md5') + +example =[[ + 1. script run tnp3 + 2. script run tnp3 -k aabbccddeeff +]] +author = "Iceman" +usage = "script run tnp3 -k " +desc =[[ +This script will try to dump the contents of a Mifare TNP3xxx card. +It will need a valid KeyA in order to find the other keys and decode the card. +Arguments: + -h - this help + -k - Sector 0 Key A. +]] + +local hashconstant = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' + +local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds +local DEBUG = true -- the debug flag +local numBlocks = 64 +local numSectors = 16 +--- +-- A debug printout-function +function dbg(args) + if not DEBUG then + return + end + + if type(args) == "table" then + local i = 1 + while result[i] do + dbg(result[i]) + i = i+1 + end + else + print("###", args) + end +end +--- +-- This is only meant to be used when errors occur +function oops(err) + print("ERROR: ",err) +end +--- +-- Usage help +function help() + print(desc) + print("Example usage") + print(example) +end +-- +-- Exit message +function ExitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function show(data) + if DEBUG then + local formatString = ("H%d"):format(string.len(data)) + local _,hexdata = bin.unpack(formatString, data) + dbg("Hexdata" , hexdata) + end +end + +function waitCmd() + local response = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT) + if response then + local count,cmd,arg0 = bin.unpack('LL',response) + if(arg0==1) then + local count,arg1,arg2,data = bin.unpack('LLH511',response,count) + return data:sub(1,32) + else + return nil, "Couldn't read block.." + end + end + return nil, "No response from device" +end + +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + local keyA + local cmd + local err + local cmdReadBlockString = 'hf mf rdbl %d A %s' + + -- Arguments for the script + for o, a in getopt.getopt(args, 'hk:') do + if o == "h" then return help() end + if o == "k" then keyA = a end + end + + -- validate input args. + keyA = keyA or '4b0b20107ccb' + if #(keyA) ~= 12 then + return oops( string.format('Wrong length of write key (was %d) expected 12', #keyA)) + end + + result, err = lib14a.read1443a(false) + if not result then + print(err) + return + end + print((" Found tag : %s"):format(result.name)) + + core.clearCommandBuffer() + + if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx + print("This is not a TNP3xxx tag. aborting.") + return + end + + -- Show info + print(('Using keyA : %s'):format(keyA)) + print( string.rep('--',20) ) + + local cmdNestedString = 'hf mf nested 1 0 A %s d' + local cmdDumpString = 'hf mf dump' + --core.console(cmdNestedString.format(keyA) ) + --core.console(cmdDumpString) + + print('Reading data need to dump data') + + -- Read block 0 + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA} + err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local block0, err = waitCmd() + if err then return oops(err) end + + -- Read block 1 + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 1,arg2 = 0,arg3 = 0, data = keyA} + local err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local block1, err = waitCmd() + if err then return oops(err) end + + + -- Read block 9 + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 9,arg2 = 0,arg3 = 0, data = '56f6313550f9'} + local err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local block9, err = waitCmd() + if err then return oops(err) end + + -- main loop + print('BLOCK MD5 DECRYPTED ASCII' ) + + for block=0,numBlocks-1,1 do + + if math.fmod(block,4) then + + end + + local base = ('%s%s%02d%s'):format(block0, block1, block, hashconstant) + local md5hash = md5.sumhexa(base) + local aestest = core.aes(md5hash, block9 ) + + local _,hex = bin.unpack(("H%d"):format(16),aestest) + + + local hexascii = string.gsub(hex, '(%x%x)', + function(value) + return string.char(tonumber(value, 16)) + end + ) + + print( block .. ' :: ' .. md5hash .. ' :: ' .. hex .. ' :: ' .. hexascii ) + + -- if core.ukbhit() then + -- print("aborted by user") + -- break + -- end + end +end + +main(args) \ No newline at end of file diff --git a/include/at91sam7s512.h b/include/at91sam7s512.h index 5be13622..2cdcbce3 100644 --- a/include/at91sam7s512.h +++ b/include/at91sam7s512.h @@ -428,7 +428,7 @@ typedef struct _AT91S_PIO { #define PIO_PDR (AT91_CAST(AT91_REG *) 0x00000004) // (PIO_PDR) PIO Disable Register #define PIO_PSR (AT91_CAST(AT91_REG *) 0x00000008) // (PIO_PSR) PIO Status Register #define PIO_OER (AT91_CAST(AT91_REG *) 0x00000010) // (PIO_OER) Output Enable Register -#define PIO_ODR (AT91_CAST(AT91_REG *) 0x00000014) // (PIO_ODR) Output Disable Registerr +#define PIO_ODR (AT91_CAST(AT91_REG *) 0x00000014) // (PIO_ODR) Output Disable Register #define PIO_OSR (AT91_CAST(AT91_REG *) 0x00000018) // (PIO_OSR) Output Status Register #define PIO_IFER (AT91_CAST(AT91_REG *) 0x00000020) // (PIO_IFER) Input Filter Enable Register #define PIO_IFDR (AT91_CAST(AT91_REG *) 0x00000024) // (PIO_IFDR) Input Filter Disable Register -- 2.39.5 From c70cef9734647bed49c76a78f5a10c086e0f2ffc Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 3 Nov 2014 15:25:54 +0100 Subject: [PATCH 03/16] fixing scripts/tnp3.lua --- client/scripts/tnp3.lua | 80 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 3eb5af1c..ea7c3a23 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -71,7 +71,15 @@ local function show(data) end end -function waitCmd() +local function readdumpkeys(infile) + t = infile:read("*all") + len = string.len(t) + local len,hex = bin.unpack(("H%d"):format(len),t) + --print(len,hex) + return hex +end + +local function waitCmd() local response = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT) if response then local count,cmd,arg0 = bin.unpack('LL',response) @@ -95,6 +103,7 @@ local function main(args) local cmd local err local cmdReadBlockString = 'hf mf rdbl %d A %s' + local input = "dumpkeys.bin" -- Arguments for the script for o, a in getopt.getopt(args, 'hk:') do @@ -113,12 +122,12 @@ local function main(args) print(err) return end - print((" Found tag : %s"):format(result.name)) + print((' Found tag : %s'):format(result.name)) core.clearCommandBuffer() if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx - print("This is not a TNP3xxx tag. aborting.") + print('This is not a TNP3xxx tag. aborting.') return end @@ -126,11 +135,18 @@ local function main(args) print(('Using keyA : %s'):format(keyA)) print( string.rep('--',20) ) - local cmdNestedString = 'hf mf nested 1 0 A %s d' - local cmdDumpString = 'hf mf dump' - --core.console(cmdNestedString.format(keyA) ) - --core.console(cmdDumpString) + print('Trying to find other keys. ') + --core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) + + -- Reading found keys file + local infile = io.open(input, "rb") + if infile == nil then + return oops('Could not read file ', input) + end + local akeys = readdumpkeys(infile):sub(0,12*16) + --print( ('KEYS: %s'):format(akeys)) + print('Reading data need to dump data') -- Read block 0 @@ -147,42 +163,46 @@ local function main(args) local block1, err = waitCmd() if err then return oops(err) end + print('Dumping data') - -- Read block 9 - cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 9,arg2 = 0,arg3 = 0, data = '56f6313550f9'} - local err = core.SendCommand(cmd:getBytes()) - if err then return oops(err) end - local block9, err = waitCmd() - if err then return oops(err) end - -- main loop print('BLOCK MD5 DECRYPTED ASCII' ) + + local key + local keyPosStart = 0 + local block + for block = 0, numBlocks-1, 1 do + local b = (block+1)%4 + if b ~= 0 then + keyPosStart = (math.floor( block / 4 ) * 12)+1 + key = akeys:sub(keyPosStart, keyPosStart + 12 ) + --print( ('%02d %s'):format(block, key)) - for block=0,numBlocks-1,1 do + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = block ,arg2 = 0,arg3 = 0, data = key} + local err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local blockdata, err = waitCmd() + if err then return oops(err) end - if math.fmod(block,4) then - - end + local base = ('%s%s%02d%s'):format(block0, block1, block, hashconstant) + local md5hash = md5.sumhexa(base) + local aestest = core.aes(md5hash, blockdata) - local base = ('%s%s%02d%s'):format(block0, block1, block, hashconstant) - local md5hash = md5.sumhexa(base) - local aestest = core.aes(md5hash, block9 ) + local _,hex = bin.unpack(("H%d"):format(16),aestest) - local _,hex = bin.unpack(("H%d"):format(16),aestest) - - - local hexascii = string.gsub(hex, '(%x%x)', + local hexascii = string.gsub(hex, '(%x%x)', function(value) return string.char(tonumber(value, 16)) end ) - print( block .. ' :: ' .. md5hash .. ' :: ' .. hex .. ' :: ' .. hexascii ) + print( ('%02d :: %s :: %s :: %s :: %s'):format(block,key,md5hash,hex,hexascii) ) - -- if core.ukbhit() then - -- print("aborted by user") - -- break - -- end + if core.ukbhit() then + print("aborted by user") + break + end + end end end -- 2.39.5 From 8aa79dee20c4df50734897c979b17bcf387c77f6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 3 Nov 2014 21:59:31 +0100 Subject: [PATCH 04/16] FIX: added some tnp3xxx identification i formatMifare.lua FIX: tnp3.lua is more or less finished. Needs testing. --- armsrc/lfops.c | 4 +- client/scripts/formatMifare.lua | 10 ++-- client/scripts/tnp3.lua | 99 +++++++++++++++++---------------- 3 files changed, 59 insertions(+), 54 deletions(-) diff --git a/armsrc/lfops.c b/armsrc/lfops.c index dc5efe68..1a7c3224 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -452,7 +452,7 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) void SimulateTagLowFrequency(int period, int gap, int ledcontrol) { int i = 0; - uint8_t *buff = (uint8_t *)BigBuf; + uint8_t *buf = (uint8_t *)BigBuf; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz @@ -493,7 +493,7 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) //#define LOW(x) AT91C_BASE_PIOA->PIO_CODR = (x) //#define HIGH(x) AT91C_BASE_PIOA->PIO_SODR = (x) - if ( buff[i] > 0 ){ + if ( buf[i] > 0 ){ HIGH(GPIO_SSC_DOUT); //FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz //FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); diff --git a/client/scripts/formatMifare.lua b/client/scripts/formatMifare.lua index 1ced0c28..66a61fba 100644 --- a/client/scripts/formatMifare.lua +++ b/client/scripts/formatMifare.lua @@ -80,18 +80,20 @@ function GetCardInfo() core.clearCommandBuffer() - if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k + if 0x18 == result.sak then -- NXP MIFARE Classic 4k | Plus 4k -- IFARE Classic 4K offers 4096 bytes split into forty sectors, -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. numSectors = 40 - elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k + elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k -- 1K offers 1024 bytes of data storage, split into 16 sector numSectors = 16 - elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k + elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k -- MIFARE Classic mini offers 320 bytes split into five sectors. numSectors = 5 - elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k" + elseif 0x10 == result.sak then -- NXP MIFARE Plus 2k numSectors = 32 + elseif 0x01 == sak then -- NXP MIFARE TNP3xxx 1K + numSectors = 16 else print("I don't know how many sectors there are on this type of card, defaulting to 16") end diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index ea7c3a23..ebe1c6db 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -7,18 +7,23 @@ local md5 = require('md5') example =[[ 1. script run tnp3 - 2. script run tnp3 -k aabbccddeeff + 2. script run tnp3 -n + 3. script run tnp3 -k aabbccddeeff + 4. script run tnp3 -k aabbccddeeff -n ]] author = "Iceman" -usage = "script run tnp3 -k " +usage = "script run tnp3 -k -n" desc =[[ This script will try to dump the contents of a Mifare TNP3xxx card. It will need a valid KeyA in order to find the other keys and decode the card. Arguments: - -h - this help - -k - Sector 0 Key A. + -h : this help + -k : Sector 0 Key A. + -n : Use the nested cmd to find all keys ]] +-- AES konstant? LEN 0x24 36, +-- I dekompilen är det för internal static array = 0x36 54 local hashconstant = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds @@ -63,19 +68,10 @@ function ExitMsg(msg) print() end -local function show(data) - if DEBUG then - local formatString = ("H%d"):format(string.len(data)) - local _,hexdata = bin.unpack(formatString, data) - dbg("Hexdata" , hexdata) - end -end - local function readdumpkeys(infile) t = infile:read("*all") len = string.len(t) local len,hex = bin.unpack(("H%d"):format(len),t) - --print(len,hex) return hex end @@ -102,13 +98,15 @@ local function main(args) local keyA local cmd local err + local useNested = false local cmdReadBlockString = 'hf mf rdbl %d A %s' local input = "dumpkeys.bin" -- Arguments for the script - for o, a in getopt.getopt(args, 'hk:') do + for o, a in getopt.getopt(args, 'hk:n') do if o == "h" then return help() end if o == "k" then keyA = a end + if o == "n" then useNested = true end end -- validate input args. @@ -119,36 +117,33 @@ local function main(args) result, err = lib14a.read1443a(false) if not result then - print(err) - return + return oops(err) end + print((' Found tag : %s'):format(result.name)) core.clearCommandBuffer() if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx - print('This is not a TNP3xxx tag. aborting.') - return + return oops('This is not a TNP3xxx tag. aborting.') end -- Show info print(('Using keyA : %s'):format(keyA)) print( string.rep('--',20) ) - print('Trying to find other keys. ') - --core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) + print('Trying to find other keys.') + if useNested then + core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) + end - -- Reading found keys file + -- Loading keyfile local infile = io.open(input, "rb") if infile == nil then return oops('Could not read file ', input) end local akeys = readdumpkeys(infile):sub(0,12*16) - --print( ('KEYS: %s'):format(akeys)) - - print('Reading data need to dump data') - -- Read block 0 cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA} err = core.SendCommand(cmd:getBytes()) @@ -158,45 +153,46 @@ local function main(args) -- Read block 1 cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 1,arg2 = 0,arg3 = 0, data = keyA} - local err = core.SendCommand(cmd:getBytes()) + err = core.SendCommand(cmd:getBytes()) if err then return oops(err) end local block1, err = waitCmd() if err then return oops(err) end - print('Dumping data') + local key + local pos = 0 + local blockNo + local blocks = {} -- main loop - print('BLOCK MD5 DECRYPTED ASCII' ) - - local key - local keyPosStart = 0 - local block - for block = 0, numBlocks-1, 1 do - local b = (block+1)%4 - if b ~= 0 then - keyPosStart = (math.floor( block / 4 ) * 12)+1 - key = akeys:sub(keyPosStart, keyPosStart + 12 ) - --print( ('%02d %s'):format(block, key)) - - cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = block ,arg2 = 0,arg3 = 0, data = key} + for blockNo = 8, numBlocks-1, 1 do + local b = blockNo%4 + if b ~= 3 then + pos = (math.floor( blockNo / 4 ) * 12)+1 + key = akeys:sub(pos, pos + 12 ) + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key} local err = core.SendCommand(cmd:getBytes()) if err then return oops(err) end local blockdata, err = waitCmd() if err then return oops(err) end - local base = ('%s%s%02d%s'):format(block0, block1, block, hashconstant) + local base = ('%s%s%d%s'):format(block0, block1, blockNo, hashconstant) local md5hash = md5.sumhexa(base) local aestest = core.aes(md5hash, blockdata) local _,hex = bin.unpack(("H%d"):format(16),aestest) - local hexascii = string.gsub(hex, '(%x%x)', - function(value) - return string.char(tonumber(value, 16)) - end - ) - - print( ('%02d :: %s :: %s :: %s :: %s'):format(block,key,md5hash,hex,hexascii) ) + -- local hexascii = string.gsub(hex, '(%x%x)', + -- function(value) + -- return string.char(tonumber(value, 16)) + -- end + -- ) + + if string.find(blockdata, '^0+$') then + blocks[blockNo] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + else + --blocks[blockNo] = ('%02d :: %s :: %s :: %s '):format(blockNo,key,md5hash,hex) + blocks[blockNo] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + end if core.ukbhit() then print("aborted by user") @@ -204,6 +200,13 @@ local function main(args) end end end + + -- Print results + print('BLK :: DATA DECRYPTED' ) + print( string.rep('--',36) ) + for _,s in pairs(blocks) do + print( s ) + end end main(args) \ No newline at end of file -- 2.39.5 From 1a5ff2c2a7feaaf2ba6dc83bee611d1cab5f4527 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 3 Nov 2014 22:29:43 +0100 Subject: [PATCH 05/16] FIX: fixed the layout of data in tnp3.lua --- client/scripts/tnp3.lua | 75 ++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index ebe1c6db..5b3da98f 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -132,12 +132,14 @@ local function main(args) print(('Using keyA : %s'):format(keyA)) print( string.rep('--',20) ) - print('Trying to find other keys.') + if useNested then + print('Trying to find keys.') core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) end -- Loading keyfile + print('Loading dumpkeys.bin') local infile = io.open(input, "rb") if infile == nil then return oops('Could not read file ', input) @@ -164,40 +166,51 @@ local function main(args) local blocks = {} -- main loop - for blockNo = 8, numBlocks-1, 1 do + for blockNo = 0, numBlocks-1, 1 do + + if core.ukbhit() then + print("aborted by user") + break + end + + pos = (math.floor( blockNo / 4 ) * 12)+1 + key = akeys:sub(pos, pos + 12 ) + cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key} + local err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local blockdata, err = waitCmd() + if err then return oops(err) end + local b = blockNo%4 + if b ~= 3 then - pos = (math.floor( blockNo / 4 ) * 12)+1 - key = akeys:sub(pos, pos + 12 ) - cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key} - local err = core.SendCommand(cmd:getBytes()) - if err then return oops(err) end - local blockdata, err = waitCmd() - if err then return oops(err) end - - local base = ('%s%s%d%s'):format(block0, block1, blockNo, hashconstant) - local md5hash = md5.sumhexa(base) - local aestest = core.aes(md5hash, blockdata) - - local _,hex = bin.unpack(("H%d"):format(16),aestest) - - -- local hexascii = string.gsub(hex, '(%x%x)', - -- function(value) - -- return string.char(tonumber(value, 16)) - -- end - -- ) - - if string.find(blockdata, '^0+$') then - blocks[blockNo] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + if blockNo < 8 then + -- Block 0-7 not encrypted + blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) else - --blocks[blockNo] = ('%02d :: %s :: %s :: %s '):format(blockNo,key,md5hash,hex) - blocks[blockNo] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) - end - - if core.ukbhit() then - print("aborted by user") - break + local base = ('%s%s%d%s'):format(block0, block1, blockNo, hashconstant) + local md5hash = md5.sumhexa(base) + local aestest = core.aes(md5hash, blockdata) + + local _,hex = bin.unpack(("H%d"):format(16),aestest) + + -- local hexascii = string.gsub(hex, '(%x%x)', + -- function(value) + -- return string.char(tonumber(value, 16)) + -- end + -- ) + + if string.find(blockdata, '^0+$') then + blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + else + --blocks[blockNo+1] = ('%02d :: %s :: %s :: %s '):format(blockNo,key,md5hash,hex) + blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,hex) + end end + + else + -- Sectorblocks, not encrypted + blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) end end -- 2.39.5 From 9b989c45b942356410416b10e96bc4ca624e4563 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 5 Nov 2014 19:16:54 +0100 Subject: [PATCH 06/16] FIX: minor parseing bug when loading dumpkeys.bin file. ADD: added some useful helperfunctions to utils.lua --- client/lualibs/utils.lua | 58 ++++++++++++++++++++++++++++++++++++++-- client/scripts/tnp3.lua | 4 +-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/client/lualibs/utils.lua b/client/lualibs/utils.lua index 3d27d5b6..6b3777db 100644 --- a/client/lualibs/utils.lua +++ b/client/lualibs/utils.lua @@ -47,11 +47,65 @@ local Utils = --- -- Convert Byte array to string of hex ConvertBytes2String = function(bytes) - s = {} + local s = {} for i = 1, #(bytes) do - s[i] = string.format("%02X",bytes[i]) + s[i] = string.format("%02X",bytes[i]) end return table.concat(s) end, + + ConvertStringToBytes = function(s) + local t={} + for k in s:gmatch"(%x%x)" do + table.insert(t,tonumber(k,16)) + end + return t + end, + + -- function convertStringToBytes(str) + -- local bytes = {} + -- local strLength = string.len(str) + -- for i=1,strLength do + -- table.insert(bytes, string.byte(str, i)) + -- end + + -- return bytes +-- end + +-- function convertBytesToString(bytes) + -- local bytesLength = table.getn(bytes) + -- local str = "" + -- for i=1,bytesLength do + -- str = str .. string.char(bytes[i]) + -- end + + -- return str +-- end + +-- function convertHexStringToBytes(str) + -- local bytes = {} + -- local strLength = string.len(str) + -- for k=2,strLength,2 do + -- local hexString = "0x" .. string.sub(str, (k - 1), k) + -- table.insert(bytes, hex.to_dec(hexString)) + -- end + + -- return bytes +-- end + +-- function convertBytesToHexString(bytes) + -- local str = "" + -- local bytesLength = table.getn(bytes) + -- for i=1,bytesLength do + -- local hexString = string.sub(hex.to_hex(bytes[i]), 3) + -- if string.len(hexString) == 1 then + -- hexString = "0" .. hexString + -- end + -- str = str .. hexString + -- end + + -- return str +-- end + } return Utils \ No newline at end of file diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 5b3da98f..4e8ca77b 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -145,7 +145,7 @@ local function main(args) return oops('Could not read file ', input) end local akeys = readdumpkeys(infile):sub(0,12*16) - + -- Read block 0 cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA} err = core.SendCommand(cmd:getBytes()) @@ -174,7 +174,7 @@ local function main(args) end pos = (math.floor( blockNo / 4 ) * 12)+1 - key = akeys:sub(pos, pos + 12 ) + key = akeys:sub(pos, pos + 11 ) cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key} local err = core.SendCommand(cmd:getBytes()) if err then return oops(err) end -- 2.39.5 From 22f1c57786097d373e6d4706588b5d9e9a09e8e5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 9 Nov 2014 17:22:04 +0100 Subject: [PATCH 07/16] Updated tnp3.lua added some possibilities to abort the "hf mf nested" command added a rudimentary items identification for tnp3xxx --- armsrc/mifarecmd.c | 28 +++++++---- client/.history | 83 +++++++++++++++++++++++++++++++++ client/cmdhfmf.c | 20 ++++---- client/lualibs/default_toys.lua | 63 +++++++++++++++++++++++++ client/mifarehost.c | 7 --- client/scripts/tnp3.lua | 27 +++++++---- 6 files changed, 195 insertions(+), 33 deletions(-) create mode 100644 client/lualibs/default_toys.lua diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 7e3e9293..0d1fb77a 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -76,7 +76,7 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) // ----------------------------- crypto1 destroy crypto1_destroy(pcs); - if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); + if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); LED_B_ON(); cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); @@ -558,6 +558,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // statistics on nonce distance if (calibrate) { // for first call only. Otherwise reuse previous calibration LED_B_ON(); + WDT_HIT(); davg = dmax = 0; dmin = 2000; @@ -596,10 +597,10 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; }; - nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 - for (i = 101; i < 1200; i++) { + nttmp = prng_successor(nt1, 140); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 + for (i = 141; i < 1200; i++) { nttmp = prng_successor(nttmp, 1); - if (nttmp == nt2) break; + if (nttmp == nt2) {break;} } if (i != 1200) { @@ -615,7 +616,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat } } - if (rtr <= 1) return; + if (rtr <= 1) return; davg = (davg + (rtr - 1)/2) / (rtr - 1); @@ -634,9 +635,18 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // get crypted nonces for target sector for(i=0; i < 2; i++) { // look for exactly two different nonces + WDT_HIT(); + if(BUTTON_PRESS()) { + DbpString("Nested: cancelled"); + crypto1_destroy(pcs); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + return; + } + target_nt[i] = 0; while(target_nt[i] == 0) { // continue until we have an unambiguous nonce - + // prepare next select. No need to power down the card. if(mifare_classic_halt(pcs, cuid)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Halt error"); @@ -697,15 +707,15 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat if (target_nt[i] == 0 && j == dmax+1 && MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (all invalid)", i+1); } } - + LED_C_OFF(); // ----------------------------- crypto1 destroy crypto1_destroy(pcs); // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); +// memset(uid, 0x44, 4); +// LogTrace(uid, 4, 0, 0, TRUE); byte_t buf[4 + 4 * 4]; memcpy(buf, &cuid, 4); diff --git a/client/.history b/client/.history index e20a63e0..d781126a 100644 --- a/client/.history +++ b/client/.history @@ -9,3 +9,86 @@ lf t55xx rd 2 lf em4x 410xsim 124s lf em4x 410xsim 0F0368568B da pl +scr run sky +script list +scr run mifare_autopwn +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 -n +scr run tnp3 +scr run tnp3 -n +hf mf nested 0 a 4b0b20107ccb d +hf mf nested 1 0 a 4b0b20107ccb d +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 +scr run tnp3 -n +scr run tnp3 +hf mf nested 1 0 a 4b0b20107ccb d +scr run tnp3 diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 1d2de683..8a48c19c 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -36,7 +36,6 @@ start: //flush queue while (ukbhit()) getchar(); - // wait cycle while (true) { printf("."); @@ -848,9 +847,7 @@ int CmdHF14AMfNested(const char *Cmd) if (ctmp != 'A' && ctmp != 'a') trgKeyType = 1; } else { - - - + switch (cmdp) { case '0': SectorsCnt = 05; break; case '1': SectorsCnt = 16; break; @@ -935,20 +932,26 @@ int CmdHF14AMfNested(const char *Cmd) } } - // nested sectors iterations = 0; PrintAndLog("nested..."); bool calibrate = true; for (i = 0; i < NESTED_SECTOR_RETRY; i++) { for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; sectorNo++) { + + if (ukbhit()) { + printf("\naborted via keyboard!\n"); + free(e_sector); + return 2; + } + for (trgKeyType = 0; trgKeyType < 2; trgKeyType++) { if (e_sector[sectorNo].foundKey[trgKeyType]) continue; PrintAndLog("-----------------------------------------------"); if(mfnested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate)) { PrintAndLog("Nested error.\n"); - return 2; - } + free(e_sector); + return 2; } else { calibrate = false; } @@ -1018,10 +1021,9 @@ int CmdHF14AMfNested(const char *Cmd) } fclose(fkeys); } - + free(e_sector); } - return 0; } diff --git a/client/lualibs/default_toys.lua b/client/lualibs/default_toys.lua new file mode 100644 index 00000000..abb56515 --- /dev/null +++ b/client/lualibs/default_toys.lua @@ -0,0 +1,63 @@ +local _names = { + --[[ + --]] + ["0400"]="BASH", + ["1600"]="BOOMER" , + ["1800"]="CAMO", + ["3000"]="CHOPCHOP" , + ["2000"]="CYNDER", + ["6400"]="JET-VAC", + ["6700"]="FLASHWING", + ["7000"]="TREE REX", + ["7100"]="LIGHTCORE SHROOMBOOM", + ["1C00"]="DARK SPYRO", + ["0600"]="DINORANG" , + ["1200"]="DOUBLE TROUBLE" , + ["1500"]="DRILLSERGEANT" , + ["1400"]="DROBOT", + ["0900"]="LIGHTCORE ERUPTOR" , + ["0B00"]="FLAMESLINGER" , + ["1F00"]="GHOST ROASTER", + ["0E00"]="GILL GRUNT" , + ["1D00"]="HEX", + ["0A00"]="IGNITOR", + ["0300"]="LIGHTNINGROD", + ["0700"]="LIGHTCORE PRISM BREAK", + ["1500"]="SLAMBAM", + ["0100"]="SONIC BOOM", + ["1000"]="SPYRO", + ["1A00"]="STEALTH ELF", + ["1B00"]="STUMP SMASH", + ["0800"]="SUNBURN", + ["0500"]="TERRAFIN", + ["1300"]="TRIGGER HAPPY", + ["1100"]="VOODOOD", + ["0200"]="WARNADO", + ["0D00"]="WHAM SHELL", + ["0000"]="WHIRLWIND", + ["1700"]="WRECKING BALL", + ["0C00"]="ZAP", + ["1900"]="ZOOK", + ["0300"]="DRAGON", + ["012D"]="ICE", + ["012E"]="PIRATE", + ["0130"]="PVPUNLOCK", + ["012F"]="UNDEAD", + ["0200"]="ANVIL" , + ["CB00"]="CROSSED SWORDS", + ["CC00"]="HOURGLASS", + ["CA00"]="REGENERATION", + ["C900"]="SECRET STASH", + ["CD00"]="SHIELD", + ["CF00"]="SPARX", + ["CE00"]="SPEED BOOTS", + ["0194"]="LEGENDARY BASH", + ["0430"]="LEGENDARY CHOPCHOP", + ["01A0"]="LEGENDARY SPYRO", + ["01A3"]="LEGENDARY TRIGGER HAPPY", + ["0202"]="PET GILL GRUNT", + ["020E"]="PET STEALTH ELF", + ["01F9"]="PET TERRAFIN", + ["0207"]="PET TRIGGER HAPPY", +} +return _names diff --git a/client/mifarehost.c b/client/mifarehost.c index ed62bcee..cda884d9 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -26,8 +26,6 @@ int compar_int(const void * a, const void * b) { else return -1; } - - // Compare 16 Bits out of cryptostate int Compare16Bits(const void * a, const void * b) { if ((*(uint64_t*)b & 0x00ff000000ff0000) == (*(uint64_t*)a & 0x00ff000000ff0000)) return 0; @@ -35,7 +33,6 @@ int Compare16Bits(const void * a, const void * b) { else return -1; } - typedef struct { union { @@ -70,15 +67,11 @@ void* nested_worker_thread(void *arg) return statelist->head.slhead; } - - - int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * resultKey, bool calibrate) { uint16_t i, len; uint32_t uid; UsbCommand resp; - StateList_t statelists[2]; struct Crypto1State *p1, *p2, *p3, *p4; diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 4e8ca77b..56d0b486 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -4,6 +4,7 @@ local bin = require('bin') local lib14a = require('read14a') local utils = require('utils') local md5 = require('md5') +local toyNames = require('default_toys') example =[[ 1. script run tnp3 @@ -92,8 +93,8 @@ end local function main(args) print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() + --print( string.rep('--',20) ) + --print() local keyA local cmd @@ -114,27 +115,30 @@ local function main(args) if #(keyA) ~= 12 then return oops( string.format('Wrong length of write key (was %d) expected 12', #keyA)) end + + -- Turn off Debug + local cmdSetDbgOff = "hf mf dbg 0" + core.console( cmdSetDbgOff) result, err = lib14a.read1443a(false) if not result then return oops(err) end - print((' Found tag : %s'):format(result.name)) - core.clearCommandBuffer() if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx return oops('This is not a TNP3xxx tag. aborting.') end + print((' Found tag : %s'):format(result.name)) + -- Show info print(('Using keyA : %s'):format(keyA)) print( string.rep('--',20) ) - + --Trying to find the other keys if useNested then - print('Trying to find keys.') core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) end @@ -165,6 +169,8 @@ local function main(args) local blockNo local blocks = {} + print('Reading card data') + -- main loop for blockNo = 0, numBlocks-1, 1 do @@ -188,8 +194,7 @@ local function main(args) -- Block 0-7 not encrypted blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) else - local base = ('%s%s%d%s'):format(block0, block1, blockNo, hashconstant) - local md5hash = md5.sumhexa(base) + local base = ('%s%s%d%s'):format(block0, block1, blockNo, hashconstant) local md5hash = md5.sumhexa(base) local aestest = core.aes(md5hash, blockdata) local _,hex = bin.unpack(("H%d"):format(16),aestest) @@ -215,6 +220,12 @@ local function main(args) end -- Print results + local uid = block0:sub(1,8) + local itemtype = block1:sub(1,4) + local cardid = block1:sub(9,24) + print( (' UID : %s'):format(uid) ) + print( (' ITEM TYPE : %s - %s'):format(itemtype, toyNames[itemtype]) ) + print( (' CARDID : %s'):format(cardid ) ) print('BLK :: DATA DECRYPTED' ) print( string.rep('--',36) ) for _,s in pairs(blocks) do -- 2.39.5 From cd5767d43da36aa44a78bd4dcfbd7016349da3c6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 9 Nov 2014 19:29:47 +0100 Subject: [PATCH 08/16] FIX: I think the dumping of data is correct now in tnp3.lua. MD5 string vs bytearrays in lua are tricky ADD: utils.lua some functions to convert between ascii, bytes and strings. --- client/lualibs/utils.lua | 33 +++++++++++++++++++++++++++++---- client/scripts/tnp3.lua | 38 +++++++++++++++++--------------------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/client/lualibs/utils.lua b/client/lualibs/utils.lua index 6b3777db..bff89c5f 100644 --- a/client/lualibs/utils.lua +++ b/client/lualibs/utils.lua @@ -46,21 +46,46 @@ local Utils = end, --- -- Convert Byte array to string of hex - ConvertBytes2String = function(bytes) - local s = {} + ConvertBytes2HexString = function(bytes) + if #bytes == 0 then + return '' + end + local s={} for i = 1, #(bytes) do s[i] = string.format("%02X",bytes[i]) end return table.concat(s) end, - - ConvertStringToBytes = function(s) + -- Convert byte array to string with ascii + ConvertBytesToAsciiString = function(bytes) + if #bytes == 0 then + return '' + end + local s={} + for i = 1, #(bytes) do + s[i] = string.char(bytes[i]) + end + return table.concat(s) + end, + ConvertHexStringToBytes = function(s) local t={} + if s == nil then return t end + if #s == 0 then return t end for k in s:gmatch"(%x%x)" do table.insert(t,tonumber(k,16)) end return t end, + ConvertAsciiStringToBytes = function(s) + local t={} + if s == nil then return t end + if #s == 0 then return t end + + for k in s:gmatch"(.)" do + table.insert(t, string.byte(k)) + end + return t + end, -- function convertStringToBytes(str) -- local bytes = {} diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 56d0b486..303963ba 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -93,7 +93,7 @@ end local function main(args) print( string.rep('--',20) ) - --print( string.rep('--',20) ) + print( string.rep('--',20) ) --print() local keyA @@ -187,35 +187,30 @@ local function main(args) local blockdata, err = waitCmd() if err then return oops(err) end - local b = blockNo%4 - - if b ~= 3 then + if blockNo%4 ~= 3 then if blockNo < 8 then -- Block 0-7 not encrypted - blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) else - local base = ('%s%s%d%s'):format(block0, block1, blockNo, hashconstant) local md5hash = md5.sumhexa(base) + local base = ('%s%s%02d%s'):format(block0, block1, blockNo, hashconstant) + local baseArr = utils.ConvertHexStringToBytes(base) + local baseStr = utils.ConvertBytesToAsciiString(baseArr) + local md5hash = md5.sumhexa(baseStr) local aestest = core.aes(md5hash, blockdata) - - local _,hex = bin.unpack(("H%d"):format(16),aestest) - - -- local hexascii = string.gsub(hex, '(%x%x)', - -- function(value) - -- return string.char(tonumber(value, 16)) - -- end - -- ) + --print(aestest, type(aestest)) + local hex = utils.ConvertAsciiStringToBytes(aestest) + hex = utils.ConvertBytes2HexString(hex) + --local _,hex = bin.unpack(("H%d"):format(16),aestest) if string.find(blockdata, '^0+$') then - blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) else - --blocks[blockNo+1] = ('%02d :: %s :: %s :: %s '):format(blockNo,key,md5hash,hex) - blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,hex) + blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex) end end - else -- Sectorblocks, not encrypted - blocks[blockNo+1] = ('%02d :: %s :: %s'):format(blockNo,blockdata,blockdata) + blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) end end @@ -226,10 +221,11 @@ local function main(args) print( (' UID : %s'):format(uid) ) print( (' ITEM TYPE : %s - %s'):format(itemtype, toyNames[itemtype]) ) print( (' CARDID : %s'):format(cardid ) ) - print('BLK :: DATA DECRYPTED' ) + print('BLK :: Decrypted Ascii' ) print( string.rep('--',36) ) for _,s in pairs(blocks) do - print( s ) + local arr = utils.ConvertHexStringToBytes(s:sub(7,#s)) + print( s, utils.ConvertBytesToAsciiString(arr) ) end end -- 2.39.5 From f595de25e95a66b9cf0e31c1925500db0abdae08 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 10 Nov 2014 21:46:21 +0100 Subject: [PATCH 09/16] ADD: html_dumplib.lua, added the functionality to save text-files. ADD: tnp3.lua, now will save the dumped data to BIN and EML FIX: tnp3.lua, added some clearcommando buffer to help the pm3 not to be blocked --- client/lualibs/html_dumplib.lua | 16 +++++++- client/scripts/tnp3.lua | 68 ++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/client/lualibs/html_dumplib.lua b/client/lualibs/html_dumplib.lua index b8c7ccaa..a7890885 100644 --- a/client/lualibs/html_dumplib.lua +++ b/client/lualibs/html_dumplib.lua @@ -47,6 +47,18 @@ local function save_HTML(javascript, filename) end +local function save_TEXT(data,filename) + -- Open the output file + local outfile = io.open(filename, "wb") + if outfile == nil then + return oops(string.format("Could not write to file %s",tostring(filename))) + end + + outfile:write(data) + io.close(outfile) + return filename +end + local function save_BIN(data, filename) -- Open the output file @@ -180,5 +192,7 @@ end return { convert_bin_to_html = convert_bin_to_html, convert_eml_to_html = convert_eml_to_html, - convert_eml_to_bin = convert_eml_to_bin, + convert_eml_to_bin = convert_eml_to_bin, + SaveAsBinary = save_BIN, + SaveAsText = save_TEXT, } diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 303963ba..44e3753b 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -4,6 +4,7 @@ local bin = require('bin') local lib14a = require('read14a') local utils = require('utils') local md5 = require('md5') +local dumplib = require('html_dumplib') local toyNames = require('default_toys') example =[[ @@ -11,9 +12,12 @@ example =[[ 2. script run tnp3 -n 3. script run tnp3 -k aabbccddeeff 4. script run tnp3 -k aabbccddeeff -n + 5. script run tnp3 -o myfile + 6. script run tnp3 -n -o myfile + 7. script run tnp3 -k aabbccddeeff -n -o myfile ]] author = "Iceman" -usage = "script run tnp3 -k -n" +usage = "script run tnp3 -k -n -o " desc =[[ This script will try to dump the contents of a Mifare TNP3xxx card. It will need a valid KeyA in order to find the other keys and decode the card. @@ -21,10 +25,9 @@ Arguments: -h : this help -k : Sector 0 Key A. -n : Use the nested cmd to find all keys + -o : filename for the saved dumps ]] --- AES konstant? LEN 0x24 36, --- I dekompilen är det för internal static array = 0x36 54 local hashconstant = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds @@ -94,7 +97,6 @@ local function main(args) print( string.rep('--',20) ) print( string.rep('--',20) ) - --print() local keyA local cmd @@ -102,12 +104,14 @@ local function main(args) local useNested = false local cmdReadBlockString = 'hf mf rdbl %d A %s' local input = "dumpkeys.bin" - + local outputTemplate = os.date("toydump-%Y-%m-%d_%H%M%S"); + -- Arguments for the script - for o, a in getopt.getopt(args, 'hk:n') do + for o, a in getopt.getopt(args, 'hk:no:') do if o == "h" then return help() end if o == "k" then keyA = a end if o == "n" then useNested = true end + if o == "o" then outputTemplate = a end end -- validate input args. @@ -130,12 +134,10 @@ local function main(args) if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx return oops('This is not a TNP3xxx tag. aborting.') end - + + -- Show tag info print((' Found tag : %s'):format(result.name)) - - -- Show info print(('Using keyA : %s'):format(keyA)) - print( string.rep('--',20) ) --Trying to find the other keys if useNested then @@ -170,7 +172,8 @@ local function main(args) local blocks = {} print('Reading card data') - + core.clearCommandBuffer() + -- main loop for blockNo = 0, numBlocks-1, 1 do @@ -197,7 +200,7 @@ local function main(args) local baseStr = utils.ConvertBytesToAsciiString(baseArr) local md5hash = md5.sumhexa(baseStr) local aestest = core.aes(md5hash, blockdata) - --print(aestest, type(aestest)) + local hex = utils.ConvertAsciiStringToBytes(aestest) hex = utils.ConvertBytes2HexString(hex) --local _,hex = bin.unpack(("H%d"):format(16),aestest) @@ -210,23 +213,44 @@ local function main(args) end else -- Sectorblocks, not encrypted - blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) + blocks[blockNo+1] = ('%02d :: %s%s'):format(blockNo,key,blockdata:sub(13,32)) end end + core.clearCommandBuffer() + -- Print results + local bindata = {} + local emldata = '' + + for _,s in pairs(blocks) do + local slice = s:sub(7,#s) + local str = utils.ConvertBytesToAsciiString( + utils.ConvertHexStringToBytes(slice) + ) + emldata = emldata..slice..'\n' + for c in (str):gmatch('.') do + bindata[#bindata+1] = c + end + end + + -- Write dump to files + local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'.bin') + print(("Wrote a BIN dump to the file %s"):format(foo)) + local bar = dumplib.SaveAsText(emldata, outputTemplate..'.eml') + print(("Wrote a EML dump to the file %s"):format(bar)) + local uid = block0:sub(1,8) local itemtype = block1:sub(1,4) local cardid = block1:sub(9,24) - print( (' UID : %s'):format(uid) ) - print( (' ITEM TYPE : %s - %s'):format(itemtype, toyNames[itemtype]) ) - print( (' CARDID : %s'):format(cardid ) ) - print('BLK :: Decrypted Ascii' ) - print( string.rep('--',36) ) - for _,s in pairs(blocks) do - local arr = utils.ConvertHexStringToBytes(s:sub(7,#s)) - print( s, utils.ConvertBytesToAsciiString(arr) ) - end + + -- Show info + print( string.rep('--',20) ) + print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) ) + print( (' UID : 0x%s'):format(uid) ) + print( (' CARDID : 0x%s'):format(cardid ) ) + print( string.rep('--',20) ) + end main(args) \ No newline at end of file -- 2.39.5 From 47cbb2d41851e680c84b3a7dd0465f7f7960a9ec Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 12 Nov 2014 23:18:46 +0100 Subject: [PATCH 10/16] ADD: tnp3.lua can now validate the checkums in the dump ADD: added CRC16 CCITT functionality to LUA FIX: tnp3.lua is now correctly decryping data while dumping --- client/lualibs/utils.lua | 9 +++++ client/scripting.c | 12 +++++++ client/scripts/tnp3.lua | 78 ++++++++++++++++++++++++++++++++++------ common/crc16.c | 23 ++++++++++++ common/crc16.h | 5 +-- 5 files changed, 114 insertions(+), 13 deletions(-) diff --git a/client/lualibs/utils.lua b/client/lualibs/utils.lua index bff89c5f..15b96ee5 100644 --- a/client/lualibs/utils.lua +++ b/client/lualibs/utils.lua @@ -86,6 +86,15 @@ local Utils = end return t end, + ConvertHexToAscii = function(s) + local t={} + if s == nil then return t end + if #s == 0 then return t end + for k in s:gmatch"(%x%x)" do + table.insert(t, string.char(tonumber(k,16))) + end + return table.concat(t) + end, -- function convertStringToBytes(str) -- local bytes = {} diff --git a/client/scripting.c b/client/scripting.c index fd065a04..f0c56baf 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -19,6 +19,7 @@ #include "nonce2key/nonce2key.h" #include "../common/iso15693tools.h" #include +#include "../common/crc16.h" /** * The following params expected: * UsbCommand c @@ -263,6 +264,16 @@ static int l_aes(lua_State *L) return 1;// return 1 to signal one return value } +static int l_crc16(lua_State *L) +{ + size_t size; + const char *p_str = luaL_checklstring(L, 1, &size); + + unsigned short retval = crc16_ccitt( p_str, size); + lua_pushinteger(L, (int) retval); + return 1; +} + /** * @brief Sets the lua path to include "./lualibs/?.lua", in order for a script to be * able to do "require('foobar')" if foobar.lua is within lualibs folder. @@ -301,6 +312,7 @@ int set_pm3_libraries(lua_State *L) {"console", l_CmdConsole}, {"iso15693_crc", l_iso15693_crc}, {"aes", l_aes}, + {"crc16", l_crc16}, {NULL, NULL} }; diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 44e3753b..006e5a5d 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -93,6 +93,16 @@ local function waitCmd() return nil, "No response from device" end +local function computeCrc16(s) + local hash = core.crc16(utils.ConvertHexToAscii(s)) + return hash +end + +local function reverseCrcBytes(crc) + crc2 = crc:sub(3,4)..crc:sub(1,2) + return tonumber(crc2,16) +end + local function main(args) print( string.rep('--',20) ) @@ -104,7 +114,7 @@ local function main(args) local useNested = false local cmdReadBlockString = 'hf mf rdbl %d A %s' local input = "dumpkeys.bin" - local outputTemplate = os.date("toydump-%Y-%m-%d_%H%M%S"); + local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S"); -- Arguments for the script for o, a in getopt.getopt(args, 'hk:no:') do @@ -175,6 +185,7 @@ local function main(args) core.clearCommandBuffer() -- main loop + io.write('Decrypting blocks > ') for blockNo = 0, numBlocks-1, 1 do if core.ukbhit() then @@ -195,9 +206,8 @@ local function main(args) -- Block 0-7 not encrypted blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) else - local base = ('%s%s%02d%s'):format(block0, block1, blockNo, hashconstant) - local baseArr = utils.ConvertHexStringToBytes(base) - local baseStr = utils.ConvertBytesToAsciiString(baseArr) + local base = ('%s%s%02x%s'):format(block0, block1, blockNo, hashconstant) + local baseStr = utils.ConvertHexToAscii(base) local md5hash = md5.sumhexa(baseStr) local aestest = core.aes(md5hash, blockdata) @@ -205,10 +215,12 @@ local function main(args) hex = utils.ConvertBytes2HexString(hex) --local _,hex = bin.unpack(("H%d"):format(16),aestest) + -- blocks with zero not encrypted. if string.find(blockdata, '^0+$') then blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) else - blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex) + blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex) + io.write( blockNo..',') end end else @@ -216,6 +228,7 @@ local function main(args) blocks[blockNo+1] = ('%02d :: %s%s'):format(blockNo,key,blockdata:sub(13,32)) end end + io.write('\n') core.clearCommandBuffer() @@ -224,7 +237,7 @@ local function main(args) local emldata = '' for _,s in pairs(blocks) do - local slice = s:sub(7,#s) + local slice = s:sub(8,#s) local str = utils.ConvertBytesToAsciiString( utils.ConvertHexStringToBytes(slice) ) @@ -235,10 +248,12 @@ local function main(args) end -- Write dump to files - local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'.bin') - print(("Wrote a BIN dump to the file %s"):format(foo)) - local bar = dumplib.SaveAsText(emldata, outputTemplate..'.eml') - print(("Wrote a EML dump to the file %s"):format(bar)) + if not DEBUG then + local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'.bin') + print(("Wrote a BIN dump to the file %s"):format(foo)) + local bar = dumplib.SaveAsText(emldata, outputTemplate..'.eml') + print(("Wrote a EML dump to the file %s"):format(bar)) + end local uid = block0:sub(1,8) local itemtype = block1:sub(1,4) @@ -251,6 +266,47 @@ local function main(args) print( (' CARDID : 0x%s'):format(cardid ) ) print( string.rep('--',20) ) -end + print('Validating checksums') + -- Checksum Typ 0 + local test1 = ('%s%s'):format(block0, block1:sub(1,28)) + local crc = block1:sub(29,32) + local revcrc = reverseCrcBytes(crc) + io.write( ('BLOCK 0-1 : %04x = %04x \n'):format(revcrc,computeCrc16(test1))) + + -- Checksum Typ 1 BLOCK 9 + local block9 = blocks[9]:sub(8,35) + test1 = ('%s0500'):format(block9) + crc = blocks[9]:sub(36,39) + revcrc = reverseCrcBytes(crc) + io.write( ('BLOCK 8 : %04x = %04x \n'):format(revcrc,computeCrc16(test1))) + + -- Checksum Typ 1 BLOCK 37 + local block37 = blocks[37]:sub(8,35) + test1 = ('%s0500'):format(block37) + crc = blocks[37]:sub(36,39) + revcrc = reverseCrcBytes(crc) + io.write( ('BLOCK 36 : %04x = %04x \n'):format(revcrc,computeCrc16(test1))) + + -- Checksum Typ 2 + -- 10,11,13 + test1 = blocks[10]:sub(8,39).. + blocks[11]:sub(8,39).. + blocks[13]:sub(8,39) + + crc = blocks[9]:sub(32,35) + revcrc = reverseCrcBytes(crc) + io.write( ('BLOCK 10-11-13 :%04x = %04x \n'):format(revcrc,computeCrc16(test1))) + -- Checksum Typ 3 + -- 15,17,18,19 + crc = blocks[9]:sub(28,31) + revcrc = reverseCrcBytes(crc) + test1 = blocks[14]:sub(8,39).. + blocks[15]:sub(8,39).. + blocks[17]:sub(8,39) + + local tohash = test1..string.rep('00',0xe0) + local hashed = computeCrc16(tohash) + io.write( ('BLOCK 14-15-17 %04x = %04x \n'):format(revcrc,hashed)) +end main(args) \ No newline at end of file diff --git a/common/crc16.c b/common/crc16.c index d181bb2a..973cd103 100644 --- a/common/crc16.c +++ b/common/crc16.c @@ -8,6 +8,7 @@ #include "crc16.h" + unsigned short update_crc16( unsigned short crc, unsigned char c ) { unsigned short i, v, tcrc = 0; @@ -20,3 +21,25 @@ unsigned short update_crc16( unsigned short crc, unsigned char c ) return ((crc >> 8) ^ tcrc)&0xffff; } + +uint16_t crc16(uint8_t const *message, int length, uint16_t remainder, uint16_t polynomial) { + + if (length == 0) + return (~remainder); + + for (int byte = 0; byte < length; ++byte) { + remainder ^= (message[byte] << 8); + for (uint8_t bit = 8; bit > 0; --bit) { + if (remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint16_t crc16_ccitt(uint8_t const *message, int length) { + return crc16(message, length, 0xffff, 0x1021); +} diff --git a/common/crc16.h b/common/crc16.h index 055a60bc..d16d83b5 100644 --- a/common/crc16.h +++ b/common/crc16.h @@ -5,10 +5,11 @@ //----------------------------------------------------------------------------- // CRC16 //----------------------------------------------------------------------------- +#include #ifndef __CRC16_H #define __CRC16_H - unsigned short update_crc16(unsigned short crc, unsigned char c); - +uint16_t crc16(uint8_t const *message, int length, uint16_t remainder, uint16_t polynomial); +uint16_t crc16_ccitt(uint8_t const *message, int length); #endif -- 2.39.5 From f91f0ebb35c1c131e5e255457212a0784c81dcd6 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 13 Nov 2014 20:14:14 +0100 Subject: [PATCH 11/16] CHG - some lua functions in utils.lua --- client/lualibs/commands.lua | 9 ++-- client/lualibs/utils.lua | 87 ++++++++++++++++++++++++++++++++++--- client/scripts/tnp3.lua | 69 +++++++---------------------- 3 files changed, 101 insertions(+), 64 deletions(-) diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index bf2a8a1f..f88eeae2 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -117,10 +117,10 @@ local _commands = { local _reverse_lookup,k,v = {} -for k, v in pairs(_commands) do - _reverse_lookup[v] = k -end -_commands.tostring = function(command) + for k, v in pairs(_commands) do + _reverse_lookup[v] = k + end + _commands.tostring = function(command) if(type(command) == 'number') then return ("%s (%d)"):format(_reverse_lookup[command]or "ERROR UNDEFINED!", command) end @@ -182,7 +182,6 @@ function Command:getBytes() local cmd = self.cmd local arg1, arg2, arg3 = self.arg1, self.arg2, self.arg3 - return bin.pack("LLLLH",cmd, arg1, arg2, arg3,data); end return _commands \ No newline at end of file diff --git a/client/lualibs/utils.lua b/client/lualibs/utils.lua index 15b96ee5..9b36dfc8 100644 --- a/client/lualibs/utils.lua +++ b/client/lualibs/utils.lua @@ -33,9 +33,86 @@ local Utils = return answer end, + + ------------ FILE READING + ReadDumpFile = function (filename) + + if filename == nil then + return nil, 'Filename is empty' + end + if #filename == 0 then + return nil, 'Filename length is zero' + end + + infile = io.open(filename, "rb") + if infile == nil then + return nil, string.format("Could not read file %s",filename) + end + local t = infile:read("*all") + len = string.len(t) + local _,hex = bin.unpack(("H%d"):format(len),t) + io.close(infile) + return hex + end, + + ------------ string split function + Split = function( inSplitPattern, outResults ) + if not outResults then + outResults = {} + end + local start = 1 + local splitStart, splitEnd = string.find( self, inSplitPattern, start ) + while splitStart do + table.insert( outResults, string.sub( self, start, splitStart-1 ) ) + start = splitEnd + 1 + splitStart, splitEnd = string.find( self, inSplitPattern, start ) + end + table.insert( outResults, string.sub( self, start ) ) + return outResults + end, + + ------------ CRC-16 ccitt checksums + + -- Takes a hex string and calculates a crc16 + Crc16 = function(s) + if s == nil then return nil end + if #s == 0 then return nil end + if type(s) == 'string' then + local utils = require('utils') + local asc = utils.ConvertHexToAscii(s) + local hash = core.crc16(asc) + return hash + end + return nil + end, + + -- input parameter is a string + -- Swaps the endianess and returns a number, + -- IE: 'cd7a' -> '7acd' -> 0x7acd + SwapEndianness = function(s, len) + if s == nil then return nil end + if #s == 0 then return '' end + if type(s) ~= 'string' then return nil end + + local retval = 0 + if len == 16 then + local t = s:sub(3,4)..s:sub(1,2) + retval = tonumber(t,16) + elseif len == 24 then + local t = s:sub(5,6)..s:sub(3,4)..s:sub(1,2) + retval = tonumber(t,16) + elseif len == 32 then + local t = s:sub(7,8)..s:sub(5,6)..s:sub(3,4)..s:sub(1,2) + retval = tonumber(t,16) + end + return retval + end, + + ------------ CONVERSIONS + -- -- Converts DECIMAL to HEX - ConvertDec2Hex = function(IN) + ConvertDecToHex = function(IN) local B,K,OUT,I,D=16,"0123456789ABCDEF","",0 while IN>0 do I=I+1 @@ -46,7 +123,7 @@ local Utils = end, --- -- Convert Byte array to string of hex - ConvertBytes2HexString = function(bytes) + ConvertBytesToHex = function(bytes) if #bytes == 0 then return '' end @@ -57,7 +134,7 @@ local Utils = return table.concat(s) end, -- Convert byte array to string with ascii - ConvertBytesToAsciiString = function(bytes) + ConvertBytesToAscii = function(bytes) if #bytes == 0 then return '' end @@ -67,7 +144,7 @@ local Utils = end return table.concat(s) end, - ConvertHexStringToBytes = function(s) + ConvertHexToBytes = function(s) local t={} if s == nil then return t end if #s == 0 then return t end @@ -76,7 +153,7 @@ local Utils = end return t end, - ConvertAsciiStringToBytes = function(s) + ConvertAsciiToBytes = function(s) local t={} if s == nil then return t end if #s == 0 then return t end diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3.lua index 006e5a5d..77de766f 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3.lua @@ -28,10 +28,10 @@ Arguments: -o : filename for the saved dumps ]] -local hashconstant = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' +local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds -local DEBUG = true -- the debug flag +local DEBUG = false -- the debug flag local numBlocks = 64 local numSectors = 16 --- @@ -154,14 +154,17 @@ local function main(args) core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) end + core.clearCommandBuffer() + -- Loading keyfile print('Loading dumpkeys.bin') - local infile = io.open(input, "rb") - if infile == nil then - return oops('Could not read file ', input) + local hex, err = utils.ReadDumpFile(input) + if not hex then + return oops(err) end - local akeys = readdumpkeys(infile):sub(0,12*16) - + + local akeys = hex:sub(0,12*16) + -- Read block 0 cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA} err = core.SendCommand(cmd:getBytes()) @@ -206,13 +209,13 @@ local function main(args) -- Block 0-7 not encrypted blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) else - local base = ('%s%s%02x%s'):format(block0, block1, blockNo, hashconstant) + local base = ('%s%s%02x%s'):format(block0, block1, blockNo, HASHCONSTANT) local baseStr = utils.ConvertHexToAscii(base) local md5hash = md5.sumhexa(baseStr) local aestest = core.aes(md5hash, blockdata) - local hex = utils.ConvertAsciiStringToBytes(aestest) - hex = utils.ConvertBytes2HexString(hex) + local hex = utils.ConvertAsciiToBytes(aestest) + hex = utils.ConvertBytesToHex(hex) --local _,hex = bin.unpack(("H%d"):format(16),aestest) -- blocks with zero not encrypted. @@ -238,8 +241,8 @@ local function main(args) for _,s in pairs(blocks) do local slice = s:sub(8,#s) - local str = utils.ConvertBytesToAsciiString( - utils.ConvertHexStringToBytes(slice) + local str = utils.ConvertBytesToAscii( + utils.ConvertHexToBytes(slice) ) emldata = emldata..slice..'\n' for c in (str):gmatch('.') do @@ -266,47 +269,5 @@ local function main(args) print( (' CARDID : 0x%s'):format(cardid ) ) print( string.rep('--',20) ) - print('Validating checksums') - -- Checksum Typ 0 - local test1 = ('%s%s'):format(block0, block1:sub(1,28)) - local crc = block1:sub(29,32) - local revcrc = reverseCrcBytes(crc) - - io.write( ('BLOCK 0-1 : %04x = %04x \n'):format(revcrc,computeCrc16(test1))) - - -- Checksum Typ 1 BLOCK 9 - local block9 = blocks[9]:sub(8,35) - test1 = ('%s0500'):format(block9) - crc = blocks[9]:sub(36,39) - revcrc = reverseCrcBytes(crc) - io.write( ('BLOCK 8 : %04x = %04x \n'):format(revcrc,computeCrc16(test1))) - - -- Checksum Typ 1 BLOCK 37 - local block37 = blocks[37]:sub(8,35) - test1 = ('%s0500'):format(block37) - crc = blocks[37]:sub(36,39) - revcrc = reverseCrcBytes(crc) - io.write( ('BLOCK 36 : %04x = %04x \n'):format(revcrc,computeCrc16(test1))) - - -- Checksum Typ 2 - -- 10,11,13 - test1 = blocks[10]:sub(8,39).. - blocks[11]:sub(8,39).. - blocks[13]:sub(8,39) - - crc = blocks[9]:sub(32,35) - revcrc = reverseCrcBytes(crc) - io.write( ('BLOCK 10-11-13 :%04x = %04x \n'):format(revcrc,computeCrc16(test1))) - -- Checksum Typ 3 - -- 15,17,18,19 - crc = blocks[9]:sub(28,31) - revcrc = reverseCrcBytes(crc) - test1 = blocks[14]:sub(8,39).. - blocks[15]:sub(8,39).. - blocks[17]:sub(8,39) - - local tohash = test1..string.rep('00',0xe0) - local hashed = computeCrc16(tohash) - io.write( ('BLOCK 14-15-17 %04x = %04x \n'):format(revcrc,hashed)) end main(args) \ No newline at end of file -- 2.39.5 From 5c065fa089f7864548bb91ef4779cf6ec054319f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 13 Nov 2014 22:02:36 +0100 Subject: [PATCH 12/16] FIX: Corrected the bug mention http://www.proxmark.org/forum/viewtopic.php?id=1612 filepath too short in "hf mf eload / esave / cload / csave" commands. Length was 14, is now 250. Should be enough for awhile. --- client/cmdhfmf.c | 16 ++++++++-------- client/mifarehost.c | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 8a48c19c..4b620275 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1419,7 +1419,7 @@ int CmdHF14AMfESet(const char *Cmd) int CmdHF14AMfELoad(const char *Cmd) { FILE * f; - char filename[20]; + char filename[255]; char *fnameptr = filename; char buf[64]; uint8_t buf8[64]; @@ -1436,7 +1436,7 @@ int CmdHF14AMfELoad(const char *Cmd) } len = strlen(Cmd); - if (len > 14) len = 14; + if (len > 250) len = 250; memcpy(filename, Cmd, len); fnameptr += len; @@ -1494,7 +1494,7 @@ int CmdHF14AMfELoad(const char *Cmd) int CmdHF14AMfESave(const char *Cmd) { FILE * f; - char filename[20]; + char filename[255]; char * fnameptr = filename; uint8_t buf[64]; int i, j, len; @@ -1511,7 +1511,7 @@ int CmdHF14AMfESave(const char *Cmd) } len = strlen(Cmd); - if (len > 14) len = 14; + if (len > 250) len = 250; if (len < 1) { // get filename @@ -1687,7 +1687,7 @@ int CmdHF14AMfCSetBlk(const char *Cmd) int CmdHF14AMfCLoad(const char *Cmd) { FILE * f; - char filename[20]; + char filename[255]; char * fnameptr = filename; char buf[64]; uint8_t buf8[64]; @@ -1728,7 +1728,7 @@ int CmdHF14AMfCLoad(const char *Cmd) return 0; } else { len = strlen(Cmd); - if (len > 14) len = 14; + if (len > 250) len = 250; memcpy(filename, Cmd, len); fnameptr += len; @@ -1851,7 +1851,7 @@ int CmdHF14AMfCGetSc(const char *Cmd) { int CmdHF14AMfCSave(const char *Cmd) { FILE * f; - char filename[20]; + char filename[255]; char * fnameptr = filename; uint8_t fillFromEmulator = 0; uint8_t buf[64]; @@ -1893,7 +1893,7 @@ int CmdHF14AMfCSave(const char *Cmd) { return 0; } else { len = strlen(Cmd); - if (len > 14) len = 14; + if (len > 250) len = 250; if (len < 1) { // get filename diff --git a/client/mifarehost.c b/client/mifarehost.c index cda884d9..358799cb 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -216,7 +216,7 @@ int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { UsbCommand c = {CMD_MIFARE_EML_MEMGET, {blockNum, blocksCount, 0}}; SendCommand(&c); - UsbCommand resp; + UsbCommand resp; if (!WaitForResponseTimeout(CMD_ACK,&resp,1500)) return 1; memcpy(data, resp.d.asBytes, blocksCount * 16); return 0; -- 2.39.5 From a0bf7ba787ea7b309d034e1d5412a7d63b1c2fa3 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 13 Nov 2014 22:13:46 +0100 Subject: [PATCH 13/16] FIX: The hf mf ekeyprn defaults to print all 40 sectorblocks of keys. (ie 4K card). Now its optional 1K / 4K and defaults to 1K. --- client/cmdhfmf.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 4b620275..10c56cdc 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1592,13 +1592,32 @@ int CmdHF14AMfECFill(const char *Cmd) int CmdHF14AMfEKeyPrn(const char *Cmd) { int i; + uint8_t numSectors; uint8_t data[16]; uint64_t keyA, keyB; + if (param_getchar(Cmd, 0) == 'h' || param_getchar(Cmd, 0)== 0x00) { + PrintAndLog("It prints the keys loaded in the emulator memory"); + PrintAndLog("Usage: hf mf ekeyprn [card memory]"); + PrintAndLog(" [card memory]: 1 = 1K (default), 4 = 4K"); + PrintAndLog(""); + PrintAndLog(" sample: hf mf ekeyprn 1"); + return 0; + } + + char cmdp = param_getchar(Cmd, 0); + + switch (cmdp) { + case '1' : + case '\0': numSectors = 16; break; + case '4' : numSectors = 40; break; + default: numSectors = 16; + } + PrintAndLog("|---|----------------|----------------|"); PrintAndLog("|sec|key A |key B |"); PrintAndLog("|---|----------------|----------------|"); - for (i = 0; i < 40; i++) { + for (i = 0; i < numSectors; i++) { if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1)) { PrintAndLog("error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); break; -- 2.39.5 From 85578fcd4e4c1ef9df478722d4fedc42f5c02d31 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 13 Nov 2014 22:21:39 +0100 Subject: [PATCH 14/16] FIX: since the "hf mf ecfill" command supports 0,1,2,4 in card sizes, I consequently changed "hf mf ekeyprn" --- client/cmdhfmf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 10c56cdc..1519c201 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1596,10 +1596,10 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) uint8_t data[16]; uint64_t keyA, keyB; - if (param_getchar(Cmd, 0) == 'h' || param_getchar(Cmd, 0)== 0x00) { + if (param_getchar(Cmd, 0) == 'h') { PrintAndLog("It prints the keys loaded in the emulator memory"); PrintAndLog("Usage: hf mf ekeyprn [card memory]"); - PrintAndLog(" [card memory]: 1 = 1K (default), 4 = 4K"); + PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); PrintAndLog(""); PrintAndLog(" sample: hf mf ekeyprn 1"); return 0; @@ -1607,12 +1607,14 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) char cmdp = param_getchar(Cmd, 0); - switch (cmdp) { + switch (ctmp) { + case '0' : numSectors = 5; break; case '1' : case '\0': numSectors = 16; break; + case '2' : numSectors = 32; break; case '4' : numSectors = 40; break; default: numSectors = 16; - } + } PrintAndLog("|---|----------------|----------------|"); PrintAndLog("|sec|key A |key B |"); -- 2.39.5 From b22f7a6bc68319533f9c83c24045d4bd79994f1f Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 13 Nov 2014 22:23:30 +0100 Subject: [PATCH 15/16] FIX: Minor correction of variablename. don't even ask. --- client/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 1519c201..901ccd49 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1607,7 +1607,7 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) char cmdp = param_getchar(Cmd, 0); - switch (ctmp) { + switch (cmdp) { case '0' : numSectors = 5; break; case '1' : case '\0': numSectors = 16; break; -- 2.39.5 From bd5d0f07e9330f18088f626bc1d18857aeaeaa8e Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 14 Nov 2014 13:24:45 +0100 Subject: [PATCH 16/16] REN: renamed tnp3.lua to tnp3dump.lua since that name is more explainatory ADD: added tnp3sim.lua a script which loads a dump and sends it to the pm3 device memory. --- client/cmdhfmf.c | 1 + client/scripts/test.lua | 10 - client/scripts/{tnp3.lua => tnp3dump.lua} | 17 +- client/scripts/tnp3sim.lua | 383 ++++++++++++++++++++++ 4 files changed, 392 insertions(+), 19 deletions(-) delete mode 100644 client/scripts/test.lua rename client/scripts/{tnp3.lua => tnp3dump.lua} (94%) create mode 100644 client/scripts/tnp3sim.lua diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 901ccd49..35bf2a00 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1623,6 +1623,7 @@ int CmdHF14AMfEKeyPrn(const char *Cmd) if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1)) { PrintAndLog("error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); break; + break; } keyA = bytes_to_num(data, 6); keyB = bytes_to_num(data + 10, 6); diff --git a/client/scripts/test.lua b/client/scripts/test.lua deleted file mode 100644 index 76adc985..00000000 --- a/client/scripts/test.lua +++ /dev/null @@ -1,10 +0,0 @@ -local foo = "This shows how to use some standard libraries" -print(foo) -local answer -repeat - io.write("Continue with this operation (y/n)? ") - io.flush() - answer=io.read() -until answer=="y" or answer=="n" -local x = "Ok then, %s" -print (x:format("whatever")) \ No newline at end of file diff --git a/client/scripts/tnp3.lua b/client/scripts/tnp3dump.lua similarity index 94% rename from client/scripts/tnp3.lua rename to client/scripts/tnp3dump.lua index 77de766f..520161b9 100644 --- a/client/scripts/tnp3.lua +++ b/client/scripts/tnp3dump.lua @@ -8,16 +8,16 @@ local dumplib = require('html_dumplib') local toyNames = require('default_toys') example =[[ - 1. script run tnp3 - 2. script run tnp3 -n - 3. script run tnp3 -k aabbccddeeff - 4. script run tnp3 -k aabbccddeeff -n - 5. script run tnp3 -o myfile - 6. script run tnp3 -n -o myfile - 7. script run tnp3 -k aabbccddeeff -n -o myfile + 1. script run tnp3dump + 2. script run tnp3dump -n + 3. script run tnp3dump -k aabbccddeeff + 4. script run tnp3dump -k aabbccddeeff -n + 5. script run tnp3dump -o myfile + 6. script run tnp3dump -n -o myfile + 7. script run tnp3dump -k aabbccddeeff -n -o myfile ]] author = "Iceman" -usage = "script run tnp3 -k -n -o " +usage = "script run tnp3dump -k -n -o " desc =[[ This script will try to dump the contents of a Mifare TNP3xxx card. It will need a valid KeyA in order to find the other keys and decode the card. @@ -216,7 +216,6 @@ local function main(args) local hex = utils.ConvertAsciiToBytes(aestest) hex = utils.ConvertBytesToHex(hex) - --local _,hex = bin.unpack(("H%d"):format(16),aestest) -- blocks with zero not encrypted. if string.find(blockdata, '^0+$') then diff --git a/client/scripts/tnp3sim.lua b/client/scripts/tnp3sim.lua new file mode 100644 index 00000000..ce772022 --- /dev/null +++ b/client/scripts/tnp3sim.lua @@ -0,0 +1,383 @@ +local cmds = require('commands') +local getopt = require('getopt') +local bin = require('bin') +local lib14a = require('read14a') +local utils = require('utils') +local md5 = require('md5') +local toyNames = require('default_toys') + +example =[[ + 1. script run tnp3sim + 2. script run tnp3sim -m + 3. script run tnp3sim -m -i myfile +]] +author = "Iceman" +usage = "script run tnp3sim -h -m -i " +desc =[[ +This script will try to dump the contents of a Mifare TNP3xxx card. +It will need a valid KeyA in order to find the other keys and decode the card. +Arguments: + -h : this help + -m : Maxed out item + -i : filename for the datadump to read (bin) +]] + +local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' + +local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds +local DEBUG = true -- the debug flag +--- +-- A debug printout-function +function dbg(args) + if not DEBUG then + return + end + + if type(args) == "table" then + local i = 1 + while result[i] do + dbg(result[i]) + i = i+1 + end + else + print("###", args) + end +end +--- +-- This is only meant to be used when errors occur +function oops(err) + print("ERROR: ",err) +end +--- +-- Usage help +function help() + print(desc) + print("Example usage") + print(example) +end +-- +-- Exit message +function ExitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function writedumpfile(infile) + t = infile:read("*all") + len = string.len(t) + local len,hex = bin.unpack(("H%d"):format(len),t) + return hex +end +-- blocks with data +-- there are two dataareas, in block 8 or block 36, ( 1==8 , +-- checksum type = 0, 1, 2, 3 +local function GetCheckSum(blocks, dataarea, chksumtype) + + local crc + local area = 36 + if dataarea == 1 then + area = 8 + end + + if chksumtype == 0 then + crc = blocks[1]:sub(29,32) + elseif chksumtype == 1 then + crc = blocks[area]:sub(29,32) + elseif chksumtype == 2 then + crc = blocks[area]:sub(25,28) + elseif chksumtype == 3 then + crc = blocks[area]:sub(21,24) + end + return utils.SwapEndianness(crc,16) +end + +local function SetCheckSum(blocks, chksumtype) + + if blocks == nil then return nil, 'Argument \"blocks\" nil' end + local newcrc + local area1 = 8 + local area2 = 36 + + if chksumtype == 0 then + newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0)) + blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2) + elseif chksumtype == 1 then + newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1)) + blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2) + newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1)) + blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2) + elseif chksumtype == 2 then + newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2)) + blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32) + newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2)) + blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32) + elseif chksumtype == 3 then + newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3)) + blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32) + newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3)) + blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32) + end +end + +function CalcCheckSum(blocks, dataarea, chksumtype) + local area = 36 + if dataarea == 1 then + area = 8 + end + + if chksumtype == 0 then + data = blocks[0]..blocks[1]:sub(1,28) + elseif chksumtype == 1 then + data = blocks[area]:sub(1,28)..'0500' + elseif chksumtype == 2 then + data = blocks[area+1]..blocks[area+2]..blocks[area+4] + elseif chksumtype == 3 then + data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0) + end + return utils.Crc16(data) +end + +local function ValidateCheckSums(blocks) + + local isOk, crc, calc + -- Checksum Type 0 + crc = GetCheckSum(blocks,1,0) + calc = CalcCheckSum(blocks, 1, 0) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk)) + + -- Checksum Type 1 (DATAAREAHEADER 1) + crc = GetCheckSum(blocks,1,1) + calc = CalcCheckSum(blocks,1,1) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk)) + + -- Checksum Type 1 (DATAAREAHEADER 2) + crc = GetCheckSum(blocks,2,1) + calc = CalcCheckSum(blocks,2,1) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk)) + + -- Checksum Type 2 (DATAAREA 1) + crc = GetCheckSum(blocks,1,2) + calc = CalcCheckSum(blocks,1,2) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk)) + + -- Checksum Type 2 (DATAAREA 2) + crc = GetCheckSum(blocks,2,2) + calc = CalcCheckSum(blocks,2,2) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk)) + + -- Checksum Type 3 (DATAAREA 1) + crc = GetCheckSum(blocks,1,3) + calc = CalcCheckSum(blocks,1,3) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk)) + + -- Checksum Type 3 (DATAAREA 2) + crc = GetCheckSum(blocks,2,3) + calc = CalcCheckSum(blocks,2,3) + if crc == calc then isOk='Ok' else isOk = 'Error' end + io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk)) +end + +-- function EncryptData() + -- local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' + -- if blockNo%4 ~= 3 then + -- if blockNo < 8 then + -- -- Block 0-7 not encrypted + -- blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) + -- else + -- local base = ('%s%s%02x%s'):format(block0, block1, blockNo, HASHCONSTANT) + -- local baseStr = utils.ConvertHexToAscii(base) + -- local md5hash = md5.sumhexa(baseStr) + -- local aestest = core.aes(md5hash, blockdata) + + -- local hex = utils.ConvertAsciiToBytes(aestest) + -- hex = utils.ConvertBytesToHex(hex) + -- --local _,hex = bin.unpack(("H%d"):format(16),aestest) + + -- -- blocks with zero not encrypted. + -- if string.find(blockdata, '^0+$') then + -- blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata) + -- else + -- blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex) + -- io.write( blockNo..',') + -- end + -- end + -- else + -- -- Sectorblocks, not encrypted + -- blocks[blockNo+1] = ('%02d :: %s%s'):format(blockNo,key,blockdata:sub(13,32)) + -- end + +-- end + +local function LoadEmulator(blocks) + local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' + local cmd + local blockdata + for _,b in pairs(blocks) do + + blockdata = b + + if _%4 ~= 3 then + if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then + local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , HASHCONSTANT) + local baseStr = utils.ConvertHexToAscii(base) + local key = md5.sumhexa(baseStr) + local enc = core.aes(key, blockdata) + local hex = utils.ConvertAsciiToBytes(enc) + hex = utils.ConvertBytesToHex(hex) + + blockdata = hex + io.write( _..',') + end + end + + cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata} + local err = core.SendCommand(cmd:getBytes()) + if err then + return err + end + end + io.write('\n') +end + +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + + local result, err, hex + local maxed = false + local inputTemplate = "dumpdata.bin" + local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M"); + + -- Arguments for the script + for o, a in getopt.getopt(args, 'hmi:o:') do + if o == "h" then return help() end + if o == "m" then maxed = true end + if o == "o" then outputTemplate = a end + if o == "i" then inputTemplate = a end + end + + -- Turn off Debug + local cmdSetDbgOff = "hf mf dbg 0" + core.console( cmdSetDbgOff) + + -- Look for tag present on reader, + result, err = lib14a.read1443a(false) + if not result then return oops(err) end + + core.clearCommandBuffer() + + if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx + return oops('This is not a TNP3xxx tag. aborting.') + end + + -- Show tag info + print((' Found tag : %s'):format(result.name)) + + -- Load dump.bin file + print( (' Load data from %s'):format(inputTemplate)) + hex, err = utils.ReadDumpFile(inputTemplate) + if not hex then return oops(err) end + + local blocks = {} + local blockindex = 0 + for i = 1, #hex, 32 do + blocks[blockindex] = hex:sub(i,i+31) + blockindex = blockindex + 1 + end + + if DEBUG then + print('Validating checksums in the loaded datadump') + ValidateCheckSums(blocks) + end + + -- + print( string.rep('--',20) ) + print(' Gathering info') + local uid = blocks[0]:sub(1,8) + local itemtype = blocks[1]:sub(1,4) + local cardid = blocks[1]:sub(9,24) + + -- Show info + print( string.rep('--',20) ) + print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) ) + print( (' UID : 0x%s'):format(uid) ) + print( (' CARDID : 0x%s'):format(cardid ) ) + print( string.rep('--',20) ) + + -- lets do something. + -- + local experience = blocks[8]:sub(1,6) + print(('Experience : %d'):format(utils.SwapEndianness(experience,24))) + local money = blocks[8]:sub(7,10) + print(('Money : %d'):format(utils.SwapEndianness(money,16))) + local fairy = blocks[9]:sub(1,8) + --FD0F = Left, FF0F = Right + local path = 'not choosen' + if fairy:sub(2,2) == 'D' then + path = 'Left' + elseif fairy:sub(2,2) == 'F' then + path = 'Right' + end + print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path)) + + local hat = blocks[9]:sub(8,11) + print(('Hat : %d'):format(utils.SwapEndianness(hat,16))) + + --0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100. + local heropoints = blocks[13]:sub(20,23) + print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16))) + + --0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed. + local challenges = blocks[16]:sub(25,32) + print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32))) + + if maxed then + print('Lets try to max out some values') + -- max out money, experience + --print (blocks[8]) + blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32) + blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32) + --print (blocks[8]) + + -- max out hero challenges + --print (blocks[16]) + blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF' + blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF' + --print (blocks[16]) + + -- max out heropoints + --print (blocks[13]) + blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32) + blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32) + --print (blocks[13]) + + -- Update Checksums + print('Updating all checksums') + SetCheckSum(blocks, 3) + SetCheckSum(blocks, 2) + SetCheckSum(blocks, 1) + SetCheckSum(blocks, 0) + + print('Validating all checksums') + ValidateCheckSums(blocks) + end + + --Load dumpdata to emulator memory + if DEBUG then + print('Sending dumpdata to emulator memory') + err = LoadEmulator(blocks) + if err then return oops(err) end + core.clearCommandBuffer() + print('The simulation is now prepared. run \"hf mf sim\" ') + end +end +main(args) \ No newline at end of file -- 2.39.5