From b915fda392487a876ccc7b0c8b79a1b31ca5e398 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Wed, 7 Jan 2015 22:00:29 +0100 Subject: [PATCH] FIX: a solution for the issue "hf mf esave - always saves 4K" FIX: a solution for the issue "hf eload, esave, cload, save - filepath variable too short" CHG: minor code clean up. ADD: AES / CRC16 for lua. (and tnp3xx scripts.) ADD: tnp3dump.lua script to dump tnp3xx tags. ADD: tnp3sim.lua script to let PM3 imitate an tnp3xx tag. Needs to be tested live --- client/cmddata.c | 25 ++- client/cmdhf.c | 33 +++- client/cmdhf14a.c | 5 +- client/cmdhf14a.h | 2 +- client/cmdhf14b.c | 10 +- client/cmdhflegic.c | 21 ++- client/cmdhfmf.c | 196 +++++++++++++------- client/cmdlf.c | 26 ++- client/cmdlfem4x.c | 16 +- client/cmdlfhitag.c | 56 ++++-- client/cmdmain.c | 2 + client/flasher.c | 3 - client/lualibs/commands.lua | 7 +- client/lualibs/utils.lua | 171 ++++++++++++++++- client/proxmark3.c | 2 +- client/scripting.c | 52 ++++++ client/scripts/tnp3dump.lua | 272 +++++++++++++++++++++++++++ client/scripts/tnp3sim.lua | 355 ++++++++++++++++++++++++++++++++++++ client/util.c | 18 +- 19 files changed, 1138 insertions(+), 134 deletions(-) create mode 100644 client/scripts/tnp3dump.lua create mode 100644 client/scripts/tnp3sim.lua diff --git a/client/cmddata.c b/client/cmddata.c index 3ac8db25..d05f3fa1 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -696,7 +696,7 @@ int CmdFSKdemod(const char *Cmd) //old CmdFSKdemod needs updating int lowLen = sizeof (LowTone) / sizeof (int); int highLen = sizeof (HighTone) / sizeof (int); - int convLen = (highLen > lowLen) ? highLen : lowLen; //if highlen > lowLen then highlen else lowlen + int convLen = (highLen > lowLen) ? highLen : lowLen; uint32_t hi = 0, lo = 0; int i, j; @@ -942,9 +942,16 @@ int CmdTuneSamples(const char *Cmd) int CmdLoad(const char *Cmd) { - FILE *f = fopen(Cmd, "r"); + char filename[FILE_PATH_SIZE] = {0x00}; + int len = 0; + + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + + FILE *f = fopen(filename, "r"); if (!f) { - PrintAndLog("couldn't open '%s'", Cmd); + PrintAndLog("couldn't open '%s'", filename); return 0; } @@ -1250,9 +1257,17 @@ int CmdPlot(const char *Cmd) int CmdSave(const char *Cmd) { - FILE *f = fopen(Cmd, "w"); + char filename[FILE_PATH_SIZE] = {0x00}; + int len = 0; + + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + + + FILE *f = fopen(filename, "w"); if(!f) { - PrintAndLog("couldn't open '%s'", Cmd); + PrintAndLog("couldn't open '%s'", filename); return 0; } int i; diff --git a/client/cmdhf.c b/client/cmdhf.c index 550f8e86..b53742e4 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -47,9 +47,11 @@ int CmdHFTune(const char *Cmd) #define iso14443_CMD_WUPA 0x52 #define iso14443_CMD_SELECT 0x93 #define iso14443_CMD_SELECT_2 0x95 +#define iso14443_CMD_SELECT_3 0x97 #define iso14443_CMD_REQ 0x26 #define iso14443_CMD_READBLOCK 0x30 #define iso14443_CMD_WRITEBLOCK 0xA0 +#define iso14443_CMD_WRITE 0xA2 #define iso14443_CMD_INC 0xC0 #define iso14443_CMD_DEC 0xC1 #define iso14443_CMD_RESTORE 0xC2 @@ -57,6 +59,15 @@ int CmdHFTune(const char *Cmd) #define iso14443_CMD_HALT 0x50 #define iso14443_CMD_RATS 0xE0 +#define iso14443_CMD_AUTH_KEYA 0x60 +#define iso14443_CMD_AUTH_KEYB 0x61 + +#define iso14443_CMD_AUTH_STEP1 0x1A +#define iso14443_CMD_AUTH_STEP2 0xAA +#define iso14443_CMD_AUTH_RESPONSE 0xAF + +#define CHINESE_BACKDOOR_INIT 0x40 +#define CHINESE_BACKDOOR_STEP2 0x43 void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { @@ -76,12 +87,22 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) case iso14443_CMD_REQ: snprintf(exp,size,"REW"); break; case iso14443_CMD_READBLOCK: snprintf(exp,size,"READBLOCK(%d)",cmd[1]); break; case iso14443_CMD_WRITEBLOCK: snprintf(exp,size,"WRITEBLOCK(%d)",cmd[1]); break; + case iso14443_CMD_WRITE: snprintf(exp,size,"WRITE"); break; case iso14443_CMD_INC: snprintf(exp,size,"INC(%d)",cmd[1]); break; case iso14443_CMD_DEC: snprintf(exp,size,"DEC(%d)",cmd[1]); break; case iso14443_CMD_RESTORE: snprintf(exp,size,"RESTORE(%d)",cmd[1]); break; case iso14443_CMD_TRANSFER: snprintf(exp,size,"TRANSFER(%d)",cmd[1]); break; case iso14443_CMD_HALT: snprintf(exp,size,"HALT"); break; case iso14443_CMD_RATS: snprintf(exp,size,"RATS"); break; + + case iso14443_CMD_AUTH_KEYA: snprintf(exp,size,"AUTH KEY A"); break; + case iso14443_CMD_AUTH_KEYB: snprintf(exp,size,"AUTH KEY B"); break; + case iso14443_CMD_AUTH_STEP1: snprintf(exp,size,"AUTH REQ NONCE"); break; + case iso14443_CMD_AUTH_STEP2: snprintf(exp,size,"AUTH STEP 2"); break; + case iso14443_CMD_AUTH_RESPONSE: snprintf(exp,size,"AUTH RESPONSE"); break; + + case CHINESE_BACKDOOR_INIT: snprintf(exp,size,"BACKDOOR INIT");break; + case CHINESE_BACKDOOR_STEP2: snprintf(exp,size,"BACKDOOR STEP2");break; default: snprintf(exp,size,"?"); break; } return; @@ -89,7 +110,6 @@ void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { - if(cmdsize > 1 && cmd[0] == ICLASS_CMD_READ) { snprintf(exp,size,"READ(%d)",cmd[1]); @@ -112,7 +132,6 @@ void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } - uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, bool iclass, bool showWaitCycles) { bool isResponse; @@ -178,8 +197,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, bool iclass, bool sho // Rough guess that this is a command from the reader // For iClass the command byte is not part of the CRC ComputeCrc14443(CRC_ICLASS, &frame[1], data_len-3, &b1, &b2); - } - else { + } else { // For other data.. CRC might not be applicable (UPDATE commands etc.) ComputeCrc14443(CRC_ICLASS, frame, data_len-2, &b1, &b2); } @@ -199,7 +217,6 @@ uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, bool iclass, bool sho } } } - } char *crc = crcError ? "!crc" :" "; @@ -207,8 +224,10 @@ uint16_t printTraceLine(uint16_t tracepos, uint8_t* trace, bool iclass, bool sho if(!isResponse) { - if(iclass) annotateIclass(explanation,sizeof(explanation),frame,data_len); - else annotateIso14443a(explanation,sizeof(explanation),frame,data_len); + if(iclass) + annotateIclass(explanation,sizeof(explanation),frame,data_len); + else + annotateIso14443a(explanation,sizeof(explanation),frame,data_len); } int num_lines = (data_len - 1)/16 + 1; diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 40173d83..0298f509 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -111,7 +111,7 @@ const manufactureName manufactureMapping[] = { // get a product description based on the UID // uid[8] tag uid // returns description of the best match -static char* getTagInfo(uint8_t uid) { +char* getTagInfo(uint8_t uid) { int i, best = -1; int len = sizeof(manufactureMapping) / sizeof(manufactureName); @@ -168,6 +168,7 @@ int CmdHF14AReader(const char *Cmd) PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); // Double & triple sized UID, can be mapped to a manufacturer. + // HACK: does this apply for Ultralight cards? if ( card.uidlen > 4 ) { PrintAndLog("MANUFACTURER : %s", getTagInfo(card.uid[0])); } @@ -624,7 +625,7 @@ static void waitCmd(uint8_t iSelect) UsbCommand resp; char *hexout; - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { + if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { recv = resp.d.asBytes; uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; PrintAndLog("received %i octets",iLen); diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index d2e203a1..aa35dec6 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -20,5 +20,5 @@ int CmdHF14AReader(const char *Cmd); int CmdHF14ASim(const char *Cmd); int CmdHF14ASnoop(const char *Cmd); - +char* getTagInfo(uint8_t uid); #endif diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index 7e4cbd00..713959f2 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -23,7 +23,6 @@ #include "cmdhf14b.h" #include "cmdmain.h" - static int CmdHelp(const char *Cmd); int CmdHF14BDemod(const char *Cmd) @@ -146,7 +145,7 @@ demodError: int CmdHF14BList(const char *Cmd) { - uint8_t got[960]; + uint8_t got[TRACE_BUFFER_SIZE]; GetFromBigBuf(got,sizeof(got),0); WaitForResponse(CMD_ACK,NULL); @@ -158,9 +157,8 @@ int CmdHF14BList(const char *Cmd) int prev = -1; for(;;) { - if(i >= 900) { - break; - } + + if(i >= TRACE_BUFFER_SIZE) { break; } bool isResponse; int timestamp = *((uint32_t *)(got+i)); @@ -177,7 +175,7 @@ int CmdHF14BList(const char *Cmd) if(len > 100) { break; } - if(i + len >= 900) { + if(i + len >= TRACE_BUFFER_SIZE) { break; } diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index bf874b62..35ba1f28 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -218,7 +218,24 @@ int CmdLegicRFRead(const char *Cmd) int CmdLegicLoad(const char *Cmd) { - FILE *f = fopen(Cmd, "r"); + char filename[FILE_PATH_SIZE] = {0x00}; + int len = 0; + + if (param_getchar(Cmd, 0) == 'h' || param_getchar(Cmd, 0)== 0x00) { + PrintAndLog("It loads datasamples from the file `filename`"); + PrintAndLog("Usage: hf legic load "); + PrintAndLog(" sample: hf legic load filename"); + return 0; + } + + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) { + PrintAndLog("Filepath too long (was %s bytes), max allowed is %s ", len, FILE_PATH_SIZE); + return 0; + } + memcpy(filename, Cmd, len); + + FILE *f = fopen(filename, "r"); if(!f) { PrintAndLog("couldn't open '%s'", Cmd); return -1; @@ -251,7 +268,7 @@ int CmdLegicSave(const char *Cmd) int requested = 1024; int offset = 0; int delivered = 0; - char filename[1024]; + char filename[FILE_PATH_SIZE]; uint8_t got[1024]; sscanf(Cmd, " %s %i %i", filename, &requested, &offset); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 46a11b56..7ad6e0a1 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -66,8 +66,7 @@ start: if (isOK != 1) return 1; // execute original function from util nonce2key - if (nonce2key(uid, nt, nr, par_list, ks_list, &r_key)) - { + if (nonce2key(uid, nt, nr, par_list, ks_list, &r_key)) { isOK = 2; PrintAndLog("Key not found (lfsr_common_prefix list is null). Nt=%08x", nt); } else { @@ -512,6 +511,7 @@ int CmdHF14AMfDump(const char *Cmd) return 2; } } + fclose(fin); // Read access rights to sectors @@ -629,8 +629,8 @@ int CmdHF14AMfRestore(const char *Cmd) { uint8_t sectorNo,blockNo; uint8_t keyType = 0; - uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t key[6] = {0xFF}; + uint8_t bldata[16] = {0x00}; uint8_t keyA[40][6]; uint8_t keyB[40][6]; uint8_t numSectors; @@ -657,21 +657,14 @@ int CmdHF14AMfRestore(const char *Cmd) return 0; } - if ((fdump = fopen("dumpdata.bin","rb")) == NULL) { - PrintAndLog("Could not find file dumpdata.bin"); - return 1; - } if ((fkeys = fopen("dumpkeys.bin","rb")) == NULL) { PrintAndLog("Could not find file dumpkeys.bin"); - fclose(fdump); return 1; } for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { if (fread(keyA[sectorNo], 1, 6, fkeys) == 0) { PrintAndLog("File reading error (dumpkeys.bin)."); - fclose(fdump); - fclose(fkeys); return 2; } } @@ -679,13 +672,16 @@ int CmdHF14AMfRestore(const char *Cmd) for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { if (fread(keyB[sectorNo], 1, 6, fkeys) == 0) { PrintAndLog("File reading error (dumpkeys.bin)."); - fclose(fdump); - fclose(fkeys); return 2; } } + fclose(fkeys); + if ((fdump = fopen("dumpdata.bin","rb")) == NULL) { + PrintAndLog("Could not find file dumpdata.bin"); + return 1; + } PrintAndLog("Restoring dumpdata.bin to card"); for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { @@ -773,11 +769,15 @@ int CmdHF14AMfNested(const char *Cmd) cmdp = param_getchar(Cmd, 0); blockNo = param_get8(Cmd, 1); ctmp = param_getchar(Cmd, 2); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { PrintAndLog("Key type must be A or B"); return 1; } - if (ctmp != 'A' && ctmp != 'a') keyType = 1; + + if (ctmp != 'A' && ctmp != 'a') + keyType = 1; + if (param_gethex(Cmd, 3, key, 12)) { PrintAndLog("Key must include 12 HEX symbols"); return 1; @@ -791,8 +791,10 @@ int CmdHF14AMfNested(const char *Cmd) PrintAndLog("Target key type must be A or B"); return 1; } - if (ctmp != 'A' && ctmp != 'a') trgKeyType = 1; + if (ctmp != 'A' && ctmp != 'a') + trgKeyType = 1; } else { + switch (cmdp) { case '0': SectorsCnt = 05; break; case '1': SectorsCnt = 16; break; @@ -869,7 +871,6 @@ int CmdHF14AMfNested(const char *Cmd) } } - // nested sectors iterations = 0; PrintAndLog("nested..."); @@ -972,7 +973,7 @@ int CmdHF14AMfChk(const char *Cmd) } FILE * f; - char filename[256]={0}; + char filename[FILE_PATH_SIZE]={0}; char buf[13]; uint8_t *keyBlock = NULL, *p; uint8_t stKeyBlock = 20; @@ -988,7 +989,6 @@ int CmdHF14AMfChk(const char *Cmd) int transferToEml = 0; int createDumpFile = 0; - keyBlock = calloc(stKeyBlock, 6); if (keyBlock == NULL) return 1; @@ -1065,7 +1065,7 @@ int CmdHF14AMfChk(const char *Cmd) keycnt++; } else { // May be a dic file - if ( param_getstr(Cmd, 2 + i,filename) > 255 ) { + if ( param_getstr(Cmd, 2 + i,filename) >= FILE_PATH_SIZE ) { PrintAndLog("File name too long"); free(keyBlock); return 2; @@ -1346,26 +1346,44 @@ int CmdHF14AMfESet(const char *Cmd) int CmdHF14AMfELoad(const char *Cmd) { FILE * f; - char filename[20]; + char filename[FILE_PATH_SIZE]; char *fnameptr = filename; char buf[64]; uint8_t buf8[64]; - int i, len, blockNum; + int i, len, blockNum, numBlocks; + int nameParamNo = 1; memset(filename, 0, sizeof(filename)); memset(buf, 0, sizeof(buf)); - if (param_getchar(Cmd, 0) == 'h' || param_getchar(Cmd, 0)== 0x00) { + char ctmp = param_getchar(Cmd, 0); + + if ( ctmp == 'h' || ctmp == 0x00) { PrintAndLog("It loads emul dump from the file `filename.eml`"); - PrintAndLog("Usage: hf mf eload "); + PrintAndLog("Usage: hf mf eload [card memory] "); + PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog(""); PrintAndLog(" sample: hf mf eload filename"); + PrintAndLog(" hf mf eload 4 filename"); return 0; } - len = strlen(Cmd); - if (len > 14) len = 14; + switch (ctmp) { + case '0' : numBlocks = 5*4; break; + case '1' : + case '\0': numBlocks = 16*4; break; + case '2' : numBlocks = 32*4; break; + case '4' : numBlocks = 256; break; + default: { + numBlocks = 16*4; + nameParamNo = 0; + } + } + + len = param_getstr(Cmd,nameParamNo,filename); + + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd, len); fnameptr += len; sprintf(fnameptr, ".eml"); @@ -1380,14 +1398,16 @@ int CmdHF14AMfELoad(const char *Cmd) blockNum = 0; while(!feof(f)){ memset(buf, 0, sizeof(buf)); + if (fgets(buf, sizeof(buf), f) == NULL) { - if((blockNum == 16*4) || (blockNum == 32*4 + 8*16)) { // supports both old (1K) and new (4K) .eml files) - break; - } + + if (blockNum >= numBlocks) break; + PrintAndLog("File reading error."); fclose(f); return 2; } + if (strlen(buf) < 32){ if(strlen(buf) && feof(f)) break; @@ -1395,6 +1415,7 @@ int CmdHF14AMfELoad(const char *Cmd) fclose(f); return 2; } + for (i = 0; i < 32; i += 2) { sscanf(&buf[i], "%02x", (unsigned int *)&buf8[i / 2]); } @@ -1406,12 +1427,12 @@ int CmdHF14AMfELoad(const char *Cmd) } blockNum++; - if (blockNum >= 32*4 + 8*16) break; + if (blockNum >= numBlocks) break; } fclose(f); - if ((blockNum != 16*4) && (blockNum != 32*4 + 8*16)) { - PrintAndLog("File content error. There must be 64 or 256 blocks."); + if ((blockNum != numBlocks)) { + PrintAndLog("File content error. Got %d must be %d blocks.",blockNum, numBlocks); return 4; } PrintAndLog("Loaded %d blocks from file: %s", blockNum, filename); @@ -1422,45 +1443,70 @@ int CmdHF14AMfELoad(const char *Cmd) int CmdHF14AMfESave(const char *Cmd) { FILE * f; - char filename[20]; + char filename[FILE_PATH_SIZE]; char * fnameptr = filename; uint8_t buf[64]; - int i, j, len; + int i, j, len, numBlocks; + int nameParamNo = 1; memset(filename, 0, sizeof(filename)); memset(buf, 0, sizeof(buf)); - if (param_getchar(Cmd, 0) == 'h') { + char ctmp = param_getchar(Cmd, 0); + + if ( ctmp == 'h' || ctmp == 'H') { PrintAndLog("It saves emul dump into the file `filename.eml` or `cardID.eml`"); - PrintAndLog("Usage: hf mf esave [file name w/o `.eml`]"); + PrintAndLog(" Usage: hf mf esave [card memory] [file name w/o `.eml`]"); + PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog(""); PrintAndLog(" sample: hf mf esave "); - PrintAndLog(" hf mf esave filename"); + PrintAndLog(" hf mf esave 4"); + PrintAndLog(" hf mf esave 4 filename"); return 0; } - len = strlen(Cmd); - if (len > 14) len = 14; + switch (ctmp) { + case '0' : numBlocks = 5*4; break; + case '1' : + case '\0': numBlocks = 16*4; break; + case '2' : numBlocks = 32*4; break; + case '4' : numBlocks = 256; break; + default: { + numBlocks = 16*4; + nameParamNo = 0; + } + } + + len = param_getstr(Cmd,nameParamNo,filename); + + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + // user supplied filename? if (len < 1) { - // get filename + // get filename (UID from memory) if (mfEmlGetMem(buf, 0, 1)) { - PrintAndLog("Cant get block: %d", 0); - return 1; + PrintAndLog("Can\'t get UID from block: %d", 0); + sprintf(filename, "dump.eml"); } for (j = 0; j < 7; j++, fnameptr += 2) - sprintf(fnameptr, "%02x", buf[j]); + sprintf(fnameptr, "%02X", buf[j]); } else { - memcpy(filename, Cmd, len); fnameptr += len; } + // add file extension sprintf(fnameptr, ".eml"); // open file f = fopen(filename, "w+"); + if ( !f ) { + PrintAndLog("Can't open file %s ", filename); + return 1; + } + // put hex - for (i = 0; i < 32*4 + 8*16; i++) { + for (i = 0; i < numBlocks; i++) { if (mfEmlGetMem(buf, i, 1)) { PrintAndLog("Cant get block: %d", i); break; @@ -1471,7 +1517,7 @@ int CmdHF14AMfESave(const char *Cmd) } fclose(f); - PrintAndLog("Saved to file: %s", filename); + PrintAndLog("Saved %d blocks to file: %s", numBlocks, filename); return 0; } @@ -1520,13 +1566,34 @@ 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') { + PrintAndLog("It prints the keys loaded in the emulator memory"); + PrintAndLog("Usage: hf mf ekeyprn [card memory]"); + PrintAndLog(" [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLog(""); + PrintAndLog(" sample: hf mf ekeyprn 1"); + return 0; + } + + char cmdp = param_getchar(Cmd, 0); + + switch (cmdp) { + 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 |"); 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; @@ -1616,15 +1683,15 @@ int CmdHF14AMfCSetBlk(const char *Cmd) int CmdHF14AMfCLoad(const char *Cmd) { FILE * f; - char filename[20]; + char filename[FILE_PATH_SIZE] = {0x00}; char * fnameptr = filename; - char buf[64]; - uint8_t buf8[64]; + char buf[64] = {0x00}; + uint8_t buf8[64] = {0x00}; uint8_t fillFromEmulator = 0; int i, len, blockNum, flags; - memset(filename, 0, sizeof(filename)); - memset(buf, 0, sizeof(buf)); + // memset(filename, 0, sizeof(filename)); + // memset(buf, 0, sizeof(buf)); if (param_getchar(Cmd, 0) == 'h' || param_getchar(Cmd, 0)== 0x00) { PrintAndLog("It loads magic Chinese card (only works with!!!) from the file `filename.eml`"); @@ -1657,7 +1724,7 @@ int CmdHF14AMfCLoad(const char *Cmd) return 0; } else { len = strlen(Cmd); - if (len > 14) len = 14; + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; memcpy(filename, Cmd, len); fnameptr += len; @@ -1702,7 +1769,7 @@ int CmdHF14AMfCLoad(const char *Cmd) } fclose(f); - if (blockNum != 16 * 4){ + if (blockNum != 16 * 4 && blockNum != 32 * 4 + 8 * 16){ PrintAndLog("File content error. There must be 64 blocks"); return 4; } @@ -1780,14 +1847,14 @@ int CmdHF14AMfCGetSc(const char *Cmd) { int CmdHF14AMfCSave(const char *Cmd) { FILE * f; - char filename[20]; + char filename[FILE_PATH_SIZE] = {0x00}; char * fnameptr = filename; uint8_t fillFromEmulator = 0; - uint8_t buf[64]; + uint8_t buf[64] = {0x00}; int i, j, len, flags; - memset(filename, 0, sizeof(filename)); - memset(buf, 0, sizeof(buf)); + // memset(filename, 0, sizeof(filename)); + // memset(buf, 0, sizeof(buf)); if (param_getchar(Cmd, 0) == 'h') { PrintAndLog("It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); @@ -1822,7 +1889,7 @@ int CmdHF14AMfCSave(const char *Cmd) { return 0; } else { len = strlen(Cmd); - if (len > 14) len = 14; + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; if (len < 1) { // get filename @@ -1842,6 +1909,11 @@ int CmdHF14AMfCSave(const char *Cmd) { // open file f = fopen(filename, "w+"); + if (f == NULL) { + PrintAndLog("File not found or locked."); + return 1; + } + // put hex flags = CSETBLOCK_INIT_FIELD + CSETBLOCK_WUPC; for (i = 0; i < 16 * 4; i++) { @@ -2021,9 +2093,9 @@ static command_t CommandTable[] = {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, - {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block into magic Chinese card"}, - {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block from magic Chinese card"}, - {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector from magic Chinese card"}, + {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, + {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, + {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"}, {"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"}, {"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"}, {NULL, NULL, 0, NULL} diff --git a/client/cmdlf.c b/client/cmdlf.c index 41dcca57..572cda6c 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -380,10 +380,8 @@ static void ChkBitstream(const char *str) int i; /* convert to bitstream if necessary */ - for (i = 0; i < (int)(GraphTraceLen / 2); i++) - { - if (GraphBuffer[i] > 1 || GraphBuffer[i] < 0) - { + for (i = 0; i < (int)(GraphTraceLen / 2); i++){ + if (GraphBuffer[i] > 1 || GraphBuffer[i] < 0) { CmdBitstream(str); break; } @@ -556,11 +554,25 @@ int CmdVchDemod(const char *Cmd) int CmdLFfind(const char *Cmd) { int ans=0; - if (!offline){ + char cmdp = param_getchar(Cmd, 0); + + if (strlen(Cmd) > 1 || cmdp == 'h' || cmdp == 'H') { + PrintAndLog("Usage: lf search <0|1>"); + PrintAndLog(" , if not set, try reading data from tag."); + PrintAndLog(""); + PrintAndLog(" sample: lf search"); + PrintAndLog(" : lf search 1"); + return 0; + } + + if (!offline || (cmdp != '1') ){ ans=CmdLFRead(""); - ans=CmdSamples("20000"); + ans=CmdSamples("20000"); + } else if (GraphTraceLen < 1000) { + PrintAndLog("Data in Graphbuffer was too small."); + return 0; } - if (GraphTraceLen<1000) return 0; + PrintAndLog("Checking for known tags:"); ans=Cmdaskmandemod(""); if (ans>0) return 1; diff --git a/client/cmdlfem4x.c b/client/cmdlfem4x.c index 32a0ff7c..e0d88c24 100644 --- a/client/cmdlfem4x.c +++ b/client/cmdlfem4x.c @@ -507,12 +507,12 @@ int CmdEM410xWrite(const char *Cmd) int CmdReadWord(const char *Cmd) { - int Word = 16; //default to invalid word + int Word = -1; //default to invalid word UsbCommand c; sscanf(Cmd, "%d", &Word); - if (Word > 15) { + if ( (Word > 15) | (Word < 0) ) { PrintAndLog("Word must be between 0 and 15"); return 1; } @@ -530,13 +530,13 @@ int CmdReadWord(const char *Cmd) int CmdReadWordPWD(const char *Cmd) { - int Word = 16; //default to invalid word + int Word = -1; //default to invalid word int Password = 0xFFFFFFFF; //default to blank password UsbCommand c; sscanf(Cmd, "%d %x", &Word, &Password); - if (Word > 15) { + if ( (Word > 15) | (Word < 0) ) { PrintAndLog("Word must be between 0 and 15"); return 1; } @@ -565,7 +565,7 @@ int CmdWriteWord(const char *Cmd) return 1; } - PrintAndLog("Writting word %d with data %08X", Word, Data); + PrintAndLog("Writing word %d with data %08X", Word, Data); c.cmd = CMD_EM4X_WRITE_WORD; c.d.asBytes[0] = 0x0; //Normal mode @@ -578,7 +578,7 @@ int CmdWriteWord(const char *Cmd) int CmdWriteWordPWD(const char *Cmd) { - int Word = 8; //default to invalid word + int Word = 16; //default to invalid word int Data = 0xFFFFFFFF; //default to blank data int Password = 0xFFFFFFFF; //default to blank password UsbCommand c; @@ -590,7 +590,7 @@ int CmdWriteWordPWD(const char *Cmd) return 1; } - PrintAndLog("Writting word %d with data %08X and password %08X", Word, Data, Password); + PrintAndLog("Writing word %d with data %08X and password %08X", Word, Data, Password); c.cmd = CMD_EM4X_WRITE_WORD; c.d.asBytes[0] = 0x1; //Password mode @@ -601,8 +601,6 @@ int CmdWriteWordPWD(const char *Cmd) return 0; } - - static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, diff --git a/client/cmdlfhitag.c b/client/cmdlfhitag.c index ab4a2609..549c427c 100644 --- a/client/cmdlfhitag.c +++ b/client/cmdlfhitag.c @@ -29,7 +29,7 @@ size_t nbytes(size_t nbits) { int CmdLFHitagList(const char *Cmd) { - uint8_t got[3000]; + uint8_t got[TRACE_BUFFER_SIZE]; GetFromBigBuf(got,sizeof(got),0); WaitForResponse(CMD_ACK,NULL); @@ -39,11 +39,25 @@ int CmdLFHitagList(const char *Cmd) int i = 0; int prev = -1; + int len = strlen(Cmd); + + char filename[FILE_PATH_SIZE] = { 0x00 }; + FILE* pf = NULL; + + if (len > FILE_PATH_SIZE) + len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + + if (strlen(filename) > 0) { + if ((pf = fopen(filename,"wb")) == NULL) { + PrintAndLog("Error: Could not open file [%s]",filename); + return 1; + } + } for (;;) { - if(i >= 1900) { - break; - } + + if(i >= TRACE_BUFFER_SIZE) { break; } bool isResponse; int timestamp = *((uint32_t *)(got+i)); @@ -68,9 +82,7 @@ int CmdLFHitagList(const char *Cmd) if (len > 100) { break; } - if (i + len >= 1900) { - break; - } + if (i + len >= TRACE_BUFFER_SIZE) { break;} uint8_t *frame = (got+i+9); @@ -103,18 +115,22 @@ int CmdLFHitagList(const char *Cmd) line); -// if (pf) { -// fprintf(pf," +%7d: %3d: %s %s\n", -// (prev < 0 ? 0 : (timestamp - prev)), -// bits, -// (isResponse ? "TAG" : " "), -// line); -// } + if (pf) { + fprintf(pf," +%7d: %3d: %s %s\n", + (prev < 0 ? 0 : (timestamp - prev)), + bits, + (isResponse ? "TAG" : " "), + line); + } prev = timestamp; i += (len + 9); } + if (pf) { + fclose(pf); + PrintAndLog("Recorded activity succesfully written to file: %s", filename); + } return 0; } @@ -126,12 +142,14 @@ int CmdLFHitagSnoop(const char *Cmd) { } int CmdLFHitagSim(const char *Cmd) { + UsbCommand c = {CMD_SIMULATE_HITAG}; - char filename[256] = { 0x00 }; + char filename[FILE_PATH_SIZE] = { 0x00 }; FILE* pf; bool tag_mem_supplied; - - param_getstr(Cmd,0,filename); + int len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); if (strlen(filename) > 0) { if ((pf = fopen(filename,"rb+")) == NULL) { @@ -227,9 +245,9 @@ int CmdLFHitagReader(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, 1, "This help"}, - {"list", CmdLFHitagList, 1, "List Hitag trace history"}, + {"list", CmdLFHitagList, 1, " List Hitag trace history"}, {"reader", CmdLFHitagReader, 1, "Act like a Hitag Reader"}, - {"sim", CmdLFHitagSim, 1, "Simulate Hitag transponder"}, + {"sim", CmdLFHitagSim, 1, " Simulate Hitag transponder"}, {"snoop", CmdLFHitagSnoop, 1, "Eavesdrop Hitag communication"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdmain.c b/client/cmdmain.c index b6517caa..15cb3f98 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -132,9 +132,11 @@ int getCommand(UsbCommand* response) bool WaitForResponseTimeout(uint32_t cmd, UsbCommand* response, size_t ms_timeout) { UsbCommand resp; + if (response == NULL) response = &resp; + // Wait until the command is received for(size_t dm_seconds=0; dm_seconds < ms_timeout/10; dm_seconds++) { diff --git a/client/flasher.c b/client/flasher.c index 2a24ba8f..c273c1f3 100644 --- a/client/flasher.c +++ b/client/flasher.c @@ -52,11 +52,8 @@ void ReceiveCommand(UsbCommand* rxcmd) { while (true) { rxlen = sizeof(UsbCommand) - (prx-prxcmd); if (uart_receive(sp,prx,&rxlen)) { -// printf("received [%zd] bytes\n",rxlen); prx += rxlen; if ((prx-prxcmd) >= sizeof(UsbCommand)) { -// printf("received: "); -// cmd_debug(rxcmd); return; } } diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index e93d0a97..13b9c8e7 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -104,8 +104,11 @@ local _commands = { CMD_MIFARE_EML_MEMSET = 0x0602, CMD_MIFARE_EML_MEMGET = 0x0603, CMD_MIFARE_EML_CARDLOAD = 0x0604, - CMD_MIFARE_EML_CSETBLOCK = 0x0605, - CMD_MIFARE_EML_CGETBLOCK = 0x0606, + + --// magic chinese card commands + CMD_MIFARE_CSETBLOCK = 0x0605, + CMD_MIFARE_CGETBLOCK = 0x0606, + CMD_MIFARE_CIDENT = 0x0607, CMD_SIMULATE_MIFARE_CARD = 0x0610, diff --git a/client/lualibs/utils.lua b/client/lualibs/utils.lua index 3d27d5b6..e84f70ad 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,12 +123,100 @@ local Utils = end, --- -- Convert Byte array to string of hex - ConvertBytes2String = function(bytes) - s = {} + ConvertBytesToHex = 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, + -- Convert byte array to string with ascii + ConvertBytesToAscii = 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, + ConvertHexToBytes = 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, + ConvertAsciiToBytes = 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, + 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 = {} + -- 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/proxmark3.c b/client/proxmark3.c index 8a967c7f..0e2a698c 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -16,7 +16,7 @@ #include #include #include -//#include "proxusb.h" + #include "proxmark3.h" #include "proxgui.h" #include "cmdmain.h" diff --git a/client/scripting.c b/client/scripting.c index 963bb64c..eed5544b 100644 --- a/client/scripting.c +++ b/client/scripting.c @@ -18,6 +18,8 @@ #include "util.h" #include "nonce2key/nonce2key.h" #include "../common/iso15693tools.h" +#include +#include "../common/crc16.h" /** * The following params expected: * UsbCommand c @@ -224,6 +226,54 @@ 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 +} + +static int l_crc16(lua_State *L) +{ + size_t size; + const char *p_str = luaL_checklstring(L, 1, &size); + + uint16_t retval = crc16_ccitt( (uint8_t*) 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. @@ -261,6 +311,8 @@ int set_pm3_libraries(lua_State *L) {"clearCommandBuffer", l_clearCommandBuffer}, {"console", l_CmdConsole}, {"iso15693_crc", l_iso15693_crc}, + {"aes", l_aes}, + {"crc16", l_crc16}, {NULL, NULL} }; diff --git a/client/scripts/tnp3dump.lua b/client/scripts/tnp3dump.lua new file mode 100644 index 00000000..520161b9 --- /dev/null +++ b/client/scripts/tnp3dump.lua @@ -0,0 +1,272 @@ +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 dumplib = require('html_dumplib') +local toyNames = require('default_toys') + +example =[[ + 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 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. +Arguments: + -h : this help + -k : Sector 0 Key A. + -n : Use the nested cmd to find all keys + -o : filename for the saved dumps +]] + +local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20' + +local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds +local DEBUG = false -- 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 readdumpkeys(infile) + t = infile:read("*all") + len = string.len(t) + local len,hex = bin.unpack(("H%d"):format(len),t) + 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) + 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 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) ) + print( string.rep('--',20) ) + + local keyA + local cmd + local err + 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: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. + keyA = keyA or '4b0b20107ccb' + 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 + + 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)) + print(('Using keyA : %s'):format(keyA)) + + --Trying to find the other keys + if useNested then + core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) + end + + core.clearCommandBuffer() + + -- Loading keyfile + print('Loading dumpkeys.bin') + local hex, err = utils.ReadDumpFile(input) + if not hex then + return oops(err) + end + + 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()) + 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} + err = core.SendCommand(cmd:getBytes()) + if err then return oops(err) end + local block1, err = waitCmd() + if err then return oops(err) end + + local key + local pos = 0 + local blockNo + local blocks = {} + + print('Reading card data') + core.clearCommandBuffer() + + -- main loop + io.write('Decrypting blocks > ') + 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 + 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 + local blockdata, err = waitCmd() + if err then return oops(err) end + + 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) + + -- 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 + io.write('\n') + + core.clearCommandBuffer() + + -- Print results + local bindata = {} + local emldata = '' + + for _,s in pairs(blocks) do + local slice = s:sub(8,#s) + local str = utils.ConvertBytesToAscii( + utils.ConvertHexToBytes(slice) + ) + emldata = emldata..slice..'\n' + for c in (str):gmatch('.') do + bindata[#bindata+1] = c + end + end + + -- Write dump to files + 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) + local cardid = block1: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) ) + +end +main(args) \ No newline at end of file diff --git a/client/scripts/tnp3sim.lua b/client/scripts/tnp3sim.lua new file mode 100644 index 00000000..f43dafa2 --- /dev/null +++ b/client/scripts/tnp3sim.lua @@ -0,0 +1,355 @@ +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 load a binary datadump of a Mifare TNP3xxx card. +It vill try to validate all checksums and view some information stored in the dump +For an experimental mode, it tries to manipulate some data. +At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim" + +Arguments: + -h : this help + -m : Maxed out items (experimental) + -i : filename for the datadump to read (bin) +]] + +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 + + +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.\n --> run \"hf mf sim 5 '..uid..'\" <--') + end +end +main(args) \ No newline at end of file diff --git a/client/util.c b/client/util.c index a077aae9..b8d5c316 100644 --- a/client/util.c +++ b/client/util.c @@ -46,12 +46,18 @@ int ukbhit(void) { #endif // log files functions -void AddLogLine(char *fileName, char *extData, char *c) { +void AddLogLine(char *file, char *extData, char *c) { FILE *fLog = NULL; - - fLog = fopen(fileName, "a"); + char filename[FILE_PATH_SIZE] = {0x00}; + int len = 0; + + len = strlen(file); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, file, len); + + fLog = fopen(filename, "a"); if (!fLog) { - printf("Could not append log file %s", fileName); + printf("Could not append log file %s", filename); return; } @@ -103,11 +109,13 @@ void print_hex(const uint8_t * data, const size_t len) } char * sprint_hex(const uint8_t * data, const size_t len) { + + int maxLen = ( len > 1024/3) ? 1024/3 : len; static char buf[1024]; char * tmp = buf; size_t i; - for (i=0; i < len && i < 1024/3; i++, tmp += 3) + for (i=0; i < maxLen; ++i, tmp += 3) sprintf(tmp, "%02x ", data[i]); return buf; -- 2.39.2