From 080ddc1595edd07a313d8bfd87f13496cc9c00d8 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 6 Oct 2024 22:03:00 +0200 Subject: [PATCH] fm11rf08s_nonces_with_data: save in JSON file --- client/pyscripts/fm11rf08s_recovery.py | 36 +++++++++++++-- client/src/cmdhfmf.c | 47 +++++++++---------- client/src/fileutils.c | 62 +++++++++++++++++++++++++- client/src/fileutils.h | 13 ++++++ include/mifare.h | 6 +++ 5 files changed, 134 insertions(+), 30 deletions(-) diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index bdf5a0009..a3f54bbd9 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -112,12 +112,42 @@ if args.init_check: print("Getting nonces...") cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {BACKDOOR_RF08S}" p.console(cmd) -try: - nt, nt_enc, par_err, data = json.loads(p.grabbed_output) -except json.decoder.JSONDecodeError: +nonces_with_data = "" +for line in p.grabbed_output.split('\n'): + if "Wrong" in line or "error" in line: + print("Error getting nonces, abort.") + exit() + if "Saved" in line: + nonces_with_data = line[line.index("`"):].strip("`") +if (nonces_with_data == ""): print("Error getting nonces, abort.") exit() +try: + with open(nonces_with_data, 'r') as file: + # Load and parse the JSON data + dict_nwd = json.load(file) +except json.decoder.JSONDecodeError: + print(f"Error parsing {nonces_with_data}, abort.") + exit() + +nt = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] +nt_enc = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] +par_err = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] +data = ["" for _ in range(NUM_SECTORS * 4)] +for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): + real_sec = sec + if sec >= NUM_SECTORS: + real_sec += 16 + nt[sec][0] = dict_nwd["nt"][f"{real_sec}"]["a"].lower() + nt[sec][1] = dict_nwd["nt"][f"{real_sec}"]["b"].lower() + nt_enc[sec][0] = dict_nwd["nt_enc"][f"{real_sec}"]["a"].lower() + nt_enc[sec][1] = dict_nwd["nt_enc"][f"{real_sec}"]["b"].lower() + par_err[sec][0] = dict_nwd["par_err"][f"{real_sec}"]["a"] + par_err[sec][1] = dict_nwd["par_err"][f"{real_sec}"]["b"] +for blk in range(NUM_SECTORS * 4): + data[blk] = dict_nwd["blocks"][f"{blk}"] + print("Generating first dump file") dumpfile = f"hf-mf-{uid:08X}-dump.bin" with (open(dumpfile, "wb")) as f: diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 29a9d5f3b..7426a039b 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9757,6 +9757,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"), arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."), arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."), + arg_str0("f", "file", "", "Specify a filename for collected data"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -9827,6 +9828,10 @@ static int CmdHF14AMfISEN(const char *Cmd) { if (collect_fm11rf08s_with_data) { collect_fm11rf08s = 1; } + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 23), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); uint8_t dbg_curr = DBG_NONE; @@ -9881,32 +9886,21 @@ static int CmdHF14AMfISEN(const char *Cmd) { } } uint8_t num_sectors = MIFARE_1K_MAXSECTOR + 1; - PrintAndLogEx(NORMAL, "[\n ["); + iso14a_fm11rf08s_nonces_with_data_t nonces_dump = {0}; for (uint8_t sec = 0; sec < num_sectors; sec++) { - PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s", - (uint32_t) bytes_to_num(resp.data.asBytes + ((sec * 2) * 9), 4), - (uint32_t) bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 9), 4), - sec < num_sectors - 1 ? "," : ""); + memcpy(nonces_dump.nt[sec][0], resp.data.asBytes + ((sec * 2) * 9), 4); + memcpy(nonces_dump.nt[sec][1], resp.data.asBytes + (((sec * 2) + 1) * 9), 4); } - PrintAndLogEx(NORMAL, " ],\n ["); for (uint8_t sec = 0; sec < num_sectors; sec++) { - PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s", - (uint32_t) bytes_to_num(resp.data.asBytes + ((sec * 2) * 9) + 4, 4), - (uint32_t) bytes_to_num(resp.data.asBytes + (((sec * 2) + 1) * 9) + 4, 4), - sec < num_sectors - 1 ? "," : ""); + memcpy(nonces_dump.nt_enc[sec][0], resp.data.asBytes + ((sec * 2) * 9) + 4, 4); + memcpy(nonces_dump.nt_enc[sec][1], resp.data.asBytes + (((sec * 2) + 1) * 9) + 4, 4); } - PrintAndLogEx(NORMAL, " ],\n ["); for (uint8_t sec = 0; sec < num_sectors; sec++) { - uint8_t p0 = resp.data.asBytes[((sec * 2) * 9) + 8]; - uint8_t p1 = resp.data.asBytes[(((sec * 2) + 1) * 9) + 8]; - PrintAndLogEx(NORMAL, " [\"%i%i%i%i\", \"%i%i%i%i\"]%s", - (p0 >> 3) & 1, (p0 >> 2) & 1, (p0 >> 1) & 1, p0 & 1, - (p1 >> 3) & 1, (p1 >> 2) & 1, (p1 >> 1) & 1, p1 & 1, - sec < num_sectors - 1 ? "," : ""); + nonces_dump.par_err[sec][0] = resp.data.asBytes[((sec * 2) * 9) + 8]; + nonces_dump.par_err[sec][1] = resp.data.asBytes[(((sec * 2) + 1) * 9) + 8]; } if (collect_fm11rf08s_with_data) { - PrintAndLogEx(NORMAL, " ],\n ["); - int bytes = MIFARE_1K_MAXSECTOR * 4 * 16; + int bytes = MIFARE_1K_MAXBLOCK * MFBLOCK_SIZE; uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (dump == NULL) { @@ -9918,16 +9912,17 @@ static int CmdHF14AMfISEN(const char *Cmd) { free(dump); return PM3_ETIMEOUT; } - for (uint8_t sec = 0; sec < MIFARE_1K_MAXSECTOR; sec++) { - for (uint8_t b = 0; b < 4; b++) { - PrintAndLogEx(NORMAL, " \"%s\"%s", - sprint_hex_inrow(dump + ((sec * 4) + b) * 16, 16), - (sec == MIFARE_1K_MAXSECTOR - 1) && (b == 3) ? "" : ","); - } + for (uint8_t blk = 0; blk < MIFARE_1K_MAXBLOCK; blk++) { + memcpy(nonces_dump.blocks[blk], dump + blk * MFBLOCK_SIZE, MFBLOCK_SIZE); } free(dump); } - PrintAndLogEx(NORMAL, " ]\n]"); + if (fnlen == 0) { + snprintf(filename, sizeof(filename), "hf-mf-%s-nonces%s", sprint_hex_inrow(card.uid, card.uidlen), collect_fm11rf08s_with_data ? "_with_data" : ""); + } + if (pm3_save_fm11rf08s_nonces(filename, &nonces_dump, collect_fm11rf08s_with_data) != PM3_SUCCESS) { + return PM3_EFAILED; + } return PM3_SUCCESS; } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 9f56dc52e..ece462f9e 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -712,6 +712,52 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, } break; } + case jsfFM11RF08SNonces: + case jsfFM11RF08SNoncesWithData: { + if (datalen != sizeof(iso14a_fm11rf08s_nonces_with_data_t)) { + return PM3_EINVARG; + } + iso14a_fm11rf08s_nonces_with_data_t * p = (iso14a_fm11rf08s_nonces_with_data_t *)data; + if (ftype == jsfFM11RF08SNoncesWithData) { + JsonSaveStr(root, "FileType", "fm11rf08s_nonces_with_data"); + } else { + JsonSaveStr(root, "FileType", "fm11rf08s_nonces"); + } + for (uint16_t sec = 0; sec < MIFARE_1K_MAXSECTOR + 1; sec++) { + uint8_t par2[2]; + uint8_t par; + uint16_t real_sec = sec; + if (sec == MIFARE_1K_MAXSECTOR) { + real_sec = 32; // advanced verification method block + } + snprintf(path, sizeof(path), "$.nt.%u.a", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt[sec][0], 4); + snprintf(path, sizeof(path), "$.nt.%u.b", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt[sec][1], 4); + snprintf(path, sizeof(path), "$.nt_enc.%u.a", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt_enc[sec][0], 4); + snprintf(path, sizeof(path), "$.nt_enc.%u.b", real_sec); + JsonSaveBufAsHexCompact(root, path, p->nt_enc[sec][1], 4); + + snprintf(path, sizeof(path), "$.par_err.%u.a", real_sec); + par = p->par_err[sec][0]; + par2[0] = (((par >> 3) & 1) << 4) | ((par >> 2) & 1); + par2[1] = (((par >> 1) & 1) << 4) | ((par >> 0) & 1); + JsonSaveBufAsHexCompact(root, path, par2, 2); + snprintf(path, sizeof(path), "$.par_err.%u.b", real_sec); + par = p->par_err[sec][1]; + par2[0] = (((par >> 3) & 1) << 4) | ((par >> 2) & 1); + par2[1] = (((par >> 1) & 1) << 4) | ((par >> 0) & 1); + JsonSaveBufAsHexCompact(root, path, par2, 2); + } + if (ftype == jsfFM11RF08SNoncesWithData) { + for (uint16_t blk = 0; blk < MIFARE_1K_MAXBLOCK; blk++) { + snprintf(path, sizeof(path), "$.blocks.%u", blk); + JsonSaveBufAsHexCompact(root, path, p->blocks[blk], MFBLOCK_SIZE); + } + } + break; + } // no action case jsfFido: break; @@ -737,7 +783,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, free(fn); goto out; } - if (verbose) { PrintAndLogEx(SUCCESS, "Saved to json file `" _YELLOW_("%s") "`", fn); } @@ -3111,3 +3156,18 @@ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft) { return PM3_SUCCESS; } +int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data) { + + if (fn == NULL || d == NULL) { + PrintAndLogEx(INFO, "No data to save, skipping..."); + return PM3_EINVARG; + } + + if (with_data) { + saveFileJSON(fn, jsfFM11RF08SNoncesWithData, (uint8_t *)d, sizeof(*d), NULL); + } else { + saveFileJSON(fn, jsfFM11RF08SNonces, (uint8_t *)d, sizeof(*d), NULL); + } + return PM3_SUCCESS; +} + diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 98e517b27..876bff44e 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -72,6 +72,8 @@ typedef enum { jsfLto, jsfCryptorf, jsfNDEF, + jsfFM11RF08SNonces, + jsfFM11RF08SNoncesWithData } JSONFileType; typedef enum { @@ -346,4 +348,15 @@ int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); * @return PM3_SUCCESS if OK */ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); + +/** STUB + * @brief Utility function to save FM11RF08S recovery data. + * + * @param fn + * @param d iso14a_fm11rf08s_nonces_with_data_t structure + * @param n the length of the structure + * @param with_data does the structure contain data blocks? + * @return PM3_SUCCESS if OK + */ +int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data); #endif // FILEUTILS_H diff --git a/include/mifare.h b/include/mifare.h index cd24a5f4f..c9685e29e 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -84,6 +84,12 @@ typedef struct { uint8_t *dump; } iso14a_mf_dump_ev1_t; +typedef struct { + uint8_t nt[17][2][4]; + uint8_t nt_enc[17][2][4]; + uint8_t par_err[17][2]; + uint8_t blocks[64][16]; // [MIFARE_1K_MAXSECTOR * 4][MFBLOCK_SIZE] +} iso14a_fm11rf08s_nonces_with_data_t; typedef enum ISO14A_COMMAND { ISO14A_CONNECT = (1 << 0),