diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 03875401c..1e43ff81e 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -330,7 +330,9 @@ int usage_hf14_cload(void){ PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " e load card with data from emulator memory"); - PrintAndLogEx(NORMAL, " load card with data from file"); + PrintAndLogEx(NORMAL, " j load card with data from json file"); + PrintAndLogEx(NORMAL, " b load card with data from binary file"); + PrintAndLogEx(NORMAL, " load card with data from eml file"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " hf mf cload mydump"); PrintAndLogEx(NORMAL, " hf mf cload e"); @@ -2631,12 +2633,21 @@ int CmdHF14AMfCLoad(const char *Cmd) { uint8_t buf8[16] = {0x00}; uint8_t fillFromEmulator = 0; int blockNum, flags = 0; + bool fillFromJson = false; + bool fillFromBin = false; + char fileName[50] = {0}; char ctmp = tolower(param_getchar(Cmd, 0)); - if ( strlen(Cmd) == 1 ) { + if ( param_getlength(Cmd, 0) == 1 ) { if (ctmp == 'h' || ctmp == 0x00) return usage_hf14_cload(); if (ctmp == 'e' ) fillFromEmulator = 1; + if (ctmp == 'j' ) fillFromJson = true; + if (ctmp == 'b' ) fillFromBin = true; } + + if (fillFromJson || fillFromBin) + param_getstr(Cmd, 1, fileName, sizeof(fileName)); + if (fillFromEmulator) { for (blockNum = 0; blockNum < 16 * 4; blockNum += 1) { @@ -2658,10 +2669,19 @@ int CmdHF14AMfCLoad(const char *Cmd) { return 0; } - uint8_t *data = calloc(1, 4096); + size_t maxdatalen = 4096; + uint8_t *data = calloc(1, maxdatalen); size_t datalen = 0; - //int res = loadFile(Cmd, "bin", data, &datalen); - int res = loadFileEML( Cmd, "eml", data, &datalen); + int res = 0; + if (fillFromBin) { + res = loadFile(fileName, "bin", data, &datalen); + } else { + if (fillFromJson) { + res = loadFileJSON(fileName, "json", data, maxdatalen, &datalen); + } else { + res = loadFileEML( Cmd, "eml", data, &datalen); + } + } if ( res ) { free(data); return 1; @@ -2878,6 +2898,7 @@ int CmdHF14AMfCSave(const char *Cmd) { saveFile(filename, "bin", dump, bytes); saveFileEML(filename, "eml", dump, bytes, MFBLOCK_SIZE); + saveFileJSON(filename, "json", jsfCardMemory, dump, bytes); free(dump); return 0; } diff --git a/client/loclass/fileutils.c b/client/loclass/fileutils.c index 44bab0193..36e16469c 100644 --- a/client/loclass/fileutils.c +++ b/client/loclass/fileutils.c @@ -135,9 +135,93 @@ out: return retval; } -int saveFileJSON(const char *preferredName, const char *suffix, uint8_t* data, size_t datalen) { - //stub - for merlokk ;) - return 1; +int saveFileJSON(const char *preferredName, const char *suffix, JSONFileType ftype, uint8_t* data, size_t datalen) { + if ( preferredName == NULL ) return 1; + if ( suffix == NULL ) return 1; + if ( data == NULL ) return 1; + + int retval = 0; + int size = sizeof(char) * (strlen(preferredName) + strlen(suffix) + 10); + char * fileName = calloc(size, sizeof(char)); + int num = 1; + sprintf(fileName,"%s.%s", preferredName, suffix); + while (fileExists(fileName)) { + sprintf(fileName,"%s-%d.%s", preferredName, num, suffix); + num++; + } + + json_t *root = json_object(); + JsonSaveStr(root, "Created", "proxmark3"); + switch(ftype) { + case jsfRaw: + JsonSaveStr(root, "FileType", "raw"); + JsonSaveBufAsHexCompact(root, "raw", data, datalen); + break; + case jsfCardMemory: + JsonSaveStr(root, "FileType", "mfcard"); + for (int i = 0; i < (datalen / 16); i++) { + char path[30] = {0}; + sprintf(path, "$.blocks.%d", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], 16); + + if (i == 0) { + JsonSaveBufAsHexCompact(root, "$.Card.UID", &data[0], 4); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &data[5], 1); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", &data[6], 2); + } + + if (mfIsSectorTrailer(i)) { + char patha[30] = {0}; + sprintf(patha, "$.SectorKeys.%d.KeyA", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, patha, &data[i * 16], 6); + + char pathb[30] = {0}; + sprintf(pathb, "$.SectorKeys.%d.KeyB", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, pathb, &data[i * 16 + 10], 6); + + char pathc[30] = {0}; + uint8_t *adata = &data[i * 16 + 6]; + sprintf(pathc, "$.SectorKeys.%d.AccessConditions", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, pathc, &data[i * 16 + 6], 4); + + memset(path, 0x00, sizeof(path)); + sprintf(path, "$.SectorKeys.%d.AccessConditionsText.block%d", mfSectorNum(i), i - 3); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(0, adata)); + + memset(path, 0x00, sizeof(path)); + sprintf(path, "$.SectorKeys.%d.AccessConditionsText.block%d", mfSectorNum(i), i - 2); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(1, adata)); + + memset(path, 0x00, sizeof(path)); + sprintf(path, "$.SectorKeys.%d.AccessConditionsText.block%d", mfSectorNum(i), i - 1); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(2, adata)); + + memset(path, 0x00, sizeof(path)); + sprintf(path, "$.SectorKeys.%d.AccessConditionsText.block%d", mfSectorNum(i), i); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(3, adata)); + + memset(path, 0x00, sizeof(path)); + sprintf(path, "$.SectorKeys.%d.AccessConditionsText.UserData", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &adata[3], 1); + } + } + break; + } + + int res = json_dump_file(root, fileName, JSON_INDENT(2)); + if (res) { + PrintAndLog("ERROR: can't save the file: %s", fileName); + json_decref(root); + retval = 200; + goto out; + } + PrintAndLog("File `%s` saved.", fileName); + + json_decref(root); + +out: + free(fileName); + return retval; } int loadFile(const char *preferredName, const char *suffix, void* data, size_t* datalen) { @@ -253,10 +337,67 @@ out: return retval; } -int loadFileJSON(const char *preferredName, const char *suffix, void* data, size_t* datalen) { - //stub - for merlokk ;) - datalen = 0; - return 1; +int loadFileJSON(const char *preferredName, const char *suffix, void* data, size_t maxdatalen, size_t* datalen) { + *datalen = 0; + json_t *root; + json_error_t error; + + if ( preferredName == NULL ) return 1; + if ( suffix == NULL ) return 1; + + int retval = 0; + int size = sizeof(char) * (strlen(preferredName) + strlen(suffix) + 10); + char * fileName = calloc(size, sizeof(char)); + sprintf(fileName,"%s.%s", preferredName, suffix); + + root = json_load_file(fileName, 0, &error); + if (!root) { + PrintAndLog("ERROR: json (%s) error on line %d: %s", fileName, error.line, error.text); + retval = 2; + goto out; + } + + if (!json_is_object(root)) { + PrintAndLog("ERROR: Invalid json (%s) format. root must be an object.", fileName); + retval = 3; + goto out; + } + + uint8_t *udata = (uint8_t *)data; + char ctype[100] = {0}; + JsonLoadStr(root, "$.FileType", ctype); + + if (!strcmp(ctype, "raw")) { + JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); + } + + if (!strcmp(ctype, "mfcard")) { + size_t sptr = 0; + for (int i = 0; i < 256; i++) { + if (sptr + 16 > maxdatalen) { + retval = 5; + goto out; + } + + char path[30] = {0}; + sprintf(path, "$.blocks.%d", i); + + size_t len = 0; + JsonLoadBufAsHex(root, path, &udata[sptr], 16, &len); + if (!len) + break; + + sptr += len; + } + + *datalen = sptr; + } + + PrintAndLog("Loaded JSON: (%s) OK.", fileName); +out: + json_decref(root); + free(fileName); + return retval; } #else //if we're on ARM diff --git a/client/loclass/fileutils.h b/client/loclass/fileutils.h index 40bd7cfeb..3852b4bf4 100644 --- a/client/loclass/fileutils.h +++ b/client/loclass/fileutils.h @@ -47,6 +47,13 @@ #include #include #include "../ui.h" +#include "../emv/emvjson.h" +#include "mifare4.h" + +typedef enum { + jsfRaw, + jsfCardMemory, +} JSONFileType; int fileExists(const char *filename); @@ -84,11 +91,12 @@ extern int saveFileEML(const char *preferredName, const char *suffix, uint8_t* d * * @param preferredName * @param suffix the file suffix. Leave out the ".". + * @param ftype type of file. * @param data The binary data to write to the file * @param datalen the length of the data * @return 0 for ok, 1 for failz */ -extern int saveFileJSON(const char *preferredName, const char *suffix, uint8_t* data, size_t datalen); +extern int saveFileJSON(const char *preferredName, const char *suffix, JSONFileType ftype, uint8_t* data, size_t datalen); /** STUB * @brief Utility function to load data from a binary file. This method takes a preferred name. @@ -121,10 +129,11 @@ extern int loadFileEML(const char *preferredName, const char *suffix, void* data * @param preferredName * @param suffix the file suffix. Leave out the ".". * @param data The data array to store the loaded bytes from file + * @param maxdatalen maximum size of data array in bytes * @param datalen the number of bytes loaded from file * @return 0 for ok, 1 for failz */ -extern int loadFileJSON(const char *preferredName, const char *suffix, void* data, size_t* datalen); +extern int loadFileJSON(const char *preferredName, const char *suffix, void* data, size_t maxdatalen, size_t* datalen); #define PrintAndLogDevice(level, format, args...) PrintAndLogEx(level, format , ## args) #else diff --git a/client/mifare4.c b/client/mifare4.c index 39869ab77..0c53a1f9b 100644 --- a/client/mifare4.c +++ b/client/mifare4.c @@ -17,6 +17,52 @@ #include "ui.h" #include "crypto/libpcrypto.h" +AccessConditions_t MFAccessConditions[] = { + {0x00, "rdAB wrAB incAB dectrAB"}, + {0x01, "rdAB dectrAB"}, + {0x02, "rdAB"}, + {0x03, "rdB wrB"}, + {0x04, "rdAB wrB"}, + {0x05, "rdB"}, + {0x06, "rdAB wrB incB dectrAB"}, + {0x07, "none"} +}; + +AccessConditions_t MFAccessConditionsTrailer[] = { + {0x00, "rdAbyA rdCbyA rdBbyA wrBbyA"}, + {0x01, "wrAbyA rdCbyA wrCbyA rdBbyA wrBbyA"}, + {0x02, "rdCbyA rdBbyA"}, + {0x03, "wrAbyB rdCbyAB wrCbyB wrBbyB"}, + {0x04, "wrAbyB rdCbyAB wrBbyB"}, + {0x05, "rdCbyAB wrCbyB"}, + {0x06, "rdCbyAB"}, + {0x07, "rdCbyAB"} +}; + +char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data) { + static char StaticNone[] = "none"; + + uint8_t data1 = ((data[1] >> 4) & 0x0f) >> blockn; + uint8_t data2 = ((data[2]) & 0x0f) >> blockn; + uint8_t data3 = ((data[2] >> 4) & 0x0f) >> blockn; + + uint8_t cond = (data1 & 0x01) << 2 | (data2 & 0x01) << 1 | (data3 & 0x01); + + if (blockn == 3) { + for (int i = 0; i < ARRAYLEN(MFAccessConditionsTrailer); i++) + if (MFAccessConditionsTrailer[i].cond == cond) { + return MFAccessConditionsTrailer[i].description; + } + } else { + for (int i = 0; i < ARRAYLEN(MFAccessConditions); i++) + if (MFAccessConditions[i].cond == cond) { + return MFAccessConditions[i].description; + } + }; + + return StaticNone; +}; + int CalculateEncIVCommand(mf4Session *session, uint8_t *iv, bool verbose) { memcpy(&iv[0], session->TI, 4); memcpy(&iv[4], &session->R_Ctr, 2); diff --git a/client/mifare4.h b/client/mifare4.h index b85d512f1..011567a1e 100644 --- a/client/mifare4.h +++ b/client/mifare4.h @@ -38,9 +38,16 @@ typedef enum { mtypWriteResp, } MACType_t; +typedef struct { + uint8_t cond; + char *description; +} AccessConditions_t; + extern int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose); extern int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose); +extern char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data); + extern uint8_t mfNumBlocksPerSector(uint8_t sectorNo); extern uint8_t mfFirstBlockOfSector(uint8_t sectorNo); extern uint8_t mfSectorTrailer(uint8_t blockNo);