diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 000000000..c476fbefe --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,115 @@ +name: MacOS Build and Test + +on: [push, pull_request] + +jobs: + macos-make: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set Git http.postBuffer to something high + run: git config --global http.postBuffer 524288000 + + - name: Handle homebrew quirks + run: rm -rf /usr/local/bin/2to3 + + - name: Update brew repos + run: brew update + continue-on-error: true + + - name: Tap RfidResearchGroup/proxmark3 + run: brew tap RfidResearchGroup/proxmark3 + + - name: Install dependencies + run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + run: make + + - name: Test + run: make check + + macos-make-btaddon: + if: always() + needs: [macos-make] + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set Git http.postBuffer to something high + run: git config --global http.postBuffer 524288000 + + - name: Handle homebrew quirks + run: rm -rf /usr/local/bin/2to3 + + - name: Update brew repos + run: brew update + continue-on-error: true + + - name: Tap RfidResearchGroup/proxmark3 + run: brew tap RfidResearchGroup/proxmark3 + + - name: Install dependencies + run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + PLATFORM_EXTRAS: BTADDON + run: make + + - name: Test + run: make check + + macos-cmake: + if: always() + needs: [macos-make, macos-make-btaddon] + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set Git http.postBuffer to something high + run: git config --global http.postBuffer 524288000 + + - name: Handle homebrew quirks + run: rm -rf /usr/local/bin/2to3 + + - name: Update brew repos + run: brew update + continue-on-error: true + + - name: Tap RfidResearchGroup/proxmark3 + run: brew tap RfidResearchGroup/proxmark3 + + - name: Install dependencies + run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc + + - name: Prepare Build Folders + run: mkdir -p client/build + + - name: Initiate cmake environment + run: cmake .. + working-directory: client/build/ + + - name: Build + env: + VERBOSE: 1 + run: make + working-directory: client/build/ + + - name: Test + env: + CHECKARGS: "--clientbin ./client/build/proxmark3" + run: make client/check diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 000000000..57a6caa1d --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,85 @@ +name: Ubuntu Build and Test + +on: [push, pull_request] + +jobs: + ubuntu-make: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + run: make + + - name: Test + run: make check + + ubuntu-make-btaddon: + if: always() + needs: [ubuntu-make] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + PLATFORM_EXTRAS: BTADDON + run: make + + - name: Test + run: make check + + ubuntu-cmake: + if: always() + needs: [ubuntu-make, ubuntu-make-btaddon] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed + + - name: Prepare Build Folders + run: mkdir -p client/build + + - name: Initiate cmake environment + run: cmake .. + working-directory: client/build/ + + - name: Build + env: + VERBOSE: 1 + run: make + working-directory: client/build/ + + - name: Test + env: + CHECKARGS: "--clientbin ./client/build/proxmark3" + run: make client/check diff --git a/.gitignore b/.gitignore index 081c0f8e8..430ae2cad 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ tools/cryptorf/cm tools/cryptorf/sm tools/cryptorf/sma tools/cryptorf/sma_multi +tools/mf_nonce_brute/mf_nonce_brute fpga/* !fpga/tests @@ -93,18 +94,21 @@ client/traces/* client/dumps/* *.ice *.new -tools/mf_nonce_brute/mf_nonce_brute -tools/andrew/* -tools/jtag_openocd/openocd_configuration -ppls patches/* *- Copy.* +/EF_* client/lualibs/mfc_default_keys.lua client/lualibs/pm3_cmd.lua # recompiled fpga_version_info.c +# log / history .proxmark3/* # .tmp files are created during compilation *.tmp + +# VSCode files +!.vscode/templates/*.json +!.vscode/extensions.json +!.vscode/tasks.json diff --git a/.travis.yml b/.travis.yml.old similarity index 100% rename from .travis.yml rename to .travis.yml.old diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..85b3997e2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.cpptools", + "austin.code-gnu-global", + "marus25.cortex-debug", + "augustocdias.tasks-shell-input" + ] +} diff --git a/.vscode/setup.sh b/.vscode/setup.sh new file mode 100755 index 000000000..70672b924 --- /dev/null +++ b/.vscode/setup.sh @@ -0,0 +1,145 @@ +#!/bin/bash +############################### +# Linux # +# Uncomment to override # +############################### +#export SerialPort="/dev/ttyACM0" +#export DebuggerPath="/usr/bin/gdb" +#export JLinkServerPath="/opt/SEGGER/JLink/JLinkGDBServerCLExe" + +############################### +# WSL # +# Uncomment to override # +############################### +#export SerialPort="/dev/ttyS4" +#export DebuggerPath="/usr/bin/gdb" +#export JLinkServerPath="/mnt/c/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + +############################### +# ProxSpace # +# Uncomment to override # +############################### +#export SerialPort="COM5" +#export JLinkServerPath="C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + +#Debugging on 256KB systems is not recommended +#This option does not override PLATFORM_SIZE +export DeviceMem="512" + + +VSCODEPATH=$(dirname "$0") + +function print_config { + echo "Updating with following configuration:" + echo "SerialPort: $SerialPort" + echo "DebuggerPath: $DebuggerPath" + echo "JLinkServerPath: $JLinkServerPath" +} + +function setup_serial_port { + if [ -z "$SerialPort" ]; then + pm3list=$($VSCODEPATH/../pm3 --list 2>/dev/null) + #Use first port listed + export SerialPort=$(echo $pm3list | head -n 1 | cut -c 4-) + if [ -z "$SerialPort" ]; then + echo >&2 "[!!] No serial port found, please set SerialPort manually" + exit 1 + fi + fi +} + +function setup_gdb_linux { + if [ -z "$DebuggerPath" ]; then + export DebuggerPath="/usr/bin/gdb" + fi + if [ ! -x "$DebuggerPath" ]; then + echo >&2 "[!!] gdb not found, please set DebuggerPath manually" + exit 1 + fi +} + +function setup_jlink_linux { + if [ -z "$JLinkServerPath" ]; then + export JLinkServerPath="/opt/SEGGER/JLink/JLinkGDBServerCLExe" + fi + if [ ! -x "$JLinkServerPath" ]; then + echo >&2 "[!!] JLinkGDBServerCLExe not found, please set JLinkServerPath manually" + exit 1 + fi + +} + +function setup_jlink_wsl { + if [ -z "$JLinkServerPath" ]; then + export JLinkServerPath="/mnt/c/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + fi + if [ ! -x "$JLinkServerPath" ]; then + echo >&2 "[!!] JLinkGDBServerCL.exe not found, please set JLinkServerPath manually" + exit 1 + fi +} + +function setup_jlink_ps { + if [ -z "$JLinkServerPath" ]; then + export JLinkServerPath="c:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + fi + jlinkpath=$(cygpath "$JLinkServerPath") + if [ ! -x "$jlinkpath" ]; then + echo >&2 "[!!] JLinkGDBServerCL.exe not found, please set JLinkServerPath manually" + exit 1 + fi +} + +function setup_wsl { + setup_serial_port + setup_gdb_linux + setup_jlink_wsl + print_config + envsubst '${SerialPort} ${DebuggerPath} ${JLinkServerPath} ${DeviceMem}' <"$VSCODEPATH/templates/launch_wsl.json" > "$VSCODEPATH/launch.json" +} + +function setup_linux { + setup_serial_port + setup_gdb_linux + setup_jlink_linux + print_config + envsubst '${SerialPort} ${DebuggerPath} ${JLinkServerPath} ${DeviceMem}' <"$VSCODEPATH/templates/launch_linux.json" > "$VSCODEPATH/launch.json" +} + +function setup_ps { + setup_serial_port + setup_jlink_ps + export DebuggerPath="Using ProxSpace gbd" + print_config + envsubst '${SerialPort} ${JLinkServerPath} ${DeviceMem}' <"$VSCODEPATH/templates/launch_ps.json" > "$VSCODEPATH/launch.json" +} + +if [ -f "$VSCODEPATH/launch.json" ]; then + read -p "Existing configuration found, do you want to override it? " -n 1 -r + if [[ $REPLY =~ ^[Yy]$ ]] + then + rm "$VSCODEPATH/launch.json.bak" 2> /dev/null + mv "$VSCODEPATH/launch.json" "$VSCODEPATH/launch.json.bak" 2> /dev/null + else + echo >&2 "[!!] user abort" + exit 1 + fi + +fi + +HOSTOS=$(uname | awk '{print toupper($0)}') +if [ "$HOSTOS" = "LINUX" ]; then + if uname -a|grep -q Microsoft; then + setup_wsl + else + setup_linux + fi +elif [ "$HOSTOS" = "DARWIN" ]; then + echo >&2 "[!!] MacOS not supported, sorry!" + exit 1 +elif [[ "$HOSTOS" =~ MINGW(32|64)_NT* ]]; then + setup_ps +else + echo >&2 "[!!] Host OS not recognized, abort: $HOSTOS" + exit 1 +fi \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7354a8a2d..67f0f235e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,23 +2,20 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", + "windows": { + "options": { + "cwd": "${workspaceFolder}", + "env": { + "PATH": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin", + "MSYSTEM": "MINGW64" + } + } + }, "tasks": [ { "label": "all: Make & run", "type": "shell", "command": "make -j && ./pm3", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -31,18 +28,6 @@ "label": "choose: Make", "type": "shell", "command": "make ${input:componentType} -j", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -52,18 +37,6 @@ "label": "client: Debug: make", "type": "shell", "command": "make client -j DEBUG=1", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -73,18 +46,6 @@ "label": "client: Debug: clean & make", "type": "shell", "command": "make client/clean && make client -j DEBUG=1", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -94,55 +55,27 @@ "label": "fullimage: Make & Flash", "type": "shell", "command": "make fullimage && ./pm3-flash-fullimage", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [] }, { "label": "BOOTROM: Make & Flash", "type": "shell", "command": "make bootrom && ./pm3-flash-bootrom", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [] }, { "label": "Run client", "type": "shell", "command": "./pm3", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [] + },{ + "label": "fullimage: clean & make debug", + "type": "shell", + "command": "make armsrc/clean && make armsrc/all DEBUG_ARM=1", + "problemMatcher": [ + "$gcc" + ], + "group": "build", } ], "inputs": [ diff --git a/.vscode/launch.json b/.vscode/templates/launch_linux.json similarity index 52% rename from .vscode/launch.json rename to .vscode/templates/launch_linux.json index f6a030c7c..5239c6bda 100644 --- a/.vscode/launch.json +++ b/.vscode/templates/launch_linux.json @@ -5,34 +5,11 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Attach", - "type": "cppdbg", - "request": "attach", - "program": "${cwd}/client/proxmark3", - //"processId": "${command:pickProcess}", - "processId": "${input:ProcessID}", - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "windows": { - "processId": "${input:ProcessIDWindows}", - "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe", - "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while - "environment": [{ - "name": "PATH","value": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin" - }] - } - },{ - "name": "(gdb) Build & Launch", + "name": "Client: (gdb) Build & Launch", "type": "cppdbg", "request": "launch", "program": "${cwd}/client/proxmark3", - "args": ["/dev/ttyACM0"], + "args": ["${SerialPort}"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], @@ -46,15 +23,36 @@ } ], "preLaunchTask": "client: Debug: clean & make", - "miDebuggerPath": "/usr/bin/gdb", - "windows": { - "args": ["COM5"], - "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe", - "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while - "environment": [{ - "name": "PATH","value": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin" - }] - } + "miDebuggerPath": "${DebuggerPath}" + },{ + "name": "Client: (gdb) Attach", + "type": "cppdbg", + "request": "attach", + "program": "${cwd}/client/proxmark3", + //"processId": "${command:pickProcess}", + "processId": "${input:ProcessID}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + },{ + "name": "Firmware: (J-Link) Build & Launch", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}", + "preLaunchTask": "fullimage: clean & make debug", + "executable": "${workspaceRoot}/armsrc/obj/fullimage.elf", + "serverpath": "${JLinkServerPath}", + "servertype": "jlink", + "device": "AT91SAM7S${DeviceMem}", + "interface": "jtag", + "serialNumber": "", //If you have more than one J-Link probe, add the serial number here. + "runToMain": false, + "armToolchainPath": "/usr/bin/" } ], "inputs": [ @@ -67,14 +65,6 @@ "command": "pgrep -n proxmark3", } - },{ - "id": "ProcessIDWindows", - "type": "command", - "command": "shellCommand.execute", - "args": { - "command": "${workspaceFolder}/../../runme64.bat -c \"cat /proc/$(pgrep -n proxmark3)/winpid\"", - } - } ] } \ No newline at end of file diff --git a/.vscode/templates/launch_ps.json b/.vscode/templates/launch_ps.json new file mode 100644 index 000000000..045b3d6c5 --- /dev/null +++ b/.vscode/templates/launch_ps.json @@ -0,0 +1,49 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "PATH": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin", + "MSYSTEM": "MINGW64" + } + }, + "configurations": [ + { + "name": "Client: (gdb) Build & Launch", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${cwd}/client/proxmark3", + "args": ["${SerialPort}"], + "stopAtEntry": false, + "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "client: Debug: clean & make", + "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe" + },{ + "name": "Firmware: (J-Link) Build & Launch", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}", + "preLaunchTask": "fullimage: clean & make debug", + "executable": "${workspaceRoot}/armsrc/obj/fullimage.elf", + "serverpath": "${JLinkServerPath}", + "servertype": "jlink", + "device": "AT91SAM7S${DeviceMem}", + "interface": "jtag", + "serialNumber": "", //If you have more than one J-Link probe, add the serial number here. + "runToMain": false, + "armToolchainPath": "${workspaceFolder}/../../msys2/mingw64/bin" + } + ] +} \ No newline at end of file diff --git a/.vscode/templates/launch_wsl.json b/.vscode/templates/launch_wsl.json new file mode 100644 index 000000000..5239c6bda --- /dev/null +++ b/.vscode/templates/launch_wsl.json @@ -0,0 +1,70 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Client: (gdb) Build & Launch", + "type": "cppdbg", + "request": "launch", + "program": "${cwd}/client/proxmark3", + "args": ["${SerialPort}"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "client: Debug: clean & make", + "miDebuggerPath": "${DebuggerPath}" + },{ + "name": "Client: (gdb) Attach", + "type": "cppdbg", + "request": "attach", + "program": "${cwd}/client/proxmark3", + //"processId": "${command:pickProcess}", + "processId": "${input:ProcessID}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + },{ + "name": "Firmware: (J-Link) Build & Launch", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}", + "preLaunchTask": "fullimage: clean & make debug", + "executable": "${workspaceRoot}/armsrc/obj/fullimage.elf", + "serverpath": "${JLinkServerPath}", + "servertype": "jlink", + "device": "AT91SAM7S${DeviceMem}", + "interface": "jtag", + "serialNumber": "", //If you have more than one J-Link probe, add the serial number here. + "runToMain": false, + "armToolchainPath": "/usr/bin/" + } + ], + "inputs": [ + { + // Using Extension "Tasks Shell Input" https://marketplace.visualstudio.com/items?itemName=augustocdias.tasks-shell-input + "id": "ProcessID", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "pgrep -n proxmark3", + } + + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f890c8d5..6a2c3e0ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,14 @@ 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 `ICE_STATE_DUMP_SIM` - standalone mode for dumping/simming one iClass tag (@iconicsec) + - Added `tools/hitag2crack/crack5opencl`, an optimized version of `crack5gpu` (@matrix) + - Fixed Makefile to account for changes when running on Apple Silicon (@tcprst) + - Added support for debugging ARM with JTAG & VSCode (@Gator96100) + - Added MFUL "Gen1b" suport to `hf_mfu_setuid.lua` (@iceman1001) + - Added possibility to get bargraph in `lf tune` and `hf tune` (@iceman1001, @doegox) + - Added `hf emrtd` ePassport dumping and parsing (@aveao) + - Added `aidsearch` to `hf 14b info` (@iceman1001) + - Added `ICE_STATE_DUMP_SIM` - standalone mode for dumping/simming one iClass tag (@iconicsec) - Added `lf em 4x50 eview` - show uploaded EM4x50 data in emul memory (@tharexde) - Fix `data rawdemod` parsing for psk2 and user defined clock (@cyberpunk-re) - Added `hf iclass encode` - encode a wiegand binary to a encrypted credential (@iceman1001) diff --git a/Makefile b/Makefile index b7e942f58..e42c0caa9 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ ifneq (,$(INSTALLTOOLS)) endif ifneq (,$(INSTALLSIMFW)) $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) - $(Q)$(CP) $(foreach fw,$(INSTALLSIMFW),tools/simmodule/$(fw)) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + $(Q)$(CP) $(foreach fw,$(INSTALLSIMFW),client/resources/$(fw)) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) endif ifeq ($(platform),Linux) $(Q)$(MKDIR) $(DESTDIR)$(UDEV_PREFIX) @@ -169,7 +169,7 @@ help: @echo "+ fpga_compress - Make tools/fpga_compress" @echo @echo "+ style - Apply some automated source code formatting rules" - @echo "+ cliparser - Generate cliparser TODO + @echo "+ cliparser - Generate cliparser TODO" @echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script" @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" diff --git a/Makefile.defs b/Makefile.defs index 48567214b..23dab5b32 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -57,6 +57,11 @@ else RANLIB= ranlib endif +# For detection of Apple Silicon +ifeq ($(platform),Darwin) + BREW_PREFIX = $(shell brew --prefix) +endif + ifeq ($(DEBUG),1) DEFCXXFLAGS = -g -O0 -pipe DEFCFLAGS = -g -O0 -fstrict-aliasing -pipe @@ -66,6 +71,11 @@ else DEFCFLAGS = -Wall -Werror -O3 -fstrict-aliasing -pipe DEFLDFLAGS = endif + +ifeq ($(DEBUG_ARM),1) + APP_CFLAGS += -g + SKIP_COMPRESSION=1 +endif # Next ones are activated only if SANITIZE=1 ifeq ($(SANITIZE),1) DEFCFLAGS += -g -fsanitize=address -fno-omit-frame-pointer diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 8a31a0e9c..4ecb3b9f6 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -2,7 +2,7 @@ # Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file. PLATFORM=PM3RDV4 -#PLATFORM=PM3OTHER +#PLATFORM=PM3GENERIC # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #STANDALONE=HF_MSDSAL diff --git a/README.md b/README.md index 56af79cda..b2581d98c 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ -# RRG / Iceman repo - Proxmark3 +# RRG / Iceman - Proxmark3 + + +| Releases | Coverity | Contributors | +| ------------------- | -------------------:| -------------------:| +| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | + + +| Actions OSX CI | Actions Ubuntu CI | Windows CI | +| ------------------- | -------------------:| -------------------:| +| ![MacOS Build and Test](https://github.com/RfidResearchGroup/proxmark3/workflows/MacOS%20Build%20and%20Test/badge.svg?branch=master) | ![Ubuntu Build and Test](https://github.com/RfidResearchGroup/proxmark3/workflows/Ubuntu%20Build%20and%20Test/badge.svg?branch=master) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | - -| Releases | Linux & OSX CI | Windows CI | Coverity | Contributors | -| ------------------- |:-------------------:| -------------------:| -------------------:| -------------------:| -| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://api.travis-ci.com/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.com/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | - - - -# PROXMARK INSTALLATION AND OVERVIEW +# PROXMARK3 INSTALLATION AND OVERVIEW | FAQ's & Updates | Installation | Use of the Proxmark | | ------------------- |:-------------------:| -------------------:| @@ -36,10 +39,25 @@ |[Notes on Color usage](/doc/colors_notes.md)|[Makefile vs CMake](/doc/md/Development/Makefile-vs-CMake.md)|[Notes on Cloner guns](/doc/cloner_notes.md)| |[Notes on cliparser usage](/doc/cliparser.md)|[Notes on clocks](/doc/clocks.md)|| -## Build for non-RDV4 Proxmark3 platforms -In order to build this repo for other Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) +## Build for Proxmark3 RDV4 +See the instruction links in the tables above to build, flash and run for your Proxmark3 RDV4 device. +## Build for generic Proxmark3 platforms +In order to build this repo for generic Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) + +With generic Proxmark3 platforms we mean: + - RDV1 + - RDV2 + - RDV3 easy + - Proxmark Evolution (needs extra care) + - Radiowar black PCB version + - Ryscorp green PCB version + - Ryscorp Pm3Pro + - VX + - numerous Chinese adapted versions of the RDV3 easy (kkmoon, pisworks etc) + +> ⚠ **Note**: About flash memory size of other Proxmark3 platforms. You need to keep a eye on how large your ARM chip built-in flash memory is. With 512kb you are fine but if its 256kb you need to compile this repo with even less functionality. When running the `./pm3-flash-all` you can see which size your device have if you have the bootloader from this repo installed. Otherwise you will find the size reported in the start message when running the Proxmark3 client `./pm3`. ## What has changed? @@ -49,7 +67,9 @@ On the hardware side: * added smart card module * added FPC connector -On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). +On the software side: + +quite a lot, see the [Changelog file](CHANGELOG.md) which we try to keep updated. ## Development @@ -57,13 +77,13 @@ On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). This repo compiles nicely on - Proxspace v3.x - - [latest release v3.7.1](https://github.com/Gator96100/ProxSpace/releases) + - [latest release v3.7.2](https://github.com/Gator96100/ProxSpace/releases) - Windows/mingw environment with Qt5.6.1 & GCC 4.9 - Ubuntu 16.04 -> 20.04 - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian - Rasbian - Android / Termux - - Mac OS X / Homebrew + - Mac OS X / Homebrew / Apple Silicon - WSL1 (Windows subsystem linux) on Windows 10 - Docker container - [ RRG / Iceman repo based ubuntu 18.04 container ](https://hub.docker.com/r/secopsconsult/proxmark3) @@ -72,26 +92,22 @@ This repo compiles nicely on Hardware to run client on - PC - Android - - Raspberry Pi & Raspberry Pi Zero + - Raspberry Pi, Raspberry Pi Zero - Nvidia Jetson Nano ## Precompiled binaries -We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where @gator96100 has set up a google drive with many mingw binaries which is up-to-date. We link to these files here as to make it easier for users. -If you are having troubles with these files, contact the package maintainer @gator96100 and read the [sticky thread at forum](http://www.proxmark.org/forum/viewtopic.php?pid=24763#p24763) where known issues has been documented. +We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where [@gator96100](https://github.com/gator96100) has set up a AWS bucket with precompiled Proxspace (Mingw) binaries which is recompiled every night and with that also up-to-date. We link to these files here as to make it easier for users. +_If you use his pre-compiled Proxspace binaries do consider buy him a coffee for his efforts. Remember nothing says thank you as good as a donation._ -Ref: -For Proxmark3 RDV4 -- [Precompiled builds for RDV40 dedicated x86](https://drive.google.com/open?id=13zUs-aiQkYaSl5KWrBtuW5IWCoHJPsue) -- [Precompiled builds for RDV40 dedicated x64](https://drive.google.com/open?id=1SyPB8t5Vo8O0Lh7PjNm3Kv-mO4BNbxjX) +If you are having troubles with these files, contact the package maintainer [@gator96100](https://github.com/gator96100) and read the [homepage of his proxmark builds](https://www.proxmarkbuilds.org/) or read the [sticky thread at forum](http://www.proxmark.org/forum/viewtopic.php?pid=24763#p24763) where known issues has been documented with regards to the precompiled builds. -For Proxmark3 RDV4 with blueshark addon -- [Precompiled builds for RDV40 dedicated with Bluetooth addon x86](https://drive.google.com/open?id=1TqWYctkRvkLshQ1ZRBHPLDzYHR-asuMO) -- [Precompiled builds for RDV40 dedicated with Bluetooth addon x64](https://drive.google.com/open?id=17ful7u2QyYmMQzQzc5fAf8nJvyoDJfSL) +### Proxmark3 RDV4 devices +- [Precompiled builds for RDV40 dedicated x64](https://www.proxmarkbuilds.org/#rdv40-64/) +- [Precompiled builds for RDV40 dedicated with Bluetooth addon x64](https://www.proxmarkbuilds.org/#rdv40_bt-64/) -Generice Proxmark3 devices (non RDV4), for Proxmark3 Easy, RDV1, RDV2, RDV3, etc etc -- [Precompiled builds for RRG / Iceman repository x86](https://drive.google.com/open?id=1PI3Xr1mussPBPnYGu4ZjWzGPARK4N7JR) -- [Precompiled builds for RRG / Iceman repository x64](https://drive.google.com/open?id=1uX9RtYGinuFrpHybu4xq_BE3HrobI20e) +### Generic Proxmark3 devices +- [Precompiled builds for RRG / Iceman repository x64](https://www.proxmarkbuilds.org/#rrg_other-64/) ## Roadmap @@ -103,28 +119,40 @@ We usually merge your contributions fast since we do like the idea of getting a ## Issues & Troubleshooting Please search the [issues](https://github.com/rfidresearchgroup/proxmark3/issues) page here and see if your issue is listed in the first instance. -Read the [Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md) guide to weed out most known problems. +Read the [Troubleshooting guide](/doc/md/Installation_Instructions/Troubleshooting.md) to weed out most known problems. -Next place to visit is the [Proxmark Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) You will find many blogposts, youtube videos, tweets, reddit +Next place to visit is the [Proxmark3 Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) +You will find many blogposts, youtube videos, tweets, reddit ### Offical channels + - [RFID Hacking community discord server](https://discord.gg/QfPvGFRQxH) - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - - [Twitter](https://twitter.com/proxmark3/) - - [Proxmark3 community discord server](https://discord.gg/QfPvGFRQxH) - - _no slack channel_ + - [Proxmark3 Twitter](https://twitter.com/proxmark3/) + - [Proxmark3 forum](http://www.proxmark.org/forum/index.php) + - _no slack channel_ -Iceman has quite a few videos on his [youtube channel](https://www.youtube.com/c/ChrisHerrmann1001) + +### Youtube channels +Iceman has quite a few videos on his channel and Quentyn has risen up the last year with good informative videos. We suggest you check them out and smash that subscribe buttons! + + - [Iceman channel](https://www.youtube.com/c/ChrisHerrmann1001) + - [Quentyn Taylor](https://www.youtube.com/channel/UCL91C3IZDv3wfj2ABhdRIrw) + - [Hacker warehouse channel](https://www.youtube.com/channel/UCimS6P854cQ23j6c_xst7EQ) + +_if you think of some more good youtube channels to be on this list, let us know!_ ## Cheat sheet -Thanks to Alex Dibs, you can enjoy a [command cheat sheet](/doc/cheatsheet.md) +You can enjoy a [command cheat sheet](/doc/cheatsheet.md) and we are trying to keep it updated. +[Thanks to Alex Dib!](https://github.com/scund00r) ## Maintainers ( package, distro ) -To all distro, package maintainers, we tried to make your life easier. `make install` is now available and if you want to know more. -- [Maintainers](/doc/md/Development/Maintainers.md) +To all distro, package maintainers, we tried to make your life easier. + +`make install` is now available and if you want to know more. +- [Notes for maintainers](/doc/md/Development/Maintainers.md) ## Why didn't you base it on official Proxmark3 Master? @@ -132,8 +160,9 @@ The separation from official Proxmark3 repo gives us a lot of freedom to create ## Proxmark3 GUI -The official PM3-GUI from Gaucho will not work. -The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers. +The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer. + +The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers are. There has been brought to our attention that there is quite a few Chinese Windows GUI available. Usually you find them on alibaba / taobao ads but we have no idea which fw/client they are compatible with. Proceed with caution if you decide to go down that road. # Donations diff --git a/armsrc/Makefile b/armsrc/Makefile index 46ba4f027..019d10820 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -217,8 +217,12 @@ $(OBJDIR)/fullimage.data.o: $(OBJDIR)/fullimage.data.bin.z $(Q)$(OBJCOPY) -O elf32-littlearm -I binary -B arm --rename-section .data=compressed_data $^ $@ $(OBJDIR)/fullimage.elf: $(OBJDIR)/fullimage.nodata.o $(OBJDIR)/fullimage.data.o +ifeq (,$(findstring WITH_NO_COMPRESSION,$(APP_CFLAGS))) $(info [=] LD $@) $(Q)$(CC) $(CROSS_LDFLAGS) -Wl,-T,ldscript,-e,_osimage_entry,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ +else + $(Q)$(CP) $(OBJDIR)/fullimage.stage1.elf $@ +endif tarbin: $(OBJS) $(info TAR $@) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index b3f752fb8..5f70fa957 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -257,6 +257,9 @@ void ReadMem(int addr) { /* osimage version information is linked in, cf commonutil.h */ /* bootrom version information is pointed to from _bootphase1_version_pointer */ extern char *_bootphase1_version_pointer, _flash_start, _flash_end, __data_src_start__; +#ifdef WITH_NO_COMPRESSION +extern char *_bootrom_end, _bootrom_start, __os_size__; +#endif static void SendVersion(void) { char temp[PM3_CMD_DATA_SIZE - 12]; /* Limited data payload in USB packets */ char VersionString[PM3_CMD_DATA_SIZE - 12] = { '\0' }; @@ -295,9 +298,11 @@ static void SendVersion(void) { strncat(VersionString, "\n ", sizeof(VersionString) - strlen(VersionString) - 1); } } +#ifndef WITH_NO_COMPRESSION // Send Chip ID and used flash memory uint32_t text_and_rodata_section_size = (uint32_t)&__data_src_start__ - (uint32_t)&_flash_start; uint32_t compressed_data_section_size = common_area.arg1; +#endif struct p { uint32_t id; @@ -308,7 +313,11 @@ static void SendVersion(void) { struct p payload; payload.id = *(AT91C_DBGU_CIDR); +#ifdef WITH_NO_COMPRESSION + payload.section_size = (uint32_t)&_bootrom_end - (uint32_t)&_bootrom_start + (uint32_t)&__os_size__; +#else payload.section_size = text_and_rodata_section_size + compressed_data_section_size; +#endif payload.versionstr_len = strlen(VersionString) + 1; memcpy(payload.versionstr, VersionString, payload.versionstr_len); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index db00f5494..759e5e646 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -133,35 +133,30 @@ static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; void printHf14aConfig(void) { DbpString(_CYAN_("HF 14a config")); - Dbprintf(" [a] Anticol override....%i %s%s%s", - hf14aconfig.forceanticol, - (hf14aconfig.forceanticol == 0) ? "( " _GREEN_("No") " ) follow standard " : "", - (hf14aconfig.forceanticol == 1) ? "( " _RED_("Yes") " ) always do anticol" : "", - (hf14aconfig.forceanticol == 2) ? "( " _RED_("Yes") " ) always skip anticol" : "" + Dbprintf(" [a] Anticol override....%s%s%s", + (hf14aconfig.forceanticol == 0) ? _GREEN_("std") " : follow standard " : "", + (hf14aconfig.forceanticol == 1) ? _RED_("force") " : always do anticol" : "", + (hf14aconfig.forceanticol == 2) ? _RED_("skip") " : always skip anticol" : "" ); - Dbprintf(" [b] BCC override........%i %s%s%s", - hf14aconfig.forcebcc, - (hf14aconfig.forcebcc == 0) ? "( " _GREEN_("No") " ) follow standard" : "", - (hf14aconfig.forcebcc == 1) ? "( " _RED_("Yes") " ) always do CL2" : "", - (hf14aconfig.forcebcc == 2) ? "( " _RED_("Yes") " ) always use card BCC" : "" + Dbprintf(" [b] BCC override........%s%s%s", + (hf14aconfig.forcebcc == 0) ? _GREEN_("std") " : follow standard" : "", + (hf14aconfig.forcebcc == 1) ? _RED_("fix") " : fix bad BCC" : "", + (hf14aconfig.forcebcc == 2) ? _RED_("ignore") " : ignore bad BCC, always use card BCC" : "" ); - Dbprintf(" [2] CL2 override........%i %s%s%s", - hf14aconfig.forcecl2, - (hf14aconfig.forcecl2 == 0) ? "( " _GREEN_("No") " ) follow standard" : "", - (hf14aconfig.forcecl2 == 1) ? "( " _RED_("Yes") " ) always do CL2" : "", - (hf14aconfig.forcecl2 == 2) ? "( " _RED_("Yes") " ) always skip CL2" : "" + Dbprintf(" [2] CL2 override........%s%s%s", + (hf14aconfig.forcecl2 == 0) ? _GREEN_("std") " : follow standard" : "", + (hf14aconfig.forcecl2 == 1) ? _RED_("force") " : always do CL2" : "", + (hf14aconfig.forcecl2 == 2) ? _RED_("skip") " : always skip CL2" : "" ); - Dbprintf(" [3] CL3 override........%i %s%s%s", - hf14aconfig.forcecl3, - (hf14aconfig.forcecl3 == 0) ? "( " _GREEN_("No") " ) follow standard" : "", - (hf14aconfig.forcecl3 == 1) ? "( " _RED_("Yes") " ) always do CL3" : "", - (hf14aconfig.forcecl3 == 2) ? "( " _RED_("Yes") " ) always skip CL3" : "" + Dbprintf(" [3] CL3 override........%s%s%s", + (hf14aconfig.forcecl3 == 0) ? _GREEN_("std") " : follow standard" : "", + (hf14aconfig.forcecl3 == 1) ? _RED_("force") " : always do CL3" : "", + (hf14aconfig.forcecl3 == 2) ? _RED_("skip") " : always skip CL3" : "" ); - Dbprintf(" [r] RATS override.......%i %s%s%s", - hf14aconfig.forcerats, - (hf14aconfig.forcerats == 0) ? "( " _GREEN_("No") " ) follow standard " : "", - (hf14aconfig.forcerats == 1) ? "( " _RED_("Yes") " ) always do RATS" : "", - (hf14aconfig.forcerats == 2) ? "( " _RED_("Yes") " ) always skip RATS" : "" + Dbprintf(" [r] RATS override.......%s%s%s", + (hf14aconfig.forcerats == 0) ? _GREEN_("std") " : follow standard " : "", + (hf14aconfig.forcerats == 1) ? _RED_("force") " : always do RATS" : "", + (hf14aconfig.forcerats == 2) ? _RED_("skip") " : always skip RATS" : "" ); } diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 32c9568d1..f8652c44a 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -154,7 +154,7 @@ t55xx_configurations_t T55xx_Timing = { { 29 * 8, 17 * 8, 15 * 8, 40 * 8, 15 * 8, 0, 0 }, // Leading 0 { 29 * 8, 17 * 8, 15 * 8, 31 * 8, 15 * 8, 47 * 8, 63 * 8 } // 1 of 4 #else -// PM3OTHER or like offical repo +// PM3GENERIC or like official repo { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 15 * 8, 0, 0 }, // Default Fixed { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 15 * 8, 0, 0 }, // Long Leading Ref. { 31 * 8, 20 * 8, 18 * 8, 40 * 8, 15 * 8, 0, 0 }, // Leading 0 diff --git a/armsrc/start.c b/armsrc/start.c index 70eee5063..062a83eb9 100644 --- a/armsrc/start.c +++ b/armsrc/start.c @@ -14,14 +14,16 @@ #include "proxmark3_arm.h" #include "appmain.h" +#ifndef WITH_NO_COMPRESSION #include "lz4.h" +#endif #include "BigBuf.h" #include "string.h" extern struct common_area common_area; extern char __data_src_start__, __data_start__, __data_end__, __bss_start__, __bss_end__; - +#ifndef WITH_NO_COMPRESSION static void uncompress_data_section(void) { int avail_in; memcpy(&avail_in, &__data_src_start__, sizeof(int)); @@ -35,6 +37,7 @@ static void uncompress_data_section(void) { // save the size of the compressed data section common_area.arg1 = avail_in; } +#endif void __attribute__((section(".startos"))) Vector(void); void Vector(void) { @@ -48,12 +51,20 @@ void Vector(void) { } common_area.flags.osimage_present = 1; + /* Set up data segment: Copy from flash to ram */ +#ifdef WITH_NO_COMPRESSION + char *data_src = &__data_src_start__; + char *data_dst = &__data_start__; + char *data_end = &__data_end__; + while (data_dst < data_end) *data_dst++ = *data_src++; +#else uncompress_data_section(); +#endif /* Set up (that is: clear) BSS. */ - char *dst = &__bss_start__; - char *end = &__bss_end__; - while (dst < end) *dst++ = 0; + char *bss_dst = &__bss_start__; + char *bss_end = &__bss_end__; + while (bss_dst < bss_end) *bss_dst++ = 0; AppMain(); } diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index ec1a28e60..ddfd288b2 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -32,7 +32,7 @@ find_package(PkgConfig) if (NOT SKIPQT EQUAL 1) if(APPLE AND EXISTS /usr/local/opt/qt5) # Homebrew installs Qt5 (up to at least 5.11.0) in - # /usr/local/qt5. Ensure that it can be found by CMake + # /usr/local/opt/qt5. Ensure that it can be found by CMake # since it is not in the default /usr/local prefix. # Add it to PATHS so that it doesn't override the # CMAKE_PREFIX_PATH environment variable. @@ -40,6 +40,16 @@ if (NOT SKIPQT EQUAL 1) # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5) endif(APPLE AND EXISTS /usr/local/opt/qt5) + if(APPLE AND EXISTS /opt/homebrew/opt/qt5) + # Homebrew on Apple Silicon installs Qt5 in + # /opt/homebrew/opt/qt5. Ensure that it can be found by CMake + # since it is not in the default /usr/local prefix. + # Add it to PATHS so that it doesn't override the + # CMAKE_PREFIX_PATH environment variable. + # QT_FIND_PACKAGE_OPTIONS should be passed to find_package, + # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) + list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt5) + endif(APPLE AND EXISTS /opt/homebrew/opt/qt5) set(QT_PACKAGELIST Qt5Core Qt5Widgets @@ -77,8 +87,8 @@ endif (EMBED_READLINE OR EMBED_BZIP2) if (NOT SKIPREADLINE EQUAL 1) if (APPLE) - find_path(READLINE_INCLUDE_DIRS readline/readline.h /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include NO_DEFAULT_PATH) - find_library(READLINE_LIBRARIES readline /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib NO_DEFAULT_PATH) + find_path(READLINE_INCLUDE_DIRS readline/readline.h /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include /opt/homebrew/opt/readline/include NO_DEFAULT_PATH) + find_library(READLINE_LIBRARIES readline /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib /opt/homebrew/opt/readline/lib NO_DEFAULT_PATH) endif (APPLE) if (EMBED_READLINE) ExternalProject_Add(ncurses diff --git a/client/Makefile b/client/Makefile index 9cfc4fc4b..a756a2ede 100644 --- a/client/Makefile +++ b/client/Makefile @@ -17,8 +17,7 @@ vpath %.dic dictionaries OBJDIR = obj ifeq ($(platform),Darwin) - # cf brew info qt: qt not symlinked anymore - PKG_CONFIG_ENV := PKG_CONFIG_PATH=/usr/local/opt/qt/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig endif ################### @@ -115,21 +114,6 @@ STATICLIBS += $(HARDNESTEDLIB) LDLIBS +=$(HARDNESTEDLIBLD) INCLUDES += $(HARDNESTEDLIBINC) -## Jansson -ifneq ($(SKIPJANSSONSYSTEM),1) - JANSSONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags jansson 2>/dev/null) - JANSSONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs jansson 2>/dev/null) - ifneq ($(JANSSONLDLIBS),) - JANSSONLIB = - JANSSONLIBLD = $(JANSSONLDLIBS) - JANSSONLIBINC = $(JANSSONINCLUDES) - JANSSON_FOUND = 1 - endif -endif -STATICLIBS += $(JANSSONLIB) -LDLIBS += $(JANSSONLIBLD) -INCLUDES += $(JANSSONLIBINC) - ## Lua ifneq ($(SKIPLUASYSTEM),1) LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null) @@ -145,6 +129,22 @@ STATICLIBS += $(LUALIB) LDLIBS += $(LUALIBLD) INCLUDES += $(LUALIBINC) +## Jansson +# Jansson section needs to be after Lua to avoid interferences on macOS if a locally incompatible Lua was available, see PR 1155 +ifneq ($(SKIPJANSSONSYSTEM),1) + JANSSONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags jansson 2>/dev/null) + JANSSONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs jansson 2>/dev/null) + ifneq ($(JANSSONLDLIBS),) + JANSSONLIB = + JANSSONLIBLD = $(JANSSONLDLIBS) + JANSSONLIBINC = $(JANSSONINCLUDES) + JANSSON_FOUND = 1 + endif +endif +STATICLIBS += $(JANSSONLIB) +LDLIBS += $(JANSSONLIBLD) +INCLUDES += $(JANSSONLIBINC) + ## mbed TLS # system library cannot be used because it is compiled by default without CMAC support STATICLIBS += $(MBEDTLSLIB) @@ -278,8 +278,8 @@ CXXINCLUDES += $(QTINCLUDES) ## Readline ifneq ($(SKIPREADLINE),1) ifeq ($(platform),Darwin) - LDLIBS += -L/usr/local/opt/readline/lib - INCLUDES += -I/usr/local/opt/readline/include + LDLIBS += -L$(BREW_PREFIX)/opt/readline/lib + INCLUDES += -I$(BREW_PREFIX)/opt/readline/include endif LDLIBS += -lreadline READLINE_FOUND = 1 diff --git a/client/android/CMakeLists.txt b/client/android/CMakeLists.txt index f459748b2..3f45f1073 100644 --- a/client/android/CMakeLists.txt +++ b/client/android/CMakeLists.txt @@ -106,6 +106,7 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdhf15.c ${PM3_ROOT}/client/src/cmdhfcryptorf.c ${PM3_ROOT}/client/src/cmdhfepa.c + ${PM3_ROOT}/client/src/cmdhfemrtd.c ${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffido.c ${PM3_ROOT}/client/src/cmdhficlass.c @@ -126,9 +127,11 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdlfawid.c ${PM3_ROOT}/client/src/cmdlfcotag.c ${PM3_ROOT}/client/src/cmdlfdestron.c - ${PM3_ROOT}/client/src/cmdlfem4x.c + ${PM3_ROOT}/client/src/cmdlfem.c + ${PM3_ROOT}/client/src/cmdlfem410x.c ${PM3_ROOT}/client/src/cmdlfem4x05.c ${PM3_ROOT}/client/src/cmdlfem4x50.c + ${PM3_ROOT}/client/src/cmdlfem4x70.c ${PM3_ROOT}/client/src/cmdlffdxb.c ${PM3_ROOT}/client/src/cmdlfgallagher.c ${PM3_ROOT}/client/src/cmdlfguard.c diff --git a/client/android/pm3_main.c b/client/android/pm3_main.c index 3d2ef917d..1fb1cc95f 100644 --- a/client/android/pm3_main.c +++ b/client/android/pm3_main.c @@ -74,13 +74,13 @@ static bool OpenPm3(void) { jint Console(JNIEnv *env, jobject instance, jstring cmd_) { if (!conn.run) { - if (OpenPm3() && TestProxmark() == PM3_SUCCESS) { + if (OpenPm3() && TestProxmark(session.current_device) == PM3_SUCCESS) { LOGD("Connected to device"); PrintAndLogEx(SUCCESS, "Connected to device"); } else { LOGD("Failed to connect to device"); PrintAndLogEx(ERR, "Failed to connect to device"); - CloseProxmark(); + CloseProxmark(session.current_device); } } @@ -110,10 +110,10 @@ jboolean IsClientRunning(JNIEnv *env, jobject instance) { * */ jboolean TestPm3(JNIEnv *env, jobject instance) { if (open() == false) { - CloseProxmark(); + CloseProxmark(session.current_device); return false; } - bool ret = (TestProxmark() == PM3_SUCCESS); + bool ret = (TestProxmark(session.current_device) == PM3_SUCCESS); return (jboolean)(ret); } @@ -121,7 +121,7 @@ jboolean TestPm3(JNIEnv *env, jobject instance) { * stop pm3 client * */ void ClosePm3(JNIEnv *env, jobject instance) { - CloseProxmark(); + CloseProxmark(session.current_device); } /* diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index f55b7a8f7..6c249e9cb 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -285,4 +285,26 @@ uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_ return rv; } +int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out) { + return arg_get_u32_hexstr_def_nlen(ctx, paramnum, def, out, 4); +} + +int arg_get_u32_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out, uint8_t nlen) { + int n = 0; + uint8_t d[nlen]; + int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &n); + if (res == 0 && n == nlen) { + uint32_t rv = 0; + for (uint8_t i = 0; i < n; i++) { + rv <<= 8; + rv |= d[i]; + } + *out = rv; + return 1; + } else if (res == 0 && n) { + *out = def; + return 2; + } + return 0; +} diff --git a/client/deps/cliparser/cliparser.h b/client/deps/cliparser/cliparser.h index 996ad81ef..40fd5c2f7 100644 --- a/client/deps/cliparser/cliparser.h +++ b/client/deps/cliparser/cliparser.h @@ -68,4 +68,6 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def); +int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out); +int arg_get_u32_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out, uint8_t nlen); #endif diff --git a/client/luascripts/hf_mf_format.lua b/client/luascripts/hf_mf_format.lua index 0bde3c2d1..7aed6b757 100644 --- a/client/luascripts/hf_mf_format.lua +++ b/client/luascripts/hf_mf_format.lua @@ -14,7 +14,7 @@ This script will generate 'hf mf wrbl' commands for each block to format a Mifar Alla datablocks gets 0x00 As default the script sets the keys A/B to 0xFFFFFFFFFFFF and the access bytes will become 0x78,0x77,0x88 -The GDB will become 0x00 +The GPB will become 0x00 The script will skip the manufactoring block 0. ]] @@ -169,7 +169,7 @@ local function main(args) GetCardInfo() -- Show info - print( string.format('Estimating number of blocks: %d', numBlocks)) + print( string.format('Estimating number of blocks: %d', numBlocks + 1)) print( string.format('Old key: %s', OldKey)) print( string.format('New key: %s', NewKey)) print( string.format('New Access: %s', Accessbytes)) diff --git a/client/luascripts/hf_mf_uidbruteforce.lua b/client/luascripts/hf_mf_uidbruteforce.lua index fc85b63bb..548898606 100644 --- a/client/luascripts/hf_mf_uidbruteforce.lua +++ b/client/luascripts/hf_mf_uidbruteforce.lua @@ -99,10 +99,10 @@ local function main(args) local command = '' if mftype == 'mfc' then - command = 'hf 14a sim t 1 u %014x' + command = 'hf 14a sim -t 1 -u %014x' msg('Bruteforcing Mifare Classic card numbers') elseif mftype == 'mfu' then - command = 'hf 14a sim t 2 u %014x' + command = 'hf 14a sim -t 2 -u %014x' msg('Bruteforcing Mifare Ultralight card numbers') else return print(usage) diff --git a/client/luascripts/hf_mfu_setuid.lua b/client/luascripts/hf_mfu_setuid.lua index 86bef09c3..921563ed6 100644 --- a/client/luascripts/hf_mfu_setuid.lua +++ b/client/luascripts/hf_mfu_setuid.lua @@ -4,26 +4,32 @@ local ansicolors = require('ansicolors') copyright = '' author = "Iceman" -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script tries to set UID on a mifare Ultralight magic card which either - answers to chinese backdoor commands - brickable magic tag (must write in one session) + + It defaults to GEN1A type of uid changeable card. ]] example = [[ - -- backdoor magic tag + -- backdoor magic tag (gen1a) script run hf_mfu_setuid -u 11223344556677 - -- brickable magic tag + -- backdoor magic tag (gen1b) script run hf_mfu_setuid -b -u 11223344556677 + + -- brickable magic tag (gen2) + script run hf_mfu_setuid -2 -u 11223344556677 ]] usage = [[ -script run hf_mfu_setuid [-h] [-b] [-u ] +script run hf_mfu_setuid [-h] [-b] [-2] [-u ] ]] arguments = [[ -h : this help -u : UID (14 hexsymbols) - -b : write to brickable magic tag + -b : write to magic tag GEN1B + -2 : write to brickable magic tag GEN2 ]] local DEBUG = true @@ -65,23 +71,33 @@ local function help() end -- --- Set UID on magic command enabled -function magicUID(b0, b1, b2) +function magicUID(b0, b1, b2, isgen1a) - print('Using backdoor Magic tag function') + if isgen1a then + print('Using backdoor Magic tag (gen1a) function') + else + print('Using backdoor Magic tag (gen1b) function') + end -- write block 0 core.console('hf 14a raw -k -a -b 7 40') - core.console('hf 14a raw -k -a 43') + if isgen1a then + core.console('hf 14a raw -k -a 43') + end core.console('hf 14a raw -c -a A200'..b0) -- write block 1 core.console('hf 14a raw -k -a -b 7 40') - core.console('hf 14a raw -k -a 43') + if isgen1a then + core.console('hf 14a raw -k -a 43') + end core.console('hf 14a raw -c -a A201'..b1) -- write block 2 core.console('hf 14a raw -k -a -b 7 40') - core.console('hf 14a raw -k -a 43') + if isgen1a then + core.console('hf 14a raw -k -a 43') + end core.console('hf 14a raw -c -a A202'..b2) end -- @@ -113,10 +129,11 @@ function main(args) local tagtype = 1 -- Read the parameters - for o, a in getopt.getopt(args, 'hu:b') do + for o, a in getopt.getopt(args, 'hu:b2') do if o == 'h' then return help() end if o == 'u' then uid = a end if o == 'b' then tagtype = 2 end + if o == '2' then tagtype = 3 end end -- uid string checks @@ -137,10 +154,11 @@ function main(args) core.clearCommandBuffer() - if tagtype == 2 then + if tagtype == 3 then brickableUID(block0, block1, block2) else - magicUID(block0, block1, block2) + local is_gen1a = (tagtype == 1) + magicUID(block0, block1, block2, is_gen1a) end --halt diff --git a/client/resources/mad.json b/client/resources/mad.json index d5c413d73..c63190cda 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -6096,6 +6096,13 @@ "service_provider": "CDVI", "system_integrator": "CDVI" }, + { + "application": "(access control and security) VIGIK", + "company": "NORALSY", + "mad": "0x4980", + "service_provider": "NORALSY", + "system_integrator": "NORALSY" + }, { "application": "Card Administratin, cardholder adminstration, access control & security, company services, miscellaneous applications", "company": "Ministry of Defense", diff --git a/tools/simmodule/sim011.bin b/client/resources/sim011.bin similarity index 100% rename from tools/simmodule/sim011.bin rename to client/resources/sim011.bin diff --git a/tools/simmodule/sim011.sha512.txt b/client/resources/sim011.sha512.txt similarity index 100% rename from tools/simmodule/sim011.sha512.txt rename to client/resources/sim011.sha512.txt diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index ecf8e50de..02ad3fcd0 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -12,7 +12,7 @@ #include // size_t #include #include // tolower -//#include // printf +#include #include "commonutil.h" // reflect... #include "comms.h" // clearCommandBuffer #include "cmdparser.h" // command_t @@ -22,88 +22,13 @@ #include "tea.h" #include "legic_prng.h" #include "cmddata.h" // demodbuffer +#include "graph.h" +#include "proxgui.h" +#include "cliparser.h" +#include "generator.h" // generate nuid static int CmdHelp(const char *Cmd); -static int usage_analyse_lcr(void) { - PrintAndLogEx(NORMAL, "Specifying the bytes of a UID with a known LRC will find the last byte value"); - PrintAndLogEx(NORMAL, "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse lcr [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " bytes to calc missing XOR in a LCR"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse lcr 04008064BA"); - PrintAndLogEx(NORMAL, "expected output: Target (BA) requires final LRC XOR byte value: 5A"); - return PM3_SUCCESS; -} -static int usage_analyse_checksum(void) { - PrintAndLogEx(NORMAL, "The bytes will be added with eachother and than limited with the applied mask"); - PrintAndLogEx(NORMAL, "Finally compute ones' complement of the least significant bytes"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse chksum [h] [v] b m "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " v suppress header"); - PrintAndLogEx(NORMAL, " b bytes to calc missing XOR in a LCR"); - PrintAndLogEx(NORMAL, " m bit mask to limit the outpuyt"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse chksum b 137AF00A0A0D m FF"); - PrintAndLogEx(NORMAL, "expected output: 0x61"); - return PM3_SUCCESS; -} -static int usage_analyse_crc(void) { - PrintAndLogEx(NORMAL, "A stub method to test different crc implementations inside the PM3 sourcecode. Just because you figured out the poly, doesn't mean you get the desired output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse crc [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " bytes to calc crc"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse crc 137AF00A0A0D"); - return PM3_SUCCESS; -} -static int usage_analyse_nuid(void) { - PrintAndLogEx(NORMAL, "Generate 4byte NUID from 7byte UID"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse hid [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " input bytes (14 hexsymbols)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse nuid 11223344556677"); - return PM3_SUCCESS; -} -static int usage_analyse_a(void) { - PrintAndLogEx(NORMAL, "Iceman's personal garbage test command"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse a [h] d "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " d bytes to send to device"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse a d 137AF00A0A0D"); - return PM3_SUCCESS; -} -static int usage_analyse_demodbuffer(void) { - PrintAndLogEx(NORMAL, "loads a binary string into demod buffer"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse demodbuff [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " Binary string to load"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse demodbuff 0011101001001011"); - return PM3_SUCCESS; -} - static uint8_t calculateLRC(uint8_t *bytes, uint8_t len) { uint8_t LRC = 0; for (uint8_t i = 0; i < len; i++) @@ -247,226 +172,323 @@ static uint16_t calcBSDchecksum4(uint8_t *bytes, uint8_t len, uint32_t mask) { // measuring LFSR maximum length static int CmdAnalyseLfsr(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse lfsr", + "looks at LEGIC Prime's lfsr, iterates the first 48 values", + "analyse lfsr --iv 55" + ); - uint8_t iv = param_get8ex(Cmd, 0, 0, 16); - uint8_t find = param_get8ex(Cmd, 1, 0, 16); + void *argtable[] = { + arg_param_begin, + arg_str1(NULL, "iv", "", "init vector data (1 hex byte)"), + arg_str0(NULL, "find", "", "lfsr data to find (1 hex byte)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int iv_len = 0; + uint8_t idata[1] = {0}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idata, sizeof(idata), &iv_len); - PrintAndLogEx(NORMAL, "LEGIC LFSR IV 0x%02X: \n", iv); - PrintAndLogEx(NORMAL, " bit# | lfsr | ^0x40 | 0x%02X ^ lfsr \n", find); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing IV byte"); + return PM3_EINVARG; + } + + int f_len = 0; + uint8_t fdata[1] = {0}; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), fdata, sizeof(fdata), &f_len); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing FIND byte"); + return PM3_EINVARG; + } + + uint8_t iv = idata[0]; + uint8_t find = fdata[0]; + + PrintAndLogEx(INFO, "LEGIC Prime lfsr"); + PrintAndLogEx(INFO, "iv..... 0x%02X", iv); + PrintAndLogEx(INFO, "----+------+-------+--------------"); + PrintAndLogEx(INFO, " i# | lfsr | ^0x40 | 0x%02X ^ lfsr", find); + PrintAndLogEx(INFO, "----+------+-------+--------------"); for (uint8_t i = 0x01; i < 0x30; i += 1) { legic_prng_init(iv); legic_prng_forward(i); uint16_t lfsr = legic_prng_get_bits(12); /* Any nonzero start state will work. */ - PrintAndLogEx(NORMAL, " %02X | %03X | %03X | %03X \n", i, lfsr, 0x40 ^ lfsr, find ^ lfsr); + PrintAndLogEx(INFO, " %02X | %03X | %03X | %03X", i, lfsr, 0x40 ^ lfsr, find ^ lfsr); } - return 0; + PrintAndLogEx(INFO, "----+------+-------+--------------"); + return PM3_SUCCESS; } + static int CmdAnalyseLCR(const char *Cmd) { - uint8_t data[50]; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_lcr(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse lcr", + "Specifying the bytes of a UID with a known LRC will find the last byte value\n" + "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", + "analyse lcr -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" + ); - int len = 0; - switch (param_gethex_to_eol(Cmd, 0, data, sizeof(data), &len)) { - case 1: - PrintAndLogEx(WARNING, "Invalid HEX value."); - return 1; - case 2: - PrintAndLogEx(WARNING, "Too many bytes. Max %zu bytes", sizeof(data)); - return 1; - case 3: - PrintAndLogEx(WARNING, "Hex must have even number of digits."); - return 1; + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calc missing XOR in a LCR"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[100] = {0x00}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; } - uint8_t finalXor = calculateLRC(data, len); - PrintAndLogEx(NORMAL, "Target [%02X] requires final LRC XOR byte value: 0x%02X", data[len - 1], finalXor); - return 0; + + uint8_t finalXor = calculateLRC(data, dlen); + PrintAndLogEx(SUCCESS, "Target [%02X] requires final LRC XOR byte value: " _YELLOW_("0x%02X"), data[dlen - 1], finalXor); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } + static int CmdAnalyseCRC(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse crc", + "A stub method to test different crc implementations inside the PM3 sourcecode.\n" + "Just because you figured out the poly, doesn't mean you get the desired output", + "analyse crc -d 137AF00A0A0D" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_crc(); + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calc crc"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[1024] = {0x00}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + CLIParserFree(ctx); - int len = strlen(Cmd); - if (len & 1) return usage_analyse_crc(); - - // add 1 for null terminator. - uint8_t *data = calloc(len + 1, sizeof(uint8_t)); - if (!data) return 1; - - if (param_gethex(Cmd, 0, data, len)) { - free(data); - return usage_analyse_crc(); + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; } - len >>= 1; - PrintAndLogEx(NORMAL, "\nTests with (%d) | %s", len, sprint_hex(data, len)); + PrintAndLogEx(INFO, "\nTests with (%d) | %s", dlen, sprint_hex(data, dlen)); // 51 f5 7a d6 uint8_t uid[] = {0x51, 0xf5, 0x7a, 0xd6}; //12 34 56 init_table(CRC_LEGIC); uint8_t legic8 = CRC8Legic(uid, sizeof(uid)); - PrintAndLogEx(NORMAL, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, len, legic8), legic8); + PrintAndLogEx(INFO, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, dlen, legic8), legic8); init_table(CRC_FELICA); - PrintAndLogEx(NORMAL, "FeliCa | %X ", crc16_xmodem(data, len)); + PrintAndLogEx(INFO, "FeliCa | %X ", crc16_xmodem(data, dlen)); - PrintAndLogEx(NORMAL, "\nTests of reflection. Current methods in source code"); - PrintAndLogEx(NORMAL, " reflect(0x3e23L,3) is %04X == 0x3e26", reflect(0x3e23L, 3)); - PrintAndLogEx(NORMAL, " reflect8(0x80) is %02X == 0x01", reflect8(0x80)); - PrintAndLogEx(NORMAL, " reflect16(0x8000) is %04X == 0x0001", reflect16(0xc6c6)); + PrintAndLogEx(INFO, "\nTests of reflection. Current methods in source code"); + PrintAndLogEx(INFO, " reflect(0x3e23L,3) is %04X == 0x3e26", reflect(0x3e23L, 3)); + PrintAndLogEx(INFO, " reflect8(0x80) is %02X == 0x01", reflect8(0x80)); + PrintAndLogEx(INFO, " reflect16(0x8000) is %04X == 0x0001", reflect16(0xc6c6)); uint8_t b1, b2; // ISO14443 crc B - compute_crc(CRC_14443_B, data, len, &b1, &b2); + compute_crc(CRC_14443_B, data, dlen, &b1, &b2); uint16_t crcBB_1 = b1 << 8 | b2; - uint16_t bbb = Crc16ex(CRC_14443_B, data, len); - PrintAndLogEx(NORMAL, "ISO14443 crc B | %04x == %04x \n", crcBB_1, bbb); + uint16_t bbb = Crc16ex(CRC_14443_B, data, dlen); + PrintAndLogEx(INFO, "ISO14443 crc B | %04x == %04x \n", crcBB_1, bbb); // Test of CRC16, '123456789' string. // - PrintAndLogEx(NORMAL, "\n\nStandard test with 31 32 33 34 35 36 37 38 39 '123456789'\n\n"); + PrintAndLogEx(INFO, "\n\nStandard test with 31 32 33 34 35 36 37 38 39 '123456789'\n\n"); uint8_t dataStr[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; legic8 = CRC8Legic(dataStr, sizeof(dataStr)); //these below has been tested OK. - PrintAndLogEx(NORMAL, "Confirmed CRC Implementations"); - PrintAndLogEx(NORMAL, "-------------------------------------\n"); - PrintAndLogEx(NORMAL, "CRC 8 based\n\n"); - PrintAndLogEx(NORMAL, "LEGIC: CRC8 : %X (C6 expected)", legic8); - PrintAndLogEx(NORMAL, "MAXIM: CRC8 : %X (A1 expected)", CRC8Maxim(dataStr, sizeof(dataStr))); - PrintAndLogEx(NORMAL, "-------------------------------------\n"); - PrintAndLogEx(NORMAL, "CRC16 based\n\n"); + PrintAndLogEx(INFO, "Confirmed CRC Implementations"); + PrintAndLogEx(INFO, "-------------------------------------\n"); + PrintAndLogEx(INFO, "CRC 8 based\n\n"); + PrintAndLogEx(INFO, "LEGIC: CRC8 : %X (C6 expected)", legic8); + PrintAndLogEx(INFO, "MAXIM: CRC8 : %X (A1 expected)", CRC8Maxim(dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "-------------------------------------\n"); + PrintAndLogEx(INFO, "CRC16 based\n\n"); // input from commandline - PrintAndLogEx(NORMAL, "CCITT | %X (29B1 expected)", Crc16ex(CRC_CCITT, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "CCITT | %X (29B1 expected)", Crc16ex(CRC_CCITT, dataStr, sizeof(dataStr))); uint8_t poll[] = {0xb2, 0x4d, 0x12, 0x01, 0x01, 0x2e, 0x3d, 0x17, 0x26, 0x47, 0x80, 0x95, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0xb3, 0x7f}; - PrintAndLogEx(NORMAL, "FeliCa | %04X (B37F expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 4)); - PrintAndLogEx(NORMAL, "FeliCa | %04X (0000 expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 2)); + PrintAndLogEx(INFO, "FeliCa | %04X (B37F expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 4)); + PrintAndLogEx(INFO, "FeliCa | %04X (0000 expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 2)); uint8_t sel_corr[] = { 0x40, 0xe1, 0xe1, 0xff, 0xfe, 0x5f, 0x02, 0x3c, 0x43, 0x01}; - PrintAndLogEx(NORMAL, "iCLASS | %04x (0143 expected)", Crc16ex(CRC_ICLASS, sel_corr, sizeof(sel_corr) - 2)); - PrintAndLogEx(NORMAL, "---------------------------------------------------------------\n\n\n"); + PrintAndLogEx(INFO, "iCLASS | %04x (0143 expected)", Crc16ex(CRC_ICLASS, sel_corr, sizeof(sel_corr) - 2)); + PrintAndLogEx(INFO, "---------------------------------------------------------------\n\n\n"); // ISO14443 crc A compute_crc(CRC_14443_A, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcAA = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ISO14443 crc A | %04x or %04x (BF05 expected)\n", crcAA, Crc16ex(CRC_14443_A, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ISO14443 crc A | %04x or %04x (BF05 expected)\n", crcAA, Crc16ex(CRC_14443_A, dataStr, sizeof(dataStr))); // ISO14443 crc B compute_crc(CRC_14443_B, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcBB = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ISO14443 crc B | %04x or %04x (906E expected)\n", crcBB, Crc16ex(CRC_14443_B, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ISO14443 crc B | %04x or %04x (906E expected)\n", crcBB, Crc16ex(CRC_14443_B, dataStr, sizeof(dataStr))); // ISO15693 crc (x.25) compute_crc(CRC_15693, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcCC = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ISO15693 crc X25| %04x or %04x (906E expected)\n", crcCC, Crc16ex(CRC_15693, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ISO15693 crc X25| %04x or %04x (906E expected)\n", crcCC, Crc16ex(CRC_15693, dataStr, sizeof(dataStr))); // ICLASS compute_crc(CRC_ICLASS, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcDD = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ICLASS crc | %04x or %04x\n", crcDD, Crc16ex(CRC_ICLASS, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ICLASS crc | %04x or %04x\n", crcDD, Crc16ex(CRC_ICLASS, dataStr, sizeof(dataStr))); // FeliCa compute_crc(CRC_FELICA, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcEE = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "FeliCa | %04x or %04x (31C3 expected)\n", crcEE, Crc16ex(CRC_FELICA, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "FeliCa | %04x or %04x (31C3 expected)\n", crcEE, Crc16ex(CRC_FELICA, dataStr, sizeof(dataStr))); - free(data); - return 0; + return PM3_SUCCESS; } + static int CmdAnalyseCHKSUM(const char *Cmd) { - uint8_t data[50]; - uint8_t cmdp = 0; - uint32_t mask = 0xFFFF; - bool errors = false; - bool useHeader = false; - int len = 0; - memset(data, 0x0, sizeof(data)); + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse chksum", + "The bytes will be added with eachother and than limited with the applied mask\n" + "Finally compute ones' complement of the least significant bytes.", + "analyse chksum -d 137AF00A0A0D -> expected output: 0x61\n" + "analyse chksum -d 137AF00A0A0D -m FF" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (param_getchar(Cmd, cmdp)) { - case 'b': - case 'B': - param_gethex_ex(Cmd, cmdp + 1, data, &len); - if (len % 2) errors = true; - len >>= 1; - cmdp += 2; - break; - case 'm': - case 'M': - mask = param_get32ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'v': - case 'V': - useHeader = true; - cmdp++; - break; - case 'h': - case 'H': - return usage_analyse_checksum(); - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calc checksum"), + arg_str0("m", "mask", "", "bit mask to limit the output (4 hex bytes max)"), + arg_lit0("v", "verbose", "verbose"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[100] = {0x00}; + memset(data, 0x0, sizeof(data)); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + const char *s = arg_get_str(ctx, 2)->sval[0]; + bool verbose = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + uint32_t mlen = 0; + if (s) + mlen = strlen(s); + + if (mlen > 8) { + PrintAndLogEx(FAILED, "Mask value is max 4 hex bytes"); + return PM3_EINVARG; + } + + uint32_t mask = 0; + if (mlen == 0) { + mask = 0xFFFF; + } else { + for (int i = 0; i < mlen; i++) { + char c = s[i]; + // capitalize + if (c >= 'a' && c <= 'f') + c -= 32; + // convert to numeric value + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else + continue; + + mask <<= 4; + mask |= c; } } - //Validations - if (errors || cmdp == 0) return usage_analyse_checksum(); - if (useHeader) { - PrintAndLogEx(NORMAL, " add | sub | add 1's compl | sub 1's compl | xor"); - PrintAndLogEx(NORMAL, "byte nibble crumb | byte nibble | byte nibble cumb | byte nibble | byte nibble cumb | BSD |"); - PrintAndLogEx(NORMAL, "------------------+-------------+------------------+-----------------+--------------------"); + PrintAndLogEx(INFO, "Mask value 0x%x", mask); + + if (verbose) { + PrintAndLogEx(INFO, " add | sub | add 1's compl | sub 1's compl | xor"); + PrintAndLogEx(INFO, "byte nibble crumb | byte nibble | byte nibble cumb | byte nibble | byte nibble cumb | BSD |"); + PrintAndLogEx(INFO, "------------------+-------------+------------------+-----------------+--------------------"); } - PrintAndLogEx(NORMAL, "0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X |\n", - calcSumByteAdd(data, len, mask) - , calcSumNibbleAdd(data, len, mask) - , calcSumCrumbAdd(data, len, mask) - , calcSumByteSub(data, len, mask) - , calcSumNibbleSub(data, len, mask) - , calcSumByteAddOnes(data, len, mask) - , calcSumNibbleAddOnes(data, len, mask) - , calcSumCrumbAddOnes(data, len, mask) - , calcSumByteSubOnes(data, len, mask) - , calcSumNibbleSubOnes(data, len, mask) - , calcSumByteXor(data, len, mask) - , calcSumNibbleXor(data, len, mask) - , calcSumCrumbXor(data, len, mask) - , calcBSDchecksum8(data, len, mask) - , calcBSDchecksum4(data, len, mask) + PrintAndLogEx(INFO, "0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X |\n", + calcSumByteAdd(data, dlen, mask) + , calcSumNibbleAdd(data, dlen, mask) + , calcSumCrumbAdd(data, dlen, mask) + , calcSumByteSub(data, dlen, mask) + , calcSumNibbleSub(data, dlen, mask) + , calcSumByteAddOnes(data, dlen, mask) + , calcSumNibbleAddOnes(data, dlen, mask) + , calcSumCrumbAddOnes(data, dlen, mask) + , calcSumByteSubOnes(data, dlen, mask) + , calcSumNibbleSubOnes(data, dlen, mask) + , calcSumByteXor(data, dlen, mask) + , calcSumNibbleXor(data, dlen, mask) + , calcSumCrumbXor(data, dlen, mask) + , calcBSDchecksum8(data, dlen, mask) + , calcBSDchecksum4(data, dlen, mask) ); - return 0; + return PM3_SUCCESS; } static int CmdAnalyseDates(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - // look for datestamps in a given array of bytes - PrintAndLogEx(NORMAL, "To be implemented. Feel free to contribute!"); - return 0; -} -static int CmdAnalyseTEASelfTest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse dates", + "Tool to look for date/time stamps in a given array of bytes", + "analyse dates" + ); - uint8_t v[8], v_le[8]; - memset(v, 0x00, sizeof(v)); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + PrintAndLogEx(NORMAL, "To be implemented. Feel free to contribute!"); + return PM3_SUCCESS; +} + +static int CmdAnalyseTEASelfTest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse tea", + "Crypto TEA self tests", + "analyse tea -d 1122334455667788" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to encrypt ( 8 hex bytes )"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[8] = {0x00}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + CLIParserFree(ctx); + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + uint8_t v_le[8]; memset(v_le, 0x00, sizeof(v_le)); uint8_t *v_ptr = v_le; - uint8_t cmdlen = strlen(Cmd); - cmdlen = (sizeof(v) << 2 < cmdlen) ? sizeof(v) << 2 : cmdlen; - - if (param_gethex(Cmd, 0, v, cmdlen) > 0) { - PrintAndLogEx(WARNING, "Can't read hex chars, uneven? :: %u", cmdlen); - return 1; - } - - SwapEndian64ex(v, 8, 4, v_ptr); + SwapEndian64ex(data, 8, 4, v_ptr); // ENCRYPTION KEY: uint8_t key[16] = {0x55, 0xFE, 0xF6, 0x30, 0x62, 0xBF, 0x0B, 0xC1, 0xC9, 0xB3, 0x7C, 0x34, 0x97, 0x3E, 0x29, 0xFB }; @@ -474,49 +496,47 @@ static int CmdAnalyseTEASelfTest(const char *Cmd) { uint8_t *key_ptr = keyle; SwapEndian64ex(key, sizeof(key), 4, key_ptr); - PrintAndLogEx(NORMAL, "TEST LE enc| %s", sprint_hex(v_ptr, 8)); + PrintAndLogEx(INFO, "TEA crypto testing"); + PrintAndLogEx(INFO, "-----------------------------------+---------"); + PrintAndLogEx(INFO, "LE enc.... %s", sprint_hex_ascii(v_ptr, 8)); tea_decrypt(v_ptr, key_ptr); - PrintAndLogEx(NORMAL, "TEST LE dec | %s", sprint_hex_ascii(v_ptr, 8)); + PrintAndLogEx(INFO, "LE dec.... %s", sprint_hex_ascii(v_ptr, 8)); tea_encrypt(v_ptr, key_ptr); + PrintAndLogEx(INFO, "enc1...... %s", sprint_hex_ascii(v_ptr, 8)); tea_encrypt(v_ptr, key_ptr); - PrintAndLogEx(NORMAL, "TEST enc2 | %s", sprint_hex_ascii(v_ptr, 8)); - - return 0; + PrintAndLogEx(INFO, "enc2...... %s", sprint_hex_ascii(v_ptr, 8)); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } -/* -static char *pb(uint32_t b) { - static char buf1[33] = {0}; - static char buf2[33] = {0}; - static char *s; - - if (s != buf1) - s = buf1; - else - s = buf2; - - memset(s, 0, sizeof(buf1)); - - uint32_t mask = 0x80000000; - for (uint8_t i = 0; i < 32; i++) { - s[i] = (mask & b) ? '1' : '0'; - mask >>= 1; - } - return s; -} -*/ - static int CmdAnalyseA(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse a", + "Iceman's personal garbage test command", + "analyse a -d 137AF00A0A0D" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to manipulate"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[100] = {0x00}; + memset(data, 0x0, sizeof(data)); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + CLIParserFree(ctx); + return PM3_SUCCESS; - return usage_analyse_a(); /* - PrintAndLogEx(NORMAL, "-- " _BLUE_("its my message") "\n"); - PrintAndLogEx(NORMAL, "-- " _RED_("its my message") "\n"); - PrintAndLogEx(NORMAL, "-- " _YELLOW_("its my message") "\n"); - PrintAndLogEx(NORMAL, "-- " _GREEN_("its my message") "\n"); - //uint8_t syncBit = 99; // The start bit is one ore more Sequence Y followed by a Sequence Z (... 11111111 00x11111). We need to distinguish from // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) @@ -528,28 +548,22 @@ static int CmdAnalyseA(const char *Cmd) { uint8_t byte_offset = 99; // reverse byte uint8_t rev = reflect8(bt); - PrintAndLogEx(NORMAL, "input %02x | %02x \n", bt, rev); + PrintAndLogEx(INFO, "input %02x | %02x \n", bt, rev); // add byte to shift register shiftReg = shiftReg << 8 | rev; - PrintAndLogEx(NORMAL, "shiftreg after %08x | pattern %08x \n", shiftReg, SYNC_16BIT); + PrintAndLogEx(INFO, "shiftreg after %08x | pattern %08x \n", shiftReg, SYNC_16BIT); uint8_t n0 = 0, n1 = 0; n0 = (rev & (uint8_t)(~(0xFF >> (8 - 4)))) >> 4; n1 = (n1 << 4) | (rev & (uint8_t)(~(0xFF << 4))); - PrintAndLogEx(NORMAL, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1)); + PrintAndLogEx(INFO, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1)); */ - /* - hex(0xb24d shr 0) 0xB24D 0b1011001001001101 - hex(0xb24d shr 1) 0x5926 - hex(0xb24d shr 2) 0x2C93 - */ - /* for (int i = 0; i < 16; i++) { - PrintAndLogEx(NORMAL, " (shiftReg >> %d) & 0xFFFF == %08x ---", i, ((shiftReg >> i) & 0xFFFF)); + PrintAndLogEx(INFO, " (shiftReg >> %d) & 0xFFFF == %08x ---", i, ((shiftReg >> i) & 0xFFFF)); // kolla om SYNC_PATTERN finns. if (((shiftReg >> 7) & 0xFFFF) == SYNC_16BIT) byte_offset = 7; @@ -561,7 +575,7 @@ static int CmdAnalyseA(const char *Cmd) { else if (((shiftReg >> 1) & 0xFFFF) == SYNC_16BIT) byte_offset = 1; else if (((shiftReg >> 0) & 0xFFFF) == SYNC_16BIT) byte_offset = 0; - PrintAndLogEx(NORMAL, "Offset %u \n", byte_offset); + PrintAndLogEx(INFO, "Offset %u \n", byte_offset); if (byte_offset != 99) break; @@ -569,14 +583,14 @@ static int CmdAnalyseA(const char *Cmd) { } uint8_t p1 = (rev & (uint8_t)(~(0xFF << byte_offset))); - PrintAndLogEx(NORMAL, "Offset %u | leftovers %02x %s \n", byte_offset, p1, pb(p1)); + PrintAndLogEx(INFO, "Offset %u | leftovers %02x %s \n", byte_offset, p1, pb(p1)); */ /* - pm3 --> da hex2bin 4db2 0100110110110010 + pm3 --> da hex2bin 4db2 0100110110110010 */ - //return 0; + //return PM3_SUCCESS; /* // split byte into two parts. uint8_t offset = 3, n0 = 0, n1 = 0; @@ -586,23 +600,23 @@ static int CmdAnalyseA(const char *Cmd) { n0 = (rev & (uint8_t)(~(0xFF >> (8-offset)))) >> offset; n1 = (n1 << offset) | (rev & (uint8_t)(~(0xFF << offset))); - PrintAndLogEx(NORMAL, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1) ); + PrintAndLogEx(INFO, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1) ); n0 = 0, n1 = 0; - // PrintAndLogEx(NORMAL, " (0xFF >> offset) == %s |\n", pb( (0xFF >> offset)) ); - //PrintAndLogEx(NORMAL, "~(0xFF >> (8-offset)) == %s |\n", pb( (uint8_t)(~(0xFF >> (8-offset))) ) ); - //PrintAndLogEx(NORMAL, " rev & xxx == %s\n\n", pb( (rev & (uint8_t)(~(0xFF << offset))) )); + // PrintAndLogEx(INFO, " (0xFF >> offset) == %s |\n", pb( (0xFF >> offset)) ); + //PrintAndLogEx(INFO, "~(0xFF >> (8-offset)) == %s |\n", pb( (uint8_t)(~(0xFF >> (8-offset))) ) ); + //PrintAndLogEx(INFO, " rev & xxx == %s\n\n", pb( (rev & (uint8_t)(~(0xFF << offset))) )); } - return 0; + return PM3_SUCCESS; // from A -- x bits into B and the rest into C. for ( uint8_t i=0; i<8; i++){ - PrintAndLogEx(NORMAL, "%u | %02X %s | %02X %s |\n", i, a, pb(a), b, pb(b) ); + PrintAndLogEx(INFO, "%u | %02X %s | %02X %s |\n", i, a, pb(a), b, pb(b) ); b = a & (a & (0xFF >> (8-i))); a >>=1; } */ -// return 0; +// return PM3_SUCCESS; /* // 14443-A @@ -613,7 +627,7 @@ static int CmdAnalyseA(const char *Cmd) { // 14443-B uint8_t u14b[] = {0x05, 0x00, 0x08, 0x39, 0x73}; - PrintAndLogEx(NORMAL, "14b check crc | %s\n", (check_crc(CRC_14443_B, u14b, sizeof(u14b))) ? "YES" : "NO"); + PrintAndLogEx(INFO, "14b check crc | %s\n", (check_crc(CRC_14443_B, u14b, sizeof(u14b))) ? "YES" : "NO"); // 15693 test uint8_t u15_c[] = {0x05, 0x00, 0x08, 0x39, 0x73}; // correct @@ -633,28 +647,10 @@ static int CmdAnalyseA(const char *Cmd) { PrintAndLogEx(FAILED, "FeliCa check wrong crc | %s\n", (check_crc(CRC_FELICA, felica_w, sizeof(felica_w))) ? "YES" : "NO"); PrintAndLogEx(SUCCESS, "FeliCa check correct crc | %s\n", (check_crc(CRC_FELICA, felica_c, sizeof(felica_c))) ? "YES" : "NO"); - PrintAndLogEx(NORMAL, "\n\n"); + PrintAndLogEx(NORMAL, "\n"); - return 0; + return PM3_SUCCESS; */ - /* - bool term = !isatty(STDIN_FILENO); - if (!term) { - char star[4]; - star[0] = '-'; - star[1] = '\\'; - star[2] = '|'; - star[3] = '/'; - - for (uint8_t k=0; k<4; k = (k+1) % 4 ) { - PrintAndLogEx(NORMAL, "\e[s%c\e[u", star[k]); - fflush(stdout); - if (kbd_enter_pressed()) { - break; - } - } - } - */ //piwi // uid(2e086b1a) nt(230736f6) ks(0b0008000804000e) nr(000000000) @@ -789,7 +785,7 @@ static int CmdAnalyseA(const char *Cmd) { /* for (uint8_t i=0; i<31; i++){ uint64_t a = keys[i] ^ keys[i+1]; - PrintAndLogEx(NORMAL, "%u | %012" PRIX64 " | \n", i, a); + PrintAndLogEx(INFO, "%u | %012" PRIX64 " | \n", i, a); } */ @@ -807,8 +803,8 @@ static int CmdAnalyseA(const char *Cmd) { 0x2F }; - PrintAndLogEx(NORMAL, "UID | %s\n", sprint_hex(uid,4 )); - PrintAndLogEx(NORMAL, "KEY A | %s\n", sprint_hex(key_s0a, 6)); + PrintAndLogEx(INFO, "UID | %s\n", sprint_hex(uid,4 )); + PrintAndLogEx(INFO, "KEY A | %s\n", sprint_hex(key_s0a, 6)); // arrays w all keys uint64_t foo[32] = {0}; @@ -828,7 +824,7 @@ static int CmdAnalyseA(const char *Cmd) { uint64_t a = foo[i]; uint64_t b = foo[i+16]; - PrintAndLogEx(NORMAL, "%02u | %012" PRIX64 " %s | %012" PRIX64 " %s\n", + PrintAndLogEx(INFO, "%02u | %012" PRIX64 " %s | %012" PRIX64 " %s\n", i, a, ( a == keya[i])?"ok":"err", @@ -837,78 +833,110 @@ static int CmdAnalyseA(const char *Cmd) { ); } */ -// return 0; -} - -static void generate4bNUID(uint8_t *uid, uint8_t *nuid) { - uint16_t crc; - uint8_t b1, b2; - - compute_crc(CRC_14443_A, uid, 3, &b1, &b2); - nuid[0] = (b2 & 0xE0) | 0xF; - nuid[1] = b1; - crc = b1; - crc |= b2 << 8; - crc = crc16_fast(&uid[3], 4, reflect16(crc), true, true); - nuid[2] = (crc >> 8) & 0xFF ; - nuid[3] = crc & 0xFF; +// return PM3_SUCCESS; } static int CmdAnalyseNuid(const char *Cmd) { - uint8_t nuid[4] = {0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse nuid", + "Generate 4byte NUID from 7byte UID", + "analyse nuid -d 11223344556677" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", "data", "", "bytes to send"), + arg_lit0("t", "test", "self test"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int uidlen = 0; uint8_t uid[7] = {0}; - int len = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_nuid(); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), uid, sizeof(uid), &uidlen); + bool selftest = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + uint8_t nuid[4] = {0}; /* src: https://www.nxp.com/docs/en/application-note/AN10927.pdf */ /* selftest1 UID 040D681AB52281 -> NUID 8F430FEF */ /* selftest2 UID 04183F09321B85 -> NUID 4F505D7D */ - if (cmdp == 't') { + if (selftest) { uint8_t uid_test1[] = {0x04, 0x0d, 0x68, 0x1a, 0xb5, 0x22, 0x81}; uint8_t nuid_test1[] = {0x8f, 0x43, 0x0f, 0xef}; uint8_t uid_test2[] = {0x04, 0x18, 0x3f, 0x09, 0x32, 0x1b, 0x85}; uint8_t nuid_test2[] = {0x4f, 0x50, 0x5d, 0x7d}; memcpy(uid, uid_test1, sizeof(uid)); - generate4bNUID(uid, nuid); + mfc_generate4b_nuid(uid, nuid); + PrintAndLogEx(INFO, "Self tests"); bool test1 = (0 == memcmp(nuid, nuid_test1, sizeof(nuid))); - PrintAndLogEx(SUCCESS, "Selftest1 %s\n", test1 ? _GREEN_("OK") : _RED_("Fail")); + PrintAndLogEx((test1) ? SUCCESS : FAILED, "1. %s -> %s ( %s )" + , sprint_hex_inrow(uid_test1, sizeof(uid_test1)) + , sprint_hex(nuid, sizeof(nuid)) + , test1 ? _GREEN_("ok") : _RED_("fail") + ); memcpy(uid, uid_test2, sizeof(uid)); - generate4bNUID(uid, nuid); + mfc_generate4b_nuid(uid, nuid); bool test2 = (0 == memcmp(nuid, nuid_test2, sizeof(nuid))); - PrintAndLogEx(SUCCESS, "Selftest2 %s\n", test2 ? _GREEN_("OK") : _RED_("Fail")); - return 0; + PrintAndLogEx((test2) ? SUCCESS : FAILED, "2. %s -> %s ( %s )\n" + , sprint_hex_inrow(uid_test2, sizeof(uid_test2)) + , sprint_hex(nuid, sizeof(nuid)) + , test2 ? _GREEN_("ok") : _RED_("fail") + ); + + return PM3_SUCCESS; } - param_gethex_ex(Cmd, 0, uid, &len); - if (len % 2 || len != 14) return usage_analyse_nuid(); + if (uidlen != 7) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } - generate4bNUID(uid, nuid); + mfc_generate4b_nuid(uid, nuid); - PrintAndLogEx(NORMAL, "UID | %s \n", sprint_hex(uid, 7)); - PrintAndLogEx(NORMAL, "NUID | %s \n", sprint_hex(nuid, 4)); - return 0; + PrintAndLogEx(INFO, "UID | %s \n", sprint_hex(uid, 7)); + PrintAndLogEx(INFO, "NUID | %s \n", sprint_hex(nuid, 4)); + return PM3_SUCCESS; } static int CmdAnalyseDemodBuffer(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse demodbuff", + "loads a binary string into demod buffer", + "analyse demodbuff -d 0011101001001011" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_demodbuffer(); + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "binary string to load"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + const char *s = arg_get_str(ctx, 1)->sval[0]; + CLIParserFree(ctx); - int bg = 0, en = 0; - if (param_getptr(Cmd, &bg, &en, 0)) - return usage_analyse_demodbuffer(); + if (s == NULL) { + PrintAndLogEx(FAILED, "Must provide a binary string"); + return PM3_EINVARG; + } - int len = MIN((en - bg + 1), MAX_DEMOD_BUF_LEN); + int len = MIN(strlen(s), MAX_DEMOD_BUF_LEN); // add 1 for null terminator. uint8_t *data = calloc(len + 1, sizeof(uint8_t)); - if (!data) return PM3_EMALLOC; + if (data == NULL) + return PM3_EMALLOC; - for (int i = 0; bg <= en; bg++, i++) { - char c = Cmd[bg]; + for (int i = 0; i <= strlen(s); i++) { + char c = s[i]; if (c == '1') DemodBuffer[i] = 1; if (c == '0') @@ -916,28 +944,114 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { PrintAndLogEx(NORMAL, "%c" NOLF, c); } - PrintAndLogEx(NORMAL, ""); - DemodBufferLen = len; free(data); + PrintAndLogEx(HINT, "Use `" _YELLOW_("data print") "` to view demod buffer"); return PM3_SUCCESS; } static int CmdAnalyseFreq(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse freq", + "calc wave lengths", + "analyse freq" + ); -// char cmdp = tolower(param_getchar(Cmd, 0)); -// if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_freq(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); const double c = 299792458; double len_125 = c / 125000; double len_134 = c / 134000; double len_1356 = c / 13560000; + double rf_range_125 = len_125 / (M_PI * 2); + double rf_range_134 = len_134 / (M_PI * 2); + double rf_range_1356 = len_1356 / (M_PI * 2); + PrintAndLogEx(INFO, "Wavelengths"); - PrintAndLogEx(INFO, " 125 kHz has %f meters", len_125); - PrintAndLogEx(INFO, " 134 kHz has %f meters", len_134); - PrintAndLogEx(INFO, " 13.56 mHz has %f meters", len_1356); + PrintAndLogEx(INFO, " 125 kHz has %f m, rf range %f m", len_125, rf_range_125); + PrintAndLogEx(INFO, " 134 kHz has %f m, rf range %f m", len_134, rf_range_134); + PrintAndLogEx(INFO, " 13.56 mHz has %f m, rf range %f m", len_1356, rf_range_1356); + return PM3_SUCCESS; +} + +static int CmdAnalyseFoo(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyze foo", + "experiments of cliparse", + "analyse foo -r a0000000a0002021" + ); + + void *argtable[] = { + arg_param_begin, + arg_strx0("r", "raw", "", "raw bytes (strx)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + // raw param + int datalen = 256; + uint8_t data[256]; + CLIGetHexWithReturn(ctx, 1, data, &datalen); + + int data3len = 512; + uint8_t data3[512]; + CLIGetStrWithReturn(ctx, 1, data3, &data3len); + + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "-r"); + PrintAndLogEx(INFO, "Got: %s", sprint_hex_inrow(data, datalen)); + PrintAndLogEx(INFO, "Got: %s", data3); + + ClearGraph(false); + GraphTraceLen = 15000; + + for (int i = 0; i < 4095; i++) { + int o = 0; + + // 0010 0000 + if (i & 0x2000) o |= 0x80; // corr_i_accum[13] + // 0001 1100 + if (i & 0x1C00) o |= 0x40; // corr_i_accum[12] | corr_i_accum[11] | corr_i_accum[10] + // 0000 1110 + if (i & 0x0E00) o |= 0x20; // corr_i_accum[12] | corr_i_accum[11] | corr_i_accum[9], + o |= (i & 0x1F0) >> 4; // corr_i_accum[8:4] + + GraphBuffer[i] = o; + } + + for (int i = 0; i < 4095; i++) { + int o = 0; + + // Send 8 bits of in phase tag signal + //if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + if ((i & 0x3800) == 0 || (i & 0x3800) == 0x3800) { + o |= (i & 0xFF0) >> 4; // corr_i_out <= corr_i_accum[11:4]; + } else { + // truncate to maximum value + //if (corr_i_accum[13] == 1'b0) + if ((i & 0x2000) == 0) { + o |= 0x7f; // corr_i_out <= 8'b01111111; + } + } + GraphBuffer[i + 5000] = o; + } + + for (int i = 0; i < 4095; i++) { + int o = i >> 5; + GraphBuffer[i + 10000] = o; + } + + RepaintGraphWindow(); + ShowGraphWindow(); return PM3_SUCCESS; } @@ -953,6 +1067,7 @@ static command_t CommandTable[] = { {"nuid", CmdAnalyseNuid, AlwaysAvailable, "create NUID from 7byte UID"}, {"demodbuff", CmdAnalyseDemodBuffer, AlwaysAvailable, "Load binary string to demodbuffer"}, {"freq", CmdAnalyseFreq, AlwaysAvailable, "Calc wave lengths"}, + {"foo", CmdAnalyseFoo, AlwaysAvailable, "muxer"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 85044f27b..90a3b3ebf 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -410,11 +410,11 @@ static command_t CommandTable[] = { {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, + {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, {"plot", CmdHFPlot, IfPm3Hfplot, "Plot signal"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, {"search", CmdHFSearch, AlwaysAvailable, "Search for known HF tags"}, - {"sniff", CmdHFSniff, IfPm3Hfsniff, " Generic HF Sniff"}, + {"sniff", CmdHFSniff, IfPm3Hfsniff, "Generic HF Sniff"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index d4418ee62..e772d406e 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -170,93 +170,6 @@ const char *getTagInfo(uint8_t uid) { static uint16_t frameLength = 0; uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; -static int usage_hf_14a_config(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14a config [a 0|1|2] [b 0|1|2] [2 0|1|2] [3 0|1|2]"); - PrintAndLogEx(NORMAL, "\nOptions:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " a 0|1|2 ATQA<>anticollision: 0=follow standard 1=execute anticol 2=skip anticol"); - PrintAndLogEx(NORMAL, " b 0|1|2 BCC: 0=follow standard 1=use fixed BCC 2=use card BCC"); - PrintAndLogEx(NORMAL, " 2 0|1|2 SAK<>CL2: 0=follow standard 1=execute CL2 2=skip CL2"); - PrintAndLogEx(NORMAL, " 3 0|1|2 SAK<>CL3: 0=follow standard 1=execute CL3 2=skip CL3"); - PrintAndLogEx(NORMAL, " r 0|1|2 SAK<>ATS: 0=follow standard 1=execute RATS 2=skip RATS"); - PrintAndLogEx(NORMAL, "\nExamples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config ")" Print current configuration"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 ")" Force execution of anticollision"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 ")" Restore ATQA interpretation"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 1 ")" Force fix of bad BCC in anticollision"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 0 ")" Restore BCC check"); - PrintAndLogEx(NORMAL, "\nExamples to revive Gen2/DirectWrite magic cards failing at anticollision:"); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 4b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 4b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 7b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 7b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 04112233445566")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); - return PM3_SUCCESS; -} - -static int usage_hf_14a_sim(void) { - PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n"); - PrintAndLogEx(NORMAL, "Usage: hf 14a sim [h] t u [n ] [x] [e] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " t : 1 = MIFARE Classic 1k"); - PrintAndLogEx(NORMAL, " 2 = MIFARE Ultralight"); - PrintAndLogEx(NORMAL, " 3 = MIFARE Desfire"); - PrintAndLogEx(NORMAL, " 4 = ISO/IEC 14443-4"); - PrintAndLogEx(NORMAL, " 5 = MIFARE Tnp3xxx"); - PrintAndLogEx(NORMAL, " 6 = MIFARE Mini"); - PrintAndLogEx(NORMAL, " 7 = AMIIBO (NTAG 215), pack 0x8080"); - PrintAndLogEx(NORMAL, " 8 = MIFARE Classic 4k"); - PrintAndLogEx(NORMAL, " 9 = FM11RF005SH Shanghai Metro"); - PrintAndLogEx(NORMAL, " 10 = JCOP 31/41 Rothult"); - PrintAndLogEx(NORMAL, " u : 4, 7 or 10 byte UID"); - PrintAndLogEx(NORMAL, " n : (Optional) Exit simulation after blocks have been read by reader. 0 = infinite"); - PrintAndLogEx(NORMAL, " x : (Optional) Performs the 'reader attack', nr/ar attack against a reader"); - PrintAndLogEx(NORMAL, " e : (Optional) Fill simulator keys from found keys"); - PrintAndLogEx(NORMAL, " v : (Optional) Verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344 x")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 112233445566778899AA")); - return PM3_SUCCESS; -} -static int usage_hf_14a_sniff(void) { - PrintAndLogEx(NORMAL, "Collect data from the field and save into command buffer."); - PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf 14a list'"); - PrintAndLogEx(NORMAL, "Usage: hf 14a sniff [c][r]"); - PrintAndLogEx(NORMAL, "c - triggered by first data from card"); - PrintAndLogEx(NORMAL, "r - triggered by first 7-bit request from reader (REQ,WUP,...)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sniff c r")); - return PM3_SUCCESS; -} - -static int usage_hf_14a_reader(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14a reader [k|s|x] [3]"); - PrintAndLogEx(NORMAL, " k keep the field active after command executed"); - PrintAndLogEx(NORMAL, " s silent (no messages)"); - PrintAndLogEx(NORMAL, " x just drop the signal field"); - PrintAndLogEx(NORMAL, " 3 ISO14443-3 select only (skip RATS)"); - PrintAndLogEx(NORMAL, " @ continuous mode. Updates hf plot as well"); - return PM3_SUCCESS; -} - static int CmdHF14AList(const char *Cmd) { char args[128] = {0}; if (strlen(Cmd) == 0) { @@ -285,142 +198,168 @@ int hf14a_getconfig(hf14a_config *config) { return PM3_SUCCESS; } -int hf14a_setconfig(hf14a_config *config) { +int hf14a_setconfig(hf14a_config *config, bool verbose) { if (!session.pm3_present) return PM3_ENOTTY; clearCommandBuffer(); - if (config != NULL) + if (config != NULL) { SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config)); - else + if (verbose) { + SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); + } + } else { SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); + } return PM3_SUCCESS; } +static int hf_14a_config_example(void) { + PrintAndLogEx(NORMAL, "\nExamples to revive Gen2/DirectWrite magic cards failing at anticollision:"); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 4b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 4b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 7b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 7b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa forcce --bcc ignore --cl2 force --cl3 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip -rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 04112233445566")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + return PM3_SUCCESS; +} static int CmdHf14AConfig(const char *Cmd) { - if (!session.pm3_present) return PM3_ENOTTY; - // if called with no params, just print the device config - if (strlen(Cmd) == 0) { - return hf14a_setconfig(NULL); - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a config", + "Configure 14a settings (use with caution)", + "hf 14a config -> Print current configuration\n" + "hf 14a config --std -> Reset default configuration (follow standard)\n" + "hf 14a config --atqa std -> Follow standard\n" + "hf 14a config --atqa force -> Force execution of anticollision\n" + "hf 14a config --atqa skip -> Skip anticollision\n" + "hf 14a config --bcc std -> Follow standard\n" + "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" + "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" + "hf 14a config --cl2 std -> Follow standard\n" + "hf 14a config --cl2 force -> Execute CL2\n" + "hf 14a config --cl2 skip -> Skip CL2\n" + "hf 14a config --cl3 std -> Follow standard\n" + "hf 14a config --cl3 force -> Execute CL3\n" + "hf 14a config --cl3 skip -> Skip CL3\n" + "hf 14a config --rats std -> Follow standard\n" + "hf 14a config --rats force -> Execute RATS\n" + "hf 14a config --rats skip -> Skip RATS"); - hf14a_config config = { - .forceanticol = -1, - .forcebcc = -1, - .forcecl2 = -1, - .forcecl3 = -1, - .forcerats = -1 + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "atqa", "", "Configure ATQA<>anticollision behavior"), + arg_str0(NULL, "bcc", "", "Configure BCC behavior"), + arg_str0(NULL, "cl2", "", "Configure SAK<>CL2 behavior"), + arg_str0(NULL, "cl3", "", "Configure SAK<>CL3 behavior"), + arg_str0(NULL, "rats", "", "Configure RATS behavior"), + arg_lit0(NULL, "std", "Reset default configuration: follow all standard"), + arg_lit0("v", "verbose", "verbose output, also prints examples for reviving Gen2 cards"), + arg_param_end }; - - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (param_getchar(Cmd, cmdp)) { - case 'h': - return usage_hf_14a_config(); - case 'a': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forceanticol = 0; - break; - case '1': - config.forceanticol = 1; - break; - case '2': - config.forceanticol = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case 'b': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcebcc = 0; - break; - case '1': - config.forcebcc = 1; - break; - case '2': - config.forcebcc = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case '2': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcecl2 = 0; - break; - case '1': - config.forcecl2 = 1; - break; - case '2': - config.forcecl2 = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case '3': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcecl3 = 0; - break; - case '1': - config.forcecl3 = 1; - break; - case '2': - config.forcecl3 = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case 'r': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcerats = 0; - break; - case '1': - config.forcerats = 1; - break; - case '2': - config.forcerats = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = 1; - break; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool defaults = arg_get_lit(ctx, 6); + int vlen = 0; + char value[10]; + int atqa = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) atqa = 0; + else if (strcmp(value, "force") == 0) atqa = 1; + else if (strcmp(value, "skip") == 0) atqa = 2; + else { + PrintAndLogEx(ERR, "atqa argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int bcc = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) bcc = 0; + else if (strcmp(value, "fix") == 0) bcc = 1; + else if (strcmp(value, "ignore") == 0) bcc = 2; + else { + PrintAndLogEx(ERR, "bcc argument must be 'std', 'fix', or 'ignore'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int cl2 = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) cl2 = 0; + else if (strcmp(value, "force") == 0) cl2 = 1; + else if (strcmp(value, "skip") == 0) cl2 = 2; + else { + PrintAndLogEx(ERR, "cl2 argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int cl3 = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) cl3 = 0; + else if (strcmp(value, "force") == 0) cl3 = 1; + else if (strcmp(value, "skip") == 0) cl3 = 2; + else { + PrintAndLogEx(ERR, "cl3 argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int rats = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) rats = 0; + else if (strcmp(value, "force") == 0) rats = 1; + else if (strcmp(value, "skip") == 0) rats = 2; + else { + PrintAndLogEx(ERR, "rats argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - // validations - if (errors) return usage_hf_14a_config(); + bool verbose = arg_get_lit(ctx, 7); - return hf14a_setconfig(&config); + CLIParserFree(ctx); + + // validations + if (strlen(Cmd) == 0) { + return hf14a_setconfig(NULL, verbose); + } + + if (verbose) { + hf_14a_config_example(); + } + + hf14a_config config = { + .forceanticol = atqa, + .forcebcc = bcc, + .forcecl2 = cl2, + .forcecl3 = cl3, + .forcerats = rats + }; + + return hf14a_setconfig(&config, verbose); } int Hf14443_4aGetCardData(iso14a_card_select_t *card) { @@ -462,37 +401,44 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { } static int CmdHF14AReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a reader", + "Reader for ISO 14443A based tags", + "hf 14a reader -@ <- Continuous mode"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("k", "keep", "keep the field active after command executed"), + arg_lit0("s", "silent", "silent (no messages)"), + arg_lit0(NULL, "drop", "just drop the signal field"), + arg_lit0(NULL, "skip", "ISO14443-3 select only (skip RATS)"), + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool disconnectAfter = true; + if (arg_get_lit(ctx, 1)) { + disconnectAfter = false; + } + + bool silent = arg_get_lit(ctx, 2); uint32_t cm = ISO14A_CONNECT; - bool disconnectAfter = true, silent = false, continuous = false; - int cmdp = 0; - int res = PM3_SUCCESS; - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_14a_reader(); - case '3': - cm |= ISO14A_NO_RATS; - break; - case 'k': - disconnectAfter = false; - break; - case 's': - silent = true; - break; - case 'x': - cm &= ~ISO14A_CONNECT; - break; - case '@': - continuous = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown command."); - return PM3_EINVARG; - } - cmdp++; + if (arg_get_lit(ctx, 3)) { + cm &= ~ISO14A_CONNECT; } + if (arg_get_lit(ctx, 4)) { + cm |= ISO14A_NO_RATS; + } + + bool continuous = arg_get_lit(ctx, 5); + + CLIParserFree(ctx); + + int res = PM3_SUCCESS; + if (!disconnectAfter) cm |= ISO14A_NO_DISCONNECT; if (continuous) { @@ -608,10 +554,23 @@ static int CmdHF14AInfo(const char *Cmd) { // Collect ISO14443 Type A UIDs static int CmdHF14ACUIDs(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a cuids", + "Collect n>0 ISO14443-a UIDs in one go", + "hf 14a cuids -n 5 <-- Collect 5 UIDs"); + + void *argtable[] = { + arg_param_begin, + arg_int0("n", "num", "", "Number of UIDs to collect"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + // requested number of UIDs - int n = atoi(Cmd); // collect at least 1 (e.g. if no parameter was given) - n = n > 0 ? n : 1; + int n = arg_get_int_def(ctx, 1, 1); + + CLIParserFree(ctx); uint64_t t1 = msclock(); PrintAndLogEx(SUCCESS, "collecting %d UIDs", n); @@ -646,83 +605,81 @@ static int CmdHF14ACUIDs(const char *Cmd) { PrintAndLogEx(SUCCESS, "end: %" PRIu64 " seconds", (msclock() - t1) / 1000); return 1; } + // ## simulate iso14443a tag int CmdHF14ASim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a sim", + "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID", + "hf 14a sim -t 1 --uid 11223344 -> MIFARE Classic 1k\n" + "hf 14a sim -t 2 -> MIFARE Ultralight\n" + "hf 14a sim -t 3 -> MIFARE Desfire\n" + "hf 14a sim -t 4 -> ISO/IEC 14443-4\n" + "hf 14a sim -t 5 -> MIFARE Tnp3xxx\n" + "hf 14a sim -t 6 -> MIFARE Mini\n" + "hf 14a sim -t 7 -> AMIIBO (NTAG 215), pack 0x8080\n" + "hf 14a sim -t 8 -> MIFARE Classic 4k\n" + "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro\n" + "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n"); - int uidlen = 0; - uint8_t flags = 0, tagtype = 1, cmdp = 0; - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + void *argtable[] = { + arg_param_begin, + arg_int1("t", "type", "<1-10> ", "Simulation type to use"), + arg_str0("u", "uid", "", "4, 7 or 10 byte UID"), + arg_int0("n", "num", "", "Exit simulation after blocks have been read by reader. 0 = infinite"), + arg_lit0(NULL, "nr", "Performs the 'reader attack', nr/ar attack against a reader"), + arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int tagtype = arg_get_int(ctx, 1); + + int uid_len = 0; + uint8_t uid[10] = {0}; + CLIGetHexWithReturn(ctx, 2, uid, &uid_len); + + uint8_t flags = 0; bool useUIDfromEML = true; - bool setEmulatorMem = false; - bool verbose = false; - bool errors = false; - sector_t *k_sector = NULL; - uint8_t k_sectorsCount = 40; - uint8_t exitAfterNReads = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_14a_sim(); - case 't': - // Retrieve the tag type - tagtype = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (tagtype == 0) - errors = true; - cmdp += 2; + if (uid_len > 0) { + switch (uid_len) { + case 10: + flags |= FLAG_10B_UID_IN_DATA; break; - case 'u': - // Retrieve the full 4,7,10 byte long uid - param_gethex_ex(Cmd, cmdp + 1, uid, &uidlen); - uidlen >>= 1; - switch (uidlen) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - break; - default: - errors = true; - break; - } - if (!errors) { - PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uidlen, sprint_hex(uid, uidlen)); - useUIDfromEML = false; - } - cmdp += 2; + case 7: + flags |= FLAG_7B_UID_IN_DATA; break; - case 'n': - exitAfterNReads = param_get8(Cmd, cmdp + 1); - cmdp += 2; - break; - case 'v': - verbose = true; - cmdp++; - break; - case 'x': - flags |= FLAG_NR_AR_ATTACK; - cmdp++; - break; - case 'e': - setEmulatorMem = true; - cmdp++; + case 4: + flags |= FLAG_4B_UID_IN_DATA; break; default: - PrintAndLogEx(WARNING, "Unknown parameter " _RED_("'%c'"), param_getchar(Cmd, cmdp)); - errors = true; - break; + PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } + PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); + useUIDfromEML = false; } - //Validations - if (errors || cmdp == 0) return usage_hf_14a_sim(); + uint8_t exitAfterNReads = arg_get_int(ctx, 3); - if (useUIDfromEML) + if (arg_get_lit(ctx, 4)) { + flags |= FLAG_NR_AR_ATTACK; + } + + bool setEmulatorMem = arg_get_lit(ctx, 5); + bool verbose = arg_get_lit(ctx, 6); + + CLIParserFree(ctx); + + sector_t *k_sector = NULL; + uint8_t k_sectorsCount = 40; + + if (useUIDfromEML) { flags |= FLAG_UID_IN_EMUL; + } struct { uint8_t tagtype; @@ -734,7 +691,7 @@ int CmdHF14ASim(const char *Cmd) { payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; - memcpy(payload.uid, uid, uidlen); + memcpy(payload.uid, uid, uid_len); clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload)); @@ -768,13 +725,32 @@ int CmdHF14ASim(const char *Cmd) { } int CmdHF14ASniff(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a sniff", + "Collect data from the field and save into command buffer.\n" + "Buffer accessible from command 'hf 14a list'", + " hf 14a sniff -c -r"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("c", "card", "triggered by first data from card"), + arg_lit0("r", "reader", "triggered by first 7-bit request from reader (REQ,WUP,...)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint8_t param = 0; - for (uint8_t i = 0; i < 2; i++) { - uint8_t ctmp = tolower(param_getchar(Cmd, i)); - if (ctmp == 'h') return usage_hf_14a_sniff(); - if (ctmp == 'c') param |= 0x01; - if (ctmp == 'r') param |= 0x02; + + if (arg_get_lit(ctx, 1)) { + param |= 0x01; } + + if (arg_get_lit(ctx, 2)) { + param |= 0x02; + } + + CLIParserFree(ctx); + clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443A_SNIFF, (uint8_t *)¶m, sizeof(uint8_t)); return PM3_SUCCESS; @@ -1539,6 +1515,7 @@ static int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { printTag("MIFARE DESFire CL2"); printTag("MIFARE DESFire EV1 256B/2K/4K/8K CL2"); printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K"); + printTag("MIFARE DESFire EV3 2K/4K/8K"); printTag("MIFARE DESFire Light 640B"); } else { printTag("MIFARE Plus EV1 2K/4K CL2 in SL3"); @@ -2135,38 +2112,51 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } static uint16_t get_sw(uint8_t *d, uint8_t n) { - if (n < 2) + if (n < 2) { return 0; - + } n -= 2; return d[n] * 0x0100 + d[n + 1]; } +static uint64_t inc_sw_error_occurence(uint16_t sw, uint64_t all_sw[256][256]) { + uint8_t sw1 = (uint8_t)(sw >> 8); + uint8_t sw2 = (uint8_t)(0xff & sw); + if (sw1 == 0x90 && sw2 == 0x00) { + return 0; // Don't count successes. + } + if (sw1 == 0x6d && sw2 == 0x00) { + return 0xffffffffffffffffULL; // Always max "Instruction not supported". + } + return ++all_sw[sw1][sw2]; +} + static int CmdHf14AFindapdu(const char *Cmd) { - // TODO: What response values should be considerd "valid" or "instersting" (worth dispalying)? // TODO: Option to select AID/File (and skip INS 0xA4). - // TODO: Validate the decoding of the APDU (not specific to this command, check - // https://cardwerk.com/smartcards/smartcard_standard_ISO7816-4_5_basic_organizations.aspx#chap5_3_2). - // TODO: Check all cases (APDUs) with no data bytes (no/short/extended length). - // TODO: Option to blacklist instructions (or whole APDUs). + // TODO: Check all instructions with extended APDUs if the card support it. + // TODO: Option to reset tag before every command. CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a apdufind", - "Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1P2 commands.\n" + "Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1/P2 commands.\n" "It loops all 256 possible values for each byte.\n" - "The loop oder is INS -> P1/P2 (alternating) -> CLA\n" + "The loop oder is INS -> P1/P2 (alternating) -> CLA.\n" "Tag must be on antenna before running.", "hf 14a apdufind\n" "hf 14a apdufind --cla 80\n" + "hf 14a apdufind --cla 80 --error-limit 20 --skip-ins a4 --skip-ins b0 --with-le\n" ); void *argtable[] = { arg_param_begin, - arg_str0("c", "cla", "", "Start value of CLASS (1 hex byte)"), - arg_str0("i", "ins", "", "Start value of INSTRUCTION (1 hex byte)"), - arg_str0(NULL, "p1", "", "Start value of P1 (1 hex byte)"), - arg_str0(NULL, "p2", "", "Start value of P2 (1 hex byte)"), - arg_u64_0("r", "reset", "", "Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes"), - arg_lit0("v", "verbose", "Verbose output"), + arg_str0("c", "cla", "", "Start value of CLASS (1 hex byte)"), + arg_str0("i", "ins", "", "Start value of INSTRUCTION (1 hex byte)"), + arg_str0(NULL, "p1", "", "Start value of P1 (1 hex byte)"), + arg_str0(NULL, "p2", "", "Start value of P2 (1 hex byte)"), + arg_u64_0("r", "reset", "", "Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes"), + arg_u64_0("e", "error-limit", "", "Maximum times an status word other than 0x9000 or 0x6D00 is shown. Default is 512."), + arg_strx0("s", "skip-ins", "", "Do not test an instructions (can be specifed multiple times)"), + arg_lit0("l", "with-le", "Serach for APDUs with Le=0 (case 2S) as well"), + arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2183,8 +2173,13 @@ static int CmdHf14AFindapdu(const char *Cmd) { int p2_len = 0; uint8_t p2_arg[1] = {0}; CLIGetHexWithReturn(ctx, 4, p2_arg, &p2_len); - uint64_t reset_time = arg_get_u64_def(ctx, 5, 5 * 60); // Reset every 5 minutes. - bool verbose = arg_get_lit(ctx, 6); + uint64_t reset_time = arg_get_u64_def(ctx, 5, 5 * 60); + uint64_t error_limit = arg_get_u64_def(ctx, 6, 512); + int ignore_ins_len = 0; + uint8_t ignore_ins_arg[250] = {0}; + CLIGetHexWithReturn(ctx, 7, ignore_ins_arg, &ignore_ins_len); + bool with_le = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); CLIParserFree(ctx); @@ -2211,6 +2206,9 @@ static int CmdHf14AFindapdu(const char *Cmd) { PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); bool inc_p1 = true; + bool skip_ins = false; + uint64_t all_sw[256][256] = { { 0 } }; + uint64_t sw_occurences = 0; uint64_t t_start = msclock(); uint64_t t_last_reset = msclock(); @@ -2218,59 +2216,55 @@ static int CmdHf14AFindapdu(const char *Cmd) { do { do { do { +retry_ins: // Exit (was the Enter key pressed)? if (kbd_enter_pressed()) { PrintAndLogEx(INFO, "User interrupted detected. Aborting"); goto out; } + // Skip/Ignore this instrctuion? + for (int i = 0; i < ignore_ins_len; i++) { + if (ins == ignore_ins_arg[i]) { + skip_ins = true; + break; + } + } + if (skip_ins) { + skip_ins = false; + continue; + } + if (verbose) { PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); } - // Send APDU. - uint8_t command[4] = {cla, ins, p1, p2}; - int command_n = sizeof(command); - res = ExchangeAPDU14a(command, command_n, activate_field, keep_field_on, response, sizeof(response), &response_n); - if (res) { - continue; - } - - // Was there and length error? If so, try with Le length (case 2 instad of case 1, - // https://stackoverflow.com/a/30679558). Le = 0x00 will get interpreted as extended length APDU - // with Le being 0x0100. - uint16_t sw = get_sw(response, response_n); - bool command_with_le = false; - if (sw == 0x6700) { - PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X\": %04X (%s)", cla, ins, p1, p2, - sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - PrintAndLogEx(INFO, "Resending current command with Le = 0x0100 (extended length APDU)"); - uint8_t command2[7] = {cla, ins, p1, p2, 0x00}; - int command2_n = sizeof(command2); - res = ExchangeAPDU14a(command2, command2_n, activate_field, keep_field_on, response, sizeof(response), &response_n); + // Send APDU without Le (case 1) and with Le = 0 (case 2S), if "with-le" was set. + uint8_t command[5] = {cla, ins, p1, p2, 0x00}; + int command_n = 4; + for (int i = 0; i < 1 + with_le; i++) { + // Send APDU. + res = ExchangeAPDU14a(command, command_n + i, activate_field, keep_field_on, response, sizeof(response), &response_n); if (res) { - continue; + DropField(); + activate_field = true; + goto retry_ins; } - command_with_le = true; - } + uint16_t sw = get_sw(response, response_n); + sw_occurences = inc_sw_error_occurence(sw, all_sw); - // Check response. - sw = get_sw(response, response_n); - if (sw != 0x6a86 && - sw != 0x6986 && - sw != 0x6d00 - ) { - if (command_with_le) { - PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X00\": %04X (%s)", cla, ins, p1, p2, + // Show response. + if (sw_occurences < error_limit) { + logLevel_t log_level = INFO; + if (sw == 0x9000) { + log_level = SUCCESS; + } + PrintAndLogEx(log_level, "Got response for APDU \"%s\": %04X (%s)", sprint_hex_inrow(command, command_n + i), sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - } else { - PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X\": %04X (%s)", cla, ins, p1, p2, - sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - } - // Show response data. - if (response_n > 2) { - PrintAndLogEx(SUCCESS, "Response data is: %s | %s", sprint_hex_inrow(response, response_n - 2), - sprint_ascii(response, response_n - 2)); + if (response_n > 2) { + PrintAndLogEx(SUCCESS, "Response data is: %s | %s", sprint_hex_inrow(response, response_n - 2), + sprint_ascii(response, response_n - 2)); + } } } activate_field = false; // Do not reativate the filed until the next reset. @@ -2304,11 +2298,11 @@ out: static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, + {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, - {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, " Collect n>0 ISO14443-a UIDs in one go"}, - {"sim", CmdHF14ASim, IfPm3Iso14443a, " -- Simulate ISO 14443-a tag"}, + {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, "Collect n>0 ISO14443-a UIDs in one go"}, + {"sim", CmdHF14ASim, IfPm3Iso14443a, "Simulate ISO 14443-a tag"}, {"sniff", CmdHF14ASniff, IfPm3Iso14443a, "sniff ISO 14443-a traffic"}, {"apdu", CmdHF14AAPDU, IfPm3Iso14443a, "Send ISO 14443-4 APDU to tag"}, {"chaining", CmdHF14AChaining, IfPm3Iso14443a, "Control ISO 14443-4 input chaining"}, diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 925bf8bef..a1a2279a0 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -27,7 +27,7 @@ int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff int CmdHF14ASim(const char *Cmd); // used by hf mfu sim int hf14a_getconfig(hf14a_config *config); -int hf14a_setconfig(hf14a_config *config); +int hf14a_setconfig(hf14a_config *config, bool verbose); int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search); const char *getTagInfo(uint8_t uid); int Hf14443_4aGetCardData(iso14a_card_select_t *card); diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 71c2f03ab..5676aa41f 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -287,7 +287,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b raw", - "Sends raw bytes to card ", + "Sends raw bytes to card", "hf 14b raw -cks --data 0200a40400 -> standard select\n" "hf 14b raw -ck --sr --data 0200a40400 -> SRx select\n" "hf 14b raw -ck --cts --data 0200a40400 -> C-ticket select\n" diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 3be321fcb..afe5b6b6f 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -19,7 +19,7 @@ #include "cmdhf14a.h" // ExchangeAPDU14a #include "protocols.h" // definitions of ISO14A/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription -#include "sha1.h" // KSeed calculation etc +#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512) #include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt #include "des.h" // mbedtls_des_key_set_parity #include "cmdhf14b.h" // exchange_14b_apdu @@ -50,15 +50,14 @@ const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; -static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length); -static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length); -static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length); -static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length); +static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path); static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen); -static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen); typedef enum { // list must match dg_table EF_COM = 0, @@ -84,28 +83,38 @@ typedef enum { // list must match dg_table } emrtd_dg_enum; static emrtd_dg_t dg_table[] = { -// tag fileid filename desc pace req fast parser dumper - {0x60, "011E", "EF_COM", "Header and Data Group Presence Information", false, true, true, emrtd_print_ef_com_info, NULL}, - {0x61, "0101", "EF_DG1", "Details recorded in MRZ", false, true, true, emrtd_print_ef_dg1_info, NULL}, - {0x75, "0102", "EF_DG2", "Encoded Face", false, true, false, NULL, emrtd_dump_ef_dg2}, - {0x63, "0103", "EF_DG3", "Encoded Finger(s)", true, false, false, NULL, NULL}, - {0x76, "0104", "EF_DG4", "Encoded Eye(s)", true, false, false, NULL, NULL}, - {0x65, "0105", "EF_DG5", "Displayed Portrait", false, false, false, NULL, emrtd_dump_ef_dg5}, - {0x66, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, NULL, NULL}, - {0x67, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, NULL, emrtd_dump_ef_dg7}, - {0x68, "0108", "EF_DG8", "Data Feature(s)", false, false, true, NULL, NULL}, - {0x69, "0109", "EF_DG9", "Structure Feature(s)", false, false, true, NULL, NULL}, - {0x6a, "010A", "EF_DG10", "Substance Feature(s)", false, false, true, NULL, NULL}, - {0x6b, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, true, emrtd_print_ef_dg11_info, NULL}, - {0x6c, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, true, emrtd_print_ef_dg12_info, NULL}, - {0x6d, "010D", "EF_DG13", "Optional Detail(s)", false, false, true, NULL, NULL}, - {0x6e, "010E", "EF_DG14", "Security Options", false, false, true, NULL, NULL}, - {0x6f, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, true, NULL, NULL}, - {0x70, "0110", "EF_DG16", "Person(s) to Notify", false, false, true, NULL, NULL}, - {0x77, "011D", "EF_SOD", "Document Security Object", false, false, true, emrtd_print_ef_sod_info, emrtd_dump_ef_sod}, - {0xff, "011C", "EF_CardAccess", "PACE SecurityInfos", true, true, true, NULL, NULL}, - {0xff, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, true, NULL, NULL}, - {0x00, NULL, NULL, NULL, false, false, false, NULL, NULL} +// tag dg# fileid filename desc pace eac req fast parser dumper + {0x60, 0, "011E", "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, + {0x61, 1, "0101", "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, + {0x75, 2, "0102", "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, + {0x63, 3, "0103", "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, + {0x76, 4, "0104", "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, + {0x65, 5, "0105", "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, + {0x66, 6, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL}, + {0x67, 7, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7}, + {0x68, 8, "0108", "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL}, + {0x69, 9, "0109", "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL}, + {0x6a, 10, "010A", "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL}, + {0x6b, 11, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL}, + {0x6c, 12, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL}, + {0x6d, 13, "010D", "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL}, + {0x6e, 14, "010E", "EF_DG14", "Security Options", false, false, false, true, NULL, NULL}, + {0x6f, 15, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, + {0x70, 16, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, + {0x77, 0, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod}, + {0xff, 0, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, NULL, NULL}, + {0xff, 0, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, + {0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL} +}; + +// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from +// https://tools.ietf.org/html/rfc3447#page-43 +static emrtd_hashalg_t hashalg_table[] = { +// name hash func len len descriptor + {"SHA-1", sha1hash, 20, 7, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}}, + {"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}}, + {"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}}, + {NULL, NULL, 0, 0, {}} }; static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { @@ -203,11 +212,16 @@ static char emrtd_calculate_check_digit(char *data) { } static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset) { - PrintAndLogEx(DEBUG, "asn1datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); + PrintAndLogEx(DEBUG, "asn1 datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); int lenfield = (int) * (datain + offset); - PrintAndLogEx(DEBUG, "asn1datalength, lenfield: %i", lenfield); + PrintAndLogEx(DEBUG, "asn1 datalength, lenfield: %02X", lenfield); if (lenfield <= 0x7f) { return lenfield; + } else if (lenfield == 0x80) { + // TODO: 0x80 means indeterminate, and this impl is a workaround. + // Giving rest of the file is a workaround, nothing more, nothing less. + // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD + return datainlen; } else if (lenfield == 0x81) { return ((int) * (datain + offset + 1)); } else if (lenfield == 0x82) { @@ -221,8 +235,8 @@ static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offset) { PrintAndLogEx(DEBUG, "asn1 fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen)); int lenfield = (int) * (datain + offset); - PrintAndLogEx(DEBUG, "asn1 fieldlength, thing: %i", lenfield); - if (lenfield <= 0x7F) { + PrintAndLogEx(DEBUG, "asn1 fieldlength, lenfield: %02X", lenfield); + if (lenfield <= 0x80) { return 1; } else if (lenfield == 0x81) { return 2; @@ -339,7 +353,7 @@ static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t // SHA1 the key unsigned char key[64]; - mbedtls_sha1(data, length + 4, key); + sha1hash(data, length + 4, key); PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4)); // Set parity bits @@ -622,9 +636,21 @@ static int emrtd_read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uin return true; } -static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *dataout, int *dataoutlen, int tag1, int tag2, bool twobytetag) { - int offset = 1; - offset += emrtd_get_asn1_field_length(datain, datainlen, offset); +static int emrtd_lds_determine_tag_length(uint8_t tag) { + if ((tag == 0x5F) || (tag == 0x7F)) { + return 2; + } + return 1; +} + +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, size_t datainlen, uint8_t *dataout, size_t *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag, size_t skiptagcount) { + int offset = 0; + int skipcounter = 0; + + if (entertoptag) { + offset += emrtd_lds_determine_tag_length(*datain); + offset += emrtd_get_asn1_field_length(datain, datainlen, offset); + } int e_idlen = 0; int e_datalen = 0; @@ -632,11 +658,7 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *d while (offset < datainlen) { PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, offset: %i, data: %X", offset, *(datain + offset)); // Determine element ID length to set as offset on asn1datalength - if ((*(datain + offset) == 0x5F) || (*(datain + offset) == 0x7F)) { - e_idlen = 2; - } else { - e_idlen = 1; - } + e_idlen = emrtd_lds_determine_tag_length(*(datain + offset)); // Get the length of the element e_datalen = emrtd_get_asn1_data_length(datain + offset, datainlen - offset, e_idlen); @@ -644,9 +666,13 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, int datainlen, uint8_t *d // Get the length of the element's length e_fieldlen = emrtd_get_asn1_field_length(datain + offset, datainlen - offset, e_idlen); + PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, e_idlen: %02X, e_datalen: %02X, e_fieldlen: %02X", e_idlen, e_datalen, e_fieldlen); + // If the element is what we're looking for, get the data and return true if (*(datain + offset) == tag1 && (!twobytetag || *(datain + offset + 1) == tag2)) { - if (datainlen > e_datalen) { + if (skipcounter < skiptagcount) { + skipcounter += 1; + } else if (datainlen > e_datalen) { *dataoutlen = e_datalen; memcpy(dataout, datain + offset + e_idlen + e_fieldlen, e_datalen); return true; @@ -681,17 +707,19 @@ static bool emrtd_select_and_read(uint8_t *dataout, int *dataoutlen, const char return true; } -static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) { +const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; +const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 }; + +static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) { int offset, datalen = 0; // This is a hacky impl that just looks for the image header. I'll improve it eventually. // based on mrpkey.py - // FF D8 FF E0 -> JPEG - // 00 00 00 0C 6A 50 -> JPEG 2000 // Note: Doing file_length - 6 to account for the longest data we're checking. + // Checks first byte before the rest to reduce overhead for (offset = 0; offset < file_length - 6; offset++) { - if ((file_contents[offset] == 0xFF && file_contents[offset + 1] == 0xD8 && file_contents[offset + 2] == 0xFF && file_contents[offset + 3] == 0xE0) || - (file_contents[offset] == 0x00 && file_contents[offset + 1] == 0x00 && file_contents[offset + 2] == 0x00 && file_contents[offset + 3] == 0x0C && file_contents[offset + 4] == 0x6A && file_contents[offset + 5] == 0x50)) { + if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) != 0) || + (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) != 0)) { datalen = file_length - offset; break; } @@ -702,21 +730,39 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length) { return PM3_ESOFT; } - saveFile(dg_table[EF_DG2].filename, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen); + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_DG2].filename); + + saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen); + + free(filepath); return PM3_SUCCESS; } -static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { +static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path) { uint8_t data[EMRTD_MAX_FILE_SIZE]; - int datalen = 0; + size_t datalen = 0; // If we can't find image in EF_DG5, return false. - if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true) == false) { + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true, 0) == false) { return PM3_ESOFT; } if (datalen < EMRTD_MAX_FILE_SIZE) { - saveFile(dg_table[EF_DG5].filename, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_DG5].filename); + + saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); + + free(filepath); } else { PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg5) datalen out-of-bounds"); return PM3_ESOFT; @@ -724,17 +770,26 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length) { return PM3_SUCCESS; } -static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { +static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path) { uint8_t data[EMRTD_MAX_FILE_SIZE]; - int datalen = 0; + size_t datalen = 0; // If we can't find image in EF_DG7, return false. - if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true) == false) { + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true, 0) == false) { return PM3_ESOFT; } if (datalen < EMRTD_MAX_FILE_SIZE) { - saveFile(dg_table[EF_DG7].filename, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_DG7].filename); + + saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); + + free(filepath); } else { PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg7) datalen out-of-bounds"); return PM3_ESOFT; @@ -742,7 +797,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length) { return PM3_SUCCESS; } -static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length) { +static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path) { int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1); int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1); @@ -751,11 +806,19 @@ static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length) { return PM3_SUCCESS; } - saveFile(dg_table[EF_SOD].filename, ".p7b", file_contents + fieldlen + 1, datalen); + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_SOD].filename); + + saveFile(filepath, ".p7b", file_contents + fieldlen + 1, datalen); + free(filepath); return PM3_ESOFT; } -static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b) { +static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b, const char *path) { uint8_t response[EMRTD_MAX_FILE_SIZE]; int resplen = 0; @@ -763,13 +826,22 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, cons return false; } + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, name); + PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile(name, ".BIN", response, resplen); + saveFile(filepath, ".BIN", response, resplen); emrtd_dg_t *dg = emrtd_fileid_to_dg(file); if ((dg != NULL) && (dg->dumper != NULL)) { - dg->dumper(response, resplen); + dg->dumper(response, resplen, path); } + + free(filepath); return true; } @@ -808,7 +880,7 @@ static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz); uint8_t kseed[20] = { 0x00 }; - mbedtls_sha1((unsigned char *)kmrz, strlen(kmrz), kseed); + sha1hash((unsigned char *)kmrz, strlen(kmrz), kseed); PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16)); emrtd_deskey(kseed, KENC_type, 16, kenc); @@ -955,7 +1027,7 @@ static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BA return true; } -int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; int resplen = 0; uint8_t ssc[8] = { 0x00 }; @@ -971,7 +1043,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } // Dump EF_CardAccess (if available) - if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, use_14b)) { + if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, use_14b, path)) { PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); } @@ -988,14 +1060,25 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab DropField(); return PM3_ESOFT; } + + + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_COM].filename); + PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); - saveFile(dg_table[EF_COM].filename, ".BIN", response, resplen); + saveFile(filepath, ".BIN", response, resplen); + + free(filepath); uint8_t filelist[50]; - int filelistlen = 0; + size_t filelistlen = 0; - if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; @@ -1012,8 +1095,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab continue; } PrintAndLogEx(DEBUG, "Current file: %s", dg->filename); - if (!dg->pace) { - emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, use_14b); + if (!dg->pace && !dg->eac) { + emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, use_14b, path); } } DropField(); @@ -1220,8 +1303,8 @@ static void emrtd_print_unknown_timestamp_5f85(uint8_t *data) { static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { uint8_t filelist[50]; - int filelistlen = 0; - int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false); + size_t filelistlen = 0; + int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); return PM3_ESOFT; @@ -1250,9 +1333,9 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { // MRZ on TD1 is 90 characters, 30 on each row. // MRZ on TD3 is 88 characters, 44 on each row. char mrz[90] = { 0x00 }; - int mrzlen = 0; + size_t mrzlen = 0; - if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true, 0)) { PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); return PM3_ESOFT; } @@ -1327,20 +1410,20 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { uint8_t taglist[100] = { 0x00 }; - int taglistlen = 0; + size_t taglistlen = 0; uint8_t tagdata[1000] = { 0x00 }; - int tagdatalen = 0; + size_t tagdatalen = 0; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------"); - if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_DG11."); return PM3_ESOFT; } for (int i = 0; i < taglistlen; i++) { - emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f); + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0); // Don't bother with empty tags if (tagdatalen == 0) { continue; @@ -1406,20 +1489,20 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { uint8_t taglist[100] = { 0x00 }; - int taglistlen = 0; + size_t taglistlen = 0; uint8_t tagdata[1000] = { 0x00 }; - int tagdatalen = 0; + size_t tagdatalen = 0; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------"); - if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_DG12."); return PM3_ESOFT; } for (int i = 0; i < taglistlen; i++) { - emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f); + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0); // Don't bother with empty tags if (tagdatalen == 0) { continue; @@ -1470,10 +1553,176 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } -static int emrtd_print_ef_sod_info(uint8_t *data, size_t datalen) { -// PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); -// PrintAndLogEx(WARNING, "TODO"); +static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { + uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; + + if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); + + if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); + + // Do true on reading into the tag as it's a "sequence" + if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); + + if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + // TODO: Not doing memcpy here, it didn't work, fix it somehow + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); + return false; + } + memcpy(dataout, emrtdsigtext, emrtdsigtextlen); + *dataoutlen = emrtdsigtextlen; + return PM3_SUCCESS; +} + +static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) { + uint8_t hashalgoset[64] = { 0x00 }; + size_t hashalgosetlen = 0; + + // We'll return hash algo -1 if we can't find anything + *hashalgo = -1; + + if (!emrtd_lds_get_data_by_tag(data, datalen, hashalgoset, &hashalgosetlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read hash algo set from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen)); + + // If last two bytes are 05 00, ignore them. + // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD + if (hashalgoset[hashalgosetlen - 2] == 0x05 && hashalgoset[hashalgosetlen - 1] == 0x00) { + hashalgosetlen -= 2; + } + + for (int hashi = 0; hashalg_table[hashi].name != NULL; hashi++) { + PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name); + // We're only interested in checking if the length matches to avoid memory shenanigans + if (hashalg_table[hashi].descriptorlen != hashalgosetlen) { + PrintAndLogEx(DEBUG, "len mismatch: %i", hashalgosetlen); + continue; + } + + if (memcmp(hashalg_table[hashi].descriptor, hashalgoset, hashalgosetlen) == 0) { + *hashalgo = hashi; + return PM3_SUCCESS; + } + } + + PrintAndLogEx(ERR, "Failed to parse hash list (Unknown algo: %s). Hash verification won't be available.", sprint_hex_inrow(hashalgoset, hashalgosetlen)); + return PM3_ESOFT; +} + +static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) { + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hash[64] = { 0x00 }; + size_t hashlen = 0; + + uint8_t hashidstr[4] = { 0x00 }; + size_t hashidstrlen = 0; + + size_t emrtdsiglen = 0; + size_t hashlistlen = 0; + size_t e_datalen = 0; + size_t e_fieldlen = 0; + size_t offset = 0; + + if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { + return false; + } + + PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo); + + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); + + while (offset < hashlistlen) { + // Get the length of the element + e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); + + // Get the length of the element's length + e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); + + switch (hashlist[offset]) { + case 0x30: + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); + if (hashlen <= 64) { + memcpy(hashes + (hashidstr[0] * 64), hash, hashlen); + } else { + PrintAndLogEx(ERR, "error (emrtd_parse_ef_sod_hashes) hashlen out-of-bounds"); + } + break; + } + // + 1 for length of ID + offset += 1 + e_datalen + e_fieldlen; + } + + return PM3_SUCCESS; +} + +static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_sod, int hash_algo) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); + + if (hash_algo == -1) { + PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("Unknown")); + } else { + PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("%s"), hashalg_table[hash_algo].name); + + uint8_t all_zeroes[64] = { 0x00 }; + bool calc_all_zero, sod_all_zero, hash_matches; + for (int i = 1; i <= 16; i++) { + calc_all_zero = (memcmp(dg_hashes_calc + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0); + sod_all_zero = (memcmp(dg_hashes_sod + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0); + hash_matches = (memcmp(dg_hashes_sod + (i * 64), dg_hashes_calc + (i * 64), hashalg_table[hash_algo].hashlen) == 0); + // Ignore files we don't haven't read and lack hashes to + if (calc_all_zero == true && sod_all_zero == true) { + continue; + } else if (calc_all_zero == true) { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File couldn't be read, but is in EF_SOD."), i); + } else if (sod_all_zero == true) { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File is not in EF_SOD."), i); + } else if (hash_matches == false) { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _RED_("Invalid"), i); + } else { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _GREEN_("Valid"), i); + } + } + } + return PM3_SUCCESS; } @@ -1519,15 +1768,30 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab } uint8_t filelist[50]; - int filelistlen = 0; + size_t filelistlen = 0; - if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false)) { + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); DropField(); return PM3_ESOFT; } - // Add EF_SOD to the list - filelist[filelistlen++] = 0x77; + + // Grab the hash list + uint8_t dg_hashes_sod[17][64] = { { 0 } }; + uint8_t dg_hashes_calc[17][64] = { { 0 } }; + int hash_algo = 0; + + if (!emrtd_select_and_read(response, &resplen, dg_table[EF_SOD].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + DropField(); + return PM3_ESOFT; + } + + res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes_sod, &hash_algo); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); + } + // Dump all files in the file list for (int i = 0; i < filelistlen; i++) { emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); @@ -1535,14 +1799,25 @@ int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; } - if (dg->fastdump && !dg->pace) { + if (dg->fastdump && !dg->pace && !dg->eac) { if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { if (dg->parser != NULL) dg->parser(response, resplen); + + PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); + // Check file hash + if (hash_algo != -1) { + PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen)); + hashalg_table[hash_algo].hasher(response, resplen, dg_hashes_calc[dg->dgnum]); + PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen)); + } } } } DropField(); + + emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo); + return PM3_SUCCESS; } @@ -1570,8 +1845,8 @@ int infoHF_EMRTD_offline(const char *path) { } uint8_t filelist[50]; - int filelistlen = 0; - res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false); + size_t filelistlen = 0; + res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); if (!res) { PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); free(data); @@ -1579,8 +1854,28 @@ int infoHF_EMRTD_offline(const char *path) { return PM3_ESOFT; } free(data); - // Add EF_SOD to the list - filelist[filelistlen++] = 0x77; + + // Grab the hash list + uint8_t dg_hashes_sod[17][64] = { { 0 } }; + uint8_t dg_hashes_calc[17][64] = { { 0 } }; + int hash_algo = 0; + + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_SOD].filename); + + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + free(filepath); + return PM3_ESOFT; + } + + res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes_sod, &hash_algo); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); + } + free(data); + // Read files in the file list for (int i = 0; i < filelistlen; i++) { emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); @@ -1588,7 +1883,7 @@ int infoHF_EMRTD_offline(const char *path) { PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); continue; } - if (!dg->pace) { + if (!dg->pace && !dg->eac) { strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg->filename); @@ -1596,11 +1891,22 @@ int infoHF_EMRTD_offline(const char *path) { // we won't halt on parsing errors if (dg->parser != NULL) dg->parser(data, datalen); + + PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); + // Check file hash + if (hash_algo != -1) { + PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen)); + hashalg_table[hash_algo].hasher(data, datalen, dg_hashes_calc[dg->dgnum]); + PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen)); + } free(data); } } } free(filepath); + + emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo); + return PM3_SUCCESS; } @@ -1640,6 +1946,7 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), arg_str0("e", "expiry", "", "expiry in YYMMDD format"), arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"), + arg_str0(NULL, "path", "", "save dump to the given dirpath"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1706,11 +2013,16 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { } } + uint8_t path[FILENAME_MAX] = { 0x00 }; + if (CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) != 0 || slen == 0) { + path[0] = '.'; + } + CLIParserFree(ctx); if (error) { return PM3_ESOFT; } - return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); + return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path); } static int cmd_hf_emrtd_info(const char *Cmd) { @@ -1725,7 +2037,7 @@ static int cmd_hf_emrtd_info(const char *Cmd) { arg_str0("n", "documentnumber", "", "document number, up to 9 chars"), arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), arg_str0("e", "expiry", "", "expiry in YYMMDD format"), - arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"), + arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars (passports only)"), arg_str0(NULL, "path", "", "display info from offline dump stored in dirpath"), arg_param_end }; diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 9df7846ad..bb32fb53d 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -15,19 +15,29 @@ typedef struct emrtd_dg_s { uint8_t tag; + uint8_t dgnum; const char *fileid; const char *filename; const char *desc; bool pace; + bool eac; // EAC only (we can't dump these) bool required; // some are required only if PACE bool fastdump; // fast to dump int (*parser)(uint8_t *data, size_t datalen); - int (*dumper)(uint8_t *data, size_t datalen); + int (*dumper)(uint8_t *data, size_t datalen, const char *path); } emrtd_dg_t; +typedef struct emrtd_hashalg_s { + const char *name; + int (*hasher)(uint8_t *datain, int datainlen, uint8_t *dataout); + size_t hashlen; + size_t descriptorlen; + const uint8_t descriptor[15]; +} emrtd_hashalg_t; + int CmdHFeMRTD(const char *Cmd); -int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path); int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); int infoHF_EMRTD_offline(const char *path); #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b4b2f4245..7b90a5bbf 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -3488,36 +3488,36 @@ static int CmdHFiClassAutopwn(const char *Cmd) { static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, - {"help", CmdHelp, AlwaysAvailable, " This help"}, -// {"clone", CmdHFiClassClone, IfPm3Iclass, "[*] Create a HID credential to Picopass / iCLASS tag"}, - {"dump", CmdHFiClassDump, IfPm3Iclass, "[*] Dump Picopass / iCLASS tag to file"}, - {"info", CmdHFiClassInfo, AlwaysAvailable, " Tag information"}, - {"list", CmdHFiClassList, AlwaysAvailable, " List iclass history"}, - {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "[*] Read Picopass / iCLASS block"}, - {"reader", CmdHFiClassReader, IfPm3Iclass, " Act like an Picopass / iCLASS reader"}, - {"restore", CmdHFiClassRestore, IfPm3Iclass, "[*] Restore a dump file onto a Picopass / iCLASS tag"}, - {"sniff", CmdHFiClassSniff, IfPm3Iclass, " Eavesdrop Picopass / iCLASS communication"}, - {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "[*] Write Picopass / iCLASS block"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, +// {"clone", CmdHFiClassClone, IfPm3Iclass, "Create a HID credential to Picopass / iCLASS tag"}, + {"dump", CmdHFiClassDump, IfPm3Iclass, "Dump Picopass / iCLASS tag to file"}, + {"info", CmdHFiClassInfo, AlwaysAvailable, "Tag information"}, + {"list", CmdHFiClassList, AlwaysAvailable, "List iclass history"}, + {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "Read Picopass / iCLASS block"}, + {"reader", CmdHFiClassReader, IfPm3Iclass, "Act like an Picopass / iCLASS reader"}, + {"restore", CmdHFiClassRestore, IfPm3Iclass, "Restore a dump file onto a Picopass / iCLASS tag"}, + {"sniff", CmdHFiClassSniff, IfPm3Iclass, "Eavesdrop Picopass / iCLASS communication"}, + {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " ---------------------"}, -// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "[*] Automatic key recovery tool for iCLASS"}, - {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "[*] Check keys"}, - {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "[*] Use loclass to perform bruteforce reader attack"}, - {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "[*] Uses authentication trace to check for key in dictionary file"}, +// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"}, + {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"}, + {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "Use loclass to perform bruteforce reader attack"}, + {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"}, - {"sim", CmdHFiClassSim, IfPm3Iclass, "[*] Simulate iCLASS tag"}, - {"eload", CmdHFiClassELoad, IfPm3Iclass, "[*] Load Picopass / iCLASS dump file into emulator memory"}, - {"esave", CmdHFiClassESave, IfPm3Iclass, "[*] Save emulator memory to file"}, - {"eview", CmdHFiClassEView, IfPm3Iclass, "[.] View emulator memory"}, + {"sim", CmdHFiClassSim, IfPm3Iclass, "Simulate iCLASS tag"}, + {"eload", CmdHFiClassELoad, IfPm3Iclass, "Load Picopass / iCLASS dump file into emulator memory"}, + {"esave", CmdHFiClassESave, IfPm3Iclass, "Save emulator memory to file"}, + {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("utils") " ---------------------"}, - {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "[*] Calc diversified keys (blocks 3 & 4) to write new keys"}, - {"encode", CmdHFiClassEncode, AlwaysAvailable, "[*] Encode binary wiegand to block 7"}, - {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "[*] Encrypt given block data"}, - {"decrypt", CmdHFiClassDecrypt, AlwaysAvailable, "[*] Decrypt given block data or tag dump file" }, - {"managekeys", CmdHFiClassManageKeys, AlwaysAvailable, "[*] Manage keys to use with iclass commands"}, - {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, " Permute function from 'heart of darkness' paper"}, - {"view", CmdHFiClassView, AlwaysAvailable, "[*] Display content from tag dump file"}, + {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "Calc diversified keys (blocks 3 & 4) to write new keys"}, + {"encode", CmdHFiClassEncode, AlwaysAvailable, "Encode binary wiegand to block 7"}, + {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "Encrypt given block data"}, + {"decrypt", CmdHFiClassDecrypt, AlwaysAvailable, "Decrypt given block data or tag dump file" }, + {"managekeys", CmdHFiClassManageKeys, AlwaysAvailable, "Manage keys to use with iclass commands"}, + {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, "Permute function from 'heart of darkness' paper"}, + {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 481a5ce14..e9b87add6 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -4869,25 +4869,41 @@ static int CmdHF14AMfMAD(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); uint8_t aid[2] = {0}; - int aidlen; + int aidlen = 0; CLIGetHexWithReturn(ctx, 2, aid, &aidlen); - uint8_t key[6] = {0}; - int keylen; - CLIGetHexWithReturn(ctx, 3, key, &keylen); + uint8_t userkey[6] = {0}; + int keylen = 0; + CLIGetHexWithReturn(ctx, 3, userkey, &keylen); bool keyB = arg_get_lit(ctx, 4); bool swapmad = arg_get_lit(ctx, 5); bool decodeholder = arg_get_lit(ctx, 6); CLIParserFree(ctx); - if (aidlen != 2 && !decodeholder && keylen > 0) { - PrintAndLogEx(WARNING, "Using default MAD keys instead"); - } - uint8_t sector0[16 * 4] = {0}; uint8_t sector10[16 * 4] = {0}; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { - PrintAndLogEx(ERR, "error, read sector 0. card don't have MAD or don't have MAD on default keys"); + + bool got_first = true; + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "error, read sector 0. card don't have MAD or don't have MAD on default keys"); + got_first = false; + } else { + PrintAndLogEx(INFO, "Authentication ( " _GREEN_("OK") " )"); + } + + // User supplied key + if (got_first == false && keylen == 6) { + PrintAndLogEx(INFO, "Trying user specified key..."); + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "error, read sector 0. card don't have MAD or don't the custom key is wrong"); + } else { + PrintAndLogEx(INFO, "Authentication ( " _GREEN_("OK") " )"); + got_first = true; + } + } + + // Both default and user supplied key failed + if (got_first == false) { return PM3_ESOFT; } @@ -4921,7 +4937,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // user specified key if (keylen == 6) { - memcpy(akey, key, 6); + memcpy(akey, userkey, 6); } uint16_t aaid = 0x0004; diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index f2282dbfd..f5222fb9a 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -2256,7 +2256,17 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid, } static int CmdHF14ADesGetUID(const char *Cmd) { - (void)Cmd; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getuid", + "Get UID from a MIFARE DESfire tag", + "hf mfdes getuid"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); uint8_t uid[16] = {0}; int res = handler_desfire_getuid(uid); @@ -3438,7 +3448,18 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { } static int CmdHF14ADesInfo(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes info", + "Get info from MIFARE DESfire tags", + "hf mfdes info"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + DropFieldDesfire(); mfdes_info_res_t info; @@ -3862,8 +3883,18 @@ static int CmdHF14ADesDump(const char *Cmd) { } static int CmdHF14ADesEnumApplications(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes enum", + "Enumerate all AID's on MIFARE DESfire tag", + "hf mfdes enum"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); - (void)Cmd; // Cmd is not used so far DropFieldDesfire(); uint8_t aid[3] = {0}; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index c69de053b..4d5106437 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -13,6 +13,7 @@ #include "commonutil.h" #include "crypto/libpcrypto.h" #include "des.h" +#include "aes.h" #include "cmdhfmf.h" #include "cmdhf14a.h" #include "comms.h" @@ -23,7 +24,6 @@ #include "cliparser.h" #include "cmdmain.h" - #define MAX_UL_BLOCKS 0x0F #define MAX_ULC_BLOCKS 0x2F #define MAX_ULEV1a_BLOCKS 0x13 @@ -42,220 +42,6 @@ static int CmdHelp(const char *Cmd); -static int usage_hf_mfu_info(void) { - PrintAndLogEx(NORMAL, "It gathers information about the tag and tries to detect what kind it is."); - PrintAndLogEx(NORMAL, "Sometimes the tags are locked down, and you may need a key to be able to read the information"); - PrintAndLogEx(NORMAL, "The following tags can be identified:\n"); - PrintAndLogEx(NORMAL, "Ultralight, Ultralight-C, Ultralight EV1, NTAG 203, NTAG 210,"); - PrintAndLogEx(NORMAL, "NTAG 212, NTAG 213, NTAG 215, NTAG 216, NTAG I2C 1K & 2K"); - PrintAndLogEx(NORMAL, "my-d, my-d NFC, my-d move, my-d move NFC\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu info k l"); - PrintAndLogEx(NORMAL, " Options : "); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu info")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu info k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu info k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_dump(void) { - PrintAndLogEx(NORMAL, "Reads all pages from Ultralight, Ultralight-C, Ultralight EV1"); - PrintAndLogEx(NORMAL, "NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216"); - PrintAndLogEx(NORMAL, "and saves binary dump into the file " _YELLOW_("`filename.bin`") " or " _YELLOW_("`cardUID.bin`")); - PrintAndLogEx(NORMAL, "It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu dump k l f p q <#pages>"); - PrintAndLogEx(NORMAL, " Options :"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, " f : " _YELLOW_("filename w/o .bin") " to save the dump as"); - PrintAndLogEx(NORMAL, " p : starting Page number to manually set a page to start the dump at"); - PrintAndLogEx(NORMAL, " q : number of Pages to manually set how many pages to dump"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_restore(void) { - PrintAndLogEx(NORMAL, "Restore dumpfile onto card."); - PrintAndLogEx(NORMAL, "Usage: hf mfu restore [h] [l] [s] k n "); - PrintAndLogEx(NORMAL, " Options :"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, " s : (optional) enable special write UID " _BLUE_("-MAGIC TAG ONLY-")); - PrintAndLogEx(NORMAL, " e : (optional) enable special write version/signature " _BLUE_("-MAGIC NTAG 21* ONLY-")); - PrintAndLogEx(NORMAL, " r : (optional) use the password found in dumpfile to configure tag. requires " _YELLOW_("'e'") " parameter to work"); - PrintAndLogEx(NORMAL, " f : " _YELLOW_("filename w .bin") " to restore"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu restore s f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu restore k AABBCCDD s f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu restore k AABBCCDD s e r f myfile")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_rdbl(void) { - PrintAndLogEx(NORMAL, "Read a block and print. It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu rdbl b k l\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b : block to read"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu rdbl b 0")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu rdbl b 0 k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu rdbl b 0 k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_wrbl(void) { - PrintAndLogEx(NORMAL, "Write a block. It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu wrbl b d k l\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b : block to write"); - PrintAndLogEx(NORMAL, " d : block data - (8 or 32 hex symbols, 32 hex symbols will do a compatibility write)"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu wrbl b 0 d 01234567")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu wrbl b 0 d 01234567 k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_eload(void) { - PrintAndLogEx(NORMAL, "It loads emul dump from the file " _YELLOW_("`filename.eml`")); - PrintAndLogEx(NORMAL, "Hint: See " _YELLOW_("`script run hf_mfu_dumptoemulator`") " to convert the .bin to the eml"); - PrintAndLogEx(NORMAL, "Usage: hf mfu eload u [numblocks]"); - PrintAndLogEx(NORMAL, " Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " u : UL (required)"); - PrintAndLogEx(NORMAL, " [filename] : without `.eml` (required)"); - PrintAndLogEx(NORMAL, " numblocks : number of blocks to load from eml file (optional)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu eload u filename")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu eload u filename 57")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_sim(void) { - PrintAndLogEx(NORMAL, "\nEmulating Ultralight tag from emulator memory\n"); - PrintAndLogEx(NORMAL, "\nBe sure to load the emulator memory first!\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu sim t 7 u [n ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " t 7 : 7 = NTAG or Ultralight sim (required)"); - PrintAndLogEx(NORMAL, " n : exit simulation after blocks have been read by reader. 0 = infinite (optional)"); - PrintAndLogEx(NORMAL, " u : 4 or 7 byte UID (optional)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu sim t 7")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu sim t 7 u 1122344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu sim t 7 u 1122344556677 n 5")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_ucauth(void) { - PrintAndLogEx(NORMAL, "Tests 3DES password on Mifare Ultralight-C tag."); - PrintAndLogEx(NORMAL, "If password is not specified, a set of known defaults will be tested."); - PrintAndLogEx(NORMAL, "Usage: hf mfu cauth [k] "); - PrintAndLogEx(NORMAL, " k - keep field on (only if a password is provided too)"); - PrintAndLogEx(NORMAL, " [password] - (32 hex symbols)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu cauth")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu cauth 000102030405060708090a0b0c0d0e0f")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_ucsetpwd(void) { - PrintAndLogEx(NORMAL, "Set 3DES password on Mifare Ultralight-C tag."); - PrintAndLogEx(NORMAL, "Usage: hf mfu setpwd "); - PrintAndLogEx(NORMAL, " [password] - (32 hex symbols)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setpwd 000102030405060708090a0b0c0d0e0f")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_ucsetuid(void) { - PrintAndLogEx(NORMAL, "Usage: hf mfu setuid "); - PrintAndLogEx(NORMAL, " [uid] - (14 hex symbols)"); - PrintAndLogEx(NORMAL, "\n"); - PrintAndLogEx(NORMAL, "This only works for " _BLUE_("Magic Ultralight") " tags."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 11223344556677")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_gendiverse(void) { - PrintAndLogEx(NORMAL, "Usage: hf mfu gen [h] [r] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " r : read uid from tag"); - PrintAndLogEx(NORMAL, " : 4 byte UID (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu gen r")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu gen 11223344")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_pwdgen(void) { - PrintAndLogEx(NORMAL, "Usage: hf mfu pwdgen [h|t] [r] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " t : selftest"); - PrintAndLogEx(NORMAL, " r : read uid from tag"); - PrintAndLogEx(NORMAL, " : 7 byte UID (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu pwdgen r")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu pwdgen 11223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu pwdgen t")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_otp_tearoff(void) { - PrintAndLogEx(NORMAL, "Tear-off test against OTP block (no 3) on MFU tags - More help sooner or later\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu otptear b i l s d t \n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b : (optional) block to run the test - default block: 8 (not OTP for safety)"); - PrintAndLogEx(NORMAL, " i