From 562c78ea7be76ff340105fafb5c7f948fab18fe3 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sun, 11 Aug 2024 22:48:34 +0200 Subject: [PATCH 01/18] fm11rf08s: try to detect and warn about ProxSpace --- client/pyscripts/fm11rf08s_recovery.py | 39 ++++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index c62ee914e..76837b321 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -33,18 +33,33 @@ BACKDOOR_RF08S = "A396EFA4E24F" NUM_SECTORS = 16 if os.path.basename(os.path.dirname(os.path.dirname(sys.argv[0]))) == 'client': # dev setup - TOOLS_PATH = f"{os.path.dirname(sys.argv[0])}/../../tools/mfc/card_only" + TOOLS_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", + "..", "..", "tools", "mfc", "card_only")) else: # assuming installed - TOOLS_PATH = f"{os.path.dirname(sys.argv[0])}/../tools" + TOOLS_PATH = os.path.normpath(os.path.join(f"{os.path.dirname(sys.argv[0])}", + "..", "tools")) -STATICNESTED_1NT = f"{TOOLS_PATH}/staticnested_1nt" -STATICNESTED_2X1NT = f"{TOOLS_PATH}/staticnested_2x1nt_rf08s" -STATICNESTED_2X1NT1KEY = f"{TOOLS_PATH}/staticnested_2x1nt_rf08s_1key" -for bin in [STATICNESTED_1NT, STATICNESTED_2X1NT, STATICNESTED_2X1NT1KEY]: +tools = { + "staticnested_1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_1nt"), + "staticnested_2x1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s"), + "staticnested_2x1nt1key": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s_1key"), +} +for tool, bin in tools.items(): if not os.path.isfile(bin): - print(f"Cannot find {bin}, abort!") - exit() + if os.path.isfile(bin + ".exe"): + tools[tool] = bin + ".exe" + + print(f"Native Windows/ProxSpace detected.") + print(f"Currently, our Python output grabbing does not work properly on this environment.") + print(f"Use WSL or Linux.") + # cf https://stackoverflow.com/questions/52373180/python-on-windows-handle-invalid-when-redirecting-stdout-writing-to-file + # for ref : https://docs.python.org/3/c-api/init_config.html#c.PyConfig.legacy_windows_stdio + exit() + + else: + print(f"Cannot find {bin}, abort!") + exit() parser = argparse.ArgumentParser(description='A script combining staticnested* tools ' 'to recover all keys from a FM11RF08S card.') @@ -140,12 +155,12 @@ for sec in range(NUM_SECTORS): continue if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]: for key_type in [0, 1]: - cmd = [STATICNESTED_1NT, f"{uid:08X}", f"{sec}", + cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{sec}", nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] if args.debug: print(' '.join(cmd)) subprocess.run(cmd, capture_output=True) - cmd = [STATICNESTED_2X1NT, + cmd = [tools["staticnested_2x1nt"], f"keys_{uid:08x}_{sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{sec:02}_{nt[sec][1]}.dic"] if args.debug: print(' '.join(cmd)) @@ -165,7 +180,7 @@ for sec in range(NUM_SECTORS): key_type = 0 else: key_type = 1 - cmd = [STATICNESTED_1NT, f"{uid:08X}", f"{sec}", + cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{sec}", nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]] if args.debug: print(' '.join(cmd)) @@ -293,7 +308,7 @@ for sec in range(NUM_SECTORS): dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type_target]}_filtered.dic" else: dic = f"keys_{uid:08x}_{sec:02}_{nt[sec][key_type_target]}.dic" - cmd = [STATICNESTED_2X1NT1KEY, nt[sec][key_type_source], found_keys[sec][key_type_source], dic] + cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic] if args.debug: print(' '.join(cmd)) result = subprocess.run(cmd, capture_output=True, text=True).stdout From 83f6e2b56b03bd3c5dc65e3466022cf124d2265c Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 14 Aug 2024 10:57:05 +0200 Subject: [PATCH 02/18] make style --- client/src/uart/uart_posix.c | 2 +- doc/commands.json | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index f728b5deb..20b249a5d 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -360,7 +360,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { free(prefix); // Freshly available port can take a while before getting permission to access it. Up to 600ms on my machine... - for (uint8_t i =0; i < 10; i++) { + for (uint8_t i = 0; i < 10; i++) { sp->fd = open(pcPortName, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (sp->fd != -1 || errno != EACCES) break; diff --git a/doc/commands.json b/doc/commands.json index 65501d183..95f7279c9 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -4340,9 +4340,10 @@ "--4k MIFARE Classic 4k / S70", "--emu Fill simulator keys from found keys", "--dump Dump found keys to binary file", - "-f, --file Filename of dictionary" + "-f, --file Filename of dictionary", + "--no-default Don't add the bunch of extra default keys" ], - "usage": "hf mf chk [-hab*] [-k ]... [--tblk ] [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [-f ]" + "usage": "hf mf chk [-hab*] [-k ]... [--tblk ] [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [-f ] [--no-default]" }, "hf mf cload": { "command": "hf mf cload", @@ -4703,9 +4704,10 @@ "-f, --file filename of dictionary", "--blk block number (single block recovery mode)", "-a single block recovery key A", - "-b single block recovery key B" + "-b single block recovery key B", + "--no-default Don't add the bunch of extra default keys" ], - "usage": "hf mf fchk [-hab] [-k ]... [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [--mem] [-f ] [--blk ]" + "usage": "hf mf fchk [-hab] [-k ]... [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [--mem] [-f ] [--blk ] [--no-default]" }, "hf mf gchpwd": { "command": "hf mf gchpwd", @@ -5044,13 +5046,14 @@ "--key2 nested key, 6 hex bytes (default=same)", "-n number of nonces (default=2)", "--reset reset between attempts, even if auth was successful", + "--hardreset hard reset (RF off/on) between attempts, even if auth was successful", "--addread auth(blk)-read(blk)-auth(blk2)", "--addauth auth(blk)-auth(blk)-auth(blk2)", "--incblk2 auth(blk)-auth(blk2)-auth(blk2+4)-...", "--corruptnrar corrupt {nR}{aR}, but with correct parity", "--corruptnrarparity correct {nR}{aR}, but with corrupted parity" ], - "usage": "hf mf isen [-hab] [--blk ] [-c ] [-k ] [--blk2 ] [--a2] [--b2] [--c2 ] [--key2 ] [-n ] [--reset] [--addread] [--addauth] [--incblk2] [--corruptnrar] [--corruptnrarparity]" + "usage": "hf mf isen [-hab] [--blk ] [-c ] [-k ] [--blk2 ] [--a2] [--b2] [--c2 ] [--key2 ] [-n ] [--reset] [--hardreset] [--addread] [--addauth] [--incblk2] [--corruptnrar] [--corruptnrarparity]" }, "hf mf mad": { "command": "hf mf mad", @@ -12810,6 +12813,6 @@ "metadata": { "commands_extracted": 740, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2024-08-03T19:17:38" + "extracted_on": "2024-08-14T08:56:40" } } From 369c5bb5dbe43d0e0f39df37f4917556bfa77c1f Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 14 Aug 2024 13:06:21 +0200 Subject: [PATCH 03/18] Added native output grabbing for Python and Lua: less hacky than output_grabber.py, should work on ProxSpace as well --- CHANGELOG.md | 1 + .../02b_run_test_py_grabber.sh | 3 - .../testembedded.lua | 11 +- .../testembedded.py | 8 + .../testembedded_grab.py | 13 -- client/experimental_lib/example_lua/test.lua | 11 +- .../example_py/02run_test_grab_interactive.sh | 3 - client/experimental_lib/example_py/test.py | 8 + .../experimental_lib/example_py/test_grab.py | 13 -- client/include/pm3.h | 5 +- client/pyscripts/fm11rf08s_recovery.py | 81 +++----- client/pyscripts/output_grabber.py | 81 -------- client/pyscripts/pm3.py | 20 +- client/src/pm3.c | 18 +- client/src/pm3.i | 10 +- client/src/pm3_luawrap.c | 34 +++- client/src/pm3_pywrap.c | 184 +++++++++++++++++- client/src/proxmark3.c | 4 +- client/src/ui.c | 62 +++++- client/src/ui.h | 1 + client/src/util.c | 4 +- client/src/util.h | 8 + doc/commands.json | 2 +- 23 files changed, 387 insertions(+), 198 deletions(-) delete mode 100755 client/experimental_client_with_swig/02b_run_test_py_grabber.sh delete mode 100755 client/experimental_client_with_swig/testembedded_grab.py delete mode 100755 client/experimental_lib/example_py/02run_test_grab_interactive.sh delete mode 100755 client/experimental_lib/example_py/test_grab.py delete mode 100644 client/pyscripts/output_grabber.py diff --git a/CHANGELOG.md b/CHANGELOG.md index df99d61b0..14e3a63a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. 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... ## [unreleased][unreleased] +- Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) - Fixed `hf mf wipe` to detect properly write errors (@doegox) - Fixed `hf mf fchk` which was leaving the RF field on when interrupted by keyboard (@doegox) diff --git a/client/experimental_client_with_swig/02b_run_test_py_grabber.sh b/client/experimental_client_with_swig/02b_run_test_py_grabber.sh deleted file mode 100755 index 8aed5b8bb..000000000 --- a/client/experimental_client_with_swig/02b_run_test_py_grabber.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -../../pm3 -c "script run testembedded_grab.py" -i diff --git a/client/experimental_client_with_swig/testembedded.lua b/client/experimental_client_with_swig/testembedded.lua index 469bfff7a..e4ad8311e 100755 --- a/client/experimental_client_with_swig/testembedded.lua +++ b/client/experimental_client_with_swig/testembedded.lua @@ -1,4 +1,13 @@ local pm3 = require("pm3") p=pm3.pm3() + p:console("hw status") -print(p.name) +p:console("hw version") +for line in p.grabbed_output:gmatch("[^\r\n]+") do + if line:find("Unique ID") or line:find("uC:") then + print(line) + end +end + +print("Device:", p.name) +p:console("Rem passthru remark! :coffee:", true) diff --git a/client/experimental_client_with_swig/testembedded.py b/client/experimental_client_with_swig/testembedded.py index 12ae8b741..6dbff0403 100755 --- a/client/experimental_client_with_swig/testembedded.py +++ b/client/experimental_client_with_swig/testembedded.py @@ -2,5 +2,13 @@ import pm3 p=pm3.pm3() + p.console("hw status") +p.console("hw version") +for line in p.grabbed_output.split('\n'): + if "Unique ID" in line: + print(line) + if "uC:" in line: + print(line) print("Device:", p.name) +p.console("Rem passthru remark! :coffee:", True) diff --git a/client/experimental_client_with_swig/testembedded_grab.py b/client/experimental_client_with_swig/testembedded_grab.py deleted file mode 100755 index fda910fb1..000000000 --- a/client/experimental_client_with_swig/testembedded_grab.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import pm3 -from output_grabber import OutputGrabber - -out = OutputGrabber() -p=pm3.pm3() -print("Device:", p.name) -with out: - p.console("hw status") -for line in out.captured_output.split('\n'): - if "Unique ID" in line: - print(line) diff --git a/client/experimental_lib/example_lua/test.lua b/client/experimental_lib/example_lua/test.lua index be12f1d5b..e8f7ad735 100755 --- a/client/experimental_lib/example_lua/test.lua +++ b/client/experimental_lib/example_lua/test.lua @@ -2,5 +2,14 @@ local pm3 = require("pm3") p=pm3.pm3("/dev/ttyACM0") + p:console("hw status") -print(p.name) +p:console("hw version") +for line in p.grabbed_output:gmatch("[^\r\n]+") do + if line:find("Unique ID") or line:find("uC:") then + print(line) + end +end + +print("Device:", p.name) +p:console("Rem passthru remark! :coffee:", true) diff --git a/client/experimental_lib/example_py/02run_test_grab_interactive.sh b/client/experimental_lib/example_py/02run_test_grab_interactive.sh deleted file mode 100755 index 5cfa7a038..000000000 --- a/client/experimental_lib/example_py/02run_test_grab_interactive.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -PYTHONPATH=../../pyscripts ipython3 -i ./test_grab.py diff --git a/client/experimental_lib/example_py/test.py b/client/experimental_lib/example_py/test.py index 39cee5301..7d9b4580a 100755 --- a/client/experimental_lib/example_py/test.py +++ b/client/experimental_lib/example_py/test.py @@ -2,5 +2,13 @@ import pm3 p=pm3.pm3("/dev/ttyACM0") + p.console("hw status") +p.console("hw version") +for line in p.grabbed_output.split('\n'): + if "Unique ID" in line: + print(line) + if "uC:" in line: + print(line) print("Device:", p.name) +p.console("Rem passthru remark! :coffee:", True) diff --git a/client/experimental_lib/example_py/test_grab.py b/client/experimental_lib/example_py/test_grab.py deleted file mode 100755 index f13773afe..000000000 --- a/client/experimental_lib/example_py/test_grab.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import pm3 -from output_grabber import OutputGrabber - -out = OutputGrabber() -p=pm3.pm3("/dev/ttyACM0") -print("Device:", p.name) -with out: - p.console("hw status") -for line in out.captured_output.split('\n'): - if "Unique ID" in line: - print(line) diff --git a/client/include/pm3.h b/client/include/pm3.h index 4cb348a9b..b19fe7174 100644 --- a/client/include/pm3.h +++ b/client/include/pm3.h @@ -16,10 +16,13 @@ #ifndef LIBPM3_H #define LIBPM3_H +#include + typedef struct pm3_device pm3; pm3 *pm3_open(const char *port); -int pm3_console(pm3 *dev, const char *cmd); +int pm3_console(pm3 *dev, const char *cmd, bool passthru); +const char *pm3_grabbed_output_get(pm3 *dev); const char *pm3_name_get(pm3 *dev); void pm3_close(pm3 *dev); pm3 *pm3_get_current_dev(void); diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 76837b321..0be9112b5 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -19,7 +19,6 @@ import time import subprocess import argparse import pm3 -from output_grabber import OutputGrabber # optional color support try: # pip install ansicolors @@ -49,14 +48,6 @@ for tool, bin in tools.items(): if not os.path.isfile(bin): if os.path.isfile(bin + ".exe"): tools[tool] = bin + ".exe" - - print(f"Native Windows/ProxSpace detected.") - print(f"Currently, our Python output grabbing does not work properly on this environment.") - print(f"Use WSL or Linux.") - # cf https://stackoverflow.com/questions/52373180/python-on-windows-handle-invalid-when-redirecting-stdout-writing-to-file - # for ref : https://docs.python.org/3/c-api/init_config.html#c.PyConfig.legacy_windows_stdio - exit() - else: print(f"Cannot find {bin}, abort!") exit() @@ -69,27 +60,24 @@ parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mod args = parser.parse_args() start_time = time.time() -out = OutputGrabber() p = pm3.pm3() restore_color = False -with out: - p.console("prefs get color") - p.console("prefs set color --off") -for line in out.captured_output.split('\n'): +p.console("prefs get color") +p.console("prefs set color --off") +for line in p.grabbed_output.split('\n'): if "ansi" in line: restore_color = True -with out: - p.console("hf 14a read") +p.console("hf 14a read") uid = None -for line in out.captured_output.split('\n'): +for line in p.grabbed_output.split('\n'): if "UID:" in line: uid = int(line[10:].replace(' ', ''), 16) if uid is None: print("Card not found") if restore_color: - with out: - p.console("prefs set color --ansi") + p.console("prefs set color --ansi") + _ = p.grabbed_output exit() print("UID: " + color(f"{uid:08X}", fg="green")) @@ -102,9 +90,8 @@ def print_key(sec, key_type, key): found_keys = [["", ""] for _ in range(NUM_SECTORS)] if not args.no_init_check: print("Checking default keys...") - with out: - p.console("hf mf fchk") - for line in out.captured_output.split('\n'): + p.console("hf mf fchk") + for line in p.grabbed_output.split('\n'): if "[+] 0" in line: res = [x.strip() for x in line.split('|')] sec = int(res[0][4:]) @@ -119,18 +106,17 @@ nt = [["", ""] for _ in range(NUM_SECTORS)] nt_enc = [["", ""] for _ in range(NUM_SECTORS)] par_err = [["", ""] for _ in range(NUM_SECTORS)] print("Getting nonces...") -with out: - for sec in range(NUM_SECTORS): - blk = sec * 4 - if found_keys[sec][0] == "" or found_keys[sec][1] == "": - # Even if one key already found, we'll need both nt - for key_type in [0, 1]: - cmd = f"hf mf isen -n1 --blk {blk} -c {key_type+4} --key {BACKDOOR_RF08S}" - p.console(cmd) - cmd += f" --c2 {key_type}" - p.console(cmd) +for sec in range(NUM_SECTORS): + blk = sec * 4 + if found_keys[sec][0] == "" or found_keys[sec][1] == "": + # Even if one key already found, we'll need both nt + for key_type in [0, 1]: + cmd = f"hf mf isen -n1 --blk {blk} -c {key_type+4} --key {BACKDOOR_RF08S}" + p.console(cmd) + cmd += f" --c2 {key_type}" + p.console(cmd) print("Processing traces...") -for line in out.captured_output.split('\n'): +for line in p.grabbed_output.split('\n'): if "nested cmd: 64" in line or "nested cmd: 65" in line: sec = int(line[24:26], 16)//4 key_type = int(line[21:23], 16) - 0x64 @@ -232,9 +218,8 @@ for sec in range(NUM_SECTORS): cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -259,9 +244,8 @@ for sec in range(NUM_SECTORS): cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -281,9 +265,8 @@ for sec in range(NUM_SECTORS): cmd = f"hf mf fchk --blk {sec * 4} -{kt} -f {dic} --no-default" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -324,9 +307,8 @@ for sec in range(NUM_SECTORS): cmd += f" -k {k}" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): + p.console(cmd) + for line in p.grabbed_output.split('\n'): if "aborted via keyboard" in line: abort = True if "found:" in line: @@ -338,8 +320,8 @@ for sec in range(NUM_SECTORS): if abort: break if restore_color: - with out: - p.console("prefs set color --ansi") + p.console("prefs set color --ansi") + _ = p.grabbed_output if abort: print("Brute-forcing phase aborted via keyboard!") @@ -389,10 +371,7 @@ else: cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump" if args.debug: print(cmd) - with out: - p.console(cmd) - for line in out.captured_output.split('\n'): - print(line) + p.console(cmd, passthru = True) elapsed_time = time.time() - start_time minutes = int(elapsed_time // 60) diff --git a/client/pyscripts/output_grabber.py b/client/pyscripts/output_grabber.py deleted file mode 100644 index 3ea17cf3f..000000000 --- a/client/pyscripts/output_grabber.py +++ /dev/null @@ -1,81 +0,0 @@ -import os -import sys -import threading -import time - -# From https://stackoverflow.com/a/29834357 -class OutputGrabber(object): - """ - Class used to grab standard output or another stream. - """ - escape_char = "\b" - - def __init__(self, stream=None, threaded=False): - self.origstream = stream - self.threaded = threaded - if self.origstream is None: - self.origstream = sys.stdout - self.origstreamfd = self.origstream.fileno() - self.captured_output = "" - - def __enter__(self): - self.start() - return self - - def __exit__(self, type, value, traceback): - self.stop() - - def start(self): - """ - Start capturing the stream data. - """ - self.captured_output = "" - # Create a pipe so the stream can be captured: - self.pipe_out, self.pipe_in = os.pipe() - # Save a copy of the stream: - self.streamfd = os.dup(self.origstreamfd) - # Replace the original stream with our write pipe: - os.dup2(self.pipe_in, self.origstreamfd) - if self.threaded: - # Start thread that will read the stream: - self.workerThread = threading.Thread(target=self.readOutput) - self.workerThread.start() - # Make sure that the thread is running and os.read() has executed: - time.sleep(0.01) - - def stop(self): - """ - Stop capturing the stream data and save the text in `captured_output`. - """ - # Print the escape character to make the readOutput method stop: - self.origstream.write(self.escape_char) - # Flush the stream to make sure all our data goes in before - # the escape character: - self.origstream.flush() - if self.threaded: - # wait until the thread finishes so we are sure that - # we have until the last character: - self.workerThread.join() - else: - self.readOutput() - # Close the pipe: - os.close(self.pipe_in) - os.close(self.pipe_out) - # Restore the original stream: - os.dup2(self.streamfd, self.origstreamfd) - # Close the duplicate stream: - os.close(self.streamfd) - - def readOutput(self): - """ - Read the stream data (one byte at a time) - and save the text in `captured_output`. - """ - while True: - char = os.read(self.pipe_out,1).decode(self.origstream.encoding, errors='replace') - if not char or self.escape_char in char: - break - self.captured_output += char - -if __name__ == "__main__": - print("This is a library, don't use it as a script") diff --git a/client/pyscripts/pm3.py b/client/pyscripts/pm3.py index 7f5a91445..ead49777a 100644 --- a/client/pyscripts/pm3.py +++ b/client/pyscripts/pm3.py @@ -1,13 +1,10 @@ -# This file was automatically generated by SWIG (http://www.swig.org). -# Version 4.0.2 +# This file was automatically generated by SWIG (https://www.swig.org). +# Version 4.2.1 # -# Do not make changes to this file unless you know what you are doing--modify +# Do not make changes to this file unless you know what you are doing - modify # the SWIG interface file instead. from sys import version_info as _swig_python_version_info -if _swig_python_version_info < (2, 7, 0): - raise RuntimeError("Python 2.7 or later required") - # Import the low-level C/C++ module if __package__ or "." in __name__: from . import _pm3 @@ -29,10 +26,10 @@ def _swig_repr(self): def _swig_setattr_nondynamic_instance_variable(set): def set_instance_attr(self, name, value): - if name == "thisown": - self.this.own(value) - elif name == "this": + if name == "this": set(self, name, value) + elif name == "thisown": + self.this.own(value) elif hasattr(self, name) and isinstance(getattr(type(self), name), property): set(self, name, value) else: @@ -69,9 +66,10 @@ class pm3(object): _pm3.pm3_swiginit(self, _pm3.new_pm3(*args)) __swig_destroy__ = _pm3.delete_pm3 - def console(self, cmd): - return _pm3.pm3_console(self, cmd) + def console(self, cmd, passthru=False): + return _pm3.pm3_console(self, cmd, passthru) name = property(_pm3.pm3_name_get) + grabbed_output = property(_pm3.pm3_grabbed_output_get) # Register pm3 in _pm3: _pm3.pm3_swigregister(pm3) diff --git a/client/src/pm3.c b/client/src/pm3.c index 7639ecc6d..b921d37b3 100644 --- a/client/src/pm3.c +++ b/client/src/pm3.c @@ -53,18 +53,32 @@ void pm3_close(pm3_device_t *dev) { msleep(100); // Make sure command is sent before killing client CloseProxmark(dev); } + free_grabber(); } -int pm3_console(pm3_device_t *dev, const char *cmd) { +int pm3_console(pm3_device_t *dev, const char *cmd, bool passthru) { // For now, there is no real device context: (void) dev; - return CommandReceived(cmd); + uint8_t prev_printAndLog = g_printAndLog; + if (! passthru) { + g_printAndLog |= PRINTANDLOG_GRAB; + g_printAndLog &= ~PRINTANDLOG_PRINT; + } + int ret = CommandReceived(cmd); + g_printAndLog = prev_printAndLog; + return ret; } const char *pm3_name_get(pm3_device_t *dev) { return dev->g_conn->serial_port_name; } +const char *pm3_grabbed_output_get(pm3_device_t *dev) { + char *tmp = g_grabbed_output.ptr; + g_grabbed_output.idx = 0; + return tmp; +} + pm3_device_t *pm3_get_current_dev(void) { return g_session.current_device; } diff --git a/client/src/pm3.i b/client/src/pm3.i index 1c9a91ed2..810f5eb22 100644 --- a/client/src/pm3.i +++ b/client/src/pm3.i @@ -8,6 +8,13 @@ /* Strip "pm3_" from API functions for SWIG */ %rename("%(strip:[pm3_])s") ""; %feature("immutable","1") pm3_current_dev; + +#ifdef PYWRAP + #include + %typemap(default) bool passthru { + $1 = Py_False; + } +#endif typedef struct { %extend { pm3() { @@ -30,8 +37,9 @@ typedef struct { pm3_close($self); } } - int console(char *cmd); + int console(char *cmd, bool passthru = false); char const * const name; + char const * const grabbed_output; } } pm3; //%nodefaultctor device; diff --git a/client/src/pm3_luawrap.c b/client/src/pm3_luawrap.c index b744305b6..c880d09fd 100644 --- a/client/src/pm3_luawrap.c +++ b/client/src/pm3_luawrap.c @@ -2768,18 +2768,23 @@ static int _wrap_pm3_console(lua_State *L) { int SWIG_arg = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; + bool arg3 = (bool) false ; int result; - SWIG_check_num_args("pm3::console", 2, 2) + SWIG_check_num_args("pm3::console", 2, 3) if (!SWIG_isptrtype(L, 1)) SWIG_fail_arg("pm3::console", 1, "pm3 *"); if (!SWIG_lua_isnilstring(L, 2)) SWIG_fail_arg("pm3::console", 2, "char *"); + if (lua_gettop(L) >= 3 && !lua_isboolean(L, 3)) SWIG_fail_arg("pm3::console", 3, "bool"); if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_pm3, 0))) { SWIG_fail_ptr("pm3_console", 1, SWIGTYPE_p_pm3); } arg2 = (char *)lua_tostring(L, 2); - result = (int)pm3_console(arg1, arg2); + if (lua_gettop(L) >= 3) { + arg3 = (lua_toboolean(L, 3) != 0); + } + result = (int)pm3_console(arg1, arg2, arg3); lua_pushnumber(L, (lua_Number) result); SWIG_arg++; return SWIG_arg; @@ -2815,6 +2820,30 @@ fail: } +static int _wrap_pm3_grabbed_output_get(lua_State *L) { + int SWIG_arg = 0; + pm3 *arg1 = (pm3 *) 0 ; + char *result = 0 ; + + SWIG_check_num_args("pm3::grabbed_output", 1, 1) + if (!SWIG_isptrtype(L, 1)) SWIG_fail_arg("pm3::grabbed_output", 1, "pm3 *"); + + if (!SWIG_IsOK(SWIG_ConvertPtr(L, 1, (void **)&arg1, SWIGTYPE_p_pm3, 0))) { + SWIG_fail_ptr("pm3_grabbed_output_get", 1, SWIGTYPE_p_pm3); + } + + result = (char *)pm3_grabbed_output_get(arg1); + lua_pushstring(L, (const char *)result); + SWIG_arg++; + return SWIG_arg; + +fail: + SWIGUNUSED; + lua_error(L); + return 0; +} + + static void swig_delete_pm3(void *obj) { pm3 *arg1 = (pm3 *) obj; delete_pm3(arg1); @@ -2829,6 +2858,7 @@ static int _proxy__wrap_new_pm3(lua_State *L) { } static swig_lua_attribute swig_pm3_attributes[] = { { "name", _wrap_pm3_name_get, SWIG_Lua_set_immutable }, + { "grabbed_output", _wrap_pm3_grabbed_output_get, SWIG_Lua_set_immutable }, {0, 0, 0} }; static swig_lua_method swig_pm3_methods[] = { diff --git a/client/src/pm3_pywrap.c b/client/src/pm3_pywrap.c index 06e68747a..93a26e2d0 100644 --- a/client/src/pm3_pywrap.c +++ b/client/src/pm3_pywrap.c @@ -3270,6 +3270,149 @@ SWIGINTERN void delete_pm3(pm3 *self) { } } +SWIGINTERN int +SWIG_AsVal_double(PyObject *obj, double *val) { + int res = SWIG_TypeError; + if (PyFloat_Check(obj)) { + if (val) *val = PyFloat_AsDouble(obj); + return SWIG_OK; +#if PY_VERSION_HEX < 0x03000000 + } else if (PyInt_Check(obj)) { + if (val) *val = (double) PyInt_AsLong(obj); + return SWIG_OK; +#endif + } else if (PyLong_Check(obj)) { + double v = PyLong_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + double d = PyFloat_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = d; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); + } else { + PyErr_Clear(); + } + } + } +#endif + return res; +} + + +#include + + +#include + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx, cx, rd; + errno = 0; + fx = floor(x); + cx = ceil(x); + rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff / summ; + if (reps < 8 * DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_long(PyObject *obj, long *val) { +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else +#endif + if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + long v = PyInt_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double(obj, &d)); + // Largest double not larger than LONG_MAX (not portably calculated easily) + // Note that double(LONG_MAX) is stored in a double rounded up by one (for 64-bit long) + // 0x7ffffffffffffc00LL == (int64_t)std::nextafter(double(__uint128_t(LONG_MAX)+1), double(0)) + const double long_max = sizeof(long) == 8 ? 0x7ffffffffffffc00LL : LONG_MAX; + // No equivalent needed for 64-bit double(LONG_MIN) is exactly LONG_MIN + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, long_max)) { + if (val) *val = (long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_bool(PyObject *obj, bool *val) { + int r; + if (!PyBool_Check(obj)) + return SWIG_ERROR; + r = PyObject_IsTrue(obj); + if (r == -1) + return SWIG_ERROR; + if (val) *val = r ? true : false; + return SWIG_OK; +} + + SWIGINTERNINLINE PyObject * SWIG_From_int(int value) { return PyInt_FromLong((long) value); @@ -3403,16 +3546,19 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { PyObject *resultobj = 0; pm3 *arg1 = (pm3 *) 0 ; char *arg2 = (char *) 0 ; + bool arg3 = (bool) false ; void *argp1 = 0 ; int res1 = 0 ; int res2 ; char *buf2 = 0 ; int alloc2 = 0 ; - PyObject *swig_obj[2] ; + bool val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; int result; (void)self; - if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 2, swig_obj)) SWIG_fail; + if (!SWIG_Python_UnpackTuple(args, "pm3_console", 2, 3, swig_obj)) SWIG_fail; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_pm3, 0 | 0); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "pm3_console" "', argument " "1"" of type '" "pm3 *""'"); @@ -3423,7 +3569,14 @@ SWIGINTERN PyObject *_wrap_pm3_console(PyObject *self, PyObject *args) { SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "pm3_console" "', argument " "2"" of type '" "char *""'"); } arg2 = (char *)(buf2); - result = (int)pm3_console(arg1, arg2); + if (swig_obj[2]) { + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "pm3_console" "', argument " "3"" of type '" "bool""'"); + } + arg3 = (bool)(val3); + } + result = (int)pm3_console(arg1, arg2, arg3); resultobj = SWIG_From_int((int)(result)); if (alloc2 == SWIG_NEWOBJ) free((char *)buf2); return resultobj; @@ -3457,6 +3610,30 @@ fail: } +SWIGINTERN PyObject *_wrap_pm3_grabbed_output_get(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + pm3 *arg1 = (pm3 *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + char *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1, SWIGTYPE_p_pm3, 0 | 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "pm3_grabbed_output_get" "', argument " "1"" of type '" "pm3 *""'"); + } + arg1 = (pm3 *)(argp1); + result = (char *)pm3_grabbed_output_get(arg1); + resultobj = SWIG_FromCharPtr((const char *)result); + return resultobj; +fail: + return NULL; +} + + SWIGINTERN PyObject *pm3_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *obj; if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; @@ -3473,6 +3650,7 @@ static PyMethodDef SwigMethods[] = { { "delete_pm3", _wrap_delete_pm3, METH_O, NULL}, { "pm3_console", _wrap_pm3_console, METH_VARARGS, NULL}, { "pm3_name_get", _wrap_pm3_name_get, METH_O, NULL}, + { "pm3_grabbed_output_get", _wrap_pm3_grabbed_output_get, METH_O, NULL}, { "pm3_swigregister", pm3_swigregister, METH_O, NULL}, { "pm3_swiginit", pm3_swiginit, METH_VARARGS, NULL}, { NULL, NULL, 0, NULL } diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 54b05670a..9ebf01a1e 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -553,7 +553,7 @@ check_script: if (cmd[0] != '\0') { uint8_t old_printAndLog = g_printAndLog; if (!printprompt) { - g_printAndLog &= PRINTANDLOG_LOG; + g_printAndLog &= ~PRINTANDLOG_PRINT; } char prompt[PROXPROMPT_MAX_SIZE] = {0}; prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true); @@ -1462,6 +1462,8 @@ int main(int argc, char *argv[]) { preferences_save(); } + free_grabber(); + return mainret; } #endif //LIBPM3 diff --git a/client/src/ui.c b/client/src/ui.c index bfe705d80..b682dee49 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -168,6 +168,33 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam return PM3_SUCCESS; } +void free_grabber(void) { + free(g_grabbed_output.ptr); + g_grabbed_output.ptr = NULL; + g_grabbed_output.size = 0; + g_grabbed_output.idx = 0; +} + +static void fill_grabber(const char *string) { + if (g_grabbed_output.ptr == NULL || g_grabbed_output.size - g_grabbed_output.idx < MAX_PRINT_BUFFER) { + char *tmp = realloc(g_grabbed_output.ptr, g_grabbed_output.size + MAX_PRINT_BUFFER); + if (tmp == NULL) { + // We leave current g_grabbed_output untouched + PrintAndLogEx(ERR, "Out of memory error in fill_grabber()"); + return; + } + g_grabbed_output.ptr = tmp; + g_grabbed_output.size += MAX_PRINT_BUFFER; + } + int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string); + if (len < 0 || len > MAX_PRINT_BUFFER) { + // We leave current g_grabbed_output_len untouched + PrintAndLogEx(ERR, "snprintf error in fill_grabber()"); + return; + } + g_grabbed_output.idx += len; +} + void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) { char buff[2000] = "Options:\n"; char format[2000] = ""; @@ -299,12 +326,15 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { } else { snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer); if (level == INPLACE) { - char buffer3[sizeof(buffer2)] = {0}; - char buffer4[sizeof(buffer2)] = {0}; - memcpy_filter_ansi(buffer3, buffer2, sizeof(buffer2), !g_session.supports_colors); - memcpy_filter_emoji(buffer4, buffer3, sizeof(buffer3), g_session.emoji_mode); - fprintf(stream, "\r%s", buffer4); - fflush(stream); + // ignore INPLACE if rest of output is grabbed + if (!(g_printAndLog & PRINTANDLOG_GRAB)) { + char buffer3[sizeof(buffer2)] = {0}; + char buffer4[sizeof(buffer2)] = {0}; + memcpy_filter_ansi(buffer3, buffer2, sizeof(buffer2), !g_session.supports_colors); + memcpy_filter_emoji(buffer4, buffer3, sizeof(buffer3), g_session.emoji_mode); + fprintf(stream, "\r%s", buffer4); + fflush(stream); + } } else { fPrintAndLog(stream, "%s", buffer2); } @@ -401,18 +431,32 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { } #endif - if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) { + if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) || + (g_printAndLog & PRINTANDLOG_GRAB)) { memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT); - if (filter_ansi) { // already done + if (!filter_ansi) { + memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true); + } + } + if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) { + if (filter_ansi) { fprintf(logfile, "%s", buffer3); } else { - memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true); fprintf(logfile, "%s", buffer); } if (linefeed) fprintf(logfile, "\n"); fflush(logfile); } + if (g_printAndLog & PRINTANDLOG_GRAB) { + if (filter_ansi) { + fill_grabber(buffer3); + } else { + fill_grabber(buffer); + } + if (linefeed) + fill_grabber("\n"); + } if (flushAfterWrite) fflush(stdout); diff --git a/client/src/ui.h b/client/src/ui.h index 033f8b9a3..09a107ba4 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -80,6 +80,7 @@ bool GetFlushAfterWrite(void); void memcpy_filter_ansi(void *dest, const void *src, size_t n, bool filter); void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n); void memcpy_filter_emoji(void *dest, const void *src, size_t n, emojiMode_t mode); +void free_grabber(void); int searchHomeFilePath(char **foundpath, const char *subdir, const char *filename, bool create_home); diff --git a/client/src/util.c b/client/src/util.c index b923b3020..bc6fbf3b4 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -36,8 +36,10 @@ #define UTIL_BUFFER_SIZE_SPRINT 8196 // global client debug variable uint8_t g_debugMode = 0; -// global client disable logging variable +// global client enable/disable printing/logging/grabbing variable uint8_t g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG; +// global pointer to grabbed output +grabbed_output g_grabbed_output = {NULL, 0, 0}; // global client tell if a pending prompt is present bool g_pendingPrompt = false; // global CPU core count override diff --git a/client/src/util.h b/client/src/util.h index eedd409f8..86c7a4f9c 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -34,8 +34,16 @@ extern uint8_t g_printAndLog; extern bool g_pendingPrompt; extern int g_numCPUs; +typedef struct { + char *ptr; + size_t size; + size_t idx; +} grabbed_output; +extern grabbed_output g_grabbed_output; + #define PRINTANDLOG_PRINT 1 #define PRINTANDLOG_LOG 2 +#define PRINTANDLOG_GRAB 4 // Return error #define PM3_RET_ERR(err, ...) { \ diff --git a/doc/commands.json b/doc/commands.json index 95f7279c9..bcee83649 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -12813,6 +12813,6 @@ "metadata": { "commands_extracted": 740, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2024-08-14T08:56:40" + "extracted_on": "2024-08-14T11:49:06" } } From e317d16eaf521e3f493210f61adda95010c8ed44 Mon Sep 17 00:00:00 2001 From: douniwan5788 Date: Wed, 14 Aug 2024 23:34:10 +0800 Subject: [PATCH 04/18] Correct the password for 8265 in magic_cards_notes.md Signed-off-by: douniwan5788 --- doc/magic_cards_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index 8762ecf3d..c2920550b 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -155,7 +155,7 @@ This is the cheapest and most common ID82xx chip available. It is usually sold a #### Characteristics * Chip is likely a Hitag μ (micro) -* Password protection (4b), usually "1AC4999C" +* Password protection (4b), usually "00000000"(default) or "9AC4999C"(FURUI) * Currently unimplemented in proxmark3 client * Other names: * ID8210 (CN) From 52980ac48b1cf48be73ed30c50bd20c341bbb377 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 14 Aug 2024 20:03:36 +0200 Subject: [PATCH 05/18] fm11rf08s script: make sure we got nonces or abort --- client/pyscripts/fm11rf08s_recovery.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 0be9112b5..7d84a6821 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -129,6 +129,17 @@ for line in p.grabbed_output.split('\n'): nt_enc[sec][key_type] = data data = line[128:136] par_err[sec][key_type] = data +for sec in range(NUM_SECTORS): + if found_keys[sec][0] == "" or found_keys[sec][1] == "": + for key_type in [0, 1]: + if (nt[sec][key_type] == "" or + nt_enc[sec][key_type] == "" or + par_err[sec][key_type] == ""): + print("Error, could not collect nonces, abort") + if restore_color: + p.console("prefs set color --ansi") + _ = p.grabbed_output + exit() print("Running staticnested_1nt & 2x1nt when doable...") keys = [[set(), set()] for _ in range(NUM_SECTORS)] From b700b1cd0867c5b5332ed285379b75008b714c85 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Wed, 14 Aug 2024 21:23:43 +0200 Subject: [PATCH 06/18] fix tools compilation with clang --- client/Makefile | 2 +- tools/mfc/card_only/Makefile | 4 ++-- tools/mfc/card_reader/Makefile | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/Makefile b/client/Makefile index 1b7090f68..9cd65bf83 100644 --- a/client/Makefile +++ b/client/Makefile @@ -394,7 +394,7 @@ ifeq ($(PYTHON_FOUND),1) endif ####################################################################################################### -# macOS doesn't like this params +# clang doesn't like this params #MYCFLAGS += --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 MYCFLAGS += -O3 diff --git a/tools/mfc/card_only/Makefile b/tools/mfc/card_only/Makefile index 9d357df55..fa705e209 100644 --- a/tools/mfc/card_only/Makefile +++ b/tools/mfc/card_only/Makefile @@ -23,8 +23,8 @@ ifneq (,$(findstring MINGW,$(platform))) CFLAGS += -D_ISOC99_SOURCE endif -# macOS doesn't like these compiler params -ifneq ($(platform),Darwin) +# clang doesn't like these compiler params +ifneq ($(DETECTED_COMPILER), clang) MYCFLAGS += --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 endif diff --git a/tools/mfc/card_reader/Makefile b/tools/mfc/card_reader/Makefile index eaadd4e1a..b0ac20879 100644 --- a/tools/mfc/card_reader/Makefile +++ b/tools/mfc/card_reader/Makefile @@ -21,8 +21,8 @@ ifneq (,$(findstring MINGW,$(platform))) CFLAGS += -D_ISOC99_SOURCE endif -# macOS doesn't like these compiler params -ifneq ($(platform),Darwin) +# clang doesn't like these compiler params +ifneq ($(DETECTED_COMPILER), clang) MYCFLAGS += --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 endif From cac651cac5c8da42600a87abf8dd07cf181b57d1 Mon Sep 17 00:00:00 2001 From: Augusto Zanellato Date: Thu, 15 Aug 2024 10:20:52 +0200 Subject: [PATCH 07/18] Add key for STS Hotel systems --- client/dictionaries/mfc_default_keys.dic | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index e440dbb76..8bc67c57b 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -2529,3 +2529,5 @@ bd6af9754c18 60FCB3C42ABF # key for hotel in greece 722F24F0722F +# STS Hotel 2A +535453535453 From 83fccbafac1fd27639ecee387cfe5698ddb95622 Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 08:29:09 -0700 Subject: [PATCH 08/18] Add custom CTF wiegand format --- CHANGELOG.md | 1 + client/src/wiegand_formats.c | 64 ++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e3a63a3..5c55318a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. 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... ## [unreleased][unreleased] +- Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) - Fixed `hf mf wipe` to detect properly write errors (@doegox) diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index eddf28621..6f022a2ba 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -20,6 +20,69 @@ #include "commonutil.h" +static bool Pack_Defcon32(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + if (card->FacilityCode > 0x00FFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x0fffff) return false; // Can't encode CN. + if (card->IssueLevel > 0x00000F) return false; // Can't encode Issue + if (card->OEM > 0) return false; // Not used in this format + + packed->Length = 42; + /* + By implementing this format I hope to make the CTF easier for people to get into next year + //~~The wiegand data consists of 3 32 bit units that we need to split the data between Bottom and Mid since we have a 42 bit format~~ + We can use the set linear field function instead this seems to be easier. + |Mid part| | Bot part of the packed data | + PFFFFFFFFF FFFFFFFIIIICCCCCCCCCCCCCCCCCCCCP + 1111111111 11111111111000000000000000001000 +FC 111111111 1111111 = FF FF +//FC Mid 111111111 0000000 = FF 80 These where used to split data between bot/mid +//FC Bot 000000000 1111111 = 00 7F +Issuance 1111 = 0F +Card Number 11111111111111111111 = 0FFFFF + + */ + + // Referenced from MSB + set_linear_field(packed, card->CardNumber, 21, 20); // 20 bit + set_linear_field(packed, card->IssueLevel, 17, 4); // 4 bit + set_linear_field(packed, card->FacilityCode, 1, 16); // 16 bits + + // Parity calc + //0123456789|0123456789|0123456789|0123456789|01 + //E E E E E |E E E E E |EO O O O O| O O O O O| O + set_bit_by_position(packed, + evenparity32( + get_nonlinear_field(packed, 16, (uint8_t[]) {2, 4, 6, 8, 10, 12, 14, 16, 18, 20})) + , 0); + + set_bit_by_position(packed, + oddparity32( + get_nonlinear_field(packed, 16, (uint8_t[]) {21, 23, 25, 27, 29, 31, 33, 35, 37, 39})) + , 41); + if (preamble) + return add_HID_header(packed); + return true; +} + +static bool Unpack_Defcon32(wiegand_message_t *packed, wiegand_card_t *card) { + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 42) return false; // Wrong length? Stop here. + + card->FacilityCode = get_linear_field(packed, 1, 16); + card->IssueLevel = get_linear_field(packed, 17, 4); + card->CardNumber = get_linear_field(packed, 21, 20); + + card->ParityValid = + (get_bit_by_position(packed, 41) == oddparity32( + get_nonlinear_field(packed, 16, (uint8_t[]) {21, 23, 25, 27, 29, 31, 33, 35, 37, 39})))&& + (get_bit_by_position(packed, 0) == + evenparity32(get_nonlinear_field(packed, 16, (uint8_t[]) {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}))); + return true; +} + + static bool Pack_H10301(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { memset(packed, 0, sizeof(wiegand_message_t)); @@ -1474,6 +1537,7 @@ static const cardformat_t FormatTable[] = { {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", {1, 1, 0, 0, 1}}, + {"Defcon32", Pack_Defcon32, Unpack_Defcon32, "Custom Defcon RFCTF 42 BIT format", {1, 1, 1, 0, 1}}, // Created by (@micsen) for the CTF {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array }; From 22dc41f4ca5234e12ff83ed50842c21819c60f9d Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 08:29:45 -0700 Subject: [PATCH 09/18] Add issuance level encoding capability to iclass encode command --- CHANGELOG.md | 1 + client/src/cmdhficlass.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c55318a6..beb4f5473 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. 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... ## [unreleased][unreleased] +- Added --issue to hf iclass encode command (@micsen) - Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 11b3712b4..5d5a30681 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4595,6 +4595,7 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), + arg_u64_0(NULL, "issue", "", "issue level"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4635,6 +4636,7 @@ static int CmdHFiClassEncode(const char *Cmd) { memset(&card, 0, sizeof(wiegand_card_t)); card.FacilityCode = arg_get_u32_def(ctx, 7, 0); card.CardNumber = arg_get_u32_def(ctx, 8, 0); + card.IssueLevel = arg_get_u32_def(ctx, 12, 0); char format[16] = {0}; int format_len = 0; From 9798071ec9493bc83d7d1df1ff412ed1f365ac1b Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 08:43:28 -0700 Subject: [PATCH 10/18] Move argument to where it belongs in the list of args and not just tacked at the bottom --- client/src/cmdhficlass.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 5d5a30681..6ec4fd487 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4592,10 +4592,10 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_str0(NULL, "enckey", "", "3DES transport key, 16 hex bytes"), arg_u64_0(NULL, "fc", "", "facility code"), arg_u64_0(NULL, "cn", "", "card number"), + arg_u64_0(NULL, "issue", "", "issue level"), arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), - arg_u64_0(NULL, "issue", "", "issue level"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4636,14 +4636,14 @@ static int CmdHFiClassEncode(const char *Cmd) { memset(&card, 0, sizeof(wiegand_card_t)); card.FacilityCode = arg_get_u32_def(ctx, 7, 0); card.CardNumber = arg_get_u32_def(ctx, 8, 0); - card.IssueLevel = arg_get_u32_def(ctx, 12, 0); + card.IssueLevel = arg_get_u32_def(ctx, 9, 0); char format[16] = {0}; int format_len = 0; - CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)format, sizeof(format), &format_len); + CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)format, sizeof(format), &format_len); - bool shallow_mod = arg_get_lit(ctx, 10); - bool verbose = arg_get_lit(ctx, 11); + bool shallow_mod = arg_get_lit(ctx, 11); + bool verbose = arg_get_lit(ctx, 12); CLIParserFree(ctx); From bebd1bb654b47483b8eb949107cc79a8c1ef07bc Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 10:31:01 -0700 Subject: [PATCH 11/18] WIP: Adding --emu functionality to `hf iclass encode` For some reason it wipes the entire emu memory --- client/src/cmdhficlass.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 6ec4fd487..b4e096998 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4594,6 +4594,7 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_u64_0(NULL, "cn", "", "card number"), arg_u64_0(NULL, "issue", "", "issue level"), arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), + arg_lit0(NULL, "emu", "Write to emulation memory instead of card"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_lit0("v", NULL, "verbose (print encoded blocks)"), arg_param_end @@ -4640,10 +4641,12 @@ static int CmdHFiClassEncode(const char *Cmd) { char format[16] = {0}; int format_len = 0; + CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)format, sizeof(format), &format_len); - bool shallow_mod = arg_get_lit(ctx, 11); - bool verbose = arg_get_lit(ctx, 12); + bool use_emulator_memory = arg_get_lit(ctx, 11); + bool shallow_mod = arg_get_lit(ctx, 12); + bool verbose = arg_get_lit(ctx, 13); CLIParserFree(ctx); @@ -4776,15 +4779,20 @@ static int CmdHFiClassEncode(const char *Cmd) { int isok = PM3_SUCCESS; // write - for (uint8_t i = 0; i < 4; i++) { - isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); - switch (isok) { - case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); - break; - default: - PrintAndLogEx(INFO, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); - break; + if (use_emulator_memory) { + uint16_t bytes_sent = 0; + iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &bytes_sent); + } else { + for (uint8_t i = 0; i < 4; i++) { + isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); + switch (isok) { + case PM3_SUCCESS: + PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); + break; + default: + PrintAndLogEx(INFO, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); + break; + } } } return isok; From 8c9739d37b9fae45bc9369a5d79219459df1698d Mon Sep 17 00:00:00 2001 From: Michael Micsen Johannessen Wehus Date: Thu, 15 Aug 2024 11:41:42 -0700 Subject: [PATCH 12/18] Add emulator support for hf iclass encode with --emu flag --- CHANGELOG.md | 2 +- client/src/cmdhficlass.c | 48 +++++++++++++++++++++++++++------------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index beb4f5473..b3163be38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ All notable changes to this project will be documented in this file. 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... ## [unreleased][unreleased] -- Added --issue to hf iclass encode command (@micsen) +- Added --issue and (--emu)lator support to `hf iclass encode` command (@micsen) - Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) - Changed `hf mf chk/fchk`: added option `--no-default` to skip loading the usual ~61 hardcoded keys (@doegox) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b4e096998..980b930a9 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4579,13 +4579,15 @@ static int CmdHFiClassEncode(const char *Cmd) { "Use either --bin or --wiegand/--fc/--cn", "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)\n" "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)\n" - "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key" + "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key\n" + "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory\n" + "When using emulator you have to first load a credential into emulator memory" ); void *argtable[] = { arg_param_begin, arg_str0(NULL, "bin", "", "Binary string i.e 0001001001"), - arg_int1(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), @@ -4607,19 +4609,29 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIGetStrWithReturn(ctx, 1, bin, &bin_len); int key_nr = arg_get_int_def(ctx, 2, -1); - bool auth = false; + bool use_emulator_memory = arg_get_lit(ctx, 11); + bool auth = false; uint8_t key[8] = {0}; - if (key_nr >= 0) { - if (key_nr < ICLASS_KEYS_MAX) { - auth = true; - memcpy(key, iClass_Key_Table[key_nr], 8); - PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); - } else { - PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); + + // If we use emulator memory skip key requirement + if (!use_emulator_memory) { + if (key_nr < 0) { + PrintAndLogEx(ERR, "Missing required arg for --ki or --emu"); return PM3_EINVARG; } + + if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } } bool use_credit_key = arg_get_lit(ctx, 3); @@ -4644,7 +4656,6 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)format, sizeof(format), &format_len); - bool use_emulator_memory = arg_get_lit(ctx, 11); bool shallow_mod = arg_get_lit(ctx, 12); bool verbose = arg_get_lit(ctx, 13); @@ -4674,7 +4685,12 @@ static int CmdHFiClassEncode(const char *Cmd) { } if (have_enc_key == false) { - use_sc = IsCardHelperPresent(false); + // The IsCardHelperPresent function clears the emulator memory + if (use_emulator_memory) { + use_sc = false; + } else { + use_sc = IsCardHelperPresent(false); + } if (use_sc == false) { size_t keylen = 0; int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&enckeyptr, &keylen); @@ -4780,8 +4796,10 @@ static int CmdHFiClassEncode(const char *Cmd) { int isok = PM3_SUCCESS; // write if (use_emulator_memory) { - uint16_t bytes_sent = 0; - iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &bytes_sent); + uint16_t byte_sent = 0; + iclass_upload_emul(credential, sizeof(credential), 6 * PICOPASS_BLOCK_SIZE, &byte_sent); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", byte_sent); + PrintAndLogEx(HINT, "You are now ready to simulate. See " _YELLOW_("`hf iclass sim -h`")); } else { for (uint8_t i = 0; i < 4; i++) { isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); From e9ff568833bfd6d9cbc9f2f5056699ee18b446c7 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Thu, 15 Aug 2024 21:19:16 -0700 Subject: [PATCH 13/18] Fix length of credential in `hf iclass sam` Signed-off-by: Eric Betts --- client/src/cmdhficlass.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 980b930a9..26cf1e1f4 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -4990,7 +4990,7 @@ static int CmdHFiClassSAM(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr)); HIDTryUnpack(&packed); PrintAndLogEx(NORMAL, ""); From 116ac0c66b5cbae473b129126866dc5f847ab035 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Aug 2024 12:09:20 +0200 Subject: [PATCH 14/18] Added more fingerprinting to hf mf info --- CHANGELOG.md | 1 + client/src/cmdhfmf.c | 46 ++++++++++++++++++++++++++++++++++++++------ include/mifare.h | 2 ++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3163be38..b409ff060 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file. 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... ## [unreleased][unreleased] +- Added more fingerprinting in `hf mf info` (@doegox) - Added --issue and (--emu)lator support to `hf iclass encode` command (@micsen) - Added custom CTF Wiegand format from Defcon32 with comments (@micsen) - Added native output grabbing for Python and Lua: less hacky than `output_grabber.py`, should work on ProxSpace as well (@doegox) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 513f3ef7c..49b67517c 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9540,20 +9540,54 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, "Sector 1 key A... " _GREEN_("%012" PRIX64), e_sector[1].Key[MF_KEY_A]); } + uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; + if (mfReadBlock(0, 4, k08s, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("%02X%02X%02X%02X%02X%02X"), k08s[0], k08s[1], k08s[2], k08s[3], k08s[4], k08s[5]); + fKeyType = MF_KEY_BD08S; + } + uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; + if (mfReadBlock(0, 4, k08, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("%02X%02X%02X%02X%02X%02X"), k08[0], k08[1], k08[2], k08[3], k08[4], k08[5]); + fKeyType = MF_KEY_BD08; + } + if (fKeyType != 0xFF) { PrintAndLogEx(SUCCESS, "Block 0.......... %s", sprint_hex(blockdata, MFBLOCK_SIZE)); } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); - if ( - (blockdata[8] == 0x03 && blockdata[15] == 0x90) || - (blockdata[9] == 0x02 && blockdata[14] == 0x1D) || - (blockdata[8] == 0x04 && blockdata[15] == 0x90) || - (memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) - ) { + if (memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { PrintAndLogEx(SUCCESS, "FUDAN based card"); } + if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x90) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 + && blockdata[15] == 0x90) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + && (blockdata[8] >= 0x01 || blockdata[8] <= 0x03) && blockdata[15] == 0x1D) { + PrintAndLogEx(SUCCESS, "Fudan FM11RF08"); + } else if (card.sak == 0x88 && memcmp(blockdata + 5, "\x88\x04\x00\x43", 4) == 0) { + PrintAndLogEx(SUCCESS, "Infineon SLE66R35"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x44", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5003"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5004"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5005"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x47", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5006"); + } else if (card.sak == 0x09 && memcmp(blockdata + 5, "\x89\x04\x00\x47", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS2006"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x48", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5007"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\xc0", 4) == 0) { + PrintAndLogEx(SUCCESS, "NXP MF1ICS5035"); + } else if (fKeyType == MF_KEY_BD08 || fKeyType == MF_KEY_BD08S) { + PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!")); + } if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { PrintAndLogEx(SUCCESS, "Dorma Kaba SAFLOK detected"); diff --git a/include/mifare.h b/include/mifare.h index 5dc04e0c7..ac09ae40c 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -23,6 +23,8 @@ #define MF_KEY_A 0 #define MF_KEY_B 1 +#define MF_KEY_BD08S 2 +#define MF_KEY_BD08 3 #define MF_MAD1_SECTOR 0x00 #define MF_MAD2_SECTOR 0x10 From 0a0f981b54794ba48499db6fc3fc594001a9fd37 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Aug 2024 12:14:21 +0200 Subject: [PATCH 15/18] hf mf info: adjust logic --- client/src/cmdhfmf.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 49b67517c..dade49c83 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9557,10 +9557,12 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); + + // cards with known backdoor if (memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { - PrintAndLogEx(SUCCESS, "FUDAN based card"); - } - if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 + // backdoor might be present, or just a clone reusing Fudan MF data... + PrintAndLogEx(SUCCESS, "Fudan based card"); + } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 && (blockdata[8] == 0x03 || blockdata[8] == 0x04) && blockdata[15] == 0x90) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08S"); } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x00\x03\x00\x10", 4) == 0 @@ -9575,7 +9577,11 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5003"); } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5004"); - } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) { + } else if (fKeyType == MF_KEY_BD08 || fKeyType == MF_KEY_BD08S) { + PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!")); + } + // other cards + if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5005"); } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x47", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5006"); @@ -9585,8 +9591,6 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5007"); } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\xc0", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5035"); - } else if (fKeyType == MF_KEY_BD08 || fKeyType == MF_KEY_BD08S) { - PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!")); } if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { From b3bf55e4e367fba1461dfbb6c820cdb0b0bd9fb2 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Aug 2024 12:15:46 +0200 Subject: [PATCH 16/18] oops --- client/src/cmdhfmf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index dade49c83..712b9a7f0 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9569,7 +9569,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { && blockdata[15] == 0x90) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08S-7B"); } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x08\x04\x00", 3) == 0 - && (blockdata[8] >= 0x01 || blockdata[8] <= 0x03) && blockdata[15] == 0x1D) { + && (blockdata[8] >= 0x01 && blockdata[8] <= 0x03) && blockdata[15] == 0x1D) { PrintAndLogEx(SUCCESS, "Fudan FM11RF08"); } else if (card.sak == 0x88 && memcmp(blockdata + 5, "\x88\x04\x00\x43", 4) == 0) { PrintAndLogEx(SUCCESS, "Infineon SLE66R35"); From c9f4679b5a5a6cb99ed9d834a3961357a1aed44e Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Aug 2024 12:55:59 +0200 Subject: [PATCH 17/18] hf mf info: always search for backdoor --- client/src/cmdhfmf.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 712b9a7f0..965ec34f8 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -9512,9 +9512,9 @@ static int CmdHF14AMfInfo(const char *Cmd) { return PM3_EMALLOC; } + uint8_t blockdata[MFBLOCK_SIZE] = {0}; res = mfCheckKeys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); if (res == PM3_SUCCESS || res == PM3_EPARTIAL) { - uint8_t blockdata[MFBLOCK_SIZE] = {0}; if (e_sector[0].foundKey[MF_KEY_A]) { PrintAndLogEx(SUCCESS, "Sector 0 key A... " _GREEN_("%012" PRIX64), e_sector[0].Key[MF_KEY_A]); @@ -9539,25 +9539,26 @@ static int CmdHF14AMfInfo(const char *Cmd) { if (e_sector[1].foundKey[MF_KEY_A]) { PrintAndLogEx(SUCCESS, "Sector 1 key A... " _GREEN_("%012" PRIX64), e_sector[1].Key[MF_KEY_A]); } + } - uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; - if (mfReadBlock(0, 4, k08s, blockdata) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("%02X%02X%02X%02X%02X%02X"), k08s[0], k08s[1], k08s[2], k08s[3], k08s[4], k08s[5]); - fKeyType = MF_KEY_BD08S; - } - uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; - if (mfReadBlock(0, 4, k08, blockdata) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("%02X%02X%02X%02X%02X%02X"), k08[0], k08[1], k08[2], k08[3], k08[4], k08[5]); - fKeyType = MF_KEY_BD08; - } + uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F}; + if (mfReadBlock(0, 4, k08s, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("%02X%02X%02X%02X%02X%02X"), k08s[0], k08s[1], k08s[2], k08s[3], k08s[4], k08s[5]); + fKeyType = MF_KEY_BD08S; + } + uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1}; + if (mfReadBlock(0, 4, k08, blockdata) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Backdoor key..... " _RED_("%02X%02X%02X%02X%02X%02X"), k08[0], k08[1], k08[2], k08[3], k08[4], k08[5]); + fKeyType = MF_KEY_BD08; + } - if (fKeyType != 0xFF) { - PrintAndLogEx(SUCCESS, "Block 0.......... %s", sprint_hex(blockdata, MFBLOCK_SIZE)); - } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); + if (fKeyType != 0xFF) { + PrintAndLogEx(SUCCESS, "Block 0.......... %s", sprint_hex(blockdata, MFBLOCK_SIZE)); + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); + if (fKeyType != 0xFF) { // cards with known backdoor if (memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) { // backdoor might be present, or just a clone reusing Fudan MF data... From 691e76675aefac2a2b06c9de7091d3a9e3d2d750 Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Fri, 16 Aug 2024 17:46:00 +0200 Subject: [PATCH 18/18] mfc/fudan: add paper ref --- client/pyscripts/fm11rf08s_recovery.py | 2 +- tools/mfc/card_only/staticnested_0nt.c | 2 +- tools/mfc/card_only/staticnested_1nt.c | 2 +- tools/mfc/card_only/staticnested_2x1nt_rf08s.c | 2 +- tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 7d84a6821..9e37e775f 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -11,7 +11,7 @@ # * 16 random keys with keyA==keyB in each sector: ~30 min # * 24 random keys, some reused across sectors: <1 min # -# Doegox, 2024 +# Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info import os import sys diff --git a/tools/mfc/card_only/staticnested_0nt.c b/tools/mfc/card_only/staticnested_0nt.c index abe5e59d0..be910d219 100644 --- a/tools/mfc/card_only/staticnested_0nt.c +++ b/tools/mfc/card_only/staticnested_0nt.c @@ -7,7 +7,7 @@ // Strategy: // * Find all possible key candidates for one reference sector, and check on-the-fly if they are compatible with any other sector we want to compare with // -// Doegox, 2024 +// Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info #include #include diff --git a/tools/mfc/card_only/staticnested_1nt.c b/tools/mfc/card_only/staticnested_1nt.c index 5bcc838c1..afd839ad6 100644 --- a/tools/mfc/card_only/staticnested_1nt.c +++ b/tools/mfc/card_only/staticnested_1nt.c @@ -8,7 +8,7 @@ // * Enumerate key candidates based on clear and encrypted nT // * Use the resulting dictionary to bruteforce the key // -// Doegox, 2024 +// Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info #include #include diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s.c index c6308a68b..c6b601518 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s.c @@ -10,7 +10,7 @@ // * Search couples of keyA/keyB satisfying some obscure relationship // * Use the resulting dictionary to bruteforce the keyA (and staticnested_2x1nt_rf08s_1key for keyB) // -// Doegox, 2024 +// Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info #include #include diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c index 94da561a8..ace95b18d 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c @@ -8,7 +8,7 @@ // * Use f08s_nested_known_collision to crack keyA // * If keyB not readable, find keyB in its dictionary based on the obscure relationship between keyA, keyB and their nT // -// Doegox, 2024 +// Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info #include #include