Skip to content

Commit de86cd8

Browse files
committed
Added support for dumping FM11RF08S data at once
1 parent 258e289 commit de86cd8

File tree

6 files changed

+110
-27
lines changed

6 files changed

+110
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ All notable changes to this project will be documented in this file.
33
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
44

55
## [unreleased][unreleased]
6-
- Added support for collecting all fm11rf08s nT/{nT}/par_err at once (@doegox)
6+
- Added support for dumping FM11RF08S data at once (@doegox)
7+
- Added support for collecting all FM11RF08S nT/{nT}/par_err at once (@doegox)
78
- Fixed `hf mfu wrbl` - compatibility write only writes 4 bytes. Now handled correctly (@iceman1001)
89
- Changed `hf mfu info` - better magic tag detection (@iceman1001)
910
- Added ELECTRA pattern decoding in `lf search` (@CiRIP)

armsrc/appmain.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1751,7 +1751,7 @@ static void PacketReceived(PacketCommandNG *packet) {
17511751
break;
17521752
}
17531753
case CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES: {
1754-
MifareAcquireStaticEncryptedNonces(packet->data.asBytes);
1754+
MifareAcquireStaticEncryptedNonces(packet->oldarg[0], packet->data.asBytes);
17551755
break;
17561756
}
17571757
case CMD_HF_MIFARE_ACQ_NONCES: {

armsrc/mifarecmd.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
10361036
// acquire static encrypted nonces in order to perform the attack described in
10371037
// Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant"
10381038
//-----------------------------------------------------------------------------
1039-
void MifareAcquireStaticEncryptedNonces(uint8_t *key) {
1039+
void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key) {
10401040

10411041
struct Crypto1State mpcs = {0, 0};
10421042
struct Crypto1State *pcs;
@@ -1048,6 +1048,7 @@ void MifareAcquireStaticEncryptedNonces(uint8_t *key) {
10481048
uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00};
10491049

10501050
uint64_t ui64Key = bytes_to_num(key, 6);
1051+
bool with_data = flags & 1;
10511052
uint32_t cuid = 0;
10521053
int16_t isOK = PM3_SUCCESS;
10531054
uint16_t num_nonces = 0;
@@ -1108,6 +1109,20 @@ void MifareAcquireStaticEncryptedNonces(uint8_t *key) {
11081109
isOK = PM3_ESOFT;
11091110
goto out;
11101111
};
1112+
if (with_data) {
1113+
uint8_t data[16];
1114+
for (uint8_t tb = blockNo; tb < blockNo + 4; tb++) {
1115+
memset(data, 0x00, sizeof(data));
1116+
int res = mifare_classic_readblock(pcs, tb, data);
1117+
if (res == 1) {
1118+
if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireStaticEncryptedNonces: Read error");
1119+
isOK = PM3_ESOFT;
1120+
goto out;
1121+
}
1122+
emlSetMem_xt(data, tb, 1, 16);
1123+
}
1124+
}
1125+
11111126
// nested authentication
11121127
uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + keyType + 4, blockNo, receivedAnswer, par_enc, NULL);
11131128
if (len != 4) {

armsrc/mifarecmd.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
3737
void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key);
3838

3939
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
40-
void MifareAcquireStaticEncryptedNonces(uint8_t *key);
40+
void MifareAcquireStaticEncryptedNonces(uint32_t flags, uint8_t *key);
4141
void MifareAcquireNonces(uint32_t arg0, uint32_t flags);
4242
void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem);
4343
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);

client/pyscripts/fm11rf08s_recovery.py

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,37 @@ def print_key(sec, key_type, key):
103103
print_key(sec, 1, found_keys[sec][1])
104104

105105
print("Getting nonces...")
106-
cmd = f"hf mf isen --collect_fm11rf08s --key {BACKDOOR_RF08S}"
106+
cmd = f"hf mf isen --collect_fm11rf08s_with_data --key {BACKDOOR_RF08S}"
107107
p.console(cmd)
108108
try:
109-
nt, nt_enc, par_err = json.loads(p.grabbed_output)
109+
nt, nt_enc, par_err, data = json.loads(p.grabbed_output)
110110
except json.decoder.JSONDecodeError:
111111
print("Error getting nonces, abort.")
112112
exit()
113113

