fm11rf08s_nonces_with_data: save in JSON file

This commit is contained in:
Philippe Teuwen 2024-10-06 22:03:00 +02:00
commit 080ddc1595
5 changed files with 134 additions and 30 deletions

View file

@ -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:

View file

@ -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", "<fn>", "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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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),