114+
print("Generating first dump file")
115+
dumpfile = f"hf-mf-{uid:08X}-dump.bin"
116+
with (open(dumpfile, "wb")) as f:
117+
for sec in range(NUM_SECTORS):
118+
for b in range(4):
119+
d = data[(sec * 4) + b]
120+
if b == 3:
121+
ka = found_keys[sec][0]
122+
kb = found_keys[sec][1]
123+
if ka == "":
124+
ka = "FFFFFFFFFFFF"
125+
if kb == "":
126+
kb = "FFFFFFFFFFFF"
127+
d = ka + d[12:20] + kb
128+
f.write(bytes.fromhex(d))
129+
print(f"Data have been dumped to `{dumpfile}`")
130+
131+
elapsed_time1 = time.time() - start_time
132+
minutes = int(elapsed_time1 // 60)
133+
seconds = int(elapsed_time1 % 60)
134+
print("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
135+
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
136+
114137
if os.path.isfile(DICT_DEF_PATH):
115138
print(f"Loading {DICT_DEF}")
116139
with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file:
@@ -259,11 +282,11 @@ def print_key(sec, key_type, key):
259282
print(f" {sec:03} | {sec*4+3:03} | {candidates[sec][0]:6} | {candidates[sec][1]:6} ")
260283
total_candidates = sum(candidates[sec][0] + candidates[sec][1] for sec in range(NUM_SECTORS))
261284

262-
elapsed_time = time.time() - start_time
263-
minutes1 = int(elapsed_time // 60)
264-
seconds1 = int(elapsed_time % 60)
265-
print("----Step 1: " + color(f"{minutes1:2}", fg="yellow") + " minutes " +
266-
color(f"{seconds1:2}", fg="yellow") + " seconds -----------")
285+
elapsed_time2 = time.time() - start_time - elapsed_time1
286+
minutes = int(elapsed_time2 // 60)
287+
seconds = int(elapsed_time2 % 60)
288+
print("----Step 2: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
289+
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
267290

268291
# fchk: 147 keys/s. Correct key found after 50% of candidates on average
269292
FCHK_KEYS_S = 147
@@ -272,7 +295,6 @@ def print_key(sec, key_type, key):
272295
seconds = int(foreseen_time % 60)
273296
print("Still about " + color(f"{minutes:2}", fg="yellow") + " minutes " +
274297
color(f"{seconds:2}", fg="yellow") + " seconds to run...")
275-
start_time = time.time()
276298

277299
abort = False
278300
print("Brute-forcing keys... Press any key to interrupt")
@@ -437,11 +459,31 @@ def print_key(sec, key_type, key):
437459
if unknown:
438460
print("[" + color("=", fg="yellow") + "] --[ " + color("FFFFFFFFFFFF", fg="yellow") +
439461
" ]-- has been inserted for unknown keys")
462+
print(plus + "Generating final dump file")
463+
dumpfile = f"hf-mf-{uid:08X}-dump.bin"
464+
with (open(dumpfile, "wb")) as f:
465+
for sec in range(NUM_SECTORS):
466+
for b in range(4):
467+
d = data[(sec * 4) + b]
468+
if b == 3:
469+
ka = found_keys[sec][0]
470+
kb = found_keys[sec][1]
471+
if ka == "":
472+
ka = "FFFFFFFFFFFF"
473+
if kb == "":
474+
kb = "FFFFFFFFFFFF"
475+
d = ka + d[12:20] + kb
476+
f.write(bytes.fromhex(d))
477+
print(plus + "Data have been dumped to `" + color(dumpfile, fg="yellow")+"`")
478+
479+
elapsed_time3 = time.time() - start_time - elapsed_time1 - elapsed_time2
480+
minutes = int(elapsed_time3 // 60)
481+
seconds = int(elapsed_time3 % 60)
482+
print("----Step 3: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
483+
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
440484

441485
elapsed_time = time.time() - start_time
442-
minutes2 = int(elapsed_time // 60)
443-
seconds2 = int(elapsed_time % 60)
444-
print("----Step 2: " + color(f"{minutes2:2}", fg="yellow") + " minutes " +
445-
color(f"{seconds2:2}", fg="yellow") + " seconds -----------")
446-
print("---- TOTAL: " + color(f"{minutes1+minutes2:2}", fg="yellow") + " minutes " +
447-
color(f"{seconds1+seconds2:2}", fg="yellow") + " seconds -----------")
486+
minutes = int(elapsed_time // 60)
487+
seconds = int(elapsed_time % 60)
488+
print("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
489+
color(f"{seconds:2}", fg="yellow") + " seconds -----------")

client/src/cmdhfmf.c

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9727,7 +9727,10 @@ static int CmdHF14AMfISEN(const char *Cmd) {
97279727
arg_lit0(NULL, "incblk2", "auth(blk)-auth(blk2)-auth(blk2+4)-..."),
97289728
arg_lit0(NULL, "corruptnrar", "corrupt {nR}{aR}, but with correct parity"),
97299729
arg_lit0(NULL, "corruptnrarparity", "correct {nR}{aR}, but with corrupted parity"),
9730-
arg_lit0(NULL, "collect_fm11rf08s", "correct all nT/{nT}/par_err of FM11RF08S in JSON. Option to be used only with -k."),
9730+
arg_rem("", ""),
9731+
arg_rem("FM11RF08S specific options:", "Incompatible with above options, except -k; output in JSON"),
9732+
arg_lit0(NULL, "collect_fm11rf08s", "collect all nT/{nT}/par_err."),
9733+
arg_lit0(NULL, "collect_fm11rf08s_with_data", "collect all nT/{nT}/par_err and data blocks."),
97319734
arg_param_end
97329735
};
97339736
CLIExecWithReturn(ctx, Cmd, argtable, true);
@@ -9793,7 +9796,11 @@ static int CmdHF14AMfISEN(const char *Cmd) {
97939796
bool incblk2 = arg_get_lit(ctx, 16);
97949797
bool corruptnrar = arg_get_lit(ctx, 17);
97959798
bool corruptnrarparity = arg_get_lit(ctx, 18);
9796-
bool collect_fm11rf08s = arg_get_lit(ctx, 19);
9799+
bool collect_fm11rf08s = arg_get_lit(ctx, 21);
9800+
bool collect_fm11rf08s_with_data = arg_get_lit(ctx, 22);
9801+
if (collect_fm11rf08s_with_data) {
9802+
collect_fm11rf08s = 1;
9803+
}
97979804
CLIParserFree(ctx);
97989805

97999806
uint8_t dbg_curr = DBG_NONE;
@@ -9840,14 +9847,14 @@ static int CmdHF14AMfISEN(const char *Cmd) {
98409847
}
98419848

98429849
if (collect_fm11rf08s) {
9843-
SendCommandNG(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, key, sizeof(key));
9850+
uint32_t flags = collect_fm11rf08s_with_data;
9851+
SendCommandMIX(CMD_HF_MIFARE_ACQ_STATIC_ENCRYPTED_NONCES, flags, 0, 0, key, sizeof(key));
98449852
if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) {
98459853
if (resp.status == PM3_ESOFT) {
98469854
return NONCE_FAIL;
98479855
}
98489856
}
98499857
uint8_t num_sectors = 16;
9850-
// TODO: get nonces and display
98519858
PrintAndLogEx(NORMAL, "[\n [");
98529859
for (uint8_t sec = 0; sec < num_sectors; sec++) {
98539860
PrintAndLogEx(NORMAL, " [\"%08x\", \"%08x\"]%s",
@@ -9871,12 +9878,30 @@ static int CmdHF14AMfISEN(const char *Cmd) {
98719878
(p1 >> 3) & 1, (p1 >> 2) & 1, (p1 >> 1) & 1, p1 & 1,
98729879
sec < num_sectors - 1 ? "," : "");
98739880
}
9874-
PrintAndLogEx(NORMAL, " ]\n]");
9881+
if (collect_fm11rf08s_with_data) {
9882+
PrintAndLogEx(NORMAL, " ],\n [");
9883+
int bytes = num_sectors * 4 * 16;
98759884

9876-
// PrintAndLogEx(SUCCESS, " nT: " _GREEN_("%s"), sprint_hex(resp.data.asBytes + (((sec * 2) + keyType) * 9), 4));
9877-
// PrintAndLogEx(SUCCESS, " {nT}: " _GREEN_("%s"), sprint_hex(resp.data.asBytes + (((sec * 2) + keyType) * 9) + 4, 4));
9878-
// // TODO: wrong par:
9879-
// PrintAndLogEx(SUCCESS, " par: " _GREEN_("%02x"), resp.data.asBytes[(((sec * 2) + keyType) * 9) + 8]);
9885+
uint8_t *dump = calloc(bytes, sizeof(uint8_t));
9886+
if (dump == NULL) {
9887+
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
9888+
return PM3_EFAILED;
9889+
}
9890+
if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) {
9891+
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
9892+
free(dump);
9893+
return PM3_ETIMEOUT;
9894+
}
9895+
for (uint8_t sec = 0; sec < num_sectors; sec++) {
9896+
for (uint8_t b = 0; b < 4; b++) {
9897+
PrintAndLogEx(NORMAL, " \"%s\"%s",
9898+
sprint_hex_inrow(dump + ((sec * 4) + b) * 16, 16),
9899+
(sec == num_sectors - 1) && (b == 3) ? "" : ",");
9900+
}
9901+
}
9902+
free(dump);
9903+
}
9904+
PrintAndLogEx(NORMAL, " ]\n]");
98809905
return PM3_SUCCESS;
98819906
}
98829907

0 commit comments

Comments
 (0)