diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md index 514b454d7..8b5872891 100644 --- a/.github/ISSUE_TEMPLATE/checklist-for-release.md +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -18,26 +18,11 @@ assignees: doegox, iceman1001 - [ ] `tools/build_all_firmwares.sh` check that the script contains all standalone modes then compile all standalone modes (linux only) - [ ] `experimental_lib` compilation & tests - [ ] `experimental_client_with_swig` compilation & tests -- [ ] Check Android `CMakeLists.txt` list of source file - [ ] GitHub Actions - green across the board ( MacOS, Ubuntu, Windows) # OS compilation and tests -```bash -#!/usr/bin/env bash - -make clean && make -j PLATFORM=PM3GENERIC PLATFORM_EXTRAS= && tools/pm3_tests.sh --long || exit 1 -make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS= || exit 1 -make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON || exit 1 -make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && sudo make install PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && ( cd /tmp; proxmark3 -c 'data load -f lf_EM4x05.pm3;lf search -1'|grep 'Valid FDX-B ID found' ) && sudo make uninstall || exit 1 - -( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3GENERIC PLATFORM_EXTRAS= && cp -a ../*scripts ../*libs . && ../../tools/pm3_tests.sh --clientbin $(pwd)/proxmark3 client ) || exit 1 -( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS= ) || exit 1 -( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON ) || exit 1 - -# Hitag2crack, optionally with --long and --opencl ... -make hitag2crack/clean && make hitag2crack && tools/pm3_tests.sh hitag2crack || exit 1 -``` +Run `tools/release_tests.sh` on: - [ ] RPI Zero - [ ] Jetson Nano @@ -47,9 +32,9 @@ make hitag2crack/clean && make hitag2crack && tools/pm3_tests.sh hitag2crack || - [ ] Kali - [ ] Debian Stable - [ ] Debian Testing -- [ ] Ubuntu21 +- [ ] Ubuntu 22 - [ ] ParrotOS -- [ ] Fedora +- [ ] Fedora 37 - [ ] OpenSuse Leap - [ ] OpenSuse Tumbleweed - [ ] OSX (MacPorts) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 73ef661f7..0ec56e054 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,7 @@ jobs: 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 libssl-dev + 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 lua5.2 sed libssl-dev - name: Install Python dependencies run: | @@ -51,7 +51,7 @@ jobs: if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index d4969452d..a632a5e37 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -19,7 +19,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set Git http.postBuffer to something high run: git config --global http.postBuffer 524288000 @@ -36,6 +36,7 @@ jobs: - name: Install dependencies run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl + continue-on-error: true - name: Install Python dependencies run: | @@ -60,7 +61,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set Git http.postBuffer to something high run: git config --global http.postBuffer 524288000 @@ -77,6 +78,7 @@ jobs: - name: Install dependencies run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl + continue-on-error: true - name: Install Python dependencies run: | @@ -102,7 +104,7 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set Git http.postBuffer to something high run: git config --global http.postBuffer 524288000 @@ -119,6 +121,7 @@ jobs: - name: Install dependencies run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl + continue-on-error: true - name: Install Python dependencies run: | diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3f558bfac..acce10e3a 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -20,13 +20,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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 libssl-dev + 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 lua5.2 sed libssl-dev - name: Install Python dependencies run: | @@ -52,13 +52,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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 libssl-dev + 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 lua5.2 sed libssl-dev - name: Install Python dependencies run: | @@ -85,13 +85,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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 libssl-dev + 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 lua5.2 sed libssl-dev - name: Install Python dependencies run: | diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5da0035d5..77c3dc70c 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -48,7 +48,7 @@ jobs: working-directory: C:\ProxSpace run: ./runme64.bat -c "exit" - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: make clean run: make clean @@ -98,9 +98,9 @@ jobs: steps: - name: WSL setup - uses: Vampire/setup-wsl@v1 + uses: Vampire/setup-wsl@v2 with: - distribution: Ubuntu-20.04 + distribution: Ubuntu-22.04 update: "true" additional-packages: git ca-certificates @@ -134,7 +134,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: make clean run: make clean diff --git a/CHANGELOG.md b/CHANGELOG.md index 11934d16d..2f04f903b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,137 @@ 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] + - Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty) + - Added iClass credit key to default iClass key table and reorganized key order (@GuruSteve) + - Changed `hf mf value` - ability to use transfer on different block (@AloneLiberty) + - Change `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001) + - Added `hf mf gdmsetcfg` - Supprt Gen4 GDM write configuration block (@iceman1001) + - Added `hf mf gdmcfg` - Support Gen4 GDM read configuration block (@iceman1001) + - Changed magic note to include a section about GDM tags (@iceman1001) + - Added `hf mf gdmsetblk` - Support Gen4 GDM write block (@iceman1001) + - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) + - Changed CLI max string argument length limit from 512 to 4096 (@iceman1001) + - Fixed `data asn1` - now handles bad input better (@iceman1001) + - Added new public key for signature MIFARE Plus Troika (@iceman100) + - Fixed the client build on Android (@wh201906) + - Added TCP connection support on Windows (@wh201906) + - Added `data num` - easy convert between dec/hex/bin (@iceman1001) + - Fixed `hf mfdes info` - now handles incorrect tag answers better (@didiera) + - Fixed `hf mfdes` generic help text is now correct (@didiera) + - Fixed `pm3` script to correctly identify WSL enabled distros (@henrygab) + - Changed device enumeration with "unique USB serial numbers when built with `FLASH` -- **_UPDATES BOOTROM ALSO_**" (@henrygab) + - Changed the readline package to v8.2 in the CMAKE files for the client (@iceman1001) + - Fixed `pm3` script for passing arguments (@doegox) + - Fixed python paths to include current directory (@jmichelp) + - Fixed infinite loops in spindelayus (@lnv42) + - Changed ICECLASS standalone to support a read/sim mode (@natesales) + - Changed `hf iclass encode` - added verbose flag (@natesales) + - Changed `hf waveshare` - now identify 1.54 nfc epaper correct (@ah01) + - Fixed `Makefile` regression that broke `make install` (@henrygab) + - Fixed `lf em 4x70 brute` - now works as expected (@adite) + - Fixed the lf sampling when bits_per_sample is less than 8 (@wh201906) + - Added `lf em 4x70 brute` command (@adite) + - Added documentation for usage of Proxmark3 under WSL2 (@henrygab) + - Fixed device permissions via updated `udev` rules (@henrygab) + - Added `--back` option to `clear` command to clear the scrollback buffer (@wh201906) + - Changed `hf iclass decrypt` - mark credentials as decrypted in the dump (@natesales) + - Changed `hf iclass view` - show credentials on a decrypted dump (@natesales) + - Changed `hf mfu info` - NTAG213TT tamper info (mjaksn) + - Added commands for configuring NTAG213TT tamper featue (@mjaksn) + - Added Mifare Classic EV1 signature write support to gen4 magic tag lua script (@augustozanellato) + - Added XOR key extraction and flag to Guardall G-Prox II (@GuruSteve) + - Changed verbiage on `hf iclass info` KeyAccess area to be congruent with AA1 and AA2 areas (@GuruSteve) + - Added `hf legic info` command for other sources (@0xdeb) + - Added `hf legic einfo` - views emulator menory (@0xdeb) + - Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb) + +## [Nitride.4.16191][2023-01-29] + - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) + - Fixed some coverity fixes (@iceman1001) + - Fixed `make accessrights` on Fedora (@mooey5775) + - Fixed `hf mfu info` - can now identify the 50 pF version of NTAG 210u(micro) (@mjacksn) + - Added `hf 15` sub-commands for controlling EAS, AFI, privacy mode, and the setting of passwords on SLIX tags (@mjacksn) + - Added new magic gen4 cards command in docs (@McEloff) + - Added `hf tesla info` - intital information command to read TESLA cards (@iceman1001) + - Changed `hf emrtd info` - looking for lower case .bin extensions (@iceman1001) + - Changed `hf emrtd dump` - looking for lower case .bin extensions (@iceman1001) + - Changed `lf paradox clone` - it now accepts FC/CN (@mwalker33) + - Added standalone mode for simulatin Nedap ID (@anon) + - Changed `hf mfu info` - now also does a simple OTP fingerprinting (@iceman1001) + - Changed `hf mf wrbl` - now checks for strict readonly ACL's in the data to write (@iceman1001) + - Changed `hf mf view` - verbose printing if strict readonly ACL's exists in dump file (@iceman1001) + - Add command `piv authsign` to get a buffer signed by the selected key (@jmichelp) + - Add command `piv scan` which tries to read all known containers on PIV (@jmichelp) + - Add support for PIV commands, over wired and contactless interfaces (@jmichelp) + - Add `--shallow` option to `hf iclass` reader commands to do shallow (ASK) reader modulation instead of OOK (@nvx) + - Improved NXP SLI/SLIX series tag identification (@nvx) + - Fixed buffer overflow in "lf em 4x05 sniff" (@HeinrichsH) + - Fixed potential NULL array printing (@jmichelp) + - Added PIV aid to resource file (@jmichelp) + - Fixed failing compilation on Proxspace environment due to how python is initialized (@jmichelp) + - Fixed length check in sim module communications (@jmichelp) + - Changed timings in i2c.c when communicating with sim module (@iceman1001) + - Moved to non-deprecated API to initialize Python interpreter (@jmichelp) + - Changed `sc upgrade` updated firmware v4.13 (RDV40) - frame buffer is now 384 bytes (@sentiprox) + - Fixed contact interface / smartcard APDU chaining logic and allow 256 bytes ADPU payload. Need SIM firmware 4.13 to work (@jmichelp) + - Fixed `lf hitag dump` - Should now work as described in the command help (@natmchugh) + - Fixed SPI flash overflow when loading dictionnaries into flash. Breaking change: added 1 more sector for Mifare - dictionnaries should be loaded again (@jmichelp) + - Added `hf mf gload, gsave, ggetblk, gsetblk` for Gen4 GTU in mifare classic mode (@DidierA) + - Fixed `trace list -r` (relative times) not working unless `-u` (microseconds) was specified, and made `--frame` respect `-u` and `-r` options (@nvx) + - Added detection of magic Gen4 GTU (@DidierA) + - Added luascript `hf_i2c_plus_2k_utils` - Script for dumping/modifying user memory of sectors 0 and 1 (@flamebarke) + - Added `hf mfu esave` - saves emulator memory to mfu dump file (@DidierA) + - Added luascript `hf_mfu_ntag` - Script for configuring NTAG216 configuration pages (@flamebarke) + - Changed `hf mf hardnested` - a detection for static encrypted nonces (@iceman1001) + - Added requirements.txt file to tools folder. Minimum to run pm3_tests.sh (@iceman1001) + - Changed `hf mf hardnested` - now can detect and use MFC EV1 signature sector key (@iceman1001) + - Changed `hf mf autopwn` - now can detect and use MFC EV1 signature sector key (@iceman1001) + - Fixed `pm3` shell script now automatically detects WSL2 with USBIPD serial ports (@iceman1001) + - Fixed `trace list -c` - annotation of CRC bytes now is colored or squared if no ansi colors is supported (@iceman1001) + - Fixed `trace list -t mf` - now also finds UID if anticollision is partial captured, to be used for mfkey (@iceman1001) + +## [Radium.4.15864][2022-10-29] + - Changed `lf indala sim` - now accepts fc / cn (@iceman1001) + - Added `lf indala brute`- brute forcing of 64b Indala ID (@iceman1001) + - Added `hf 14a ndefwrite` - write raw NDEF records to TYPE4A tags (@iceman1001) + - Changed ndef output to be more dense. Honors verbose now (@iceman1001) + - Fixed `hf mf ndefwrite` - now skips not ndef formatted sectors (@iceman1001) + - Fixed `hf mf ndefread` - now skips not ndef formatted sectors correctly (@iceman1001) + - Fixed `lf hitag` - keep inside the arrays for key/password/nrar (@iceman1001) + - Added `hf mf ndefwrite` - write raw NDEF records to MIFARE Classic tag (@iceman1001) + - Changed `hf mf cwipe` - swapped the block0 data to genuine manufacture ones (@iceman1001) + - Added `hf mf ndefformat` - format a MIFARE Classic tag as NFC tag with Data Exchange Format (NDEF) (@iceman1001) + - Changed `hf 14b dump, view` to get correct chip type in case of SRT512 and friends (@DidierA) + - Added `hf 15 eview` and `hf 15 esave` - Retrieve emulator image for ISO15693 simulation (@markus-oehme-pg40) + - Changed `hf 15 sim` - now supports reader writes (@markus-oehme-pg40) + - Added `hf 15 eload` - specify memory image for ISO15693 simulation (@markus-oehme-pg40) + - Added `hf 15 sim --blocksize` - configure block size for simulation (@markus-oehme-pg40) + - Fixed buffer overflow in mfu ndef decode (@mwalker) + - Changed spiffs write/append to send in 8192 chunks to ensure its eraised (@mwalker) + - Fixed spiffs dump to ensure to fails correctly if no big_buff was allocated (@mwalker) + - Change Client Makefile to respect global flags (@blshkv) + - Change Makefile, honors global CC values (@blshkv) + - Fixed bad memory handling in MifareSim device side (@iceman1001) + - Added json topaz file format (@iceman1001) + - Added `hf topaz rdbl, wrbl, view` commands (@iceman1001) + - Added more details to the annotations of `hf mfdes list` output (@nvx) + - Changed `hf iclass view` and related to suppress consecutive blocks with repeated contents with a `-z` flag, or `prefs set output --dense` (@nvx) + - Changed `hf iclass list` to display matched keys on the CHECK command rather than the card response, and made it check for elite keys too (@nvx) + - Fixed `hf iclass info` and `hf iclass view` key access info looking at the wrong card config bit (@nvx) + - Added `hf gallagher decode` command and fix Gallagher diversification for card master key (@nvx) + - Changed `hf texkom reader` - now supports mmbit-002 (kibi-002, kb5004xk1) russian tag (@merlokk) + - Added `hf sniff --smode` skip/group adc data to consume less memory. Now it can sniff very long signals (@merlokk) + - Added `hf fudan` skeleton commands (@iceman1001) + - Added `--reboot-to-bootloader` arg to pm3 + - Changed `hf 14b raw` - now supports selecting Fuji/Xerox tag (@horror) + - Added `hf xerox dump` - dump a Fuji/Xerox tag (@horror) + - Added `hf xerox info` - read Fuji/Xerox tag (@horror) + - Fixed `hf 14a reader --ecp` to work consistently (@kormax) - Change `trace list -t 14a` to annotate ECP frames of all valid V1 and V2 formats (@kormax) + - Added 122 new keys from Flipper Zero community to `mfc_default_keys.dic` (@UberGuidoZ) + - Added showing password for the read command in the `lf t55xx sniff` command (@merlokk) + - Added reading texcom tk13 and tk17 tags with `hf texkom read` command (@merlokk @iceman1001) + - Added simulating texcom tk13 and tk17 tags with `hf texkom sim` command (@merlokk) - Changed `hf mf staticnested` - significant speedups by using two encrypted nonces (@xianglin1998) - Change use of `sprintf` in code to `snprintf` to fix compilation on macOS (@Doridian) - Change `hf mf mad` - it now takes a dump file for offline MAD decoding (@iceman1001) @@ -64,6 +194,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed - AID limitations when using Gallagher key diversification (@DarkMatterMatt) - Added new standalone mode `lf_em4100rsww` (@zabszk) - Fixed `hf 15 slixdisable` wrong pass id (@r1ddl3rz) + - Added `script run hf_mf_hid_sim.lua` (@micsen) ## [Frostbit.4.14831][2022-01-11] - Changed Wiegand format lookup - now case-insensitive (@iceman1001) @@ -1281,7 +1412,10 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Mifare simulation, `hf mf sim` (was broken a long time) (@pwpiwi) - Major improvements in LF area and data operations. (@marshmellow42, @iceman1001) - Issues regarding LF simulation (@pwpiwi) + - Issue interpreting NXP "get sys info" command return value for icode tags. (@mjacksn) ### Added - iClass functionality: full simulation of iclass tags, so tags can be simulated with data (not only CSN). Not yet support for write/update, but readers do not seem to enforce update. (@holiman). - iClass decryption. Proxmark can now decrypt data on an iclass tag, but requires you to have the HID decryption key locally on your computer, as this is not bundled with the sourcecode. + - `hf 15 info` can detect NTAG 5 tags + - `hf 15 info` include an EAS status check on more of the icode tags which support EAS (SLI, SLIX, SLIX-L, and SLIX-S) diff --git a/Makefile b/Makefile index a846bd706..be4af6682 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfk #all clean install uninstall check: %: hitag2crack/% INSTALLTOOLS=pm3_eml2lower.sh pm3_eml2upper.sh pm3_mfdread.py pm3_mfd2eml.py pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py -INSTALLSIMFW=sim011.bin sim011.sha512.txt +INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt INSTALLSCRIPTS=pm3 pm3-flash pm3-flash-all pm3-flash-bootrom pm3-flash-fullimage INSTALLSHARES=tools/jtag_openocd traces INSTALLDOCS=doc/*.md doc/md @@ -65,7 +65,13 @@ ifneq (,$(INSTALLSIMFW)) endif ifeq ($(platform),Linux) $(Q)$(INSTALLSUDO) $(MKDIR) $(DESTDIR)$(UDEV_PREFIX) - $(Q)$(INSTALLSUDO) $(CP) driver/77-pm3-usb-device-blacklist.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +# If user is running ArchLinux, use group 'uucp' +# Else, use group 'dialout' +ifneq ($(wildcard /etc/arch-release),) + $(Q)$(INSTALLSUDO) $(CP) driver/77-pm3-usb-device-blacklist-uucp.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +else + $(Q)$(INSTALLSUDO) $(CP) driver/77-pm3-usb-device-blacklist-dialout.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +endif endif uninstall: common/uninstall @@ -75,25 +81,32 @@ common/uninstall: ifneq (,$(INSTALLSCRIPTS)) $(Q)$(INSTALLSUDO) $(RM) $(foreach script,$(INSTALLSCRIPTS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH)$(PATHSEP)$(notdir $(script))) endif + ifneq (,$(INSTALLSHARES)) $(Q)$(INSTALLSUDO) $(RMDIR) $(foreach share,$(INSTALLSHARES),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)$(PATHSEP)$(notdir $(share))) endif + ifneq (,$(INSTALLDOCS)) $(Q)$(INSTALLSUDO) $(RMDIR) $(foreach doc,$(INSTALLDOCS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH)$(PATHSEP)$(notdir $(doc))) - $(Q)$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH) + $(Q)-$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH) endif + ifneq (,$(INSTALLTOOLS)) $(Q)$(INSTALLSUDO) $(RM) $(foreach tool,$(INSTALLTOOLS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)$(PATHSEP)$(notdir $(tool))) endif - $(Q)$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) + + $(Q)-$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) + ifneq (,$(INSTALLSIMFW)) $(Q)$(INSTALLSUDO) $(RM) $(foreach fw,$(INSTALLSIMFW),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(notdir $(fw))) endif - $(Q)$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + + $(Q)-$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + ifeq ($(platform),Linux) $(Q)$(INSTALLSUDO) $(RM) $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules endif - $(Q)$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)-$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) # tests mfkey/check: FORCE @@ -248,28 +261,39 @@ ifeq ($(PLATFORM_CHANGED),true) $(Q)$(MAKE) --no-print-directory -C recovery clean $(Q)$(MAKE) --no-print-directory -C client clean $(Q)$(MAKE) --no-print-directory -C tools/fpga_compress clean - $(Q)echo CACHED_PLATFORM=$(PLATFORM) > .Makefile.options.cache - $(Q)echo CACHED_PLATFORM_EXTRAS=$(PLATFORM_EXTRAS) >> .Makefile.options.cache - $(Q)echo CACHED_PLATFORM_DEFS=$(PLATFORM_DEFS) >> .Makefile.options.cache + $(Q)$(ECHO) CACHED_PLATFORM=$(PLATFORM) > .Makefile.options.cache + $(Q)$(ECHO) CACHED_PLATFORM_EXTRAS=$(PLATFORM_EXTRAS) >> .Makefile.options.cache + $(Q)$(ECHO) CACHED_PLATFORM_DEFS=$(PLATFORM_DEFS) >> .Makefile.options.cache endif # configure system to ignore PM3 device as a modem (ModemManager blacklist, effective *only* if ModemManager is not using _strict_ policy) # Read doc/md/ModemManager-Must-Be-Discarded.md for more info udev: - sudo cp -rf driver/77-pm3-usb-device-blacklist.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules - sudo udevadm control --reload-rules +ifneq ($(wildcard /etc/arch-release),) +# If user is running ArchLinux, use group 'uucp' + $(SUDO) cp -rf driver/77-pm3-usb-device-blacklist-uucp.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +else +# Else, use group 'dialout' + $(SUDO) cp -rf driver/77-pm3-usb-device-blacklist-dialout.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +endif + $(SUDO) udevadm control --reload-rules + $(SUDO) udevadm trigger --action=change -# configure system to add user to the dialout group +# configure system to add user to the dialout group and if bluetooth group exists, add user to it # you need to logout, relogin to get this access right correct. # Finally, you might need to run the proxmark3 client under SUDO on some systems accessrights: ifneq ($(wildcard /etc/arch-release),) #If user is running ArchLinux, use specific command and group - $(Q)sudo usermod -aG uucp $(USER) - $(Q)getent group bluetooth >/dev/null && sudo usermod -aG bluetooth $(USER) || true + $(Q)$(SUDO) $(USERMOD) uucp $(USER) + $(Q)$(GETENT_BL) >/dev/null && $(SUDO) $(USERMOD) bluetooth $(USER) || true +else ifneq ($(wildcard /etc/fedora-release),) +# If the user is running Fedora, use `usermod` with the dialout group + $(Q)$(SUDO) $(USERMOD) dialout $(USER) + $(Q)$(GETENT_BL) >/dev/null && $(SUDO) $(USERMOD) bluetooth $(USER) || true else - $(Q)sudo adduser $(USER) dialout - $(Q)getent group bluetooth >/dev/null && sudo adduser $(USER) bluetooth || true + $(Q)$(SUDO) $(ADDUSER) $(USER) dialout + $(Q)$(GETENT_BL) >/dev/null && $(SUDO) $(ADDUSER) $(USER) bluetooth || true endif # easy printing of MAKE VARIABLES diff --git a/Makefile.defs b/Makefile.defs index 9b6947094..bcbbaa67e 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -29,18 +29,17 @@ GZIP = gzip MKDIR = mkdir -p RM = rm -f RMDIR = rm -rf -# rmdir only if dir is empty, tolerate failure -RMDIR_SOFT = -rmdir +# rmdir only if dir is empty, you must add "-" when using it to tolerate failure +RMDIR_SOFT = rmdir MV = mv TOUCH = touch FALSE = false TAR = tar TARFLAGS ?= -v --ignore-failed-read -r TARFLAGS += -C .. -f -CROSS ?= arm-none-eabi- -CC = gcc -CXX = g++ -LD = g++ +CROSS ?= arm-none-eabi- +CC ?= gcc +CXX ?= g++ SH = sh BASH = bash PERL = perl @@ -48,6 +47,14 @@ SWIG = swig CC_VERSION = $(shell $(CC) -dumpversion 2>/dev/null|sed 's/\..*//') CC_VERSION := $(or $(strip $(CC_VERSION)),0) +ECHO = echo +SUDO = sudo +USERMOD = usermod -aG +ADDUSER = adduser +GETENT_BL = getent group bluetooth + + + PATHSEP=/ PREFIX ?= /usr/local UDEV_PREFIX ?= /etc/udev/rules.d @@ -78,9 +85,13 @@ else endif ifeq ($(USE_BREW),1) - BREW_PREFIX = $(shell brew --prefix 2>/dev/null) - ifeq ($(strip $(BREW_PREFIX)),) - USE_BREW = 0 + ifneq ($(strip $(HOMEBREW_PREFIX)),) + BREW_PREFIX = $(HOMEBREW_PREFIX) + else + BREW_PREFIX = $(shell brew --prefix 2>/dev/null) + ifeq ($(strip $(BREW_PREFIX)),) + USE_BREW = 0 + endif endif endif diff --git a/Makefile.host b/Makefile.host index a2a9e829a..8ca8bc50d 100644 --- a/Makefile.host +++ b/Makefile.host @@ -86,8 +86,8 @@ $(BINDIR)/$(LIB_A): $(MYOBJS) $(MYCXXOBJS) $(Q)$(RANLIB) $@ $(BINDIR)/% : $(OBJDIR)/%.o $(MYOBJS) $(MYCXXOBJS) $(MYLIBS) - $(info [=] LD $(notdir $@)) - $(Q)$(LD) $(LDFLAGS) $(MYOBJS) $(MYCXXOBJS) $< -o $@ $(MYLIBS) $(MYLDLIBS) + $(info [=] CXX $(notdir $@)) + $(Q)$(CXX) $(LDFLAGS) $(MYOBJS) $(MYCXXOBJS) $< -o $@ $(MYLIBS) $(MYLDLIBS) %.o: %.c $(OBJDIR)/%.o : %.c $(OBJDIR)/%.d | $(OBJDIR) diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 5dc19e8f0..aeb541cee 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -5,8 +5,23 @@ PLATFORM=PM3RDV4 #PLATFORM=PM3GENERIC # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON +#PLATFORM_EXTRAS=FLASH +#PLATFORM_EXTRAS=BTADDON FLASH #STANDALONE=LF_SAMYRUN +# Uncomment the lines below in order to make a 256KB image +# and comment out the lines above + +#PLATFORM=PM3GENERIC +#PLATFORM_SIZE=256 +#STANDALONE= +#SKIP_HITAG=1 +#SKIP_FELICA=1 +#SKIP_HFPLOT=1 +#SKIP_NFCBARCODE=1 +#SKIP_ZX8211=1 +#SKIP_LF=1 + # To accelerate repetitive compilations: # Install package "ccache" -> Debian/Ubuntu: /usr/lib/ccache, Fedora/CentOS/RHEL: /usr/lib64/ccache # And uncomment the following line diff --git a/README.md b/README.md index 0738cd144..20416bdbb 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ We define generic Proxmark3 platforms as following devices. - **Note**: unknown pin assignments. - ⚠ Ryscorp Proxmark3 Pro - **Note**: device has different fpga and unknown pin assignments. - - **Note**: Company have dissappared, leaving their customers in the dark. + - **Note**: Company have disappeared, leaving their customers in the dark. - ⚠ iCopy-X - **Note**: experimental support, currently incompatible with iCopy-X GUI as Proxmark client commands are now using cliparser. - **Note**: see also [icopyx-community repos](https://github.com/iCopy-X-Community/) for upstream sources, reversed hw etc. @@ -93,24 +93,30 @@ We define generic Proxmark3 platforms as following devices. - ⚠ VX - **Note**: unknown device hw - ⚠ Proxmark3 X - - **Note**: unknown device hw. + - **Note**: unknown device hw +- ⚠ Proxmark3 Ultimate + - **Note**: unknown device hw +- ⚠ Proxmark3 SE + - **Note**: unknown device hw + +When it comes to these new unknown models we are depending on the community to report in if this repo works and what they did to make it work. -**256kb flash memory size of generic Proxmark3 platforms** +**256KB flash memory size of generic Proxmark3 platforms** > ⚠ **Note**: > 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. +> 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`. > -> [OBS! Read the 256kb flash memory advisory](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#256kb-versions) +> [OBS! Read the 256KB flash memory advisory](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#256KB-versions) # What has changed? Proxmark3 RDV4 hardware modifications: - * added flash memory 256kb + * added flash memory 256KB * added smart card module * added FPC connector for peripherals such as Bluetooth+battery addon * improved antennas @@ -200,7 +206,7 @@ The official PM3-GUI from Gaucho will not work. Not to mention is quite old and ## Official channels Where do you find the community? - - [RFID Hacking community discord server](https://discord.gg/iceman) + - [RFID Hacking community discord server](https://t.ly/d4_C) - [Proxmark3 IRC channel](https://web.libera.chat/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - [Proxmark3 forum](http://www.proxmark.org/forum/index.php) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..93cabb3fb --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +This is an open source project driven by volunteering devs contributing their time for free. + +This is a bleeding edge repository. +The maintainers actively is working out of this repository and will be periodically re-structuring the code to make it easier to comprehend, navigate, build, test, and contribute to, so DO expect significant changes to code layout on a regular basis. + + +## Reporting a Vulnerability + +if you find a vulnerability feel free to make a pull request with your fix and we will deal with it. + + +👉 Remember! +If you intend to contribute to the code, please read the coding style notes first. We usually merge your contributions fast since we do like the idea of getting a functionality in the Proxmark3 and weed out the bugs afterwards. diff --git a/armsrc/Makefile b/armsrc/Makefile index 413570565..5024aa1de 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -35,6 +35,7 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ -ffunction-sections -fdata-sections SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c +SRC_HF = hfops.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c #UNUSED: mifaresniff.c @@ -132,6 +133,7 @@ THUMBSRC = start.c \ $(SRC_EM4x50) \ $(SRC_EM4x70) \ $(SRC_SPIFFS) \ + $(SRC_HF) \ $(SRC_ISO14443a) \ $(SRC_ISO14443b) \ $(SRC_CRAPTO1) \ @@ -150,7 +152,8 @@ THUMBSRC = start.c \ BigBuf.c \ ticks.c \ clocks.c \ - hfsnoop.c + hfsnoop.c \ + generator.c # These are to be compiled in ARM mode @@ -182,7 +185,7 @@ showinfo: .DELETE_ON_ERROR: # version_pm3.c should be remade on every time fullimage.stage1.elf should be remade -version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) +version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) .FORCE $(info [-] GEN $@) $(Q)$(SH) ../tools/mkversion.sh > $@ || $(CP) $< $@ @@ -254,7 +257,7 @@ uninstall: $(info [@] Uninstalling fullimage from $(DESTDIR)$(PREFIX)...) $(Q)$(INSTALLSUDO) $(RM) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(INSTALLFWTAG) -.PHONY: all clean help install uninstall +.PHONY: all clean help install uninstall .FORCE help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index 6b4510da8..cb41bc148 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -50,12 +50,18 @@ define KNOWN_STANDALONE_DEFINITIONS | LF_ICEHID | LF HID collector to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ +| LF_NEDAP_SIM | LF Nedap ID simple simulator | +| | | ++----------------------------------------------------------+ | LF_NEXID | LF Nexwatch collector to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ | LF_PROXBRUTE | HID ProxII bruteforce | | | - Brad Antoniewicz | +----------------------------------------------------------+ +| LF_PROX2BRUTE | HID ProxII bruteforce v2 | +| | | ++----------------------------------------------------------+ | LF_SAMYRUN | HID26 read/clone/sim | | (default) | - Samy Kamkar | +----------------------------------------------------------+ @@ -118,11 +124,14 @@ define KNOWN_STANDALONE_DEFINITIONS +----------------------------------------------------------+ endef -STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE LF_NEXID -STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG HF_REBLAY DANKARMULTI + +STANDALONE_MODES := LF_SKELETON +STANDALONE_MODES += LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE +STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_YOUNG +STANDALONE_MODES += DANKARMULTI STANDALONE_MODES_REQ_BT := HF_REBLAY STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_MFCSIM HF_LEGICSIM +STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 05a63f1dd..48ac2217f 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -21,13 +21,21 @@ SRC_STANDALONE = placeholder.c ifneq (,$(findstring WITH_STANDALONE_LF_SKELETON,$(APP_CFLAGS))) SRC_STANDALONE = lf_skeleton.c endif -# WITH_STANDALONE_LF_SAMYRUN -ifneq (,$(findstring WITH_STANDALONE_LF_SAMYRUN,$(APP_CFLAGS))) - SRC_STANDALONE = lf_samyrun.c +# WITH_STANDALONE_LF_EM4100EMUL +ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS))) + SRC_STANDALONE = lf_em4100emul.c endif -# WITH_STANDALONE_LF_PROXBRUTE -ifneq (,$(findstring WITH_STANDALONE_LF_PROXBRUTE,$(APP_CFLAGS))) - SRC_STANDALONE = lf_proxbrute.c +# WITH_STANDALONE_LF_EM4100RSWB +ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RSWB,$(APP_CFLAGS))) + SRC_STANDALONE = lf_em4100rswb.c +endif +# WITH_STANDALONE_LF_EM4100RSWW +ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RSWW,$(APP_CFLAGS))) + SRC_STANDALONE = lf_em4100rsww.c +endif +# WITH_STANDALONE_LF_EM4100RWC +ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RWC,$(APP_CFLAGS))) + SRC_STANDALONE = lf_em4100rwc.c endif # WITH_STANDALONE_LF_HIDBRUTE ifneq (,$(findstring WITH_STANDALONE_LF_HIDBRUTE,$(APP_CFLAGS))) @@ -37,21 +45,33 @@ endif ifneq (,$(findstring WITH_STANDALONE_LF_HIDFCBRUTE,$(APP_CFLAGS))) SRC_STANDALONE = lf_hidfcbrute.c endif -# WITH_STANDALONE_HF_YOUNG -ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) - SRC_STANDALONE = hf_young.c +# WITH_STANDALONE_LF_ICEHID +ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) + SRC_STANDALONE = lf_icehid.c endif -# WITH_STANDALONE_HF_MATTYRUN -ifneq (,$(findstring WITH_STANDALONE_HF_MATTYRUN,$(APP_CFLAGS))) - SRC_STANDALONE = hf_mattyrun.c +# WITH_STANDALONE_LF_NEDAP_SIM +ifneq (,$(findstring WITH_STANDALONE_LF_NEDAP_SIM,$(APP_CFLAGS))) + SRC_STANDALONE = lf_nedap_sim.c endif -# WITH_STANDALONE_HF_COLIN -ifneq (,$(findstring WITH_STANDALONE_HF_COLIN,$(APP_CFLAGS))) - SRC_STANDALONE = vtsend.c hf_colin.c frozen.c nprintf.c +# WITH_STANDALONE_LF_NEXID +ifneq (,$(findstring WITH_STANDALONE_LF_NEXID,$(APP_CFLAGS))) + SRC_STANDALONE = lf_nexid.c endif -# WITH_STANDALONE_HF_BOG -ifneq (,$(findstring WITH_STANDALONE_HF_BOG,$(APP_CFLAGS))) - SRC_STANDALONE = hf_bog.c +# WITH_STANDALONE_LF_SAMYRUN +ifneq (,$(findstring WITH_STANDALONE_LF_SAMYRUN,$(APP_CFLAGS))) + SRC_STANDALONE = lf_samyrun.c +endif +# WITH_STANDALONE_LF_PROXBRUTE +ifneq (,$(findstring WITH_STANDALONE_LF_PROXBRUTE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_proxbrute.c +endif +# WITH_STANDALONE_LF_PROX2BRUTE +ifneq (,$(findstring WITH_STANDALONE_LF_PROX2BRUTE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_prox2brute.c +endif +# WITH_STANDALONE_LF_THAREXDE +ifneq (,$(findstring WITH_STANDALONE_LF_THAREXDE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_tharexde.c endif # WITH_STANDALONE_HF_14ASNIFF ifneq (,$(findstring WITH_STANDALONE_HF_14ASNIFF,$(APP_CFLAGS))) @@ -69,33 +89,21 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_AVEFUL,$(APP_CFLAGS))) SRC_STANDALONE = hf_aveful.c endif -# WITH_STANDALONE_HF_TCPRST -ifneq (,$(findstring WITH_STANDALONE_HF_TCPRST,$(APP_CFLAGS))) - SRC_STANDALONE = hf_tcprst.c +# WITH_STANDALONE_HF_BOG +ifneq (,$(findstring WITH_STANDALONE_HF_BOG,$(APP_CFLAGS))) + SRC_STANDALONE = hf_bog.c endif -# WITH_STANDALONE_LF_ICEHID -ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) - SRC_STANDALONE = lf_icehid.c +# WITH_STANDALONE_HF_COLIN +ifneq (,$(findstring WITH_STANDALONE_HF_COLIN,$(APP_CFLAGS))) + SRC_STANDALONE = vtsend.c hf_colin.c frozen.c nprintf.c endif -# WITH_STANDALONE_LF_NEXID -ifneq (,$(findstring WITH_STANDALONE_LF_NEXID,$(APP_CFLAGS))) - SRC_STANDALONE = lf_nexid.c +# WITH_STANDALONE_HF_CRAFTBYTE +ifneq (,$(findstring WITH_STANDALONE_HF_CRAFTBYTE,$(APP_CFLAGS))) + SRC_STANDALONE = hf_craftbyte.c endif -# WITH_STANDALONE_LF_EM4100EMUL -ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS))) - SRC_STANDALONE = lf_em4100emul.c -endif -# WITH_STANDALONE_LF_EM4100RSWB -ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RSWB,$(APP_CFLAGS))) - SRC_STANDALONE = lf_em4100rswb.c -endif -# WITH_STANDALONE_LF_EM4100RSWW -ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RSWW,$(APP_CFLAGS))) - SRC_STANDALONE = lf_em4100rsww.c -endif -# WITH_STANDALONE_LF_EM4100RWC -ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RWC,$(APP_CFLAGS))) - SRC_STANDALONE = lf_em4100rwc.c +# WITH_STANDALONE_HF_ICECLASS +ifneq (,$(findstring WITH_STANDALONE_HF_ICECLASS,$(APP_CFLAGS))) + SRC_STANDALONE = hf_iceclass.c endif # WITH_STANDALONE_HF_LEGIC ifneq (,$(findstring WITH_STANDALONE_HF_LEGIC,$(APP_CFLAGS))) @@ -105,33 +113,33 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_LEGICSIM,$(APP_CFLAGS))) SRC_STANDALONE = hf_legicsim.c endif +# WITH_STANDALONE_HF_MATTYRUN +ifneq (,$(findstring WITH_STANDALONE_HF_MATTYRUN,$(APP_CFLAGS))) + SRC_STANDALONE = hf_mattyrun.c +endif +# WITH_STANDALONE_HF_MFCSIM +ifneq (,$(findstring WITH_STANDALONE_HF_MFCSIM,$(APP_CFLAGS))) + SRC_STANDALONE = hf_mfcsim.c +endif # WITH_STANDALONE_HF_MSDSAL ifneq (,$(findstring WITH_STANDALONE_HF_MSDSAL,$(APP_CFLAGS))) SRC_STANDALONE = hf_msdsal.c endif -# WITH_STANDALONE_HF_ICECLASS -ifneq (,$(findstring WITH_STANDALONE_HF_ICECLASS,$(APP_CFLAGS))) - SRC_STANDALONE = hf_iceclass.c +# WITH_STANDALONE_HF_REBLAY +ifneq (,$(findstring WITH_STANDALONE_HF_REBLAY,$(APP_CFLAGS))) + SRC_STANDALONE = hf_reblay.c endif -# WITH_STANDALONE_LF_THAREXDE -ifneq (,$(findstring WITH_STANDALONE_LF_THAREXDE,$(APP_CFLAGS))) - SRC_STANDALONE = lf_tharexde.c -endif -# WITH_STANDALONE_HF_CRAFTBYTE -ifneq (,$(findstring WITH_STANDALONE_HF_CRAFTBYTE,$(APP_CFLAGS))) - SRC_STANDALONE = hf_craftbyte.c +# WITH_STANDALONE_HF_TCPRST +ifneq (,$(findstring WITH_STANDALONE_HF_TCPRST,$(APP_CFLAGS))) + SRC_STANDALONE = hf_tcprst.c endif # WITH_STANDALONE_HF_TMUDFORD ifneq (,$(findstring WITH_STANDALONE_HF_TMUDFORD,$(APP_CFLAGS))) SRC_STANDALONE = hf_tmudford.c endif - # WITH_STANDALONE_HF_REBLAY -ifneq (,$(findstring WITH_STANDALONE_HF_REBLAY,$(APP_CFLAGS))) - SRC_STANDALONE = hf_reblay.c -endif - # WITH_STANDALONE_HF_MFCSIM -ifneq (,$(findstring WITH_STANDALONE_HF_MFCSIM,$(APP_CFLAGS))) - SRC_STANDALONE = hf_mfcsim.c +# WITH_STANDALONE_HF_YOUNG +ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) + SRC_STANDALONE = hf_young.c endif ifneq (,$(findstring WITH_STANDALONE_DANKARMULTI,$(APP_CFLAGS))) diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 8f9bf4c63..481475bbc 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -43,9 +43,10 @@ #define ICE_STATE_ATTACK 2 #define ICE_STATE_READER 3 #define ICE_STATE_CONFIGCARD 4 -#define ICE_STATE_DUMP_SIM 5 +#define ICE_STATE_DUMP_SIM 5 +#define ICE_STATE_READ_SIM 6 -#define HF_ICLASS_NUM_MODES 6 +#define HF_ICLASS_NUM_MODES 7 // ==================================================== // Select which standalone function to be active. @@ -56,6 +57,7 @@ //#define ICE_USE ICE_STATE_READER //#define ICE_USE ICE_STATE_CONFIGCARD //#define ICE_USE ICE_STATE_DUMP_SIM +//#define ICE_USE ICE_STATE_READ_SIM // ==================================================== @@ -322,6 +324,7 @@ static int reader_dump_mode(void) { .use_credit_key = false, .do_auth = true, .send_reply = false, + .shallow_mod = false, }; memcpy(auth.key, legacy_aa1_key, sizeof(auth.key)); @@ -333,7 +336,7 @@ static int reader_dump_mode(void) { // select tag. uint32_t eof_time = 0; - bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); + bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time, false); if (res == false) { switch_off(); continue; @@ -382,7 +385,7 @@ static int reader_dump_mode(void) { // main read loop for (uint16_t i = start_block; i <= app1_limit; i++) { - if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time, false)) { dumped++; } } @@ -394,7 +397,7 @@ static int reader_dump_mode(void) { auth.use_credit_key = true; memcpy(auth.key, aa2_key, sizeof(auth.key)); - res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); + res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time, false); if (res) { // sanity check of CSN. @@ -408,7 +411,7 @@ static int reader_dump_mode(void) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; for (uint16_t i = app1_limit + 1; i <= app2_limit; i++) { - if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time, false)) { dumped++; } } @@ -458,6 +461,7 @@ static int dump_sim_mode(void) { .use_credit_key = false, .do_auth = true, .send_reply = false, + .shallow_mod = false, }; memcpy(auth.key, legacy_aa1_key, sizeof(auth.key)); @@ -469,7 +473,7 @@ static int dump_sim_mode(void) { // select tag. uint32_t eof_time = 0; - bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); + bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time, false); if (res == false) { switch_off(); continue; @@ -518,7 +522,7 @@ static int dump_sim_mode(void) { // main read loop for (uint16_t i = start_block; i <= app1_limit; i++) { - if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time, false)) { dumped++; } } @@ -530,7 +534,7 @@ static int dump_sim_mode(void) { auth.use_credit_key = true; memcpy(auth.key, aa2_key, sizeof(auth.key)); - res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); + res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time, false); if (res) { // sanity check of CSN. @@ -544,7 +548,7 @@ static int dump_sim_mode(void) { start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; for (uint16_t i = app1_limit + 1; i <= app2_limit; i++) { - if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time, false)) { dumped++; } } @@ -718,6 +722,16 @@ void RunMod(void) { mode = ICE_STATE_NONE; break; } + case ICE_STATE_READ_SIM: { + DbpString("-=[ enter " _CYAN_("`read & sim`") " mode, read cards, then sim after button press ]=-"); + DbpString("Entering reader dump mode"); + reader_dump_mode(); + SpinDelay(1200); // debounce button press + DbpString("Entering fullsim mode"); + fullsim_mode(); + DbpString("Exiting fullsim mode"); + LEDsoff(); + } } } diff --git a/armsrc/Standalone/hf_legicsim.c b/armsrc/Standalone/hf_legicsim.c index 7bb27fe8a..7765672b0 100644 --- a/armsrc/Standalone/hf_legicsim.c +++ b/armsrc/Standalone/hf_legicsim.c @@ -111,7 +111,7 @@ void RunMod(void) { //Indicate which card will be simulated LED(i, 0); - //Try to load dump form flash + //Try to load dump from flash sprintf(cur_dump_file, HF_LEGICSIM_DUMPFILE_SIM, i); Dbprintf(_YELLOW_("[Slot: %d] Try to load dump file: %s"), i, cur_dump_file); if (!fill_eml_from_file(cur_dump_file)) { diff --git a/armsrc/Standalone/hf_mfcsim.c b/armsrc/Standalone/hf_mfcsim.c index 34541a390..86ff62f6c 100644 --- a/armsrc/Standalone/hf_mfcsim.c +++ b/armsrc/Standalone/hf_mfcsim.c @@ -56,10 +56,11 @@ static char cur_dump_file[22] = {0}; static bool fill_eml_from_file(char *dumpfile) { // check file exist - if (!exists_in_spiffs(dumpfile)) { + if (exists_in_spiffs(dumpfile) == false) { Dbprintf(_RED_("Dump file %s not found!"), dumpfile); return false; } + //check dumpfile size uint32_t size = size_in_spiffs(dumpfile); if (size != DUMP_SIZE) { @@ -67,9 +68,12 @@ static bool fill_eml_from_file(char *dumpfile) { BigBuf_free(); return false; } + //read and load dump file - if (g_dbglevel >= DBG_INFO) + if (g_dbglevel >= DBG_INFO) { Dbprintf(_YELLOW_("Found dump file %s. Uploading to emulator memory..."), dumpfile); + } + emlClearMem(); uint8_t *emCARD = BigBuf_get_EM_addr(); rdv40_spiffs_read_as_filetype(dumpfile, emCARD, size, RDV40_SPIFFS_SAFETY_SAFE); @@ -77,7 +81,7 @@ static bool fill_eml_from_file(char *dumpfile) { } static bool write_file_from_eml(char *dumpfile) { - if (!exists_in_spiffs(dumpfile)) { + if (exists_in_spiffs(dumpfile) == false) { Dbprintf(_RED_("Dump file %s not found!"), dumpfile); return false; } @@ -99,14 +103,18 @@ void RunMod(void) { bool flag_has_dumpfile = false; for (int i = 1;; i++) { + //Exit! usbcommand break if (data_available()) break; - //Infinite loop + // infinite loop if (i > 15) { - if (!flag_has_dumpfile) - break; //still no dump file found - i = 1; //next loop + // still no dump file found + if (flag_has_dumpfile == false) { + break; + } + // next loop + i = 1; } //Indicate which card will be simulated @@ -115,7 +123,7 @@ void RunMod(void) { //Try to load dump form flash sprintf(cur_dump_file, HF_MFCSIM_DUMPFILE_SIM, i); Dbprintf(_YELLOW_("[Slot: %d] Try to load dump file: %s"), i, cur_dump_file); - if (!fill_eml_from_file(cur_dump_file)) { + if (fill_eml_from_file(cur_dump_file) == false) { Dbprintf(_YELLOW_("[Slot: %d] Dump load Failed, Next one!"), i); LEDsoff(); continue; @@ -145,8 +153,10 @@ void RunMod(void) { } Dbprintf(_YELLOW_("[Slot: %d] Write Success! Change to next one!"), i); } - if (!flag_has_dumpfile) + + if (flag_has_dumpfile == false) { Dbprintf("No dump file found!"); + } Dbprintf("Breaked! Exit standalone mode!"); SpinErr(15, 200, 3); return; diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index f253b595b..55c545489 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -386,28 +386,28 @@ void RunMod(void) { // received a HALT } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { - DbpString(_YELLOW_("+") "Received a HALT"); +// DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; // received a WAKEUP } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { - DbpString(_YELLOW_("+") "WAKEUP Received"); +// DbpString(_YELLOW_("+") "WAKEUP Received"); prevCmd = 0; p_response = &responses[RESP_INDEX_ATQA]; // received request for UID (cascade 1) } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { - DbpString(_YELLOW_("+") "Request for UID C1"); +// DbpString(_YELLOW_("+") "Request for UID C1"); p_response = &responses[RESP_INDEX_UIDC1]; // received a SELECT (cascade 1) } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { - DbpString(_YELLOW_("+") "Request for SELECT S1"); +// DbpString(_YELLOW_("+") "Request for SELECT S1"); p_response = &responses[RESP_INDEX_SAKC1]; // received a RATS request } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { - DbpString(_YELLOW_("+") "Request for RATS"); +// DbpString(_YELLOW_("+") "Request for RATS"); prevCmd = 0; p_response = &responses[RESP_INDEX_RATS]; diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 6c7f16ede..cf290fa31 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -314,24 +314,24 @@ void RunMod() { } } if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST - DbpString(_YELLOW_("+") "REQUEST Received"); +// DbpString(_YELLOW_("+") "REQUEST Received"); p_response = &responses[RESP_INDEX_ATQA]; } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT - DbpString(_YELLOW_("+") "Received a HALT"); +// DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; resp = 0; } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP - DbpString(_YELLOW_("+") "WAKEUP Received"); +// DbpString(_YELLOW_("+") "WAKEUP Received"); p_response = &responses[RESP_INDEX_ATQA]; resp = 0; } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) - DbpString(_YELLOW_("+") "Request for UID C1"); +// DbpString(_YELLOW_("+") "Request for UID C1"); p_response = &responses[RESP_INDEX_UIDC1]; } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) - DbpString(_YELLOW_("+") "Request for SELECT S1"); +// DbpString(_YELLOW_("+") "Request for SELECT S1"); p_response = &responses[RESP_INDEX_SAKC1]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request - DbpString(_YELLOW_("+") "Request for RATS"); +// DbpString(_YELLOW_("+") "Request for RATS"); p_response = &responses[RESP_INDEX_RATS]; resp = 1; } else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension diff --git a/armsrc/Standalone/hf_tmudford.c b/armsrc/Standalone/hf_tmudford.c index 158250a0c..a945a49ec 100644 --- a/armsrc/Standalone/hf_tmudford.c +++ b/armsrc/Standalone/hf_tmudford.c @@ -75,7 +75,8 @@ void RunMod(void) { } else if (state == STATE_EMUL) { Iso15693InitTag(); Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); - SimTagIso15693(card.uid); + // default block size is 4 + SimTagIso15693(card.uid, 4); state = STATE_READ; } diff --git a/armsrc/Standalone/lf_em4100rsww.c b/armsrc/Standalone/lf_em4100rsww.c index 727d188a3..dc202d49a 100644 --- a/armsrc/Standalone/lf_em4100rsww.c +++ b/armsrc/Standalone/lf_em4100rsww.c @@ -11,7 +11,7 @@ // then from shell: // hexdump lf.bin -e '5/1 "%02X" /0 "\n"' // -// To recall only LAST stored ID from flash use lf-last instead of lf file. +// To recall only LAST stored ID from flash use lf-last instead of lf file. // //----------------------------------------------------------------------------- // Modes of operation: diff --git a/armsrc/Standalone/lf_nedap_sim.c b/armsrc/Standalone/lf_nedap_sim.c new file mode 100644 index 000000000..726f13ca8 --- /dev/null +++ b/armsrc/Standalone/lf_nedap_sim.c @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// This simple mode encode, then emulate a Nedap identificator until button pressed +// lots of code from client side, cmdlfnedap, util, etc. +//----------------------------------------------------------------------------- +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "lfops.h" +#include "util.h" +#include "dbprint.h" +#include "string.h" +#include "BigBuf.h" +#include "crc16.h" + +#define MODULE_LONG_NAME "LF Nedap simple simulator" + +typedef struct _NEDAP_TAG { + uint8_t subType; + uint16_t customerCode; + uint32_t id; + + uint8_t bIsLong; +} NEDAP_TAG, *PNEDAP_TAG; + +const NEDAP_TAG Tag = {.subType = 0x5, .customerCode = 0x123, .id = 42424, .bIsLong = 1}; + +static int NedapPrepareBigBuffer(const NEDAP_TAG *pTag); +static void biphaseSimBitInverted(uint8_t c, int *n, uint8_t *phase); +static void NedapGen(uint8_t subType, uint16_t customerCode, uint32_t id, bool isLong, uint8_t *data); +static uint8_t isEven_64_63(const uint8_t *data); +static inline uint32_t bitcount32(uint32_t a); +static void bytes_to_bytebits(const void *src, const size_t srclen, void *dest); + +void ModInfo(void) { + DbpString(" " MODULE_LONG_NAME); +} + +void RunMod(void) { + int n; + + StandAloneMode(); + + Dbprintf("[=] " MODULE_LONG_NAME " -- started"); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + Dbprintf("[=] NEDAP (%s) - ID: " _GREEN_("%05u") " subtype: " _GREEN_("%1u") " customer code: " _GREEN_("%u / 0x%03X"), Tag.bIsLong ? "128b" : "64b", Tag.id, Tag.subType, Tag.customerCode, Tag.customerCode); + + n = NedapPrepareBigBuffer(&Tag); + do { + WDT_HIT(); + + if (data_available()) + break; + + SimulateTagLowFrequency(n, 0, true); + + } while (BUTTON_HELD(1000) == BUTTON_NO_CLICK); + + Dbprintf("[=] " MODULE_LONG_NAME " -- exiting"); + + LEDsoff(); +} + +static int NedapPrepareBigBuffer(const NEDAP_TAG *pTag) { + int ret = 0; + uint8_t data[16], bitStream[sizeof(data) * 8], phase = 0; + uint16_t i, size = pTag->bIsLong ? sizeof(data) : (sizeof(data) / 2); + + NedapGen(pTag->subType, pTag->customerCode, pTag->id, pTag->bIsLong, data); + bytes_to_bytebits(data, size, bitStream); + size <<= 3; + + for (i = 0; i < size; i++) { + biphaseSimBitInverted(!bitStream[i], &ret, &phase); + } + if (phase == 1) { //run a second set inverted to keep phase in check + for (i = 0; i < size; i++) { + biphaseSimBitInverted(!bitStream[i], &ret, &phase); + } + } + + return ret; +} + +static void biphaseSimBitInverted(uint8_t c, int *n, uint8_t *phase) { + uint8_t *dest = BigBuf_get_addr(); + + if (c) { + memset(dest + (*n), c ^ 1 ^ *phase, 32); + memset(dest + (*n) + 32, c ^ *phase, 32); + } else { + memset(dest + (*n), c ^ *phase, 64); + *phase ^= 1; + } + *n += 64; +} + +#define FIXED_71 0x71 +#define FIXED_40 0x40 +#define UNKNOWN_A 0x00 +#define UNKNOWN_B 0x00 +static const uint8_t translateTable[10] = {8, 2, 1, 12, 4, 5, 10, 13, 0, 9}; +static void NedapGen(uint8_t subType, uint16_t customerCode, uint32_t id, bool isLong, uint8_t *data) { // 8 or 16 + uint8_t buffer[7]; + + uint8_t r1 = (uint8_t)(id / 10000); + uint8_t r2 = (uint8_t)((id % 10000) / 1000); + uint8_t r3 = (uint8_t)((id % 1000) / 100); + uint8_t r4 = (uint8_t)((id % 100) / 10); + uint8_t r5 = (uint8_t)(id % 10); + + // first part + uint8_t idxC1 = r1; + uint8_t idxC2 = (idxC1 + 1 + r2) % 10; + uint8_t idxC3 = (idxC2 + 1 + r3) % 10; + uint8_t idxC4 = (idxC3 + 1 + r4) % 10; + uint8_t idxC5 = (idxC4 + 1 + r5) % 10; + + buffer[0] = 0xc0 | (subType & 0x0F); + buffer[1] = (customerCode & 0x0FF0) >> 4; + buffer[2] = ((customerCode & 0x000F) << 4) | translateTable[idxC1]; + buffer[3] = (translateTable[idxC2] << 4) | translateTable[idxC3]; + buffer[4] = (translateTable[idxC4] << 4) | translateTable[idxC5]; + + // checksum + init_table(CRC_XMODEM); + uint16_t checksum = crc16_xmodem(buffer, 5); + + buffer[6] = ((checksum & 0x000F) << 4) | (buffer[4] & 0x0F); + buffer[5] = (checksum & 0x00F0) | ((buffer[4] & 0xF0) >> 4); + buffer[4] = ((checksum & 0x0F00) >> 4) | (buffer[3] & 0x0F); + buffer[3] = ((checksum & 0xF000) >> 8) | ((buffer[3] & 0xF0) >> 4); + + // carry calc + uint8_t carry = 0; + for (uint8_t i = 0; i < sizeof(buffer); i++) { + uint8_t tmp = buffer[sizeof(buffer) - 1 - i]; + data[7 - i] = ((tmp & 0x7F) << 1) | carry; + carry = (tmp & 0x80) >> 7; + } + data[0] = 0xFE | carry; + data[7] |= isEven_64_63(data); + + // second part + if (isLong) { + uint8_t id0 = r1; + uint8_t id1 = (r2 << 4) | r3; + uint8_t id2 = (r4 << 4) | r5; + + data[8] = (id2 >> 1); + data[9] = ((id2 & 0x01) << 7) | (id1 >> 2); + data[10] = ((id1 & 0x03) << 6) | (id0 >> 3); + data[11] = ((id0 & 0x07) << 5) | (FIXED_71 >> 4); + data[12] = ((FIXED_71 & 0x0F) << 4) | (FIXED_40 >> 5); + data[13] = ((FIXED_40 & 0x1F) << 3) | (UNKNOWN_A >> 6); + data[14] = ((UNKNOWN_A & 0x3F) << 2) | (UNKNOWN_B >> 7); + data[15] = ((UNKNOWN_B & 0x7F) << 1); + data[15] |= isEven_64_63(data + 8); + } +} + +static uint8_t isEven_64_63(const uint8_t *data) { // 8 + uint32_t tmp[2]; + memcpy(tmp, data, 8); + return (bitcount32(tmp[0]) + (bitcount32(tmp[1] & 0xfeffffff))) & 1; +} + +static void bytes_to_bytebits(const void *src, const size_t srclen, void *dest) { + uint8_t *s = (uint8_t *)src, *d = (uint8_t *)dest; + size_t i = srclen * 8, j = srclen; + + while (j--) { + uint8_t b = s[j]; + d[--i] = (b >> 0) & 1; + d[--i] = (b >> 1) & 1; + d[--i] = (b >> 2) & 1; + d[--i] = (b >> 3) & 1; + d[--i] = (b >> 4) & 1; + d[--i] = (b >> 5) & 1; + d[--i] = (b >> 6) & 1; + d[--i] = (b >> 7) & 1; + } +} + +static inline uint32_t bitcount32(uint32_t a) { +#if defined __GNUC__ + return __builtin_popcountl(a); +#else + a = a - ((a >> 1) & 0x55555555); + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); + return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24; +#endif +} diff --git a/armsrc/Standalone/lf_prox2brute.c b/armsrc/Standalone/lf_prox2brute.c new file mode 100644 index 000000000..ecfaac9e3 --- /dev/null +++ b/armsrc/Standalone/lf_prox2brute.c @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Yann Gascuel 2023 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// LF HID ProxII Brutforce v2 by lnv42 - based on Proxbrute by Brad antoniewicz +// +// Following code is a trivial brute forcer for when you know the facility +// code and want to find valid(s) card number(s). It will try all card +// fnumbers rom CARDNUM_START to CARDNUM_END one by one (max. ~65k tries). +// This brute force will be a lot faster than Proxbrute that will try all +// possibles values for LF low, even those with bad checksum (~4g tries). +// LEDs will help you know which card number(s) worked. +// +//----------------------------------------------------------------------------- +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "lfops.h" +#include "parity.h" + +#define CARDNUM_START 0 +#define CARDNUM_END 0xFFFF +#define FACILITY_CODE 2 + +void ModInfo(void) { + DbpString(" LF HID ProxII bruteforce v2"); +} + +// samy's sniff and repeat routine for LF +void RunMod(void) { + StandAloneMode(); + Dbprintf(">> LF HID proxII bruteforce v2 a.k.a Prox2Brute Started <<"); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + const uint32_t high = 0x20; // LF high value is always 0x20 here + uint32_t low = 0; + + uint32_t fac = FACILITY_CODE, cardnum = 0; + + LED_D_ON(); + while (BUTTON_HELD(200) != BUTTON_HOLD) { // Waiting for a 200ms button press + WDT_HIT(); + // exit from SamyRun, send a usbcommand. + if (data_available()) { // early exit + DbpString("[=] You can take the shell back :) ..."); + LEDsoff(); + return; + } + } + + LED_C_ON(); + WAIT_BUTTON_RELEASED(); // We are now ready to start brutforcing card numbers + LEDsoff(); + + Dbprintf("[=] Starting HID ProxII Bruteforce from card %08x to %08x", + CARDNUM_START, MIN(CARDNUM_END, 0xFFFF)); + + for (cardnum = CARDNUM_START ; cardnum <= MIN(CARDNUM_END, 0xFFFF) ; cardnum++) { + WDT_HIT(); + + // exit from SamyRun, send a usbcommand. + if (data_available()) break; + + // short button press may be used for fast-forward + if (BUTTON_HELD(1000) == BUTTON_HOLD) break; // long button press (>=1sec) exit + + // calculate the new LF low value including Card number, Facility code and checksum + low = (cardnum << 1) | (fac << 17); + low |= oddparity32((low >> 1) & 0xFFF); + low |= evenparity32((low >> 13) & 0xFFF) << 25; + + Dbprintf("[=] trying Facility = %08x, Card = %08x, raw = %08x%08x", + fac, cardnum, high, low); + + // Start simulating an HID TAG, with high/low values, no led control and 20000 cycles timeout + CmdHIDsimTAGEx(0, high, low, 0, false, 20000); + + // switch leds to be able to know (aproximatly) which card number worked (64 tries loop) + LED_A_INV(); // switch led A every try + if ((cardnum - CARDNUM_START) % 8 == 7) // switch led B every 8 tries + LED_B_INV(); + if ((cardnum - CARDNUM_START) % 16 == 15) // switch led C every 16 tries + LED_C_INV(); + if ((cardnum - CARDNUM_START) % 32 == 31) // switch led D every 32 tries + LED_D_INV(); + } + + SpinErr((LED_A | LED_B | LED_C | LED_D), 250, 5); // Xmax tree + Dbprintf("[=] Ending HID ProxII Bruteforce from card %08x to %08x", + CARDNUM_START, cardnum - 1); + DbpString("[=] You can take the shell back :) ..."); + LEDsoff(); // This is the end +} diff --git a/armsrc/Standalone/readme.md b/armsrc/Standalone/readme.md index 8cca3ce03..fe4bdfe1e 100644 --- a/armsrc/Standalone/readme.md +++ b/armsrc/Standalone/readme.md @@ -100,6 +100,8 @@ STANDALONE_MODES_REQ_FLASH := STANDALONE_MODES_REQ_BT := ``` +Please respect alphabetic order! + ## Update MAKEFILE.INC ^[Top](#top) @@ -117,6 +119,8 @@ ifneq (,$(findstring WITH_STANDALONE_LF_FOO,$(APP_CFLAGS))) endif ``` +Please respect alphabetic order! + ## Adding identification string of your mode ^[Top](#top) @@ -174,9 +178,11 @@ Once you're ready to share your mode, please * add a line in CHANGELOG.md * add your mode in the modes table in `doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md` -* add your mode in `tools/build_all_firmwares.sh` +* add your mode in `tools/build_all_firmwares.sh` such that it reflects `armsrc/Standalone/Makefile.hal` list of firmwares to build. -and submit your PR. +Please respect alphabetic order of standalone modes everywhere! + +Then submit your PR. Once approved, add also your mode in https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode diff --git a/armsrc/appmain.c b/armsrc/appmain.c index afcdc04ac..ebc7ecb27 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -33,6 +33,7 @@ #include "legicrf.h" #include "BigBuf.h" #include "iclass_cmd.h" +#include "hfops.h" #include "iso14443a.h" #include "iso14443b.h" #include "iso15693.h" @@ -344,7 +345,7 @@ static void print_debug_level(void) { char dbglvlstr[20] = {0}; switch (g_dbglevel) { case DBG_NONE: - sprintf(dbglvlstr, "none"); + sprintf(dbglvlstr, "off"); break; case DBG_ERROR: sprintf(dbglvlstr, "error"); @@ -452,6 +453,12 @@ static void SendCapabilities(void) { capabilities.baudrate = g_usart_baudrate; #endif +#ifdef RDV4 + capabilities.is_rdv4 = true; +#else + capabilities.is_rdv4 = false; +#endif + #ifdef WITH_FLASH capabilities.compiled_with_flash = true; capabilities.hw_available_flash = FlashInit(); @@ -1220,6 +1227,10 @@ static void PacketReceived(PacketCommandNG *packet) { em4x70_write_key((em4x70_data_t *)packet->data.asBytes, true); break; } + case CMD_LF_EM4X70_BRUTE: { + em4x70_brute((em4x70_data_t *)packet->data.asBytes, true); + break; + } #endif #ifdef WITH_ZX8211 @@ -1255,12 +1266,27 @@ static void PacketReceived(PacketCommandNG *packet) { ReaderIso15693(NULL); break; } + case CMD_HF_ISO15693_EML_CLEAR: { + EmlClearIso15693(); + break; + } + case CMD_HF_ISO15693_EML_SETMEM: { + struct p { + uint32_t offset; + uint8_t count; + uint8_t data[]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + EmlSetMemIso15693(payload->count, payload->data, payload->offset); + break; + } case CMD_HF_ISO15693_SIMULATE: { struct p { uint8_t uid[8]; + uint8_t block_size; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - SimTagIso15693(payload->uid); + SimTagIso15693(payload->uid, payload->block_size); break; } case CMD_HF_ISO15693_CSETUID: { @@ -1271,20 +1297,76 @@ static void PacketReceived(PacketCommandNG *packet) { SetTag15693Uid(payload->uid); break; } - case CMD_HF_ISO15693_SLIX_L_DISABLE_PRIVACY: { + case CMD_HF_ISO15693_SLIX_DISABLE_EAS: { struct p { uint8_t pwd[4]; + bool usepwd; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - DisablePrivacySlixLIso15693(payload->pwd); + DisableEAS_AFISlixIso15693(payload->pwd, payload->usepwd); break; } - case CMD_HF_ISO15693_SLIX_L_DISABLE_AESAFI: { + case CMD_HF_ISO15693_SLIX_ENABLE_EAS: { + struct p { + uint8_t pwd[4]; + bool usepwd; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + EnableEAS_AFISlixIso15693(payload->pwd, payload->usepwd); + break; + } + case CMD_HF_ISO15693_SLIX_WRITE_PWD: { + struct p { + uint8_t old_pwd[4]; + uint8_t new_pwd[4]; + uint8_t pwd_id; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + WritePasswordSlixIso15693(payload->old_pwd, payload->new_pwd, payload->pwd_id); + break; + } + case CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY: { struct p { uint8_t pwd[4]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - DisableEAS_AFISlixLIso15693(payload->pwd); + DisablePrivacySlixIso15693(payload->pwd); + break; + } + case CMD_HF_ISO15693_SLIX_ENABLE_PRIVACY: { + struct p { + uint8_t pwd[4]; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + EnablePrivacySlixIso15693(payload->pwd); + break; + } + case CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI: { + struct p { + uint8_t pwd[4]; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + PassProtectAFISlixIso15693(payload->pwd); + break; + } + case CMD_HF_ISO15693_WRITE_AFI: { + struct p { + uint8_t pwd[4]; + bool use_pwd; + uint8_t uid[8]; + bool use_uid; + uint8_t afi; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + WriteAFIIso15693(payload->pwd, payload->use_pwd, payload->uid, payload->use_uid, payload->afi); + break; + } + case CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS: { + struct p { + uint8_t pwd[4]; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + PassProtextEASSlixIso15693(payload->pwd); break; } @@ -1384,6 +1466,26 @@ static void PacketReceived(PacketCommandNG *packet) { } #endif +#ifdef WITH_GENERAL_HF + case CMD_HF_ACQ_RAW_ADC: { + uint32_t samplesCount = 0; + memcpy(&samplesCount, packet->data.asBytes, 4); + HfReadADC(samplesCount, true); + break; + } + case CMD_HF_TEXKOM_SIMULATE: { + struct p { + uint8_t data[8]; + uint8_t modulation; + uint32_t timeout; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + HfSimulateTkm(payload->data, payload->modulation, payload->timeout); + break; + } + +#endif + #ifdef WITH_ISO14443a case CMD_HF_ISO14443A_PRINT_CONFIG: { printHf14aConfig(); @@ -1428,6 +1530,7 @@ static void PacketReceived(PacketCommandNG *packet) { iso14443a_antifuzz(payload->flag); break; } + // EPA related case CMD_HF_EPA_COLLECT_NONCE: { EPA_PACE_Collect_Nonce(packet); break; @@ -1436,6 +1539,11 @@ static void PacketReceived(PacketCommandNG *packet) { EPA_PACE_Replay(packet); break; } + case CMD_HF_EPA_PACE_SIMULATE: { + EPA_PACE_Simulate(packet); + break; + } + case CMD_HF_MIFARE_READER: { struct p { uint8_t first_run; @@ -1476,7 +1584,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_VALUE: { - MifareValue(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + MifareValue(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } case CMD_HF_MIFAREU_WRITEBL: { @@ -1578,7 +1686,7 @@ static void PacketReceived(PacketCommandNG *packet) { MifareECardLoadExt(payload->sectorcnt, payload->keytype); break; } - // Work with "magic Chinese" card + // Gen1a / 1b - "magic Chinese" card case CMD_HF_MIFARE_CSETBL: { MifareCSetBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; @@ -1605,13 +1713,53 @@ static void PacketReceived(PacketCommandNG *packet) { MifareGen3Freez(); break; } + // Gen 4 GTU magic cards case CMD_HF_MIFARE_G4_RDBL: { struct p { uint8_t blockno; uint8_t pwd[4]; + uint8_t workFlags; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareG4ReadBlk(payload->blockno, payload->pwd); + MifareG4ReadBlk(payload->blockno, payload->pwd, payload->workFlags); + break; + } + case CMD_HF_MIFARE_G4_WRBL: { + struct p { + uint8_t blockno; + uint8_t pwd[4]; + uint8_t data[16]; // data to be written + uint8_t workFlags; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareG4WriteBlk(payload->blockno, payload->pwd, payload->data, payload->workFlags); + break; + } + case CMD_HF_MIFARE_G4_GDM_CONFIG: { + struct p { + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareReadConfigBlockGDM(payload->key); + break; + } + case CMD_HF_MIFARE_G4_GDM_WRCFG: { + struct p { + uint8_t data[16]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareWriteConfigBlockGDM(payload->data); + break; + } + case CMD_HF_MIFARE_G4_GDM_WRBL: { + struct p { + uint8_t blockno; + uint8_t keytype; + uint8_t key[6]; + uint8_t data[16]; // data to be written + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareWriteBlockGDM(payload->blockno, payload->keytype, payload->key, payload->data); break; } case CMD_HF_MIFARE_PERSONALIZE_UID: { @@ -1763,11 +1911,13 @@ static void PacketReceived(PacketCommandNG *packet) { struct p { uint32_t samplesToSkip; uint32_t triggersToSkip; + uint8_t skipMode; + uint8_t skipRatio; } PACKED; struct p *payload = (struct p *)packet->data.asBytes; uint16_t len = 0; - int res = HfSniff(payload->samplesToSkip, payload->triggersToSkip, &len); + int res = HfSniff(payload->samplesToSkip, payload->triggersToSkip, &len, payload->skipMode, payload->skipRatio); struct { uint16_t len; @@ -2143,20 +2293,26 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t size = packet->oldarg[1]; uint8_t *buff = BigBuf_malloc(size); - rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); + if (buff == NULL) { + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Could not allocate buffer"); + // Trigger a finish downloading signal with an PM3_EMALLOC + reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_EMALLOC, NULL, 0); + } else { + rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); + // arg0 = filename + // arg1 = size + // arg2 = RFU - // arg0 = filename - // arg1 = size - // arg2 = RFU - - for (size_t i = 0; i < size; i += PM3_CMD_DATA_SIZE) { - size_t len = MIN((size - i), PM3_CMD_DATA_SIZE); - int result = reply_old(CMD_SPIFFS_DOWNLOADED, i, len, 0, buff + i, len); - if (result != PM3_SUCCESS) - Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result); + for (size_t i = 0; i < size; i += PM3_CMD_DATA_SIZE) { + size_t len = MIN((size - i), PM3_CMD_DATA_SIZE); + int result = reply_old(CMD_SPIFFS_DOWNLOADED, i, len, 0, buff + i, len); + if (result != PM3_SUCCESS) + Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result); + } + // Trigger a finish downloading signal with an ACK frame + reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_SUCCESS, NULL, 0); + BigBuf_free(); } - // Trigger a finish downloading signal with an ACK frame - reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } @@ -2263,6 +2419,30 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } + case CMD_SPIFFS_ELOAD: { + LED_B_ON(); + + uint8_t *em = BigBuf_get_EM_addr(); + if (em == NULL) { + reply_ng(CMD_SPIFFS_ELOAD, PM3_EMALLOC, NULL, 0); + LED_B_OFF(); + break; + } + + char *fn = (char *)packet->data.asBytes; + + uint32_t size = size_in_spiffs(fn); + if (size == 0) { + reply_ng(CMD_SPIFFS_ELOAD, PM3_SUCCESS, NULL, 0); + LED_B_OFF(); + break; + } + + rdv40_spiffs_read_as_filetype(fn, em, size, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_ELOAD, PM3_SUCCESS, NULL, 0); + LED_B_OFF(); + break; + } case CMD_FLASHMEM_SET_SPIBAUDRATE: { if (packet->length != sizeof(uint32_t)) break; @@ -2318,8 +2498,11 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } - if (page < 3) + if (page < 3) { isok = Flash_WipeMemoryPage(page); + // let spiffs check and update its info post flash erase + rdv40_spiffs_check(); + } reply_mix(CMD_ACK, isok, 0, 0, 0, 0); LED_B_OFF(); @@ -2521,7 +2704,18 @@ void __attribute__((noreturn)) AppMain(void) { #endif #ifdef WITH_SMARTCARD - I2C_init(); + I2C_init(false); +#endif + +#ifdef WITH_FLASH + if (FlashInit()) { + uint64_t flash_uniqueID = 0; + if (!Flash_CheckBusy(BUSY_TIMEOUT)) { // OK because firmware was built for devices with flash + Flash_UniqueID((uint8_t *)(&flash_uniqueID)); + } + FlashStop(); + usb_update_serial(flash_uniqueID); + } #endif #ifdef WITH_FPC_USART diff --git a/armsrc/cmd.c b/armsrc/cmd.c index 8857fc66f..c179ed2a9 100644 --- a/armsrc/cmd.c +++ b/armsrc/cmd.c @@ -131,7 +131,7 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, const uint8_t *data, return PM3_SUCCESS; } -int reply_ng(uint16_t cmd, int16_t status, uint8_t *data, size_t len) { +int reply_ng(uint16_t cmd, int16_t status, const uint8_t *data, size_t len) { return reply_ng_internal(cmd, status, data, len, true); } diff --git a/armsrc/cmd.h b/armsrc/cmd.h index 4a42d9898..b578db8e8 100644 --- a/armsrc/cmd.h +++ b/armsrc/cmd.h @@ -28,7 +28,7 @@ extern bool g_reply_via_fpc; extern bool g_reply_via_usb; int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); -int reply_ng(uint16_t cmd, int16_t status, uint8_t *data, size_t len); +int reply_ng(uint16_t cmd, int16_t status, const uint8_t *data, size_t len); int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); int receive_ng(PacketCommandNG *rx); diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index 84361e2c3..2358fa8b1 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -755,7 +755,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, memcpy(ovect, data, block_size); } - uint8_t edata[DESFIRE_MAX_CRYPTO_BLOCK_SIZE]; + uint8_t edata[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; switch (key->type) { case T_DES: diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 4dad5f236..806442f7f 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -21,6 +21,7 @@ #include "dbprint.h" #include "lfadc.h" #include "commonutil.h" +#include "optimized_cipherutils.h" #include "em4x70.h" #include "appmain.h" // tear @@ -85,7 +86,7 @@ static int em4x70_receive(uint8_t *bits, size_t length); static bool find_listen_window(bool command); static void init_tag(void) { - memset(tag.data, 0x00, ARRAYLEN(tag.data)); + memset(tag.data, 0x00, sizeof(tag.data)); } static void em4x70_setup_read(void) { @@ -298,14 +299,14 @@ static bool check_ack(void) { // returns true if signal structue corresponds to ACK, anything else is // counted as NAK (-> false) // ACK 64 + 64 - // NACK 64 + 48 + // NAK 64 + 48 if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) && check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) { // ACK return true; } - // Othewise it was a NACK or Listen Window + // Otherwise it was a NAK or Listen Window return false; } @@ -339,7 +340,7 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon uint8_t grnd[EM4X70_MAX_RECEIVE_LENGTH] = {0}; int num = em4x70_receive(grnd, 20); if (num < 20) { - Dbprintf("Auth failed"); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Auth failed"); return PM3_ESOFT; } bits2bytes(grnd, 24, response); @@ -349,6 +350,80 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon return PM3_ESOFT; } +static int set_byte(uint8_t *target, int value) { + int c = value > 0xFF; + *target = reflect8(value); + return c; +} + +static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t *frnd, uint16_t start_key, uint8_t *response) { + + uint8_t auth_resp[3] = {0}; + uint8_t rev_rnd[7]; + uint8_t temp_rnd[7]; + + reverse_arraycopy((uint8_t *)rnd, rev_rnd, sizeof(rev_rnd)); + memcpy(temp_rnd, rnd, sizeof(temp_rnd)); + + for (int k = start_key; k <= 0xFFFF; ++k) { + int c = 0; + + WDT_HIT(); + + uint16_t rev_k = reflect16(k); + switch (address) { + case 9: + c = set_byte(&temp_rnd[0], rev_rnd[0] + (rev_k & 0xFF)); + c = set_byte(&temp_rnd[1], rev_rnd[1] + c + ((rev_k >> 8) & 0xFF)); + c = set_byte(&temp_rnd[2], rev_rnd[2] + c); + c = set_byte(&temp_rnd[3], rev_rnd[3] + c); + c = set_byte(&temp_rnd[4], rev_rnd[4] + c); + c = set_byte(&temp_rnd[5], rev_rnd[5] + c); + set_byte(&temp_rnd[6], rev_rnd[6] + c); + break; + + case 8: + c = set_byte(&temp_rnd[2], rev_rnd[2] + (rev_k & 0xFF)); + c = set_byte(&temp_rnd[3], rev_rnd[3] + c + ((rev_k >> 8) & 0xFF)); + c = set_byte(&temp_rnd[4], rev_rnd[4] + c); + c = set_byte(&temp_rnd[5], rev_rnd[5] + c); + set_byte(&temp_rnd[6], rev_rnd[6] + c); + break; + + case 7: + c = set_byte(&temp_rnd[4], rev_rnd[4] + (rev_k & 0xFF)); + c = set_byte(&temp_rnd[5], rev_rnd[5] + c + ((rev_k >> 8) & 0xFF)); + set_byte(&temp_rnd[6], rev_rnd[6] + c); + break; + + default: + Dbprintf("Bad block number given: %d", address); + return PM3_ESOFT; + } + + // Report progress every 256 attempts + if ((k % 0x100) == 0) { + Dbprintf("Trying: %04X", k); + } + + // Due to performance reason, we only try it once. Therefore you need a very stable RFID communcation. + if (authenticate(temp_rnd, frnd, auth_resp) == PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) + Dbprintf("Authentication success with rnd: %02X%02X%02X%02X%02X%02X%02X", temp_rnd[0], temp_rnd[1], temp_rnd[2], temp_rnd[3], temp_rnd[4], temp_rnd[5], temp_rnd[6]); + response[0] = (k >> 8) & 0xFF; + response[1] = k & 0xFF; + return PM3_SUCCESS; + } + + if (BUTTON_PRESS() || data_available()) { + Dbprintf("EM4x70 Bruteforce Interrupted"); + return PM3_EOPABORTED; + } + } + + return PM3_ESOFT; +} + static int send_pin(const uint32_t pin) { // sends pin code for unlocking @@ -576,7 +651,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) { } if (!foundheader) { - Dbprintf("Failed to find read header"); + if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Failed to find read header"); return 0; } @@ -738,6 +813,27 @@ void em4x70_auth(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_AUTH, status, response, sizeof(response)); } +void em4x70_brute(em4x70_data_t *etd, bool ledcontrol) { + uint8_t status = 0; + uint8_t response[2] = {0}; + + command_parity = etd->parity; + + init_tag(); + em4x70_setup_read(); + + // Find the Tag + if (get_signalproperties() && find_em4x70_tag()) { + + // Bruteforce partial key + status = bruteforce(etd->address, etd->rnd, etd->frnd, etd->start_key, response) == PM3_SUCCESS; + } + + StopTicks(); + lf_finalize(ledcontrol); + reply_ng(CMD_LF_EM4X70_BRUTE, status, response, sizeof(response)); +} + void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; diff --git a/armsrc/em4x70.h b/armsrc/em4x70.h index 363f119e3..0fd640f86 100644 --- a/armsrc/em4x70.h +++ b/armsrc/em4x70.h @@ -32,6 +32,7 @@ typedef enum { void em4x70_info(em4x70_data_t *etd, bool ledcontrol); void em4x70_write(em4x70_data_t *etd, bool ledcontrol); +void em4x70_brute(em4x70_data_t *etd, bool ledcontrol); void em4x70_unlock(em4x70_data_t *etd, bool ledcontrol); void em4x70_auth(em4x70_data_t *etd, bool ledcontrol); void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol); diff --git a/armsrc/epa.c b/armsrc/epa.c index 7206e5fd2..7bd563676 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -592,3 +592,44 @@ int EPA_Setup(void) { Dbprintf("No card found"); return 1; } + +void EPA_PACE_Simulate(PacketCommandNG *c) { + + //---------Initializing--------- + + // Get password from arguments + unsigned char pwd[6]; + memcpy(pwd, c->data.asBytes, 6); + + // Set up communication with the card + int res = EPA_Setup(); + if (res != 0) { + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_PACE_SIMULATE, 1, res); + return; + } + + // Read EF.CardAccess + uint8_t card_access[210] = {0}; + int card_access_length = EPA_Read_CardAccess(card_access, 210); + + // The response has to be at least this big to hold the OID + if (card_access_length < 18) { + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_PACE_SIMULATE, 2, card_access_length); + return; + } + + // PACEInfo of the card + pace_version_info_t pace_version_info; + + // Search for the PACE OID + res = EPA_Parse_CardAccess(card_access, card_access_length, &pace_version_info); + + if (res != 0 || pace_version_info.version == 0) { + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_PACE_SIMULATE, 3, res); + return; + } + + Dbprintf("Standardized Domain Parameter: %i", pace_version_info.parameter_id); + DbpString(""); + DbpString("finished"); +} diff --git a/armsrc/epa.h b/armsrc/epa.h index d3ed7b75e..72524f836 100644 --- a/armsrc/epa.h +++ b/armsrc/epa.h @@ -32,9 +32,7 @@ typedef struct { // general functions void EPA_Finish(void); -size_t EPA_Parse_CardAccess(uint8_t *data, - size_t length, - pace_version_info_t *pace_info); +size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *pace_info); int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length); int EPA_Setup(void); @@ -44,5 +42,6 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce); void EPA_PACE_Collect_Nonce(PacketCommandNG *c); void EPA_PACE_Replay(PacketCommandNG *c); +void EPA_PACE_Simulate(PacketCommandNG *c); #endif /* __EPA_H */ diff --git a/armsrc/felica.c b/armsrc/felica.c index 3cf75a0b4..0a74a30cc 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -298,7 +298,7 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // 8-byte IDm, number of blocks, blocks numbers // number of blocks limited to 4 for FelicaLite(S) static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t *blocks) { - if (blocknum > 4 || blocknum <= 0) + if (blocknum > 4 || blocknum == 0) Dbprintf("Invalid number of blocks, %d != 4", blocknum); uint8_t c = 0, i = 0; diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index a8abf0a7a..802c77558 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -613,10 +613,12 @@ void switch_off(void) { if (g_dbglevel > 3) { Dbprintf("switch_off"); } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); if (downloaded_bitstream == FPGA_BITSTREAM_HF || downloaded_bitstream == FPGA_BITSTREAM_HF_15) { FpgaDisableSscDma(); } + set_tracing(false); LEDsoff(); } diff --git a/armsrc/frozen.c b/armsrc/frozen.c index 455d9a3f2..a77d7dd9d 100644 --- a/armsrc/frozen.c +++ b/armsrc/frozen.c @@ -235,9 +235,10 @@ static int json_get_utf8_char_len(unsigned char ch) { /* string = '"' { quoted_printable_chars } '"' */ static int json_parse_string(struct frozen *f) { - int n, ch = 0, len = 0; + int ch = 0; TRY(json_test_and_skip(f, '"')); { + int len = 0; SET_STATE(f, f->cur, "", 0); for (; f->cur < f->end; f->cur += len) { ch = *(unsigned char *) f->cur; @@ -245,6 +246,7 @@ static int json_parse_string(struct frozen *f) { EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ EXPECT(len <= json_left(f), JSON_STRING_INCOMPLETE); if (ch == '\\') { + int n; EXPECT((n = json_get_escape_len(f->cur + 1, json_left(f))) > 0, n); len += n; } else if (ch == '"') { @@ -295,17 +297,17 @@ static int json_parse_number(struct frozen *f) { #if JSON_ENABLE_ARRAY /* array = '[' [ value { ',' value } ] ']' */ static int json_parse_array(struct frozen *f) { - int i = 0, current_path_len; - char buf[20]; CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); TRY(json_test_and_skip(f, '[')); { { + int i = 0; SET_STATE(f, f->cur - 1, "", 0); while (json_cur(f) != ']') { + char buf[20]; snprintf(buf, sizeof(buf), "[%d]", i); i++; - current_path_len = json_append_to_path(f, buf, strlen(buf)); + int current_path_len = json_append_to_path(f, buf, strlen(buf)); f->cur_name = f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; f->cur_name_len = strlen(buf) - 2 /*braces*/; @@ -1427,9 +1429,12 @@ static void json_next_cb(void *userdata, const char *name, size_t name_len, static void *json_next(const char *s, int len, void *handle, const char *path, struct json_token *key, struct json_token *val, int *i) { - struct json_token tmpval, *v = val == NULL ? &tmpval : val; - struct json_token tmpkey, *k = key == NULL ? &tmpkey : key; - int tmpidx, *pidx = i == NULL ? &tmpidx : i; + struct json_token tmpval; + struct json_token *v = val == NULL ? &tmpval : val; + struct json_token tmpkey; + struct json_token *k = key == NULL ? &tmpkey : key; + int tmpidx; + int *pidx = i == NULL ? &tmpidx : i; struct next_data data = {handle, path, (int) strlen(path), 0, k, v, pidx}; json_walk(s, len, json_next_cb, &data); return data.found ? data.handle : NULL; diff --git a/armsrc/hfops.c b/armsrc/hfops.c new file mode 100644 index 000000000..6a97be76a --- /dev/null +++ b/armsrc/hfops.c @@ -0,0 +1,273 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// HF general operations +//----------------------------------------------------------------------------- + +#include "hfops.h" + +#include +#include "appmain.h" +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "commonutil.h" +#include "lfsampling.h" + +int HfReadADC(uint32_t samplesCount, bool ledcontrol) { + if (ledcontrol) LEDsoff(); + + BigBuf_Clear_ext(false); + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); + + // Setup + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + if (ledcontrol) LED_A_ON(); + + uint32_t sbs = samplesCount; + initSampleBuffer(&sbs); + + uint32_t wdtcntr = 0; + for (;;) { + if (BUTTON_PRESS()) { + break; + } + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint16_t sample = AT91C_BASE_SSC->SSC_RHR; + + // FPGA side: + // corr_i_out <= {2'b00, corr_amplitude[13:8]}; + // corr_q_out <= corr_amplitude[7:0]; + if (sample > 0x1fff) + sample = 0xff; + else + sample = sample >> 5; + logSample(sample & 0xff, 1, 8, false); + if (getSampleCounter() >= samplesCount) + break; + + if (wdtcntr++ > 512) { + WDT_HIT(); + wdtcntr = 0; + } + } else { + continue; + } + } + + FpgaDisableTracing(); + + FpgaSetupSsc(FPGA_MAJOR_MODE_OFF); + // Turn the field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + uint32_t scnt = getSampleCounter(); + reply_ng(CMD_HF_ACQ_RAW_ADC, PM3_SUCCESS, (uint8_t *)&scnt, 4); + if (ledcontrol) LEDsoff(); + + return 0; +} + +uint8_t encode_acc = 0; +uint8_t encode_acc_bit_count = 0; +uint32_t encode_indx = 0; + +static void EncodeInit(void) { + encode_acc = 0; + encode_acc_bit_count = 0; + encode_indx = 0; +} + +static void EncodeAddBit(uint8_t *data, uint8_t bit, uint8_t bit_count) { + for (int i = 0; i < bit_count; i++) { + encode_acc = (encode_acc << 1) | (bit & 0x01); + encode_acc_bit_count++; + if (encode_acc_bit_count > 7) { + data[encode_indx++] = encode_acc; + encode_acc = 0; + encode_acc_bit_count = 0; + } + } +} + +static uint32_t EncodeFinish(uint8_t *data) { + if (encode_acc_bit_count > 0) { + encode_acc = encode_acc << (8 - encode_acc_bit_count); + data[encode_indx++] = encode_acc; + } + + return encode_indx; +} + +static uint32_t HfEncodeTkm(const uint8_t *uid, uint8_t modulation, uint8_t *data) { + uint32_t len = 0; + if (modulation == 0) { + // TK-13 + // 74ns 1 field cycle, + // carrier frequency is fc/64 (212kHz), 4.7 mks + // 100 field cycle = impulse 1.6 ( 1 bit from real tag) + // 1000 field cycle = `1` 15.6 (17 bit from real tag) + // 500 field cycle = `0` 7.8 ( 7 bit from real tag) + + EncodeInit(); + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + if (((uid[i] << j) & 0x80) != 0) { + // `1` + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 17); + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 7); + } else { + // `0` + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 7); + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 17); + } + } + } + len = EncodeFinish(data); + } else { + // TK-17 + // 74ns 1 field cycle, + // carrier frequency is fc/64 (212kHz), 4.7 mks + // 0 --- 8 --- 12-15 --- 18-19 --- 26-28 --- 32 + // DO NOT NORMALIZE!!!! it must be with some error like this!!!! + // `00` -- 1-25-1-5 + // `01` -- 1-12-1-18 + // `10` -- 1-17-1-13 + // `11` -- 1-7-1-23 + + EncodeInit(); + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j += 2) { + uint8_t twobit = ((uid[i] >> j) & 0x03); + if (twobit == 0x00) { + // `00` + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 25); + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 5); + } else if (twobit == 0x01) { + // `01` + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 12); + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 18); + } else if (twobit == 0x02) { + // `10` + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 17); + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 13); + } else { // twobit == 0x03 + // `11` + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 7); + EncodeAddBit(data, 1, 1); + EncodeAddBit(data, 0, 23); + } + } + } + EncodeAddBit(data, 1, 1); + len = EncodeFinish(data); + } + + return len; +} + +int HfSimulateTkm(uint8_t *uid, uint8_t modulation, uint32_t timeout) { + // free eventually allocated BigBuf memory + BigBuf_free_keep_EM(); + + LEDsoff(); + + uint8_t *data = BigBuf_calloc(256); + uint32_t elen = HfEncodeTkm(uid, modulation, data); + if (elen == 0) { + DbpString("encode error"); + reply_ng(CMD_HF_TEXKOM_SIMULATE, PM3_EAPDU_ENCODEFAIL, NULL, 0); + return PM3_EAPDU_ENCODEFAIL; + } + + LED_C_ON(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + + bool button_pressed = false; + bool exit_loop = false; + bool field_on = false; + + uint32_t startTime = GetTickCount(); + while (exit_loop == false) { + + button_pressed = BUTTON_PRESS(); + if (button_pressed || data_available()) { + break; + } + + WDT_HIT(); + + if (timeout > 0 && startTime + timeout < GetTickCount()) + break; + + // in mV + int vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + if (vHf > MF_MINFIELDV) { + if (field_on == false) { + LED_A_ON(); + SpinDelay(50); + } + field_on = true; + } else { + if (field_on) { + LED_A_OFF(); + } + field_on = false; + continue; + } + + SpinDelay(3); + + for (int i = 0; i < elen;) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = data[i++]; + } + } + } + + switch_off(); + + if (button_pressed) + DbpString("Exit by press button"); + + reply_ng(CMD_HF_TEXKOM_SIMULATE, PM3_SUCCESS, NULL, 0); + + return PM3_SUCCESS; +} diff --git a/armsrc/hfops.h b/armsrc/hfops.h new file mode 100644 index 000000000..352f9d2bc --- /dev/null +++ b/armsrc/hfops.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// HF general operations +//----------------------------------------------------------------------------- + +#ifndef HFOPS_H +#define HFOPS_H + +#include "common.h" + +int HfReadADC(uint32_t samplesCount, bool ledcontrol); +int HfSimulateTkm(uint8_t *uid, uint8_t modulation, uint32_t timeout); + +#endif diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index ba9ef0740..59931ea66 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -36,7 +36,56 @@ static void RAMFUNC optimizedSniff(uint16_t *dest, uint16_t dsize) { } } -int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { +static void RAMFUNC skipSniff(uint8_t *dest, uint16_t dsize, uint8_t skipMode, uint8_t skipRatio) { + uint32_t accum = (skipMode == HF_SNOOP_SKIP_MIN) ? 0xffffffff : 0; + uint8_t ratioindx = 0; + while (dsize > 0) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + volatile uint16_t val = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); + switch (skipMode) { + case HF_SNOOP_SKIP_MAX: + if (accum < (val & 0xff)) + accum = val & 0xff; + if (accum < (val >> 8)) + accum = val >> 8; + break; + case HF_SNOOP_SKIP_MIN: + if (accum > (val & 0xff)) + accum = val & 0xff; + if (accum > (val >> 8)) + accum = val >> 8; + break; + case HF_SNOOP_SKIP_AVG: + accum += (val & 0xff) + (val & 0xff); + break; + default: { // HF_SNOOP_SKIP_DROP and the rest + if (ratioindx == 0) + accum = val & 0xff; + } + } + + ratioindx++; + if (ratioindx >= skipRatio) { + if (skipMode == HF_SNOOP_SKIP_AVG && skipRatio > 0) { + accum = accum / (skipRatio * 2); + if (accum <= 0xff) + *dest = accum; + else + *dest = 0xff; + } else { + *dest = accum; + } + + dest++; + dsize --; + accum = (skipMode == HF_SNOOP_SKIP_MIN) ? 0xffffffff : 0; + ratioindx = 0; + } + } + } +} + +int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint8_t skipMode, uint8_t skipRatio) { BigBuf_free(); BigBuf_Clear_ext(false); @@ -105,7 +154,10 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { } } - optimizedSniff((uint16_t *)mem, *len); + if (skipMode == 0) + optimizedSniff((uint16_t *)mem, *len); + else + skipSniff(mem, *len, skipMode, skipRatio); if (g_dbglevel >= DBG_INFO) { Dbprintf("Trigger kicked in (%d >= 180)", r); @@ -157,6 +209,6 @@ void HfPlotDownload(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // Trigger a finish downloading signal with an ACK frame - reply_mix(CMD_ACK, 1, 0, FPGA_TRACE_SIZE, 0, 0); + reply_ng(CMD_FPGAMEM_DOWNLOAD, PM3_SUCCESS, NULL, 0); LED_B_OFF(); } diff --git a/armsrc/hfsnoop.h b/armsrc/hfsnoop.h index 4b715753d..a68d28a79 100644 --- a/armsrc/hfsnoop.h +++ b/armsrc/hfsnoop.h @@ -18,6 +18,13 @@ #include "proxmark3_arm.h" -int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len); +// what to do with skipped data +#define HF_SNOOP_SKIP_NONE (0) +#define HF_SNOOP_SKIP_DROP (1) +#define HF_SNOOP_SKIP_MAX (2) +#define HF_SNOOP_SKIP_MIN (3) +#define HF_SNOOP_SKIP_AVG (4) + +int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len, uint8_t skipMode, uint8_t skipRatio); void HfPlotDownload(void); #endif diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index a4a3630c7..db219bcf2 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -1268,7 +1268,6 @@ void ReadHitagS(hitag_function htf, hitag_data *htd, bool ledcontrol) { uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; uint8_t tx[HITAG_FRAME_LEN]; - size_t txlen; int t_wait = HITAG_T_WAIT_MAX; @@ -1284,7 +1283,7 @@ void ReadHitagS(hitag_function htf, hitag_data *htd, bool ledcontrol) { WDT_HIT(); //send read request - txlen = 0; + size_t txlen = 0; uint8_t cmd = 0x0c; txlen = concatbits(tx, txlen, &cmd, 8 - 4, 4); uint8_t addr = pageNum; diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 12d7c9cd2..ca3cb46fd 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -53,7 +53,8 @@ static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { #define I2C_DELAY_2CLK I2CSpinDelayClk(2) #define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) -#define ISO7618_MAX_FRAME 255 +// The SIM module v4 supports up to 384 bytes for the length. +#define ISO7816_MAX_FRAME 270 // try i2c bus recovery at 100kHz = 5us high, 5us low void I2C_recovery(void) { @@ -90,7 +91,7 @@ void I2C_recovery(void) { DbpString("I2C bus recovery complete"); } -void I2C_init(void) { +void I2C_init(bool has_ticks) { // Configure reset pin, close up pull up, push-pull output, default high AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; @@ -105,6 +106,10 @@ void I2C_init(void) { AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + if (has_ticks) { + WaitMS(2); + } + bool isok = (SCL_read && SDA_read); if (isok == false) I2C_recovery(); @@ -132,7 +137,7 @@ void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { // Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter. void I2C_Reset_EnterMainProgram(void) { StartTicks(); - I2C_init(); + I2C_init(true); I2C_SetResetStatus(0, 0, 0); WaitMS(30); I2C_SetResetStatus(1, 0, 0); @@ -145,7 +150,7 @@ void I2C_Reset_EnterMainProgram(void) { // Reserve for firmware update. void I2C_Reset_EnterBootloader(void) { StartTicks(); - I2C_init(); + I2C_init(true); I2C_SetResetStatus(0, 1, 1); WaitMS(100); I2C_SetResetStatus(1, 1, 1); @@ -166,7 +171,7 @@ static bool WaitSCL_H_delay(uint32_t delay) { // 5000 * 3.07us = 15350us. 15.35ms // 15000 * 3.07us = 46050us. 46.05ms static bool WaitSCL_H(void) { - return WaitSCL_H_delay(15000); + return WaitSCL_H_delay(5000); } static bool WaitSCL_L_delay(uint32_t delay) { @@ -178,19 +183,21 @@ static bool WaitSCL_L_delay(uint32_t delay) { } return false; } + // 5000 * 3.07us = 15350us. 15.35ms +// 15000 * 3.07us = 46050us. 46.05ms static bool WaitSCL_L(void) { - return WaitSCL_L_delay(15000); + return WaitSCL_L_delay(5000); } // Wait max 1800ms or until SCL goes LOW. // It timeout reading response from card // Which ever comes first static bool WaitSCL_L_timeout(void) { - volatile uint32_t delay = 1700; + volatile uint32_t delay = 200; while (delay--) { // exit on SCL LOW - if (!SCL_read) + if (SCL_read == false) return true; WaitMS(1); @@ -205,12 +212,15 @@ static bool I2C_Start(void) { SDA_H; I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) return false; + if (!WaitSCL_H()) + return false; I2C_DELAY_2CLK; - if (!SCL_read) return false; - if (!SDA_read) return false; + if (!SCL_read) + return false; + if (!SDA_read) + return false; SDA_L; I2C_DELAY_2CLK; @@ -225,8 +235,9 @@ static bool I2C_WaitForSim(void) { // 8051 speaks with smart card. // 1000*50*3.07 = 153.5ms - // 1byte transfer == 1ms with max frame being 256bytes - return WaitSCL_H_delay(1000 * 300); + // 1000*110*3.07 = 337.7ms + // 1byte transfer == 1ms with max frame being 256bytes + return WaitSCL_H_delay(1000 * 110); } // send i2c STOP @@ -395,8 +406,8 @@ bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { } //Sends array of data (Array, length, command to be written , SlaveDevice address ). -// len = uint8 (max buffer to write 256bytes) -bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { +// len = uint16 because we need to write up to 256 bytes +bool I2C_BufferWrite(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { bool bBreak = true; do { if (!I2C_Start()) @@ -433,8 +444,8 @@ bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t dev } // read one array of data (Data array, Readout length, command to be written , SlaveDevice address ). -// len = uint8 (max buffer to read 256bytes) -int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { +// len = uint16 because we need to read up to 256bytes +int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { if (!data || len == 0) return 0; @@ -445,6 +456,7 @@ int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t d bool bBreak = true; uint16_t readcount = 0; + uint16_t recv_len = 0; do { if (!I2C_Start()) @@ -484,11 +496,34 @@ int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t d len--; - // The first byte in response is the message length - if (!readcount && (len > *data)) { - len = *data; + // Starting firmware v4 the length is encoded on the first two bytes. + // This only applies if command is I2C_DEVICE_CMD_READ. + if (device_cmd == I2C_DEVICE_CMD_READ) { + switch (readcount) { + case 0: + // Length (MSB) + recv_len = (*data) << 8; + break; + case 1: + // Length (LSB) + recv_len += *data; + // Adjust len if needed + if (len > recv_len) { + len = recv_len; + } + break; + default: + // Data byte received + data++; + break; + } } else { - data++; + // Length is encoded on 1 byte + if ((readcount == 0) && (len > *data)) { + len = *data; + } else { + data++; + } } readcount++; @@ -501,8 +536,8 @@ int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t d I2C_Stop(); - // return bytecount - first byte (which is length byte) - return --readcount; + // return bytecount - bytes encoding length + return readcount - (device_cmd == I2C_DEVICE_CMD_READ ? 2 : 1); } int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { @@ -612,10 +647,14 @@ bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t d void I2C_print_status(void) { DbpString(_CYAN_("Smart card module (ISO 7816)")); uint8_t maj, min; - if (I2C_get_version(&maj, &min) == PM3_SUCCESS) + if (I2C_get_version(&maj, &min) == PM3_SUCCESS) { Dbprintf(" version................. " _YELLOW_("v%x.%02d"), maj, min); - else + if (maj < 4) { + DbpString(" " _RED_("Outdated firmware.") " Please upgrade to v4.x or above."); + } + } else { DbpString(" version................. " _RED_("FAILED")); + } } int I2C_get_version(uint8_t *maj, uint8_t *min) { @@ -631,7 +670,7 @@ int I2C_get_version(uint8_t *maj, uint8_t *min) { } // Will read response from smart card module, retries 3 times to get the data. -bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { +bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { uint8_t i = 5; int16_t len = 0; @@ -656,7 +695,7 @@ bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { if (len <= 1) return false; - *destlen = (uint8_t)len & 0xFF; + *destlen = len; return true; } @@ -678,10 +717,14 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { return false; // read bytes from module - uint8_t len = sizeof(card_ptr->atr); + uint16_t len = sizeof(card_ptr->atr); if (sc_rx_bytes(card_ptr->atr, &len) == false) return false; + if (len > sizeof(card_ptr->atr)) { + len = sizeof(card_ptr->atr); + } + uint8_t pos_td = 1; if ((card_ptr->atr[1] & 0x10) == 0x10) pos_td++; if ((card_ptr->atr[1] & 0x20) == 0x20) pos_td++; @@ -697,7 +740,7 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { uint8_t chksum = 0; // xor property. will be zero when xored with chksum. - for (uint8_t i = 1; i < len; ++i) + for (uint16_t i = 1; i < len; ++i) chksum ^= card_ptr->atr[i]; if (chksum) { @@ -706,7 +749,7 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { } } - card_ptr->atr_len = len; + card_ptr->atr_len = (uint8_t)(len & 0xff); if (verbose) { LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); } @@ -732,8 +775,8 @@ void SmartCardAtr(void) { void SmartCardRaw(smart_card_raw_t *p) { LED_D_ON(); - uint8_t len = 0; - uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME); + uint16_t len = 0; + uint8_t *resp = BigBuf_malloc(ISO7816_MAX_FRAME); // check if alloacted... smartcard_command_t flags = p->flags; @@ -777,7 +820,7 @@ void SmartCardRaw(smart_card_raw_t *p) { } // read bytes from module - len = ISO7618_MAX_FRAME; + len = ISO7816_MAX_FRAME; res = sc_rx_bytes(resp, &len); if (res) { LogTrace(resp, len, 0, 0, NULL, false); diff --git a/armsrc/i2c.h b/armsrc/i2c.h index c1b6ada03..972704c1b 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -31,7 +31,7 @@ #define I2C_DEVICE_CMD_SEND_T0 0x07 void I2C_recovery(void); -void I2C_init(void); +void I2C_init(bool has_ticks); void I2C_Reset(void); void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA); @@ -41,14 +41,14 @@ void I2C_Reset_EnterBootloader(void); bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address); bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address); -bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); -int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); +bool I2C_BufferWrite(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); +int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); // for firmware int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); -bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen); +bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen); // bool GetATR(smart_card_atr_t *card_ptr, bool verbose); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 326d77298..5a68a8207 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1245,29 +1245,28 @@ send: } // THE READER CODE -static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time) { +static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod) { CodeIso15693AsReader(frame, len); tosend_t *ts = get_tosend(); - TransmitTo15693Tag(ts->buf, ts->max, start_time); + TransmitTo15693Tag(ts->buf, ts->max, start_time, shallow_mod); *end_time = *start_time + (32 * ((8 * ts->max) - 4)); // subtract the 4 padding bits after EOF LogTrace_ISO15693(frame, len, (*start_time * 4), (*end_time * 4), NULL, true); } static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t *resp, size_t max_resp_size, uint8_t expected_size, uint8_t tries, uint32_t *start_time, - uint16_t timeout, uint32_t *eof_time) { + uint16_t timeout, uint32_t *eof_time, bool shallow_mod) { uint16_t resp_len = 0; - int res; while (tries-- > 0) { - iclass_send_as_reader(cmd, cmdsize, start_time, eof_time); + iclass_send_as_reader(cmd, cmdsize, start_time, eof_time, shallow_mod); if (resp == NULL) { return true; } - res = GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time, false, true, &resp_len); + int res = GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time, false, true, &resp_len); if (res == PM3_SUCCESS && expected_size == resp_len) { return true; } @@ -1282,7 +1281,7 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t * * @return false = fail * true = Got all. */ -static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time, uint8_t *status) { +static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time, uint8_t *status, bool shallow_mod) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; @@ -1299,7 +1298,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // wakeup uint32_t start_time = GetCountSspClk(); - iclass_send_as_reader(act_all, 1, &start_time, eof_time); + iclass_send_as_reader(act_all, 1, &start_time, eof_time, shallow_mod); int res; uint16_t resp_len = 0; res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time, false, true, &resp_len); @@ -1308,7 +1307,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // send Identify start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(identify, 1, &start_time, eof_time); + iclass_send_as_reader(identify, 1, &start_time, eof_time, shallow_mod); // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); @@ -1320,7 +1319,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // select the card start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(select, sizeof(select), &start_time, eof_time); + iclass_send_as_reader(select, sizeof(select), &start_time, eof_time, shallow_mod); // expect a 10-byte response here, 8 byte CSN and 2 byte CRC res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); @@ -1332,7 +1331,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // card selected, now read config (block1) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, eof_time); + iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, eof_time, shallow_mod); // expect a 8-byte response here res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); @@ -1350,7 +1349,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // read App Issuer Area block 5 start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time, shallow_mod); // expect a 10-byte response here res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); @@ -1364,7 +1363,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, eof_time); + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, eof_time, shallow_mod); // expect a 8-byte response here res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); @@ -1386,7 +1385,7 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 read_aia[3] = 0x10; start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; - iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time, shallow_mod); // expect a 10-byte response here res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); @@ -1402,9 +1401,9 @@ static bool select_iclass_tag_ex(picopass_hdr_t *hdr, bool use_credit_key, uint3 return true; } -bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time) { +bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time, bool shallow_mod) { uint8_t result = 0; - return select_iclass_tag_ex(hdr, use_credit_key, eof_time, &result); + return select_iclass_tag_ex(hdr, use_credit_key, eof_time, &result, shallow_mod); } // Reader iClass Anticollission @@ -1413,6 +1412,7 @@ void ReaderIClass(uint8_t flags) { // flag to use credit key bool use_credit_key = ((flags & FLAG_ICLASS_READER_CREDITKEY) == FLAG_ICLASS_READER_CREDITKEY); + bool shallow_mod = (flags & FLAG_ICLASS_READER_SHALLOW_MOD); if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { Iso15693InitReader(); @@ -1427,7 +1427,7 @@ void ReaderIClass(uint8_t flags) { uint32_t eof_time = 0; picopass_hdr_t hdr = {0}; - if (select_iclass_tag_ex(&hdr, use_credit_key, &eof_time, &res) == false) { + if (select_iclass_tag_ex(&hdr, use_credit_key, &eof_time, &res, shallow_mod) == false) { reply_ng(CMD_HF_ICLASS_READER, PM3_ERFTRANS, NULL, 0); goto out; } @@ -1498,7 +1498,7 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, ui cmd_check[7] = pmac[2]; cmd_check[8] = pmac[3]; } - return iclass_send_cmd_with_retries(cmd_check, sizeof(cmd_check), resp_auth, sizeof(resp_auth), 4, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time); + return iclass_send_cmd_with_retries(cmd_check, sizeof(cmd_check), resp_auth, sizeof(resp_auth), 4, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time, payload->shallow_mod); } @@ -1516,6 +1516,8 @@ void iClass_Authentication_fast(iclass_chk_t *p) { return; } + bool shallow_mod = p->shallow_mod; + uint8_t check[9] = { ICLASS_CMD_CHECK }; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; @@ -1537,7 +1539,7 @@ void iClass_Authentication_fast(iclass_chk_t *p) { bool isOK = false; uint32_t start_time = 0, eof_time = 0; - if (select_iclass_tag(&hdr, p->use_credit_key, &eof_time) == false) + if (select_iclass_tag(&hdr, p->use_credit_key, &eof_time, shallow_mod) == false) goto out; start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -1566,14 +1568,14 @@ void iClass_Authentication_fast(iclass_chk_t *p) { check[8] = keys[i].mac[3]; // expect 4bytes, 3 retries times.. - isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 2, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 2, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); if (isOK) goto out; start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; // Auth Sequence MUST begin with reading e-purse. (block2) // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - iclass_send_as_reader(readcheck_cc, sizeof(readcheck_cc), &start_time, &eof_time); + iclass_send_as_reader(readcheck_cc, sizeof(readcheck_cc), &start_time, &eof_time, shallow_mod); LED_B_OFF(); } @@ -1586,11 +1588,11 @@ out: // Tries to read block. // retries 3times. // reply 8 bytes block -bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time) { +bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time, bool shallow_mod) { uint8_t resp[10]; uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); - bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time); + bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time, shallow_mod); if (isOK) memcpy(data, resp, 8); return isOK; @@ -1602,6 +1604,7 @@ bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, ui void iClass_ReadBlock(uint8_t *msg) { iclass_auth_req_t *payload = (iclass_auth_req_t *)msg; + bool shallow_mod = payload->shallow_mod; iclass_readblock_resp_t response = { .isOK = true }; memset(response.data, 0, sizeof(response.data)); @@ -1614,7 +1617,7 @@ void iClass_ReadBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr_t hdr = {0}; - bool res = select_iclass_tag(&hdr, payload->use_credit_key, &eof_time); + bool res = select_iclass_tag(&hdr, payload->use_credit_key, &eof_time, shallow_mod); if (res == false) { if (payload->send_reply) { response.isOK = res; @@ -1642,7 +1645,7 @@ void iClass_ReadBlock(uint8_t *msg) { // read data uint8_t resp[10]; - res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); if (res) { memcpy(response.data, resp, sizeof(response.data)); if (payload->send_reply) { @@ -1670,6 +1673,7 @@ void iClass_Dump(uint8_t *msg) { iclass_dump_req_t *cmd = (iclass_dump_req_t *)msg; iclass_auth_req_t *req = &cmd->req; + bool shallow_mod = req->shallow_mod; uint8_t *dataout = BigBuf_malloc(ICLASS_16KS_SIZE); if (dataout == NULL) { @@ -1689,7 +1693,7 @@ void iClass_Dump(uint8_t *msg) { picopass_hdr_t hdr = {0}; memset(&hdr, 0xff, sizeof(picopass_hdr_t)); - bool res = select_iclass_tag(&hdr, req->use_credit_key, &eof_time); + bool res = select_iclass_tag(&hdr, req->use_credit_key, &eof_time, shallow_mod); if (res == false) { if (req->send_reply) { reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0); @@ -1724,7 +1728,7 @@ void iClass_Dump(uint8_t *msg) { uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, i, 0x00, 0x00}; AddCrc(c + 1, 1); - res = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + res = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); if (res) { memcpy(dataout + (8 * i), resp, 8); } else { @@ -1759,7 +1763,7 @@ void iClass_Dump(uint8_t *msg) { BigBuf_free(); } -static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, bool use_mac) { +static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, bool use_mac, bool shallow_mod) { // write command: cmd, 1 blockno, 8 data, 4 mac uint8_t write[14] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; @@ -1775,7 +1779,7 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, uint8_t resp[10] = {0}; uint32_t eof_time = 0, start_time = 0; - bool isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + bool isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time, shallow_mod); if (isOK == false) { return false; } @@ -1807,6 +1811,7 @@ void iClass_WriteBlock(uint8_t *msg) { LED_A_ON(); iclass_writeblock_req_t *payload = (iclass_writeblock_req_t *)msg; + bool shallow_mod = payload->req.shallow_mod; uint8_t write[14] = { 0x80 | ICLASS_CMD_UPDATE, payload->req.blockno }; uint8_t write_len = 14; @@ -1816,7 +1821,7 @@ void iClass_WriteBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr_t hdr = {0}; - uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time); + uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod); if (res == false) { goto out; } @@ -1846,17 +1851,22 @@ void iClass_WriteBlock(uint8_t *msg) { AddCrc(write + 1, 9); write_len -= 2; } else { - // Secure tags uses MAC - uint8_t wb[9]; - wb[0] = payload->req.blockno; - memcpy(wb + 1, payload->data, 8); - if (payload->req.use_credit_key) - doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else - doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + if (payload->req.use_replay) { + memcpy(write + 10, payload->mac, sizeof(payload->mac)); + } else { + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = payload->req.blockno; + memcpy(wb + 1, payload->data, 8); - memcpy(write + 10, mac, sizeof(mac)); + if (payload->req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(write + 10, mac, sizeof(mac)); + } } start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -1866,7 +1876,7 @@ void iClass_WriteBlock(uint8_t *msg) { uint8_t tries = 3; while (tries-- > 0) { - iclass_send_as_reader(write, write_len, &start_time, &eof_time); + iclass_send_as_reader(write, write_len, &start_time, &eof_time, shallow_mod); if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred res = false; @@ -1934,6 +1944,8 @@ void iClass_Restore(iclass_restore_req_t *msg) { return; } + bool shallow_mod = msg->req.shallow_mod; + LED_A_ON(); Iso15693InitReader(); @@ -1942,7 +1954,7 @@ void iClass_Restore(iclass_restore_req_t *msg) { picopass_hdr_t hdr = {0}; // select - bool res = select_iclass_tag(&hdr, msg->req.use_credit_key, &eof_time); + bool res = select_iclass_tag(&hdr, msg->req.use_credit_key, &eof_time, shallow_mod); if (res == false) { goto out; } @@ -1983,7 +1995,7 @@ void iClass_Restore(iclass_restore_req_t *msg) { } // data + mac - if (iclass_writeblock_ext(item.blockno, item.data, mac, use_mac)) { + if (iclass_writeblock_ext(item.blockno, item.data, mac, use_mac, shallow_mod)) { Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), item.blockno, item.blockno); written++; } else { diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 6801777f9..ebbda2e9f 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -38,8 +38,8 @@ void iClass_Authentication_fast(iclass_chk_t *p); bool iclass_auth(iclass_auth_req_t *payload, uint8_t *out); void iClass_ReadBlock(uint8_t *msg); -bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time); +bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time, bool shallow_mod); -bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time); +bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time, bool shallow_mod); bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 7cda6000d..637215cc3 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -34,6 +34,7 @@ #include "commonutil.h" #include "crc16.h" #include "protocols.h" +#include "generator.h" #define MAX_ISO14A_TIMEOUT 524288 @@ -927,9 +928,13 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len) { (void)b; uint8_t flip = 0; - uint16_t checker = 0; + uint16_t checker = 4000; for (;;) { + WDT_HIT(); + + // ever 3 * 4000, check if we got any data from client + // takes long time, usually messes with simualtion if (flip == 3) { if (data_available()) return false; @@ -937,14 +942,14 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len) { flip = 0; } - if (checker >= 3000) { + // button press, takes a bit time, might mess with simualtion + if (checker-- == 0) { if (BUTTON_PRESS()) return false; flip++; - checker = 0; + checker = 4000; } - ++checker; if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -1034,6 +1039,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // PPS response static uint8_t rPPS[3] = { 0xD0 }; + static uint8_t rPACK[4] = { 0x00, 0x00, 0x00, 0x00 }; + switch (tagType) { case 1: { // MIFARE Classic 1k rATQA[0] = 0x04; @@ -1046,6 +1053,30 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // some first pages of UL/NTAG dump is special data mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); *pages = MAX(mfu_header->pages, 15); + + // counters and tearing flags + // for old dumps with all zero headers, we need to set default values. + for (uint8_t i = 0; i < 3; i++) { + + counters[i] = le24toh(mfu_header->counter_tearing[i]); + + if (mfu_header->counter_tearing[i][3] != 0x00) { + tearings[i] = mfu_header->counter_tearing[i][3]; + } + } + + // GET_VERSION + if (memcmp(mfu_header->version, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) == 0) { + memcpy(rVERSION, "\x00\x04\x04\x02\x01\x00\x11\x03", 8); + } else { + memcpy(rVERSION, mfu_header->version, 8); + } + AddCrc14A(rVERSION, sizeof(rVERSION) - 2); + + // READ_SIG + memcpy(rSIGN, mfu_header->signature, 32); + AddCrc14A(rSIGN, sizeof(rSIGN) - 2); + } break; case 3: { // MIFARE DESFire @@ -1100,6 +1131,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // READ_SIG memcpy(rSIGN, mfu_header->signature, 32); AddCrc14A(rSIGN, sizeof(rSIGN) - 2); + } break; case 8: { // MIFARE Classic 4k @@ -1221,6 +1253,19 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r AddCrc14A(rPPS, sizeof(rPPS) - 2); + if (tagType == 7) { + uint8_t pwd[4]; + uint8_t gen_pwd[4]; + uint16_t start = (*pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH; + emlGetMemBt(pwd, start, sizeof(pwd)); + Uint4byteToMemBe(gen_pwd, ul_ev1_pwdgenB(data)); + if (memcmp(pwd, gen_pwd, sizeof(pwd)) == 0) { + rPACK[0] = 0x80; + rPACK[1] = 0x80; + } + } + AddCrc14A(rPACK, sizeof(rPACK) - 2); + static tag_response_info_t responses_init[] = { { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type { .response = rUIDc1, .response_n = sizeof(rUIDc1) }, // Anticollision cascade1 - respond with uid @@ -1232,14 +1277,16 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r { .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS { .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response { .response = rSIGN, .response_n = sizeof(rSIGN) }, // EV1/NTAG READ_SIG response - { .response = rPPS, .response_n = sizeof(rPPS) } // PPS response + { .response = rPPS, .response_n = sizeof(rPPS) }, // PPS response + { .response = rPACK, .response_n = sizeof(rPACK) } // PACK response }; - // "precompile" responses. There are 11 predefined responses with a total of 80 bytes data to transmit. + // "precompile" responses. There are 12 predefined responses with a total of 84 bytes data to transmit. + // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) - // 81 * 8 data bits, 81 * 1 parity bits, 11 start bits, 11 stop bits, 11 correction bits - // 81 * 8 + 81 + 11 + 11 + 11 == 762 -#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 762 + // 85 * 8 data bits, 85 * 1 parity bits, 12 start bits, 12 stop bits, 12 correction bits + // 85 * 8 + 85 + 12 + 12 + 12 == 801 +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 801 uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size @@ -1635,14 +1682,23 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); p_response = NULL; } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 authentication + + /* // PWD stored in dump now uint8_t pwd[4]; emlGetMemBt(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); + if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) { + Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(data)); + Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]); + } + if (memcmp(receivedCmd + 1, pwd, 4) == 0) { + uint8_t pack[4]; emlGetMemBt(pack, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); if (memcmp(pack, "\x00\x00\x00\x00", 4) == 0) { - memcpy(pack, "\x80\x80\x00\x00", 4); + pack[0] = 0x80; + pack[1] = 0x80; } AddCrc14A(pack, sizeof(pack) - 2); EmSendCmd(pack, sizeof(pack)); @@ -1651,6 +1707,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ if (g_dbglevel >= DBG_DEBUG) Dbprintf("Auth attempt: %08x", bytes_to_num(receivedCmd + 1, 4)); } p_response = NULL; + */ + p_response = &responses[RESP_INDEX_PACK]; } else if (receivedCmd[0] == MIFARE_ULEV1_VCSL && len == 23 && tagType == 7) { uint8_t cmd[3]; emlGetMemBt(cmd, (pages - 2) * 4 + 1 + MFU_DUMP_PREFIX_LENGTH, 1); @@ -2441,9 +2499,11 @@ static void iso14a_set_ATS_times(const uint8_t *ats) { static int GetATQA(uint8_t *resp, uint8_t *resp_par, bool use_ecp, bool use_magsafe) { -#define ECP_DELAY 15 +#define ECP_DELAY 10 +#define ECP_RETRY_TIMEOUT 100 #define WUPA_RETRY_TIMEOUT 10 // 10ms + // 0x26 - REQA // 0x52 - WAKE-UP // 0x7A - MAGESAFE WAKE UP @@ -2454,15 +2514,25 @@ static int GetATQA(uint8_t *resp, uint8_t *resp_par, bool use_ecp, bool use_mags wupa[0] = MAGSAFE_CMD_WUPA_4; } + if (use_ecp) { + // In case a device was already selected, we send a S-BLOCK deselect to bring it into an idle state so it can be selected again + uint8_t deselect_cmd[] = {0xc2, 0xe0, 0xb4}; + ReaderTransmit(deselect_cmd, sizeof(deselect_cmd), NULL); + // Read response if present + (void) ReaderReceive(resp, resp_par); + } + uint32_t save_iso14a_timeout = iso14a_get_timeout(); iso14a_set_timeout(1236 / 128 + 1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + bool first_try = true; + uint32_t retry_timeout = use_ecp ? ECP_RETRY_TIMEOUT : WUPA_RETRY_TIMEOUT; uint32_t start_time = GetTickCount(); int len; // we may need several tries if we did send an unknown command or a wrong authentication before... do { - if (use_ecp) { + if (use_ecp && !first_try) { uint8_t ecp[] = { 0x6a, 0x02, 0xC8, 0x01, 0x00, 0x03, 0x00, 0x02, 0x79, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xD8}; ReaderTransmit(ecp, sizeof(ecp), NULL); SpinDelay(ECP_DELAY); @@ -2476,12 +2546,13 @@ static int GetATQA(uint8_t *resp, uint8_t *resp_par, bool use_ecp, bool use_mags } } - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field ReaderTransmitBitsPar(wupa, 7, NULL, NULL); // Receive the ATQA len = ReaderReceive(resp, resp_par); - } while (len == 0 && GetTickCountDelta(start_time) <= WUPA_RETRY_TIMEOUT); + + first_try = false; + } while (len == 0 && GetTickCountDelta(start_time) <= retry_timeout); iso14a_set_timeout(save_iso14a_timeout); return len; @@ -2530,7 +2601,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint uint8_t fudan_read[] = { 0x30, 0x01, 0x8B, 0xB9}; ReaderTransmit(fudan_read, sizeof(fudan_read), NULL); if (!ReaderReceive(resp, resp_par)) { - Dbprintf("Card didn't answer to select all"); + if (g_dbglevel >= DBG_INFO) Dbprintf("Card didn't answer to select all"); return 0; } @@ -2579,7 +2650,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // SELECT_ALL ReaderTransmit(sel_all, sizeof(sel_all), NULL); if (!ReaderReceive(resp, resp_par)) { - Dbprintf("Card didn't answer to CL%i select all", cascade_level + 1); + if (g_dbglevel >= DBG_INFO) Dbprintf("Card didn't answer to CL%i select all", cascade_level + 1); return 0; } @@ -2657,7 +2728,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // Receive the SAK if (!ReaderReceive(resp, resp_par)) { - Dbprintf("Card didn't answer to select"); + if (g_dbglevel >= DBG_INFO) Dbprintf("Card didn't answer to select"); return 0; } sak = resp[0]; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index cf3ecf854..104d6b680 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -104,7 +104,8 @@ typedef enum { RESP_INDEX_RATS, RESP_INDEX_VERSION, RESP_INDEX_SIGNATURE, - RESP_INDEX_PPS + RESP_INDEX_PPS, + RESP_INDEX_PACK, } resp_index_t; #ifndef AddCrc14A diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 655ed367e..e4f1e4e93 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1381,11 +1381,11 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t } if (Demod.len > 0) { - uint32_t sof_time = *eof_time - - (Demod.len * (8 + 2)) // time for byte transfers - - (10) // time for TR1 - - (10 + 2) // time for SOF transfer - - (10); // time for EOF transfer + uint32_t sof_time = *eof_time - ETU_TO_SSP( + (Demod.len * (8 + 2)) // time for byte transfers +// + (10) // time for TR1 + + (10 + 2) // time for SOF transfer + + (10)); // time for EOF transfer LogTrace(Demod.output, Demod.len, sof_time, *eof_time, NULL, false); } return Demod.len; @@ -1435,9 +1435,28 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { } WDT_HIT(); } + + // transmit remaining bits. we need one-sample granularity now + + volatile uint8_t data = ts->buf[ts->max], last_bits = ts->bit; + + for (uint8_t i = 0; i < last_bits; i++) { + volatile uint16_t send_word = (data & 0x80) ? 0x0000 : 0xFFFF; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + + data <<= 1; + } + WDT_HIT(); + + LED_B_OFF(); - *start_time += DELAY_ARM_TO_TAG; +// *start_time += DELAY_ARM_TO_TAG; // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) {}; @@ -1447,7 +1466,7 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { // Code a layer 2 command (string of octets, including CRC) into ToSend[], // so that it is ready to transmit to the tag using TransmitFor14443b(). //----------------------------------------------------------------------------- -static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { +static void CodeIso14443bAsReader(const uint8_t *cmd, int len, bool framing) { /* * QUESTION: how long is a 1 or 0 in pulses in the xcorr_848 mode? * 1 "stuffbit" = 1ETU (9us) @@ -1460,14 +1479,18 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { int i; tosend_reset(); - // Send SOF - // 10-11 ETUs of ZERO - for (i = 0; i < 10; i++) { - tosend_stuffbit(0); + // add framing enable flag. xerox chips use unframed commands during anticollision + + if (framing) { + // Send SOF + // 10-11 ETUs of ZERO + for (i = 0; i < 10; i++) { + tosend_stuffbit(0); + } + // 2-3 ETUs of ONE + tosend_stuffbit(1); + tosend_stuffbit(1); } - // 2-3 ETUs of ONE - tosend_stuffbit(1); - tosend_stuffbit(1); // Sending cmd, LSB // from here we add BITS @@ -1493,28 +1516,36 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { // FOR PICC it ranges 0-19us == 0 - 2 ETU } - // Send EOF - // 10-11 ETUs of ZERO - for (i = 0; i < 10; i++) { - tosend_stuffbit(0); + if (framing) { + // Send EOF + // 10-11 ETUs of ZERO + for (i = 0; i < 10; i++) { + tosend_stuffbit(0); + } } - - int pad = (10 + 2 + (len * 10) + 10) & 0x7; - for (i = 0; i < 16 - pad; ++i) - tosend_stuffbit(1); - + // we can't use padding now + /* + int pad = (10 + 2 + (len * 10) + 10) & 0x7; + for (i = 0; i < 16 - pad; ++i) + tosend_stuffbit(1); + */ } /* * Convenience function to encode, transmit and trace iso 14443b comms */ -static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time) { +static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { tosend_t *ts = get_tosend(); - CodeIso14443bAsReader(cmd, len); + CodeIso14443bAsReader(cmd, len, framing); TransmitFor14443b_AsReader(start_time); if (g_trigger) LED_A_ON(); - *eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10; + +// eof_time in ssp clocks, but bits was added here! +// *eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10; + + *eof_time = *start_time + ETU_TO_SSP(8 * ts->max); + LogTrace(cmd, len, *start_time, *eof_time, NULL, true); } @@ -1545,7 +1576,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void // send uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time); + CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int len = Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time); @@ -1578,7 +1609,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void AddCrc14B(data_bytes, len - 2); // transmit S-Block - CodeAndTransmit14443bAsReader(data_bytes, len, &start_time, &eof_time); + CodeAndTransmit14443bAsReader(data_bytes, len, &start_time, &eof_time, true); // retrieve the result again (with increased timeout) eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; @@ -1637,7 +1668,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(cmdINIT, sizeof(cmdINIT), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmdINIT, sizeof(cmdINIT), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); @@ -1657,7 +1688,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { } start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(cmdMSBUID, sizeof(cmdMSBUID), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmdMSBUID, sizeof(cmdMSBUID), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); @@ -1675,7 +1706,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { } start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(cmdLSBUID, sizeof(cmdLSBUID), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmdLSBUID, sizeof(cmdLSBUID), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); @@ -1706,7 +1737,7 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r_init, sizeof(r_init), iso14b_timeout, &eof_time); @@ -1728,7 +1759,7 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 2); start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r_select, sizeof(r_select), iso14b_timeout, &eof_time); @@ -1752,7 +1783,7 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 1); start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(select_srx, 3, &start_time, &eof_time); // Only first three bytes for this one + CodeAndTransmit14443bAsReader(select_srx, 3, &start_time, &eof_time, true); // Only first three bytes for this one eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r_papid, sizeof(r_papid), iso14b_timeout, &eof_time); @@ -1772,6 +1803,178 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { return 0; } + +// Xerox tag connect function: wup, anticoll, attrib, password +// the original chips require all commands in this sequence + +// 0: OK, 1: select fail, 2: attrib fail, 3: crc fail, 4: password fail +int iso14443b_select_xrx_card(iso14b_card_select_t *card) { +// AFI + static const uint8_t x_wup1[] = { 0x0D, 0x37, 0x21, 0x92, 0xf2 }; + static const uint8_t x_wup2[] = { 0x5D, 0x37, 0x21, 0x71, 0x71 }; + uint8_t slot_mark[1]; + + uint8_t x_atqb[24] = {0x0}; // ATQB len = 18 + + uint32_t start_time = 0; + uint32_t eof_time = 0; + + iso14b_set_timeout(24); // wait for carrier + + // wup1 + CodeAndTransmit14443bAsReader(x_wup1, sizeof(x_wup1), &start_time, &eof_time, true); + + start_time = eof_time + US_TO_SSP(9000); // 9ms before next cmd + + // wup2 + CodeAndTransmit14443bAsReader(x_wup2, sizeof(x_wup2), &start_time, &eof_time, true); + + uint64_t uid = 0; + int retlen; + + for (int uid_pos = 0; uid_pos < 64; uid_pos += 2) { + int slot; + + for (slot = 0; slot < 4; slot++) { + start_time = eof_time + ETU_TO_SSP(30); //(24); // next slot after 24 ETU + + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + + if (retlen > 0) { + FpgaDisableTracing(); + + Dbprintf("unexpected data %d", retlen); + Dbprintf("crc %s", check_crc(CRC_14443_B, x_atqb, retlen) ? "OK" : "BAD"); + return 1; + } + + // tx unframed slot-marker + + if (Demod.posCount) { // no rx, but subcarrier burst detected + uid |= (uint64_t)slot << uid_pos; + + slot_mark[0] = 0xB1 + (slot << 1); // ack slot + CodeAndTransmit14443bAsReader(slot_mark, sizeof(slot_mark), &start_time, &eof_time, false); + break; + } else { // no subcarrier burst + slot_mark[0] = 0xA1 + (slot << 1); // nak slot + CodeAndTransmit14443bAsReader(slot_mark, sizeof(slot_mark), &start_time, &eof_time, false); + } + } + + if (4 == slot) { + FpgaDisableTracing(); + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("no answer to anticollision"); + } + return 1; + } + } + + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("anticollision uid %llx", uid); + } + + // ATQB too short? + if (retlen < 18) { + return 1; + } + + // VALIDATE CRC + if (check_crc(CRC_14443_B, x_atqb, 18) == false) { // use fixed len because unstable EOF catch + return 3; + } + + if (x_atqb[0] != 0x50) { +// DbpString("aqtb bad"); + return 1; + } + + if (card) { + card->uidlen = 8; + memcpy(card->uid, x_atqb + 1, 8); + memcpy(card->atqb, x_atqb + 9, 7); + } + +// DbpString("aqtb ok"); + + // send ATTRIB command + + uint8_t txbuf[18]; + + txbuf[1] = 0x1d; + memcpy(txbuf + 2, &uid, 8); + txbuf[10] = 0; + txbuf[11] = 0xF; + txbuf[12] = 1; + txbuf[13] = 0xF; + + AddCrc14B(txbuf + 1, 13); + + start_time = eof_time + ISO14B_TR2; + CodeAndTransmit14443bAsReader(txbuf + 1, 15, &start_time, &eof_time, true); + + eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + FpgaDisableTracing(); + + if (retlen < 3) { +// DbpString("attrib failed"); + return 2; + } + + if (check_crc(CRC_14443_B, x_atqb, 3) == false) { + return 3; + } + + if (x_atqb[0] != 0) { +// DbpString("attrib failed"); + return 2; + } + +// DbpString("attrib ok"); + + // apply PASSWORD command + + txbuf[0] = 2; + txbuf[1] = 0x38; + // uid from previous command used + txbuf[10] = 3; + txbuf[11] = 0x4e; + txbuf[12] = 0x4b; + txbuf[13] = 0x53; + txbuf[14] = 0x4F; + + AddCrc14B(txbuf, 15); + + start_time = eof_time + ISO14B_TR2; + CodeAndTransmit14443bAsReader(txbuf, 17, &start_time, &eof_time, true); + + eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + + if (retlen < 4) { +// DbpString("passwd failed"); + return 4; + } + + if (check_crc(CRC_14443_B, x_atqb, 4) == false) { + return 3; + } + + if (x_atqb[0] != 2 || x_atqb[1] != 0) { +// DbpString("passwd failed"); + return 4; + } + +// DbpString("passwd ok"); + + return 0; +} + /* Perform the ISO 14443 B Card Selection procedure * Currently does NOT do any collision handling. * It expects 0-1 cards in the device's range. @@ -1794,7 +1997,7 @@ int iso14443b_select_card(iso14b_card_select_t *card) { // first, wake up the tag uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(wupb, sizeof(wupb), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(wupb, sizeof(wupb), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r_pupid, sizeof(r_pupid), iso14b_timeout, &eof_time); @@ -1823,7 +2026,7 @@ int iso14443b_select_card(iso14b_card_select_t *card) { attrib[7] = r_pupid[10] & 0x0F; AddCrc14B(attrib, 9); start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r_attrib, sizeof(r_attrib), iso14b_timeout, &eof_time); @@ -1916,7 +2119,7 @@ static int read_srx_block(uint8_t blocknr, uint8_t *block) { uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r_block, sizeof(r_block), iso14b_timeout, &eof_time); @@ -2223,6 +2426,13 @@ void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p) { if (status > 0) goto out; } + if ((p->flags & ISO14B_SELECT_XRX) == ISO14B_SELECT_XRX) { + status = iso14443b_select_xrx_card(&card); + reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, (uint8_t *)&card, sendlen); + // 0: OK, 1: select fail, 2: attrib fail, 3: crc fail, 4: password fail + if (status != 0) goto out; + } + if ((p->flags & ISO14B_APDU) == ISO14B_APDU) { uint8_t res; status = iso14443b_apdu(p->raw, p->rawlen, (p->flags & ISO14B_SEND_CHAINING), buf, sizeof(buf), &res); @@ -2239,7 +2449,7 @@ void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p) { } uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(p->raw, p->rawlen, &start_time, &eof_time); + CodeAndTransmit14443bAsReader(p->raw, p->rawlen, &start_time, &eof_time, true); if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred FpgaDisableTracing(); diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 3dcbceaae..2579e6f1c 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -39,6 +39,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void int iso14443b_select_card(iso14b_card_select_t *card); int iso14443b_select_card_srx(iso14b_card_select_t *card); +int iso14443b_select_xrx_card(iso14b_card_select_t *card); void SimulateIso14443bTag(uint8_t *pupi); void AcquireRawAdcSamplesIso14443b(uint32_t parameter); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index ac81b494c..daa84f886 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -116,7 +116,6 @@ #define CMD_READ_RESP 13 #define CMD_INV_RESP 12 #define CMD_SYSINFO_RESP 17 -#define CMD_READBLOCK_RESP 7 //#define Crc(data, len) Crc(CRC_15693, (data), (len)) #define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) @@ -178,6 +177,36 @@ static void CodeIso15693AsReaderEOF(void) { } +static int get_uid_slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid) { + + uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + + uint8_t cmd[5] = {0}; + BuildIdentifyRequest(cmd); + uint16_t recvlen = 0; + SendDataTag(cmd, sizeof(cmd), false, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, eof_time, &recvlen); + + if (recvlen != 12) { + return PM3_ETIMEOUT; + } + + uid[0] = answer[2]; + uid[1] = answer[3]; + uid[2] = answer[4]; + uid[3] = answer[5]; + uid[4] = answer[6]; + uid[5] = answer[7]; + uid[6] = answer[8]; + uid[7] = answer[9]; + + BigBuf_free(); + return PM3_SUCCESS; +} + + // encode data using "1 out of 256" scheme // data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances @@ -263,9 +292,9 @@ void CodeIso15693AsTag(const uint8_t *cmd, size_t len) { } // Transmit the command (to the tag) that was placed in cmd[]. -void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { +void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time, bool shallow_mod) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | (shallow_mod ? FPGA_HF_READER_MODE_SEND_SHALLOW_MOD : FPGA_HF_READER_MODE_SEND_FULL_MOD)); if (*start_time < DELAY_ARM_TO_TAG) { *start_time = DELAY_ARM_TO_TAG; @@ -1586,7 +1615,7 @@ void AcquireRawAdcSamplesIso15693(void) { tosend_t *ts = get_tosend(); uint32_t start_time = 0; - TransmitTo15693Tag(ts->buf, ts->max, &start_time); + TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; @@ -1673,7 +1702,7 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla dma_start_time = GetCountSspClk() & 0xfffffff0; } - volatile uint16_t sniffdata; + volatile uint16_t sniffdata = 0; volatile uint16_t sniffdata_prev = sniffdata; sniffdata = *upTo++; @@ -1900,7 +1929,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t } tosend_t *ts = get_tosend(); - TransmitTo15693Tag(ts->buf, ts->max, &start_time); + TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred *resp_len = 0; @@ -1923,7 +1952,7 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui CodeIso15693AsReaderEOF(); tosend_t *ts = get_tosend(); - TransmitTo15693Tag(ts->buf, ts->max, &start_time); + TransmitTo15693Tag(ts->buf, ts->max, &start_time, false); uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // subtract the 4 padding bits after EOF LogTrace_ISO15693(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); @@ -2098,9 +2127,28 @@ void Iso15693InitTag(void) { StartCountSspClk(); } + +void EmlClearIso15693(void) { + // Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent + // an inconvenient reset in the future by Iso15693InitTag + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15); + BigBuf_Clear_EM(); + reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0); +} + +void EmlSetMemIso15693(uint8_t count, uint8_t *data, uint32_t offset) { + uint8_t *emCARD = BigBuf_get_EM_addr(); + memcpy(emCARD + offset, data, count); +} + +void EmlGetMemIso15693(uint8_t count, uint8_t *output, uint32_t offset) { + uint8_t *emCARD = BigBuf_get_EM_addr(); + memcpy(output, emCARD + offset, count); +} + // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg -void SimTagIso15693(uint8_t *uid) { +void SimTagIso15693(uint8_t *uid, uint8_t block_size) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); @@ -2109,12 +2157,10 @@ void SimTagIso15693(uint8_t *uid) { LED_A_ON(); - Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]); + Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X block size %d", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7], block_size); LED_C_ON(); - - enum { NO_FIELD, IDLE, ACTIVATED, SELECTED, HALTED } chip_state = NO_FIELD; bool button_pressed = false; @@ -2188,7 +2234,7 @@ void SimTagIso15693(uint8_t *uid) { bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - // Build GET_SYSTEM_INFO command + // Build GET_SYSTEM_INFO response uint8_t resp_sysinfo[CMD_SYSINFO_RESP] = {0}; resp_sysinfo[0] = 0; // Response flags. @@ -2207,8 +2253,8 @@ void SimTagIso15693(uint8_t *uid) { resp_sysinfo[10] = 0; // DSFID resp_sysinfo[11] = 0; // AFI - resp_sysinfo[12] = 0x1B; // Memory size. - resp_sysinfo[13] = 0x03; // Memory size. + resp_sysinfo[12] = 0x1F; // Block count + resp_sysinfo[13] = block_size - 1; // Block size. resp_sysinfo[14] = 0x01; // IC reference. // CRC @@ -2221,28 +2267,97 @@ void SimTagIso15693(uint8_t *uid) { LogTrace_ISO15693(resp_sysinfo, CMD_SYSINFO_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); } - // READ_BLOCK - if ((cmd[1] == ISO15693_READBLOCK)) { + // READ_BLOCK and READ_MULTI_BLOCK + if ((cmd[1] == ISO15693_READBLOCK) || (cmd[1] == ISO15693_READ_MULTI_BLOCK)) { bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); + bool addressed = cmd[0] & ISO15_REQ_ADDRESS; + bool option = cmd[0] & ISO15_REQ_OPTION; uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - // Build GET_SYSTEM_INFO command - uint8_t resp_readblock[CMD_READBLOCK_RESP] = {0}; + uint8_t address_offset = 0; + if (addressed) { + address_offset = 8; + } - resp_readblock[0] = 0; // Response flags. - resp_readblock[1] = 0; // Block data. - resp_readblock[2] = 0; // Block data. - resp_readblock[3] = 0; // Block data. - resp_readblock[4] = 0; // Block data. + uint8_t block_idx = cmd[2 + address_offset]; + uint8_t block_count = 1; + if (cmd[1] == ISO15693_READ_MULTI_BLOCK) { + block_count = cmd[3 + address_offset] + 1; + } + + // Build READ_(MULTI_)BLOCK response + int response_length = 3 + block_size * block_count; + int security_offset = 0; + if (option) { + response_length += block_count; + security_offset = 1; + } + uint8_t resp_readblock[response_length]; + memset(resp_readblock, 0, response_length); + + resp_readblock[0] = 0; // Response flags + for (int j = 0; j < block_count; j++) { + // where to put the data of the current block + int work_offset = 1 + j * (block_size + security_offset); + if (option) { + resp_readblock[work_offset] = 0; // Security status + } + // Block data + if (block_size * (block_idx + j + 1) <= CARD_MEMORY_SIZE) { + EmlGetMemIso15693(block_size, resp_readblock + (work_offset + security_offset), + block_size * (block_idx + j)); + } else { + memset(resp_readblock + work_offset + security_offset, 0, block_size); + } + } // CRC - AddCrc15(resp_readblock, 5); - CodeIso15693AsTag(resp_readblock, CMD_READBLOCK_RESP); + AddCrc15(resp_readblock, response_length - 2); + CodeIso15693AsTag(resp_readblock, response_length); tosend_t *ts = get_tosend(); TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); - LogTrace_ISO15693(resp_readblock, CMD_READBLOCK_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); + LogTrace_ISO15693(resp_readblock, response_length, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); + } + + // WRITE_BLOCK and WRITE_MULTI_BLOCK + if ((cmd[1] == ISO15693_WRITEBLOCK) || (cmd[1] == ISO15693_WRITE_MULTI_BLOCK)) { + bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); + bool addressed = cmd[0] & ISO15_REQ_ADDRESS; + uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; + + uint8_t address_offset = 0; + if (addressed) { + address_offset = 8; + } + + uint8_t block_idx = cmd[2 + address_offset]; + uint8_t block_count = 1; + uint8_t multi_offset = 0; + if (cmd[1] == ISO15693_WRITE_MULTI_BLOCK) { + block_count = cmd[3 + address_offset] + 1; + multi_offset = 1; + } + uint8_t *data = cmd + 3 + address_offset + multi_offset; + + // write data + EmlSetMemIso15693(block_count * block_size, data, block_idx * block_size); + + // Build WRITE_(MULTI_)BLOCK response + int response_length = 3; + uint8_t resp_writeblock[response_length]; + memset(resp_writeblock, 0, response_length); + resp_writeblock[0] = 0; // Response flags + + // CRC + AddCrc15(resp_writeblock, response_length - 2); + CodeIso15693AsTag(resp_writeblock, response_length); + + tosend_t *ts = get_tosend(); + + TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); + LogTrace_ISO15693(resp_writeblock, response_length, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); } } @@ -2346,6 +2461,8 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint case ISO15693_WRITE_AFI: case ISO15693_LOCK_AFI: case ISO15693_WRITE_DSFID: + case ISO15693_WRITE_PASSWORD: + case ISO15693_PASSWORD_PROTECT_EAS: case ISO15693_LOCK_DSFID: timeout = ISO15693_READER_TIMEOUT_WRITE; request_answer = data[0] & ISO15_REQ_OPTION; @@ -2555,7 +2672,7 @@ void SetTag15693Uid(const uint8_t *uid) { switch_off(); } -static void init_password_15693_slixl(uint8_t *buffer, uint8_t *pwd, const uint8_t *rnd) { +static void init_password_15693_Slix(uint8_t *buffer, uint8_t *pwd, const uint8_t *rnd) { memcpy(buffer, pwd, 4); if (rnd) { buffer[0] ^= rnd[0]; @@ -2565,14 +2682,14 @@ static void init_password_15693_slixl(uint8_t *buffer, uint8_t *pwd, const uint8 } } -static bool get_rnd_15693_slixl(uint32_t start_time, uint32_t *eof_time, uint8_t *rnd) { +static bool get_rnd_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *rnd) { // 0x04, == NXP from manufacture id list. uint8_t c[] = {ISO15_REQ_DATARATE_HIGH, ISO15693_GET_RANDOM_NUMBER, 0x04, 0x00, 0x00 }; AddCrc15(c, 3); uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; uint16_t recvlen = 0; - int res = SendDataTag(c, sizeof(c), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + int res = SendDataTag(c, sizeof(c), true, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); if (res != PM3_SUCCESS && recvlen != 5) { return false; } @@ -2583,15 +2700,16 @@ static bool get_rnd_15693_slixl(uint32_t start_time, uint32_t *eof_time, uint8_t return true; } -static uint32_t set_pass_15693_slixl(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, uint8_t *password) { +static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, uint8_t *password) { + uint8_t rnd[2]; - if (get_rnd_15693_slixl(start_time, eof_time, rnd) == false) { + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; } // 0x04, == NXP from manufacture id list. - uint8_t c[] = {ISO15_REQ_DATARATE_HIGH, ISO15693_SET_PASSWORD, 0x04, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - init_password_15693_slixl(&c[4], password, rnd); + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_SET_PASSWORD, 0x04, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + init_password_15693_Slix(&c[4], password, rnd); AddCrc15(c, 8); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -2604,16 +2722,226 @@ static uint32_t set_pass_15693_slixl(uint32_t start_time, uint32_t *eof_time, ui return PM3_SUCCESS; } -/* -static uint32_t enable_privacy_15693_slixl(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, uint8_t *password) { +static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, uint8_t *password, uint8_t *uid) { + + uint8_t rnd[2]; - if (get_rnd_15693_slixl(start_time, eof_time, rnd) == false) { + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + // 0x04, == NXP from manufacture id list. + uint8_t c[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_SET_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + init_password_15693_Slix(&c[12], password, rnd); + + memcpy(&c[3], uid, 8); + AddCrc15(c, 16); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + + int res = SendDataTag(c, sizeof(c), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + return PM3_SUCCESS; +} + +static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *password) { + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + // 0x04, == NXP from manufacture id list. + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xBA, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + init_password_15693_Slix(&c[3], password, rnd); + AddCrc15(c, 7); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + int res = SendDataTag(c, sizeof(c), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + return PM3_SUCCESS; +} + +static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *password, bool usepwd) { + + uint8_t uid[8]; + get_uid_slix(start_time, eof_time, uid); + + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + if (usepwd) { + + int res_setpass = set_pass_15693_Slix(start_time, eof_time, 0x10, password, uid); + + if (res_setpass != PM3_SUCCESS) { + return PM3_EWRONGANSWER; + } + } + + // 0x04, == NXP from manufacture id list. + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA3, 0x04, 0x00, 0x00}; + AddCrc15(c, 3); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + int res = SendDataTag(c, sizeof(c), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + return PM3_SUCCESS; +} + + +static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *password, bool usepwd) { + + uint8_t uid[8]; + get_uid_slix(start_time, eof_time, uid); + + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + if (usepwd) { + int res_setpass = set_pass_15693_Slix(start_time, eof_time, 0x10, password, uid); + + if (res_setpass != PM3_SUCCESS) { + return PM3_EWRONGANSWER; + } + } + // 0x04, == NXP from manufacture id list. + uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA2, 0x04, 0x00, 0x00}; + //init_password_15693_Slix(&c[3], password, rnd); + AddCrc15(c, 3); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + int res = SendDataTag(c, sizeof(c), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + return PM3_SUCCESS; +} + +static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pwd_id, uint8_t *password, uint8_t *uid) { + + uint8_t new_pwd_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pwd_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + memcpy(&new_pwd_cmd[3], uid, 8); + memcpy(&new_pwd_cmd[12], password, 4); + + AddCrc15(new_pwd_cmd, 16); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + + int res_wrp = SendDataTag(new_pwd_cmd, sizeof(new_pwd_cmd), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res_wrp != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + + return PM3_SUCCESS; +} + +static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eof_time, bool set_option_flag, uint8_t *password) { + + uint8_t flags; + + if (set_option_flag) + flags = ISO15_REQ_DATARATE_HIGH | ISO15_REQ_OPTION; + else + flags = ISO15_REQ_DATARATE_HIGH; + + + uint8_t uid[8]; + get_uid_slix(start_time, eof_time, uid); + + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { + return PM3_ETIMEOUT; + } + + int res_setpass = set_pass_15693_Slix(start_time, eof_time, 0x10, password, uid); + + if (res_setpass != PM3_SUCCESS) { + return PM3_EWRONGANSWER; + } + + uint8_t new_pass_protect_cmd[] = { flags, ISO15693_PASSWORD_PROTECT_EAS, 0x04, 0x00, 0x00}; + AddCrc15(new_pass_protect_cmd, 3); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + + int res = SendDataTag(new_pass_protect_cmd, sizeof(new_pass_protect_cmd), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res != PM3_SUCCESS && recvlen != 3) { + return PM3_EWRONGANSWER; + } + + return PM3_SUCCESS; +} + +static uint32_t write_afi_15693(uint32_t start_time, uint32_t *eof_time, uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi) { + + if (!use_uid) { + int res_getuid = get_uid_slix(start_time, eof_time, uid); + + if (res_getuid != PM3_SUCCESS) { + return res_getuid; + } + } + + if (usepwd) { + int res_setpass = set_pass_15693_Slix(start_time, eof_time, 0x10, password, uid); + + if (res_setpass != PM3_SUCCESS) { + return PM3_EWRONGANSWER; + } + } + + uint8_t cmd[] = { ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS, ISO15693_WRITE_AFI, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + memcpy(&cmd[2], uid, 8); + cmd[10] = afi; + AddCrc15(cmd, 11); + + start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint16_t recvlen = 0; + + int res = SendDataTag(cmd, sizeof(cmd), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen); + if (res != PM3_SUCCESS || recvlen != 3) { + return PM3_EWRONGANSWER; + } + + return PM3_SUCCESS; +} + +/* +static uint32_t enable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, uint8_t *password) { + uint8_t rnd[2]; + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; } uint8_t c[] = {ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS, ISO15693_ENABLE_PRIVACY, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; memcpy(&c[3], uid, 8); - init_password_15693_slixl(&c[11], password, rnd); + init_password_15693_Slix(&c[11], password, rnd); AddCrc15(c, 15); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -2626,16 +2954,16 @@ static uint32_t enable_privacy_15693_slixl(uint32_t start_time, uint32_t *eof_ti return PM3_SUCCESS; } -static uint32_t write_password_15693_slixl(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, uint8_t *password) { +static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, uint8_t *password) { uint8_t rnd[2]; - if (get_rnd_15693_slixl(start_time, eof_time, rnd) == false) { + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; } uint8_t c[] = {ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS, ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; memcpy(&c[3], uid, 8); c[11] = pass_id; - init_password_15693_slixl(&c[12], password, NULL); + init_password_15693_Slix(&c[12], password, NULL); AddCrc15(c, 16); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -2649,16 +2977,16 @@ static uint32_t write_password_15693_slixl(uint32_t start_time, uint32_t *eof_ti return PM3_SUCCESS; } -static uint32_t destroy_15693_slixl(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t *password) { +static uint32_t destroy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t *password) { uint8_t rnd[2]; - if (get_rnd_15693_slixl(start_time, eof_time, rnd) == false) { + if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; } uint8_t c[] = {ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS, ISO15693_DESTROY, ISO15693_ENABLE_PRIVACY, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; memcpy(&c[3], uid, 8); - init_password_15693_slixl(&c[11], password, rnd); + init_password_15693_Slix(&c[11], password, rnd); AddCrc15(c, 15); start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -2673,8 +3001,32 @@ static uint32_t destroy_15693_slixl(uint32_t start_time, uint32_t *eof_time, uin */ -// Sets a PRIVACY password to all ZEROS -void DisablePrivacySlixLIso15693(uint8_t *password) { +void WritePasswordSlixIso15693(uint8_t *old_password, uint8_t *new_password, uint8_t pwd_id) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + int res = PM3_EFAILED; + + uint8_t uid[8]; + get_uid_slix(start_time, &eof_time, uid); + + res = set_pass_15693_Slix(start_time, &eof_time, pwd_id, old_password, uid); + if (res != PM3_SUCCESS) { + reply_ng(CMD_HF_ISO15693_SLIX_WRITE_PWD, res, NULL, 0); + switch_off(); + return; + } + + res = write_password_15693_Slix(start_time, &eof_time, pwd_id, new_password, uid); + + reply_ng(CMD_HF_ISO15693_SLIX_WRITE_PWD, res, NULL, 0); + + switch_off(); + +} + +void DisablePrivacySlixIso15693(uint8_t *password) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -2684,13 +3036,12 @@ void DisablePrivacySlixLIso15693(uint8_t *password) { // 0x04 Privacy // 0x08 Destroy SLIX-L // 0x10 EAS/AFI - int res = set_pass_15693_slixl(start_time, &eof_time, 0x04, password); - reply_ng(CMD_HF_ISO15693_SLIX_L_DISABLE_PRIVACY, res, NULL, 0); + int res = disable_privacy_15693_Slix(start_time, &eof_time, 0x04, password); + reply_ng(CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY, res, NULL, 0); switch_off(); } -// Sets a EAS/AFI password to all ZEROS -void DisableEAS_AFISlixLIso15693(uint8_t *password) { +void EnablePrivacySlixIso15693(uint8_t *password) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -2700,8 +3051,71 @@ void DisableEAS_AFISlixLIso15693(uint8_t *password) { // 0x04 Privacy // 0x08 Destroy SLIX-L // 0x10 EAS/AFI - int res = set_pass_15693_slixl(start_time, &eof_time, 0x10, password); - reply_ng(CMD_HF_ISO15693_SLIX_L_DISABLE_AESAFI, res, NULL, 0); + int res = set_privacy_15693_Slix(start_time, &eof_time, password); + reply_ng(CMD_HF_ISO15693_SLIX_ENABLE_PRIVACY, res, NULL, 0); switch_off(); } + +void DisableEAS_AFISlixIso15693(uint8_t *password, bool usepwd) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + + // Password identifier Password byte + // 0x04 Privacy + // 0x08 Destroy SLIX-L + // 0x10 EAS/AFI + int res = disable_eas_15693_Slix(start_time, &eof_time, password, usepwd); + + + + reply_ng(CMD_HF_ISO15693_SLIX_DISABLE_EAS, res, NULL, 0); + switch_off(); +} + +void EnableEAS_AFISlixIso15693(uint8_t *password, bool usepwd) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + + // Password identifier Password byte + // 0x04 Privacy + // 0x08 Destroy SLIX-L + // 0x10 EAS/AFI + int res = enable_eas_15693_Slix(start_time, &eof_time, password, usepwd); + reply_ng(CMD_HF_ISO15693_SLIX_ENABLE_EAS, res, NULL, 0); + switch_off(); +} + +void PassProtextEASSlixIso15693(uint8_t *password) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + int res = pass_protect_EASAFI_15693_Slix(start_time, &eof_time, false, password); + reply_ng(CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS, res, NULL, 0); + switch_off(); +} +void PassProtectAFISlixIso15693(uint8_t *password) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + int res = pass_protect_EASAFI_15693_Slix(start_time, &eof_time, true, password); + reply_ng(CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI, res, NULL, 0); + switch_off(); +} + +void WriteAFIIso15693(uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi) { + LED_D_ON(); + Iso15693InitReader(); + StartCountSspClk(); + uint32_t start_time = 0, eof_time = 0; + int res = write_afi_15693(start_time, &eof_time, password, use_pwd, uid, use_uid, afi); + //int res = PM3_SUCCESS; + reply_ng(CMD_HF_ISO15693_WRITE_AFI, res, NULL, 0); + switch_off(); +} diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 277074189..b087edd2e 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -40,13 +40,16 @@ void CodeIso15693AsTag(const uint8_t *cmd, size_t len); void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); -void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time); +void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time, bool shallow_mod); int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time, bool fsk, bool recv_speed, uint16_t *resp_len); //void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader -void SimTagIso15693(uint8_t *uid); // simulate an ISO15693 tag +void EmlClearIso15693(void); +void EmlSetMemIso15693(uint8_t count, uint8_t *data, uint32_t offset); +void EmlGetMemIso15693(uint8_t count, uint8_t *output, uint32_t offset); +void SimTagIso15693(uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data); // send arbitrary commands from CLI @@ -59,6 +62,12 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui void SetTag15693Uid(const uint8_t *uid); -void DisablePrivacySlixLIso15693(uint8_t *password); -void DisableEAS_AFISlixLIso15693(uint8_t *password); +void WritePasswordSlixIso15693(uint8_t *old_password, uint8_t *new_password, uint8_t pwd_id); +void DisablePrivacySlixIso15693(uint8_t *password); +void EnablePrivacySlixIso15693(uint8_t *password); +void DisableEAS_AFISlixIso15693(uint8_t *password, bool usepwd); +void EnableEAS_AFISlixIso15693(uint8_t *password, bool usepwd); +void PassProtextEASSlixIso15693(uint8_t *password); +void PassProtectAFISlixIso15693(uint8_t *password); +void WriteAFIIso15693(uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi); #endif diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 637d22fee..c6af0dace 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -79,6 +79,7 @@ static uint16_t rx_frame_from_fpga(void) { return AT91C_BASE_SSC->SSC_RHR; } } + return 0; } //----------------------------------------------------------------------------- diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index a1ff37356..a56e013ee 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -296,7 +296,9 @@ void LFSetupFPGAForADC(int divisor, bool reader_field) { uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, int16_t trigger_threshold, bool verbose, uint32_t sample_size, uint32_t cancel_after, int32_t samples_to_skip, bool ledcontrol) { - initSampleBuffer(&sample_size); + initSampleBuffer(&sample_size); // sample size in bytes + sample_size <<= 3; // sample size in bits + sample_size /= bits_per_sample; // sample count if (g_dbglevel >= DBG_DEBUG) { printSamples(); @@ -368,8 +370,11 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in } // Ensure that DC offset removal and noise check is performed for any device-side processing - removeSignalOffset(data.buffer, samples.total_saved); - computeSignalProperties(data.buffer, samples.total_saved); + if (bits_per_sample == 8) { + // these functions only consider bps==8 + removeSignalOffset(data.buffer, samples.total_saved); + computeSignalProperties(data.buffer, samples.total_saved); + } return data.numbits; } /** diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 9ee2a5fc9..90dbf2bfc 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -214,6 +214,65 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { LEDsoff(); } +void MifareReadConfigBlockGDM(uint8_t *key) { + + int retval = PM3_SUCCESS; + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // variables + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint64_t ui64key = bytes_to_num(key, 6); + uint8_t outbuf[16] = {0x00}; + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_authex_2(pcs, cuid, 0, 0, ui64key, AUTH_FIRST, NULL, NULL, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (mifare_classic_readblock_ex(pcs, cuid, 0, outbuf, MIFARE_MAGIC_GDM_READ_CFG)) { + retval = PM3_ESOFT; + goto OUT; + }; + + if (mifare_classic_halt(pcs, cuid)) { + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_G4_GDM_CONFIG, retval, outbuf, sizeof(outbuf)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); +} + //----------------------------------------------------------------------------- // Select, Authenticate, Read a MIFARE tag. // read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) @@ -394,7 +453,6 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { memcpy(blockdata, datain + 10, 16); // variables - uint8_t isOK = 0; uint8_t uid[10] = {0x00}; uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; @@ -410,57 +468,202 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { LED_B_OFF(); LED_C_OFF(); - while (true) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - break; - }; + uint8_t retval = 0; - if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - break; - }; + if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); + goto OUT; + }; - if (mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); - break; - }; + if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + goto OUT; + }; - if (mifare_classic_halt(pcs, cuid)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; + int res = mifare_classic_writeblock(pcs, cuid, blockNo, blockdata); + if (res == PM3_ETEAROFF) { + retval = PM3_ETEAROFF; + goto OUT; + } else if (res) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + retval = PM3_ESOFT; + goto OUT; } + if (mifare_classic_halt(pcs, cuid)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); + goto OUT; + }; + + retval = 1; + +OUT: crypto1_deinit(pcs); - if (g_dbglevel >= 2) DbpString("WRITE BLOCK FINISHED"); - - reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); - + reply_mix(CMD_ACK, retval, 0, 0, 0, 0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); } -void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) { +void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain) { + + int retval = PM3_SUCCESS; + + // check args + if (datain == NULL) { + retval = PM3_EINVARG; + goto OUT; + } + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // variables + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint64_t ui64key = bytes_to_num(key, 6); + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_authex_2(pcs, cuid, blockno, keytype, ui64key, AUTH_FIRST, NULL, NULL, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + int res = mifare_classic_writeblock_ex(pcs, cuid, blockno, datain, true); + if (res == PM3_ETEAROFF) { + retval = PM3_ETEAROFF; + goto OUT; + } else if (res) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_halt(pcs, cuid)) { + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); +} + +void MifareWriteConfigBlockGDM(uint8_t *datain) { + + int retval = PM3_SUCCESS; + + // check args + if (datain == NULL) { + retval = PM3_EINVARG; + goto OUT; + } + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // variables + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + uint64_t key = 0; + if (mifare_classic_authex_2(pcs, cuid, 0, 0, key, AUTH_FIRST, NULL, NULL, true)) { + retval = PM3_ESOFT; + goto OUT; + }; + + int res = mifare_classic_write_cfg_block_gdm(pcs, cuid, datain); + if (res == PM3_ETEAROFF) { + retval = PM3_ETEAROFF; + goto OUT; + } else if (res) { + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_halt(pcs, cuid)) { + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_G4_GDM_WRCFG, retval, NULL, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); +} + + +void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { // params uint8_t blockNo = arg0; uint8_t keyType = arg1; + uint8_t transferKeyType = arg2; uint64_t ui64Key = 0; + uint64_t transferUi64Key = 0; uint8_t blockdata[16] = {0x00}; ui64Key = bytes_to_num(datain, 6); - memcpy(blockdata, datain + 10, 16); + memcpy(blockdata, datain + 11, 16); + transferUi64Key = bytes_to_num(datain + 27, 6); // variables uint8_t action = datain[9]; + uint8_t transferBlk = datain[10]; + bool needAuth = datain[33]; uint8_t isOK = 0; uint8_t uid[10] = {0x00}; uint32_t cuid = 0; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t len = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; @@ -490,6 +693,21 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) { break; }; + if (needAuth) { + // transfer to other sector + if (mifare_classic_auth(pcs, cuid, transferBlk, transferKeyType, transferUi64Key, AUTH_NESTED)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error"); + break; + } + } + + // send transfer (commit the change) + len = mifare_sendcmd_short(pcs, 1, MIFARE_CMD_TRANSFER, (transferBlk != 0) ? transferBlk : blockNo, receivedAnswer, NULL, NULL); + if (len != 1 && receivedAnswer[0] != 0x0A) { // 0x0a - ACK + if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error in transfer: %02x", receivedAnswer[0]); + break; + } + if (mifare_classic_halt(pcs, cuid)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); break; @@ -775,7 +993,7 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (ALL)"); continue; } @@ -794,7 +1012,7 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } @@ -852,7 +1070,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint64_t ui64Key = bytes_to_num(datain, 6); uint32_t cuid = 0; - int16_t isOK = 0; + int16_t isOK = PM3_SUCCESS; uint16_t num_nonces = 0; uint8_t nt_par_enc = 0; uint8_t cascade_levels = 0; @@ -878,18 +1096,21 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, LED_C_ON(); + uint8_t prev_enc_nt[] = {0, 0, 0, 0}; + uint8_t prev_counter = 0; + for (uint16_t i = 0; i <= PM3_CMD_DATA_SIZE - 9;) { // Test if the action was cancelled if (BUTTON_PRESS()) { - isOK = 2; + isOK = PM3_EOPABORTED; field_off = true; break; } - if (!have_uid) { // need a full select cycle to get the uid first + if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Can't select card (ALL)"); continue; } @@ -908,7 +1129,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Can't select card (UID)"); continue; } @@ -917,7 +1138,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, if (slow) SpinDelayUs(HARDNESTED_PRE_AUTHENTICATION_LEADTIME); - uint32_t nt1; + uint32_t nt1 = 0; if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Auth1 error"); continue; @@ -939,11 +1160,32 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, memcpy(buf + i, receivedAnswer, 4); nt_par_enc = par_enc[0] & 0xf0; } else { - nt_par_enc |= par_enc[0] >> 4; + nt_par_enc |= par_enc[0] >> 4; memcpy(buf + i + 4, receivedAnswer, 4); memcpy(buf + i + 8, &nt_par_enc, 1); i += 9; } + + + if (prev_enc_nt[0] == receivedAnswer[0] && + prev_enc_nt[1] == receivedAnswer[1] && + prev_enc_nt[2] == receivedAnswer[2] && + prev_enc_nt[3] == receivedAnswer[3] + ) { + prev_counter++; + } + memcpy(prev_enc_nt, receivedAnswer, 4); + if (prev_counter == 5) { + if (g_dbglevel >= DBG_EXTENDED) { + DbpString("Static encrypted nonce detected, exiting..."); + uint32_t a = bytes_to_num(prev_enc_nt, 4); + uint32_t b = bytes_to_num(receivedAnswer, 4); + Dbprintf("( %08x vs %08x )", a, b); + } + isOK = PM3_ESTATIC_NONCE; + break; + } + } LED_C_OFF(); @@ -952,7 +1194,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); LED_B_OFF(); - if (g_dbglevel >= 3) DbpString("AcquireEncryptedNonces finished"); + if (g_dbglevel >= DBG_ERROR) DbpString("AcquireEncryptedNonces finished"); if (field_off) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1009,6 +1251,9 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 LED_B_ON(); WDT_HIT(); + uint32_t prev_enc_nt = 0; + uint8_t prev_counter = 0; + uint16_t unsuccessful_tries = 0; uint16_t davg = 0; dmax = 0; @@ -1030,7 +1275,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 continue; } - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Can't select card"); rtr--; continue; @@ -1051,11 +1296,13 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 }; // cards with fixed nonce + /* if (nt1 == nt2) { Dbprintf("Nested: %08x vs %08x", nt1, nt2); break; } - + */ + uint32_t nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 for (i = 101; i < 1200; i++) { nttmp = prng_successor(nttmp, 1); @@ -1077,6 +1324,21 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 isOK = PM3_EFAILED; } } + + + if (nt1 == nt2) { + prev_counter++; + } + prev_enc_nt = nt2; + + if (prev_counter == 5) { + if (g_dbglevel >= DBG_EXTENDED) { + DbpString("Static encrypted nonce detected, exiting..."); + Dbprintf("( %08x vs %08x )", prev_enc_nt, nt2); + } + isOK = PM3_ESTATIC_NONCE; + break; + } } if (rtr > 1) @@ -1094,10 +1356,13 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 LED_C_ON(); // get crypted nonces for target sector - for (i = 0; i < 2 && !isOK; i++) { // look for exactly two different nonces + for (i = 0; ((i < 2) && (isOK == PM3_SUCCESS)); i++) { + + // look for exactly two different nonces target_nt[i] = 0; - while (target_nt[i] == 0) { // continue until we have an unambiguous nonce + // continue until we have an unambiguous nonce + while (target_nt[i] == 0) { // Test if the action was cancelled if (BUTTON_PRESS() || data_available()) { @@ -1111,7 +1376,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 continue; } - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Can't select card"); continue; }; @@ -1190,6 +1455,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 memcpy(payload.nt_b, &target_nt[1], 4); memcpy(payload.ks_b, &target_ks[1], 4); + LED_B_ON(); reply_ng(CMD_HF_MIFARE_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); @@ -1225,8 +1491,8 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, LED_C_ON(); // Main loop - get crypted nonces for target sector - for (uint8_t rtr = 0; rtr < 2; rtr++) { - + for (uint8_t rtr = 0; rtr < 2; rtr++) { + if (mifare_classic_halt(pcs, cuid)) { continue; } @@ -1235,13 +1501,13 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, continue; }; - // first colleciton + // first collection if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { continue; }; // pre-generate nonces - if (keyType == 1 && nt1 == 0x009080A2) { + if (targetKeyType == 1 && nt1 == 0x009080A2) { target_nt[0] = prng_successor(nt1, 161); target_nt[1] = prng_successor(nt1, 321); } else { @@ -1253,11 +1519,11 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, if (len != 4) { continue; }; - + nt2 = bytes_to_num(receivedAnswer, 4); target_ks[0] = nt2 ^ target_nt[0]; - - // second colleciton + + // second collection if (mifare_classic_halt(pcs, cuid)) { continue; @@ -1812,13 +2078,14 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { uint64_t key = 0; uint32_t cuid = 0; - int i, res; uint8_t cascade_levels = 0; struct { uint8_t key[6]; bool found; } PACKED keyresult; keyresult.found = false; + memset(keyresult.key, 0x00, sizeof(keyresult.key)); + bool have_uid = false; uint8_t keyType = datain[0]; @@ -1849,12 +2116,12 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { set_tracing(false); - for (i = 0; i < key_count; i++) { + for (uint16_t i = 0; i < key_count; i++) { // Iceman: use piwi's faster nonce collecting part in hardnested. - if (!have_uid) { // need a full select cycle to get the uid first + if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("ChkKeys: Can't select card (ALL)"); --i; // try same key once again continue; @@ -1874,7 +2141,7 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { + if (iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("ChkKeys: Can't select card (UID)"); --i; // try same key once again continue; @@ -1882,12 +2149,10 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { } key = bytes_to_num(datain + i * 6, 6); - res = mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST); - + if (mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST)) { // CHK_TIMEOUT(); - - if (res) continue; + } memcpy(keyresult.key, datain + i * 6, 6); keyresult.found = true; @@ -1895,14 +2160,12 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { } LED_B_ON(); + crypto1_deinit(pcs); reply_ng(CMD_HF_MIFARE_CHKKEYS, PM3_SUCCESS, (uint8_t *)&keyresult, sizeof(keyresult)); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - set_tracing(false); - crypto1_deinit(pcs); - g_dbglevel = oldbg; } @@ -2334,9 +2597,12 @@ void MifareCIdent(bool is_mfc) { uint8_t isGen = 0; uint8_t rec[1] = {0x00}; uint8_t recpar[1] = {0x00}; - uint8_t rats[4] = { ISO14443A_CMD_RATS, 0x80, 0x31, 0x73 }; - uint8_t rdblf0[4] = { ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; - uint8_t rdbl00[4] = { ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; + uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73}; + uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; + uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; + uint8_t gen4gmd[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92}; + uint8_t gen4GetConf[8] = {GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0}; + uint8_t superGen1[9] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D}; uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); uint8_t *uid = BigBuf_malloc(10); @@ -2368,6 +2634,24 @@ void MifareCIdent(bool is_mfc) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); int res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res == 2) { + // Check for Magic Gen4 GTU with default password: + // Get config should return 30 or 32 bytes + AddCrc14A(gen4GetConf, sizeof(gen4GetConf) - 2); + ReaderTransmit(gen4GetConf, sizeof(gen4GetConf), NULL); + res = ReaderReceive(buf, par); + if (res == 32 || res == 34) { + isGen = MAGIC_GEN_4GTU; + goto OUT; + } + } + + // reset card + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(40); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); if (res == 2) { if (cuid == 0xAA55C396) { isGen = MAGIC_GEN_UNFUSED; @@ -2377,19 +2661,29 @@ void MifareCIdent(bool is_mfc) { ReaderTransmit(rats, sizeof(rats), NULL); res = ReaderReceive(buf, par); if (res) { + // test for super card + ReaderTransmit(superGen1, sizeof(superGen1), NULL); + res = ReaderReceive(buf, par); + if (res == 22) { + isGen = MAGIC_SUPER_GEN1; - // test for some MFC gen2 - if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) { + // check for super card gen2 + // not available after RATS, reset card before executing + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(40); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - // super card ident - uint8_t super[] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D}; - ReaderTransmit(super, sizeof(super), NULL); + iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); res = ReaderReceive(buf, par); - if (res == 22) { - isGen = MAGIC_SUPER; - goto OUT; + if (res == 18) { + isGen = MAGIC_SUPER_GEN2; } + goto OUT; + } + // test for some MFC gen2 + if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) { isGen = MAGIC_GEN_2; goto OUT; } @@ -2439,7 +2733,7 @@ void MifareCIdent(bool is_mfc) { } } } else { - // magic MFC Gen3 test + // magic MFC Gen3 test 1 FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(40); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -2451,6 +2745,21 @@ void MifareCIdent(bool is_mfc) { isGen = MAGIC_GEN_3; } } + + // magic MFC Gen4 GDM test + if (isGen != MAGIC_GEN_3) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(40); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res == 2) { + ReaderTransmit(gen4gmd, sizeof(gen4gmd), NULL); + res = ReaderReceive(buf, par); + if (res == 4) { + isGen = MAGIC_GEN_4GDM; + } + } + } } }; @@ -2465,6 +2774,8 @@ OUT: void MifareHasStaticNonce(void) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // variables int retval = PM3_SUCCESS; uint32_t nt = 0; @@ -2682,42 +2993,152 @@ OUT: BigBuf_free(); } -void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd) { - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); +void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags) { + bool setup = ((workFlags & MAGIC_INIT) == MAGIC_INIT) ; + bool done = ((workFlags & MAGIC_OFF) == MAGIC_OFF) ; + int res = 0; int retval = PM3_SUCCESS; - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); - uint8_t *uid = BigBuf_malloc(10); - if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { - retval = PM3_ESOFT; + if (buf == NULL) { + retval = PM3_EMALLOC; goto OUT; } - LED_B_ON(); - uint32_t save_iso14a_timeout = iso14a_get_timeout(); - iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 1000); // 2 seconds timeout + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } - uint8_t cmd[] = { 0xCF, 0x00, 0x00, 0x00, 0x00, 0xCE, blockno, 0x00, 0x00}; + if (setup) { + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + } + + LED_B_ON(); + + static uint32_t save_iso14a_timeout; + if (setup) { + save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 1000); // 2 seconds timeout + } + + uint8_t cmd[] = { GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_READ, blockno, + 0x00, 0x00 + }; memcpy(cmd + 1, pwd, 4); AddCrc14A(cmd, sizeof(cmd) - 2); ReaderTransmit(cmd, sizeof(cmd), NULL); - int res = ReaderReceive(buf, par); + res = ReaderReceive(buf, par); + if (res != 18) { retval = PM3_ESOFT; } - iso14a_set_timeout(save_iso14a_timeout); + + if (done || retval != 0) iso14a_set_timeout(save_iso14a_timeout); LED_B_OFF(); OUT: - reply_ng(CMD_HF_MIFARE_G4_RDBL, retval, buf, 18); + reply_ng(CMD_HF_MIFARE_G4_RDBL, retval, buf, res); // turns off - OnSuccessMagic(); + if (done || retval != 0) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + if (done || retval != 0) set_tracing(false); + BigBuf_free(); +} + +void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t workFlags) { + bool setup = ((workFlags & MAGIC_INIT) == MAGIC_INIT) ; + bool done = ((workFlags & MAGIC_OFF) == MAGIC_OFF) ; + + int res = 0; + int retval = PM3_SUCCESS; + + uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + if (buf == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + // check args + if (data == NULL) { + retval = PM3_EINVARG; + goto OUT; + } + + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + if (par == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + + if (setup) { + uint8_t *uid = BigBuf_malloc(10); + if (uid == NULL) { + retval = PM3_EMALLOC; + goto OUT; + } + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + } + + LED_B_ON(); + + static uint32_t save_iso14a_timeout; + if (setup) { + save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 1000); // 2 seconds timeout + } + + uint8_t cmd[] = { GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_WRITE, blockno, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + + memcpy(cmd + 1, pwd, 4); + memcpy(cmd + 7, data, 16); + + AddCrc14A(cmd, sizeof(cmd) - 2); + + ReaderTransmit(cmd, sizeof(cmd), NULL); + res = ReaderReceive(buf, par); + + if ((res != 4) || (memcmp(buf, "\x90\x00\xfd\x07", 4) != 0)) { + retval = PM3_ESOFT; + } + + if (done || retval != 0) iso14a_set_timeout(save_iso14a_timeout); + LED_B_OFF(); + +OUT: + reply_ng(CMD_HF_MIFARE_G4_WRBL, retval, buf, res); + // turns off + if (done || retval != 0) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + if (done || retval != 0) set_tracing(false); BigBuf_free(); } diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 66bd2bacb..a6623c006 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -26,7 +26,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes); void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); @@ -46,18 +46,26 @@ void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype); int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); +// MFC GEN1a /1b void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); // Work with "magic Chinese" card void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); void MifareCIdent(bool is_mfc); // is "magic chinese" card? void MifareHasStaticNonce(void); // Has the tag a static nonce? +// MFC GEN3 int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len); void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID without manufacturer block void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overwrite manufacturer block void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes +// MFC GEN4 GDM +void MifareReadConfigBlockGDM(uint8_t *key); +void MifareWriteConfigBlockGDM(uint8_t *datain); +void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain); + // MFC GEN4 GTU -void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd); +void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags); +void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t workFlags); void MifareSetMod(uint8_t *datain); void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key); diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 08d7a9379..de2a76a6a 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -139,6 +139,7 @@ void MifareDesfireGetInformation(void) { uint8_t details[14]; } PACKED payload; + memset(&payload, 0x00, sizeof(payload)); /* 1 = PCB 1 2 = cid 2 @@ -181,6 +182,12 @@ void MifareDesfireGetInformation(void) { return; } + if (len < sizeof(payload.versionHW) + 1) { + Dbprintf("Tag answer to MFDES_GET_VERSION was too short: data in Hardware Information is probably invalid."); + print_result("Answer", resp, len); + memset(resp + len, 0xFF, sizeof(payload.versionHW) + 1 - len); // clear remaining bytes + } + memcpy(payload.versionHW, resp + 1, sizeof(payload.versionHW)); // ADDITION_FRAME 1 @@ -193,6 +200,13 @@ void MifareDesfireGetInformation(void) { switch_off(); return; } + + if (len < sizeof(payload.versionSW) + 1) { + Dbprintf("Tag answer to MFDES_ADDITIONAL_FRAME 1 was too short: data in Software Information is probably invalid."); + print_result("Answer", resp, len); + memset(resp + len, 0xFF, sizeof(payload.versionSW) + 1 - len); // clear remaining bytes + } + memcpy(payload.versionSW, resp + 1, sizeof(payload.versionSW)); // ADDITION_FRAME 2 @@ -205,6 +219,12 @@ void MifareDesfireGetInformation(void) { return; } + if (len < sizeof(payload.details) + 1) { + Dbprintf("Tag answer to MFDES_ADDITIONAL_FRAME 2 was too short: data in Batch number and Production date is probably invalid"); + print_result("Answer", resp, len); + memset(resp + len, 0xFF, sizeof(payload.details) + 1 - len); // clear remaining bytes + } + memcpy(payload.details, resp + 1, sizeof(payload.details)); LED_B_ON(); diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 47cae98ed..c2f81df21 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -486,6 +486,13 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t *rats = NULL; uint8_t rats_len = 0; + + // if fct is called with NULL we need to assign some memory since this pointer is passaed around + uint8_t datain_tmp[10] = {0}; + if (datain == NULL) { + datain = datain_tmp; + } + //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 // This will be used in the reader-only attack. diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index fd9d86ccc..472c2616f 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -18,6 +18,7 @@ //----------------------------------------------------------------------------- #include "mifareutil.h" +#include "appmain.h" // tearoff hook #include "string.h" #include "BigBuf.h" #include "iso14443a.h" @@ -137,25 +138,25 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested) { return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); } - int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { - int len; - uint32_t pos, nt, ntpp; // Supplied tag nonce - uint8_t par[1] = {0x00}; + return mifare_classic_authex_2(pcs, uid, blockNo, keyType, ui64Key, isNested, ntptr, timing, false); +} +int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm) { + + // "random" reader nonce: uint8_t nr[4]; - uint8_t mf_nr_ar[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + num_to_bytes(prng_successor(GetTickCount(), 32), 4, nr); + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // "random" reader nonce: - num_to_bytes(prng_successor(GetTickCount(), 32), 4, nr); - - // Transmit MIFARE_CLASSIC_AUTH - len = mifare_sendcmd_short(pcs, isNested, 0x60 + (keyType & 0x01), blockNo, receivedAnswer, receivedAnswerPar, timing); + // Transmit MIFARE_CLASSIC_AUTH 0x60, 0x61 or GDM 0x80 + uint8_t cmdbyte = (is_gdm) ? MIFARE_MAGIC_GDM_AUTH_KEY : MIFARE_AUTH_KEYA + (keyType & 0x01); + int len = mifare_sendcmd_short(pcs, isNested, cmdbyte, blockNo, receivedAnswer, receivedAnswerPar, timing); if (len != 4) return 1; // Save the tag nonce (nt) - nt = bytes_to_num(receivedAnswer, 4); + uint32_t nt = bytes_to_num(receivedAnswer, 4); // ----------------------------- crypto1 create if (isNested) @@ -181,7 +182,9 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN *ntptr = nt; // Generate (encrypted) nr+parity by loading it into the cipher (Nr) - par[0] = 0; + uint32_t pos; + uint8_t par[1] = {0x00}; + uint8_t mf_nr_ar[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; for (pos = 0; pos < 4; pos++) { mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7 - pos)); @@ -217,8 +220,8 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN return 2; } - ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0, 0); - + // Supplied tag nonce + uint32_t ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0, 0); if (ntpp != bytes_to_num(receivedAnswer, 4)) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Authentication failed. Error card response"); return 3; @@ -227,13 +230,14 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN } int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + return mifare_classic_readblock_ex(pcs, uid, blockNo, blockData, ISO14443A_CMD_READBLOCK); +} +int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte) { - int len; - uint8_t bt[2] = {0x00, 0x00}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + uint16_t len = mifare_sendcmd_short(pcs, 1, iso_byte, blockNo, receivedAnswer, receivedAnswerPar, NULL); if (len == 1) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error %02x", receivedAnswer[0]); return 1; @@ -243,6 +247,7 @@ int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blo return 2; } + uint8_t bt[2] = {0x00, 0x00}; memcpy(bt, receivedAnswer + 16, 2); AddCrc14A(receivedAnswer, 16); if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { @@ -411,57 +416,114 @@ int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { } int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { - // variables - uint16_t len = 0; - uint32_t pos = 0; - uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send - uint8_t res = 0; + return mifare_classic_writeblock_ex(pcs, uid, blockNo, blockData, false); +} +int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm) { - uint8_t d_block[18], d_block_enc[18]; + // variables uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // command MIFARE_CLASSIC_WRITEBLOCK - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + // command MIFARE_MAGIC_GDM_WRITEBLOCK + uint16_t len; + if (is_gdm) { + len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + } else { + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + } if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); return 1; } + uint8_t d_block[18], d_block_enc[18]; memcpy(d_block, blockData, 16); AddCrc14A(d_block, 16); + // enough for 18 Bytes to send + uint8_t par[3] = {0x00, 0x00, 0x00}; // crypto - for (pos = 0; pos < 18; pos++) { + for (uint32_t pos = 0; pos < 18; pos++) { d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); } ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); - // Receive the response - len = ReaderReceive(receivedAnswer, receivedAnswerPar); + // tearoff occurred + if (tearoff_hook() == PM3_ETEAROFF) { + return PM3_ETEAROFF; + } else { + // Receive the response + len = ReaderReceive(receivedAnswer, receivedAnswerPar); - res = 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; + uint8_t res = 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; - if ((len != 1) || (res != 0x0A)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); - return 2; + if ((len != 1) || (res != 0x0A)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); + return 2; + } } return 0; } +int mifare_classic_write_cfg_block_gdm(struct Crypto1State *pcs, uint32_t uid, uint8_t *blockData) { + + // variables + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + uint16_t len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITE_CFG, 0, receivedAnswer, receivedAnswerPar, NULL); + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { + return 1; + } + + uint8_t d_block[18], d_block_enc[18]; + memcpy(d_block, blockData, 16); + AddCrc14A(d_block, 16); + + // enough for 18 Bytes to send + uint8_t par[3] = {0x00, 0x00, 0x00}; + // crypto + for (uint32_t pos = 0; pos < 18; pos++) { + d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; + par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); + } + + ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); + + // tearoff occurred + if (tearoff_hook() == PM3_ETEAROFF) { + return PM3_ETEAROFF; + } else { + // Receive the response + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + + uint8_t res = 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; + + if ((len != 1) || (res != 0x0A)) { + return 2; + } + } + return 0; +} + + + int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action) { // variables uint16_t len = 0; uint32_t pos = 0; uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send - uint8_t res = 0; uint8_t d_block[18], d_block_enc[18]; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; @@ -471,6 +533,8 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo if (action == 0x01) command = MIFARE_CMD_DEC; + if (action == 0x02) + command = MIFARE_CMD_RESTORE; // Send increment or decrement command len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, receivedAnswerPar, NULL); @@ -495,7 +559,7 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo len = ReaderReceive(receivedAnswer, receivedAnswerPar); if (len != 0) { // Something not right, len == 0 (no response is ok as its waiting for transfer - res = 0; + uint8_t res = 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; @@ -505,13 +569,6 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); return 2; } - } else { - // send trnasfer (commit the change) - len = mifare_sendcmd_short(pcs, 1, MIFARE_CMD_TRANSFER, blockNo, receivedAnswer, receivedAnswerPar, NULL); - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } } return 0; diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index aa5ef7e59..e5731ce36 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -72,11 +72,17 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t // mifare classic int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing); +int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm); + int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte); + int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); int mifare_classic_halt_ex(struct Crypto1State *pcs); int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm); int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action); +int mifare_classic_write_cfg_block_gdm(struct Crypto1State *pcs, uint32_t uid, uint8_t *blockData); // Ultralight/NTAG... int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack); diff --git a/armsrc/optimized_cipherutils.h b/armsrc/optimized_cipherutils.h index 73fbfc730..1bef017a2 100644 --- a/armsrc/optimized_cipherutils.h +++ b/armsrc/optimized_cipherutils.h @@ -39,14 +39,14 @@ typedef struct { uint8_t *buffer; - uint8_t numbits; - uint8_t position; + uint32_t numbits; + uint32_t position; } BitstreamIn_t; typedef struct { uint8_t *buffer; - uint8_t numbits; - uint8_t position; + uint32_t numbits; + uint32_t position; } BitstreamOut_t; bool headBit(BitstreamIn_t *stream); diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index bf20f8a70..e243b8ed1 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -18,7 +18,7 @@ // SPIFFS api for RDV40 Integration //----------------------------------------------------------------------------- -#define SPIFFS_CFG_PHYS_SZ (1024 * 128) +#define SPIFFS_CFG_PHYS_SZ (1024 * 192) #define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024) #define SPIFFS_CFG_PHYS_ADDR (0) #define SPIFFS_CFG_LOG_PAGE_SZ (256) @@ -69,7 +69,7 @@ static s32_t rdv40_spiffs_llread(u32_t addr, u32_t size, u8_t *dst) { static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) { - if (!FlashInit()) { + if (FlashInit() == false) { return 129; } Flash_Write(addr, src, size); @@ -77,9 +77,7 @@ static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) { } static s32_t rdv40_spiffs_llerase(u32_t addr, u32_t size) { - uint8_t erased = 0; - - if (!FlashInit()) { + if (FlashInit() == false) { return 130; } @@ -99,10 +97,12 @@ static s32_t rdv40_spiffs_llerase(u32_t addr, u32_t size) { if (g_dbglevel >= DBG_DEBUG) Dbprintf("LLERASEDBG : block : %d, sector : %d \n", block, sector); - erased = Flash_Erase4k(block, sector); + uint8_t erased = Flash_Erase4k(block, sector); Flash_CheckBusy(BUSY_TIMEOUT); FlashStop(); + // iceman: SPIFFS_OK expands to 0, erased is bool from Flash_Erase4k, which returns TRUE if ok. + // so this return logic looks wrong. return (SPIFFS_OK == erased); } @@ -151,8 +151,17 @@ int rdv40_spiffs_mount(void) { // uncached version // int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds, // sizeof(spiffs_fds), 0, 0, 0); cached version, experimental - int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds, sizeof(spiffs_fds), spiffs_cache_buf, - sizeof(spiffs_cache_buf), 0); + int ret = SPIFFS_mount( + &fs, + &cfg, + spiffs_work_buf, + spiffs_fds, + sizeof(spiffs_fds), + spiffs_cache_buf, + sizeof(spiffs_cache_buf), + 0 + ); + if (ret == SPIFFS_OK) { RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_MOUNTED; } @@ -189,33 +198,38 @@ int rdv40_spiffs_check(void) { void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); - if (SPIFFS_write(&fs, fd, src, size) < 0) - Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + if (SPIFFS_write(&fs, fd, src, size) < 0) { + Dbprintf("wr errno %i\n", SPIFFS_errno(&fs)); + } SPIFFS_close(&fs, fd); } void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_APPEND | SPIFFS_RDWR, 0); - if (SPIFFS_write(&fs, fd, src, size) < 0) + if (SPIFFS_write(&fs, fd, src, size) < 0) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + } SPIFFS_close(&fs, fd); } void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size) { spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_RDWR, 0); - if (SPIFFS_read(&fs, fd, dst, size) < 0) + if (SPIFFS_read(&fs, fd, dst, size) < 0) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + } SPIFFS_close(&fs, fd); } static void rename_in_spiffs(const char *old_filename, const char *new_filename) { - if (SPIFFS_rename(&fs, old_filename, new_filename) < 0) + if (SPIFFS_rename(&fs, old_filename, new_filename) < 0) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + } } static void remove_from_spiffs(const char *filename) { - if (SPIFFS_remove(&fs, filename) < 0) + if (SPIFFS_remove(&fs, filename) < 0) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + } } uint32_t size_in_spiffs(const char *filename) { @@ -233,8 +247,11 @@ static rdv40_spiffs_fsinfo info_of_spiffs(void) { fsinfo.pageSize = LOG_PAGE_SIZE; fsinfo.maxOpenFiles = RDV40_SPIFFS_MAX_FD; fsinfo.maxPathLength = SPIFFS_OBJ_NAME_LEN; - if (SPIFFS_info(&fs, &fsinfo.totalBytes, &fsinfo.usedBytes) < 0) + + if (SPIFFS_info(&fs, &fsinfo.totalBytes, &fsinfo.usedBytes) < 0) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + } + fsinfo.freeBytes = fsinfo.totalBytes - fsinfo.usedBytes; // Rounding without float may be improved fsinfo.usedPercent = ((100 * fsinfo.usedBytes) + (fsinfo.totalBytes / 2)) / fsinfo.totalBytes; @@ -245,16 +262,18 @@ static rdv40_spiffs_fsinfo info_of_spiffs(void) { int exists_in_spiffs(const char *filename) { spiffs_stat stat; int rc = SPIFFS_stat(&fs, filename, &stat); - return rc == SPIFFS_OK; + return (rc == SPIFFS_OK); } static RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) { RDV40SpiFFSFileType filetype = RDV40_SPIFFS_FILETYPE_UNKNOWN; char symlinked[SPIFFS_OBJ_NAME_LEN]; sprintf(symlinked, "%s.lnk", filename); + if (exists_in_spiffs(filename)) { filetype = RDV40_SPIFFS_FILETYPE_REAL; } + if (exists_in_spiffs(symlinked)) { if (filetype != RDV40_SPIFFS_FILETYPE_UNKNOWN) { filetype = RDV40_SPIFFS_FILETYPE_BOTH; @@ -262,19 +281,20 @@ static RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) { filetype = RDV40_SPIFFS_FILETYPE_SYMLINK; } } + if (g_dbglevel >= DBG_DEBUG) { switch (filetype) { case RDV40_SPIFFS_FILETYPE_REAL: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_REAL"); + Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_REAL")); break; case RDV40_SPIFFS_FILETYPE_SYMLINK: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_SYMLINK"); + Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_SYMLINK")); break; case RDV40_SPIFFS_FILETYPE_BOTH: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_BOTH"); + Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_BOTH")); break; case RDV40_SPIFFS_FILETYPE_UNKNOWN: - Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_UNKNOWN"); + Dbprintf("Filetype is " _YELLOW_("RDV40_SPIFFS_FILETYPE_UNKNOWN")); break; } } @@ -386,10 +406,13 @@ just get back to this state. If not, just don't. // went well, it will return SPIFFS_OK if everything went well, and a report // back the chain a SPI_ERRNO if not. int rdv40_spiffs_lazy_mount_rollback(int changed) { - if (!changed) + if (!changed) { return SPIFFS_OK; - if (rdv40_spiffs_mounted()) + } + + if (rdv40_spiffs_mounted()) { return rdv40_spiffs_unmount(); + } return rdv40_spiffs_mount(); } /////////////////////////////////////////////////////////////////////////////// @@ -410,15 +433,41 @@ int rdv40_spiffs_lazy_mount_rollback(int changed) { // statement or some function taking function parameters // TODO : forbid writing to a filename which already exists as lnk ! // TODO : forbid writing to a filename.lnk which already exists without lnk ! +// Note: Writing in SPIFFS_WRITE_CHUNK_SIZE (8192) byte chucks helps to ensure "free space" has been erased by GC (Garbage collection) int rdv40_spiffs_write(const char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( - write_to_spiffs(filename, src, size); + uint32_t idx; + if (size <= SPIFFS_WRITE_CHUNK_SIZE) { + // write small file + write_to_spiffs(filename, src, size); + size = 0; + } else { // + // write first SPIFFS_WRITE_CHUNK_SIZE bytes + // need to write the first chuck of data, then append + write_to_spiffs(filename, src, SPIFFS_WRITE_CHUNK_SIZE); + } + // append remaing SPIFFS_WRITE_CHUNK_SIZE byte chuncks + for (idx = 1; idx < (size / SPIFFS_WRITE_CHUNK_SIZE); idx++) { + append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], SPIFFS_WRITE_CHUNK_SIZE); + } + // append remaing bytes + if (((int64_t)size - (SPIFFS_WRITE_CHUNK_SIZE * idx)) > 0) { + append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], size - (SPIFFS_WRITE_CHUNK_SIZE * idx)); + } ) } int rdv40_spiffs_append(const char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( - append_to_spiffs(filename, src, size); + uint32_t idx; + // Append any SPIFFS_WRITE_CHUNK_SIZE byte chunks + for (idx = 0; idx < (size / SPIFFS_WRITE_CHUNK_SIZE); idx++) { + append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], SPIFFS_WRITE_CHUNK_SIZE); + } + // Append remain bytes + if (((int64_t)size - (SPIFFS_WRITE_CHUNK_SIZE * idx)) > 0) { + append_to_spiffs(filename, &src[SPIFFS_WRITE_CHUNK_SIZE * idx], size - (SPIFFS_WRITE_CHUNK_SIZE * idx)); + } ) } @@ -433,24 +482,24 @@ int rdv40_spiffs_read(const char *filename, uint8_t *dst, uint32_t size, RDV40Sp // TODO : forbid writing to a filename.lnk which already exists without lnk ! int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - rename_in_spiffs((char *)old_filename, (char *)new_filename); // + rename_in_spiffs(old_filename, new_filename); // ) } int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - remove_from_spiffs((char *)filename); // + remove_from_spiffs(filename); // ) } int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - copy_in_spiffs((char *)src, (char *)dst); // + copy_in_spiffs(src, dst); // ) } int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - *buf = size_in_spiffs((char *)filename); // + *buf = size_in_spiffs(filename); // ) } @@ -489,12 +538,12 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD sprintf(linkfilename, "%s.lnk", filename); if (g_dbglevel >= DBG_DEBUG) - Dbprintf("Linkk real filename is : " _YELLOW_("%s"), linkfilename); + Dbprintf("Linkk real filename is " _YELLOW_("%s"), linkfilename); read_from_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); if (g_dbglevel >= DBG_DEBUG) - Dbprintf("Symlink destination is : " _YELLOW_("%s"), linkdest); + Dbprintf("Symlink destination is " _YELLOW_("%s"), linkdest); read_from_spiffs((char *)linkdest, (uint8_t *)dst, size); ) @@ -534,12 +583,12 @@ int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, R rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); break; case RDV40_SPIFFS_FILETYPE_SYMLINK: - rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); + rdv40_spiffs_read_as_symlink(filename, (uint8_t *)dst, size, level); break; case RDV40_SPIFFS_FILETYPE_BOTH: case RDV40_SPIFFS_FILETYPE_UNKNOWN: default: - ; + break; } ) } @@ -559,18 +608,20 @@ void rdv40_spiffs_safe_print_fsinfo(void) { rdv40_spiffs_fsinfo fsinfo; rdv40_spiffs_getfsinfo(&fsinfo, RDV40_SPIFFS_SAFETY_SAFE); - Dbprintf(" Logical block size......... " _YELLOW_("%d")" bytes", fsinfo.blockSize); - Dbprintf(" Logical page size.......... " _YELLOW_("%d")" bytes", fsinfo.pageSize); - Dbprintf(" Max open files............. " _YELLOW_("%d")" file descriptors", fsinfo.maxOpenFiles); - Dbprintf(" Max path length............ " _YELLOW_("%d")" chars", fsinfo.maxPathLength); + Dbprintf(" Logical block size... " _YELLOW_("%d")" bytes", fsinfo.blockSize); + Dbprintf(" Logical page size.... " _YELLOW_("%d")" bytes", fsinfo.pageSize); + Dbprintf(" Max open files....... " _YELLOW_("%d")" file descriptors", fsinfo.maxOpenFiles); + Dbprintf(" Max path length...... " _YELLOW_("%d")" chars", fsinfo.maxPathLength); DbpString(""); Dbprintf(" Filesystem size used available use% mounted"); + DbpString("------------------------------------------------------------------"); Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /" , fsinfo.totalBytes , fsinfo.usedBytes , fsinfo.freeBytes , fsinfo.usedPercent ); + DbpString(""); } // this function is safe and WILL rollback since it is only a PRINTING function, diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index 01bf113e8..5958e559b 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -24,7 +24,11 @@ extern "C" { #include "spiffs_config.h" -typedef enum spiffs_safety_level { RDV40_SPIFFS_SAFETY_NORMAL, RDV40_SPIFFS_SAFETY_LAZY, RDV40_SPIFFS_SAFETY_SAFE } RDV40SpiFFSSafetyLevel; +typedef enum spiffs_safety_level { + RDV40_SPIFFS_SAFETY_NORMAL, + RDV40_SPIFFS_SAFETY_LAZY, + RDV40_SPIFFS_SAFETY_SAFE +} RDV40SpiFFSSafetyLevel; typedef enum spiffs_file_type { RDV40_SPIFFS_FILETYPE_REAL, @@ -125,6 +129,8 @@ void rdv40_spiffs_safe_wipe(void); #define SPIFFS_ERR_TEST -10100 +// Amount of data to write/append to a file in one go. +#define SPIFFS_WRITE_CHUNK_SIZE 8192 // spiffs file descriptor index type. must be signed typedef s16_t spiffs_file; @@ -198,16 +204,16 @@ typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, #ifndef SPIFFS_DBG #define SPIFFS_DBG(...) \ - printf(__VA_ARGS__) + Dbprintf(__VA_ARGS__) #endif #ifndef SPIFFS_GC_DBG -#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) +#define SPIFFS_GC_DBG(...) Dbprintf(__VA_ARGS__) #endif #ifndef SPIFFS_CACHE_DBG -#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) +#define SPIFFS_CACHE_DBG(...) Dbprintf(__VA_ARGS__) #endif #ifndef SPIFFS_CHECK_DBG -#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) +#define SPIFFS_CHECK_DBG(...) Dbprintf(__VA_ARGS__) #endif /* Any write to the filehandle is appended to end of the file */ diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h index eb699d976..f1d54a471 100644 --- a/armsrc/spiffs_config.h +++ b/armsrc/spiffs_config.h @@ -136,7 +136,7 @@ typedef uint8_t u8_t; // Define maximum number of gc runs to perform to reach desired free pages. #ifndef SPIFFS_GC_MAX_RUNS -#define SPIFFS_GC_MAX_RUNS 5 +#define SPIFFS_GC_MAX_RUNS 10 #endif // Enable/disable statistics on gc. Debug/test purpose only. @@ -236,7 +236,7 @@ typedef uint8_t u8_t; // Instead of giving parameters in config struct, singleton build must // give parameters in defines below. #ifndef SPIFFS_CFG_PHYS_SZ -#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*128) +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*192) #endif #ifndef SPIFFS_CFG_PHYS_ERASE_SZ #define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) diff --git a/bootrom/Makefile b/bootrom/Makefile index ec531f1af..cad3e17d1 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -34,11 +34,17 @@ VERSIONSRC = version_pm3.c # THUMBSRC := # stdint.h provided locally until GCC 4.5 becomes C99 compliant -APP_CFLAGS = -I. -ffunction-sections -fdata-sections +APP_CFLAGS = -I. -ffunction-sections -fdata-sections -DAS_BOOTROM # stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc, no-common makes sure uninitialized vars don't end up in COMMON area APP_CFLAGS += -fno-stack-protector -fno-pie -fno-common +ifneq (,$(findstring WITH_FLASH,$(PLATFORM_DEFS))) + APP_CFLAGS += -DWITH_FLASH + APP_CFLAGS += -I../common_arm + THUMBSRC += flashmem.c ticks.c +endif + # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common_arm/Makefile.common @@ -48,7 +54,7 @@ INSTALLFW = $(OBJDIR)/bootrom.elf OBJS = $(OBJDIR)/bootrom.s19 # version_pm3.c should be remade on every compilation -version_pm3.c: default_version_pm3.c +version_pm3.c: default_version_pm3.c .FORCE $(info [=] GEN $@) $(Q)$(SH) ../tools/mkversion.sh > $@ || $(PERL) ../tools/mkversion.pl > $@ || $(CP) $< $@ @@ -82,7 +88,7 @@ uninstall: $(info [@] Uninstalling bootrom from $(DESTDIR)$(PREFIX)...) $(Q)$(INSTALLSUDO) $(RM) $(foreach fw,$(INSTALLFW),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(notdir $(fw))) -.PHONY: all clean help install showinfo +.PHONY: all clean help install showinfo .FORCE help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 93c4d605e..4c87a65b0 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -20,6 +20,10 @@ #include "clocks.h" #include "usb_cdc.h" +#ifdef WITH_FLASH +#include "flashmem.h" +#endif + #include "proxmark3_arm.h" #define DEBUG 0 @@ -214,8 +218,18 @@ static void flash_mode(void) { bootrom_unlocked = false; uint8_t rx[sizeof(PacketCommandOLD)]; g_common_area.command = COMMON_AREA_COMMAND_NONE; - if (!g_common_area.flags.button_pressed && BUTTON_PRESS()) + if (!g_common_area.flags.button_pressed && BUTTON_PRESS()) { g_common_area.flags.button_pressed = 1; + } + +#ifdef WITH_FLASH + if (FlashInit()) { // checks for existence of flash also ... OK because bootrom was built for devices with flash + uint64_t flash_uniqueID = 0; + Flash_UniqueID((uint8_t *)&flash_uniqueID); + FlashStop(); + usb_update_serial(flash_uniqueID); + } +#endif usb_enable(); @@ -296,10 +310,10 @@ void BootROM(void) { LED_B_OFF(); LED_A_OFF(); - // Set the first 256kb memory flashspeed + // Set the first 256KB memory flashspeed AT91C_BASE_EFC0->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); - // 9 = 256, 10+ is 512kb + // 9 = 256, 10+ is 512KB uint8_t id = (*(AT91C_DBGU_CIDR) & 0xF00) >> 8; if (id > 9) AT91C_BASE_EFC1->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); diff --git a/bootrom/ldscript-flash b/bootrom/ldscript-flash index 5d63f9689..374c2d6c7 100644 --- a/bootrom/ldscript-flash +++ b/bootrom/ldscript-flash @@ -53,6 +53,7 @@ SECTIONS *(.rodata.*) *(.data) *(.data.*) + *(.ramfunc) . = ALIGN(4); } >ram AT>bootphase2 :phase2 diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index b86335c66..ee1ddcc25 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -121,7 +121,7 @@ if (NOT SKIPREADLINE EQUAL 1) ExternalProject_Add_StepTargets(ncurses configure build install) ExternalProject_Add(readline - URL ftp://ftp.gnu.org/gnu/readline/readline-8.1.tar.gz + URL ftp://ftp.gnu.org/gnu/readline/readline-8.2.tar.gz PREFIX deps/readline DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/readline CONFIGURE_COMMAND ./configure CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} --host=arm --enable-static @@ -163,11 +163,12 @@ endif (NOT SKIPJANSSONSYSTEM EQUAL 1) if(EMBED_BZIP2) set(BZIP2_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/bzip2/src/bzip2) + # Specify SOURCE_DIR will cause some errors ExternalProject_Add(bzip2 GIT_REPOSITORY https://android.googlesource.com/platform/external/bzip2 GIT_TAG platform-tools-30.0.2 PREFIX deps/bzip2 - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/bzip2 + # SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/bzip2 CONFIGURE_COMMAND mkdir -p ${BZIP2_BUILD_DIR} && git archive --format tar HEAD | tar -C ${BZIP2_BUILD_DIR} -x BUILD_IN_SOURCE ON BUILD_COMMAND make -C ${BZIP2_BUILD_DIR} -j4 CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} libbz2.a @@ -275,6 +276,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhfepa.c ${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffido.c + ${PM3_ROOT}/client/src/cmdhffudan.c ${PM3_ROOT}/client/src/cmdhfgallagher.c ${PM3_ROOT}/client/src/cmdhfcipurse.c ${PM3_ROOT}/client/src/cmdhficlass.c @@ -292,9 +294,12 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhfseos.c ${PM3_ROOT}/client/src/cmdhfst.c ${PM3_ROOT}/client/src/cmdhfst25ta.c + ${PM3_ROOT}/client/src/cmdhftesla.c + ${PM3_ROOT}/client/src/cmdhftexkom.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c ${PM3_ROOT}/client/src/cmdhfwaveshare.c + ${PM3_ROOT}/client/src/cmdhfxerox.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c @@ -333,6 +338,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdmain.c ${PM3_ROOT}/client/src/cmdnfc.c ${PM3_ROOT}/client/src/cmdparser.c + ${PM3_ROOT}/client/src/cmdpiv.c ${PM3_ROOT}/client/src/cmdscript.c ${PM3_ROOT}/client/src/cmdsmartcard.c ${PM3_ROOT}/client/src/cmdtrace.c @@ -579,6 +585,9 @@ if (MINGW) set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}") + + # link Winsock2 + set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK}) endif (MINGW) target_include_directories(proxmark3 PRIVATE diff --git a/client/Makefile b/client/Makefile index e2dd1a224..d0cee355e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -37,8 +37,7 @@ OBJDIR = obj ifeq ($(USE_BREW),1) INCLUDES += -I$(BREW_PREFIX)/include LDLIBS += -L$(BREW_PREFIX)/lib - PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig - PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt5/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/lib/pkgconfig:$(BREW_PREFIX)/opt/qt/lib/pkgconfig:$(BREW_PREFIX)/opt/qt5/lib/pkgconfig endif ifdef ($(USE_MACPORTS),1) @@ -94,7 +93,6 @@ LUALIBLD = LUAPLATFORM = generic ifneq (,$(findstring MINGW,$(platform))) LUAPLATFORM = mingw - LDLIBS += -lws2_32 else ifeq ($(platform),Darwin) LUAPLATFORM = macosx @@ -104,6 +102,11 @@ else endif endif +## Winsock2 +ifneq (,$(findstring MINGW,$(platform))) + LDLIBS += -lws2_32 +endif + ## Reveng REVENGLIBPATH = ./deps/reveng REVENGLIBINC = -I$(REVENGLIBPATH) @@ -290,6 +293,7 @@ ifneq ($(SKIPQT),1) QTLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) MOC = $(shell $(PKG_CONFIG_ENV) pkg-config --variable=host_bins Qt5Core)/moc UIC = $(shell $(PKG_CONFIG_ENV) pkg-config --variable=host_bins Qt5Core)/uic + QMAKE = $(shell $(PKG_CONFIG_ENV) pkg-config --variable=host_bins Qt5Core)/qmake ifneq ($(QTLDLIBS),) QT5_FOUND = 1 else @@ -298,6 +302,7 @@ ifneq ($(SKIPQT),1) QTLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs QtCore QtGui 2>/dev/null) MOC = $(shell $(PKG_CONFIG_ENV) pkg-config --variable=moc_location QtCore) UIC = $(shell $(PKG_CONFIG_ENV) pkg-config --variable=uic_location QtCore) + QMAKE = $(shell $(PKG_CONFIG_ENV) pkg-config --variable=exec_prefix QtCore)/bin/qmake endif ifeq ($(QTLDLIBS),) # if both pkg-config commands failed, search in common places @@ -314,6 +319,7 @@ ifneq ($(SKIPQT),1) endif MOC = $(QTDIR)/bin/moc UIC = $(QTDIR)/bin/uic + QMAKE = $(QTDIR)/bin/qmake endif endif ifneq ($(QTLDLIBS),) @@ -363,9 +369,11 @@ endif ####################################################################################################### CFLAGS ?= $(DEFCFLAGS) +CFLAGS += $(MYDEFS) $(MYCFLAGS) $(MYINCLUDES) + # We cannot just use CFLAGS+=... because it has impact on sub-makes if CFLAGS is defined in env: PM3CFLAGS = $(CFLAGS) -PM3CFLAGS += -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) +PM3CFLAGS += -g -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) # WIP Testing #PM3CFLAGS += -std=c11 -pedantic @@ -405,6 +413,8 @@ endif PM3CFLAGS += -DHAVE_SNPRINTF CXXFLAGS ?= -Wall -Werror -O3 +CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) + PM3CXXFLAGS = $(CXXFLAGS) PM3CXXFLAGS += -I../include -I./include @@ -420,6 +430,8 @@ endif PM3CXXFLAGS += -DHAVE_SNPRINTF LDFLAGS ?= $(DEFLDFLAGS) +LDFLAGS += $(MYLDFLAGS) + PM3LDFLAGS = $(LDFLAGS) ifeq ($(platform),Darwin) PM3LDFLAGS += -framework Foundation -framework AppKit @@ -438,9 +450,9 @@ ifeq ($(SKIPQT),1) else ifeq ($(QT_FOUND),1) ifeq ($(QT5_FOUND),1) - $(info GUI support: QT5 found, enabled ($(shell QT_SELECT=5 qmake -v 2>/dev/null|grep -o 'Qt version.*'))) + $(info GUI support: QT5 found, enabled ($(shell QT_SELECT=5 $(QMAKE) -v 2>/dev/null|grep -o 'Qt version.*'))) else - $(info GUI support: QT4 found, enabled ($(shell QT_SELECT=4 qmake -v 2>/dev/null|grep -o 'Qt version.*'))) + $(info GUI support: QT4 found, enabled ($(shell QT_SELECT=4 $(QMAKE) -v 2>/dev/null|grep -o 'Qt version.*'))) endif else $(info GUI support: QT not found, disabled) @@ -479,7 +491,7 @@ ifeq ($(SKIPPYTHON),1) $(info Python3 library: skipped) else ifeq ($(PYTHON_FOUND),1) - $(info Python3 library: Python3 v$(shell pkg-config --modversion python3) found, enabled) + $(info Python3 library: Python3 v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion python3) found, enabled) else $(info Python3 library: Python3 not found, disabled) endif @@ -557,6 +569,7 @@ SRCS = mifare/aiddesfire.c \ cmdhfemrtd.c \ cmdhffelica.c \ cmdhffido.c \ + cmdhffudan.c \ cmdhfgallagher.c \ cmdhfksx6924.c \ cmdhfcipurse.c \ @@ -574,9 +587,12 @@ SRCS = mifare/aiddesfire.c \ cmdhfseos.c \ cmdhfst.c \ cmdhfst25ta.c \ + cmdhftesla.c \ cmdhfthinfilm.c \ cmdhftopaz.c \ + cmdhftexkom.c \ cmdhfwaveshare.c \ + cmdhfxerox.c \ cmdhw.c \ cmdlf.c \ cmdlfawid.c \ @@ -615,6 +631,7 @@ SRCS = mifare/aiddesfire.c \ cmdmain.c \ cmdnfc.c \ cmdparser.c \ + cmdpiv.c \ cmdscript.c \ cmdsmartcard.c \ cmdtrace.c \ @@ -747,8 +764,9 @@ all-static: LDLIBS:=-static $(LDLIBS) all-static: $(BINS) proxmark3: $(OBJS) $(STATICLIBS) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua - $(info [=] LD $@) - $(Q)$(LD) $(PM3LDFLAGS) $(OBJS) $(STATICLIBS) $(LDLIBS) -o $@ + $(info [=] CXX $@) +# $(Q)$(CXX) $(PM3LDFLAGS) $(OBJS) $(STATICLIBS) $(LDLIBS) -o $@ + $(Q)$(CXX) $(PM3CFLAGS) $(PM3LDFLAGS) $(OBJS) $(STATICLIBS) $(LDLIBS) -o $@ src/proxgui.cpp: src/ui/ui_overlays.h src/ui/ui_image.h diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index 980e3134d..e5e8f946c 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -11,10 +11,10 @@ #include "cliparser.h" #include #include -#include // Get color constants -#include // get PrintAndLogEx -#include // tolower -#include // PRIu64 +#include // color constants +#include // PrintAndLogEx +#include // tolower +#include // PRIu64 #ifndef ARRAYLEN # define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0])) @@ -22,10 +22,10 @@ // Custom Colors // To default the color return s -#define _SectionTagColor_(s) _GREEN_(s) -#define _ExampleColor_(s) _YELLOW_(s) -#define _CommandColor_(s) _RED_(s) -#define _DescriptionColor_(s) _CYAN_(s) +#define _SectionTagColor_(s) _GREEN_(s) +#define _ExampleColor_(s) _YELLOW_(s) +#define _CommandColor_(s) _RED_(s) +#define _DescriptionColor_(s) _CYAN_(s) #define _ArgColor_(s) s #define _ArgHelpColor_(s) s // End Custom Colors @@ -33,66 +33,79 @@ // Example width set to 50 to allow help descriptions to align. approx line 93 int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp) { - *ctx = malloc(sizeof(CLIParserContext)); - if (!*ctx) { + *ctx = calloc(sizeof(CLIParserContext), sizeof(uint8_t)); + if (*ctx == NULL) { PrintAndLogEx(ERR, "ERROR: Insufficient memory\n"); return 2; } + (*ctx)->argtable = NULL; (*ctx)->argtableLen = 0; (*ctx)->programName = vprogramName; (*ctx)->programHint = vprogramHint; (*ctx)->programHelp = vprogramHelp; memset((*ctx)->buf, 0x00, sizeof((*ctx)->buf)); - return 0; + + return PM3_SUCCESS; } void CLIParserPrintHelp(CLIParserContext *ctx) { - if (ctx->programHint) + if (ctx->programHint) { PrintAndLogEx(NORMAL, "\n"_DescriptionColor_("%s"), ctx->programHint); + } PrintAndLogEx(NORMAL, "\n"_SectionTagColor_("usage:")); PrintAndLogEx(NORMAL, " "_CommandColor_("%s")NOLF, ctx->programName); arg_print_syntax(stdout, ctx->argtable, "\n\n"); PrintAndLogEx(NORMAL, _SectionTagColor_("options:")); - arg_print_glossary(stdout, ctx->argtable, " "_ArgColor_("%-30s")" "_ArgHelpColor_("%s")"\n"); - PrintAndLogEx(NORMAL, ""); - if (ctx->programHelp) { - PrintAndLogEx(NORMAL, _SectionTagColor_("examples/notes:")); - char *buf = NULL; - int idx = 0; - buf = realloc(buf, strlen(ctx->programHelp) + 1); // more then enough as we are splitting - char *p2; // pointer to split example from comment. + if (ctx->programHelp) { + + // allocate more then enough memory as we are splitting + char *s = calloc(strlen(ctx->programHelp) + 1, sizeof(uint8_t)); + if (s == NULL) { + PrintAndLogEx(FAILED, "cannot allocate memory"); + return; + } + + PrintAndLogEx(NORMAL, _SectionTagColor_("examples/notes:")); + + // pointer to split example from comment. + char *p2; + + int idx = 0; int egWidth = 30; for (int i = 0; i <= strlen(ctx->programHelp); i++) { // <= so to get string terminator. - buf[idx++] = ctx->programHelp[i]; + + s[idx++] = ctx->programHelp[i]; + if ((ctx->programHelp[i] == '\n') || (ctx->programHelp[i] == 0x00)) { - buf[idx - 1] = 0x00; - p2 = strstr(buf, "->"); // See if the example has a comment. + + s[idx - 1] = 0x00; + p2 = strstr(s, "->"); // See if the example has a comment. + if (p2 != NULL) { *(p2 - 1) = 0x00; - if (strlen(buf) > 28) - egWidth = strlen(buf) + 5; + if (strlen(s) > 28) + egWidth = strlen(s) + 5; else egWidth = 30; - PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, buf, p2); + PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, s, p2); } else { - PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, buf); + PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, s); } + idx = 0; } } - + free(s); PrintAndLogEx(NORMAL, ""); - free(buf); } - fflush(stdout); } @@ -127,7 +140,7 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta return 3; } - return 0; + return PM3_SUCCESS; } enum ParserState { @@ -144,21 +157,25 @@ int CLIParserParseString(CLIParserContext *ctx, const char *str, void *vargtable int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData) { int argc = 0; - char *argv[200] = {NULL}; + char *argv[MAX_INPUT_ARG_LENGTH] = {NULL}; int len = strlen(str); + memset(ctx->buf, 0x00, ARRAYLEN(ctx->buf)); + char *bufptr = ctx->buf; char *bufptrend = ctx->buf + ARRAYLEN(ctx->buf) - 1; char *spaceptr = NULL; enum ParserState state = PS_FIRST; argv[argc++] = bufptr; - // param0 = program name - memcpy(ctx->buf, ctx->programName, strlen(ctx->programName) + 1); // with 0x00 + // param0 = program name + with 0x00 + memcpy(ctx->buf, ctx->programName, strlen(ctx->programName) + 1); + bufptr += strlen(ctx->programName) + 1; - if (len) + if (len) { argv[argc++] = bufptr; + } // parse params for (int i = 0; i < len; i++) { @@ -214,10 +231,9 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen = 0; int tmplen = 0; - uint8_t tmpstr[(256 * 2) + 1] = {0}; + uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0}; // concat all strings in argstr into tmpstr[] - // int res = CLIParamStrToBuf(argstr, tmpstr, sizeof(tmpstr), &tmplen); if (res || (tmplen == 0)) { return res; @@ -242,7 +258,7 @@ int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen = 0; int tmplen = 0; - uint8_t tmpstr[(256 * 2) + 1] = {0}; + uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0}; // concat all strings in argstr into tmpstr[] // @@ -268,7 +284,7 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int if (!argstr->count) return 0; - uint8_t tmpstr[(512 * 2) + 1] = {0}; + uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0}; int ibuf = 0; for (int i = 0; i < argstr->count; i++) { @@ -303,7 +319,7 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array, int *value) { char data[200] = {0}; - int datalen = 0; + int datalen = 200; int res = CLIParamStrToBuf(argstr, (uint8_t *)data, sizeof(data), &datalen); if (res) return res; @@ -316,7 +332,7 @@ int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array int val = -1; int cntr = 0; - for (int i = 0; i < CLI_MAX_OPTLIST_LEN && option_array[i].text != NULL; i++) { + for (int i = 0; (i < CLI_MAX_OPTLIST_LEN) && (option_array[i].text != NULL); i++) { // exact match if (strcmp(option_array[i].text, data) == 0) { *value = option_array[i].code; @@ -346,9 +362,10 @@ int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array const char *CLIGetOptionListStr(const CLIParserOption *option_array, int value) { static const char *errmsg = "n/a"; - for (int i = 0; i < CLI_MAX_OPTLIST_LEN && option_array[i].text != NULL; i++) { - if (option_array[i].code == value) + for (int i = 0; (i < CLI_MAX_OPTLIST_LEN) && (option_array[i].text != NULL); i++) { + if (option_array[i].code == value) { return option_array[i].text; + } } return errmsg; } diff --git a/client/deps/cliparser/cliparser.h b/client/deps/cliparser/cliparser.h index f50fec2ee..812b44506 100644 --- a/client/deps/cliparser/cliparser.h +++ b/client/deps/cliparser/cliparser.h @@ -12,6 +12,7 @@ #define __CLIPARSER_H #include "argtable3.h" #include +#include #include "util.h" #define arg_param_begin arg_lit0("h", "help", "This help") @@ -53,13 +54,16 @@ #define CLIGetOptionListWithReturn(ctx, paramnum, option_array, option_array_len, value) if (CLIGetOptionList(arg_get_str((ctx), (paramnum)), (option_array), (option_array_len), (value))) {CLIParserFree((ctx)); return PM3_ESOFT;} +#define MAX_INPUT_ARG_LENGTH 4096 + + typedef struct { void **argtable; size_t argtableLen; const char *programName; const char *programHint; const char *programHelp; - char buf[1024 + 60]; + char buf[MAX_INPUT_ARG_LENGTH + 60]; } CLIParserContext; #define CLI_MAX_OPTLIST_LEN 50 diff --git a/client/deps/hardnested.cmake b/client/deps/hardnested.cmake index 524d4c939..ec545e2a8 100644 --- a/client/deps/hardnested.cmake +++ b/client/deps/hardnested.cmake @@ -17,7 +17,7 @@ target_compile_definitions(pm3rrg_rdv4_hardnested_nosimd PRIVATE NOSIMD_BUILD) ## Mingw platforms: AMD64 set(X86_CPUS x86 x86_64 i686 AMD64) set(ARM64_CPUS arm64 aarch64) -set(ARM32_CPUS armel armhf) +set(ARM32_CPUS armel armhf armv7-a) message(STATUS "CMAKE_SYSTEM_PROCESSOR := ${CMAKE_SYSTEM_PROCESSOR}") diff --git a/client/deps/hardnested/hardnested_bf_core.c b/client/deps/hardnested/hardnested_bf_core.c index 0d4e680fb..63eba8eba 100644 --- a/client/deps/hardnested/hardnested_bf_core.c +++ b/client/deps/hardnested/hardnested_bf_core.c @@ -163,7 +163,8 @@ static void *malloc_bitslice(size_t x) { } #define free_bitslice(x) free(x) #else -#define malloc_bitslice(x) memalign(MAX_BITSLICES / 8, (x)) +//#define malloc_bitslice(x) memalign(MAX_BITSLICES / 8, (x)) +#define malloc_bitslice(x) __builtin_assume_aligned(memalign(MAX_BITSLICES / 8, (x)), MAX_BITSLICES / 8); #define free_bitslice(x) free(x) #endif @@ -529,7 +530,6 @@ stop_tests: bucket_states_tested += bucket_size[block_idx]; // prepare to set new states state_p = &states[KEYSTREAM_SIZE]; - continue; } } out: diff --git a/client/deps/hardnested/hardnested_tables.c b/client/deps/hardnested/hardnested_tables.c index ce2f42cff..aade0c0cd 100644 --- a/client/deps/hardnested/hardnested_tables.c +++ b/client/deps/hardnested/hardnested_tables.c @@ -85,7 +85,8 @@ static void *malloc_bitarray(size_t x) { } #define free_bitarray(x) free(x) #else -#define malloc_bitarray(x) memalign(__BIGGEST_ALIGNMENT__, (x)) +//#define malloc_bitarray(x) memalign(__BIGGEST_ALIGNMENT__, (x)) +#define malloc_bitarray(x) __builtin_assume_aligned(memalign(__BIGGEST_ALIGNMENT__, (x)), __BIGGEST_ALIGNMENT__); #define free_bitarray(x) free(x) #endif diff --git a/client/dictionaries/ht2_default.dic b/client/dictionaries/ht2_default.dic index 2dc816512..f503fa543 100644 --- a/client/dictionaries/ht2_default.dic +++ b/client/dictionaries/ht2_default.dic @@ -10,3 +10,4 @@ 25293C2F # # Paxton HT2 +BDF5E846 diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index ee39c1e4c..d2d6489b2 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -10,7 +10,32 @@ AEA684A6DAB23278 # AA1 F0E1D2C3B4A59687 # Kd from PicoPass 2k documentation 5CBCF1DA45D5FB4F # PicoPass Default Exchange Key 31ad7ebd2f282168 # From HID multiclassSE reader -6EFD46EFCBB3C875 # From pastebin: https://pastebin.com/uHqpjiuU -E033CA419AEE43F9 # From pastebin: https://pastebin.com/uHqpjiuU -2020666666668888 # iCopy-X iCL tags -6666202066668888 # iCopy-X iCS tags reversed from the SOs +# +# From pastebin: https://pastebin.com/uHqpjiuU +6EFD46EFCBB3C875 +E033CA419AEE43F9 +# +# iCopy-x DRM keys +2020666666668888 # iCL tags +6666202066668888 # iCS tags reversed from the SOs +# +# default picopass KD / Page 0 / Book 1 +FDCB5A52EA8F3090 +237FF9079863DF44 +5ADC25FB27181D32 +83B881F2936B2E49 +43644E61EE866BA5 +897034143D016080 +82D17B44C0122963 +4895CA7DE65E2025 +DADAD4C57BE271B7 +E41E9EDEF5719ABF +293D275EC3AF9C7F +C3C169251B8A70FB +F41DAF58B20C8B91 +28877A609EC0DD2B +66584C91EE80D5E5 +C1B74D7478053AE2 +# +# default iCLASS RFIDeas +6B65797374726B72 diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 1f5bf601b..44ec319f5 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -3,145 +3,223 @@ # -- iceman fork version -- # -- contribute to this list, sharing is caring -- # -ffffffffffff # Defaultkey(firstkeyusedbyprogramifnouserdefinedkey) -000000000000 # Blankkey -a0a1a2a3a4a5 # NFC Forum MADkey -A5A4A3A2A1A0 # MAD access key A (reversed) -89ECA97F8C2A # MAD access key B -b0b1b2b3b4b5 -c0c1c2c3c4c5 -d0d1d2d3d4d5 -aabbccddeeff -4d3a99c351dd -1a982c7e459a -d3f7d3f7d3f7 # key A Wien -5a1b85fce20a # key B Wien -714c5c886e97 -587ee5f9350f -a0478cc39091 -533cb6c723f6 -8fd0a4f256e9 -e00000000000 # iCopy-X -e7d6064c5860 -b27ccab30dbd +# Default key +FFFFFFFFFFFF # -d2ece8b9395e # lib / Nat Bieb +# Blank key +000000000000 +# +# NFC Forum MADkey +A0A1A2A3A4A5 +# +# MAD access key A (reversed) +A5A4A3A2A1A0 +# +# MAD access key B +89ECA97F8C2A +# +# +B0B1B2B3B4B5 +C0C1C2C3C4C5 +D0D1D2D3D4D5 +AABBCCDDEEFF +4D3A99C351DD +1A982C7E459A +# +# key A Wien +D3F7D3F7D3F7 +# +# key B Wien +5A1B85FCE20A +# +# +714C5C886E97 +587EE5F9350F +A0478CC39091 +533CB6C723F6 +8FD0A4F256E9 +# +# iCopy-X +E00000000000 +# +# +E7D6064C5860 +B27CCAB30DBD +# +# lib / Nat Bieb +D2ECE8B9395E # NSCP default key 1494E81663D7 +# +# NFC tools +7c9fb8474242 # # Kiev keys -569369c5a0e5 # kiev -632193be1c3c # kiev -644672bd4afe # kiev -8fe644038790 # kiev -9de89e070277 # kiev -b5ff67cba951 # kiev / ov-chipkaart -eff603e1efe9 # kiev -f14ee7cae863 # kiev +569369C5A0E5 +632193BE1C3C +644672BD4AFE +8FE644038790 +9DE89E070277 +B5FF67CBA951 +EFF603E1EFE9 +F14EE7CAE863 # # RKF -fc00018778f7 # Västtrafiken KeyA, RKF ÖstgötaTrafiken KeyA -0297927c0f77 # Västtrafiken KeyA -54726176656c # Västtrafiken KeyA -00000ffe2488 # Västtrafiken KeyB -776974687573 # Västtrafiken KeyB -ee0042f88840 # Västtrafiken KeyB -26940b21ff5d # RKF SLKeyA -a64598a77478 # RKF SLKeyA -5c598c9c58b5 # RKF SLKeyB -e4d2770a89be # RKF SLKeyB -722bfcc5375f # RKF RejskortDanmark KeyA -f1d83f964314 # RKF RejskortDanmark KeyB -505249564141 # RKF JOJOPRIVAKeyA -505249564142 # RKF JOJOPRIVAKeyB -47524f555041 # RKF JOJOGROUPKeyA -47524f555042 # RKF JOJOGROUPKeyB -434f4d4d4f41 # RKF JOJOGROUPKeyA -434f4d4d4f42 # RKF JOJOGROUPKeyB -# -4b0b20107ccb # TNP3xxx +# Västtrafiken KeyA, RKF ÖstgötaTrafiken KeyA +FC00018778F7 +# +# Västtrafiken KeyA +0297927C0F77 +54726176656C +# +# Västtrafiken KeyB +00000FFE2488 +776974687573 +EE0042F88840 +# +# RKF SLKeyA +26940B21FF5D +A64598A77478 +# +# RKF SLKeyB +5C598C9C58B5 +E4D2770A89BE +# +# RKF Rejskort Danmark KeyA +722BFCC5375F +# +# RKF Rejskort Danmark KeyB +F1D83F964314 +# +# RKF JOJOPRIVA KeyA +505249564141 +# +# RKF JOJOPRIVA KeyB +505249564142 +# +# RKF JOJOGROUP KeyA +47524F555041 +434F4D4D4F41 +# +# RKF JOJOGROUP KeyB +47524F555042 +434F4D4D4F42 +# +# TNP3xxx +4B0B20107CCB # # Access control system 605F5E5D5C5B # +#NSP Global keys A and B (uk housing access control) +199404281970 +199404281998 +# # more Keys from mfc_default_keys.lua 000000000001 000000000002 -00000000000a -00000000000b +00000000000A +00000000000B 010203040506 -0123456789ab +0123456789AB 100000000000 111111111111 -123456789abc -12f2ee3478c1 -14d446e33363 -1999a3554a55 +123456789ABC +12F2EE3478C1 +14D446E33363 +1999A3554A55 200000000000 222222222222 -27dd91f1fcf1 -2BA9621E0A36 # DirectoryandeventlogKeyB -4AF9D7ADEBE4 # DirectoryandeventlogKeyA +27DD91F1FCF1 +# +# Directory and eventlog KeyB +2BA9621E0A36 +# +# Directory and eventlog KeyA +4AF9D7ADEBE4 +# +# 333333333333 -33f974b42769 -34d1df9934c5 -43ab19ef5c31 +33F974B42769 +34D1DF9934C5 +43AB19EF5C31 444444444444 505249565441 505249565442 555555555555 -55f5a5dd38c9 +55F5A5DD38C9 666666666666 777777777777 888888888888 999999999999 -99c636334433 -a00000000000 -a053a292a4af -a94133013401 -aaaaaaaaaaaa -abcdef123456 # Keyfromladyada.net -b00000000000 -b127c6f41436 -bbbbbbbbbbbb -bd493a3962b6 -c934fe34d934 -cccccccccccc -dddddddddddd -eeeeeeeeeeee +99C636334433 +A00000000000 +A053A292A4AF +A94133013401 +AAAAAAAAAAAA +# +# Key from ladyada.net +ABCDEF123456 +# +# +B00000000000 +B127C6F41436 +BBBBBBBBBBBB +BD493A3962B6 +C934FE34D934 +CCCCCCCCCCCC +DDDDDDDDDDDD +EEEEEEEEEEEE # # elevator -# data from forum +# data from forum FFFFFF545846 # -f1a97341a9fc -44ab09010845 # hotel system -85fed980ea5a # hotel system # -43454952534E # ARD (fr) key A -4A2B29111213 # ARD (fr) key B +F1A97341A9FC +# +# hotel system +44AB09010845 +85FED980EA5A +# +# ARD (fr) key A +43454952534E +# ARD (fr) key B +4A2B29111213 +# # 4143414F5250 -a9b43414F585 # Tehran Railway -1FB235AC1388 # Tehran Railway +# +# Tehran Railway +A9B43414F585 +1FB235AC1388 # # Data from http://irq5.io/2013/04/13/decoding-bcard-conference-badges/ -f4a9ef2afc6d # BCARD KeyB +# BCARD KeyB +F4A9EF2AFC6D # -# Data from ... -89eac97f8c2a # S0 B -43c7600dee6b # S4 A -0120bf672a64 # S6 A -fb0b20df1f34 # S6 B # -a9f953def0a3 +# S0 B +89EAC97F8C2A +# +# S4 A +43C7600DEE6B +# +# S6 A +0120BF672A64 +# +# S6 B +FB0B20DF1F34 +# +# +A9F953DEF0A3 # # Data from forum -74a386ad0a6d -3f7a5c2dbd81 -21edf95e7433 -c121ff19f681 -3d5d9996359a +74A386AD0A6D +3F7A5C2DBD81 +21EDF95E7433 +C121FF19F681 +3D5D9996359A # # Here be BIP keys... 3A42F33AF429 @@ -178,24 +256,36 @@ D49E2826664F 6A470D54127C # # Data from http://pastebin.com/AK9Bftpw -48ffe71294a0 # Länstrafiken i Västerbotten -e3429281efc1 # Länstrafiken i Västerbotten -16f21a82ec84 # Länstrafiken i Västerbotten -460722122510 # Länstrafiken i Västerbotten +# Länstrafiken i Västerbotten +48FFE71294A0 +E3429281EFC1 +16F21A82EC84 +460722122510 # # 3dprinter -AAFB06045877 # EPI Envisionte# 3dprinter +# EPI Envisionte# 3dprinter +AAFB06045877 # # gym -3e65e4fb65b3 # Fysiken A -25094df6f148 # Fysiken B -a05dbd98e0fc # CleverFit -AA4DDA458EBB # GoFit -EAB8066C7479 # GoFit +# Fysiken A +3E65E4FB65B3 # -d3b595e9dd63 # Hotel KeyCard -afbecd121004 # Hotel KeyCard -6471a5ef2d1a # SimonsVoss +# Fysiken B +25094DF6F148 +# +# CleverFit +A05DBD98E0FC +# +# GoFit +AA4DDA458EBB +EAB8066C7479 +# +# Hotel KeyCard +D3B595E9DD63 +AFBECD121004 +# +# SimonsVoss +6471A5EF2D1A # # ID06 4E3552426B32 @@ -208,7 +298,7 @@ D21762B2DE3B 0E83A374B513 1F1FFE000000 A10F303FC879 -1322285230b8 +1322285230B8 0C71BCFB7E72 C3C88C6340B8 F101622750B7 @@ -217,8 +307,8 @@ F101622750B7 7C335FB121B5 B39AE17435DC # -# -454841585443 # key A +# key A +454841585443 # # Data from http://pastebin.com/gQ6nk38G D39BB83F5297 @@ -251,45 +341,45 @@ A0B0C0D0E0F0 A1B1C1D1E1F1 # # Data from msk social -2735fc181807 -2aba9519f574 -84fd7f7a12b6 -186d8c4b93f9 -3a4bba8adaf0 -8765b17968a2 -40ead80721ce -0db5e6523f7c -51119dae5216 -83e3549ce42d -136bdb246cac -7de02a7f6025 -bf23a53c1f63 -cb9a1f2d7368 -c7c0adb3284f -9f131d8c2057 -67362d90f973 -6202a38f69e2 -100533b89331 -653a87594079 -d8a274b2e026 -b20b83cb145c -9afa6cb4fc3d -a229e68ad9e5 -49c2b5296ef4 +2735FC181807 +2ABA9519F574 +84FD7F7A12B6 +186D8C4B93F9 +3A4BBA8ADAF0 +8765B17968A2 +40EAD80721CE +0DB5E6523F7C +51119DAE5216 +83E3549CE42D +136BDB246CAC +7DE02A7F6025 +BF23A53C1F63 +CB9A1F2D7368 +C7C0ADB3284F +9F131D8C2057 +67362D90F973 +6202A38F69E2 +100533B89331 +653A87594079 +D8A274B2E026 +B20B83CB145C +9AFA6CB4FC3D +A229E68AD9E5 +49C2B5296EF4 # # Data from http://pastebin.com/RRJUEDCM -0d258fe90296 -e55a3ca71826 -a4f204203f56 -eeb420209d0c -911e52fd7ce4 -752fbb5b7b45 -66b03aca6ee9 -48734389edc3 -17193709adf4 -1acc3189578c -c2b7ec7d4eb1 -369a4663acd2 +0D258FE90296 +E55A3CA71826 +A4F204203F56 +EEB420209D0C +911E52FD7CE4 +752FBB5B7B45 +66B03ACA6EE9 +48734389EDC3 +17193709ADF4 +1ACC3189578C +C2B7EC7D4EB1 +369A4663ACD2 # # Data from https://github.com/zhangjingye03/zxcardumper # zxcard Key A/B @@ -297,59 +387,29 @@ c2b7ec7d4eb1 003003003003 # # Data from http://phreakerclub.com/forum/showthread.php?p=41266 -26973ea74321 -71f3a315ad26 -51044efb5aab -ac70ca327a04 -eb0a8ff88ade +26973EA74321 +71F3A315AD26 +51044EFB5AAB +AC70CA327A04 +EB0A8FF88ADE # # Transport system Metromoney -2803bcb0c7e1 -9c616585e26d -4fa9eb49f75e -2dade48942c5 -a160fcd5ec4c +2803BCB0C7E1 +9C616585E26D +4FA9EB49F75E +2DADE48942C5 +A160FCD5EC4C 112233445566 -361a62f35bc9 -# +361A62F35BC9 +# # Transport system Spain -83f3cb98c258 -070d486bc555 -a9b018868cc1 -9dcdb136110c -749934cc8ed3 -506db955f161 -f088a85e71d7 -72b458d60363 -70c714869dc7 -b32464412ee3 -f253c30568c4 -1c68315674ac -cfe63749080a -c1e6f8afc9ec -dd0de3ba08a6 -3d923eb73534 -ff94f86b09a6 -d61707ffdfb1 -8223205047b6 -9951a273dee7 -c9449301af93 -66695a45c9fa -89aa9d743812 -c41514defc07 -c52876869800 -5353b3aecb53 -2e4169a5c79d -4bb747e48c2a -6285a1c8eb5c -5145c34dba19 -25352912cd8d -81b20c274c3f 00B70875AF1D 04B787B2F3A5 05412723F1B6 05C301C8795A 066F5AF3CCEE +070D486BC555 +070d486bc555 0A1B6C50E04E 0AD0956DF6EE 0AD6B7E37183 @@ -359,154 +419,230 @@ c52876869800 18E887D625B4 1ABC15934F5A 1AF66F83F5BE +1C68315674AC +1c68315674ac +25352912CD8D +25352912cd8d 260480290483 2900AAC52BC3 2910AFE15C99 +2E4169A5C79D +2e4169a5c79d 374521A38BCC 3A4C47757B07 3A524B7A7B37 3C4ABB877EAF +3D923EB73534 +3d923eb73534 3F3A534B7B7B 4B787B273A50 4B92DF1BF25D +4BB747E48C2A +4bb747e48c2a 4F0E4AE8051A +506DB955F161 +506db955f161 +5145C34DBA19 +5145c34dba19 514B797B2F3A 529CF51F05C5 52B26C199862 +5353B3AECB53 +5353b3aecb53 57A18BFEC381 5A7D87876EA8 +6285A1C8EB5C +6285a1c8eb5c 64CBADC7A313 65B6C3200736 +66695A45C9FA +66695a45c9fa 67B1B3A4E497 6B0454D5D3C3 6B3B7AF45777 6C273F431564 702C1BF025DD +70C714869DC7 +70c714869dc7 +72B458D60363 +72b458d60363 738385948494 +749934CC8ED3 +749934cc8ed3 76E450094393 777B1F3A4F4A 7B173A4E4976 81504133B13C +81B20C274C3F +81b20c274c3f +8223205047B6 +8223205047b6 826576A1AB68 +83F3CB98C258 +83f3cb98c258 +89AA9D743812 +89aa9d743812 8A55194F6587 8DFACF11E778 8FD6D76742DC +9951A273DEE7 +9951a273dee7 9AFEE1F65742 9D56D83658AC +9DCDB136110C +9dcdb136110c 9FAC23197904 A1AB3A08712C A514B797B373 A58AB5619631 A5BB18152EF1 A777B233A4F4 +A9B018868CC1 +a9b018868cc1 AB19BC885A29 AB91BDA25F00 AE98BA1E6F2C B133A4D48757 +B32464412EE3 +b32464412ee3 B3A4C47757B0 B6803136F5AF B793ADA6DB0C B95BFDEBA7E4 C0AA2BBD27CD +C1E6F8AFC9EC +c1e6f8afc9ec C27F5C1A9C2B +C41514DEFC07 +c41514defc07 +C52876869800 +c52876869800 +C9449301AF93 +c9449301af93 C9BE49675FE4 CCCE24102003 CDE668FDCDBA +CFE63749080A +cfe63749080a D23A31A4AAB9 +D61707FFDFB1 +d61707ffdfb1 +DD0DE3BA08A6 +dd0de3ba08a6 DEDD7688BC38 E9AE90885C39 +F088A85E71D7 +f088a85e71d7 F0A3C5182007 +F253C30568C4 +f253c30568c4 F3A524B7A7B3 +FF94F86B09A6 +ff94f86b09a6 # # Data from mall -abba1234fcb0 # playland balikesir -314f495254ff # A trio bowling bahcelievler -4152414b4e41 # A trio bowling bahcelievler -4E474434FFFF # karinca park nigde +# playland balikesir +ABBA1234FCB0 +# +# A trio bowling bahcelievler +314F495254FF +4152414B4E41 +# +# karinca park nigde +4E474434FFFF +# +# hotel system +537930363139 # # Data from https://github.com/RadioWar/NFCGUI -44dd5a385aaf -21a600056cb0 -b1aca33180a5 -dd61eb6bce22 -1565a172770f -3e84d2612e2a -f23442436765 -79674f96c771 -87df99d496cb -c5132c8980bc -a21680c27773 -f26e21edcee2 -675557ecc92e -f4396e468114 -6db17c16b35b -4186562a5bb2 -2feae851c199 -db1a3338b2eb -157b10d84c6b -a643f952ea57 -df37dcb6afb3 -4c32baf326e0 -91ce16c07ac5 -3c5d1c2bcd18 -c3f19ec592a2 -f72a29005459 -185fa3438949 -321a695bd266 -d327083a60a7 -45635ef66ef3 -5481986d2d62 -cba6ae869ad5 -645a166b1eeb -a7abbc77cc9e -f792c4c76a5c -bfb6796a11db +44DD5A385AAF +21A600056CB0 +B1ACA33180A5 +DD61EB6BCE22 +1565A172770F +3E84D2612E2A +F23442436765 +79674F96C771 +87DF99D496CB +C5132C8980BC +A21680C27773 +F26E21EDCEE2 +675557ECC92E +F4396E468114 +6DB17C16B35B +4186562A5BB2 +2FEAE851C199 +DB1A3338B2EB +157B10D84C6B +A643F952EA57 +DF37DCB6AFB3 +4C32BAF326E0 +91CE16C07AC5 +3C5D1C2BCD18 +C3F19EC592A2 +F72A29005459 +185FA3438949 +321A695BD266 +D327083A60A7 +45635EF66EF3 +5481986D2D62 +CBA6AE869AD5 +645A166B1EEB +A7ABBC77CC9E +F792C4C76A5C +BFB6796A11DB # # Data from Salto A/B 6A1987C40A21 7F33625BC129 # # Data from forum -2338b4913111 +2338B4913111 # # Data from stoye -cb779c50e1bd -a27d3804c259 -003cc420001a -f9861526130f -381ece050fbd -a57186bdd2b9 -48c739e21a04 -36abf5874ed7 -649d2abbbd20 -bbe8fffcf363 -ab4e7045e97d -340e40f81cd8 -e4f65c0ef32c -d2a597d76936 -a920f32fe93a -86afd95200f7 -9b832a9881ff -26643965b16e -0c669993c776 -b468d1991af9 -d9a37831dce5 -2fc1f32f51b1 -0ffbf65b5a14 -c5cfe06d9ea3 -c0dece673829 +CB779C50E1BD +A27D3804C259 +003CC420001A +F9861526130F +381ECE050FBD +A57186BDD2B9 +48C739E21A04 +36ABF5874ED7 +649D2ABBBD20 +BBE8FFFCF363 +AB4E7045E97D +340E40F81CD8 +E4F65C0EF32C +D2A597D76936 +A920F32FE93A +86AFD95200F7 +9B832A9881FF +26643965B16E +0C669993C776 +B468D1991AF9 +D9A37831DCE5 +2FC1F32F51B1 +0FFBF65B5A14 +C5CFE06D9EA3 +C0DECE673829 # -a56c2df9a26d +# +A56C2DF9A26D # # Data from https://pastebin.com/vbwast74 +68D3F7307C89 # -68d3f7307c89 -568c9083f71c # Smart Rider. Western Australian Public Transport Cards +# Smart Rider. Western Australian Public Transport Cards +568C9083F71C # +# Bangkok metro key +97F5DA640B18 # -97F5DA640B18 # Bangkok metro key -A8844B0BCA06 # Metro Valencia key -857464D3AAD1 # HTC Eindhoven key +# Metro Valencia key +A8844B0BCA06 +# +# HTC Eindhoven key +857464D3AAD1 # # Vigik Keys # Various sources : @@ -515,95 +651,108 @@ A8844B0BCA06 # Metro Valencia key # * Own dumps # # French VIGIK -314B49474956 # VIGIK1 A -564c505f4d41 # VIGIK1 B -ba5b895da162 # VIGIK1 B +# VIGIK1 A +314B49474956 +# +# VIGIK1 B +564C505F4D41 +BA5B895DA162 # # Vigik mystery Keys Mifare 1k EV1 (S50) -5c8ff9990da2 # 16 A -75ccb59c9bed # 17 A -d01afeeb890a # 16 B -4b791bea7bcc # 17 B +# 16 A +5C8FF9990DA2 # -021209197591 # BTCINO UNDETERMINED SPREAKD 0x01->0x13 key -2ef720f2af76 -414c41524f4e -424c41524f4e -4a6352684677 -bf1f4424af76 -536653644c65 +# 17 A +75CCB59C9BED +# +# 16 B +D01AFEEB890A +# +# 17 B +4B791BEA7BCC +# +# BTCINO UNDETERMINED SPREAKD 0x01->0x13 key +021209197591 +# +# +2EF720F2AF76 +414C41524F4E +424C41524F4E +4A6352684677 +BF1F4424AF76 +536653644C65 # # Intratone Cogelec # Data from http://bouzdeck.com/rfid/32-cloning-a-mifare-classic-1k-tag.html 484558414354 -a22ae129c013 -49fae4e3849f -38fcf33072e0 -8ad5517b4b18 -509359f131b1 -6c78928e1317 -aa0720018738 -a6cac2886412 -62d0c424ed8e -e64a986a5d94 -8fa1d601d0a2 -89347350bd36 -66d2b7dc39ef -6bc1e1ae547d -22729a9bd40f +A22AE129C013 +49FAE4E3849F +38FCF33072E0 +8AD5517B4B18 +509359F131B1 +6C78928E1317 +AA0720018738 +A6CAC2886412 +62D0C424ED8E +E64A986A5D94 +8FA1D601D0A2 +89347350BD36 +66D2B7DC39EF +6BC1E1AE547D +22729A9BD40F # # Data from https://dfir.lu/blog/cloning-a-mifare-classic-1k-tag.html -925b158f796f -fad63ecb5891 -bba840ba1c57 -cc6b3b3cd263 -6245e47352e6 -8ed41e8b8056 -2dd39a54e1f3 -6d4c5b3658d2 -1877ed29435a -52264716efde -961c0db4a7ed -703140fd6d86 -157c9a513fa5 -e2a5dc8e066f +925B158F796F +FAD63ECB5891 +BBA840BA1C57 +CC6B3B3CD263 +6245E47352E6 +8ED41E8B8056 +2DD39A54E1F3 +6D4C5B3658D2 +1877ED29435A +52264716EFDE +961C0DB4A7ED +703140FD6D86 +157C9A513FA5 +E2A5DC8E066F # # Data from forum, schlage 9691T fob -ef1232ab18a0 +EF1232AB18A0 # # Data from a oyster card -374bf468607f -bfc8e353af63 -15cafd6159f6 -62efd80ab715 -987a7f7f1a35 -c4104fa3c526 -4c961f23e6be -67546972bc69 -f4cd5d4c13ff -94414c1a07dc -16551d52fd20 -9cb290282f7d -77a84170b574 -ed646c83a4f3 -e703589db50b -513c85d06cde -95093f0b2e22 -543b01b27a95 -c6d375b99972 -ee4cc572b40e -5106ca7e4a69 -c96bd1ce607f -167a1be102e0 -a8d0d850a606 -a2abb693ce34 -7b296c40c486 -91f93a5564c9 -e10623e7a016 -b725f9cbf183 +374BF468607F +BFC8E353AF63 +15CAFD6159F6 +62EFD80AB715 +987A7F7F1A35 +C4104FA3C526 +4C961F23E6BE +67546972BC69 +F4CD5D4C13FF +94414C1A07DC +16551D52FD20 +9CB290282F7D +77A84170B574 +ED646C83A4F3 +E703589DB50B +513C85D06CDE +95093F0B2E22 +543B01B27A95 +C6D375B99972 +EE4CC572B40E +5106CA7E4A69 +C96BD1CE607F +167A1BE102E0 +A8D0D850A606 +A2ABB693CE34 +7B296C40C486 +91F93A5564C9 +E10623E7A016 +B725F9CBF183 # # Data from FDi tag -8829da9daf76 +8829DA9DAF76 # # Data from GitHub issue 0A7932DC7E65 @@ -615,7 +764,6 @@ b725f9cbf183 11428B5BCE0F 18971D893494 25D60050BF6E -3FA7217EC575 44F0B5FBE344 7B296F353C6B 8553263F4FF0 @@ -629,17 +777,20 @@ D4FE03CE5B09 D4FE03CE5B0A D4FE03CE5B0F E241E8AFCBAF +# Transport system Argentina - SUBE +# Shared key - sec 3 blk 15 +3FA7217EC575 # # Data from forum post 123F8888F322 050908080008 # # Data from hoist -4f9f59c9c875 +4F9F59C9C875 # # Data from pastebin -66f3ed00fed7 -f7a39753d018 +66F3ED00FED7 +F7A39753D018 # # Data from https://pastebin.com/Z7pEeZif 386B4D634A65 @@ -674,28 +825,30 @@ f7a39753d018 77646B633657 # # Data from TransPert -2031d1e57a3b -53c11f90822a -9189449ea24e +2031D1E57A3B +53C11F90822A +9189449EA24E # # data from Github -410b9b40b872 -2cb1a90071c8 +410B9B40B872 +2CB1A90071C8 +# # -# data from 8697389ACA26 1AB23CD45EF6 013889343891 # # -0000000018de -16ddcb6b3f24 +0000000018DE +16DDCB6B3F24 # # Data from https://pastebin.com/vwDRZW7d -EC0A9B1A9E06 # Vingcard Mifare 4k Staff card -6C94E1CED026 # Vingcard Mifare 4k Staff card -0F230695923F # Vingcard Mifare 4k Staff card -0000014B5C31 # Vingcard Mifare 4k Staff card +# Vingcard Mifare 4k Staff card +EC0A9B1A9E06 +6C94E1CED026 +0F230695923F +0000014B5C31 +# # BEDB604CC9D1 B8A1F613CF3D @@ -705,91 +858,108 @@ B66AC040203A 2E641D99AD5B AD4FB33388BF 69FB7B7CD8EE -2A6D9205E7CA # Hotel -2a2c13cc242a +# +# Hotel +2A6D9205E7CA +13B91C226E56 +# +# KABA Hotel Locks +2A2C13CC242A +# +# 27FBC86A00D0 01FA3FC68349 # -13B91C226E56 # Hotel +# Smart Rider. Western Australian Public Transport Cards +6D44B5AAF464 +1717E34A7A8A # -6D44B5AAF464 # Smart Rider. Western Australian Public Transport Cards -1717E34A7A8A # Smart Rider. Western Australian Public Transport Cards +# RFIDeas +6B6579737472 # -6B6579737472 # RFIDeas +# HID MIFARE Classic 1k Key +484944204953 +204752454154 +# HID MIFARE SO +3B7E4FD575AD +11496F97752A # -484944204953 # HID MIFARE Classic 1k Key -204752454154 # HID MIFARE Classic 1k Key -3B7E4FD575AD # HID MIFARE SO -11496F97752A # HID MIFARE SO +# Luxeo/Aztek cashless vending +415A54454B4D # -415A54454B4D # Luxeo/Aztek cashless vending +# BQT +321958042333 # -321958042333 # BQT +# Aperio KEY_A Sector 1, 12, 13, 14, 15 Data Start 0 Length 48 +160A91D29A9C # -160A91D29A9C # Aperio KEY_A Sector 1, 12, 13, 14, 15 Data Start 0 Length 48 -# -b7bf0c13066e # Gallagher +# Gallagher +B7BF0C13066E # # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) 009FB42D98ED 002E626E2820 # # Boston, MA, USA Transit - MBTA Charlie Card -3060206f5b0a # charlie -5ec39b022f2b # charlie -3a09594c8587 # charlie -f1b9f5669cc8 # charlie -f662248e7e89 # charlie -62387b8d250d # charlie -f238d78ff48f # charlie -9dc282d46217 # charlie -afd0ba94d624 # charlie -92ee4dc87191 # charlie -b35a0e4acc09 # charlie -756ef55e2507 # charlie -447ab7fd5a6b # charlie -932b9cb730ef # charlie -1f1a0a111b5b # charlie -ad9e0a1ca2f7 # charlie -d58023ba2bdc # charlie -62ced42a6d87 # charlie -2548a443df28 # charlie -2ed3b15e7c0f # charlie -f66224ee1e89 # charlie +3060206F5B0A +5EC39B022F2B +3A09594C8587 +F1B9F5669CC8 +F662248E7E89 +62387B8D250D +F238D78FF48F +9DC282D46217 +AFD0BA94D624 +92EE4DC87191 +B35A0E4ACC09 +756EF55E2507 +447AB7FD5A6B +932B9CB730EF +1F1A0A111B5B +AD9E0A1CA2F7 +D58023BA2BDC +62CED42A6D87 +2548A443DF28 +2ED3B15E7C0F +F66224EE1E89 # -60012e9ba3fa # -de1fcbec764b -81bfbe8cacba -bff123126c9b -2f47741062a0 -b4166b0a27ea -a170d9b59f95 -400bc9be8976 -d80511fc2ab4 -1fcef3005bcf -bb467463acd6 -e67c8010502d -ff58ba1b4478 +60012E9BA3FA +# +# +DE1FCBEC764B +81BFBE8CACBA +BFF123126C9B +2F47741062A0 +B4166B0A27EA +A170D9B59F95 +400BC9BE8976 +D80511FC2AB4 +1FCEF3005BCF +BB467463ACD6 +E67C8010502D +FF58BA1B4478 +# # Data from https://pastebin.com/Kz8xp4ev -fbf225dc5d58 +FBF225DC5D58 # # Data https://pastebin.com/BEm6bdAE # vingcard.txt -4708111c8604 -3d50d902ea48 -96a301bce267 -6700f10fec09 -7a09cc1db70a -560f7cff2d81 -66b31e64ca4b -9e53491f685b -3a09911d860c -8a036920ac0c -361f69d2c462 -d9bcde7fc489 -0c03a720f208 -6018522fac02 +# Note: most likely diversified +4708111C8604 +3D50D902EA48 +96A301BCE267 +6700F10FEC09 +7A09CC1DB70A +560F7CFF2D81 +66B31E64CA4B +9E53491F685B +3A09911D860C +8A036920AC0C +361F69D2C462 +D9BCDE7FC489 +0C03A720F208 +6018522FAC02 # # Data from https://pastebin.com/4t2yFMgt # Mifare technische Universität Graz TUG @@ -797,10 +967,13 @@ D58660D1ACDE 50A11381502C C01FC822C6E5 0854BF31111E -# More keys: -8a19d40cf2b5 -ae8587108640 -135b88a94b8b # SafLock standalone door locks. +# +# More keys +8A19D40CF2B5 +AE8587108640 +# +# SafLock standalone door locks +135B88A94B8B # # Russian Troika card 08B386463229 @@ -858,7 +1031,7 @@ F8493407799D 6B8BD9860763 D3A297DC2698 # -# Keys from MifareClassicTool project +# Keys from Mifare Classic Tool project 044CE1872BC3 045CECA15535 0BE5FAC8B06A @@ -910,12 +1083,11 @@ E56AC127DD45 EA0FD73CB149 FC0001877BF7 FD8705E721B0 -00ada2cd516d +00ADA2CD516D # # -## -237a4d0d9119 -0ed7846c2bc9 +237A4D0D9119 +0ED7846C2BC9 FFFFD06F83E3 FFFFAE82366C F89C86B2A961 @@ -960,15 +1132,17 @@ A2B2C9D187FB # Hotel Adina 9EBC3EB37130 # -# most likely diversed individual keys. +# most likely diversifed individual keys. # data from https://github.com/korsehindi/proxmark3/commit/24fdbfa9a1d5c996aaa5c192bc07e4ab28db4c5c 491CDC863104 A2F63A485632 98631ED2B229 19F1FFE02563 -563A22C01FC8 # Argentina -43CA22C13091 # Argentina -25094DF2C1BD # Argentina +# +# Argentina +563A22C01FC8 +43CA22C13091 +25094DF2C1BD # # OMNITEC.ES HOTEL TIMECARD / MAINTENANCECARD AFBECD120454 @@ -977,7 +1151,6 @@ AFBECD120454 842146108088 # # TAPCARD PUBLIC TRANSPORT LA -# EA1B88DF0A76 D1991E71E2C5 05F89678CFCF @@ -1012,7 +1185,6 @@ E3AD9E9BA5D4 6C9EC046C1A4 # # ROC HIGHSCHOOL ACCESSCARD -# B021669B44BB B18CDCDE52B7 A22647F422AE @@ -1044,8 +1216,7 @@ BE7C4F6C7A9A 5EC7938F140A 82D58AA49CCB # -# MELONCARD -# +# MELON CARD 323334353637 # # @@ -1065,42 +1236,40 @@ A7FB4824ACBF 10F3BEBC01DF # # Transportes Insular La Palma -# -0172066b2f03 -0000085f0000 -1a80b93f7107 -70172066b2f0 -b1a80c94f710 -0b0172066b2f -0f1a81c95071 -f0f0172066b2 -1131a81d9507 -2f130172066b -71171a82d951 -b2f170172066 -1711b1a82e96 -6b2f1b017206 -62711f1a83e9 -66b2f1f01720 -97271231a83f -066b2f230172 -f97371271a84 -2066b2f27017 -50983712b1a8 -72066b2f2b01 -850984712f1a -172066b2f2f0 -a85198481331 -0172066b2f33 -1a8619858137 -70172066b2f3 -b1a862985913 -3b0172066b2f -3f1a87298691 -f3f0172066b2 +0172066B2F03 +0000085F0000 +1A80B93F7107 +70172066B2F0 +B1A80C94F710 +0B0172066B2F +0F1A81C95071 +F0F0172066B2 +1131A81D9507 +2F130172066B +71171A82D951 +B2F170172066 +1711B1A82E96 +6B2F1B017206 +62711F1A83E9 +66B2F1F01720 +97271231A83F +066B2F230172 +F97371271A84 +2066B2F27017 +50983712B1A8 +72066B2F2B01 +850984712F1A +172066B2F2F0 +A85198481331 +0172066B2F33 +1A8619858137 +70172066B2F3 +B1A862985913 +3B0172066B2F +3F1A87298691 +F3F0172066B2 # # Tehran ezpay -# 38A88AEC1C43 CBD2568BC7C6 7BCB4774EC8F @@ -1113,15 +1282,14 @@ EEC0626B01A1 D3B1C7EA5C53 41C82D231497 0B8B21C692C2 -604Ac8D87C7E +604AC8D87C7E 8E7B29460F12 BB3D7B11D224 # # Chaco -# -b210cfa436d2 -b8b1cfa646a8 -a9f95891f0a4 +B210CFA436D2 +B8B1CFA646A8 +A9F95891F0A4 # # Keys from APK application "Scan Badge" 4A4C474F524D @@ -1141,167 +1309,168 @@ A0004A000036 4243414F5250 DFE73BE48AC6 # +# B069D0D03D17 000131B93F28 # # From the DFW Area, TX, USA -# -a506370e7c0f -26396f2042e7 -70758fdd31e0 -9f9d8eeddcce -06ff5f03aa1a -4098653289d3 -904735f00f9e -b4c36c79da8d -68f9a1f0b424 -5a85536395b3 -7dd399d4e897 -ef4c5a7ac6fc -b47058139187 -8268046cd154 -67cc03b7d577 +A506370E7C0F +26396F2042E7 +70758FDD31E0 +9F9D8EEDDCCE +06FF5F03AA1A +4098653289D3 +904735F00F9E +B4C36C79DA8D +68F9A1F0B424 +5A85536395B3 +7DD399D4E897 +EF4C5A7AC6FC +B47058139187 +8268046CD154 +67CC03B7D577 # # From the HTL Mödling, NÖ, AT -# -a5524645cd91 -d964406e67b4 -99858a49c119 -7b7e752b6a2d -c27d999912ea -66a163ba82b4 -4c60f4b15ba8 +A5524645CD91 +D964406E67B4 +99858A49C119 +7B7E752B6A2D +C27D999912EA +66A163BA82B4 +4C60F4B15BA8 # # CAFE + CO, AT -# -35d850d10a24 -4b511f4d28dd -e45230e7a9e8 -535f47d35e39 -fb6c88b7e279 +35D850D10A24 +4B511F4D28DD +E45230E7A9E8 +535F47D35E39 +FB6C88B7E279 # # Metro Card, AT -# 223C3427108A # # Unknown, AT -# -23d4cdff8da3 -e6849fcc324b -12fd3a94df0e +23D4CDFF8DA3 +E6849FCC324B +12FD3A94DF0E # # Unknown, AT +0B83797A9C64 +39AD2963D3D1 # -0b83797a9c64 -39ad2963d3d1 -34b16cd59ff8 # Hotel Berlin Classic room A KEY -bb2c0007d022 # Hotel Berlin Classic room B KEY +# Hotel Berlin Classic room A KEY +34B16CD59FF8 +# +# Hotel Berlin Classic room B KEY +BB2C0007D022 # # Coinmatic laundry Smart card # data from: https://pastebin.com/XZQiLtUf -# -0734bfb93dab -85a438f72a8a +0734BFB93DAB +85A438F72A8A # # Data from forum, Chinese hotel -58ac17bf3629 -b62307b62307 +58AC17BF3629 +B62307B62307 # -a2a3cca2a3cc +# +A2A3CCA2A3CC # # Granada, ES Transport Card 000000270000 -0f385ffb6529 -29173860fc76 -2fca8492f386 -385efa542907 -3864fcba5937 -3f3865fccb69 -6291b3860fc8 -63fca9492f38 -863fcb959373 -87291f3861fc -913385ffb752 -b385efa64290 -c9739233861f -f3864fcca693 -fc9839273862 +0F385FFB6529 +29173860FC76 +2FCA8492F386 +385EFA542907 +3864FCBA5937 +3F3865FCCB69 +6291B3860FC8 +63FCA9492F38 +863FCB959373 +87291F3861FC +913385FFB752 +B385EFA64290 +C9739233861F +F3864FCCA693 +FC9839273862 # # various hotel keys 34D3C568B348 91FF18E63887 4D8B8B95FDEE 354A787087F1 -4a306e62e9b6 +4A306E62E9B6 B9C874AE63D0 # # Data from official repo -f00dfeedd0d0 -0bb31dc123e5 -7578bf2c66a9 -cd212889c3ed -6936c035ae1b -c6c866aa421e -590bd659cdd2 -aa734d2f40e0 -09800ff94aaf -5a12f83326e7 -c554ef6a6015 -0d8ca561bdf3 -b8937130b6ba -d7744a1a0c44 -82908b57ef4f -fe04ecfe5577 -# comfort inn hotel -4d57414c5648 -4d48414c5648 -# -# unknown hotel key -6d9b485a4845 +F00DFEEDD0D0 +0BB31DC123E5 +7578BF2C66A9 +CD212889C3ED +6936C035AE1B +C6C866AA421E +590BD659CDD2 +AA734D2F40E0 +09800FF94AAF +5A12F83326E7 +C554EF6A6015 +0D8CA561BDF3 +B8937130B6BA +D7744A1A0C44 +82908B57EF4F +FE04ECFE5577 # -5a7a52d5e20d # Bosch Solution 6000 +# comfort inn hotel +4D57414C5648 +4D48414C5648 +# +# unknown hotel key +6D9B485A4845 +# +# Bosch Solution 6000 +5A7A52D5E20D # # Found in TagInfo app -C1E51C63B8F5 # RATB key +# RATB key +C1E51C63B8F5 1DB710648A65 -18F34C92A56E # E-GO card key +# E-GO card key +18F34C92A56E # # Library Card MFP - SL1 -4a832584637d -ca679d6291b0 -30d9690fc5bc -5296c26109d4 -e77952748484 -91c2376005a1 -30b7680b2bc9 -e2a9e88bfe16 -43b04995d234 -aade86b1f9c1 -5ea088c824c9 -c67beb41ffbf -b84d52971107 -52b0d3f6116e +4A832584637D +CA679D6291B0 +30D9690FC5BC +5296C26109D4 +E77952748484 +91C2376005A1 +30B7680B2BC9 +E2A9E88BFE16 +43B04995D234 +AADE86B1F9C1 +5EA088C824C9 +C67BEB41FFBF +B84D52971107 +52B0D3F6116E # # Data from https://pastebin.com/cLSQQ9xN -ca3a24669d45 -4087c6a75a96 -403f09848b87 -d73438698eea -5f31f6fcd3a0 -a0974382c4c5 -a82045a10949 +CA3A24669D45 +4087C6A75A96 +403F09848B87 +D73438698EEA +5F31F6FCD3A0 +A0974382C4C5 +A82045A10949 # # Data from https://pastebin.com/2iV8h93h # # funnivarium # forum ankara -# 2602FFFFFFFF # # macera adasi # ankara kentpark # INACTIVE -# 0A4600FF00FF DFF293979FA7 4D6F62692E45 @@ -1310,21 +1479,17 @@ DFF293979FA7 # petrol ofisi # positive card # ode-gec -# 0406080A0C0E # # konya elkart -# 988ACDECDFB0 120D00FFFFFF # # bowlingo # serdivan avym -# 4AE23A562A80 # -# kart54 -# +# kart 54 2AFFD6F88B97 A9F3F289B70C DB6819558A25 @@ -1335,20 +1500,16 @@ B16B2E573235 # # crazy park # kizilay avm -# 00DD300F4F10 # # kartsistem B -# FEE2A3FBC5B6 # # toru ent # taurus avm -# 005078565703 # # Ving? -# 0602721E8F06 FC0B50AF8700 F7BA51A9434E @@ -1396,7 +1557,6 @@ D0DDDF2933EC # # bursakart # bursa transport card -# 755D49191A78 DAC7E0CBA8FD 68D3263A8CD6 @@ -1407,31 +1567,30 @@ B2FE3B2875A6 # # playland # maltepe park -# ABCC1276FCB0 AABAFFCC7612 # # lunasan # kocaeli fair -# 26107E7006A0 # # gamefactory # ozdilek -# 17D071403C20 # +# 534F4C415249 -534f4c303232 +534F4C303232 # # Nespresso, smart card -# key-gen algo, these keys are for one card -ff9a84635bd2 -6f30126ee7e4 -6039abb101bb -f1a1239a4487 +# key-gen algo, these keys are for one card (keys diversified) +FF9A84635BD2 +6F30126EE7E4 +6039ABB101BB +F1A1239A4487 # -b882fd4a9f78 +# +B882FD4A9F78 CD7FFFF81C4A AA0857C641A3 C8AACD7CF3D1 @@ -1443,25 +1602,25 @@ A7395CCB42A0 D14E615E0545 69D92108C8B5 703265497350 -D75971531042 -10510049D725 -35C649004000 +D75971531042 +10510049D725 +35C649004000 5B0C7EC83645 -05F5EC05133C -521B517352C7 -94B6A644DFF6 -2CA4A4D68B8E -A7765C952DDF -E2F14D0A0E28 -DC018FC1D126 -4927C97F1D57 -046154274C11 -155332417E00 -6B13935CD550 -C151D998C669 -D973D917A4C7 -130662240200 -9386E2A48280 +05F5EC05133C +521B517352C7 +94B6A644DFF6 +2CA4A4D68B8E +A7765C952DDF +E2F14D0A0E28 +DC018FC1D126 +4927C97F1D57 +046154274C11 +155332417E00 +6B13935CD550 +C151D998C669 +D973D917A4C7 +130662240200 +9386E2A48280 52750A0E592A 075D1A4DD323 32CA52054416 @@ -1471,54 +1630,57 @@ D973D917A4C7 1F0128447C00 411053C05273 42454C4C4147 -C428C4550A75 -730956C72BC2 -28D70900734C -4F75030AD12B -6307417353C1 +C428C4550A75 +730956C72BC2 +28D70900734C +4F75030AD12B +6307417353C1 D65561530174 D1F71E05AD9D -F7FA2F629BB1 -0E620691B9FE -43E69C28F08C -735175696421 -424C0FFBF657 +F7FA2F629BB1 +0E620691B9FE +43E69C28F08C +735175696421 +424C0FFBF657 51E97FFF51E9 -E7316853E731 -00460740D722 -35D152154017 -5D0762D13401 -0F35D5660653 -1170553E4304 -0C4233587119 -F678905568C3 -50240A68D1D8 -2E71D3BD262A -540D5E6355CC -D1417E431949 +E7316853E731 +00460740D722 +35D152154017 +5D0762D13401 +0F35D5660653 +1170553E4304 +0C4233587119 +F678905568C3 +50240A68D1D8 +2E71D3BD262A +540D5E6355CC +D1417E431949 4BF6DE347FB6 # # -3a471b2192bf -a297ceb7d34b -ae76242931f1 +3A471B2192BF +A297CEB7D34B +AE76242931F1 # # 124578ABFEDC ABFEDC124578 4578ABFEDC12 # -# Data from +# Data from # premier inn hotel chain -5e594208ef02 -af9e38d36582 +5E594208EF02 +AF9E38D36582 # -#Norwegian building site identication card. (HMS KORT) -10DF4D1859C8 # Key a -B5244E79B0C8 # Key B +# Norwegian building site identication card. (HMS KORT) +# Key a +10DF4D1859C8 +# +# Key B +B5244E79B0C8 # # Ukraine hotel -f5c1c4c5de34 +F5C1C4C5DE34 # # Data from Mifare Classic Tool repo # Rotterdam University of applied sciences campus card @@ -1546,7 +1708,7 @@ B290401B0CAD AD11006B0601 # # Data from Mifare Classic Tool repo -# Armenian Metro +# Armenian Metro E4410EF8ED2D 6A68A7D83E11 0D6057E8133B @@ -1557,24 +1719,20 @@ D3F3B958B8A3 CE99FBC8BD26 # # keys from Eurothermes group (Switzerland) -d66d91829013 -75b691829013 -83e391829013 -a23c91829013 -e46a91829013 -d9e091829013 -fed791829013 -155f91829013 -06cc91829013 -8ddc91829013 -54af91829013 -29a791829013 +D66D91829013 +75B691829013 +83E391829013 +A23C91829013 +E46A91829013 +D9E091829013 +FED791829013 +155F91829013 +06CC91829013 +8DDC91829013 +54AF91829013 +29A791829013 668091829013 -00008627c10a -# -# keys from NSP Manchester University UK Accomodation Staff and students -199404281970 -199404281998 +00008627C10A # # easycard 310D51E539CA @@ -1585,7 +1743,6 @@ F53E9F4114A9 AD38C17DE7D2 # # SUBE cards keys (new) -# 2DEB57A3EA8F 32C1BB023F87 70E3AD3F2D29 @@ -1614,14 +1771,11 @@ F5C1B3F62FDA 7E6545076619 # # SUBE cards keys (old) -# -7B296F353C6B 4C5A766DFE3A 32C6768847F5 F68930789631 8B42B6D64B02 B627A3CB13F8 -3FA7217EC575 562A4FB8260B 88DDC24E1671 91CB7802A559 @@ -1644,8 +1798,9 @@ BFE25035B0C8 D5C172325DD3 992B152E834A CE75D7EADEAF - -# Russian Podorozhnik card (Saint-Petersburg transport, may be combined with Troika) +# +# Russian Podorozhnik card (Saint-Petersburg transport) +# may be combined with Troika 038B5F9B5A2A 04DC35277635 0C420A20E056 @@ -1675,9 +1830,11 @@ D27058C6E2C7 E19504C39461 FA1FBB3F0F1F FF16014FEFC7 -################################## +# # Keys from Flipper Zero Community -# Includes captured keys from devs and community +# Last update: Aug 13, 2022 +# +# unknown if keys are diversified or static default # # Strelka Extension 5C83859F2224 @@ -1764,23 +1921,23 @@ CB9D507CE56D # Armenian Underground Ticket A0A1A2A8A4A5 # -# BadgeMaker Leaked from https://github.com/UberGuidoZ +# Badge Maker Leaked from https://github.com/UberGuidoZ 1A1B1C1D1E1F 1665FE2AE945 158B51947A8E -EL67EC67C7FF -D53732OFF9OE +E167EC67C7FF +D537320FF90E 5E56BFA9E2C9 F81CED821B63 C81584EF5EDF 9551F8F9259D -36EL765CE3E8 +36E1765CE3E8 509052C8E42E 776C9B03BE71 -C608EL3ADD50 +C608E13ADD50 BEE8B345B949 -EDOEC56EEFDD -9716D524LE28 +ED0EC56EEFDD +9716D5241E28 05D1FC14DC31 3321FB75A356 F22A78E29880 @@ -1794,9 +1951,9 @@ DB32A6811327 8AA8544A2207 8C5819E780A4 7549E90353A2 -2E52ABEOCE95 +2E52ABE0CE95 E46210ED98AB -61DO30COD7A8 +61D030C0D7A8 18E20102821E DA59354DFB88 040047C12B75 @@ -1806,11 +1963,179 @@ D10008074A6F 6F6674776172 6520446F7665 # -# Apartment keyfobs (USA) from Corvette830 +# Apartment keyfobs (USA) (Corvette830) E60F8387F0B9 FFD46FF6C5EE 4F9661ED2E70 576A798C9904 1C5179C4A8A1 16CA203B811B -11AC8C8F3AF2 \ No newline at end of file +11AC8C8F3AF2 +# +# The Westin Jakarta Indonesia (D4DB0D) +# Peppers Hotel Unknown location (D4D0D) +6E0DD4136B0A +141940E9B71B +3B1D3AAC866E +95E9EE4CCF8F +FEA6B332F04A +BE0EC5155806 +0500D6BFCC4F +FC5AC7678BE3 +F09BB8DD142D +B4B3FFEDBE0A +540E0D2D1D08 +# +# Schlage 9691T Keyfob (seasnaill) +7579B671051A +4F4553746B41 +# +# Vigik ScanBadge App (fr.badgevigik.scanbadge) +# Website https://badge-vigik.fr/ (Alex) +0000A2B3C86F +021200C20307 +021209197507 +1E34B127AF9C +303041534956 +4143532D494E +41454E521985 +43412D627400 +455249524345 +456666456666 +45B722C63319 +484585414354 +4D414C414741 +536563644C65 +57D27B730760 +593DD8FE167A +6472616E7265 +65626F726369 +680E95F3C287 +709BA7D4F920 +8829DAD9AF76 +92D0A0999CBA +948EE7CFC9DB +9EB7C8A6D4E3 +A22AE12C9013 +AFC984A3576E +# +# Vigik verified by quantum-x +# https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976 +A00027000099 +A00016000028 +A00003000028 +A0000F000345 +A00001000030 +A00002000086 +A00002000036 +A00002000088 +A00000000058 +A00000000096 +A00000000008 +A00000043D79 +A00000000064 +A00025000030 +A00003000057 +# +# BH USA 2013 conference +012279BAD3E5 +# +# iGuard Simple (and reverse) keys +AAAAAAFFFFFF +FFFFFFAAAAAA +# +# Random Hotel A Key Sec 0 Blk 3 - KABA Lock (VideoMan) +3111A3A303EB +# Transport system Uruguay - STM +# Shared key - sec 0 blk 3 +D144BD193063 +# +# Data from http://www.proxmark.org/forum/viewtopic.php?pid=45659#p45659 +3515AE068CAD +# +# Keys Catering +6A0D531DA1A7 +4BB29463DC29 +# +# Keys Swim +8627C10A7014 +453857395635 +# +# Data from "the more, the marriott" mifare project (colonel borkmundus) +# +# Isn't theirs Saflok ? +# +# 20230125-01, Elite Member Marriott Rewards +43012BD9EB87 +# 20230125-02, Elite Member Marriott Rewards +3119A70628EB +# 20230125-03, Elite Member Marriott Rewards +23C9FDD9A366 +# 20230125-04, Elite Member Marriott Rewards +7B4DFC6D6525 +# 20230125-05, Elite Member Marriott Rewards +1330824CD356 +# 20230125-06, Elite Member Marriott Rewards +30AAD6A711EF +# 20230125-07, Fairfield Inn & Suites Marriott +7B3B589A5525 +# 20230125-08, Moxy Hotels +20C166C00ADB +# 20230125-09, Westin Hotels & Resorts +7D0A1C277C05 +2058580A941F +8C29F8320617 +# 20230125-10, Westin Hotels & Resorts +C40964215509 +D44CFC178460 +5697519A8F02 +# 20230125-12, AC Hotels Marriott +7B56B2B38725 +# 20230125-13, AC Hotels Marriott +8EA8EC3F2320 +# 20230125-14, Waldorf Astoria Chicago +011C6CF459E8 +# 20230125-24, Aria Resort & Casino +A18D9F4E75AF +# 20230125-25, Aria Resort & Casino +316B8FAA12EF +# 20230125-26, Residence Inn Mariott +3122AE5341EB +# 20230125-27, Residence Inn Mariott +F72CD208FDF9 +# 20230125-28, Marriott +035C70558D7B +# 20230125-29, Marriott +12AB4C37BB8B +# 20230125-30, Marriott +9966588CB9A0 +# 20230125-31, Sheraton +42FC522DE987 +# 20230125-32, The Industrialist +2158E314C3DF +# 20230125-39, The Ritz-Carlton Balharbour +30FB20D0EFEF +# 20230125-40, The Ritz-Carlton Balharbour +66A3B064CC4B +# 20230125-41, The Ritz-Carlton Balharbour +D18296CD9E6E +# 20230125-42, The Ritz-Carlton Balharbour +D20289CD9E6E +# 20230125-44, Graduate Hotels +209A2B910545 +C49DAE1C6049 +# 20230125-46, AmericInn +8AC04C1A4A25 +# 20230129-53, Marriott Bonvoy +6E029927600D +3E173F64C01C +C670A9AD6066 +# +# 1k - the industrialist +2158E314C3DF +# +# 1k - waldorf astoria +011C6CF459E8 +# +# Food GEM +6686FADE5566 diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index 2b7f89947..e56904ea4 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -7,9 +7,10 @@ # ref. http://www.proxmark.org/forum/viewtopic.php?pid=40662#p40662 # default PROX 50524F58 -# blue gun EM4305 +# blue gun EM4305 F9DCEBA0 # chinese "handheld RFID writer" blue cloner from circa 2013 (also sold by xfpga.com) +# ID/HID CARD COPER SK-663 65857569 # ref. http://kazus.ru/forums/showpost.php?p=1045937&postcount=77 05D73B9F @@ -25,6 +26,8 @@ A5B4C3D2 1C0B5848 # ref. http://www.proxmark.org/forum/viewtopic.php?pid=35075#p35075 00434343 +# default PASS +50415353 # DNGR - DT default pwd 444E4752 4E457854 @@ -38,8 +41,6 @@ E9920427 50520901 # iCopy-X 20206666 -# ID/HID CARD COPER SK-663 -65857569 # password found on discord 5469616E # wCopy NSR102-IDIC @@ -49,8 +50,8 @@ C0F5009A 07CEE75D # # prefered pwds of members in the community -feedbeef -deadc0de +FEEDBEEF +DEADC0DE # Default pwd, simple: 00000000 11111111 @@ -68,13 +69,12 @@ CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF -a0a1a2a3 -b0b1b2b3 -50415353 +A0A1A2A3 +B0B1B2B3 00000001 00000002 -0000000a -0000000b +0000000A +0000000B 01020304 02030405 03040506 @@ -118,8 +118,10 @@ F0000000 AABBCCDD BBCCDDEE CCDDEEFF -0CB7E7FC # rfidler? -FABADA11 # china? +# rfidler? +0CB7E7FC +# china? +FABADA11 # 20 most common len==8 87654321 12341234 @@ -130,23 +132,33 @@ FABADA11 # china? 11112222 13131313 10041004 +# pii +31415926 # -31415926 # pii -abcd1234 +ABCD1234 20002000 19721972 -aa55aa55 # amiboo -55aa55aa # rev amiboo -4f271149 # seeds ul-ev1 -07d7bb0b # seeds ul-ev1 -9636ef8f # seeds ul-ev1 -b5f44686 # seeds ul-ev1 -9E3779B9 # TEA -C6EF3720 # TEA -7854794A # xbox tea constant :) -F1EA5EED # burtle -69314718 # ln2 -57721566 # euler constant (dec) -93C467E3 # euler constant (hex) -27182818 # natural log -50415353 # PASS +# rev amiboo +AA55AA55 +55AA55AA +# seeds ul-ev1 +4F271149 +07D7BB0B +9636EF8F +B5F44686 +# TEA +9E3779B9 +C6EF3720 +# +# xbox tea constant :) +7854794A +# burtle +F1EA5EED +# ln2 +69314718 +# euler constant (dec) +57721566 +# euler constant (hex) +93C467E3 +# natural log +27182818 diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 5c64fd135..bdc122e58 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -122,7 +122,7 @@ if (NOT SKIPREADLINE EQUAL 1) ExternalProject_Add_StepTargets(ncurses configure build install) ExternalProject_Add(readline - URL ftp://ftp.gnu.org/gnu/readline/readline-8.1.tar.gz + URL ftp://ftp.gnu.org/gnu/readline/readline-8.2.tar.gz PREFIX deps/readline DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/readline CONFIGURE_COMMAND ./configure CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} --host=arm --enable-static @@ -276,6 +276,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhfepa.c ${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffido.c + ${PM3_ROOT}/client/src/cmdhffudan.c ${PM3_ROOT}/client/src/cmdhfgallagher.c ${PM3_ROOT}/client/src/cmdhfcipurse.c ${PM3_ROOT}/client/src/cmdhficlass.c @@ -293,9 +294,12 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhfseos.c ${PM3_ROOT}/client/src/cmdhfst.c ${PM3_ROOT}/client/src/cmdhfst25ta.c + ${PM3_ROOT}/client/src/cmdhftesla.c + ${PM3_ROOT}/client/src/cmdhftexkom.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c ${PM3_ROOT}/client/src/cmdhfwaveshare.c + ${PM3_ROOT}/client/src/cmdhfxerox.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c @@ -334,6 +338,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdmain.c ${PM3_ROOT}/client/src/cmdnfc.c ${PM3_ROOT}/client/src/cmdparser.c + ${PM3_ROOT}/client/src/cmdpiv.c ${PM3_ROOT}/client/src/cmdscript.c ${PM3_ROOT}/client/src/cmdsmartcard.c ${PM3_ROOT}/client/src/cmdtrace.c diff --git a/client/lualibs/amiibo_tools.lua b/client/lualibs/amiibo_tools.lua new file mode 100644 index 000000000..b4c3f3bbf --- /dev/null +++ b/client/lualibs/amiibo_tools.lua @@ -0,0 +1,2648 @@ +local amiibo_tools = {} + +-- curl https://raw.githubusercontent.com/N3evin/AmiiboAPI/master/database/amiibo.json | jq 'del(.amiibos[].release)' | jq 'del(.characters)' | pbcopy --> transform to table +amiibo_tools.db = +{ + amiibo_series = { + ["0x00"] = "Super Smash Bros.", + ["0x01"] = "Super Mario Bros.", + ["0x02"] = "Chibi-Robo!", + ["0x03"] = "Yoshi's Woolly World", + ["0x04"] = "Splatoon", + ["0x05"] = "Animal Crossing", + ["0x06"] = "8-bit Mario", + ["0x07"] = "Skylanders", + ["0x09"] = "Legend Of Zelda", + ["0x0a"] = "Shovel Knight", + ["0x0c"] = "Kirby", + ["0x0d"] = "Pokemon", + ["0x0e"] = "Mario Sports Superstars", + ["0x0f"] = "Monster Hunter", + ["0x10"] = "BoxBoy!", + ["0x11"] = "Pikmin", + ["0x12"] = "Fire Emblem", + ["0x13"] = "Metroid", + ["0x14"] = "Others", + ["0x15"] = "Mega Man", + ["0x16"] = "Diablo", + ["0x17"] = "Power Pros", + ["0x18"] = "Monster Hunter Rise", + ["0x19"] = "Yu-Gi-Oh!", + ["0xff"] = "Super Nintendo World" + }, + amiibos = { + ["0x0000000000000002"] = { + name = "Mario" + }, + ["0x0000000000340102"] = { + name = "Mario" + }, + ["0x00000000003c0102"] = { + name = "Mario - Gold Edition" + }, + ["0x00000000003d0102"] = { + name = "Mario - Silver Edition" + }, + ["0x0000000002380602"] = { + name = "8-Bit Mario Classic Color" + }, + ["0x0000000002390602"] = { + name = "8-Bit Mario Modern Color" + }, + ["0x0000000003710102"] = { + name = "Mario - Wedding" + }, + ["0x00000003039bff02"] = { + name = "Mario - Power Up Band" + }, + ["0x0000010000190002"] = { + name = "Dr. Mario" + }, + ["0x0000030003a60102"] = { + name = "Mario - Cat" + }, + ["0x00010000000c0002"] = { + name = "Luigi" + }, + ["0x0001000000350102"] = { + name = "Luigi" + }, + ["0x00010003039cff02"] = { + name = "Luigi - Power Up Band" + }, + ["0x0002000000010002"] = { + name = "Peach" + }, + ["0x0002000000360102"] = { + name = "Peach" + }, + ["0x0002000003720102"] = { + name = "Peach - Wedding" + }, + ["0x00020003039dff02"] = { + name = "Peach - Power Up Band" + }, + ["0x0002010003a70102"] = { + name = "Peach - Cat" + }, + ["0x0003000000020002"] = { + name = "Yoshi" + }, + ["0x0003000000370102"] = { + name = "Yoshi" + }, + ["0x00030003039fff02"] = { + name = "Yoshi - Power Up Band" + }, + ["0x0003010200410302"] = { + name = "Green Yarn Yoshi" + }, + ["0x0003010200420302"] = { + name = "Pink Yarn Yoshi" + }, + ["0x0003010200430302"] = { + name = "Light Blue Yarn Yoshi" + }, + ["0x00030102023e0302"] = { + name = "Mega Yarn Yoshi" + }, + ["0x0004000002620102"] = { + name = "Rosalina" + }, + ["0x0004010000130002"] = { + name = "Rosalina & Luma" + }, + ["0x0005000000140002"] = { + name = "Bowser" + }, + ["0x0005000000390102"] = { + name = "Bowser" + }, + ["0x0005000003730102"] = { + name = "Bowser - Wedding" + }, + ["0x0005ff00023a0702"] = { + name = "Hammer Slam Bowser" + }, + ["0x0006000000150002"] = { + name = "Bowser Jr." + }, + ["0x00070000001a0002"] = { + name = "Wario" + }, + ["0x0007000002630102"] = { + name = "Wario" + }, + ["0x0008000000030002"] = { + name = "Donkey Kong" + }, + ["0x0008000002640102"] = { + name = "Donkey Kong" + }, + ["0x0008ff00023b0702"] = { + name = "Turbo Charge Donkey Kong" + }, + ["0x00090000000d0002"] = { + name = "Diddy Kong" + }, + ["0x0009000002650102"] = { + name = "Diddy Kong" + }, + ["0x000a000000380102"] = { + name = "Toad" + }, + ["0x000a000303a0ff02"] = { + name = "Toad - Power Up Band" + }, + ["0x0013000002660102"] = { + name = "Daisy" + }, + ["0x00130000037a0002"] = { + name = "Daisy" + }, + ["0x00130003039eff02"] = { + name = "Daisy - Power Up Band" + }, + ["0x0014000002670102"] = { + name = "Waluigi" + }, + ["0x0015000003670102"] = { + name = "Goomba" + }, + ["0x0017000002680102"] = { + name = "Boo" + }, + ["0x0023000003680102"] = { + name = "Koopa Troopa" + }, + ["0x00240000038d0002"] = { + name = "Piranha Plant" + }, + ["0x00800102035d0302"] = { + name = "Poochy" + }, + ["0x00c00000037b0002"] = { + name = "King K. Rool" + }, + ["0x0100000000040002"] = { + name = "Link" + }, + ["0x01000000034b0902"] = { + name = "Link - Ocarina of Time" + }, + ["0x01000000034c0902"] = { + name = "Link - Majora's Mask" + }, + ["0x01000000034d0902"] = { + name = "Link - Twilight Princess" + }, + ["0x01000000034e0902"] = { + name = "Link - Skyward Sword" + }, + ["0x01000000034f0902"] = { + name = "8-Bit Link" + }, + ["0x0100000003530902"] = { + name = "Link - Archer" + }, + ["0x0100000003540902"] = { + name = "Link - Rider" + }, + ["0x01000000037c0002"] = { + name = "Young Link" + }, + ["0x0100000003990902"] = { + name = "Link - Link's Awakening" + }, + ["0x0100010000160002"] = { + name = "Toon Link" + }, + ["0x0100010003500902"] = { + name = "Toon Link - The Wind Waker" + }, + ["0x01010000000e0002"] = { + name = "Zelda" + }, + ["0x0101000003520902"] = { + name = "Toon Zelda - The Wind Waker" + }, + ["0x0101000003560902"] = { + name = "Zelda" + }, + ["0x0101010000170002"] = { + name = "Sheik" + }, + ["0x0101030004140902"] = { + name = "Zelda & Loftwing" + }, + ["0x01020100001b0002"] = { + name = "Ganondorf" + }, + ["0x01030000024f0902"] = { + name = "Midna & Wolf Link" + }, + ["0x0105000003580902"] = { + name = "Daruk" + }, + ["0x0106000003590902"] = { + name = "Urbosa" + }, + ["0x01070000035a0902"] = { + name = "Mipha" + }, + ["0x01080000035b0902"] = { + name = "Revali" + }, + ["0x0140000003550902"] = { + name = "Guardian" + }, + ["0x01410000035c0902"] = { + name = "Bokoblin" + }, + ["0x0180000000080002"] = { + name = "Villager" + }, + ["0x01810000024b0502"] = { + name = "Isabelle - Summer Outfit" + }, + ["0x01810000037d0002"] = { + name = "Isabelle" + }, + ["0x0181000100440502"] = { + name = "Isabelle" + }, + ["0x0181000101d40502"] = { + name = "Isabelle - Character Parfait" + }, + ["0x01810100023f0502"] = { + name = "Isabelle - Winter Outfit" + }, + ["0x0181010100b40502"] = { + name = "Isabelle - Winter" + }, + ["0x01810201011a0502"] = { + name = "Isabelle - Kimono" + }, + ["0x0181030101700502"] = { + name = "Isabelle - Dress" + }, + ["0x0181040103aa0502"] = { + name = "Isabelle" + }, + ["0x0181050103bf0502"] = { + name = "Isabelle - Sweater" + }, + ["0x0182000002400502"] = { + name = "K. K. Slider" + }, + ["0x0182000100a80502"] = { + name = "K.K. Slider" + }, + ["0x0182000101d80502"] = { + name = "K. K. Slider - Pikopuri" + }, + ["0x0182000103b20502"] = { + name = "K.K. Slider" + }, + ["0x0182010100460502"] = { + name = "DJ KK" + }, + ["0x0183000002420502"] = { + name = "Tom Nook" + }, + ["0x0183000100450502"] = { + name = "Tom Nook" + }, + ["0x01830101010e0502"] = { + name = "Tom Nook - Jacket" + }, + ["0x0183020103a80502"] = { + name = "Tom Nook" + }, + ["0x0183030103be0502"] = { + name = "Tom Nook - Coat" + }, + ["0x01840000024d0502"] = { + name = "Timmy & Tommy" + }, + ["0x0184050103a90502"] = { + name = "Timmy & Tommy" + }, + ["0x01850001004b0502"] = { + name = "Timmy" + }, + ["0x0185020101170502"] = { + name = "Timmy - Full Apron" + }, + ["0x0185040101790502"] = { + name = "Timmy - Suit" + }, + ["0x0186010100af0502"] = { + name = "Tommy - Uniform" + }, + ["0x0186030101750502"] = { + name = "Tommy - Suit" + }, + ["0x0187000100470502"] = { + name = "Sable" + }, + ["0x0187000103b00502"] = { + name = "Sable" + }, + ["0x0188000002410502"] = { + name = "Mabel" + }, + ["0x0188000101120502"] = { + name = "Mabel" + }, + ["0x0188000103af0502"] = { + name = "Mabel" + }, + ["0x0189000100ab0502"] = { + name = "Label" + }, + ["0x0189010103b10502"] = { + name = "Label" + }, + ["0x018a000002450502"] = { + name = "Reese" + }, + ["0x018a000100a90502"] = { + name = "Reese" + }, + ["0x018b000002460502"] = { + name = "Cyrus" + }, + ["0x018b000101150502"] = { + name = "Cyrus" + }, + ["0x018c000002430502"] = { + name = "Digby" + }, + ["0x018c0001004c0502"] = { + name = "Digby" + }, + ["0x018c010101180502"] = { + name = "Digby - Raincoat" + }, + ["0x018d0000024c0502"] = { + name = "Rover" + }, + ["0x018d0001010c0502"] = { + name = "Rover" + }, + ["0x018e000002490502"] = { + name = "Resetti" + }, + ["0x018e000100490502"] = { + name = "Resetti" + }, + ["0x018e010101780502"] = { + name = "Resetti - Without Hat" + }, + ["0x018f000100b30502"] = { + name = "Don Resetti" + }, + ["0x018f010101190502"] = { + name = "Don Resetti - Without Hat" + }, + ["0x0190000101710502"] = { + name = "Brewster" + }, + ["0x01910001004e0502"] = { + name = "Harriet" + }, + ["0x0192000002470502"] = { + name = "Blathers" + }, + ["0x01920001010d0502"] = { + name = "Blathers" + }, + ["0x0192000103ad0502"] = { + name = "Blathers" + }, + ["0x0193000002480502"] = { + name = "Celeste" + }, + ["0x0193000101740502"] = { + name = "Celeste" + }, + ["0x0193000103ae0502"] = { + name = "Celeste" + }, + ["0x01940000024a0502"] = { + name = "Kicks" + }, + ["0x0194000100aa0502"] = { + name = "Kicks" + }, + ["0x0194000103b60502"] = { + name = "Kicks" + }, + ["0x0195000100b00502"] = { + name = "Porter" + }, + ["0x01960000024e0502"] = { + name = "Kapp'n" + }, + ["0x0196000100480502"] = { + name = "Kapp'n" + }, + ["0x0197000101770502"] = { + name = "Leilani" + }, + ["0x0198000100b10502"] = { + name = "Leila" + }, + ["0x0199000101160502"] = { + name = "Grams" + }, + ["0x019a000100b70502"] = { + name = "Chip" + }, + ["0x019b000100b60502"] = { + name = "Nat" + }, + ["0x019c000101730502"] = { + name = "Phineas" + }, + ["0x019d000100ac0502"] = { + name = "Copper" + }, + ["0x019e000100ad0502"] = { + name = "Booker" + }, + ["0x019f000101110502"] = { + name = "Pete" + }, + ["0x01a00001010f0502"] = { + name = "Pelly" + }, + ["0x01a1000101100502"] = { + name = "Phyllis" + }, + ["0x01a20001017d0502"] = { + name = "Gulliver" + }, + ["0x01a2000103b90502"] = { + name = "Gulliver" + }, + ["0x01a30001004a0502"] = { + name = "Joan" + }, + ["0x01a40001004d0502"] = { + name = "Pascal" + }, + ["0x01a5000101720502"] = { + name = "Katrina" + }, + ["0x01a6000100500502"] = { + name = "Saharah" + }, + ["0x01a6000103b70502"] = { + name = "Saharah" + }, + ["0x01a7000101140502"] = { + name = "Wendell" + }, + ["0x01a80001004f0502"] = { + name = "Redd" + }, + ["0x01a80101017e0502"] = { + name = "Redd - Shirt" + }, + ["0x01a9000101760502"] = { + name = "Gracie" + }, + ["0x01aa000100530502"] = { + name = "Lyle" + }, + ["0x01ab0001017c0502"] = { + name = "Pave" + }, + ["0x01ac0001017f0502"] = { + name = "Zipper" + }, + ["0x01ad000100b80502"] = { + name = "Jack" + }, + ["0x01ae0001011b0502"] = { + name = "Franklin" + }, + ["0x01af0001011c0502"] = { + name = "Jingle" + }, + ["0x01b0000100520502"] = { + name = "Tortimer" + }, + ["0x01b1000100b20502"] = { + name = "Shrunk" + }, + ["0x01b10101017b0502"] = { + name = "Shrunk - Loud Jacket" + }, + ["0x01b3000100b50502"] = { + name = "Blanca" + }, + ["0x01b4000101130502"] = { + name = "Leif" + }, + ["0x01b5000100510502"] = { + name = "Luna" + }, + ["0x01b6000100ae0502"] = { + name = "Katie" + }, + ["0x01c1000002440502"] = { + name = "Lottie" + }, + ["0x01c1000100540502"] = { + name = "Lottie" + }, + ["0x01c10101017a0502"] = { + name = "Lottie - Black Skirt And Bow" + }, + ["0x01c1020103bb0502"] = { + name = "Lottie - Island" + }, + ["0x0200000100a10502"] = { + name = "Cyrano" + }, + ["0x02010001016a0502"] = { + name = "Antonio" + }, + ["0x0202000101030502"] = { + name = "Pango" + }, + ["0x02030001019a0502"] = { + name = "Anabelle" + }, + ["0x0206000103120502"] = { + name = "Snooty" + }, + ["0x0208000100960502"] = { + name = "Annalisa" + }, + ["0x02090001019f0502"] = { + name = "Olaf" + }, + ["0x0214000100e40502"] = { + name = "Teddy" + }, + ["0x0215000101820502"] = { + name = "Pinky" + }, + ["0x0216000100570502"] = { + name = "Curt" + }, + ["0x0217000101b30502"] = { + name = "Chow" + }, + ["0x02190001007e0502"] = { + name = "Nate" + }, + ["0x021a000100da0502"] = { + name = "Groucho" + }, + ["0x021b000100800502"] = { + name = "Tutu" + }, + ["0x021c000102f70502"] = { + name = "Ursala" + }, + ["0x021d000101cd0502"] = { + name = "Grizzly" + }, + ["0x021e000101230502"] = { + name = "Paula" + }, + ["0x021f000103170502"] = { + name = "Ike" + }, + ["0x0220000100fd0502"] = { + name = "Charlise" + }, + ["0x02210001013c0502"] = { + name = "Beardo" + }, + ["0x0222000101440502"] = { + name = "Klaus" + }, + ["0x022d000100f20502"] = { + name = "Jay" + }, + ["0x022e000101d30502"] = { + name = "Robin" + }, + ["0x022f0001011e0502"] = { + name = "Anchovy" + }, + ["0x0230000101d20502"] = { + name = "Twiggy" + }, + ["0x02310001006a0502"] = { + name = "Jitters" + }, + ["0x0232000102ea0502"] = { + name = "Piper" + }, + ["0x0233000103060502"] = { + name = "Admiral" + }, + ["0x0235000100840502"] = { + name = "Midge" + }, + ["0x0238000102f80502"] = { + name = "Jacob" + }, + ["0x023c000100bd0502"] = { + name = "Lucha" + }, + ["0x023d000101b50502"] = { + name = "Jacques" + }, + ["0x023e000100d10502"] = { + name = "Peck" + }, + ["0x023f000101660502"] = { + name = "Sparro" + }, + ["0x024a000101d10502"] = { + name = "Angus" + }, + ["0x024b000101260502"] = { + name = "Rodeo" + }, + ["0x024d000102f60502"] = { + name = "Stu" + }, + ["0x024f000100810502"] = { + name = "T-Bone" + }, + ["0x0251000100c10502"] = { + name = "Coach" + }, + ["0x0252000100fe0502"] = { + name = "Vic" + }, + ["0x025d000100550502"] = { + name = "Bob" + }, + ["0x025e000101250502"] = { + name = "Mitzi" + }, + ["0x025f000101c50502"] = { + name = "Rosie" + }, + ["0x025f000101d70502"] = { + name = "Rosie - Amiibo Festival" + }, + ["0x0260000100d20502"] = { + name = "Olivia" + }, + ["0x0261000100650502"] = { + name = "Kiki" + }, + ["0x0262000101370502"] = { + name = "Tangy" + }, + ["0x0263000100750502"] = { + name = "Punchy" + }, + ["0x0264000101ac0502"] = { + name = "Purrl" + }, + ["0x0265000101540502"] = { + name = "Moe" + }, + ["0x0266000100680502"] = { + name = "Kabuki" + }, + ["0x0267000101080502"] = { + name = "Kid Cat" + }, + ["0x02680001007d0502"] = { + name = "Monique" + }, + ["0x02690001011f0502"] = { + name = "Tabby" + }, + ["0x026a000101460502"] = { + name = "Stinky" + }, + ["0x026b000100e90502"] = { + name = "Kitty" + }, + ["0x026c000100c30502"] = { + name = "Tom" + }, + ["0x026d0001013f0502"] = { + name = "Merry" + }, + ["0x026e000100ba0502"] = { + name = "Felicity" + }, + ["0x026f000101900502"] = { + name = "Lolly" + }, + ["0x0270000100ff0502"] = { + name = "Ankha" + }, + ["0x02710001019b0502"] = { + name = "Rudy" + }, + ["0x0272000101860502"] = { + name = "Katt" + }, + ["0x027d000100630502"] = { + name = "Bluebear" + }, + ["0x027e000101690502"] = { + name = "Maple" + }, + ["0x027f000100b90502"] = { + name = "Poncho" + }, + ["0x0280000100830502"] = { + name = "Pudge" + }, + ["0x0281000101200502"] = { + name = "Kody" + }, + ["0x0282000101810502"] = { + name = "Stitches" + }, + ["0x0282000101d60502"] = { + name = "Stitches - Amiibo Festival" + }, + ["0x0283000100c70502"] = { + name = "Vladimir" + }, + ["0x0284000102fe0502"] = { + name = "Murphy" + }, + ["0x0286000103130502"] = { + name = "Olive" + }, + ["0x02870001005a0502"] = { + name = "Cheri" + }, + ["0x028a000102e90502"] = { + name = "June" + }, + ["0x028b000100e30502"] = { + name = "Pekoe" + }, + ["0x028c0001013e0502"] = { + name = "Chester" + }, + ["0x028d000101bd0502"] = { + name = "Barold" + }, + ["0x028e0001019e0502"] = { + name = "Tammy" + }, + ["0x028f0101031a0502"] = { + name = "Marty" + }, + ["0x0299000100950502"] = { + name = "Goose" + }, + ["0x029a000100ee0502"] = { + name = "Benedict" + }, + ["0x029b000100cb0502"] = { + name = "Egbert" + }, + ["0x029e0001013d0502"] = { + name = "Ava" + }, + ["0x02a2000101ba0502"] = { + name = "Becky" + }, + ["0x02a3000102ff0502"] = { + name = "Plucky" + }, + ["0x02a4000100720502"] = { + name = "Knox" + }, + ["0x02a50001018c0502"] = { + name = "Broffina" + }, + ["0x02a6000101240502"] = { + name = "Ken" + }, + ["0x02b1000100690502"] = { + name = "Patty" + }, + ["0x02b2000100c40502"] = { + name = "Tipper" + }, + ["0x02b70001030f0502"] = { + name = "Norma" + }, + ["0x02b80001019c0502"] = { + name = "Naomi" + }, + ["0x02c3000100dc0502"] = { + name = "Alfonso" + }, + ["0x02c4000100670502"] = { + name = "Alli" + }, + ["0x02c5000103080502"] = { + name = "Boots" + }, + ["0x02c7000101220502"] = { + name = "Del" + }, + ["0x02c9000100cd0502"] = { + name = "Sly" + }, + ["0x02ca000101ca0502"] = { + name = "Gayle" + }, + ["0x02cb000101360502"] = { + name = "Drago" + }, + ["0x02d6000100560502"] = { + name = "Fauna" + }, + ["0x02d7000101300502"] = { + name = "Bam" + }, + ["0x02d8000100e20502"] = { + name = "Zell" + }, + ["0x02d9000101c80502"] = { + name = "Bruce" + }, + ["0x02da000101330502"] = { + name = "Deirdre" + }, + ["0x02db0001005e0502"] = { + name = "Lopez" + }, + ["0x02dc000100be0502"] = { + name = "Fuchsia" + }, + ["0x02dd000100ea0502"] = { + name = "Beau" + }, + ["0x02de0001009c0502"] = { + name = "Diana" + }, + ["0x02df000101910502"] = { + name = "Erik" + }, + ["0x02e00101031d0502"] = { + name = "Chelsea" + }, + ["0x02ea000101800502"] = { + name = "Goldie" + }, + ["0x02ea000101d50502"] = { + name = "Goldie - Amiibo Festival" + }, + ["0x02eb000100de0502"] = { + name = "Butch" + }, + ["0x02ec000101c40502"] = { + name = "Lucky" + }, + ["0x02ed0001015a0502"] = { + name = "Biskit" + }, + ["0x02ee000101990502"] = { + name = "Bones" + }, + ["0x02ef000100580502"] = { + name = "Portia" + }, + ["0x02f0000100a70502"] = { + name = "Walker" + }, + ["0x02f1000101450502"] = { + name = "Daisy" + }, + ["0x02f2000100cc0502"] = { + name = "Cookie" + }, + ["0x02f3000102f90502"] = { + name = "Maddie" + }, + ["0x02f4000103050502"] = { + name = "Bea" + }, + ["0x02f8000101380502"] = { + name = "Mac" + }, + ["0x02f9000101020502"] = { + name = "Marcel" + }, + ["0x02fa000100970502"] = { + name = "Benjamin" + }, + ["0x02fb000100900502"] = { + name = "Cherry" + }, + ["0x02fc0001018f0502"] = { + name = "Shep" + }, + ["0x0307000100640502"] = { + name = "Bill" + }, + ["0x03080001014d0502"] = { + name = "Joey" + }, + ["0x0309000100c60502"] = { + name = "Pate" + }, + ["0x030a000101c70502"] = { + name = "Maelle" + }, + ["0x030b000100790502"] = { + name = "Deena" + }, + ["0x030c000101b80502"] = { + name = "Pompom" + }, + ["0x030d000101840502"] = { + name = "Mallary" + }, + ["0x030e0001012f0502"] = { + name = "Freckles" + }, + ["0x030f0001016d0502"] = { + name = "Derwin" + }, + ["0x0310000100f80502"] = { + name = "Drake" + }, + ["0x0311000100d60502"] = { + name = "Scoot" + }, + ["0x0312000103090502"] = { + name = "Weber" + }, + ["0x0313000101210502"] = { + name = "Miranda" + }, + ["0x0314000102f40502"] = { + name = "Ketchup" + }, + ["0x0316000101c00502"] = { + name = "Gloria" + }, + ["0x0317000100a60502"] = { + name = "Molly" + }, + ["0x03180001006c0502"] = { + name = "Quillson" + }, + ["0x0323000100760502"] = { + name = "Opal" + }, + ["0x0324000101890502"] = { + name = "Dizzy" + }, + ["0x03250001010a0502"] = { + name = "Big Top" + }, + ["0x0326000101390502"] = { + name = "Eloise" + }, + ["0x0327000101c30502"] = { + name = "Margie" + }, + ["0x0328000102eb0502"] = { + name = "Paolo" + }, + ["0x03290001009d0502"] = { + name = "Axel" + }, + ["0x032a000103070502"] = { + name = "Ellie" + }, + ["0x032c000101480502"] = { + name = "Tucker" + }, + ["0x032d000100bc0502"] = { + name = "Tia" + }, + ["0x032e0101031c0502"] = { + name = "Chai" + }, + ["0x03380001011d0502"] = { + name = "Lily" + }, + ["0x0339000101b10502"] = { + name = "Ribbot" + }, + ["0x033a000101cc0502"] = { + name = "Frobert" + }, + ["0x033b000100fa0502"] = { + name = "Camofrog" + }, + ["0x033c000101000502"] = { + name = "Drift" + }, + ["0x033d0001013a0502"] = { + name = "Wart Jr." + }, + ["0x033e000101a20502"] = { + name = "Puddles" + }, + ["0x033f0001008f0502"] = { + name = "Jeremiah" + }, + ["0x03410001030e0502"] = { + name = "Tad" + }, + ["0x0342000101280502"] = { + name = "Cousteau" + }, + ["0x0343000102ef0502"] = { + name = "Huck" + }, + ["0x0344000100c50502"] = { + name = "Prince" + }, + ["0x03450001005f0502"] = { + name = "Jambette" + }, + ["0x0347000103020502"] = { + name = "Raddle" + }, + ["0x03480001006b0502"] = { + name = "Gigi" + }, + ["0x03490001018d0502"] = { + name = "Croque" + }, + ["0x034a000101430502"] = { + name = "Diva" + }, + ["0x034b0001009f0502"] = { + name = "Henry" + }, + ["0x0356000101350502"] = { + name = "Chevre" + }, + ["0x0357000100eb0502"] = { + name = "Nan" + }, + ["0x0358000102fa0502"] = { + name = "Billy" + }, + ["0x035a000100850502"] = { + name = "Gruff" + }, + ["0x035c000101290502"] = { + name = "Velma" + }, + ["0x035d000100c90502"] = { + name = "Kidd" + }, + ["0x035e0001018e0502"] = { + name = "Pashmina" + }, + ["0x0369000100d30502"] = { + name = "Cesar" + }, + ["0x036a0001019d0502"] = { + name = "Peewee" + }, + ["0x036b0001018b0502"] = { + name = "Boone" + }, + ["0x036d000103040502"] = { + name = "Louie" + }, + ["0x036e000102fb0502"] = { + name = "Boyd" + }, + ["0x03700001015d0502"] = { + name = "Violet" + }, + ["0x03710001005c0502"] = { + name = "Al" + }, + ["0x03720001010b0502"] = { + name = "Rocket" + }, + ["0x0373000101340502"] = { + name = "Hans" + }, + ["0x0374010103190502"] = { + name = "Rilla" + }, + ["0x037e000101560502"] = { + name = "Hamlet" + }, + ["0x037f000101aa0502"] = { + name = "Apple" + }, + ["0x0380000101870502"] = { + name = "Graham" + }, + ["0x0381000100d50502"] = { + name = "Rodney" + }, + ["0x03820001016b0502"] = { + name = "Soleil" + }, + ["0x03830001009b0502"] = { + name = "Clay" + }, + ["0x0384000100860502"] = { + name = "Flurry" + }, + ["0x0385000101060502"] = { + name = "Hamphrey" + }, + ["0x0390000101850502"] = { + name = "Rocco" + }, + ["0x0392000101270502"] = { + name = "Bubbles" + }, + ["0x0393000100a00502"] = { + name = "Bertha" + }, + ["0x0394000100890502"] = { + name = "Biff" + }, + ["0x0395000102fc0502"] = { + name = "Bitty" + }, + ["0x0398000100bf0502"] = { + name = "Harry" + }, + ["0x0399000101c20502"] = { + name = "Hippeux" + }, + ["0x03a40001014f0502"] = { + name = "Buck" + }, + ["0x03a50001015b0502"] = { + name = "Victoria" + }, + ["0x03a6000100c80502"] = { + name = "Savannah" + }, + ["0x03a7000101a10502"] = { + name = "Elmer" + }, + ["0x03a8000100910502"] = { + name = "Rosco" + }, + ["0x03a9000100710502"] = { + name = "Winnie" + }, + ["0x03aa000100e60502"] = { + name = "Ed" + }, + ["0x03ab000103160502"] = { + name = "Cleo" + }, + ["0x03ac000101880502"] = { + name = "Peaches" + }, + ["0x03ad000101b20502"] = { + name = "Annalise" + }, + ["0x03ae000100870502"] = { + name = "Clyde" + }, + ["0x03af0001012c0502"] = { + name = "Colton" + }, + ["0x03b0000101a90502"] = { + name = "Papi" + }, + ["0x03b1000100f00502"] = { + name = "Julian" + }, + ["0x03bc0001008a0502"] = { + name = "Yuka" + }, + ["0x03bd000100f90502"] = { + name = "Alice" + }, + ["0x03be000101980502"] = { + name = "Melba" + }, + ["0x03bf000101bc0502"] = { + name = "Sydney" + }, + ["0x03c0000103100502"] = { + name = "Gonzo" + }, + ["0x03c1000100bb0502"] = { + name = "Ozzie" + }, + ["0x03c40001012b0502"] = { + name = "Canberra" + }, + ["0x03c50001015c0502"] = { + name = "Lyman" + }, + ["0x03c6000100930502"] = { + name = "Eugene" + }, + ["0x03d1000100c20502"] = { + name = "Kitt" + }, + ["0x03d2000100e50502"] = { + name = "Mathilda" + }, + ["0x03d3000102f30502"] = { + name = "Carrie" + }, + ["0x03d6000101570502"] = { + name = "Astrid" + }, + ["0x03d7000101b40502"] = { + name = "Sylvia" + }, + ["0x03d9000101a50502"] = { + name = "Walt" + }, + ["0x03da000101510502"] = { + name = "Rooney" + }, + ["0x03db0001006d0502"] = { + name = "Marcie" + }, + ["0x03e6000100ec0502"] = { + name = "Bud" + }, + ["0x03e70001012a0502"] = { + name = "Elvis" + }, + ["0x03e8000102f50502"] = { + name = "Rex" + }, + ["0x03ea0001030b0502"] = { + name = "Leopold" + }, + ["0x03ec000101830502"] = { + name = "Mott" + }, + ["0x03ed000101a30502"] = { + name = "Rory" + }, + ["0x03ee0001008b0502"] = { + name = "Lionel" + }, + ["0x03fa000100d00502"] = { + name = "Nana" + }, + ["0x03fb000101cf0502"] = { + name = "Simon" + }, + ["0x03fc000101470502"] = { + name = "Tammi" + }, + ["0x03fd000101580502"] = { + name = "Monty" + }, + ["0x03fe000101a40502"] = { + name = "Elise" + }, + ["0x03ff000100f40502"] = { + name = "Flip" + }, + ["0x04000001006f0502"] = { + name = "Shari" + }, + ["0x0401000100660502"] = { + name = "Deli" + }, + ["0x040c000101590502"] = { + name = "Dora" + }, + ["0x040d000100780502"] = { + name = "Limberg" + }, + ["0x040e000100880502"] = { + name = "Bella" + }, + ["0x040f000101500502"] = { + name = "Bree" + }, + ["0x04100001007f0502"] = { + name = "Samson" + }, + ["0x0411000101ab0502"] = { + name = "Rod" + }, + ["0x04140001030a0502"] = { + name = "Candi" + }, + ["0x0415000101bb0502"] = { + name = "Rizzo" + }, + ["0x0416000100fb0502"] = { + name = "Anicotti" + }, + ["0x0418000100d80502"] = { + name = "Broccolo" + }, + ["0x041a000100e00502"] = { + name = "Moose" + }, + ["0x041b000100f10502"] = { + name = "Bettina" + }, + ["0x041c000101410502"] = { + name = "Greta" + }, + ["0x041d0001018a0502"] = { + name = "Penelope" + }, + ["0x041e0001015f0502"] = { + name = "Chadder" + }, + ["0x0429000100700502"] = { + name = "Octavian" + }, + ["0x042a0001012d0502"] = { + name = "Marina" + }, + ["0x042b000101af0502"] = { + name = "Zucker" + }, + ["0x0436000101940502"] = { + name = "Queenie" + }, + ["0x0437000101050502"] = { + name = "Gladys" + }, + ["0x0438000103000502"] = { + name = "Sandy" + }, + ["0x0439000103110502"] = { + name = "Sprocket" + }, + ["0x043b000103030502"] = { + name = "Julia" + }, + ["0x043c000101cb0502"] = { + name = "Cranston" + }, + ["0x043d0001007c0502"] = { + name = "Phil" + }, + ["0x043e000101490502"] = { + name = "Blanche" + }, + ["0x043f000101550502"] = { + name = "Flora" + }, + ["0x0440000100ca0502"] = { + name = "Phoebe" + }, + ["0x044b0001016c0502"] = { + name = "Apollo" + }, + ["0x044c0001008e0502"] = { + name = "Amelia" + }, + ["0x044d000101930502"] = { + name = "Pierce" + }, + ["0x044e000103150502"] = { + name = "Buzz" + }, + ["0x0450000100cf0502"] = { + name = "Avery" + }, + ["0x04510001015e0502"] = { + name = "Frank" + }, + ["0x0452000100730502"] = { + name = "Sterling" + }, + ["0x0453000101040502"] = { + name = "Keaton" + }, + ["0x0454000101ae0502"] = { + name = "Celia" + }, + ["0x045f000101a80502"] = { + name = "Aurora" + }, + ["0x0460000100a50502"] = { + name = "Roald" + }, + ["0x0461000101610502"] = { + name = "Cube" + }, + ["0x0462000100f60502"] = { + name = "Hopper" + }, + ["0x0463000101310502"] = { + name = "Friga" + }, + ["0x0464000100c00502"] = { + name = "Gwen" + }, + ["0x04650001006e0502"] = { + name = "Puck" + }, + ["0x0468000102f20502"] = { + name = "Wade" + }, + ["0x0469000101640502"] = { + name = "Boomer" + }, + ["0x046a000101d00502"] = { + name = "Iggly" + }, + ["0x046b000101970502"] = { + name = "Tex" + }, + ["0x046c0001008c0502"] = { + name = "Flo" + }, + ["0x046d000100f30502"] = { + name = "Sprinkle" + }, + ["0x0478000101630502"] = { + name = "Curly" + }, + ["0x0479000100920502"] = { + name = "Truffles" + }, + ["0x047a000100600502"] = { + name = "Rasher" + }, + ["0x047b000100f50502"] = { + name = "Hugh" + }, + ["0x047c000101a00502"] = { + name = "Lucy" + }, + ["0x047d0001012e0502"] = { + name = "Spork/Crackle" + }, + ["0x04800001008d0502"] = { + name = "Cobb" + }, + ["0x0481000102f10502"] = { + name = "Boris" + }, + ["0x0482000102fd0502"] = { + name = "Maggie" + }, + ["0x0483000101b00502"] = { + name = "Peggy" + }, + ["0x04850001014c0502"] = { + name = "Gala" + }, + ["0x0486000100fc0502"] = { + name = "Chops" + }, + ["0x0487000101bf0502"] = { + name = "Kevin" + }, + ["0x0488000100980502"] = { + name = "Pancetti" + }, + ["0x0489000100ef0502"] = { + name = "Agnes" + }, + ["0x04940001009a0502"] = { + name = "Bunnie" + }, + ["0x0495000101920502"] = { + name = "Dotty" + }, + ["0x0496000100d90502"] = { + name = "Coco" + }, + ["0x04970001007a0502"] = { + name = "Snake" + }, + ["0x04980001014a0502"] = { + name = "Gaston" + }, + ["0x0499000100df0502"] = { + name = "Gabi" + }, + ["0x049a0001014e0502"] = { + name = "Pippy" + }, + ["0x049b000100610502"] = { + name = "Tiffany" + }, + ["0x049c000101400502"] = { + name = "Genji" + }, + ["0x049d000100ed0502"] = { + name = "Ruby" + }, + ["0x049e000101b70502"] = { + name = "Doc" + }, + ["0x049f000103010502"] = { + name = "Claude" + }, + ["0x04a00001016e0502"] = { + name = "Francine" + }, + ["0x04a10001016f0502"] = { + name = "Chrissy" + }, + ["0x04a2000102e80502"] = { + name = "Hopkins" + }, + ["0x04a3000101c90502"] = { + name = "OHare" + }, + ["0x04a4000100d40502"] = { + name = "Carmen" + }, + ["0x04a5000100740502"] = { + name = "Bonbon" + }, + ["0x04a6000100a30502"] = { + name = "Cole" + }, + ["0x04a7000101a60502"] = { + name = "Mira" + }, + ["0x04a80101031e0502"] = { + name = "Toby" + }, + ["0x04b2000101b90502"] = { + name = "Tank" + }, + ["0x04b3000100dd0502"] = { + name = "Rhonda" + }, + ["0x04b40001030c0502"] = { + name = "Spike" + }, + ["0x04b6000102ec0502"] = { + name = "Hornsby" + }, + ["0x04b9000101600502"] = { + name = "Merengue" + }, + ["0x04ba0001005d0502"] = { + name = "Renée" + }, + ["0x04c5000101010502"] = { + name = "Vesta" + }, + ["0x04c6000101670502"] = { + name = "Baabara" + }, + ["0x04c7000100940502"] = { + name = "Eunice" + }, + ["0x04c8000102ed0502"] = { + name = "Stella" + }, + ["0x04c90001030d0502"] = { + name = "Cashmere" + }, + ["0x04cc000100a40502"] = { + name = "Willow" + }, + ["0x04cd000101520502"] = { + name = "Curlos" + }, + ["0x04ce000100db0502"] = { + name = "Wendy" + }, + ["0x04cf000100e10502"] = { + name = "Timbra" + }, + ["0x04d0000101960502"] = { + name = "Frita" + }, + ["0x04d10001009e0502"] = { + name = "Muffy" + }, + ["0x04d2000101a70502"] = { + name = "Pietro" + }, + ["0x04d30101031b0502"] = { + name = "Étoile" + }, + ["0x04dd000100a20502"] = { + name = "Peanut" + }, + ["0x04de000100ce0502"] = { + name = "Blaire" + }, + ["0x04df000100e80502"] = { + name = "Filbert" + }, + ["0x04e0000100f70502"] = { + name = "Pecan" + }, + ["0x04e1000101be0502"] = { + name = "Nibbles" + }, + ["0x04e2000101090502"] = { + name = "Agent S" + }, + ["0x04e3000101650502"] = { + name = "Caroline" + }, + ["0x04e4000101b60502"] = { + name = "Sally" + }, + ["0x04e5000101ad0502"] = { + name = "Static" + }, + ["0x04e6000100820502"] = { + name = "Mint" + }, + ["0x04e7000101320502"] = { + name = "Ricky" + }, + ["0x04e8000101ce0502"] = { + name = "Cally" + }, + ["0x04ea000103180502"] = { + name = "Tasha" + }, + ["0x04eb000102f00502"] = { + name = "Sylvana" + }, + ["0x04ec000100770502"] = { + name = "Poppy" + }, + ["0x04ed000100620502"] = { + name = "Sheldon" + }, + ["0x04ee0001014b0502"] = { + name = "Marshal" + }, + ["0x04ef0001013b0502"] = { + name = "Hazel" + }, + ["0x04fa000101680502"] = { + name = "Rolf" + }, + ["0x04fb000101c60502"] = { + name = "Rowan" + }, + ["0x04fc000102ee0502"] = { + name = "Tybalt" + }, + ["0x04fd0001007b0502"] = { + name = "Bangle" + }, + ["0x04fe000100590502"] = { + name = "Leonardo" + }, + ["0x04ff000101620502"] = { + name = "Claudia" + }, + ["0x0500000100e70502"] = { + name = "Bianca" + }, + ["0x050b000100990502"] = { + name = "Chief" + }, + ["0x050c000101c10502"] = { + name = "Lobo" + }, + ["0x050d000101420502"] = { + name = "Wolfgang" + }, + ["0x050e000100d70502"] = { + name = "Whitney" + }, + ["0x050f000103140502"] = { + name = "Dobie" + }, + ["0x0510000101070502"] = { + name = "Freya" + }, + ["0x0511000101950502"] = { + name = "Fang" + }, + ["0x0513000102e70502"] = { + name = "Vivian" + }, + ["0x0514000101530502"] = { + name = "Skye" + }, + ["0x05150001005b0502"] = { + name = "Kyle" + }, + ["0x0580000000050002"] = { + name = "Fox" + }, + ["0x05810000001c0002"] = { + name = "Falco" + }, + ["0x05840000037e0002"] = { + name = "Wolf" + }, + ["0x05c0000000060002"] = { + name = "Samus" + }, + ["0x05c0000003651302"] = { + name = "Samus Aran" + }, + ["0x05c0000004121302"] = { + name = "Samus - Metroid Dread" + }, + ["0x05c00100001d0002"] = { + name = "Zero Suit Samus" + }, + ["0x05c1000003661302"] = { + name = "Metroid" + }, + ["0x05c20000037f0002"] = { + name = "Ridley" + }, + ["0x05c3000003800002"] = { + name = "Dark Samus" + }, + ["0x05c4000004131302"] = { + name = "E.M.M.I." + }, + ["0x0600000000120002"] = { + name = "Captain Falcon" + }, + ["0x06400100001e0002"] = { + name = "Olimar" + }, + ["0x06420000035f1102"] = { + name = "Pikmin" + }, + ["0x06c00000000f0002"] = { + name = "Little Mac" + }, + ["0x0700000000070002"] = { + name = "Wii Fit Trainer" + }, + ["0x0740000000100002"] = { + name = "Pit" + }, + ["0x0741000000200002"] = { + name = "Dark Pit" + }, + ["0x07420000001f0002"] = { + name = "Palutena" + }, + ["0x07800000002d0002"] = { + name = "Mr. Game & Watch" + }, + ["0x07810000002e0002"] = { + name = "R.O.B. - Famicom" + }, + ["0x0781000000330002"] = { + name = "R.O.B. - NES" + }, + ["0x07820000002f0002"] = { + name = "Duck Hunt" + }, + ["0x078f000003810002"] = { + name = "Ice Climbers" + }, + ["0x07c0000000210002"] = { + name = "Mii Brawler" + }, + ["0x07c0010000220002"] = { + name = "Mii Swordfighter" + }, + ["0x07c0020000230002"] = { + name = "Mii Gunner" + }, + ["0x08000100003e0402"] = { + name = "Inkling Girl" + }, + ["0x08000100025f0402"] = { + name = "Inkling Girl - Lime Green" + }, + ["0x0800010003690402"] = { + name = "Inkling Girl - Neon Pink" + }, + ["0x0800010003820002"] = { + name = "Inkling" + }, + ["0x08000200003f0402"] = { + name = "Inkling Boy" + }, + ["0x0800020002600402"] = { + name = "Inkling Boy - Purple" + }, + ["0x08000200036a0402"] = { + name = "Inkling Boy - Neon Green" + }, + ["0x0800030000400402"] = { + name = "Inkling Squid" + }, + ["0x0800030002610402"] = { + name = "Inkling Squid - Orange" + }, + ["0x08000300036b0402"] = { + name = "Inkling Squid - Neon Purple" + }, + ["0x08010000025d0402"] = { + name = "Callie" + }, + ["0x08020000025e0402"] = { + name = "Marie" + }, + ["0x0803000003760402"] = { + name = "Pearl" + }, + ["0x0804000003770402"] = { + name = "Marina" + }, + ["0x08050100038e0402"] = { + name = "Octoling Girl" + }, + ["0x08050200038f0402"] = { + name = "Octoling Boy" + }, + ["0x0805030003900402"] = { + name = "Octoling Octopus" + }, + ["0x09c0010102690e02"] = { + name = "Mario - Soccer" + }, + ["0x09c00201026a0e02"] = { + name = "Mario - Baseball" + }, + ["0x09c00301026b0e02"] = { + name = "Mario - Tennis" + }, + ["0x09c00401026c0e02"] = { + name = "Mario - Golf" + }, + ["0x09c00501026d0e02"] = { + name = "Mario - Horse Racing" + }, + ["0x09c10101026e0e02"] = { + name = "Luigi - Soccer" + }, + ["0x09c10201026f0e02"] = { + name = "Luigi - Baseball" + }, + ["0x09c1030102700e02"] = { + name = "Luigi - Tennis" + }, + ["0x09c1040102710e02"] = { + name = "Luigi - Golf" + }, + ["0x09c1050102720e02"] = { + name = "Luigi - Horse Racing" + }, + ["0x09c2010102730e02"] = { + name = "Peach - Soccer" + }, + ["0x09c2020102740e02"] = { + name = "Peach - Baseball" + }, + ["0x09c2030102750e02"] = { + name = "Peach - Tennis" + }, + ["0x09c2040102760e02"] = { + name = "Peach - Golf" + }, + ["0x09c2050102770e02"] = { + name = "Peach - Horse Racing" + }, + ["0x09c3010102780e02"] = { + name = "Daisy - Soccer" + }, + ["0x09c3020102790e02"] = { + name = "Daisy - Baseball" + }, + ["0x09c30301027a0e02"] = { + name = "Daisy - Tennis" + }, + ["0x09c30401027b0e02"] = { + name = "Daisy - Golf" + }, + ["0x09c30501027c0e02"] = { + name = "Daisy - Horse Racing" + }, + ["0x09c40101027d0e02"] = { + name = "Yoshi - Soccer" + }, + ["0x09c40201027e0e02"] = { + name = "Yoshi - Baseball" + }, + ["0x09c40301027f0e02"] = { + name = "Yoshi - Tennis" + }, + ["0x09c4040102800e02"] = { + name = "Yoshi - Golf" + }, + ["0x09c4050102810e02"] = { + name = "Yoshi - Horse Racing" + }, + ["0x09c5010102820e02"] = { + name = "Wario - Soccer" + }, + ["0x09c5020102830e02"] = { + name = "Wario - Baseball" + }, + ["0x09c5030102840e02"] = { + name = "Wario - Tennis" + }, + ["0x09c5040102850e02"] = { + name = "Wario - Golf" + }, + ["0x09c5050102860e02"] = { + name = "Wario - Horse Racing" + }, + ["0x09c6010102870e02"] = { + name = "Waluigi - Soccer" + }, + ["0x09c6020102880e02"] = { + name = "Waluigi - Baseball" + }, + ["0x09c6030102890e02"] = { + name = "Waluigi - Tennis" + }, + ["0x09c60401028a0e02"] = { + name = "Waluigi - Golf" + }, + ["0x09c60501028b0e02"] = { + name = "Waluigi - Horse Racing" + }, + ["0x09c70101028c0e02"] = { + name = "Donkey Kong - Soccer" + }, + ["0x09c70201028d0e02"] = { + name = "Donkey Kong - Baseball" + }, + ["0x09c70301028e0e02"] = { + name = "Donkey Kong - Tennis" + }, + ["0x09c70401028f0e02"] = { + name = "Donkey Kong - Golf" + }, + ["0x09c7050102900e02"] = { + name = "Donkey Kong - Horse Racing" + }, + ["0x09c8010102910e02"] = { + name = "Diddy Kong - Soccer" + }, + ["0x09c8020102920e02"] = { + name = "Diddy Kong - Baseball" + }, + ["0x09c8030102930e02"] = { + name = "Diddy Kong - Tennis" + }, + ["0x09c8040102940e02"] = { + name = "Diddy Kong - Golf" + }, + ["0x09c8050102950e02"] = { + name = "Diddy Kong - Horse Racing" + }, + ["0x09c9010102960e02"] = { + name = "Bowser - Soccer" + }, + ["0x09c9020102970e02"] = { + name = "Bowser - Baseball" + }, + ["0x09c9030102980e02"] = { + name = "Bowser - Tennis" + }, + ["0x09c9040102990e02"] = { + name = "Bowser - Golf" + }, + ["0x09c90501029a0e02"] = { + name = "Bowser - Horse Racing" + }, + ["0x09ca0101029b0e02"] = { + name = "Bowser Jr. - Soccer" + }, + ["0x09ca0201029c0e02"] = { + name = "Bowser Jr. - Baseball" + }, + ["0x09ca0301029d0e02"] = { + name = "Bowser Jr. - Tennis" + }, + ["0x09ca0401029e0e02"] = { + name = "Bowser Jr. - Golf" + }, + ["0x09ca0501029f0e02"] = { + name = "Bowser Jr. - Horse Racing" + }, + ["0x09cb010102a00e02"] = { + name = "Boo - Soccer" + }, + ["0x09cb020102a10e02"] = { + name = "Boo - Baseball" + }, + ["0x09cb030102a20e02"] = { + name = "Boo - Tennis" + }, + ["0x09cb040102a30e02"] = { + name = "Boo - Golf" + }, + ["0x09cb050102a40e02"] = { + name = "Boo - Horse Racing" + }, + ["0x09cc010102a50e02"] = { + name = "Baby Mario - Soccer" + }, + ["0x09cc020102a60e02"] = { + name = "Baby Mario - Baseball" + }, + ["0x09cc030102a70e02"] = { + name = "Baby Mario - Tennis" + }, + ["0x09cc040102a80e02"] = { + name = "Baby Mario - Golf" + }, + ["0x09cc050102a90e02"] = { + name = "Baby Mario - Horse Racing" + }, + ["0x09cd010102aa0e02"] = { + name = "Baby Luigi - Soccer" + }, + ["0x09cd020102ab0e02"] = { + name = "Baby Luigi - Baseball" + }, + ["0x09cd030102ac0e02"] = { + name = "Baby Luigi - Tennis" + }, + ["0x09cd040102ad0e02"] = { + name = "Baby Luigi - Golf" + }, + ["0x09cd050102ae0e02"] = { + name = "Baby Luigi - Horse Racing" + }, + ["0x09ce010102af0e02"] = { + name = "Birdo - Soccer" + }, + ["0x09ce020102b00e02"] = { + name = "Birdo - Baseball" + }, + ["0x09ce030102b10e02"] = { + name = "Birdo - Tennis" + }, + ["0x09ce040102b20e02"] = { + name = "Birdo - Golf" + }, + ["0x09ce050102b30e02"] = { + name = "Birdo - Horse Racing" + }, + ["0x09cf010102b40e02"] = { + name = "Rosalina - Soccer" + }, + ["0x09cf020102b50e02"] = { + name = "Rosalina - Baseball" + }, + ["0x09cf030102b60e02"] = { + name = "Rosalina - Tennis" + }, + ["0x09cf040102b70e02"] = { + name = "Rosalina - Golf" + }, + ["0x09cf050102b80e02"] = { + name = "Rosalina - Horse Racing" + }, + ["0x09d0010102b90e02"] = { + name = "Metal Mario - Soccer" + }, + ["0x09d0020102ba0e02"] = { + name = "Metal Mario - Baseball" + }, + ["0x09d0030102bb0e02"] = { + name = "Metal Mario - Tennis" + }, + ["0x09d0040102bc0e02"] = { + name = "Metal Mario - Golf" + }, + ["0x09d0050102bd0e02"] = { + name = "Metal Mario - Horse Racing" + }, + ["0x09d1010102be0e02"] = { + name = "Pink Gold Peach - Soccer" + }, + ["0x09d1020102bf0e02"] = { + name = "Pink Gold Peach - Baseball" + }, + ["0x09d1030102c00e02"] = { + name = "Pink Gold Peach - Tennis" + }, + ["0x09d1040102c10e02"] = { + name = "Pink Gold Peach - Golf" + }, + ["0x09d1050102c20e02"] = { + name = "Pink Gold Peach - Horse Racing" + }, + ["0x0a00000103ab0502"] = { + name = "Orville" + }, + ["0x0a01000103ac0502"] = { + name = "Wilbur" + }, + ["0x0a02000103b30502"] = { + name = "C.J." + }, + ["0x0a03000103b40502"] = { + name = "Flick" + }, + ["0x0a04000103b50502"] = { + name = "Daisy Mae" + }, + ["0x0a05000103b80502"] = { + name = "Harvey" + }, + ["0x0a06000103ba0502"] = { + name = "Wisp" + }, + ["0x0a07000103bc0502"] = { + name = "Niko" + }, + ["0x0a08000103bd0502"] = { + name = "Wardell" + }, + ["0x0a09000103c00502"] = { + name = "Sherb" + }, + ["0x0a0a000103c10502"] = { + name = "Megan" + }, + ["0x0a0b000103c20502"] = { + name = "Dom" + }, + ["0x0a0c000103c30502"] = { + name = "Audie" + }, + ["0x0a0d000103c40502"] = { + name = "Cyd" + }, + ["0x0a0e000103c50502"] = { + name = "Judy" + }, + ["0x0a0f000103c60502"] = { + name = "Raymond" + }, + ["0x0a10000103c70502"] = { + name = "Reneigh" + }, + ["0x0a11000103c80502"] = { + name = "Sasha" + }, + ["0x0a12000103c90502"] = { + name = "Ione" + }, + ["0x0a13000103ca0502"] = { + name = "Tiansheng" + }, + ["0x0a14000103cb0502"] = { + name = "Shino" + }, + ["0x0a15000103cc0502"] = { + name = "Marlo" + }, + ["0x0a16000103cd0502"] = { + name = "Petri" + }, + ["0x0a17000103ce0502"] = { + name = "Cephalobot" + }, + ["0x0a18000103cf0502"] = { + name = "Quinn" + }, + ["0x0a19000103d00502"] = { + name = "Chabwick" + }, + ["0x0a1a000103d10502"] = { + name = "Zoe" + }, + ["0x0a1b000103d20502"] = { + name = "Ace" + }, + ["0x0a1c000103d30502"] = { + name = "Rio" + }, + ["0x0a1d000103d40502"] = { + name = "Frett" + }, + ["0x0a1e000103d50502"] = { + name = "Azalea" + }, + ["0x0a1f000103d60502"] = { + name = "Roswell" + }, + ["0x0a20000103d70502"] = { + name = "Faith" + }, + ["0x0a400000041d0002"] = { + name = "Min Min" + }, + ["0x1902000003830002"] = { + name = "Ivysaur" + }, + ["0x1906000000240002"] = { + name = "Charizard" + }, + ["0x1907000003840002"] = { + name = "Squirtle" + }, + ["0x1919000000090002"] = { + name = "Pikachu" + }, + ["0x1927000000260002"] = { + name = "Jigglypuff" + }, + ["0x19960000023d0002"] = { + name = "Mewtwo" + }, + ["0x19ac000003850002"] = { + name = "Pichu" + }, + ["0x1ac0000000110002"] = { + name = "Lucario" + }, + ["0x1b92000000250002"] = { + name = "Greninja" + }, + ["0x1bd7000003860002"] = { + name = "Incineroar" + }, + ["0x1d000001025c0d02"] = { + name = "Shadow Mewtwo" + }, + ["0x1d01000003750d02"] = { + name = "Detective Pikachu" + }, + ["0x1d40000003870002"] = { + name = "Pokemon Trainer" + }, + ["0x1f000000000a0002"] = { + name = "Kirby" + }, + ["0x1f00000002540c02"] = { + name = "Kirby" + }, + ["0x1f01000000270002"] = { + name = "Meta Knight" + }, + ["0x1f01000002550c02"] = { + name = "Meta Knight" + }, + ["0x1f02000000280002"] = { + name = "King Dedede" + }, + ["0x1f02000002560c02"] = { + name = "King Dedede" + }, + ["0x1f03000002570c02"] = { + name = "Waddle Dee" + }, + ["0x1f400000035e1002"] = { + name = "Qbby" + }, + ["0x21000000000b0002"] = { + name = "Marth" + }, + ["0x2101000000180002"] = { + name = "Ike" + }, + ["0x2102000000290002"] = { + name = "Lucina" + }, + ["0x21030000002a0002"] = { + name = "Robin" + }, + ["0x2104000002520002"] = { + name = "Roy" + }, + ["0x21050000025a0002"] = { + name = "Corrin" + }, + ["0x2105010003630002"] = { + name = "Corrin - Player 2" + }, + ["0x2106000003601202"] = { + name = "Alm" + }, + ["0x2107000003611202"] = { + name = "Celica" + }, + ["0x21080000036f1202"] = { + name = "Chrom" + }, + ["0x2108000003880002"] = { + name = "Chrom" + }, + ["0x2109000003701202"] = { + name = "Tiki" + }, + ["0x210b000003a50002"] = { + name = "Byleth" + }, + ["0x22400000002b0002"] = { + name = "Shulk" + }, + ["0x22800000002c0002"] = { + name = "Ness" + }, + ["0x2281000002510002"] = { + name = "Lucas" + }, + ["0x22c00000003a0202"] = { + name = "Chibi Robo" + }, + ["0x3200000000300002"] = { + name = "Sonic" + }, + ["0x32400000025b0002"] = { + name = "Bayonetta" + }, + ["0x3240010003640002"] = { + name = "Bayonetta - Player 2" + }, + ["0x3340000000320002"] = { + name = "Pac-Man" + }, + ["0x3380000003781402"] = { + name = "Solaire of Astora" + }, + ["0x3480000000310002"] = { + name = "Mega Man" + }, + ["0x3480000002580002"] = { + name = "Mega Man - Gold Edition" + }, + ["0x3480000003791502"] = { + name = "Mega Man" + }, + ["0x34c0000002530002"] = { + name = "Ryu" + }, + ["0x34c1000003890002"] = { + name = "Ken" + }, + ["0x3500010002e10f02"] = { + name = "One-Eyed Rathalos and Rider - Male" + }, + ["0x3500020002e20f02"] = { + name = "One-Eyed Rathalos and Rider - Female" + }, + ["0x3501000002e30f02"] = { + name = "Nabiru" + }, + ["0x3502010002e40f02"] = { + name = "Rathian and Cheval" + }, + ["0x3503010002e50f02"] = { + name = "Barioth and Ayuria" + }, + ["0x3504010002e60f02"] = { + name = "Qurupeco and Dan" + }, + ["0x35050000040c0f02"] = { + name = "Razewing Ratha" + }, + ["0x35060000040d0f02"] = { + name = "Ena" + }, + ["0x35070000040e0f02"] = { + name = "Tsukino" + }, + ["0x35080000040f1802"] = { + name = "Magnamalo" + }, + ["0x3509000004101802"] = { + name = "Palico" + }, + ["0x35090100042b1802"] = { + name = "Palico" + }, + ["0x350a000004111802"] = { + name = "Palamute" + }, + ["0x350a0100042c1802"] = { + name = "Palamute" + }, + ["0x350b0000042d1802"] = { + name = "Malzeno" + }, + ["0x35c0000002500a02"] = { + name = "Shovel Knight" + }, + ["0x35c0000003920a02"] = { + name = "Shovel Knight - Gold Edition" + }, + ["0x35c10000036c0a02"] = { + name = "Plague Knight" + }, + ["0x35c20000036d0a02"] = { + name = "Specter Knight" + }, + ["0x35c30000036e0a02"] = { + name = "King Knight" + }, + ["0x3600000002590002"] = { + name = "Cloud" + }, + ["0x3600010003620002"] = { + name = "Cloud - Player 2" + }, + ["0x3640000003a20002"] = { + name = "Hero" + }, + ["0x3740000103741402"] = { + name = "Super Mario Cereal" + }, + ["0x37800000038a0002"] = { + name = "Snake" + }, + ["0x37c00000038b0002"] = { + name = "Simon" + }, + ["0x37c10000038c0002"] = { + name = "Richter" + }, + ["0x3800000103931702"] = { + name = "Pawapuro" + }, + ["0x3801000103941702"] = { + name = "Ikari" + }, + ["0x3802000103951702"] = { + name = "Daijobu" + }, + ["0x3803000103961702"] = { + name = "Hayakawa" + }, + ["0x3804000103971702"] = { + name = "Yabe" + }, + ["0x3805000103981702"] = { + name = "Ganda" + }, + ["0x3840000104241902"] = { + name = "Yuga Ohdo" + }, + ["0x3841000104251902"] = { + name = "Tatsuhisa “Luke” Kamijō" + }, + ["0x3842000104261902"] = { + name = "Gakuto Sōgetsu" + }, + ["0x3843000104271902"] = { + name = "Romin Kirishima" + }, + ["0x3844000104281902"] = { + name = "Roa Kirishima" + }, + ["0x3845000104291902"] = { + name = "Nail Saionji" + }, + ["0x38460001042a1902"] = { + name = "Asana Mutsuba" + }, + ["0x38c0000003911602"] = { + name = "Loot Goblin" + }, + ["0x3a00000003a10002"] = { + name = "Joker" + }, + ["0x3b40000003a30002"] = { + name = "Banjo & Kazooie" + }, + ["0x3c80000003a40002"] = { + name = "Terry" + } + }, + game_series = { + ["0x000"] = "Super Mario", + ["0x001"] = "Super Mario", + ["0x002"] = "Super Mario", + ["0x008"] = "Yoshi's Woolly World", + ["0x00c"] = "Donkey Kong", + ["0x010"] = "The Legend of Zelda", + ["0x014"] = "Breath of the Wild", + ["0x018"] = "Animal Crossing", + ["0x019"] = "Animal Crossing", + ["0x01a"] = "Animal Crossing", + ["0x01b"] = "Animal Crossing", + ["0x01c"] = "Animal Crossing", + ["0x020"] = "Animal Crossing", + ["0x021"] = "Animal Crossing", + ["0x022"] = "Animal Crossing", + ["0x023"] = "Animal Crossing", + ["0x024"] = "Animal Crossing", + ["0x025"] = "Animal Crossing", + ["0x026"] = "Animal Crossing", + ["0x027"] = "Animal Crossing", + ["0x028"] = "Animal Crossing", + ["0x029"] = "Animal Crossing", + ["0x02a"] = "Animal Crossing", + ["0x02b"] = "Animal Crossing", + ["0x02c"] = "Animal Crossing", + ["0x02d"] = "Animal Crossing", + ["0x02e"] = "Animal Crossing", + ["0x02f"] = "Animal Crossing", + ["0x030"] = "Animal Crossing", + ["0x031"] = "Animal Crossing", + ["0x032"] = "Animal Crossing", + ["0x033"] = "Animal Crossing", + ["0x034"] = "Animal Crossing", + ["0x035"] = "Animal Crossing", + ["0x036"] = "Animal Crossing", + ["0x037"] = "Animal Crossing", + ["0x038"] = "Animal Crossing", + ["0x039"] = "Animal Crossing", + ["0x03a"] = "Animal Crossing", + ["0x03b"] = "Animal Crossing", + ["0x03c"] = "Animal Crossing", + ["0x03d"] = "Animal Crossing", + ["0x03e"] = "Animal Crossing", + ["0x03f"] = "Animal Crossing", + ["0x040"] = "Animal Crossing", + ["0x041"] = "Animal Crossing", + ["0x042"] = "Animal Crossing", + ["0x043"] = "Animal Crossing", + ["0x044"] = "Animal Crossing", + ["0x045"] = "Animal Crossing", + ["0x046"] = "Animal Crossing", + ["0x047"] = "Animal Crossing", + ["0x048"] = "Animal Crossing", + ["0x049"] = "Animal Crossing", + ["0x04a"] = "Animal Crossing", + ["0x04b"] = "Animal Crossing", + ["0x04c"] = "Animal Crossing", + ["0x04d"] = "Animal Crossing", + ["0x04e"] = "Animal Crossing", + ["0x04f"] = "Animal Crossing", + ["0x050"] = "Animal Crossing", + ["0x051"] = "Animal Crossing", + ["0x0a0"] = "Animal Crossing", + ["0x0a1"] = "Animal Crossing", + ["0x0a2"] = "Animal Crossing", + ["0x058"] = "Star Fox", + ["0x05c"] = "Metroid", + ["0x060"] = "F-Zero", + ["0x064"] = "Pikmin", + ["0x06c"] = "Punch Out", + ["0x070"] = "Wii Fit", + ["0x074"] = "Kid Icarus", + ["0x078"] = "Classic Nintendo", + ["0x07c"] = "Mii", + ["0x080"] = "Splatoon", + ["0x09c"] = "Mario Sports Superstars", + ["0x09d"] = "Mario Sports Superstars", + ["0x0a4"] = "ARMS", + ["0x190"] = "Pokemon", + ["0x191"] = "Pokemon", + ["0x192"] = "Pokemon", + ["0x199"] = "Pokemon", + ["0x19a"] = "Pokemon", + ["0x1ac"] = "Pokemon", + ["0x1b9"] = "Pokemon", + ["0x1bd"] = "Pokemon", + ["0x1d0"] = "Pokemon", + ["0x1d4"] = "Pokemon", + ["0x1f0"] = "Kirby", + ["0x1f4"] = "BoxBoy!", + ["0x210"] = "Fire Emblem", + ["0x224"] = "Xenoblade", + ["0x228"] = "Earthbound", + ["0x22c"] = "Chibi Robo", + ["0x320"] = "Sonic", + ["0x324"] = "Bayonetta", + ["0x334"] = "Pac-man", + ["0x338"] = "Dark Souls", + ["0x348"] = "Megaman", + ["0x34c"] = "Street fighter", + ["0x350"] = "Monster Hunter", + ["0x35c"] = "Shovel Knight", + ["0x360"] = "Final Fantasy", + ["0x364"] = "Dragon Quest", + ["0x374"] = "Kellogs", + ["0x378"] = "Metal Gear Solid", + ["0x37c"] = "Castlevania", + ["0x380"] = "Power Pros", + ["0x384"] = "Yu-Gi-Oh!", + ["0x38c"] = "Diablo", + ["0x3a0"] = "Persona", + ["0x3b4"] = "Banjo Kazooie", + ["0x3c8"] = "Fatal Fury" + }, + types = { + ["0x00"] = "Figure", + ["0x01"] = "Card", + ["0x02"] = "Yarn", + ["0x03"] = "Band" + } +} + +return amiibo_tools diff --git a/client/luascripts/hf_14a_i2crevive.lua b/client/luascripts/hf_14a_i2crevive.lua index 3610979b6..59659bb5c 100644 --- a/client/luascripts/hf_14a_i2crevive.lua +++ b/client/luascripts/hf_14a_i2crevive.lua @@ -62,7 +62,7 @@ function main(args) local i local cmds = {} - --check for params + --check for params for o, a in getopt.getopt(args, 'h') do if o == 'h' then return help() end end diff --git a/client/luascripts/hf_i2c_plus_2k_utils.lua b/client/luascripts/hf_i2c_plus_2k_utils.lua new file mode 100644 index 000000000..5e7652316 --- /dev/null +++ b/client/luascripts/hf_i2c_plus_2k_utils.lua @@ -0,0 +1,310 @@ +local getopt = require('getopt') +local lib14a = require('read14a') +local cmds = require('commands') +local utils = require('utils') +local ansicolors = require('ansicolors') + +--- Commands +NTAG_I2C_PLUS_2K = '0004040502021503C859' +GET_VERSION = '60' +SELECT_SECTOR_PKT1 = 'C2FF' +SELECT_SECTOR0_PKT2 = '00000000' +SELECT_SECTOR1_PKT2 = '01000000' +READ_BLOCK = '30' +WRITE_BLOCK = 'A2' +ACK = '0A' +NAK = '00' +--- + + +--- Arguments +copyright = '' +author = 'Shain Lakin' +version = 'v1.0.0' +desc =[[ + +This script can be used to read blocks, write blocks, dump sectors, +or write a files hex bytes to sector 0 or 1 on the NTAG I2C PLUS 2K tag. + +]] + +example =[[ + + Read block 04 from sector 1: + script run hf_i2c_plus_2k_utils -m r -s 1 -b 04 + + Write FFFFFFFF to block A0 sector 1: + script run hf_i2c_plus_2k_utils -m w -s 1 -b A0 -d FFFFFFFF + + Dump sector 1 user memory to console and file: + script run hf_i2c_plus_2k_utils -m d -s 1 + + Write a files hex bytes to sector 1 starting at block 04: + script run hf_i2c_plus_2k_utils -m f -s 1 -f data.txt + +]] +usage = [[ + + Read mode: + script run hf_i2c_plus_2k_utils -m r -s -b + + Write mode: + script run hf_i2c_plus_2k_utils -m w -s -b -d + + Dump mode: + script run hf_i2c_plus_2k_utils -m d -s + + File mode: + script run hf_i2c_plus_2k_utils -m f -s -f + +]] +arguments = [[ + -h this help + -m mode (r/w/f) + -b block (hex) + -f file + -s sector (0/1) + -d data (hex) +]] +--- + + +--- Help function +local function help() + + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) + +end +--- + + +--- Message function +local function msg(string) + print(ansicolors.magenta..string.rep('-',29)..ansicolors.reset) + print(ansicolors.cyan..string..ansicolors.reset) + print(ansicolors.magenta..string.rep('-',29)..ansicolors.reset) +end +--- + + +--- Error handling +local function warn(err) + + print(ansicolors.magenta.."ERROR:"..ansicolors.reset,err) + core.clearCommandBuffer() + return nil, err + +end +--- + + +--- Setup tx/rx +local function sendRaw(rawdata, options) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + + lib14a.ISO14A_COMMAND.ISO14A_RAW + + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + + local c = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, + arg2 = string.len(rawdata)/2, + data = rawdata} + + return c:sendMIX(options.ignore_response) +end +--- + + +--- Function to connect +local function connect() + core.clearCommandBuffer() + + info, err = lib14a.read(true, true) + if err then + lib14a.disconnect() + return error(err) + else + return info.uid + end + core.clearCommandBuffer() + +end +--- + + +--- Function to disconnect +local function disconnect() + core.clearCommandBuffer() + lib14a.disconnect() +end +--- + + +--- Function to get response data +local function getResponseData(usbpacket) + + local resp = Command.parse(usbpacket) + local len = tonumber(resp.arg1) * 2 + return string.sub(tostring(resp.data), 0, len); + +end +--- + + +--- Function to send raw bytes +local function send(payload) + + local usb, err = sendRaw(payload,{ignore_response = false}) + if err then return warn(err) end + return getResponseData(usb) + +end +--- + +--- Function to select sector +local function select_sector(sector) + send(SELECT_SECTOR_PKT1) + if sector == '0' then + send(SELECT_SECTOR0_PKT2) + elseif sector == '1' then + send(SELECT_SECTOR1_PKT2) + end +end +--- + +--- Function to write file to sector +local function filewriter(file,sector) + file_bytes = utils.ReadDumpFile(file) + len = string.len(file_bytes) / 8 + start_char = 1 + end_char = 8 + block_counter = 4 + -- NTAG_I2C_PLUS_2K:SECTOR_0:225,SECTOR_1:255 + end_block = 225 + connect() + select_sector(sector) + for count = 1, len do + block = file_bytes:sub(start_char, end_char) + data = send(WRITE_BLOCK..string.format("%02x",block_counter)..block) + print('[*] Writing bytes '..block..' to page '..string.format("%02x", block_counter)) + if data == ACK then + print(ansicolors.cyan..'[*] Received ACK, write successful'..ansicolors.reset) + else + print(ansicolors.magenta..'[!] Write failed'..ansicolors.reset) + end + start_char = start_char + 8 + end_char = end_char + 8 + block_counter = block_counter + 1 + if block_counter == end_block then + print(ansicolors.magenta..'[!] Not enough memory space!'..ansicolors.reset) + break + end + end + disconnect() +end +--- + +--- Function to dump user memory to console and disk +local function dump(sector,uid) + connect() + select_sector(sector) + counter = 0 + dest = uid..'.hex' + file = io.open(dest, 'a') + io.output(file) + print("\n[+] Dumping sector "..sector.."\n") + print(ansicolors.magenta..string.rep('--',16)..ansicolors.reset) + for count = 1, 64 do + result = send(READ_BLOCK..string.format("%02x", counter)) + print(ansicolors.cyan..result:sub(1,32)..ansicolors.reset) + io.write(result:sub(1,32)) + counter = counter + 4 + end + io.close(file) + print(ansicolors.magenta..string.rep('--',16)..ansicolors.reset) + print("\n[+] Memory dump saved to "..uid..".hex") + disconnect() +end +--- + + +--- Function to read and write blocks +local function exec(cmd, sector, block, bytes) + connect() + select_sector(sector) + if cmd == READ_BLOCK then + data = send(cmd..block) + msg(data:sub(1,8)) + elseif cmd == WRITE_BLOCK then + if bytes == 'NOP' then + err = '[!] You need to pass some data' + warn(err) + print(usage) + do return end + else + data = send(cmd..block..bytes) + if data == ACK then + print(ansicolors.cyan..'[+] Received ACK, write succesful'..ansicolors.reset) + elseif data ~= ACK then + print(ansicolors.magenta..'[!] Write failed'..ansicolors.reset) + end + end + end + disconnect() + return(data) +end +--- + + +--- Main +local function main(args) + + for o, a in getopt.getopt(args, 'm:b:s:d:f:h') do + if o == 'm' then mode = a end + if o == 'b' then block = a end + if o == 's' then sector = a end + if o == 'd' then bytes = a end + if o == 'f' then file = a end + if o == 'h' then return help() end + end + + uid = connect() + + connect() + version = send(GET_VERSION) + disconnect() + + if version == NTAG_I2C_PLUS_2K then + + if mode == 'r' then + print('\n[+] Reading sector '..sector..' block '..block) + exec(READ_BLOCK,sector,block,bytes) + elseif mode == 'w' then + print('\n[+] Writing '..bytes..' to sector '..sector..' block '..block) + exec(WRITE_BLOCK,sector,block,bytes) + elseif mode == 'f' then + filewriter(file,sector) + elseif mode == 'd' then + dump(sector,uid) + end + + else + return print(usage) + end + + if command == '' then return print(usage) end + +end +--- + + +main(args) diff --git a/client/luascripts/hf_mf_dump-luxeo.lua b/client/luascripts/hf_mf_dump_luxeo.lua similarity index 98% rename from client/luascripts/hf_mf_dump-luxeo.lua rename to client/luascripts/hf_mf_dump_luxeo.lua index 2b2c5d9c7..aaf7d572f 100644 --- a/client/luascripts/hf_mf_dump-luxeo.lua +++ b/client/luascripts/hf_mf_dump_luxeo.lua @@ -1,7 +1,7 @@ --- -- This Lua script is designed to run with Iceman/RRG Proxmark3 fork -- Just copy hf_mf_dump-luxeo.lua to client/luascripts/ --- and run "script run hf_mf_dump-luxeo" +-- and run "script run hf_mf_dump_luxeo" -- requirements local cmds = require('commands') @@ -12,16 +12,16 @@ local ansicolors = require('ansicolors') copyright = '' author = '0xdrrb' -version = 'v0.1.2' +version = 'v0.1.3' desc = [[ This is a script that tries to dump and decrypt the data of a specific type of Mifare laundromat token. OBS! Tag must be on the antenna. ]] example = [[ - script run hf_mf_dump-luxeo + script run hf_mf_dump_luxeo ]] usage = [[ -script run hf_mf_dump-luxeo +script run hf_mf_dump_luxeo ]] arguments = [[ -h This help diff --git a/client/luascripts/hf_mf_em_util.lua b/client/luascripts/hf_mf_em_util.lua index 69537d7dd..e82bc9d1e 100644 --- a/client/luascripts/hf_mf_em_util.lua +++ b/client/luascripts/hf_mf_em_util.lua @@ -63,8 +63,8 @@ local function card_format(key_a,key_b,ab,user,s70) core.console(cmd) print(cmd) core.clearCommandBuffer() - if s70 == false and k > 15 then - return + if s70 == false and k > 15 then + return end end end diff --git a/client/luascripts/hf_mf_sim_hid.lua b/client/luascripts/hf_mf_sim_hid.lua new file mode 100644 index 000000000..4091a10b5 --- /dev/null +++ b/client/luascripts/hf_mf_sim_hid.lua @@ -0,0 +1,185 @@ +-- +-- hf_mf_sim_hid.lua - A tool to clone a large number of tags at once. +-- Adapted from lf_hid_bulkclone.lua +-- Created 16.08.2022 +local getopt = require('getopt') +local ansicolors = require('ansicolors') + +copyright = '' +author = "Michael Micsen" +version = 'v0.0.1' +desc = [[ +Perform simulation of Mifare credentials with HID encoding +This script only supports: H10301 +]] +example = [[ + -- + script run hf_mf_sim_hid.lua -f 1 -c 10000 +]] +usage = [[ +script run hf_mf_sim_hid.lua -f facility -c card_number +]] +arguments = [[ + -h : this help + -f : facility id + -c : starting card id +]] +local DEBUG = true +--local bxor = bit32.bxor +local bor = bit32.bor +local lshift = bit32.lshift +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end +--[[Implement a function to simply visualize the bitstream in a text format +--This is especially helpful for troubleshooting bitwise math issues]]-- +local function toBits(num,bits) + -- returns a table of bits, most significant first. + bits = bits or math.max(1, select(2, math.frexp(num))) + local t = {} -- will contain the bits + for b = bits, 1, -1 do + t[b] = math.fmod(num, 2) + num = math.floor((num - t[b]) / 2) + end + return table.concat(t) +end + +--[[ + Likely, I'm an idiot, but I couldn't find any parity functions in Lua + This can also be done with a combination of bitwise operations (in fact, + is the canonically "correct" way to do it, but my brain doesn't just + default to this and so counting some ones is good enough for me +]]-- +local function evenparity(s) + local _, count = string.gsub(s, '1', '') + local p = count % 2 + if (p == 0) then + return false + else + return true + end +end + +local function isempty(s) + return s == nil or s == '' +end + +--[[ + The Proxmark3 "clone" functions expect the data to be in hex format so + take the card id number and facility ID as arguments and construct the + hex. This should be easy enough to extend to non 26bit formats +]]-- +local function cardHex(i, f) + + fac = lshift(f, 16) + id = bor(i, fac) + stream = toBits(id, 24) + + --As the function defaults to even parity and returns a boolean, + --perform a 'not' function to get odd parity + high = evenparity(string.sub(stream,1,12)) and 1 or 0 + low = not evenparity(string.sub(stream,13)) and 1 or 0 + bits = bor( lshift(id, 1), low) + bits = bor( bits, lshift(high, 25)) + + --Add sentinel bit + sentinel = lshift(1, 26) + bits = bor(bits, sentinel) + + + return ('%08x'):format(bits) +end +--- +-- main +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + --I really wish a better getopt function would be brought in supporting + --long arguments, but it seems this library was chosen for BSD style + --compatibility + for o, a in getopt.getopt(args, 'f:c:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 0') + facility = 0 + else + facility = a + end + end + if o == 'c' then + print(a) + if isempty(a) then return oops('You must supply the flag -c (card number)1') end + cardnum = a + end + end + + --Due to my earlier complaints about how this specific getopt library + --works, specifying ':' does not enforce supplying a value, thus we + --need to do these checks all over again. + if isempty(cardnum) then return oops('You must supply the flag -c (card number)2') end + --If the facility ID is non specified, ensure we code it as zero + if isempty(facility) then + print('Using 0 for the facility code as -f was not supplied') + facility = 0 + end + + -- Write the MAD to read for a Mifare HID credential + core.console('hf mf esetblk -b 1 -d 1B014D48000000000000000000000000') + core.console('hf mf esetblk -b 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A') + --Write the sector trailer for the credential sector + core.console('hf mf esetblk -b 7 -d 484944204953787788AA204752454154') + local cardh = cardHex(cardnum, facility) + print('Hex') + print(cardh) + core.console( ('hf mf esetblk -b 5 -d 020000000000000000000000%s'):format(cardh) ) + + core.console('hf mf sim --1k -i') +end + +main(args) diff --git a/client/luascripts/hf_mf_uidbruteforce.lua b/client/luascripts/hf_mf_uidbruteforce.lua index 62872e2a3..4835ba03b 100644 --- a/client/luascripts/hf_mf_uidbruteforce.lua +++ b/client/luascripts/hf_mf_uidbruteforce.lua @@ -109,8 +109,8 @@ local function main(args) command = 'hf 14a sim -t 1 -u ' .. uid_format msg('Bruteforcing Mifare Classic card numbers') elseif mftype == 'mfc4' then - command = 'hf 14a sim -t 8 -u ' .. uid_format - msg('Bruteforcing Mifare Classic 4K card numbers') + command = 'hf 14a sim -t 8 -u ' .. uid_format + msg('Bruteforcing Mifare Classic 4K card numbers') elseif mftype == 'mfu' then command = 'hf 14a sim -t 2 -u ' .. uid_format msg('Bruteforcing Mifare Ultralight card numbers') diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index e28dbdd08..93e64e333 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -13,7 +13,7 @@ local err_lock = 'use -k or change cfg0 block' local _print = 0 copyright = '' author = 'Nathan Glaser' -version = 'v1.0.4' +version = 'v1.0.5' date = 'Created - Jan 2022' desc = 'This script enables easy programming of an Ultimate Mifare Magic card' example = [[ @@ -50,17 +50,17 @@ arguments = [[ -c read magic configuration -u UID (8-14 hexsymbols), set UID on tag -t tag type to impersonate - 1 = Mifare Mini S20 4-byte 12 = NTAG 210 - 2 = Mifare Mini S20 7-byte 13 = NTAG 212 - 3 = Mifare 1k S50 4-byte 14 = NTAG 213 - 4 = Mifare 1k S50 7-byte 15 = NTAG 215 - 5 = Mifare 4k S70 4-byte 16 = NTAG 216 - 6 = Mifare 4k S70 7-byte 17 = NTAG I2C 1K - *** 7 = UL - NOT WORKING FULLY 18 = NTAG I2C 2K - *** 8 = UL-C - NOT WORKING FULLY 19 = NTAG I2C 1K PLUS - 9 = UL EV1 48b 20 = NTAG I2C 2K PLUS - 10 = UL EV1 128b 21 = NTAG 213F - *** 11 = UL Plus - NOT WORKING YET 22 = NTAG 216F + 1 = Mifare Mini S20 4-byte 12 = NTAG 210 + 2 = Mifare Mini S20 7-byte 13 = NTAG 212 + 3 = Mifare 1k S50 4-byte 14 = NTAG 213 + 4 = Mifare 1k S50 7-byte 15 = NTAG 215 + 5 = Mifare 4k S70 4-byte 16 = NTAG 216 + 6 = Mifare 4k S70 7-byte 17 = NTAG I2C 1K + *** 7 = UL - NOT WORKING FULLY 18 = NTAG I2C 2K + *** 8 = UL-C - NOT WORKING FULLY 19 = NTAG I2C 1K PLUS + 9 = UL EV1 48b 20 = NTAG I2C 2K PLUS + 10 = UL EV1 128b 21 = NTAG 213F + *** 11 = UL Plus - NOT WORKING YET 22 = NTAG 216F -p NTAG password (8 hexsymbols), set NTAG password on tag. -a NTAG pack ( 4 hexsymbols), set NTAG pack on tag. @@ -178,7 +178,7 @@ local function read_config() if not info then return false, "Can't select card" end -- read Ultimate Magic Card CONFIG if magicconfig == nil then - magicconfig = send("CF".._key.."C6") + magicconfig = send("CF".._key.."C6") else print('No Config') end -- extract data from CONFIG - based on CONFIG in https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md#gen-4-gtu @@ -186,7 +186,7 @@ local function read_config() atqaf = atqa1..' '..atqa2 cardtype, cardprotocol, gtustr, atsstr = 'unknown', 'unknown', 'unknown', 'unknown' if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end - if #magicconfig ~= 64 then lib14a.disconnect(); return nil, "partial read of configuration, "..err_lock end + if #magicconfig ~= 64 and #magicconfig ~= 68 then lib14a.disconnect(); return nil, "partial read of configuration, "..err_lock end if gtumode == '00' then gtustr = 'Pre-write/Shadow Mode' elseif gtumode == '01' then gtustr = 'Restore Mode' elseif gtumode == '02' then gtustr = 'Disabled' @@ -196,93 +196,92 @@ local function read_config() else atsstr = (string.sub(ats, 3)) end if ulprotocol == '00' then - cardprotocol = 'MIFARE Classic Protocol' - ultype = 'Disabled' - if uidlength == '00' then - uid = send("CF".._key.."CE00"):sub(1,8) - if atqaf == '00 04' and sak == '09' then cardtype = 'MIFARE Mini S20 4-byte UID' - elseif atqaf == '00 04' and sak == '08' then cardtype = 'MIFARE 1k S50 4-byte UID' - elseif atqaf == '00 02' and sak == '18' then cardtype = 'MIFARE 4k S70 4-byte UID' - end - elseif uidlength == '01' then - uid = send("CF".._key.."CE00"):sub(1,14) - if atqaf == '00 44' and sak == '09' then cardtype = 'MIFARE Mini S20 7-byte UID' - elseif atqaf == '00 44' and sak == '08' then cardtype = 'MIFARE 1k S50 7-byte UID' - elseif atqaf == '00 42' and sak == '18' then cardtype = 'MIFARE 4k S70 7-byte UID' - end - end + cardprotocol = 'MIFARE Classic Protocol' + ultype = 'Disabled' + if uidlength == '00' then + uid = send("CF".._key.."CE00"):sub(1,8) + if atqaf == '00 04' and sak == '09' then cardtype = 'MIFARE Mini S20 4-byte UID' + elseif atqaf == '00 04' and sak == '08' then cardtype = 'MIFARE 1k S50 4-byte UID' + elseif atqaf == '00 02' and sak == '18' then cardtype = 'MIFARE 4k S70 4-byte UID' + end + elseif uidlength == '01' then + uid = send("CF".._key.."CE00"):sub(1,14) + if atqaf == '00 44' and sak == '09' then cardtype = 'MIFARE Mini S20 7-byte UID' + elseif atqaf == '00 44' and sak == '08' then cardtype = 'MIFARE 1k S50 7-byte UID' + elseif atqaf == '00 42' and sak == '18' then cardtype = 'MIFARE 4k S70 7-byte UID' + end + end elseif ulprotocol == '01' then - -- Read Ultralight config only if UL protocol is enabled - cardprotocol = 'MIFARE Ultralight/NTAG' - block0 = send("3000") - uid0 = block0:sub(1,6) - uid = uid0..block0:sub(9,16) - if ulmode == '00' then ultype = 'Ultralight EV1' - elseif ulmode == '01' then ultype = 'NTAG21x' - elseif ulmode == '02' then ultype = 'Ultralight-C' - elseif ulmode == '03' then ultype = 'Ultralight' - end - -- read VERSION - cversion = send('30FA'):sub(1,16) - -- pwdblock must be set since the 30F1 and 30F2 special commands don't work on the ntag21x part of the UMC - if ulmode == '03' then versionstr = 'Ultralight' - elseif ulmode == '02' then versionstr = 'Ultralight-C' - elseif cversion == '0004030101000B03' then versionstr = 'UL EV1 48b' - elseif cversion == '0004030101000B03' then versionstr = 'UL EV1 48b' - elseif cversion == '0004030101000E03' then versionstr = 'UL EV1 128b' - elseif cversion == '0004040101000B03' then versionstr = 'NTAG 210' - elseif cversion == '0004040101000E03' then versionstr = 'NTAG 212' - elseif cversion == '0004040201000F03' then versionstr = 'NTAG 213' - elseif cversion == '0004040201001103' then versionstr = 'NTAG 215' - elseif cversion == '0004040201001303' then versionstr = 'NTAG 216' - elseif cversion == '0004040502011303' then versionstr = 'NTAG I2C 1K' - elseif cversion == '0004040502011503' then versionstr = 'NTAG I2C 2K' - elseif cversion == '0004040502021303' then versionstr = 'NTAG I2C 1K PLUS' - elseif cversion == '0004040502021503' then versionstr = 'NTAG I2C 2K PLUS' - elseif cversion == '0004040401000F03' then versionstr = 'NTAG 213F' - elseif cversion == '0004040401001303' then versionstr = 'NTAG 216F' - end - -- read PWD - cpwd = send("30F0"):sub(1,8) - pwd = send("30E5"):sub(1,8) - -- 04 response indicates that blocks has been locked down. - if pwd == '04' then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end - -- read PACK - cpack = send("30F1"):sub(1,4) - pack = send("30E6"):sub(1,4) - -- read SIGNATURE - signature1 = send('30F2'):sub(1,32) - signature2 = send('30F6'):sub(1,32) - lib14a.disconnect() + -- Read Ultralight config only if UL protocol is enabled + cardprotocol = 'MIFARE Ultralight/NTAG' + block0 = send("3000") + uid0 = block0:sub(1,6) + uid = uid0..block0:sub(9,16) + if ulmode == '00' then ultype = 'Ultralight EV1' + elseif ulmode == '01' then ultype = 'NTAG21x' + elseif ulmode == '02' then ultype = 'Ultralight-C' + elseif ulmode == '03' then ultype = 'Ultralight' + end + -- read VERSION + cversion = send('30FA'):sub(1,16) + -- pwdblock must be set since the 30F1 and 30F2 special commands don't work on the ntag21x part of the UMC + if ulmode == '03' then versionstr = 'Ultralight' + elseif ulmode == '02' then versionstr = 'Ultralight-C' + elseif cversion == '0004030101000B03' then versionstr = 'UL EV1 48b' + elseif cversion == '0004030101000E03' then versionstr = 'UL EV1 128b' + elseif cversion == '0004040101000B03' then versionstr = 'NTAG 210' + elseif cversion == '0004040101000E03' then versionstr = 'NTAG 212' + elseif cversion == '0004040201000F03' then versionstr = 'NTAG 213' + elseif cversion == '0004040201001103' then versionstr = 'NTAG 215' + elseif cversion == '0004040201001303' then versionstr = 'NTAG 216' + elseif cversion == '0004040502011303' then versionstr = 'NTAG I2C 1K' + elseif cversion == '0004040502011503' then versionstr = 'NTAG I2C 2K' + elseif cversion == '0004040502021303' then versionstr = 'NTAG I2C 1K PLUS' + elseif cversion == '0004040502021503' then versionstr = 'NTAG I2C 2K PLUS' + elseif cversion == '0004040401000F03' then versionstr = 'NTAG 213F' + elseif cversion == '0004040401001303' then versionstr = 'NTAG 216F' + end + -- read PWD + cpwd = send("30F0"):sub(1,8) + pwd = send("30E5"):sub(1,8) + -- 04 response indicates that blocks has been locked down. + if pwd == '04' then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end + -- read PACK + cpack = send("30F1"):sub(1,4) + pack = send("30E6"):sub(1,4) + -- read SIGNATURE + signature1 = send('30F2'):sub(1,32) + signature2 = send('30F6'):sub(1,32) + lib14a.disconnect() end if _print < 1 then - print(string.rep('=', 88)) - print('\t\t\tUltimate Magic Card Configuration') - print(string.rep('=', 88)) - print(' - Raw Config ', string.sub(magicconfig, 1, -9)) - print(' - Card Protocol ', cardprotocol) - print(' - Ultralight Mode ', ultype) - print(' - ULM Backdoor Key ', readpass) - print(' - GTU Mode ', gtustr) - if ulprotocol == '01' then - print(' - Card Type ', versionstr) - else - print(' - Card Type ', cardtype) - end - print(' - UID ', uid) - print(' - ATQA ', atqaf) - print(' - SAK ', sak) - if ulprotocol == '01' then - print('') - print(string.rep('=', 88)) - print('\t\t\tMagic UL/NTAG 21* Configuration') - print(string.rep('=', 88)) - print(' - ATS ', atsstr) - print(' - Password ', '[0xE5] '..pwd, '[0xF0] '..cpwd) - print(' - Pack ', '[0xE6] '..pack, '[0xF1] '..cpack) - print(' - Version ', cversion) - print(' - Signature ', signature1..signature2) - end + print(string.rep('=', 88)) + print('\t\t\tUltimate Magic Card Configuration') + print(string.rep('=', 88)) + print(' - Raw Config ', string.sub(magicconfig, 1, -9)) + print(' - Card Protocol ', cardprotocol) + print(' - Ultralight Mode ', ultype) + print(' - ULM Backdoor Key ', readpass) + print(' - GTU Mode ', gtustr) + if ulprotocol == '01' then + print(' - Card Type ', versionstr) + else + print(' - Card Type ', cardtype) + end + print(' - UID ', uid) + print(' - ATQA ', atqaf) + print(' - SAK ', sak) + if ulprotocol == '01' then + print('') + print(string.rep('=', 88)) + print('\t\t\tMagic UL/NTAG 21* Configuration') + print(string.rep('=', 88)) + print(' - ATS ', atsstr) + print(' - Password ', '[0xE5] '..pwd, '[0xF0] '..cpwd) + print(' - Pack ', '[0xE6] '..pack, '[0xF1] '..cpack) + print(' - Version ', cversion) + print(' - Signature ', signature1..signature2) + end end lib14a.disconnect() return true, 'Ok' @@ -292,41 +291,41 @@ end local function write_uid(useruid) -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end local info = connect() if not info then return false, "Can't select card" end -- Writes a MFC UID with GEN4 magic commands. if ulprotocol == '00' then - -- uid string checks - if useruid == nil then return nil, 'empty uid string' end - if #useruid == 0 then return nil, 'empty uid string' end - if (#useruid ~= 8) and (#useruid ~= 14) then return nil, 'UID wrong length. Should be 4 or 7 hex bytes' end - print('Writing new UID ', useruid) - local uidbytes = utils.ConvertHexToBytes(useruid) - local bcc1 = bxor(bxor(bxor(uidbytes[1], uidbytes[2]), uidbytes[3]), uidbytes[4]) - local block0 = string.format('%02X%02X%02X%02X%02X', uidbytes[1], uidbytes[2], uidbytes[3], uidbytes[4], bcc1) - local resp = send('CF'.._key..'CD00'..block0) + -- uid string checks + if useruid == nil then return nil, 'empty uid string' end + if #useruid == 0 then return nil, 'empty uid string' end + if (#useruid ~= 8) and (#useruid ~= 14) then return nil, 'UID wrong length. Should be 4 or 7 hex bytes' end + print('Writing new UID ', useruid) + local uidbytes = utils.ConvertHexToBytes(useruid) + local bcc1 = bxor(bxor(bxor(uidbytes[1], uidbytes[2]), uidbytes[3]), uidbytes[4]) + local block0 = string.format('%02X%02X%02X%02X%02X', uidbytes[1], uidbytes[2], uidbytes[3], uidbytes[4], bcc1) + local resp = send('CF'.._key..'CD00'..block0) -- Writes a MFUL UID with bcc1, bcc2 using NTAG21xx commands. elseif ulprotocol == '01' then - -- uid string checks - if useruid == nil then return nil, 'empty uid string' end - if #useruid == 0 then return nil, 'empty uid string' end - if #useruid ~= 14 then return nil, 'uid wrong length. Should be 7 hex bytes' end - print('Writing new UID ', useruid) - local uidbytes = utils.ConvertHexToBytes(useruid) - local bcc1 = bxor(bxor(bxor(uidbytes[1], uidbytes[2]), uidbytes[3]), 0x88) - local bcc2 = bxor(bxor(bxor(uidbytes[4], uidbytes[5]), uidbytes[6]), uidbytes[7]) - local block0 = string.format('%02X%02X%02X%02X', uidbytes[1], uidbytes[2], uidbytes[3], bcc1) - local block1 = string.format('%02X%02X%02X%02X', uidbytes[4], uidbytes[5], uidbytes[6], uidbytes[7]) - local block2 = string.format('%02X%02X%02X%02X', bcc2, 0x48, 0x00, 0x00) - local resp - resp = send('A200'..block0) - resp = send('A201'..block1) - resp = send('A202'..block2) + -- uid string checks + if useruid == nil then return nil, 'empty uid string' end + if #useruid == 0 then return nil, 'empty uid string' end + if #useruid ~= 14 then return nil, 'uid wrong length. Should be 7 hex bytes' end + print('Writing new UID ', useruid) + local uidbytes = utils.ConvertHexToBytes(useruid) + local bcc1 = bxor(bxor(bxor(uidbytes[1], uidbytes[2]), uidbytes[3]), 0x88) + local bcc2 = bxor(bxor(bxor(uidbytes[4], uidbytes[5]), uidbytes[6]), uidbytes[7]) + local block0 = string.format('%02X%02X%02X%02X', uidbytes[1], uidbytes[2], uidbytes[3], bcc1) + local block1 = string.format('%02X%02X%02X%02X', uidbytes[4], uidbytes[5], uidbytes[6], uidbytes[7]) + local block2 = string.format('%02X%02X%02X%02X', bcc2, 0x48, 0x00, 0x00) + local resp + resp = send('A200'..block0) + resp = send('A201'..block1) + resp = send('A202'..block2) else - print('Incorrect ul') + print('Incorrect ul') end lib14a.disconnect() if resp ~= nil then @@ -340,8 +339,8 @@ end local function write_atqasak(atqasak) -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end if atqasak == nil then return nil, 'Empty ATQA/SAK string' end if #atqasak == 0 then return nil, 'Empty ATQA/SAK string' end @@ -351,25 +350,25 @@ end local atqauserf = atqauser2..atqauser1 local sakuser = atqasak:sub(5,6) if sakuser == '04' then - print('Never set SAK bit 3 (e.g. SAK=04), it indicates an extra cascade level is required') - return nil + print('Never set SAK bit 3 (e.g. SAK=04), it indicates an extra cascade level is required') + return nil elseif (sakuser == '20' or sakuser == '28') and atslen == '00' then - print('When SAK equals 20 or 28, ATS must be turned on') - return nil + print('When SAK equals 20 or 28, ATS must be turned on') + return nil elseif atqauser2 == '40' then - print('ATQA of [00 40] will cause the card to not answer.') - return nil + print('ATQA of [00 40] will cause the card to not answer.') + return nil else - local info = connect() - if not info then return false, "Can't select card" end - print('New ATQA: '..atqauser1..' '..atqauser2..' New SAK: '..sakuser) - local resp = send("CF".._key.."35"..atqauserf..sakuser) - lib14a.disconnect() - if resp == nil then - return nil, oops('Failed to write ATQA/SAK') - else - return true, 'Ok' - end + local info = connect() + if not info then return false, "Can't select card" end + print('New ATQA: '..atqauser1..' '..atqauser2..' New SAK: '..sakuser) + local resp = send("CF".._key.."35"..atqauserf..sakuser) + lib14a.disconnect() + if resp == nil then + return nil, oops('Failed to write ATQA/SAK') + else + return true, 'Ok' + end end end --- @@ -377,8 +376,8 @@ end local function write_ntagpwd(ntagpwd) -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end if ulprotocol == '00' then return nil, 'Magic Card is not using the Ultralight Protocol' end -- PWD string checks @@ -402,8 +401,8 @@ end local function write_pack(userpack) -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end if ulprotocol == 0 then return nil, 'Magic Card is not using the Ultralight Protocol' end -- PACK string checks @@ -427,8 +426,8 @@ local function write_otp(block3) if #block3 ~= 8 then return nil, 'OTP wrong length. Should be 4 hex bytes' end -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end if ulprotocol == '00' then return nil, 'Magic Card is not using the Ultralight Protocol' end local info = connect() @@ -451,8 +450,8 @@ local function write_version(data) if #data ~= 16 then return nil, 'version wrong length. Should be 8 hex bytes' end -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end if ulprotocol == '00' then return nil, 'Magic Card is not using the Ultralight Protocol' end print('Writing new version', data) @@ -479,22 +478,34 @@ local function write_signature(data) if #data ~= 64 then return nil, 'data wrong length. Should be 32 hex bytes' end -- read CONFIG if not magicconfig then - _print = 1 - read_config() + _print = 1 + read_config() end local info = connect() if not info then return false, "Can't select card" end - if ulprotocol == '00' then return nil, 'Magic Card is not using the Ultralight Protocol' end - print('Writing new signature',data) - local b,c - local cmd = 'A2F%d%s' - local j = 2 - for i = 1, #data, 8 do - b = data:sub(i,i+7) - c = cmd:format(j,b) - local resp = send(c) - if resp ~= '0A' then lib14a.disconnect(); return nil, oops('Failed to write signature') end - j = j + 1 + if ulprotocol == '00' then + print('Writing new MFC signature',data) + send('CF'.._key..'6B48') + lib14a.disconnect() + connect() -- not 100% sure why it's needed, but without this blocks aren't actually written + local sig1 = data:sub(1, 32) + local sig2 = data:sub(33, 64) + + send('CF'.._key..'CD45'..sig1) + send('CF'.._key..'CD46'..sig2) + send('CF'.._key..'CD475C8FF9990DA270F0F8694B791BEA7BCC') + else + print('Writing new MFUL signature',data) + local b,c + local cmd = 'A2F%d%s' + local j = 2 + for i = 1, #data, 8 do + b = data:sub(i,i+7) + c = cmd:format(j,b) + local resp = send(c) + if resp ~= '0A' then lib14a.disconnect(); return nil, oops('Failed to write signature') end + j = j + 1 + end end lib14a.disconnect() return true, 'Ok' @@ -509,19 +520,19 @@ local function write_gtu(gtu) local info = connect() if not info then return false, "Can't select card" end if gtu == '00' then - print('Enabling GTU Pre-Write') - send('CF'.._key..'32'..gtu) + print('Enabling GTU Pre-Write') + send('CF'.._key..'32'..gtu) elseif gtu == '01' then - print('Enabling GTU Restore Mode') - send('CF'.._key..'32'..gtu) + print('Enabling GTU Restore Mode') + send('CF'.._key..'32'..gtu) elseif gtu == '02' then - print('Disabled GTU') - send('CF'.._key..'32'..gtu) + print('Disabled GTU') + send('CF'.._key..'32'..gtu) elseif gtu == '03' then - print('Disabled GTU, high speed R/W mode for Ultralight') - send('CF'.._key..'32'..gtu) + print('Disabled GTU, high speed R/W mode for Ultralight') + send('CF'.._key..'32'..gtu) else - print('Failed to set GTU mode') + print('Failed to set GTU mode') end lib14a.disconnect() return true, 'Ok' @@ -537,13 +548,13 @@ local function write_ats(atsuser) local atscardlendecimal = tonumber(atscardlen, 16) local atsf = string.sub(atsuser, 3) if (#atsf / 2) ~= atscardlendecimal then - oops('Given length of ATS ('..atscardlendecimal..') does not match the ATS_length ('..(#atsf / 2)..')') - return true, 'Ok' + oops('Given length of ATS ('..atscardlendecimal..') does not match the ATS_length ('..(#atsf / 2)..')') + return true, 'Ok' else - local info = connect() - if not info then return false, "Can't select card" end - print('Writing '..atscardlendecimal..' ATS bytes of '..atsf) - send("CF".._key.."34"..atsuser) + local info = connect() + if not info then return false, "Can't select card" end + print('Writing '..atscardlendecimal..' ATS bytes of '..atsf) + send("CF".._key.."34"..atsuser) end lib14a.disconnect() return true, 'Ok' @@ -557,11 +568,11 @@ local function write_ulp(ulp) local info = connect() if not info then return false, "Can't select card" end if ulp == '00' then - print('Changing card to Mifare Classic Protocol') - send("CF".._key.."69"..ulp) + print('Changing card to Mifare Classic Protocol') + send("CF".._key.."69"..ulp) elseif ulp == '01' then - print('Changing card to Ultralight Protocol') - send("CF".._key.."69"..ulp) + print('Changing card to Ultralight Protocol') + send("CF".._key.."69"..ulp) else oops('Protocol needs to be either 00 or 01') end @@ -577,17 +588,17 @@ local function write_ulm(ulm) local info = connect() if not info then return false, "Can't select card" end if ulm == '00' then - print('Changing card UL mode to Ultralight EV1') - send("CF".._key.."6A"..ulm) + print('Changing card UL mode to Ultralight EV1') + send("CF".._key.."6A"..ulm) elseif ulm == '01' then - print('Changing card UL mode to NTAG') - send("CF".._key.."6A"..ulm) + print('Changing card UL mode to NTAG') + send("CF".._key.."6A"..ulm) elseif ulm == '02' then - print('Changing card UL mode to Ultralight-C') - send("CF".._key.."6A"..ulm) + print('Changing card UL mode to Ultralight-C') + send("CF".._key.."6A"..ulm) elseif ulm == '03' then - print('Changing card UL mode to Ultralight') - send("CF".._key.."6A"..ulm) + print('Changing card UL mode to Ultralight') + send("CF".._key.."6A"..ulm) else oops('UL mode needs to be either 00, 01, 02, 03') end @@ -604,50 +615,50 @@ local function set_type(tagtype) if tagtype == 1 then print('Setting: Ultimate Magic card to Mifare mini S20 4-byte') connect() - send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000900") - lib14a.disconnect() + send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000900") + lib14a.disconnect() write_uid('04112233') -- Setting Mifare mini S20 7-byte elseif tagtype == 2 then print('Setting: Ultimate Magic card to Mifare mini S20 7-byte') connect() - send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000900") - lib14a.disconnect() + send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000900") + lib14a.disconnect() write_uid('04112233445566') -- Setting Mifare 1k S50 4--byte elseif tagtype == 3 then print('Setting: Ultimate Magic card to Mifare 1k S50 4-byte') connect() - send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") - lib14a.disconnect() + send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") + lib14a.disconnect() write_uid('04112233') -- Setting Mifare 1k S50 7-byte elseif tagtype == 4 then print('Setting: Ultimate Magic card to Mifare 1k S50 7-byte') connect() - send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800") - lib14a.disconnect() + send("CF".._key.."F000010000000002000978009102DABC19101011121314151644000800") + lib14a.disconnect() write_uid('04112233445566') -- Setting Mifare 4k S70 4-byte elseif tagtype == 5 then print('Setting: Ultimate Magic card to Mifare 4k S70 4-byte') connect() - send("CF".._key.."F000000000000002000978009102DABC19101011121314151602001800") - lib14a.disconnect() + send("CF".._key.."F000000000000002000978009102DABC19101011121314151602001800") + lib14a.disconnect() write_uid('04112233') -- Setting Mifare 4k S70 7-byte elseif tagtype == 6 then print('Setting: Ultimate Magic card to Mifare 4k S70 7-byte') connect() - send("CF".._key.."F000010000000002000978009102DABC19101011121314151642001800") - lib14a.disconnect() + send("CF".._key.."F000010000000002000978009102DABC19101011121314151642001800") + lib14a.disconnect() write_uid('04112233445566') -- Setting UL elseif tagtype == 7 then print('Setting: Ultimate Magic card to UL') connect() - send("CF".._key.."F0010100000000030A0A78008102DBA0C119402AB5BA4D321A44000003") - lib14a.disconnect() + send("CF".._key.."F0010100000000030A0A78008102DBA0C119402AB5BA4D321A44000003") + lib14a.disconnect() write_uid('04112233445566') write_otp('00000000') -- Setting OTP to default 00 00 00 00 write_version('0000000000000000') -- UL-C does not have a version @@ -655,48 +666,48 @@ local function set_type(tagtype) elseif tagtype == 8 then print('Setting: Ultimate Magic card to UL-C') connect() - send("CF".._key.."F0010100000000030A0A78008102DBA0C119402AB5BA4D321A44000002") - print('Setting default permissions and 3des key') - send('A22A30000000') -- Auth0 page 48/0x30 and above need authentication - send('A22B80000000') -- Auth1 read and write access restricted - send('A22C42524541') -- Default 3des key - send('A22D4B4D4549') - send('A22E46594F55') - send('A22F43414E21') - lib14a.disconnect() + send("CF".._key.."F0010100000000030A0A78008102DBA0C119402AB5BA4D321A44000002") + print('Setting default permissions and 3des key') + send('A22A30000000') -- Auth0 page 48/0x30 and above need authentication + send('A22B80000000') -- Auth1 read and write access restricted + send('A22C42524541') -- Default 3des key + send('A22D4B4D4549') + send('A22E46594F55') + send('A22F43414E21') + lib14a.disconnect() write_uid('04112233445566') write_otp('00000000') -- Setting OTP to default 00 00 00 00 write_version('0000000000000000') -- UL-C does not have a version elseif tagtype == 9 then print('Setting: Ultimate Magic card to UL-EV1 48') - connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000000") + connect() + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000000") -- Setting UL-Ev1 default config bl 16,17 - send('a2E5FFFFFFFF') -- A2F0 block does not align correctly to actual pwd block - send('a2E6FFFFFFFF') -- A2F1 block does not align correctly to actual pack block + send('a2E5FFFFFFFF') -- A2F0 block does not align correctly to actual pwd block + send('a2E6FFFFFFFF') -- A2F1 block does not align correctly to actual pack block send('a210000000FF') send('a21100050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_otp('00000000') -- Setting OTP to default 00 00 00 00 write_version('0004030101000b03') -- UL-EV1 (48) 00 04 03 01 01 00 0b 03 elseif tagtype == 10 then print('Setting: Ultimate Magic card to UL-EV1 128') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000000") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000000") -- Setting UL-Ev1 default config bl 37,38 - send('a2E5FFFFFFFF') -- A2F0 block does not align correctly to actual pwd block - send('a2E6FFFFFFFF') -- A2F1 block does not align correctly to actual pack block + send('a2E5FFFFFFFF') -- A2F0 block does not align correctly to actual pwd block + send('a2E6FFFFFFFF') -- A2F1 block does not align correctly to actual pack block send('a225000000FF') send('a22600050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_otp('00000000') -- Setting OTP to default 00 00 00 00 write_version('0004030101000e03') -- UL-EV1 (128) 00 04 03 01 01 00 0e 03 elseif tagtype == 12 then print('Setting: Ultimate Magic card to NTAG 210') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG210 default CC block456 send('a203e1100600') send('a2040300fe00') @@ -704,13 +715,13 @@ local function set_type(tagtype) -- Setting cfg1/cfg2 send('a210000000FF') send('a21100050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040101000b03') -- NTAG210 00 04 04 01 01 00 0b 03 elseif tagtype == 13 then print('Setting: Ultimate Magic card to NTAG 212') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG212 default CC block456 send('a203e1101000') send('a2040103900a') @@ -718,13 +729,13 @@ local function set_type(tagtype) -- Setting cfg1/cfg2 send('a225000000FF') send('a22600050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040101000E03') -- NTAG212 00 04 04 01 01 00 0E 03 elseif tagtype == 14 then print('Setting: Ultimate Magic card to NTAG 213') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG213 default CC block456 send('a203e1101200') send('a2040103a00c') @@ -732,13 +743,13 @@ local function set_type(tagtype) -- setting cfg1/cfg2 send('a229000000ff') send('a22a00050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040201000F03') -- NTAG213 00 04 04 02 01 00 0f 03 elseif tagtype == 15 then print('Setting: Ultimate Magic card to NTAG 215') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG215 default CC block456 send('a203e1103e00') send('a2040300fe00') @@ -746,13 +757,13 @@ local function set_type(tagtype) -- setting cfg1/cfg2 send('a283000000ff') send('a28400050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040201001103') -- NTAG215 00 04 04 02 01 00 11 03 elseif tagtype == 16 then print('Setting: Ultimate Magic card to NTAG 216') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG216 default CC block456 send('a203e1106d00') send('a2040300fe00') @@ -760,56 +771,56 @@ local function set_type(tagtype) -- setting cfg1/cfg2 send('a2e3000000ff') send('a2e400050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040201001303') -- NTAG216 00 04 04 02 01 00 13 03 elseif tagtype == 17 then print('Setting: Ultimate Magic card to NTAG I2C 1K') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG I2C 1K default CC block456 send('a203e1106D00') send('a2040300fe00') send('a20500000000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040502011303') -- NTAG_I2C_1K 00 04 04 05 02 01 13 03 elseif tagtype == 18 then print('Setting: Ultimate Magic card to NTAG I2C 2K') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG I2C 2K default CC block456 send('a203e110EA00') send('a2040300fe00') send('a20500000000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040502011503') -- NTAG_I2C_2K 00 04 04 05 02 01 15 03 elseif tagtype == 19 then print('Setting: Ultimate Magic card to NTAG I2C plus 1K') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG I2C 1K default CC block456 send('a203e1106D00') send('a2040300fe00') send('a20500000000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040502021303') -- NTAG_I2C_1K 00 04 04 05 02 02 13 03 elseif tagtype == 20 then print('Setting: Ultimate Magic card to NTAG I2C plus 2K') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG I2C 2K default CC block456 send('a203e1106D00') send('a2040300fe00') send('a20500000000') - write_uid('04112233445566') + write_uid('04112233445566') write_version('0004040502021503') -- NTAG_I2C_2K 00 04 04 05 02 02 15 03 elseif tagtype == 21 then print('Setting: Ultimate Magic card to NTAG 213F') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG213 default CC block456 send('a203e1101200') send('a2040103a00c') @@ -817,13 +828,13 @@ local function set_type(tagtype) -- setting cfg1/cfg2 send('a229000000ff') send('a22a00050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040401000F03') -- NTAG213F 00 04 04 04 01 00 0f 03 elseif tagtype == 22 then print('Setting: Ultimate Magic card to NTAG 216F') connect() - send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") + send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") -- Setting NTAG216 default CC block456 send('a203e1106d00') send('a2040300fe00') @@ -831,11 +842,11 @@ local function set_type(tagtype) -- setting cfg1/cfg2 send('a2e3000000ff') send('a2e400050000') - lib14a.disconnect() - write_uid('04112233445566') + lib14a.disconnect() + write_uid('04112233445566') write_version('0004040401001303') -- NTAG216F 00 04 04 04 01 00 13 03 else - oops('No matching tag types') + oops('No matching tag types') end lib14a.disconnect() if resp == '04' then @@ -845,86 +856,101 @@ local function set_type(tagtype) end end --- +-- returns true if b is the index of a sector trailer +local function mfIsSectorTrailer(b) + n=b+1 + if (n < 32*4 ) then + if (n % 4 == 0) then return true + else return false + end + end + + if (n % 16 == 0) then return true + end + + return false +end +--- -- wipe tag local function wipe(wtype) local info = connect() if not info then return false, "Can't select card" end if wtype == '0' then - print('Starting Mifare Wipe') + print('Starting Mifare Wipe') send("CF".._key.."F000000000000002000978009102DABC19101011121314151604000800") send("CF".._key.."CD000102030404080400000000000000BEAF") - local err, msg, resp - local cmd_empty = 'CF'.._key..'CD%02X00000000000000000000000000000000' - local cmd_cfg1 = 'CF'.._key..'CD%02XFFFFFFFFFFFFFF078069FFFFFFFFFFFF' - for b = 1, 0xFB do - if b == 0x03 or b == 0x07 or b == 0x0B or b == 0x0F or b == 0x13 or b == 0x17 or b == 0x1B or b == 0x1F or b == 0x23 or b == 0x27 or b == 0x2B or b == 0x2F or b == 0x33 or b == 0x37 or b == 0x3B or b == 0x3F then - local cmd = (cmd_cfg1):format(b) - resp = send(cmd) - else - local cmd = (cmd_empty):format(b) - resp = send(cmd) - end - if resp == nil then - io.write('\nwrote block '..b, ' failed\n') - err = true - else - io.write('.') - end - io.flush() - end - print('\n') - err, msg = set_type(3) - if err == nil then return err, msg end - lib14a.disconnect() - return true, 'Ok' + local err, msg, resp + local cmd_empty = 'CF'.._key..'CD%02X00000000000000000000000000000000' + local cmd_trail = 'CF'.._key..'CD%02XFFFFFFFFFFFFFF078069FFFFFFFFFFFF' + for b = 1, 0xFF do + if mfIsSectorTrailer(b) then + local cmd = (cmd_trail):format(b) + resp = send(cmd) + else + local cmd = (cmd_empty):format(b) + resp = send(cmd) + end + if resp == nil then + io.write('\nwrote block '..b, ' failed\n') + err = true + else + io.write('.') + end + io.flush() + end + print('\n') + err, msg = set_type(3) + if err == nil then return err, msg end + lib14a.disconnect() + return true, 'Ok' elseif wtype == '1' then - print('Starting Ultralight Wipe') - local err, msg, resp - local cmd_empty = 'A2%02X00000000' - local cmd_cfg1 = 'A2%02X000000FF' - local cmd_cfg2 = 'A2%02X00050000' - print('Wiping tag') - local info = connect() - if not info then return false, "Can't select card" end + print('Starting Ultralight Wipe') + local err, msg, resp + local cmd_empty = 'A2%02X00000000' + local cmd_cfg1 = 'A2%02X000000FF' + local cmd_cfg2 = 'A2%02X00050000' + print('Wiping tag') + local info = connect() + if not info then return false, "Can't select card" end send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000001") - for b = 3, 0xFB do - --configuration block 0 - if b == 0x29 or b == 0x83 or b == 0xe3 then - local cmd = (cmd_cfg1):format(b) - resp = send(cmd) - --configuration block 1 - elseif b == 0x2a or b == 0x84 or b == 0xe4 then - local cmd = (cmd_cfg2):format(b) - resp = send(cmd) - else - resp = send(cmd_empty:format(b)) - end - if resp == '04' or #resp == 0 then - io.write('\nwrote block '..b, ' failed\n') - err = true - else - io.write('.') - end - io.flush() - end - io.write('\r\n') - lib14a.disconnect() - print('\n') - if err then return nil, "Tag locked down, "..err_lock end - -- set NTAG213 default values - err, msg = set_type(14) - if err == nil then return err, msg end - --set UID - err, msg = write_uid('04112233445566') - if err == nil then return err, msg end - --set NTAG pwd - err, msg = write_ntagpwd('FFFFFFFF') - if err == nil then return err, msg end - --set pack - err, msg = write_pack('0000') - if err == nil then return err, msg end - lib14a.disconnect() - return true, 'Ok' + for b = 3, 0xFB do + --configuration block 0 + if b == 0x29 or b == 0x83 or b == 0xe3 then + local cmd = (cmd_cfg1):format(b) + resp = send(cmd) + --configuration block 1 + elseif b == 0x2a or b == 0x84 or b == 0xe4 then + local cmd = (cmd_cfg2):format(b) + resp = send(cmd) + else + resp = send(cmd_empty:format(b)) + end + if resp == '04' or #resp == 0 then + io.write('\nwrote block '..b, ' failed\n') + err = true + else + io.write('.') + end + io.flush() + end + io.write('\r\n') + lib14a.disconnect() + print('\n') + if err then return nil, "Tag locked down, "..err_lock end + -- set NTAG213 default values + err, msg = set_type(14) + if err == nil then return err, msg end + --set UID + err, msg = write_uid('04112233445566') + if err == nil then return err, msg end + --set NTAG pwd + err, msg = write_ntagpwd('FFFFFFFF') + if err == nil then return err, msg end + --set pack + err, msg = write_pack('0000') + if err == nil then return err, msg end + lib14a.disconnect() + return true, 'Ok' else oops('Use 0 for Mifare wipe or 1 for Ultralight wipe') end end diff --git a/client/luascripts/hf_mfu_amiibo_restore.lua b/client/luascripts/hf_mfu_amiibo_restore.lua new file mode 100644 index 000000000..c31db7098 --- /dev/null +++ b/client/luascripts/hf_mfu_amiibo_restore.lua @@ -0,0 +1,164 @@ +local cmds = require('commands') +local getopt = require('getopt') +local os = require('os') +local io = require('io') +local bin = require('bin') +local utils = require('utils') +local ansicolors = require('ansicolors') +local amiibo_tools = require('amiibo_tools') + +copyright = '' +author = 'George Talusan' +version = 'v0.0.1' +desc = [[ +This script will try to restore a binary datadump of an Amiibo to a blank NTAG215. +It will recalculate PWD and PACK if necessary, set the appropriate password and sector lock bytes. + +NOTE: PyAmiibo must be installed. The helper script pyscripts/amiibo_change_uid.py depends on PyAmiibo. + +YMMV if a non-blank NTAG215 is provided! +]] +example = [[ + 1. script run hf_mfu_amiibo_restore + 2. script run hf_mfu_amiibo_restore -f myfile -k password +]] +usage = [[ +script run hf_mfu_amiibo_restore [-h] [-f -k ] +]] +arguments = [[ + -h : this help + -f : filename for the datadump to read (bin) + -k : password of blank NTAG 215 (use `hf mfu info` to find it) +]] + +local DEBUG = false -- the debug flag + +local bxor = bit32.bxor +local sub = string.sub +local format = string.format + +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while result[i] do + dbg(result[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, err +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +-- +-- Exit message +local function ExitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function main(args) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + + local result, err, hex + local inputTemplate = 'dumpdata.bin' + local password + + for o, a in getopt.getopt(args, 'hf:k:') do + if o == 'h' then return help() end + if o == 'f' then inputTemplate = a end + if o == 'k' then password = a end + end + + print(('Loading data from %s'):format(inputTemplate)) + hex, err = utils.ReadDumpFile(inputTemplate) + if not hex then return oops(err) end + + if not password or #password ~= 8 then + return oops('Expecting 4 byte password (hint: use `hf mfu info` to get it)') + end + + -- chomp emu header + if #hex == 1192 then + hex = hex:sub(113) + end + + local amiibo_offset = 0 + local amiibo_info = hex:sub(amiibo_offset + 169, amiibo_offset + 169 + 15):lower() + local amiibo_game = amiibo_info:sub(1, 3) + local amiibo_type = amiibo_info:sub(7, 8) + local amiibo_series = amiibo_info:sub(13, 14) + + dbg('raw: '..ansicolors.green..amiibo_info..ansicolors.reset) + print('game: '..ansicolors.green..amiibo_tools.db.game_series[("0x%s"):format(amiibo_game)]..ansicolors.reset) + print('character: '..ansicolors.green..amiibo_tools.db.amiibos[("0x%s"):format(amiibo_info)].name..ansicolors.reset) + print('type: '..ansicolors.green..amiibo_tools.db.types[("0x%s"):format(amiibo_type)]..ansicolors.reset) + print('series: '..ansicolors.green..amiibo_tools.db.amiibo_series[("0x%s"):format(amiibo_series)]..ansicolors.reset) + + local uid = core.ul_read_uid(); + if uid == nil then + return oops("Can't read UID of NTAG215 card. Reposition card and try again.") + end + + local tmp = ('%s.bin'):format(os.tmpname()) + local amiibo_file = io.open(tmp, 'w+b') + amiibo_file:write(bin.pack('H', hex)) + amiibo_file:close() + local tmp2 = ('%s.bin'):format(os.tmpname()) + + print('generating new Amiibo binary for NTAG215 '..ansicolors.green..uid) + core.clearCommandBuffer() + core.console(('script run amiibo_change_uid %s %s %s %s'):format(uid, tmp, tmp2, core.search_file('resources/key_retail', '.bin'))) + + -- let's sanity check the output + hex, err = utils.ReadDumpFile(tmp2) + if not hex or #hex ~= 1080 then + os.remove(tmp) + os.remove(tmp2) + return oops('There was a problem generating the output Amiibo') + end + + core.console(('hf mfu restore -f %s -k %s'):format(tmp2, password)) + + -- re-write some blocks because `hf mfu restore` won't write out blocks 0-3, and PyAmiibo won't give a PACK/PWD + local pwd, pack = core.keygen_algo_b(uid) + core.console(('hf mfu wrbl -b 3 -d F110FFEE -k %s'):format(password)) -- CC? + core.console(('hf mfu wrbl -b 134 -d %04X0000 -k %s'):format(pack, password)) -- PACK/RFUI + core.console(('hf mfu wrbl -b 133 -d %08X -k %s'):format(pwd, password)) -- PWD + core.console(('hf mfu wrbl -b 131 -d 00000004 -k %08X'):format(pwd)) -- CFG0 + core.console(('hf mfu wrbl -b 132 -d 5F000000 -k %08X'):format(pwd)) -- CFG1 + + local lock_bytes = hex:sub(17, 24) + dbg('lock_bytes: '..lock_bytes) + core.console(('hf mfu wrbl -b 2 -d %s -k %08X'):format(lock_bytes, pwd)) -- BCC1/static lock + core.console(('hf mfu wrbl -b 130 -d 01000FBD -k %08X'):format(pwd)) -- dynamic lock/RFUI + + os.remove(tmp) + os.remove(tmp2) +end +main(args) diff --git a/client/luascripts/hf_mfu_amiibo_sim.lua b/client/luascripts/hf_mfu_amiibo_sim.lua new file mode 100644 index 000000000..e00248c90 --- /dev/null +++ b/client/luascripts/hf_mfu_amiibo_sim.lua @@ -0,0 +1,152 @@ +local cmds = require('commands') +local getopt = require('getopt') +local bin = require('bin') +local utils = require('utils') +local ansicolors = require('ansicolors') +local amiibo_tools = require('amiibo_tools') + +copyright = '' +author = 'George Talusan' +version = 'v0.0.2' +desc = [[ +This script will try to load a binary datadump of an Amiibo. +It will recalculate PWD and PACK if necessary. +]] +example = [[ + 1. script run hf_mfu_amiibo_sim + 2. script run hf_mfu_amiibo_sim -f myfile +]] +usage = [[ +script run hf_mfu_amiibo_sim [-h] [-f ] +]] +arguments = [[ + -h : this help + -f : filename for the datadump to read (bin) +]] + +local DEBUG = false -- the debug flag + +local bxor = bit32.bxor +local sub = string.sub +local format = string.format + +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while result[i] do + dbg(result[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, err +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +-- +-- Exit message +local function ExitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function LoadEmulator(uid, blocks) + io.write('Sending Amiibo to emulator memory') + local cmd, blockdata + for i=0,148,1 do + blockdata = blocks[i] + io.write('.') + io.flush() + core.clearCommandBuffer() + cmd = Command:newNG{cmd = cmds.CMD_HF_MIFARE_EML_MEMSET, data = ('%02x%02x%02x%s'):format(i, 1, 4, blockdata)} + local err, msg = cmd:sendNG(true) + if err == nil then return err, msg end + end + io.write('\n') +end + +local function main(args) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + + local result, err, hex + local inputTemplate = 'dumpdata.bin' + + for o, a in getopt.getopt(args, 'hf:u:') do + if o == 'h' then return help() end + if o == 'f' then inputTemplate = a end + end + + print(('Loading data from %s'):format(inputTemplate)) + hex, err = utils.ReadDumpFile(inputTemplate) + if not hex then return oops(err) end + + -- only deal with missing PWD and PACK, or with 56 emu hdr + if #hex ~= 1064 and #hex ~= 1080 and #hex ~= 1192 then return oops('Expecting either a plain binary or emulator dump') end + + local amiibo_offset = (#hex == 1064 or #hex == 1080) and 0 or 112 + local amiibo_info = hex:sub(amiibo_offset + 169, amiibo_offset + 169 + 15):lower() + local amiibo_game = amiibo_info:sub(1, 3) + local amiibo_type = amiibo_info:sub(7, 8) + local amiibo_series = amiibo_info:sub(13, 14) + + dbg('raw: '..ansicolors.green..amiibo_info..ansicolors.reset) + print('game: '..ansicolors.green..amiibo_tools.db.game_series[("0x%s"):format(amiibo_game)]..ansicolors.reset) + print('character: '..ansicolors.green..amiibo_tools.db.amiibos[("0x%s"):format(amiibo_info)].name..ansicolors.reset) + print('type: '..ansicolors.green..amiibo_tools.db.types[("0x%s"):format(amiibo_type)]..ansicolors.reset) + print('series: '..ansicolors.green..amiibo_tools.db.amiibo_series[("0x%s"):format(amiibo_series)]..ansicolors.reset) + + local blocks = {} + local blockindex = 0 + + -- add empty header if necessary + if (#hex == 1064 or #hex == 1080) then + for i = 0, 13, 1 do + blocks[i] = '00000000' + end + blocks[2] = '00000086' + blockindex = 14 + end + for i = 1, #hex, 8 do + blocks[blockindex] = hex:sub(i, i+7) + blockindex = blockindex + 1 + end + + -- force lock bytes, otherwise the Amiibo won't be recognized + blocks[16] = blocks[16]:sub(1, 4)..'0FE0' + + -- add PWD and PACK + local uid = blocks[14]:sub(1, 6)..blocks[15]:sub(1, 8) + blocks[147] = ("%08x"):format(bxor(bxor(tonumber(sub(uid, 2, 10), 16), tonumber(sub(uid, 6, 14), 16)), 0xaa55aa55)) + blocks[148] = "80800000" + + err = LoadEmulator(uid, blocks) + if err then return oops(err) end + core.clearCommandBuffer() + core.console(("hf mfu sim -t 7 -u %s"):format(uid)) +end +main(args) diff --git a/client/luascripts/hf_mfu_uidkeycalc-italy.lua b/client/luascripts/hf_mfu_uidkeycalc_italy.lua similarity index 96% rename from client/luascripts/hf_mfu_uidkeycalc-italy.lua rename to client/luascripts/hf_mfu_uidkeycalc_italy.lua index 4777a186d..d2fbdadde 100644 --- a/client/luascripts/hf_mfu_uidkeycalc-italy.lua +++ b/client/luascripts/hf_mfu_uidkeycalc_italy.lua @@ -6,20 +6,20 @@ local ansicolors = require('ansicolors') copyright = '' author = "Iceman" -version = 'v1.0.1' +version = 'v1.0.2' desc = [[ This script calculates mifare Ultralight-EV1 pwd based on uid diversification for an Italian ticketsystem Algo not found by me. ]] example =[[ -- if called without, it reads tag uid - script run hf_mfu_uidkeycalc-italy + script run hf_mfu_uidkeycalc_italy -- - script run hf_mfu_uidkeycalc-italy -u 11223344556677 + script run hf_mfu_uidkeycalc_italy -u 11223344556677 ]] usage = [[ -script run hf_mfu_uidkeycalc-italy -h -u " +script run hf_mfu_uidkeycalc_italy -h -u " ]] arguments = [[ -h : this help diff --git a/client/luascripts/hf_ntag_dt.lua b/client/luascripts/hf_ntag_dt.lua new file mode 100644 index 000000000..5626a0b07 --- /dev/null +++ b/client/luascripts/hf_ntag_dt.lua @@ -0,0 +1,171 @@ +local getopt = require('getopt') +local ansicolors = require('ansicolors') + +copyright = '' +author = 'Shain Lakin' +version = 'v1.0.0' +desc =[[ + +This script modifies the DT NeXT implant (NTAG216) configuration pages. + +- NeXT Defaults - + +Default hf mfu info: +---------------------------------------------------------------------- +[=] --- Tag Configuration +[=] cfg0 [227/0xE3]: 04 00 00 E3 +[=] - strong modulation mode disabled +[=] - page 227 and above need authentication +[=] cfg1 [228/0xE4]: 00 05 00 00 +[=] - Unlimited password attempts +[=] - NFC counter disabled +[=] - NFC counter not protected +[=] - user configuration writeable +[=] - write access is protected with password +[=] - 05, Virtual Card Type Identifier is default +[=] PWD [229/0xE5]: 00 00 00 00 - (cannot be read) +[=] PACK [230/0xE6]: 00 00 - (cannot be read) +[=] RFU [230/0xE6]: 00 00 - (cannot be read) +---------------------------------------------------------------------- + +Default blocks 0xE0 to 0xE6: +------------------------------------- +[=] 224/0xE0 | 00 00 00 00 | 0 | .... +[=] 225/0xE1 | 4E 45 78 54 | 0 | NExT +[=] 226/0xE2 | 00 00 7F BD | 0 | .... +[=] 227/0xE3 | 04 00 00 E3 | 0 | .... +[=] 228/0xE4 | 00 05 00 00 | 0 | .... +[=] 229/0xE5 | 44 4E 47 52 | 0 | DNGR +[=] 230/0xE6 | 00 00 00 00 | 0 | .... +------------------------------------- +]] + +example =[[ + +Set a new password of SUDO using the default password of DNGR: + + script run hf_ntag_dt -x pass -p DNGR -n SUDO + +Enable password protection from hex block 04 onwards (User memory): + + script run hf_ntag_dt -x protect -p DNGR -a 04 + +Enable password protection from hex block E3 onwards (Configuration Pages): + + script run hf_ntag_dt -x protect -p DNGR -a E3 + +Disable password protection: + + script run hf_ntag_dt -x protect -p DNGR -a FF + +Enable the counter and enable read + write password protection on password protected pages +(protected block start page specified using -x protect mode): + + script run hf_ntag_dt -x conf -p DNGR -c enable -m rw + +Disable the counter and enable write only password protection on password protected pages +(protected block start specified using -x protect mode): + + script run hf_ntag_dt -x conf -p DNGR -c disable -m w + +]] +usage = [[ + + script run hf_ntag_dt -x pass -p -n + script run hf_ntag_dt -x protect -p -a + script run hf_ntag_dt -x conf -p -c -m + +]] +arguments = [[ + -h this help + -x mode (pass, protect, conf) + -p password (ascii) + -n new password (ascii) + -a auth0 block (hex) + -c counter (enable/disable) + -m protection mode (r/rw) +]] +--- +--- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +--- Print user message +local function msg(msg) + print( string.rep('--',20) ) + print('') + print(msg) + print('') + print( string.rep('--',20) ) +end +--- +--- String to hex function +local function strhex(str) + return (str:gsub(".", function(char) return string.format("%2x", char:byte()) end)) + end +--- +-- Main +local function main(args) + + for o, a in getopt.getopt(args, 'b:m:c:a:p:x:n:h') do + if o == 'm' then prot_mode = a end + if o == 'c' then counter = a end + if o == 'a' then auth0_block = a end + if o == 'p' then passwd = strhex(a) end + if o == 'x' then mode = a end + if o == 'n' then new_pass = strhex(a) end + if o == 'h' then return help() end + end + + if mode == 'pass' then + command = 'hf mfu wrbl -b 229 -d '..new_pass..' -k '..passwd + msg('Writing '..new_pass..' to PASSWD block (229/0xE5) : \n\n'..command) + core.console(command) + command = 'hf mfu rdbl -b 0 -k '..new_pass..'' + msg('Verifying password is correctly set : \n\n'..command) + core.console(command) + elseif mode == 'conf' then + if counter == 'enable' then + if prot_mode == 'r' then + command = 'hf mfu wrbl -b 228 -d 10050000 -k '..passwd + msg('Enabling counter and setting write access to protected pages as password protected : \n\n'..command) + core.console(command) + elseif prot_mode == 'rw' then + command = 'hf mfu wrbl -b 228 -d 90050000 -k '..passwd + msg('Enabling counter and setting read/write access to protected pages as password protected : \n\n'..command) + core.console(command) + end + elseif counter == 'disable' then + if prot_mode == 'w' then + command = 'hf mfu wrbl -b 228 -d 00050000 -k '..passwd + msg('Disabling counter and setting write password protection on protected pages : \n\n'..command) + core.console(command) + elseif prot_mode == 'rw' then + command = 'hf mfu wrbl -b 228 -d 80050000 -k '..passwd + msg('Disabling counter and setting read/write password protection on protected pages : \n\n'..command) + core.console(command) + end + end + elseif mode == 'protect' then + command = 'hf mfu wrbl -k '..passwd..' -b 227 -d 040000'..auth0_block + msg('Enabling password protection from block '..auth0_block..' onwards : \n\n'..command) + core.console(command) + else + return print(usage) + end + + if command == '' then return print(usage) end + + +end +main(args) diff --git a/client/luascripts/lf_ident_json.lua b/client/luascripts/lf_ident_json.lua index fb0d82554..73794547c 100644 --- a/client/luascripts/lf_ident_json.lua +++ b/client/luascripts/lf_ident_json.lua @@ -6,7 +6,7 @@ local ac = require('ansicolors') copyright = '' author = "Christian Herrmann" -version = 'v1.0.0' +version = 'v1.0.1' desc = [[ This script loads a json format file, with the field "data" and a hexbyte array of data. Ie t55x7 dump, it tries to identify which system based on block1, and detect block0 settings. @@ -16,7 +16,7 @@ example = [[ script run lf_ident_json -i lf_t55xx.json ]] usage = [[ -script run lf_en4100_bulk.lua [-h] [-c] [-p password] [-s ] [-v] +script run lf_ident_json.lua [-h] [-c] [-p password] [-s ] [-v] ]] arguments = [[ -h : this help diff --git a/client/pyscripts/amiibo_change_uid.py b/client/pyscripts/amiibo_change_uid.py new file mode 100644 index 000000000..4c7b2354a --- /dev/null +++ b/client/pyscripts/amiibo_change_uid.py @@ -0,0 +1,44 @@ +# Requirements: +# python3 -m pip install pyamiibo + +import sys +from amiibo import AmiiboDump, AmiiboMasterKey + +def main(): + if(len(sys.argv) != 5): + print(""" + \t{0} - helper script for integrating with PyAmiibo + + Usage: {0} + + Example: + + \t{0} 04123456789ABC my_amiibo_original.bin my_amiibo_with_new_uid.bin keyfile.bin + + \n""".format(sys.argv[0])) + return 1 + + uid = sys.argv[1] + infile = sys.argv[2] + outfile = sys.argv[3] + keyfile = sys.argv[4] + + if len(uid) != 14: + print('expecting 7 byte UID') + return 1 + + with open(keyfile, 'rb') as keybin: + master_key = AmiiboMasterKey.from_combined_bin(keybin.read()) + + with open(infile, 'rb') as fin, open(outfile, 'wb') as fout: + dump = AmiiboDump(master_key, fin.read(), is_locked=True) + dump.unlock() + dump.uid_hex = uid + dump.lock() + fout.write(dump.data) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) + diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index 0188397f3..81a153bba 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -1247,6 +1247,14 @@ "Description": "PIV End Point Applet. Last 2 bytes designate version", "Type": "" }, + { + "AID": "A000000308000010000100", + "Vendor": "National Institute of Standards and Technology", + "Country": "United States", + "Name": "Personal Identity Verification (PIV) / ID-ONE PIV BIO", + "Description": "PIV End Point Applet. Last 2 bytes designate version", + "Type": "" + }, { "AID": "A00000031510100528", "Vendor": "Currence Holding/PIN BV", @@ -2206,5 +2214,61 @@ "Name": "SL Resekort", "Description": "transport card", "Type": "transport" + }, + { + "AID": "4F53452E5641532E3031", + "Vendor": "Apple, Google", + "Country": "", + "Name": "Value-Added Services (OSE.VAS.01)", + "Description": "Used by Apple VAS and Google SmartTap", + "Type": "loyalty" + }, + { + "AID": "A000000476D0000101", + "Vendor": "Google", + "Country": "", + "Name": "Google SmartTap 1.3 (Deprecated)", + "Description": "", + "Type": "loyalty" + }, + { + "AID": "A000000476D0000111", + "Vendor": "Google", + "Country": "", + "Name": "Google SmartTap 2.0", + "Description": "", + "Type": "loyalty" + }, + { + "AID": "A0000002480400", + "Vendor": "ISO/IEC JTC1/SC17", + "Country": "", + "Name": "Personal identification (mDL)", + "Description": "ISO/IEC 18013-5:2021 compliant Mobile driving licence (mDL) application.", + "Type": "identity" + }, + { + "AID": "A000000676", + "Vendor": "Blackboard", + "Country": "United States", + "Name": "Student ID", + "Description": "Student ID cards", + "Type": "identity" + }, + { + "AID": "A000000809434343444B467631", + "Vendor": "Car Connectivity Consortium (CCC)", + "Country": "", + "Name": "Digital Car Key", + "Description": "", + "Type": "access" + }, + { + "AID": "A0000008580101", + "Vendor": "Apple", + "Country": "", + "Name": "Apple Home Key", + "Description": "NFC Home Key for select HomeKit-compatible locks", + "Type": "access" } ] diff --git a/client/resources/mad.json b/client/resources/mad.json index 078db1831..cfa6294d3 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -6097,6 +6097,41 @@ "service_provider": "CDVI", "system_integrator": "CDVI" }, + { + "application": "(access control and security) VIGIK", + "company": "CDV International", + "mad": "0x4914", + "service_provider": "CDVI", + "system_integrator": "CDVI" + }, + { + "application": "(access control and security) VIGIK", + "company": "CDV International", + "mad": "0x4915", + "service_provider": "CDVI", + "system_integrator": "CDVI" + }, + { + "application": "(access control and security) VIGIK", + "company": "CDV International", + "mad": "0x4916", + "service_provider": "CDVI", + "system_integrator": "CDVI" + }, + { + "application": "(access control and security) VIGIK", + "company": "CDV International", + "mad": "0x4917", + "service_provider": "CDVI", + "system_integrator": "CDVI" + }, + { + "application": "(access control and security) VIGIK", + "company": "CDV International", + "mad": "0x4918", + "service_provider": "CDVI", + "system_integrator": "CDVI" + }, { "application": "(access control and security) VIGIK", "company": "NORALSY", diff --git a/client/resources/sim013.bin b/client/resources/sim013.bin new file mode 100644 index 000000000..3fae92bd8 Binary files /dev/null and b/client/resources/sim013.bin differ diff --git a/client/resources/sim013.sha512.txt b/client/resources/sim013.sha512.txt new file mode 100644 index 000000000..202eff3c4 --- /dev/null +++ b/client/resources/sim013.sha512.txt @@ -0,0 +1 @@ +3bb6cbd893ce07720d8514d00706b230b4026a54d6c4dc068ca294beea3f3735c75dae65bc153f573b7b891753c85860f6427fb0ad7edb804d58925c50d1fc76 *sim013.bin diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 76b9d9b8d..563fda14f 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -17,17 +17,17 @@ //----------------------------------------------------------------------------- #include "cipursecore.h" -#include // memcpy memset - -#include "commonutil.h" // ARRAYLEN -#include "comms.h" // DropField -#include "util_posix.h" // msleep +#include // memcpy memset +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // DropField +#include "util_posix.h" // msleep #include "cmdhf14a.h" #include "../emv/emvcore.h" #include "../emv/emvjson.h" -#include "../iso7816/apduinfo.h" // sAPDU_t +#include "../iso7816/apduinfo.h" // sAPDU_t #include "ui.h" #include "util.h" +#include "protocols.h" // ISO7816 APDU return codes // context for secure channel CipurseContext_t cipurseContext; @@ -112,7 +112,7 @@ static int CIPURSEExchangeEx(bool activate_field, bool leave_field_on, sAPDU_t a *sw = isw; } - if (isw != 0x9000) { + if (isw != ISO7816_OK) { if (GetAPDULogging()) { if (*sw >> 8 == 0x61) { PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff); @@ -255,7 +255,7 @@ bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose) { // authenticate res = CIPURSEMutualAuthenticate(keyindex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000 || len != 16) { + if (res != 0 || sw != ISO7816_OK || len != 16) { if (sw == 0x6988) { if (verbose) { PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key"); @@ -412,10 +412,14 @@ void CIPURSEPrintDGI(uint8_t *dgi, size_t dgilen) { } for (int i = 0; i < len / 20; i++) { - PrintAndLogEx(INFO, "Key[%d]............ %s", i + 1, sprint_hex_inrow(&dgi[3 + i * 20 + 0], 16)); - PrintAndLogEx(INFO, " Additional info.. 0x%02x", dgi[3 + i * 20 + 16]); + uint8_t kvv[CIPURSE_KVV_LENGTH] = {0}; - CipurseCGetKVV(&dgi[3 + i * 20 + 0], kvv); + uint8_t aeskey[16] = {0}; + memcpy(aeskey, &dgi[3 + i * 20 + 0], sizeof(aeskey)); + + PrintAndLogEx(INFO, "Key[%d]............ %s", i + 1, sprint_hex_inrow(aeskey, sizeof(aeskey))); + PrintAndLogEx(INFO, " Additional info.. 0x%02x", dgi[3 + i * 20 + 16]); + CipurseCGetKVV(aeskey, kvv); bool kvvvalid = (memcmp(kvv, &dgi[3 + i * 20 + 17], 3) == 0); PrintAndLogEx(INFO, " KVV.............. %s (%s)", sprint_hex_inrow(&dgi[3 + i * 20 + 17], 3), (kvvvalid) ? _GREEN_("valid") : _RED_("invalid")); } diff --git a/client/src/cmdcrc.c b/client/src/cmdcrc.c index 98befcc00..ae424662c 100644 --- a/client/src/cmdcrc.c +++ b/client/src/cmdcrc.c @@ -424,8 +424,8 @@ static int CmdrevengSearch(const char *Cmd) { uint8_t width[NMODELS] = {0}; int count = 0; - char result[30]; - char revResult[30]; + char result[50 + 1] = {0}; + char revResult[50 + 1] = {0}; int ans = GetModels(Models, &count, width); bool found = false; if (!ans) { @@ -461,7 +461,7 @@ static int CmdrevengSearch(const char *Cmd) { continue; } - memset(result, 0, 30); + memset(result, 0, sizeof(result)); char *inCRC = calloc(crcChars + 1, sizeof(char)); if (inCRC == NULL) { return 0; diff --git a/client/src/cmddata.c b/client/src/cmddata.c index ad7879650..9524cbc63 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -35,6 +35,9 @@ #include "cmdlft55xx.h" // print... #include "crypto/asn1utils.h" // ASN1 decode / print #include "cmdflashmemspiffs.h" // SPIFFS flash memory download +#include "mbedtls/bignum.h" // big num +#include "mbedtls/entropy.h" // +#include "mbedtls/ctr_drbg.h" // random generator uint8_t g_DemodBuffer[MAX_DEMOD_BUF_LEN]; size_t g_DemodBufferLen = 0; @@ -894,7 +897,7 @@ static int CmdAutoCorr(const char *Cmd) { } if (window >= g_GraphTraceLen) { - PrintAndLogEx(WARNING, "window must be smaller than trace (" _YELLOW_("%zu") " samples)", g_GraphTraceLen); + PrintAndLogEx(WARNING, "window must be smaller than trace ( " _YELLOW_("%zu") " samples )", g_GraphTraceLen); return PM3_EINVARG; } @@ -1230,7 +1233,7 @@ int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bo PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh, fclow, invert)); PrintAndLogEx(INFO, "-----------------------"); - printDemodBuff(0, false, invert, false); + printDemodBuff(0, false, false, false); } goto out; } else { @@ -1779,7 +1782,7 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_conf BitstreamOut_t bout = { got, bits_per_sample * n, 0}; uint32_t j = 0; - for (j = 0; j * bits_per_sample < n * 8 && j < n; j++) { + for (j = 0; j * bits_per_sample < n * 8 && j * bits_per_sample < MAX_GRAPH_TRACE_LEN * 8; j++) { uint8_t sample = getByte(bits_per_sample, &bout); g_GraphBuffer[j] = ((int) sample) - 127; } @@ -1817,7 +1820,7 @@ static int CmdSamples(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_int0("n", NULL, "", "num of samples (512 - 40000)"), - arg_lit0("v", "verbose", "verbose"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2032,6 +2035,8 @@ static int CmdLoad(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "file to load"), + arg_lit0("b", "bin", "binary file"), + arg_lit0("n", "no-fix", "Load data from file without any transformations"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2039,6 +2044,8 @@ static int CmdLoad(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool is_bin = arg_get_lit(ctx, 2); + bool nofix = arg_get_lit(ctx, 3); CLIParserFree(ctx); char *path = NULL; @@ -2048,8 +2055,13 @@ static int CmdLoad(const char *Cmd) { } } - FILE *f = fopen(path, "r"); - if (!f) { + FILE *f; + if (is_bin) + f = fopen(path, "rb"); + else + f = fopen(path, "r"); + + if (f == NULL) { PrintAndLogEx(WARNING, "couldn't open '%s'", path); free(path); return PM3_EFILE; @@ -2057,24 +2069,38 @@ static int CmdLoad(const char *Cmd) { free(path); g_GraphTraceLen = 0; - char line[80]; - while (fgets(line, sizeof(line), f)) { - g_GraphBuffer[g_GraphTraceLen] = atoi(line); - g_GraphTraceLen++; - if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN) - break; + if (is_bin) { + uint8_t val[2]; + while (fread(val, 1, 1, f)) { + g_GraphBuffer[g_GraphTraceLen] = val[0] - 127; + g_GraphTraceLen++; + + if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN) + break; + } + } else { + char line[80]; + while (fgets(line, sizeof(line), f)) { + g_GraphBuffer[g_GraphTraceLen] = atoi(line); + g_GraphTraceLen++; + + if (g_GraphTraceLen >= MAX_GRAPH_TRACE_LEN) + break; + } } fclose(f); PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " samples", g_GraphTraceLen); - uint8_t bits[g_GraphTraceLen]; - size_t size = getFromGraphBuf(bits); + if (nofix == false) { + uint8_t bits[g_GraphTraceLen]; + size_t size = getFromGraphBuf(bits); - removeSignalOffset(bits, size); - setGraphBuf(bits, size); - computeSignalProperties(bits, size); + removeSignalOffset(bits, size); + setGraphBuf(bits, size); + computeSignalProperties(bits, size); + } setClockGrid(0, 0); g_DemodBufferLen = 0; @@ -2402,6 +2428,19 @@ static int CmdZerocrossings(const char *Cmd) { return PM3_SUCCESS; } +static bool data_verify_hex(uint8_t *d, size_t n) { + if (d == NULL) + return false; + + for (size_t i = 0; i < n; i++) { + if (isxdigit(d[i]) == false) { + PrintAndLogEx(ERR, "Non hex digit found"); + return false; + } + } + return true; +} + /** * @brief Utility for conversion via cmdline. * @param Cmd @@ -2478,12 +2517,8 @@ static int Cmdhex2bin(const char *Cmd) { return PM3_EINVARG; } - for (int i = 0; i < dlen; i++) { - char x = data[i]; - if (isxdigit(x) == false) { - PrintAndLogEx(ERR, "Non hex digit found"); - return PM3_EINVARG; - } + if (data_verify_hex((uint8_t *)data, dlen) == false) { + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "" NOLF); @@ -2744,13 +2779,19 @@ static int print_modulation(lf_modulation_t b) { static int try_detect_modulation(void) { - lf_modulation_t tests[6]; +#define LF_NUM_OF_TESTS 6 + + lf_modulation_t tests[LF_NUM_OF_TESTS]; + for (int i = 0; i < ARRAYLEN(tests); i++) { + memset(&tests[i], 0, sizeof(lf_modulation_t)); + } + int clk = 0, firstClockEdge = 0; - uint8_t hits = 0, ans = 0; - uint8_t fc1 = 0, fc2 = 0; + uint8_t hits = 0, fc1 = 0, fc2 = 0; bool st = false; - ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); + + uint8_t ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); if (ans && ((fc1 == 10 && fc2 == 8) || (fc1 == 8 && fc2 == 5))) { @@ -2874,8 +2915,8 @@ static int CmdAsn1Decoder(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int dlen = 256; - uint8_t data[256]; + int dlen = 2048; + uint8_t data[2048]; CLIGetHexWithReturn(ctx, 1, data, &dlen); CLIParserFree(ctx); @@ -2886,8 +2927,6 @@ static int CmdAsn1Decoder(const char *Cmd) { return PM3_SUCCESS; } - - static int CmdDiff(const char *Cmd) { CLIParserContext *ctx; @@ -3034,8 +3073,8 @@ static int CmdDiff(const char *Cmd) { } */ - size_t n = (datalenA > datalenB) ? datalenB : datalenA; - PrintAndLogEx(DEBUG, "data len: %zu A %zu B %zu", n, datalenA, datalenB); + size_t biggest = (datalenA > datalenB) ? datalenA : datalenB; + PrintAndLogEx(DEBUG, "data len: %zu A %zu B %zu", biggest, datalenA, datalenB); if (inA == NULL) PrintAndLogEx(INFO, "inA null"); @@ -3062,102 +3101,68 @@ static int CmdDiff(const char *Cmd) { char line[880] = {0}; // print data diff loop - int i; - for (i = 0; i < n; i += width) { + for (int i = 0 ; i < biggest ; i += width) { + char dlnA[240] = {0}; + char dlnB[240] = {0}; + char dlnAii[180] = {0}; + char dlnBii[180] = {0}; - memset(line, 0, sizeof(line)); + memset(dlnA, 0, sizeof(dlnA)); + memset(dlnB, 0, sizeof(dlnB)); + memset(dlnAii, 0, sizeof(dlnAii)); + memset(dlnBii, 0, sizeof(dlnBii)); - int diff = memcmp(inA + i, inB + i, width); + for (int j = i; j < i + width; j++) { + int dlnALen = strlen(dlnA); + int dlnBLen = strlen(dlnB); + int dlnAiiLen = strlen(dlnAii); + int dlnBiiLen = strlen(dlnBii); - // if ok, just print - if (diff == 0) { - hex_to_buffer((uint8_t *)line, inA + i, width, width, 0, 1, true); - ascii_to_buffer((uint8_t *)(line + strlen(line)), inA + i, width, width, 0); - strcat(line + strlen(line), " | "); - hex_to_buffer((uint8_t *)(line + strlen(line)), inB + i, width, width, 0, 1, true); - ascii_to_buffer((uint8_t *)(line + strlen(line)), inB + i, width, width, 0); - } else { - - char dlnA[240] = {0}; - char dlnB[240] = {0}; - char dlnAii[180] = {0}; - char dlnBii[180] = {0}; - - memset(dlnA, 0, sizeof(dlnA)); - memset(dlnB, 0, sizeof(dlnB)); - memset(dlnAii, 0, sizeof(dlnAii)); - memset(dlnBii, 0, sizeof(dlnBii)); - - // if diff, time to find it - for (int j = i; j < (i + width); j++) { - - char ca = inA[j]; - char cb = inB[j]; - - int dlnALen = strlen(dlnA); - int dlnBLen = strlen(dlnB); - int dlnAiiLen = strlen(dlnAii); - int dlnBiiLen = strlen(dlnBii); - - if (inA[j] != inB[j]) { - - // diff / add colors - snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, _GREEN_("%02X "), inA[j]); - snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, _RED_("%02X "), inB[j]); - snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, _GREEN_("%c"), ((ca < 32) || (ca == 127)) ? '.' : ca); - snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, _RED_("%c"), ((cb < 32) || (cb == 127)) ? '.' : cb); - - } else { - // normal - snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "%02X ", inA[j]); - snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "%02X ", inB[j]); - snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, "%c", ((ca < 32) || (ca == 127)) ? '.' : ca); - snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, "%c", ((cb < 32) || (cb == 127)) ? '.' : cb); - } + //both files ended + if (j >= datalenA && j >= datalenB) { + snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "-- "); + snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, ".") ; + snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "-- "); + snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, ".") ; + continue ; + } + + char ca, cb; + + if (j >= datalenA) { + // file A ended. print B without colors + cb = inB[j]; + snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "-- "); + snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, ".") ; + snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "%02X ", inB[j]); + snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, "%c", ((cb < 32) || (cb == 127)) ? '.' : cb); + continue ; + } + ca = inA[j]; + if (j >= datalenB) { + // file B ended. print A without colors + snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "%02X ", inA[j]); + snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, "%c", ((ca < 32) || (ca == 127)) ? '.' : ca); + snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "-- "); + snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, ".") ; + continue ; + } + cb = inB[j]; + if (inA[j] != inB[j]) { + // diff / add colors + snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, _GREEN_("%02X "), inA[j]); + snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, _RED_("%02X "), inB[j]); + snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, _GREEN_("%c"), ((ca < 32) || (ca == 127)) ? '.' : ca); + snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, _RED_("%c"), ((cb < 32) || (cb == 127)) ? '.' : cb); + } else { + // normal + snprintf(dlnA + dlnALen, sizeof(dlnA) - dlnALen, "%02X ", inA[j]); + snprintf(dlnB + dlnBLen, sizeof(dlnB) - dlnBLen, "%02X ", inB[j]); + snprintf(dlnAii + dlnAiiLen, sizeof(dlnAii) - dlnAiiLen, "%c", ((ca < 32) || (ca == 127)) ? '.' : ca); + snprintf(dlnBii + dlnBiiLen, sizeof(dlnBii) - dlnBiiLen, "%c", ((cb < 32) || (cb == 127)) ? '.' : cb); } - snprintf(line, sizeof(line), "%s%s | %s%s", dlnA, dlnAii, dlnB, dlnBii); - } - PrintAndLogEx(INFO, "%03X | %s", i, line); - } - - // mod - - - // print different length - bool tallestA = (datalenA > datalenB); - if (tallestA) { - n = datalenA; - } else { - n = datalenB; - } - - // print data diff loop - for (; i < n; i += width) { - - memset(line, 0, sizeof(line)); - - if (tallestA) { - hex_to_buffer((uint8_t *)line, inA + i, width, width, 0, 1, true); - ascii_to_buffer((uint8_t *)(line + strlen(line)), inA + i, width, width, 0); - strcat(line + strlen(line), " | "); - for (int j = 0; j < width; j++) { - strcat(line + strlen(line), "-- "); - } - for (int j = 0; j < width; j++) { - strcat(line + strlen(line), "."); - } - } else { - - for (int j = 0; j < width; j++) { - strcat(line + strlen(line), "-- "); - } - for (int j = 0; j < width; j++) { - strcat(line + strlen(line), "."); - } - strcat(line + strlen(line), " | "); - hex_to_buffer((uint8_t *)(line + strlen(line)), inB + i, width, width, 0, 1, true); - ascii_to_buffer((uint8_t *)(line + strlen(line)), inB + i, width, width, 0); } + snprintf(line, sizeof(line), "%s%s | %s%s", dlnA, dlnAii, dlnB, dlnBii); PrintAndLogEx(INFO, "%03X | %s", i, line); } @@ -3171,6 +3176,129 @@ static int CmdDiff(const char *Cmd) { return PM3_SUCCESS; } +static int CmdNumCon(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data num", + "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary\n" + "Will print message if number is a prime number\n", + "data num --dec 2023\n" + "data num --hex 0x1000\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "dec", "", "decimal value"), + arg_str0(NULL, "hex", "", "hexadecimal value"), + arg_str0(NULL, "bin", "", "binary value"), + arg_lit0("i", NULL, "print inverted value"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int dlen = 256; + char dec[256]; + memset(dec, 0, sizeof(dec)); + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dec, sizeof(dec), &dlen); + + + int hlen = 256; + char hex[256]; + memset(hex, 0, sizeof(hex)); + res = CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &hlen); + + int blen = 256; + char bin[256]; + memset(bin, 0, sizeof(bin)); + res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)bin, sizeof(bin), &blen); + + bool shall_invert = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + // sanity checks + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + // results for MPI actions + bool ret = false; + + // container of big number + mbedtls_mpi N; + mbedtls_mpi_init(&N); + + + // hex + if (hlen > 0) { + if (data_verify_hex((uint8_t *)hex, hlen) == false) { + return PM3_EINVARG; + } + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 16, hex)); + } + + // decimal + if (dlen > 0) { + // should have decimal string check here too + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 10, dec)); + } + + // binary + if (blen > 0) { + // should have bianry string check here too + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 2, bin)); + } + + mbedtls_mpi base; + mbedtls_mpi_init(&base); + mbedtls_mpi_add_int(&base, &base, 10); + + if (shall_invert) { + PrintAndLogEx(INFO, "should invert"); + MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base)); + } + + // printing + typedef struct { + const char *desc; + uint8_t radix; + } radix_t; + + radix_t radix[] = { + {"dec..... ", 10}, + {"hex..... 0x", 16}, + {"bin..... 0b", 2} + }; + + char s[600] = {0}; + size_t slen = 0; + + for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); + if (slen > 0) { + PrintAndLogEx(INFO, "%s%s", radix[i].desc, s); + } + } + + // check if number is a prime + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init(&ctr_drbg); + mbedtls_entropy_init(&entropy); + + MBEDTLS_MPI_CHK(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)); + + res = mbedtls_mpi_is_prime_ext(&N, 50, mbedtls_ctr_drbg_random, &ctr_drbg); + if (res == 0) { + PrintAndLogEx(INFO, "prime... " _YELLOW_("yes")); + } + +cleanup: + mbedtls_mpi_free(&N); + mbedtls_mpi_free(&base); + mbedtls_entropy_free(&entropy); + mbedtls_ctr_drbg_free(&ctr_drbg); + return PM3_SUCCESS; +} static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -3214,6 +3342,7 @@ static command_t CommandTable[] = { {"hexsamples", CmdHexsamples, IfPm3Present, "Dump big buffer as hex bytes"}, {"hex2bin", Cmdhex2bin, AlwaysAvailable, "Converts hexadecimal to binary"}, {"load", CmdLoad, AlwaysAvailable, "Load contents of file into graph window"}, + {"num", CmdNumCon, AlwaysAvailable, "Converts dec/hex/bin"}, {"print", CmdPrintDemodBuff, AlwaysAvailable, "Print the data in the DemodBuffer"}, {"samples", CmdSamples, IfPm3Present, "Get raw samples for graph window (GraphBuffer)"}, {"save", CmdSave, AlwaysAvailable, "Save signal trace data (from graph window)"}, diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 97126ad9b..e8db64885 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -204,19 +204,23 @@ static int CmdFlashMemLoad(const char *Cmd) { size_t datalen = 0; uint32_t keycount = 0; int res = 0; + uint8_t keylen = 0; uint8_t *data = calloc(FLASH_MEM_MAX_SIZE, sizeof(uint8_t)); switch (d) { case DICTIONARY_MIFARE: offset = DEFAULT_MF_KEYS_OFFSET; - res = loadFileDICTIONARY(filename, data + 2, &datalen, 6, &keycount); + keylen = 6; + res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } // limited space on flash mem - if (keycount > 0xFFFF) - keycount &= 0xFFFF; + if (keycount > DEFAULT_MF_KEYS_MAX) { + keycount = DEFAULT_MF_KEYS_MAX; + datalen = keycount * keylen; + } data[0] = (keycount >> 0) & 0xFF; data[1] = (keycount >> 8) & 0xFF; @@ -224,14 +228,17 @@ static int CmdFlashMemLoad(const char *Cmd) { break; case DICTIONARY_T55XX: offset = DEFAULT_T55XX_KEYS_OFFSET; - res = loadFileDICTIONARY(filename, data + 2, &datalen, 4, &keycount); + keylen = 4; + res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } // limited space on flash mem - if (keycount > 0xFFFF) - keycount &= 0xFFFF; + if (keycount > DEFAULT_T55XX_KEYS_MAX) { + keycount = DEFAULT_T55XX_KEYS_MAX; + datalen = keycount * keylen; + } data[0] = (keycount >> 0) & 0xFF; data[1] = (keycount >> 8) & 0xFF; @@ -239,14 +246,16 @@ static int CmdFlashMemLoad(const char *Cmd) { break; case DICTIONARY_ICLASS: offset = DEFAULT_ICLASS_KEYS_OFFSET; - res = loadFileDICTIONARY(filename, data + 2, &datalen, 8, &keycount); + res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount); if (res || !keycount) { free(data); return PM3_EFILE; } // limited space on flash mem - if (keycount > 0xFFFF) - keycount &= 0xFFFF; + if (keycount > DEFAULT_ICLASS_KEYS_MAX) { + keycount = DEFAULT_ICLASS_KEYS_MAX; + datalen = keycount * keylen; + } data[0] = (keycount >> 0) & 0xFF; data[1] = (keycount >> 8) & 0xFF; @@ -632,6 +641,11 @@ static int CmdFlashMemInfo(const char *Cmd) { // Verify (public key) bool is_verified = (mbedtls_rsa_pkcs1_verify(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device) == 0); + if (got_private == false) { + mbedtls_rsa_free(rsa); + free(rsa); + } + mbedtls_pk_free(&pkctx); PrintAndLogEx(NORMAL, ""); diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index ccb697966..97f7df3c7 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -47,6 +47,10 @@ #include "cmdhfseos.h" // SEOS #include "cmdhfst25ta.h" // ST25TA #include "cmdhfwaveshare.h" // Waveshare +#include "cmdhftexkom.h" // Texkom +#include "cmdhfxerox.h" // Xerox +#include "cmdhffudan.h" // Fudan cards +#include "cmdhftesla.h" // Tesla #include "cmdtrace.h" // trace list #include "ui.h" #include "proxgui.h" @@ -89,7 +93,7 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for LTO-CM tag..."); if (IfPm3Iso14443a()) { - if (infoLTO(false) == PM3_SUCCESS) { + if (reader_lto(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LTO-CM tag") " found\n"); res = PM3_SUCCESS; } @@ -120,7 +124,7 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for iCLASS / PicoPass tag..."); if (IfPm3Iclass()) { - if (read_iclass_csn(false, false) == PM3_SUCCESS) { + if (read_iclass_csn(false, false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iCLASS tag / PicoPass tag") " found\n"); res = PM3_SUCCESS; } @@ -138,7 +142,7 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for Topaz tag..."); if (IfPm3Iso14443a()) { - if (readTopazUid(false) == PM3_SUCCESS) { + if (readTopazUid(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Topaz tag") " found\n"); res = PM3_SUCCESS; } @@ -163,6 +167,23 @@ int CmdHFSearch(const char *Cmd) { } } + // texkom + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for TEXKOM tag..."); + if (read_texkom_uid(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("TEXKOM tag") " found\n"); + res = PM3_SUCCESS; + } + + // xerox + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for Fuji/Xerox tag..."); + if (IfPm3Iso14443b()) { + if (read_xerox_uid(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Fuji/Xerox tag") " found\n"); + res = PM3_SUCCESS; + } + } /* PROMPT_CLEARLINE; @@ -281,6 +302,23 @@ int CmdHFTune(const char *Cmd) { return PM3_SUCCESS; } +typedef enum { + HF_SNOOP_SKIP_NONE = 0x00, + HF_SNOOP_SKIP_DROP = 0x01, + HF_SNOOP_SKIP_MAX = 0x02, + HF_SNOOP_SKIP_MIN = 0x03, + HF_SNOOP_SKIP_AVG = 0x04 +} HFSnoopSkipMode; + +const CLIParserOption HFSnoopSkipModeOpts[] = { + {HF_SNOOP_SKIP_NONE, "none"}, + {HF_SNOOP_SKIP_DROP, "drop"}, + {HF_SNOOP_SKIP_MAX, "min"}, + {HF_SNOOP_SKIP_MIN, "max"}, + {HF_SNOOP_SKIP_AVG, "avg"}, + {0, NULL}, +}; + // Collects pars of u8, // uses 16bit transfers from FPGA for speed // Takes all available bigbuff memory @@ -297,8 +335,10 @@ int CmdHFSniff(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_u64_0(NULL, "sp", "", "skip sample pairs"), - arg_u64_0(NULL, "st", "", "skip number of triggers"), + arg_u64_0(NULL, "sp", "", "skip sample pairs"), + arg_u64_0(NULL, "st", "", "skip number of triggers"), + arg_str0(NULL, "smode", "[none|drop|min|max|avg]", "Skip mode. It switches on the function that applies to several samples before they saved to memory"), + arg_int0(NULL, "sratio", "", "Skip ratio. It applied the function above to (ratio * 2) samples. For ratio = 1 it 2 samples."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -306,12 +346,31 @@ int CmdHFSniff(const char *Cmd) { struct { uint32_t samplesToSkip; uint32_t triggersToSkip; + uint8_t skipMode; + uint8_t skipRatio; } PACKED params; params.samplesToSkip = arg_get_u32_def(ctx, 1, 0); params.triggersToSkip = arg_get_u32_def(ctx, 2, 0); + + int smode = 0; + if (CLIGetOptionList(arg_get_str(ctx, 3), HFSnoopSkipModeOpts, &smode)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + params.skipMode = smode; + params.skipRatio = arg_get_int_def(ctx, 4, 0); + CLIParserFree(ctx); + if (params.skipMode != HF_SNOOP_SKIP_NONE) { + PrintAndLogEx(INFO, "Skip mode. Function: %s, each: %d sample", + CLIGetOptionListStr(HFSnoopSkipModeOpts, params.skipMode), + params.skipRatio * 2 + ); + } + clearCommandBuffer(); SendCommandNG(CMD_HF_SNIFF, (uint8_t *)¶ms, sizeof(params)); @@ -330,6 +389,7 @@ int CmdHFSniff(const char *Cmd) { PrintAndLogEx(INFO, "Button pressed, user aborted"); break; } + if (resp.status == PM3_SUCCESS) { struct r { @@ -363,10 +423,10 @@ int CmdHFSniff(const char *Cmd) { int handle_hf_plot(void) { - uint8_t buf[FPGA_TRACE_SIZE]; + uint8_t buf[FPGA_TRACE_SIZE] = {0}; PacketResponseNG response; - if (!GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &response, 4000, true)) { + if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &response, 4000, true) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -420,6 +480,7 @@ static command_t CommandTable[] = { {"emrtd", CmdHFeMRTD, AlwaysAvailable, "{ Machine Readable Travel Document... }"}, {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, + {"fudan", CmdHFFudan, AlwaysAvailable, "{ Fudan RFIDs... }"}, {"gallagher", CmdHFGallagher, AlwaysAvailable, "{ Gallagher DESFire RFIDs... }"}, {"ksx6924", CmdHFKSX6924, AlwaysAvailable, "{ KS X 6924 (T-Money, Snapper+) RFIDs }"}, {"jooki", CmdHF_Jooki, AlwaysAvailable, "{ Jooki RFIDs... }"}, @@ -433,8 +494,11 @@ static command_t CommandTable[] = { {"ntag424", CmdHF_ntag424, AlwaysAvailable, "{ NXP NTAG 4242 DNA RFIDs... }"}, {"seos", CmdHFSeos, AlwaysAvailable, "{ SEOS RFIDs... }"}, {"st25ta", CmdHFST25TA, AlwaysAvailable, "{ ST25TA RFIDs... }"}, + {"tesla", CmdHFTESLA, AlwaysAvailable, "{ TESLA Cards... }"}, + {"texkom", CmdHFTexkom, AlwaysAvailable, "{ Texkom RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"xerox", CmdHFXerox, AlwaysAvailable, "{ Fuji/Xerox cartridge RFIDs... }"}, {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 42c19781a..e1453be3b 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -18,9 +18,9 @@ #include "cmdhf14a.h" #include #include -#include "cmdparser.h" // command_t -#include "commonutil.h" // ARRAYLEN -#include "comms.h" // clearCommandBuffer +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // clearCommandBuffer #include "cmdtrace.h" #include "cliparser.h" #include "cmdhfmf.h" @@ -29,18 +29,26 @@ #include "emv/emvcore.h" #include "ui.h" #include "crc16.h" -#include "util_posix.h" // msclock +#include "util_posix.h" // msclock #include "aidsearch.h" -#include "cmdhf.h" // handle HF plot +#include "cmdhf.h" // handle HF plot #include "cliparser.h" -#include "protocols.h" // definitions of ISO14A/7816 protocol, MAGIC_GEN_1A -#include "iso7816/apduinfo.h" // GetAPDUCodeDescription -#include "nfc/ndef.h" // NDEFRecordsDecodeAndPrint -#include "cmdnfc.h" // print_type4_cc_info -#include "fileutils.h" // saveFile -#include "atrs.h" // getATRinfo +#include "protocols.h" // definitions of ISO14A/7816 protocol, MAGIC_GEN_1A +#include "iso7816/apduinfo.h" // GetAPDUCodeDescription +#include "nfc/ndef.h" // NDEFRecordsDecodeAndPrint +#include "cmdnfc.h" // print_type4_cc_info +#include "fileutils.h" // saveFile +#include "atrs.h" // getATRinfo +#include "desfire.h" // desfire enums +#include "mifare/desfirecore.h" // desfire context -static bool APDUInFramingEnable = true; +static bool g_apdu_in_framing_enable = true; +bool Get_apdu_in_framing(void) { + return g_apdu_in_framing_enable; +} +void Set_apdu_in_framing(bool v) { + g_apdu_in_framing_enable = v; +} static int CmdHelp(const char *Cmd); static int waitCmd(bool i_select, uint32_t timeout, bool verbose); @@ -400,7 +408,12 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { if (select_status == 3) { PrintAndLogEx(INFO, "E->Card doesn't support standard iso14443-3 anticollision"); - PrintAndLogEx(SUCCESS, "\tATQA : %02X %02X", card->atqa[1], card->atqa[0]); + // identify TOPAZ + if (card->atqa[1] == 0x0C && card->atqa[0] == 0x00) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz info`")); + } else { + PrintAndLogEx(SUCCESS, "\tATQA : %02X %02X", card->atqa[1], card->atqa[0]); + } return 1; } @@ -511,7 +524,14 @@ static int CmdHF14AReader(const char *Cmd) { if (select_status == 3) { if (!(silent && continuous)) { PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); - PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); + + // identify TOPAZ + if (card.atqa[1] == 0x0C && card.atqa[0] == 0x00) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz info`")); + } else { + PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); + } + } DropField(); res = PM3_ESOFT; @@ -648,22 +668,23 @@ static int CmdHF14ACUIDs(const char *Cmd) { 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"); + "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" + "Use type 7 for Mifare Ultralight EV1, Amiibo (NTAG215 pack 0x8080)", + "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 -> MFU EV1 / NTAG 215 Amiibo\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"); 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_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks have been read by reader. 0 = infinite"), arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), @@ -672,7 +693,7 @@ int CmdHF14ASim(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int tagtype = arg_get_int(ctx, 1); + int tagtype = arg_get_int_def(ctx, 1, 1); int uid_len = 0; uint8_t uid[10] = {0}; @@ -701,7 +722,7 @@ int CmdHF14ASim(const char *Cmd) { useUIDfromEML = false; } - uint8_t exitAfterNReads = arg_get_int(ctx, 3); + uint8_t exitAfterNReads = arg_get_int_def(ctx, 3, 0); if (arg_get_lit(ctx, 4)) { flags |= FLAG_NR_AR_ATTACK; @@ -712,6 +733,11 @@ int CmdHF14ASim(const char *Cmd) { CLIParserFree(ctx); + if (tagtype > 10) { + PrintAndLogEx(ERR, "Undefined tag %d", tagtype); + return PM3_EINVARG; + } + sector_t *k_sector = NULL; uint8_t k_sectorsCount = 40; @@ -737,7 +763,7 @@ int CmdHF14ASim(const char *Cmd) { PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); bool keypress = kbd_enter_pressed(); - while (!keypress) { + while (keypress == false) { if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) continue; if (resp.status != PM3_SUCCESS) break; @@ -1030,7 +1056,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea int res; // 3 byte here - 1b framing header, 2b crc16 - if (APDUInFramingEnable && + if (g_apdu_in_framing_enable && ((gs_frame_len && (datainlen > gs_frame_len - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) { int clen = 0; @@ -1434,29 +1460,34 @@ static int CmdHF14AChaining(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a chaining", "Enable/Disable ISO14443a input chaining. Maximum input length goes from ATS.", - "hf 14a chaining disable -> disable chaining\n" - "hf 14a chaining -> show chaining enable/disable state\n"); + "hf 14a chaining -> show chaining enable/disable state\n" + "hf 14a chaining --off -> disable chaining\n" + ); void *argtable[] = { arg_param_begin, - arg_str0(NULL, NULL, "", NULL), + arg_lit0("1", "on", "enabled chaining"), + arg_lit0("0", "off", "disable chaining"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - struct arg_str *str = arg_get_str(ctx, 1); - int len = arg_get_str_len(ctx, 1); - - if (len && (!strcmp(str->sval[0], "enable") || !strcmp(str->sval[0], "1"))) - APDUInFramingEnable = true; - - if (len && (!strcmp(str->sval[0], "disable") || !strcmp(str->sval[0], "0"))) - APDUInFramingEnable = false; - + bool on = arg_get_lit(ctx, 1); + bool off = arg_get_lit(ctx, 2); CLIParserFree(ctx); - PrintAndLogEx(INFO, "\nISO 14443-4 input chaining %s.\n", APDUInFramingEnable ? "enabled" : "disabled"); + if ((on + off) > 1) { + PrintAndLogEx(INFO, "Select only one option"); + return PM3_EINVARG; + } + if (on) + Set_apdu_in_framing(true); + + if (off) + Set_apdu_in_framing(false); + + PrintAndLogEx(INFO, "\nISO 14443-4 input chaining %s.\n", g_apdu_in_framing_enable ? "enabled" : "disabled"); return PM3_SUCCESS; } @@ -1475,6 +1506,7 @@ typedef enum { MTOTHER = 64, MTEMV = 128, MTFUDAN = 256, + MTISO18092 = 512, } nxp_mifare_type_t; // Based on NXP AN10833 Rev 3.6 and NXP AN10834 Rev 4.1 @@ -1487,6 +1519,13 @@ static int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { if ((sak & 0x19) == 0x19) { printTag("MIFARE Classic 2K"); type |= MTCLASSIC; + } else if ((sak & 0x40) == 0x40) { + if ((atqa & 0x0110) == 0x0110) + printTag("P2P Support / Proprietary"); + else + printTag("P2P Support / Android"); + + type |= MTISO18092; } else if ((sak & 0x38) == 0x38) { printTag("SmartMX with MIFARE Classic 4K"); type |= MTCLASSIC; @@ -1763,7 +1802,16 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (select_status == 3) { PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); - PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); + + if (verbose) { + PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); + } + + // identify TOPAZ + if (card.atqa[1] == 0x0C && card.atqa[0] == 0x00) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz info`")); + } + DropField(); return select_status; } @@ -1783,6 +1831,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bool isST = false; bool isEMV = false; bool isFUDAN = false; + bool isISO18092 = false; int nxptype = MTNONE; if (card.uidlen <= 4) { @@ -1802,6 +1851,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((nxptype & MTEMV) == MTEMV) isEMV = true; + if ((nxptype & MTISO18092) == MTISO18092) + isISO18092 = true; + } else { // Double & triple sized UID, can be mapped to a manufacturer. @@ -2201,8 +2253,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } } - if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { - if (sw == 0x9000) { + if (sw == ISO7816_OK || sw == ISO7816_INVALID_DF || sw == ISO7816_FILE_TERMINATED) { + if (sw == ISO7816_OK) { if (verbose) PrintAndLogEx(SUCCESS, "Application ( " _GREEN_("ok") " )"); } else { if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); @@ -2238,9 +2290,15 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } } } else { - PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); - if ((card.sak & 0x20) == 0x20) { - PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--"); + + if (isISO18092) { + PrintAndLogEx(INFO, "proprietary iso18092 card found"); + } else { + + PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); + if ((card.sak & 0x20) == 0x20) { + PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--"); + } } if (select_status == 1) select_status = 2; @@ -2277,7 +2335,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } uint8_t signature[32] = {0}; - res = detect_mfc_ev1_signature(signature); + res = read_mfc_ev1_signature(signature); if (res == PM3_SUCCESS) { mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); } @@ -2301,7 +2359,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv search -s`")); if (isFUDAN) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf 14a raw`") " - since FUDAN is different"); + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf fudan dump`")); + /* PrintAndLogEx(HINT, " hf 14a raw -a -b 7 -k 26"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3000"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3001"); @@ -2311,6 +2370,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(HINT, " hf 14a raw -k -c 3005"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3006"); PrintAndLogEx(HINT, " hf 14a raw -c 3007"); + */ } PrintAndLogEx(NORMAL, ""); @@ -2331,14 +2391,14 @@ int infoHF14A4Applications(bool verbose) { if (res) break; - if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { + if (sw == ISO7816_OK || sw == ISO7816_INVALID_DF || sw == ISO7816_FILE_TERMINATED) { if (!found) { if (verbose) PrintAndLogEx(INFO, "----------------- " _CYAN_("Short AID search") " -----------------"); } found++; - if (sw == 0x9000) { + if (sw == ISO7816_OK) { if (verbose) PrintAndLogEx(SUCCESS, "Application " _CYAN_("%s") " ( " _GREEN_("ok") " )", hintAIDList[i].desc); cardFound[i] = true; @@ -2369,16 +2429,23 @@ int infoHF14A4Applications(bool verbose) { return found; } -static uint64_t inc_sw_error_occurrence(uint16_t sw, uint64_t all_sw[256][256]) { +static uint32_t inc_sw_error_occurrence(uint16_t sw, uint32_t *all_sw) { uint8_t sw1 = (uint8_t)(sw >> 8); uint8_t sw2 = (uint8_t)(0xff & sw); + + // Don't count successes if (sw1 == 0x90 && sw2 == 0x00) { - return 0; // Don't count successes. + return 0; } - if (sw1 == 0x6d && sw2 == 0x00) { - return 0xffffffffffffffffULL; // Always max "Instruction not supported". + + // Always max "Instruction not supported" + if (sw1 == 0x6D && sw2 == 0x00) { + return 0xFFFFFFFFUL; } - return ++all_sw[sw1][sw2]; + + all_sw[(sw1 * 256) + sw2]++; + + return all_sw[(sw1 * 256) + sw2]; } static int CmdHf14AFindapdu(const char *Cmd) { @@ -2414,20 +2481,26 @@ static int CmdHf14AFindapdu(const char *Cmd) { int cla_len = 0; uint8_t cla_arg[1] = {0}; CLIGetHexWithReturn(ctx, 1, cla_arg, &cla_len); + int ins_len = 0; uint8_t ins_arg[1] = {0}; CLIGetHexWithReturn(ctx, 2, ins_arg, &ins_len); + int p1_len = 0; uint8_t p1_arg[1] = {0}; CLIGetHexWithReturn(ctx, 3, p1_arg, &p1_len); + 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); - uint64_t error_limit = arg_get_u64_def(ctx, 6, 512); + uint32_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); @@ -2439,7 +2512,8 @@ static int CmdHf14AFindapdu(const char *Cmd) { uint8_t ins = ins_arg[0]; uint8_t p1 = p1_arg[0]; uint8_t p2 = p2_arg[0]; - uint8_t response[PM3_CMD_DATA_SIZE]; + + uint8_t response[PM3_CMD_DATA_SIZE] = {0}; int response_n = 0; uint8_t aSELECT_AID[80]; int aSELECT_AID_n = 0; @@ -2459,8 +2533,9 @@ static int CmdHf14AFindapdu(const char *Cmd) { bool inc_p1 = false; bool skip_ins = false; - uint64_t all_sw[256][256] = { { 0 } }; - uint64_t sw_occurrences = 0; + uint32_t all_sw[256][256] = { { 0 } }; + uint32_t sw_occurrences = 0; + uint64_t t_start = msclock(); uint64_t t_last_reset = msclock(); @@ -2482,6 +2557,7 @@ retry_ins: break; } } + if (skip_ins) { skip_ins = false; continue; @@ -2502,13 +2578,14 @@ retry_ins: activate_field = true; goto retry_ins; } + uint16_t sw = get_sw(response, response_n); - sw_occurrences = inc_sw_error_occurrence(sw, all_sw); + sw_occurrences = inc_sw_error_occurrence(sw, all_sw[0]); // Show response. if (sw_occurrences < error_limit) { logLevel_t log_level = INFO; - if (sw == 0x9000) { + if (sw == ISO7816_OK) { log_level = SUCCESS; } @@ -2528,15 +2605,19 @@ retry_ins: } } } - activate_field = false; // Do not reativate the filed until the next reset. + // Do not reativate the filed until the next reset. + activate_field = false; } while (++ins != ins_arg[0]); + // Increment P1/P2 in an alternating fashion. if (inc_p1) { p1++; } else { p2++; } + inc_p1 = !inc_p1; + // Check if re-selecting the card is needed. uint64_t t_since_last_reset = ((msclock() - t_last_reset) / 1000); if (t_since_last_reset > reset_time) { @@ -2546,9 +2627,12 @@ retry_ins: PrintAndLogEx(INFO, "Last reset was %" PRIu64 " seconds ago. Resetting the tag to prevent timeout issues", t_since_last_reset); } PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); + } while (p1 != p1_arg[0] || p2 != p2_arg[0]); + cla++; PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); + } while (cla != cla_arg[0]); out: @@ -2568,12 +2652,15 @@ int CmdHF14ANdefRead(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "save raw NDEF to file"), + arg_litn("v", "verbose", 0, 2, "show technical data"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); bool activate_field = true; @@ -2598,7 +2685,7 @@ int CmdHF14ANdefRead(const char *Cmd) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { // Try NDEF Type 4 Tag v1.0 param_gethex_to_eol("00a4040007d2760000850100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -2612,7 +2699,7 @@ int CmdHF14ANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -2624,7 +2711,6 @@ int CmdHF14ANdefRead(const char *Cmd) { keep_field_on = true; // --------------- CC file reading ---------------- - uint8_t aSELECT_FILE_CC[30]; int aSELECT_FILE_CC_n = 0; if (backward_compatibility_v1) { @@ -2639,7 +2725,7 @@ int CmdHF14ANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting CC file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -2654,7 +2740,7 @@ int CmdHF14ANdefRead(const char *Cmd) { return res; } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading CC file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -2665,7 +2751,10 @@ int CmdHF14ANdefRead(const char *Cmd) { memcpy(cc_data, response, sizeof(cc_data)); uint8_t file_id[2] = {cc_data[9], cc_data[10]}; - print_type4_cc_info(cc_data, sizeof(cc_data)); + if (verbose) { + print_type4_cc_info(cc_data, sizeof(cc_data)); + } + uint16_t max_rapdu_size = (cc_data[3] << 8 | cc_data[4]) - 2; max_rapdu_size = max_rapdu_size < sizeof(response) - 2 ? max_rapdu_size : sizeof(response) - 2; @@ -2685,14 +2774,13 @@ int CmdHF14ANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; } // read first 2 bytes to get NDEF length - uint8_t aREAD_NDEF[30]; int aREAD_NDEF_n = 0; param_gethex_to_eol("00b0000002", 0, aREAD_NDEF, sizeof(aREAD_NDEF), &aREAD_NDEF_n); @@ -2703,7 +2791,7 @@ int CmdHF14ANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -2742,7 +2830,7 @@ int CmdHF14ANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); free(ndef_file); @@ -2763,26 +2851,446 @@ int CmdHF14ANdefRead(const char *Cmd) { saveFile(filename, ".bin", ndef_file, ndef_size); } - NDEFRecordsDecodeAndPrint(ndef_file, ndef_size); + NDEFRecordsDecodeAndPrint(ndef_file, ndef_size, verbose); free(ndef_file); return PM3_SUCCESS; } +int CmdHF14ANdefFormat(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a ndefformat", + "Format ISO14443-a Tag as a NFC tag with Data Exchange Format (NDEF)", + "hf 14a ndefformat\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_litn("v", "verbose", 0, 2, "show technical data"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (g_session.pm3_present == false) + return PM3_ENOTTY; + + bool activate_field = true; + bool keep_field_on = false; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + SetAPDULogging(false); + + // step 1 - Select NDEF Tag application + uint8_t aSELECT_AID[80]; + int aSELECT_AID_n = 0; + param_gethex_to_eol("00a4040007d276000085010100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + + if (res != PM3_SUCCESS) { + return res; + } + + if (resplen < 2) { + return PM3_ESOFT; + } + + bool have_application = true; + uint16_t sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + have_application = false; + PrintAndLogEx(INFO, "no NDEF application found"); + } else { + PrintAndLogEx(INFO, "found ndef application"); + } + + + // setup desfire authentication context + uint8_t empty_key[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + DesfireContext_t dctx; + dctx.secureChannel = DACNone; + DesfireSetKey(&dctx, 0, T_DES, empty_key); + DesfireSetKdf(&dctx, MFDES_KDF_ALGO_NONE, NULL, 0); + DesfireSetCommandSet(&dctx, DCCNativeISO); + DesfireSetCommMode(&dctx, DCMPlain); + + // step 1 - create application + if (have_application == false) { + // "hf mfdes createapp --aid 000001 --fid E110 --ks1 0B --ks2 A1 --dfhex D2760000850101 -t des -n 0 -k 0000000000000000" + PrintAndLogEx(INFO, "creating NDEF application..."); + + // authenticae first to AID 00 00 00 + res = DesfireSelectAndAuthenticateEx(&dctx, DACEV1, 0x000000, false, verbose); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(INFO, "failed empty auth.."); + return res; + } + + // create application + uint8_t dfname[] = {0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01}; + uint8_t ks1 = 0x0B; + uint8_t ks2 = 0xA1; // bit FileID in ks2 + uint32_t appid = 0x0000001; + uint16_t fileid = 0xE110; + uint8_t data[250] = {0}; + size_t datalen = 0; + + DesfireAIDUintToByte(appid, &data[0]); + data[3] = ks1; + data[4] = ks2; + Uint2byteToMemLe(&data[5], fileid); + memcpy(&data[7], dfname, sizeof(dfname)); + datalen = 14; + + if (verbose) { + PrintAndLogEx(INFO, "---------------------------"); + PrintAndLogEx(INFO, _CYAN_("Creating Application using:")); + PrintAndLogEx(INFO, "AID........... 0x%02X%02X%02X", data[2], data[1], data[0]); + PrintAndLogEx(INFO, "Key Set 1..... 0x%02X", data[3]); + PrintAndLogEx(INFO, "Key Set 2..... 0x%02X", data[4]); + PrintAndLogEx(INFO, "ISO file ID... %s", (data[4] & 0x20) ? "enabled" : "disabled"); + if ((data[4] & 0x20)) { + PrintAndLogEx(INFO, "ISO file ID... 0x%04X", MemLeToUint2byte(&data[5])); + PrintAndLogEx(INFO, "DF Name[%02d] %s | %s\n", 7, sprint_ascii(dfname, sizeof(dfname)), sprint_hex(dfname, sizeof(dfname))); + } + PrintKeySettings(data[3], data[4], true, true); + PrintAndLogEx(INFO, "---------------------------"); + } + + res = DesfireCreateApplication(&dctx, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire CreateApplication command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Desfire application %06x successfully " _GREEN_("created"), appid); + + + // step 2 - create capability container (CC File) + + // authenticae to the new AID 00 00 01 + uint8_t aes_key[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + dctx.secureChannel = DACNone; + DesfireSetKey(&dctx, 0, T_AES, aes_key); + DesfireSetKdf(&dctx, MFDES_KDF_ALGO_NONE, NULL, 0); + DesfireSetCommandSet(&dctx, DCCNativeISO); + DesfireSetCommMode(&dctx, DCMPlain); + res = DesfireSelectAndAuthenticateEx(&dctx, DACEV1, 0x000001, false, verbose); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(INFO, "failed aid auth.."); + return res; + } + + // hf mfdes createfile --aid 000001 --fid 01 --isofid E103 --amode plain --size 00000F + // --rrights free --wrights key0 --rwrights key0 --chrights key0 + // -n 0 -t aes -k 00000000000000000000000000000000 -m plain + uint8_t fid = 0x01; + uint16_t isofid = 0xE103; + uint32_t fsize = 0x0F; + uint8_t filetype = 0x00; // standard file + + // file access mode: plain 0x00 + // read access: free 0x0E + // write access: key0 0x00 + // r/w access: key0 0x00 + // change access: key0 0x00 + memset(data, 0x00, sizeof(data)); + datalen = 0; + + data[0] = fid; + data[1] = isofid & 0xff; + data[2] = (isofid >> 8) & 0xff; + datalen = 3; + + uint8_t *settings = &data[datalen]; + settings[0] = 0x00; + datalen++; + + DesfireEncodeFileAcessMode(&settings[1], 0x0E, 0x00, 0x00, 0x00) ; + datalen += 2; + + Uint3byteToMemLe(&data[datalen], fsize); + datalen += 3; + + if (verbose) { + PrintAndLogEx(INFO, "App: %06x. File num: 0x%02x type: 0x%02x data[%zu]: %s", appid, data[0], filetype, datalen, sprint_hex(data, datalen)); + } + + DesfirePrintCreateFileSettings(filetype, data, datalen); + + res = DesfireCreateFile(&dctx, filetype, data, datalen, true); // check length only if we dont use raw mode + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire CreateFile command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "%s file %02x in the app %06x created " _GREEN_("successfully"), GetDesfireFileType(filetype), data[0], appid); + + + + // hf mfdes write --aid 000001 --fid 01 -d 000F20003B00340406E10400FF00FF + // -n 0 -t aes -k 00000000000000000000000000000000 -m plain + res = DesfireSelectAndAuthenticateEx(&dctx, DACEV1, 0x000001, false, verbose); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(INFO, "failed aid auth.."); + return res; + } + + uint8_t fnum = 0x01; + uint32_t offset = 0; + uint8_t cc_data[] = {0x00, 0x0F, 0x20, 0x00, 0x3B, 0x00, 0x34, 0x04, 0x06, 0xE1, 0x04, 0x00, 0xFF, 0x00, 0x00}; + + res = DesfireWriteFile(&dctx, fnum, offset, sizeof(cc_data), cc_data); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire WriteFile command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (verbose) { + PrintAndLogEx(INFO, "Write data file %02x " _GREEN_("success"), fnum); + } + + + + // step 3 - create NDEF record file + // hf mfdes write --aid 000001 --fid 02 -d 000CD1010855016E78702E636F6DFE + // -n 0 -t aes -k 00000000000000000000000000000000 -m plain + + fid = 0x02; + isofid = 0xE104; + fsize = 0xFF; + filetype = 0x00; // standard file + + // file access mode: plain 0x00 + // read access: free 0x0E + // write access: key0 0x00 + // r/w access: key0 0x00 + // change access: key0 0x00 + memset(data, 0x00, sizeof(data)); + datalen = 0; + + data[0] = fid; + data[1] = isofid & 0xff; + data[2] = (isofid >> 8) & 0xff; + datalen = 3; + + settings = &data[datalen]; + settings[0] = 0x00; + datalen++; + + DesfireEncodeFileAcessMode(&settings[1], 0x0E, 0x00, 0x00, 0x00) ; + datalen += 2; + + Uint3byteToMemLe(&data[datalen], fsize); + datalen += 3; + + if (verbose) { + PrintAndLogEx(INFO, "App: %06x. File num: 0x%02x type: 0x%02x data[%zu]: %s", appid, data[0], filetype, datalen, sprint_hex(data, datalen)); + } + + DesfirePrintCreateFileSettings(filetype, data, datalen); + + res = DesfireCreateFile(&dctx, filetype, data, datalen, true); // check length only if we dont use raw mode + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire CreateFile command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "%s file %02x in the app %06x created " _GREEN_("successfully"), GetDesfireFileType(filetype), data[0], appid); + + DropField(); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "finished"); + return PM3_SUCCESS; +} + +int CmdHF14ANdefWrite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a ndefwrite", + "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.\n", + "hf 14a ndefwrite -d 0300FE -> write empty record to tag\n" + "hf 14a ndefwrite -f myfilename\n" + "hf 14a ndefwrite -d 003fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", NULL, "", "raw NDEF hex bytes"), + arg_str0("f", "file", "", "write raw NDEF file to tag"), + arg_lit0("p", NULL, "fix NDEF record headers / terminator block if missing"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t raw[256] = {0}; + int rawlen = 0; + CLIGetHexWithReturn(ctx, 1, raw, &rawlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool fix_msg = arg_get_lit(ctx, 3); + bool verbose = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (g_session.pm3_present == false) { + return PM3_ENOTTY; + } + + if ((rawlen && fnlen) || (rawlen == 0 && fnlen == 0)) { + PrintAndLogEx(WARNING, "Please specify either raw hex or filename"); + return PM3_EINVARG; + } + + int res = PM3_SUCCESS; + int32_t bytes = rawlen; + + // read dump file + if (fnlen) { + uint8_t *dump = NULL; + size_t bytes_read = 0; + res = pm3_load_dump(filename, (void **)&dump, &bytes_read, sizeof(raw)); + if (res != PM3_SUCCESS) { + return res; + } + memcpy(raw, dump, bytes_read); + bytes = bytes_read; + free(dump); + } + + if (verbose) { + PrintAndLogEx(INFO, "Num of bytes... %i (raw %i)", bytes, rawlen); + } + + // Has raw bytes ndef message header?bytes + switch (raw[0]) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0xFD: + case 0xFE: + break; + default: { + if (fix_msg == false) { + PrintAndLogEx(WARNING, "raw NDEF message doesn't have a proper header, continuing..."); + } else { + if (bytes + 2 > sizeof(raw)) { + PrintAndLogEx(WARNING, "no room for header, exiting..."); + return PM3_EMALLOC; + } + uint8_t tmp_raw[256]; + memcpy(tmp_raw, raw, sizeof(tmp_raw)); + raw[0] = 0x00; + raw[1] = bytes; + memcpy(raw + 2, tmp_raw, sizeof(raw) - 2); + bytes += 2; + PrintAndLogEx(SUCCESS, "Added generic message header (0x03)"); + } + } + } + + // Has raw bytes ndef a terminator block? + if (raw[bytes - 1] != 0xFE) { + if (fix_msg == false) { + PrintAndLogEx(WARNING, "raw NDEF message doesn't have a terminator block, continuing..."); + } else { + + if (bytes + 1 > sizeof(raw)) { + PrintAndLogEx(WARNING, "no room for terminator block, exiting..."); + return PM3_EMALLOC; + } + raw[bytes] = 0xFE; + bytes++; + PrintAndLogEx(SUCCESS, "Added terminator block (0xFE)"); + } + } + + if (verbose) { + PrintAndLogEx(INFO, "Num of Bytes... %u", bytes); + print_buffer(raw, bytes, 0); + } + + + // setup desfire authentication context + // authenticae to the new AID 00 00 01 + uint8_t aes_key[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + DesfireContext_t dctx; + dctx.secureChannel = DACNone; + DesfireSetKey(&dctx, 0, T_AES, aes_key); + DesfireSetKdf(&dctx, MFDES_KDF_ALGO_NONE, NULL, 0); + DesfireSetCommandSet(&dctx, DCCNativeISO); + DesfireSetCommMode(&dctx, DCMPlain); + res = DesfireSelectAndAuthenticateEx(&dctx, DACEV1, 0x000001, false, verbose); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(INFO, "failed aid auth.."); + return res; + } + + // write ndef file + + // hf mfdes write --aid 000002 --fid 02 - + // -n 0 -t aes -k 00000000000000000000000000000000 -m plain + uint8_t fnum = 0x02; + uint32_t offset = 0; + + res = DesfireWriteFile(&dctx, fnum, offset, bytes, raw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire WriteFile command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (verbose) { + PrintAndLogEx(INFO, "Write data file %02x " _GREEN_("success"), fnum); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "finished"); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, - {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, - {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, - {"ndefread", CmdHF14ANdefRead, IfPm3Iso14443a, "Read an NDEF file from ISO 14443-A Type 4 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"}, - {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("operations") " ---------------------"}, {"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, {"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"}, + {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, "Collect n>0 ISO14443-a UIDs in one go"}, + {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, + {"sim", CmdHF14ASim, IfPm3Iso14443a, "Simulate ISO 14443-a tag"}, + {"sniff", CmdHF14ASniff, IfPm3Iso14443a, "sniff ISO 14443-a traffic"}, + {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, + {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------------- " _CYAN_("apdu") " -------------------------"}, + {"apdu", CmdHF14AAPDU, IfPm3Iso14443a, "Send ISO 14443-4 APDU to tag"}, {"apdufind", CmdHf14AFindapdu, IfPm3Iso14443a, "Enumerate APDUs - CLA/INS/P1P2"}, + {"chaining", CmdHF14AChaining, IfPm3Iso14443a, "Control ISO 14443-4 input chaining"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------------- " _CYAN_("ndef") " -------------------------"}, + {"ndefformat", CmdHF14ANdefFormat, IfPm3Iso14443a, "Format ISO 14443-A as NFC Type 4 tag"}, + {"ndefread", CmdHF14ANdefRead, IfPm3Iso14443a, "Read an NDEF file from ISO 14443-A Type 4 tag"}, + {"ndefwrite", CmdHF14ANdefWrite, IfPm3Iso14443a, "Write NDEF records to ISO 14443-A tag"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 72d2e6e4f..a811db66b 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -37,9 +37,11 @@ typedef struct { } hintAIDList_t; int CmdHF14A(const char *Cmd); -int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff -int CmdHF14ASim(const char *Cmd); // used by hf mfu sim -int CmdHF14ANdefRead(const char *Cmd); +int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff +int CmdHF14ASim(const char *Cmd); // used by hf mfu sim +int CmdHF14ANdefRead(const char *Cmd); // used by cmdnfc.c +int CmdHF14ANdefFormat(const char *Cmd); // used by cmdnfc.c +int CmdHF14ANdefWrite(const char *Cmd); // used by cmdnfc.c int hf14a_getconfig(hf14a_config *config); int hf14a_setconfig(hf14a_config *config, bool verbose); @@ -51,4 +53,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card); + +bool Get_apdu_in_framing(void); +void Set_apdu_in_framing(bool v); #endif diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index d981727b4..12440c676 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -42,6 +42,9 @@ // client side time out, waiting for device to ask tag a APDU to answer #define APDU_TIMEOUT 2000 +// for static arrays +#define ST25TB_SR_BLOCK_SIZE 4 + // iso14b apdu input frame length static uint16_t apdu_frame_length = 0; //static uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; @@ -123,8 +126,8 @@ static void hf14b_aid_search(bool verbose) { } } - if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { - if (sw == 0x9000) { + if (sw == ISO7816_OK || sw == ISO7816_INVALID_DF || sw == ISO7816_FILE_TERMINATED) { + if (sw == ISO7816_OK) { if (verbose) PrintAndLogEx(SUCCESS, "Application ( " _GREEN_("ok") " )"); } else { if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); @@ -283,10 +286,11 @@ static int CmdHF14BCmdRaw(const char *Cmd) { arg_lit0("s", "std", "activate field, use ISO14B select"), arg_lit0(NULL, "sr", "activate field, use SRx ST select"), arg_lit0(NULL, "cts", "activate field, use ASK C-ticket select"), + arg_lit0(NULL, "xrx", "activate field, use Fuji/Xerox select"), arg_lit0("c", "crc", "calculate and append CRC"), arg_lit0("r", NULL, "do not read response from card"), arg_int0("t", "timeout", "", "timeout in ms"), - arg_lit0("v", "verbose", "verbose"), + arg_lit0("v", "verbose", "verbose output"), arg_str0("d", "data", "", "data, bytes to send"), arg_param_end }; @@ -296,10 +300,11 @@ static int CmdHF14BCmdRaw(const char *Cmd) { bool select_std = arg_get_lit(ctx, 2); bool select_sr = arg_get_lit(ctx, 3); bool select_cts = arg_get_lit(ctx, 4); - bool add_crc = arg_get_lit(ctx, 5); - bool read_reply = (arg_get_lit(ctx, 6) == false); - int user_timeout = arg_get_int_def(ctx, 7, -1); - bool verbose = arg_get_lit(ctx, 8); + bool select_xrx = arg_get_lit(ctx, 5); + bool add_crc = arg_get_lit(ctx, 6); + bool read_reply = (arg_get_lit(ctx, 7) == false); + int user_timeout = arg_get_int_def(ctx, 8, -1); + bool verbose = arg_get_lit(ctx, 9); uint32_t flags = ISO14B_CONNECT; if (add_crc) { @@ -318,11 +323,15 @@ static int CmdHF14BCmdRaw(const char *Cmd) { flags |= (ISO14B_SELECT_CTS | ISO14B_CLEARTRACE); if (verbose) PrintAndLogEx(INFO, "using ASK/C-ticket select"); + } else if (select_xrx) { + flags |= (ISO14B_SELECT_XRX | ISO14B_CLEARTRACE); + if (verbose) + PrintAndLogEx(INFO, "using Fuji/Xerox select"); } uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; int datalen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 9), data, sizeof(data), &datalen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 10), data, sizeof(data), &datalen); if (res && verbose) { PrintAndLogEx(INFO, "called with no raw bytes"); } @@ -395,6 +404,12 @@ static int CmdHF14BCmdRaw(const char *Cmd) { PrintAndLogEx(SUCCESS, "Got response for ASK/C-ticket select"); } + if (select_xrx) { + success = wait_cmd_14b(verbose, true, user_timeout); + if (verbose && success) + PrintAndLogEx(SUCCESS, "Got response for Fuji/Xerox select"); + } + // get back response from the raw bytes you sent. if (success && datalen > 0) { wait_cmd_14b(true, false, user_timeout); @@ -403,10 +418,14 @@ static int CmdHF14BCmdRaw(const char *Cmd) { return PM3_SUCCESS; } -static bool get_14b_UID(iso14b_card_select_t *card) { +static bool get_14b_UID(uint8_t *d, iso14b_type_t *found_type) { - if (card == NULL) + // sanity checks + if (d == NULL || found_type == NULL) { return false; + } + + *found_type = ISO14B_NONE; iso14b_raw_cmd_t packet = { .flags = (ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT), @@ -417,11 +436,11 @@ static bool get_14b_UID(iso14b_card_select_t *card) { PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (resp.oldarg[0] == 0) { - memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + memcpy(d, resp.data.asBytes, sizeof(iso14b_card_select_t)); + *found_type = ISO14B_SR; return true; } } @@ -433,7 +452,21 @@ static bool get_14b_UID(iso14b_card_select_t *card) { if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (resp.oldarg[0] == 0) { - memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + memcpy(d, resp.data.asBytes, sizeof(iso14b_card_select_t)); + *found_type = ISO14B_STANDARD; + return true; + } + } + + // test CT + packet.flags = (ISO14B_CONNECT | ISO14B_SELECT_CTS | ISO14B_DISCONNECT); + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + + if (resp.oldarg[0] == 0) { + memcpy(d, resp.data.asBytes, sizeof(iso14b_cts_card_select_t)); + *found_type = ISO14B_CT; return true; } } @@ -714,7 +747,7 @@ static void print_ct_general_info(void *vcard) { iso14b_cts_card_select_t card; memcpy(&card, (iso14b_cts_card_select_t *)vcard, sizeof(iso14b_cts_card_select_t)); - uint32_t uid32 = (card.uid[0] | card.uid[1] << 8 | card.uid[2] << 16 | card.uid[3] << 24); + uint32_t uid32 = MemLeToUint4byte(card.uid); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "ASK C-Ticket"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " ( " _YELLOW_("%010u") " )", sprint_hex(card.uid, sizeof(card.uid)), uid32); @@ -723,6 +756,72 @@ static void print_ct_general_info(void *vcard) { PrintAndLogEx(NORMAL, ""); } +static void print_hdr(void) { + PrintAndLogEx(INFO, " block# | data |lck| ascii"); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); +} + +static void print_footer(void) { + PrintAndLogEx(INFO, "---------+--------------+---+----------"); + PrintAndLogEx(NORMAL, ""); +} + +/* +static void print_ct_blocks(uint8_t *data, size_t len) { + + size_t blocks = len / ST25TB_SR_BLOCK_SIZE; + + print_hdr(); + + for (int i = 0; i <= blocks; i++) { + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", + i, + i, + sprint_hex(data + (i * 4), 4), + " ", + sprint_ascii(data + (i * 4), 4) + ); + } + print_footer(); +} +*/ + +static void print_sr_blocks(uint8_t *data, size_t len, const uint8_t *uid) { + + size_t blocks = (len / ST25TB_SR_BLOCK_SIZE) - 1 ; + uint8_t *systemblock = data + blocks * ST25TB_SR_BLOCK_SIZE ; + uint8_t chipid = get_st_chipid(uid); + PrintAndLogEx(SUCCESS, _GREEN_("%s") " tag", get_st_chip_model(chipid)); + + PrintAndLogEx(DEBUG, "systemblock : %s", sprint_hex(systemblock, ST25TB_SR_BLOCK_SIZE)); + PrintAndLogEx(DEBUG, " otp lock : %02x %02x", *systemblock, *(systemblock + 1)); + + print_hdr(); + + for (int i = 0; i < blocks; i++) { + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", + i, + i, + sprint_hex(data + (i * ST25TB_SR_BLOCK_SIZE), ST25TB_SR_BLOCK_SIZE), + get_st_lock_info(chipid, systemblock, i), + sprint_ascii(data + (i * ST25TB_SR_BLOCK_SIZE), ST25TB_SR_BLOCK_SIZE) + ); + } + + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", + 0xFF, + 0xFF, + sprint_hex(systemblock, ST25TB_SR_BLOCK_SIZE), + get_st_lock_info(chipid, systemblock, 0xFF), + sprint_ascii(systemblock, ST25TB_SR_BLOCK_SIZE) + ); + + print_footer(); +} + // iceman, calypso? // 05 00 00 = find one tag in field // 1d xx xx xx xx 00 08 01 00 = attrib xx=UID (resp 10 [f9 e0]) @@ -833,7 +932,7 @@ static int CmdHF14Binfo(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("s", "aidsearch", "checks if AIDs from aidlist.json is present on the card and prints information about found AIDs"), - arg_lit0("v", "verbose", "verbose"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1083,16 +1182,17 @@ static int CmdHF14BReader(const char *Cmd) { CLIParserInit(&ctx, "hf 14b reader", "Act as a 14443B reader to identify a tag", "hf 14b reader\n" + "hf 14b reader -@ -> continuous reader mode" ); void *argtable[] = { arg_param_begin, - arg_lit0("s", "silent", "silent (no messages)"), + arg_lit0("v", "verbose", "verbose output"), arg_lit0("@", NULL, "optional - continuous reader mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool verbose = (arg_get_lit(ctx, 1) == false); + bool verbose = arg_get_lit(ctx, 1); bool cm = arg_get_lit(ctx, 2); CLIParserFree(ctx); @@ -1268,166 +1368,163 @@ static int CmdHF14BDump(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); - iso14b_card_select_t card; - if (get_14b_UID(&card) == false) { + + uint8_t select[sizeof(iso14b_card_select_t)] = {0}; + iso14b_type_t select_cardtype = ISO14B_NONE; + if (get_14b_UID(select, &select_cardtype) == false) { PrintAndLogEx(WARNING, "no tag found"); return PM3_SUCCESS; } - if (card.uidlen != 8) { - PrintAndLogEx(FAILED, "current dump command only work with SRI4K / SRI512 tags"); - return PM3_SUCCESS; - } + if (select_cardtype == ISO14B_CT) { + iso14b_cts_card_select_t ct_card; + memcpy(&ct_card, (iso14b_cts_card_select_t *)&select, sizeof(iso14b_cts_card_select_t)); - // detect cardsize - // 1 = 4096 - // 2 = 512 - uint8_t cardtype = get_st_cardsize(card.uid); - uint8_t blocks = 0; - uint16_t cardsize = 0; + uint32_t uid32 = MemLeToUint4byte(ct_card.uid); + PrintAndLogEx(SUCCESS, "UID: " _GREEN_("%s") " ( " _YELLOW_("%010u") " )", sprint_hex(ct_card.uid, 4), uid32); - switch (cardtype) { - case 2: - cardsize = (512 / 8) + 4; - blocks = 0x0F; - break; - case 1: - default: - cardsize = (4096 / 8) + 4; - blocks = 0x7F; - break; - } + // Have to figure out how large one of these are.. + PrintAndLogEx(FAILED, "Dumping CT tags is not implemented yet."); - uint8_t chipid = get_st_chipid(card.uid); - PrintAndLogEx(SUCCESS, "found a " _GREEN_("%s") " tag", get_st_chip_model(chipid)); - - // detect blocksize from card :) - PrintAndLogEx(INFO, "reading tag memory from UID " _GREEN_("%s"), sprint_hex_inrow(SwapEndian64(card.uid, card.uidlen, 8), card.uidlen)); - - iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 2); - if (packet == NULL) { - PrintAndLogEx(FAILED, "failed to allocate memory"); - return PM3_EMALLOC; - } - packet->flags = (ISO14B_CONNECT | ISO14B_SELECT_SR); - packet->timeout = 0; - packet->rawlen = 0; - - clearCommandBuffer(); - SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t)); - PacketResponseNG resp; - - // select - int status; - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - status = resp.oldarg[0]; - if (status < 0) { - PrintAndLogEx(FAILED, "failed to select arg0[%" PRId64 "]", resp.oldarg[0]); - free(packet); - return switch_off_field_14b(); - } - } - - PrintAndLogEx(INFO, "." NOLF); - - uint8_t data[cardsize]; - memset(data, 0, sizeof(data)); - uint16_t blocknum = 0; - - for (int retry = 0; retry < 5; retry++) { - - // set up the read command - packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); - packet->rawlen = 2; - packet->raw[0] = ISO14443B_READ_BLK; - packet->raw[1] = blocknum & 0xFF; - - clearCommandBuffer(); - SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + 2); - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - - status = resp.oldarg[0]; - if (status < 0) { - PrintAndLogEx(FAILED, "retrying one more time"); - continue; - } - - uint16_t len = (resp.oldarg[1] & 0xFFFF); - uint8_t *recv = resp.data.asBytes; - - if (check_crc(CRC_14443_B, recv, len) == false) { - PrintAndLogEx(FAILED, "crc fail, retrying one more time"); - continue; - } - - - // last read - if (blocknum == 0xFF) { - // we reserved space for this block after 0x0F and 0x7F, ie 0x10, 0x80 - memcpy(data + (blocks * 4), recv, 4); - break; - } - memcpy(data + (blocknum * 4), recv, 4); - - - retry = 0; - blocknum++; - if (blocknum > blocks) { - // read config block - blocknum = 0xFF; - } - - PrintAndLogEx(NORMAL, "." NOLF); - fflush(stdout); - } - } - free(packet); - - PrintAndLogEx(NORMAL, ""); - - if (blocknum != 0xFF) { - PrintAndLogEx(FAILED, "dump failed"); + // print_ct_blocks(data, cardsize); return switch_off_field_14b(); } - PrintAndLogEx(DEBUG, "systemblock : %s", sprint_hex(data + (blocks * 4), 4)); - PrintAndLogEx(DEBUG, " otp lock : %02x %02x", data[(blocks * 4)], data[(blocks * 4) + 1]); - - - PrintAndLogEx(INFO, " block# | data |lck| ascii"); - PrintAndLogEx(INFO, "---------+--------------+---+----------"); - - for (int i = 0; i <= blocks; i++) { - PrintAndLogEx(INFO, - "%3d/0x%02X | %s | %s | %s", - i, - i, - sprint_hex(data + (i * 4), 4), - get_st_lock_info(chipid, data + (blocks * 4), i), - sprint_ascii(data + (i * 4), 4) - ); + if (select_cardtype == ISO14B_STANDARD) { + // Have to figure out how large one of these are.. + PrintAndLogEx(FAILED, "Dumping Standard ISO14443-B tags is not implemented yet."); + // print_std_blocks(data, cardsize); + return switch_off_field_14b(); } - PrintAndLogEx(INFO, - "%3d/0x%02X | %s | %s | %s", - 0xFF, - 0xFF, - sprint_hex(data + (0xFF * 4), 4), - get_st_lock_info(chipid, data + (blocks * 4), 0xFF), - sprint_ascii(data + (0xFF * 4), 4) - ); - PrintAndLogEx(INFO, "---------+--------------+---+----------"); - PrintAndLogEx(NORMAL, ""); - // save to file - if (fnlen < 1) { - PrintAndLogEx(INFO, "using UID as filename"); - char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); - FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); + if (select_cardtype == ISO14B_SR) { + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)&select, sizeof(iso14b_card_select_t)); + + // detect cardsize + // 1 = 4096 + // 2 = 512 + uint8_t cardtype = get_st_cardsize(card.uid); + uint8_t lastblock = 0; + uint16_t cardsize = 0; + + switch (cardtype) { + case 2: + cardsize = (512 / 8) + ST25TB_SR_BLOCK_SIZE; + lastblock = 0x0F; + break; + case 1: + default: + cardsize = (4096 / 8) + ST25TB_SR_BLOCK_SIZE; + lastblock = 0x7F; + break; + } + + uint8_t chipid = get_st_chipid(card.uid); + PrintAndLogEx(SUCCESS, "found a " _GREEN_("%s") " tag", get_st_chip_model(chipid)); + + // detect blocksize from card :) + PrintAndLogEx(INFO, "reading tag memory from UID " _GREEN_("%s"), sprint_hex_inrow(SwapEndian64(card.uid, card.uidlen, 8), card.uidlen)); + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 2); + if (packet == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + packet->flags = (ISO14B_CONNECT | ISO14B_SELECT_SR); + packet->timeout = 0; + packet->rawlen = 0; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t)); + PacketResponseNG resp; + + // select SR tag + int status; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(FAILED, "failed to select arg0[%" PRId64 "]", resp.oldarg[0]); + free(packet); + return switch_off_field_14b(); + } + } + + PrintAndLogEx(INFO, "." NOLF); + + uint8_t data[cardsize]; + memset(data, 0, sizeof(data)); + uint16_t blocknum = 0; + + for (int retry = 0; retry < 5; retry++) { + + // set up the read command + packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); + packet->rawlen = 2; + packet->raw[0] = ISO14443B_READ_BLK; + packet->raw[1] = blocknum & 0xFF; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + 2); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + + status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(FAILED, "retrying one more time"); + continue; + } + + uint16_t len = (resp.oldarg[1] & 0xFFFF); + uint8_t *recv = resp.data.asBytes; + + if (check_crc(CRC_14443_B, recv, len) == false) { + PrintAndLogEx(FAILED, "crc fail, retrying one more time"); + continue; + } + + + // last read + if (blocknum == 0xFF) { + // we reserved space for this block after 0x0F and 0x7F, ie 0x10, 0x80 + memcpy(data + ((lastblock + 1) * ST25TB_SR_BLOCK_SIZE), recv, ST25TB_SR_BLOCK_SIZE); + break; + } + memcpy(data + (blocknum * ST25TB_SR_BLOCK_SIZE), recv, ST25TB_SR_BLOCK_SIZE); + + + retry = 0; + blocknum++; + if (blocknum > lastblock) { + // read config block + blocknum = 0xFF; + } + + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + } + } + free(packet); + + PrintAndLogEx(NORMAL, ""); + + if (blocknum != 0xFF) { + PrintAndLogEx(FAILED, "dump failed"); + return switch_off_field_14b(); + } + + print_sr_blocks(data, cardsize, card.uid); + + // save to file + if (fnlen < 1) { + PrintAndLogEx(INFO, "using UID as filename"); + char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); + } + + size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; + pm3_save_dump(filename, data, datalen, jsf14b, ST25TB_SR_BLOCK_SIZE); } - size_t datalen = (blocks + 1) * 4; - pm3_save_dump(filename, data, datalen, jsf14b, 4); return switch_off_field_14b(); } /* @@ -1907,12 +2004,15 @@ int CmdHF14BNdefRead(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "save raw NDEF to file"), + arg_litn("v", "verbose", 0, 2, "show technical data"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); bool activate_field = true; @@ -1935,7 +2035,7 @@ int CmdHF14BNdefRead(const char *Cmd) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); res = PM3_ESOFT; goto out; @@ -1955,7 +2055,7 @@ int CmdHF14BNdefRead(const char *Cmd) { goto out; sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); res = PM3_ESOFT; goto out; @@ -1971,7 +2071,7 @@ int CmdHF14BNdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); res = PM3_ESOFT; goto out; @@ -1990,7 +2090,7 @@ int CmdHF14BNdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); res = PM3_ESOFT; goto out; @@ -1999,13 +2099,83 @@ int CmdHF14BNdefRead(const char *Cmd) { if (fnlen != 0) { saveFile(filename, ".bin", response + 2, resplen - 4); } - res = NDEFRecordsDecodeAndPrint(response + 2, resplen - 4); + res = NDEFRecordsDecodeAndPrint(response + 2, resplen - 4, verbose); out: switch_off_field_14b(); return res; } +/* extract uid from filename + * filename must match '^hf-14b-[0-9A-F]{16}' + */ +uint8_t *get_uid_from_filename(const char *filename) { + static uint8_t uid[8] ; + memset(uid, 0, 8) ; + char uidinhex[17] ; + if (strlen(filename) < 23 || strncmp(filename, "hf-14b-", 7)) { + PrintAndLogEx(ERR, "can't get uid from filename '%s'. Expected format is hf-14b-...", filename); + return uid ; + } + // extract uid part from filename + strncpy(uidinhex, filename + 7, 16) ; + uidinhex[16] = '\0' ; + int len = hex_to_bytes(uidinhex, uid, 8); + if (len == 8) + return SwapEndian64(uid, 8, 8); + else { + PrintAndLogEx(ERR, "get_uid_from_filename failed: hex_to_bytes returned %d", len); + memset(uid, 0, 8); + } + return uid ; +} + +static int CmdHF14BView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b view", + "Print a ISO14443-B dump file (bin/eml/json)", + "hf 14b view -f hf-14b-01020304-dump.bin" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + // read dump file + uint8_t *dump = NULL; + size_t bytes_read = 0; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (ST25TB_SR_BLOCK_SIZE * 0xFF)); + if (res != PM3_SUCCESS) { + return res; + } + + uint16_t block_cnt = bytes_read / ST25TB_SR_BLOCK_SIZE; + + if (verbose) { + PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); + PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); + } + + // figure out a way to identify the different dump files. + // STD/SR/CT is difference + print_sr_blocks(dump, bytes_read, get_uid_from_filename(filename)); + //print_std_blocks(dump, bytes_read); + //print_ct_blocks(dump, bytes_read); + + free(dump); + return PM3_SUCCESS; +} + + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"apdu", CmdHF14BAPDU, IfPm3Iso14443b, "Send ISO 14443-4 APDU to tag"}, @@ -2020,6 +2190,7 @@ static command_t CommandTable[] = { {"sniff", CmdHF14BSniff, IfPm3Iso14443b, "Eavesdrop ISO-14443-B"}, {"rdbl", CmdHF14BSriRdBl, IfPm3Iso14443b, "Read SRI512/SRIX4x block"}, {"sriwrite", CmdHF14BWriteSri, IfPm3Iso14443b, "Write data to a SRI512 or SRIX4K tag"}, + {"view", CmdHF14BView, AlwaysAvailable, "Display content from tag dump file"}, // {"valid", srix4kValid, AlwaysAvailable, "srix4k checksum test"}, {NULL, NULL, NULL, NULL} }; @@ -2057,20 +2228,25 @@ int readHF14B(bool loop, bool verbose) { do { // try std 14b (atqb) if (HF14B_std_reader(verbose)) - return PM3_SUCCESS; + if (loop) + continue; // try ST Microelectronics 14b if (HF14B_st_reader(verbose)) - return PM3_SUCCESS; + if (loop) + continue; // try ASK CT 14b if (HF14B_ask_ct_reader(verbose)) - return PM3_SUCCESS; + if (loop) + continue; // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. if (HF14B_other_reader(verbose)) - return PM3_SUCCESS; + if (loop) + continue; + } while (loop && kbd_enter_pressed() == false); diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index b81149722..e66f5d19b 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -25,6 +25,7 @@ int CmdHF14B(const char *Cmd); int CmdHF14BNdefRead(const char *Cmd); +uint8_t *get_uid_from_filename(const char *filename); int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout); int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index d780056a6..c80f8cac4 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -47,6 +47,8 @@ #define Logic0 Iso15693Logic0 #define Logic1 Iso15693Logic1 #define FrameEOF Iso15693FrameEOF +#define CARD_MEMORY_SIZE 4096 +#define HF15_UID_LENGTH 8 #ifndef Crc15 # define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) @@ -66,7 +68,7 @@ typedef struct { // structure and database for uid -> tagtype lookups typedef struct { uint64_t uid; - int mask; // how many MSB bits used + uint64_t mask; // how many MSB bits used, or mask itself if larger than 64 const char *desc; } productName_t; @@ -89,17 +91,23 @@ static const productName_t uidmapping[] = { // E0 04 xx // 04 = Manufacturer code (Philips/NXP) // XX = IC id (Chip ID Family) - //I-Code SLI SL2 ICS20 [IC id = 01] - //I-Code SLI-S [IC id = 02] - //I-Code SLI-L [IC id = 03] - //I-Code SLIX [IC id = 01 + bit36 set to 1 (starting from bit0 - different from normal SLI)] + //I-Code SLI SL2 ICS20 [IC id = 01 + bit35 set to 0 + bit36 set to 0] + //I-Code SLIX [IC id = 01 + bit35 set to 0 + bit36 set to 1] //I-Code SLIX2 [IC id = 01 + bit35 set to 1 + bit36 set to 0] + //I-Code SLI-S [IC id = 02 + bit36 set to 0] //I-Code SLIX-S [IC id = 02 + bit36 set to 1] + //I-Code SLI-L [IC id = 03 + bit36 set to 0] //I-Code SLIX-L [IC id = 03 + bit36 set to 1] { 0xE004000000000000LL, 16, "NXP Semiconductors Germany (Philips)" }, { 0xE004010000000000LL, 24, "NXP(Philips); IC SL2 ICS20/ICS21(SLI) ICS2002/ICS2102(SLIX) ICS2602(SLIX2)" }, - { 0xE004020000000000LL, 24, "NXP(Philips); IC SL2 ICS53/ICS54(SLI-S) ICS5302/ICS5402(SLIX-S)" }, - { 0xE004030000000000LL, 24, "NXP(Philips); IC SL2 ICS50/ICS51(SLI-L) ICS5002/ICS5102(SLIX-L)" }, + { 0xE004011800000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC NTP53x2/NTP5210/NTA5332(NTAG 5)" }, + { 0xE004010000000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC SL2 ICS20/ICS21(SLI)" }, + { 0xE004011000000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC SL2 ICS2002/ICS2102(SLIX)" }, + { 0xE004010800000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC SL2 ICS2602(SLIX2)" }, + { 0xE004020000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); IC SL2 ICS53/ICS54(SLI-S)" }, + { 0xE004021000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); ICS5302/ICS5402(SLIX-S)" }, + { 0xE004030000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); IC SL2 ICS50/ICS51(SLI-L)" }, + { 0xE004031000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); ICS5002/ICS5102(SLIX-L)" }, // E0 05 XX .. .. .. // 05 = Manufacturer code (Infineon) @@ -269,16 +277,14 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { } }; */ - uint8_t i; - uint8_t revuid[8]; - for (i = 0; i < sizeof(revuid); i++) { - revuid[i] = uid[7 - i]; - } - uint8_t revsign[32]; - for (i = 0; i < sizeof(revsign); i++) { - revsign[i] = signature[31 - i]; - } + uint8_t revuid[8] = {0}; + reverse_array_copy(uid, sizeof(revuid), revuid); + + uint8_t revsign[32] = {0}; + reverse_array_copy(signature, sizeof(revsign), revsign); + + uint8_t i; int reason = 0; bool is_valid = false; for (i = 0; i < ARRAYLEN(nxp_15693_public_keys); i++) { @@ -363,7 +369,11 @@ static const char *getTagInfo_15(uint8_t *uid) { int i = 0, best = -1; memcpy(&myuid, uid, sizeof(uint64_t)); while (uidmapping[i].mask > 0) { - mask = (~0ULL) << (64 - uidmapping[i].mask); + if (uidmapping[i].mask > 64) { + mask = uidmapping[i].mask; + } else { + mask = (~0ULL) << (64 - uidmapping[i].mask); + } if ((myuid & mask) == uidmapping[i].uid) { if (best == -1) { best = i; @@ -641,6 +651,94 @@ static int CmdHF15Samples(const char *Cmd) { return PM3_SUCCESS; } +static int NxpTestEAS(uint8_t *uid) { + uint8_t fast = 1; + uint8_t reply = 1; + PacketResponseNG resp; + uint16_t reqlen = 0; + uint8_t req[PM3_CMD_DATA_SIZE] = {0}; + + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] = ISO15693_EAS_ALARM; + req[reqlen++] = 0x04; // IC manufacturer code + memcpy(req + 3, uid, 8); // add UID + reqlen += 8; + + AddCrc15(req, reqlen); + reqlen += 2; + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + + if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "iso15693 timeout"); + } else { + PrintAndLogEx(NORMAL, ""); + + + if (resp.length < 2) { + PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is not active"); + } else { + uint8_t *recv = resp.data.asBytes; + + if (!(recv[0] & ISO15_RES_ERROR)) { + PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is active."); + PrintAndLogEx(INFO, " EAS sequence: %s", sprint_hex(recv + 1, 32)); + } + } + } + + return PM3_SUCCESS; +} + +static int NxpCheckSig(uint8_t *uid) { + uint8_t fast = 1; + uint8_t reply = 1; + PacketResponseNG resp; + uint16_t reqlen = 0; + uint8_t req[PM3_CMD_DATA_SIZE] = {0}; + + // Check if we can also read the signature + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] = ISO15693_READ_SIGNATURE; + req[reqlen++] = 0x04; // IC manufacturer code + memcpy(req + 3, uid, 8); // add UID + reqlen += 8; + + AddCrc15(req, reqlen); + reqlen += 2; + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + + if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "iso15693 timeout"); + DropField(); + return PM3_ETIMEOUT; + } + + DropField(); + + if (resp.length < 2) { + PrintAndLogEx(WARNING, "iso15693 card doesn't answer to READ SIGNATURE command"); + return PM3_EWRONGANSWER; + } + + uint8_t *recv = resp.data.asBytes; + + if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return PM3_EWRONGANSWER; + } + + uint8_t signature[32] = {0x00}; + memcpy(signature, recv + 1, 32); + + nxp_15693_print_signature(uid, signature); + + return PM3_SUCCESS; +} + // Get NXP system information from SLIX2 tag/VICC static int NxpSysInfo(uint8_t *uid) { @@ -690,14 +788,14 @@ static int NxpSysInfo(uint8_t *uid) { } bool support_signature = (recv[5] & 0x01); - bool support_easmode = (recv[4] & 0x03); + bool support_easmode = (recv[4] & 0x04); PrintAndLogEx(INFO, "--------- " _CYAN_("NXP Sysinfo") " ---------"); PrintAndLogEx(INFO, " raw : %s", sprint_hex(recv, 8)); PrintAndLogEx(INFO, " Password protection configuration:"); PrintAndLogEx(INFO, " * Page L read%s password protected", ((recv[2] & 0x01) ? "" : " not")); PrintAndLogEx(INFO, " * Page L write%s password protected", ((recv[2] & 0x02) ? "" : " not")); - PrintAndLogEx(INFO, " * Page H read%s password protected", ((recv[2] & 0x08) ? "" : " not")); + PrintAndLogEx(INFO, " * Page H read%s password protected", ((recv[2] & 0x10) ? "" : " not")); PrintAndLogEx(INFO, " * Page H write%s password protected", ((recv[2] & 0x20) ? "" : " not")); PrintAndLogEx(INFO, " Lock bits:"); @@ -710,89 +808,23 @@ static int NxpSysInfo(uint8_t *uid) { PrintAndLogEx(INFO, " * User memory password protection%s supported", ((recv[4] & 0x01) ? "" : " not")); PrintAndLogEx(INFO, " * Counter feature%s supported", ((recv[4] & 0x02) ? "" : " not")); PrintAndLogEx(INFO, " * EAS ID%s supported by EAS ALARM command", support_easmode ? "" : " not"); - PrintAndLogEx(INFO, " * EAS password protection%s supported", ((recv[4] & 0x04) ? "" : " not")); + PrintAndLogEx(INFO, " * EAS password protection%s supported", ((recv[4] & 0x08) ? "" : " not")); PrintAndLogEx(INFO, " * AFI password protection%s supported", ((recv[4] & 0x10) ? "" : " not")); PrintAndLogEx(INFO, " * Extended mode%s supported by INVENTORY READ command", ((recv[4] & 0x20) ? "" : " not")); PrintAndLogEx(INFO, " * EAS selection%s supported by extended mode in INVENTORY READ command", ((recv[4] & 0x40) ? "" : " not")); PrintAndLogEx(INFO, " * READ SIGNATURE command%s supported", support_signature ? "" : " not"); PrintAndLogEx(INFO, " * Password protection for READ SIGNATURE command%s supported", ((recv[5] & 0x02) ? "" : " not")); - PrintAndLogEx(INFO, " * STAY QUIET PERSISTENT command%s supported", ((recv[5] & 0x03) ? "" : " not")); + PrintAndLogEx(INFO, " * STAY QUIET PERSISTENT command%s supported", ((recv[5] & 0x04) ? "" : " not")); PrintAndLogEx(INFO, " * ENABLE PRIVACY command%s supported", ((recv[5] & 0x10) ? "" : " not")); PrintAndLogEx(INFO, " * DESTROY command%s supported", ((recv[5] & 0x20) ? "" : " not")); - PrintAndLogEx(INFO, " * Additional 32 bits feature flags are%s transmitted", ((recv[5] & 0x80) ? "" : " not")); + PrintAndLogEx(INFO, " * Additional 32 bits feature flags are%s transmitted", ((recv[7] & 0x80) ? "" : " not")); if (support_easmode) { - reqlen = 0; - req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[reqlen++] = ISO15693_EAS_ALARM; - req[reqlen++] = 0x04; // IC manufacturer code - memcpy(req + 3, uid, 8); // add UID - reqlen += 8; - - AddCrc15(req, reqlen); - reqlen += 2; - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); - - if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "iso15693 timeout"); - } else { - PrintAndLogEx(NORMAL, ""); - - - if (resp.length < 2) { - PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is not active"); - } else { - recv = resp.data.asBytes; - - if (!(recv[0] & ISO15_RES_ERROR)) { - PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is active."); - PrintAndLogEx(INFO, " EAS sequence: %s", sprint_hex(recv + 1, 32)); - } - } - } + NxpTestEAS(uid); } if (support_signature) { - // Check if we can also read the signature - reqlen = 0; - req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[reqlen++] = ISO15693_READ_SIGNATURE; - req[reqlen++] = 0x04; // IC manufacturer code - memcpy(req + 3, uid, 8); // add UID - reqlen += 8; - - AddCrc15(req, reqlen); - reqlen += 2; - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); - - if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "iso15693 timeout"); - DropField(); - return PM3_ETIMEOUT; - } - - DropField(); - - if (resp.length < 2) { - PrintAndLogEx(WARNING, "iso15693 card doesn't answer to READ SIGNATURE command"); - return PM3_EWRONGANSWER; - } - - recv = resp.data.asBytes; - - if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); - return PM3_EWRONGANSWER; - } - - uint8_t signature[32] = {0x00}; - memcpy(signature, recv + 1, 32); - - nxp_15693_print_signature(uid, signature); + NxpCheckSig(uid); } return PM3_SUCCESS; @@ -934,6 +966,12 @@ static int CmdHF15Info(const char *Cmd) { if (data[8] == 0x04 && data[7] == 0x01 && nxp_version == 0x08) { PrintAndLogEx(DEBUG, "SLIX2 Detected, getting NXP System Info"); return NxpSysInfo(uid); + } else if (data[8] == 0x04 && data[7] == 0x01 && nxp_version == 0x18) { //If it is an NTAG 5 + PrintAndLogEx(DEBUG, "NTAG 5 Detected, getting NXP System Info"); + return NxpSysInfo(uid); + } else if (data[8] == 0x04 && (data[7] == 0x01 || data[7] == 0x02 || data[7] == 0x03)) { //If SLI, SLIX, SLIX-l, or SLIX-S check EAS status + PrintAndLogEx(DEBUG, "SLI, SLIX, SLIX-L, or SLIX-S Detected checking EAS status"); + return NxpTestEAS(uid); } PrintAndLogEx(NORMAL, ""); @@ -988,6 +1026,230 @@ static int CmdHF15Reader(const char *Cmd) { return PM3_SUCCESS; } +static int hf15EmlClear(void) { + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_EML_CLEAR, NULL, 0); + PacketResponseNG resp; + WaitForResponse(CMD_HF_ISO15693_EML_CLEAR, &resp); + return PM3_SUCCESS; +} + +static int hf15EmlSetMem(uint8_t *data, uint8_t count, size_t offset) { + struct p { + uint32_t offset; + uint8_t count; + uint8_t data[]; + } PACKED; + + size_t size = count; + if (size > (PM3_CMD_DATA_SIZE - sizeof(struct p))) { + return PM3_ESOFT; + } + + size_t paylen = sizeof(struct p) + size; + struct p *payload = calloc(1, paylen); + + payload->offset = offset; + payload->count = count; + memcpy(payload->data, data, size); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_EML_SETMEM, (uint8_t *)payload, paylen); + free(payload); + return PM3_SUCCESS; +} + +static int CmdHF15ELoad(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 eload", + "Load memory image from file to be used with 'hf 15 sim'", + "hf 15 eload -f hf-15-01020304.bin\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of image"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + uint8_t *data = NULL; + size_t bytes_read = 0; + int res = pm3_load_dump(filename, (void **)&data, &bytes_read, CARD_MEMORY_SIZE); + if (res != PM3_SUCCESS) { + return res; + } + + if (bytes_read > CARD_MEMORY_SIZE) { + PrintAndLogEx(FAILED, "Memory image too large."); + free(data); + return PM3_EINVARG; + } + if (bytes_read == 0) { + PrintAndLogEx(FAILED, "Memory image empty."); + free(data); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "Clearing emulator memory"); + fflush(stdout); + hf15EmlClear(); + + PrintAndLogEx(INFO, "Uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); + + // fast push mode + g_conn.block_after_ACK = true; + + int chuncksize = 64; + size_t offset = 0; + + while (bytes_read > 0) { + if (bytes_read <= chuncksize) { + // Disable fast mode on last packet + g_conn.block_after_ACK = false; + } + + int tosend = MIN(chuncksize, bytes_read); + if (hf15EmlSetMem(data + offset, tosend, offset) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Can't set emulator memory at offest: %zu / 0x%zx", offset, offset); + free(data); + return PM3_ESOFT; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + + offset += tosend; + bytes_read -= tosend; + } + free(data); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf 15 sim -h`")); + PrintAndLogEx(INFO, "Done!"); + return PM3_SUCCESS; +} + +static int CmdHF15ESave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 esave", + "Save emulator memory into three files (BIN/EML/JSON) ", + "hf 15 esave -f hf-15-01020304" + "hf 15 esave -b 8 -c 42 -f hf-15-01020304" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump"), + arg_int0("b", "blocksize", "", "block size, defaults to 4"), + arg_int0("c", "count", "", "number of blocks to export, defaults to all"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + int blocksize = arg_get_int_def(ctx, 2, 4); + int count = arg_get_int_def(ctx, 3, -1); + CLIParserFree(ctx); + + int bytes = CARD_MEMORY_SIZE; + if (count > 0 && count * blocksize <= bytes) { + bytes = count * blocksize; + } + + // reserve memory + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "Downloading %u bytes from emulator memory", bytes); + if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(dump); + return PM3_ETIMEOUT; + } + + pm3_save_dump(filename, dump, bytes, jsf15, blocksize); + free(dump); + return PM3_SUCCESS; +} + +static void print_hrule(int blocksize) { + char dashes[] = "------------------------------------------------------------"; + PrintAndLogEx(INFO, "-----+%.*s-+-%.*s-", 3 * blocksize, dashes, blocksize, dashes); +} + +static void print_blocks_15693(uint8_t *data, uint16_t bytes, int blocksize) { + int blocks = bytes / blocksize; + PrintAndLogEx(NORMAL, ""); + print_hrule(blocksize); + char spaces[] = " "; + PrintAndLogEx(INFO, " blk | data %.*s| ascii", MAX(0, 3 * blocksize - 5), spaces); + print_hrule(blocksize); + for (int i = 0; i < blocks; i++) { + PrintAndLogEx(INFO, "%4d | %s ", i, sprint_hex_ascii(data + (i * blocksize), blocksize)); + + } + if (bytes % blocksize != 0) { + // If there is something left over print it too + // This will have a broken layout, but should not happen anyway + PrintAndLogEx(INFO, "%4d | %s ", blocks, sprint_hex_ascii(data + (blocks * blocksize), + bytes % blocksize)); + } + print_hrule(blocksize); + PrintAndLogEx(NORMAL, ""); +} + +static int CmdHF15EView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 eview", + "It displays emulator memory", + "hf 15 eview\n" + "hf 15 eview -b 8 -c 60\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("b", "blocksize", "", "block size, defaults to 4"), + arg_int0("c", "count", "", "number of blocks to display, defaults to all"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int blocksize = arg_get_int_def(ctx, 1, 4); + int count = arg_get_int_def(ctx, 2, -1); + CLIParserFree(ctx); + + int bytes = CARD_MEMORY_SIZE; + if (count > 0 && count * blocksize <= bytes) { + bytes = count * blocksize; + } + + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "Downloading %u bytes from emulator memory", bytes); + if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(dump); + return PM3_ETIMEOUT; + } + + print_blocks_15693(dump, bytes, blocksize); + free(dump); + return PM3_SUCCESS; +} + // Simulation is still not working very good // helptext static int CmdHF15Sim(const char *Cmd) { @@ -1000,23 +1262,27 @@ static int CmdHF15Sim(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1("u", "uid", "<8b hex>", "UID eg E011223344556677"), + arg_int0("b", "blocksize", "", "block size, defaults to 4"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); struct { uint8_t uid[8]; + uint8_t block_size; } PACKED payload; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, payload.uid, &uidlen); - CLIParserFree(ctx); - if (uidlen != 8) { PrintAndLogEx(WARNING, "UID must include 16 HEX symbols"); + CLIParserFree(ctx); return PM3_EINVARG; } + payload.block_size = arg_get_int_def(ctx, 2, 4); + CLIParserFree(ctx); + PrintAndLogEx(SUCCESS, "Starting simulating UID " _YELLOW_("%s"), iso15693_sprintUID(NULL, payload.uid)); PrintAndLogEx(INFO, "press " _YELLOW_("`Pm3 button`") " to cancel"); @@ -1085,94 +1351,81 @@ static int CmdHF15WriteAfi(const char *Cmd) { CLIParserInit(&ctx, "hf 15 writeafi", "Write AFI on card", "hf 15 writeafi -* --afi 12\n" - "hf 15 writeafi -u E011223344556677 --afi 12" + "hf 15 writeafi -u E011223344556677 --afi 12 -p 0F0F0F0F" ); - void *argtable[6 + 2] = {}; - uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_int1(NULL, "afi", "", "AFI number (0-255)"); - argtable[arglen++] = arg_param_end; + void *argtable[5] = {}; + argtable[0] = arg_param_begin; + argtable[1] = arg_str0("u", "uid", "", "full UID, 8 bytes"); + argtable[2] = arg_int1(NULL, "afi", "", "AFI number (0-255)"); + argtable[3] = arg_str0("p", "pwd", "", "optional AFI/EAS password"); + argtable[4] = arg_param_end; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + struct { + uint8_t pwd[4]; + bool use_pwd; + uint8_t uid[8]; + bool use_uid; + uint8_t afi; + } PACKED payload; - uint8_t uid[8]; int uidlen = 0; - CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool unaddressed = arg_get_lit(ctx, 2); - bool scan = arg_get_lit(ctx, 3); - int fast = (arg_get_lit(ctx, 4) == false); - bool add_option = arg_get_lit(ctx, 5); + CLIGetHexWithReturn(ctx, 1, payload.uid, &uidlen); + + payload.afi = arg_get_int_def(ctx, 2, 0); + + int pwdlen; + CLIGetHexWithReturn(ctx, 3, payload.pwd, &pwdlen); - int afi = arg_get_int_def(ctx, 6, 0); CLIParserFree(ctx); + payload.use_pwd = false; + if (pwdlen == 4) { + payload.use_pwd = true; + } + + payload.use_uid = false; + if (uidlen == 8) { + payload.use_uid = true; + } + // sanity checks - if ((scan + unaddressed + uidlen) > 1) { - PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + if (uidlen != 0 && uidlen != 8) { + PrintAndLogEx(WARNING, "uid must be 8 hex bytes if provided"); return PM3_EINVARG; } - // request to be sent to device/card - uint16_t flags = arg_get_raw_flag(uidlen, unaddressed, scan, add_option); - uint8_t req[16] = {flags, ISO15693_WRITE_AFI}; - uint16_t reqlen = 2; - - if (unaddressed == false) { - if (scan) { - if (getUID(false, uid) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "no tag found"); - return PM3_EINVARG; - } - uidlen = 8; - } - - if (uidlen == 8) { - // add UID (scan, uid) - memcpy(req + reqlen, uid, sizeof(uid)); - reqlen += sizeof(uid); - } - PrintAndLogEx(SUCCESS, "Using UID... " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); + if (pwdlen > 0 && pwdlen != 4) { + PrintAndLogEx(WARNING, "password must be 4 hex bytes if provided"); + return PM3_ESOFT; } - // enforce, since we are writing - req[0] |= ISO15_REQ_OPTION; - - req[reqlen++] = (uint8_t)afi; - - AddCrc15(req, reqlen); - reqlen += 2; - - // arg: len, speed, recv? - // arg0 (datalen, cmd len? .arg0 == crc?) - // arg1 (speed == 0 == 1 of 256, == 1 == 1 of 4 ) - // arg2 (recv == 1 == expect a response) - uint8_t read_respone = 1; - PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_respone, req, reqlen); - - if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { - PrintAndLogEx(ERR, "iso15693 timeout"); + SendCommandNG(CMD_HF_ISO15693_WRITE_AFI, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_WRITE_AFI, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); DropField(); - return PM3_ETIMEOUT; - } - DropField(); - - if (resp.status == PM3_ETEAROFF) { - return resp.status; + return PM3_ESOFT; } - uint8_t *data = resp.data.asBytes; - - if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); - return PM3_EWRONGANSWER; + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "error writing AFI"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "Wrote AFI 0x%02X", payload.afi); + break; + } } - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Wrote AFI 0x%02X", afi); - return PM3_SUCCESS; + return resp.status; } // Writes the DSFID (Data Storage Format Identifier) of a card @@ -1377,7 +1630,7 @@ static int CmdHF15Dump(const char *Cmd) { if (CheckCrc15(recv, resp.length) == false) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); + PrintAndLogEx(FAILED, "crc ( " _RED_("fail") " )"); continue; } @@ -1520,11 +1773,13 @@ static int CmdHF15Readmulti(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[8]; + uint8_t uid[HF15_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool unaddressed = arg_get_lit(ctx, 2); - bool scan = arg_get_lit(ctx, 3); + bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; //Default fallback to scan for tag. Overriding unaddressed parameter. int fast = (arg_get_lit(ctx, 4) == false); bool add_option = arg_get_lit(ctx, 5); @@ -1539,7 +1794,7 @@ static int CmdHF15Readmulti(const char *Cmd) { return PM3_EINVARG; } - if ((scan + unaddressed + uidlen) > 1) { + if ((scan + unaddressed + uid_set) > 1) { PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); return PM3_EINVARG; } @@ -1555,14 +1810,13 @@ static int CmdHF15Readmulti(const char *Cmd) { PrintAndLogEx(WARNING, "no tag found"); return PM3_EINVARG; } - uidlen = 8; + } else { + reverse_array(uid, HF15_UID_LENGTH); } + // add UID (scan, uid) + memcpy(req + reqlen, uid, HF15_UID_LENGTH); + reqlen += HF15_UID_LENGTH; - if (uidlen == 8) { - // add UID (scan, uid) - memcpy(req + reqlen, uid, sizeof(uid)); - reqlen += sizeof(uid); - } PrintAndLogEx(SUCCESS, "Using UID... " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); } // add OPTION flag, in order to get lock-info @@ -1603,7 +1857,7 @@ static int CmdHF15Readmulti(const char *Cmd) { uint8_t *data = resp.data.asBytes; if (CheckCrc15(data, resp.length) == false) { - PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); + PrintAndLogEx(FAILED, "crc ( " _RED_("fail") " )"); return PM3_ESOFT; } @@ -1654,11 +1908,13 @@ static int CmdHF15Readblock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[8]; + uint8_t uid[HF15_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool unaddressed = arg_get_lit(ctx, 2); - bool scan = arg_get_lit(ctx, 3); + bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; //Default fallback to scan for tag. Overriding unaddressed parameter. int fast = (arg_get_lit(ctx, 4) == false); bool add_option = arg_get_lit(ctx, 5); @@ -1666,36 +1922,31 @@ static int CmdHF15Readblock(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if ((scan + unaddressed + uidlen) > 1) { + if ((scan + unaddressed + uid_set) > 1) { PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); return PM3_EINVARG; } - // default fallback to scan for tag. - // overriding unaddress parameter :) - if (uidlen != 8) { - scan = true; - } - // request to be sent to device/card uint16_t flags = arg_get_raw_flag(uidlen, unaddressed, scan, add_option); uint8_t req[PM3_CMD_DATA_SIZE] = {flags, ISO15693_READBLOCK}; uint16_t reqlen = 2; if (unaddressed == false) { + // default fallback to scan for tag. + // overriding unaddress parameter :) if (scan) { if (getUID(false, uid) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "no tag found"); return PM3_EINVARG; } - uidlen = 8; + } else { + reverse_array(uid, HF15_UID_LENGTH); } + // add UID (scan, uid) + memcpy(req + reqlen, uid, HF15_UID_LENGTH); + reqlen += HF15_UID_LENGTH; - if (uidlen == 8) { - // add UID (scan, uid) - memcpy(req + reqlen, uid, sizeof(uid)); - reqlen += sizeof(uid); - } PrintAndLogEx(SUCCESS, "Using UID... " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); } // add OPTION flag, in order to get lock-info @@ -1734,7 +1985,7 @@ static int CmdHF15Readblock(const char *Cmd) { uint8_t *data = resp.data.asBytes; if (CheckCrc15(data, resp.length) == false) { - PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); + PrintAndLogEx(FAILED, "crc ( " _RED_("fail") " )"); return PM3_ESOFT; } @@ -1785,7 +2036,7 @@ static int hf_15_write_blk(bool verbose, bool fast, uint8_t *req, uint8_t reqlen uint8_t *recv = resp.data.asBytes; if (CheckCrc15(recv, resp.length) == false) { if (verbose) { - PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); + PrintAndLogEx(FAILED, "crc ( " _RED_("fail") " )"); } return PM3_ESOFT; } @@ -1819,11 +2070,13 @@ static int CmdHF15Write(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[8]; + uint8_t uid[HF15_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool unaddressed = arg_get_lit(ctx, 2); - bool scan = arg_get_lit(ctx, 3); + bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; //Default fallback to scan for tag. Overriding unaddressed parameter. int fast = (arg_get_lit(ctx, 4) == false); bool add_option = arg_get_lit(ctx, 5); @@ -1836,7 +2089,7 @@ static int CmdHF15Write(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if ((scan + unaddressed + uidlen) > 1) { + if ((scan + unaddressed + uid_set) > 1) { PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); return PM3_EINVARG; } @@ -1848,9 +2101,6 @@ static int CmdHF15Write(const char *Cmd) { // default fallback to scan for tag. // overriding unaddress parameter :) - if (uidlen != 8) { - scan = true; - } // request to be sent to device/card uint16_t flags = arg_get_raw_flag(uidlen, unaddressed, scan, add_option); @@ -1866,18 +2116,16 @@ static int CmdHF15Write(const char *Cmd) { PrintAndLogEx(WARNING, "no tag found"); return PM3_EINVARG; } - uidlen = 8; + } else { + reverse_array(uid, HF15_UID_LENGTH); } + // add UID (scan, uid) + memcpy(req + reqlen, uid, HF15_UID_LENGTH); + reqlen += HF15_UID_LENGTH; - if (uidlen == 8) { - // add UID (scan, uid) - memcpy(req + reqlen, uid, sizeof(uid)); - reqlen += sizeof(uid); - } PrintAndLogEx(SUCCESS, "Using UID... " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); } - req[reqlen++] = (uint8_t)block; memcpy(req + reqlen, d, sizeof(d)); reqlen += sizeof(d); @@ -2096,25 +2344,159 @@ static int CmdHF15CSetUID(const char *Cmd) { // reverse cardUID to compare uint8_t revuid[8] = {0}; - uint8_t i = 0; - while (i < sizeof(revuid)) { - revuid[i] = carduid[7 - i]; - i++; - } + reverse_array_copy(carduid, sizeof(carduid), revuid); if (memcmp(revuid, payload.uid, 8) != 0) { - PrintAndLogEx(FAILED, "setting new UID (" _RED_("failed") ")"); + PrintAndLogEx(FAILED, "setting new UID ( " _RED_("fail") " )"); return PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "setting new UID (" _GREEN_("ok") ")"); + PrintAndLogEx(SUCCESS, "setting new UID ( " _GREEN_("ok") " )"); return PM3_SUCCESS; } } +static int CmdHF15SlixEASEnable(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 slixeasenable", + "Enable EAS mode on SLIX ISO-15693 tag", + "hf 15 slixeasenable -p 0F0F0F0F"); + + void *argtable[] = { + arg_param_begin, + arg_str0("p", "pwd", "", "optional password, 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + struct { + uint8_t pwd[4]; + bool usepwd; + } PACKED payload; + int pwdlen = 0; + + int ret_pwdparse = CLIParamHexToBuf(arg_get_str(ctx, 1), payload.pwd, 4, &pwdlen); + if ((pwdlen > 0 && pwdlen != 4) || ret_pwdparse != 0) { + PrintAndLogEx(WARNING, "password must be 4 hex bytes if provided"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIParserFree(ctx); + + if (pwdlen > 0) { + PrintAndLogEx(INFO, "Trying to enable EAS mode using password " _GREEN_("%s") + , sprint_hex_inrow(payload.pwd, sizeof(payload.pwd)) + ); + payload.usepwd = true; + } else { + PrintAndLogEx(INFO, "Trying to enable EAS mode without using a password"); + payload.usepwd = false; + } + + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_ENABLE_EAS, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_ENABLE_EAS, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + if (pwdlen > 0) { + PrintAndLogEx(WARNING, "the password provided was not accepted"); + } else { + PrintAndLogEx(WARNING, "either a password is required or EAS mode is locked"); + } + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "EAS mode is now enabled ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + +static int CmdHF15SlixEASDisable(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 slixeasdisable", + "Disable EAS mode on SLIX ISO-15693 tag", + "hf 15 slixeasdisable -p 0F0F0F0F"); + + void *argtable[] = { + arg_param_begin, + arg_str0("p", "pwd", "", "optional password, 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + struct { + uint8_t pwd[4]; + bool usepwd; + + } PACKED payload; + int pwdlen = 0; + + int ret_pwdparse = CLIParamHexToBuf(arg_get_str(ctx, 1), payload.pwd, 4, &pwdlen); + CLIParserFree(ctx); + + if ((pwdlen > 0 && pwdlen != 4) || ret_pwdparse != 0) { + PrintAndLogEx(WARNING, "password must be 4 hex bytes if provided"); + return PM3_ESOFT; + } + + if (pwdlen > 0) { + PrintAndLogEx(INFO, "Trying to disable EAS mode using password " _GREEN_("%s") + , sprint_hex_inrow(payload.pwd, sizeof(payload.pwd)) + ); + payload.usepwd = true; + } else { + PrintAndLogEx(INFO, "Trying to enable EAS mode without using a password"); + payload.usepwd = false; + } + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_DISABLE_EAS, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_DISABLE_EAS, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + if (pwdlen > 0) { + PrintAndLogEx(WARNING, "the password provided was not accepted"); + } else { + PrintAndLogEx(WARNING, "either a password is required or EAS mode is locked"); + } + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "EAS mode is now disabled ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + static int CmdHF15SlixDisable(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf 15 slixdisable", + CLIParserInit(&ctx, "hf 15 slixprivacydisable", "Disable privacy mode on SLIX ISO-15693 tag", "hf 15 slixdisable -p 0F0F0F0F"); @@ -2137,8 +2519,8 @@ static int CmdHF15SlixDisable(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); - SendCommandNG(CMD_HF_ISO15693_SLIX_L_DISABLE_PRIVACY, (uint8_t *)&payload, sizeof(payload)); - if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_L_DISABLE_PRIVACY, &resp, 2000) == false) { + SendCommandNG(CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); DropField(); return PM3_ESOFT; @@ -2161,29 +2543,315 @@ static int CmdHF15SlixDisable(const char *Cmd) { return resp.status; } +static int CmdHF15SlixEnable(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 slixprivacyenable", + "Enable privacy mode on SLIX ISO-15693 tag", + "hf 15 slixenable -p 0F0F0F0F"); + + void *argtable[] = { + arg_param_begin, + arg_str1("p", "pwd", "", "password, 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + struct { + uint8_t pwd[4]; + } PACKED payload; + int pwdlen = 0; + CLIGetHexWithReturn(ctx, 1, payload.pwd, &pwdlen); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Trying to enable privacy mode using password " _GREEN_("%s") + , sprint_hex_inrow(payload.pwd, sizeof(payload.pwd)) + ); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_ENABLE_PRIVACY, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_ENABLE_PRIVACY, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "password was not accepted"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "privacy mode is now enabled ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + +static int CmdHF15SlixWritePassword(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 slixwritepwd", + "Write a password on a SLIX family ISO-15693 tag.n" + "Some tags do not support all different password types.", + "hf 15 slixwritepwd -t READ -o 00000000 -n 12131415"); + + void *argtable[] = { + arg_param_begin, + arg_str1("t", "type", "", "which password field to write to"), + arg_str0("o", "old", "", "old password (if present), 8 hex bytes"), + arg_str1("n", "new", "", "new password, 8 hex bytes"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + struct { + uint8_t old_pwd[4]; + uint8_t new_pwd[4]; + uint8_t pwd_id; + } PACKED payload; + int pwdlen = 0; + + CLIGetHexWithReturn(ctx, 2, payload.old_pwd, &pwdlen); + + if (pwdlen > 0 && pwdlen != 4) { + PrintAndLogEx(WARNING, "old password must be 4 hex bytes if provided"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIGetHexWithReturn(ctx, 3, payload.new_pwd, &pwdlen); + + if (pwdlen != 4) { + PrintAndLogEx(WARNING, "new password must be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + int vlen = 0; + char value[10]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)value, sizeof(value), &vlen); + CLIParserFree(ctx); + + if (vlen > 0) { + if (strcmp(value, "read") == 0) { + PrintAndLogEx(SUCCESS, "Selected read pass"); + payload.pwd_id = 0x01; + } else if (strcmp(value, "write") == 0) { + PrintAndLogEx(SUCCESS, "Selected write pass"); + payload.pwd_id = 0x02; + } else if (strcmp(value, "privacy") == 0) { + PrintAndLogEx(SUCCESS, "Selected privacy pass"); + payload.pwd_id = 0x04; + } else if (strcmp(value, "destroy") == 0) { + PrintAndLogEx(SUCCESS, "Selected destroy pass"); + payload.pwd_id = 0x08; + } else if (strcmp(value, "easafi") == 0) { + PrintAndLogEx(SUCCESS, "Selected easafi pass"); + payload.pwd_id = 0x10; + } else { + PrintAndLogEx(ERR, "t argument must be 'read', 'write', 'privacy', 'destroy', or 'easafi'"); + return PM3_EINVARG; + } + } + + PrintAndLogEx(INFO, "Trying to write " _YELLOW_("%s") " as " _YELLOW_("%s") " password" + , sprint_hex_inrow(payload.new_pwd, sizeof(payload.new_pwd)), value); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_WRITE_PWD, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_WRITE_PWD, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "password was not accepted"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "password written ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + +static int CmdHF15AFIPassProtect(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 passprotectafi", + "This command enables the password protect of AFI.\n" + "*** OBS! This action can not be undone! ***", + "hf 15 passprotectafi -p 00000000 --force"); + + void *argtable[] = { + arg_param_begin, + arg_str1("p", "pwd", "", "EAS/AFI password, 8 hex bytes"), + arg_lit0(NULL, "force", "Force execution of command (irreversible) "), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + struct { + uint8_t pwd[4]; + } PACKED payload; + int pwdlen = 0; + + CLIGetHexWithReturn(ctx, 1, payload.pwd, &pwdlen); + + bool force = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (pwdlen != 4) { + PrintAndLogEx(WARNING, "password must be 4 hex bytes"); + return PM3_ESOFT; + } + + if (force == false) { + PrintAndLogEx(WARNING, "Use `--force` flag to override. OBS! Irreversable command"); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Trying to enable AFI password protection..."); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "error enabling AFI password protection"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "AFI password protected ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; + +} + +static int CmdHF15EASPassProtect(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 passprotecteas", + "This command enables the password protect of EAS.\n" + "*** OBS! This action can not be undone! ***", + "hf 15 passprotecteas -p 00000000 --force"); + + void *argtable[] = { + arg_param_begin, + arg_str1("p", "pwd", "", "EAS/AFI password, 8 hex bytes"), + arg_lit0(NULL, "force", "Force execution of command (irreversible) "), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + struct { + uint8_t pwd[4]; + } PACKED payload; + int pwdlen = 0; + + CLIGetHexWithReturn(ctx, 1, payload.pwd, &pwdlen); + + bool force = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (pwdlen != 4) { + PrintAndLogEx(WARNING, "password must be 4 hex bytes"); + return PM3_ESOFT; + } + + if (force == false) { + PrintAndLogEx(WARNING, "Use `--force` flag to override. OBS! Irreversable command"); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Trying to enable EAS password protection..."); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + DropField(); + return PM3_ESOFT; + } + + switch (resp.status) { + case PM3_ETIMEOUT: { + PrintAndLogEx(WARNING, "no tag found"); + break; + } + case PM3_EWRONGANSWER: { + PrintAndLogEx(WARNING, "error enabling EAS password protection"); + break; + } + case PM3_SUCCESS: { + PrintAndLogEx(SUCCESS, "EAS password protected ( " _GREEN_("ok") " ) "); + break; + } + } + return resp.status; +} + static command_t CommandTable[] = { - {"-----------", CmdHF15Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, - {"help", CmdHF15Help, AlwaysAvailable, "This help"}, - {"list", CmdHF15List, AlwaysAvailable, "List ISO-15693 history"}, - {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO-15693 from tag"}, - {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO-15693 tag, save to file"}, - {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, - {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO-15693 traffic"}, - {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, - {"rdbl", CmdHF15Readblock, IfPm3Iso15693, "Read a block"}, - {"rdmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple blocks"}, - {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO-15693 reader"}, - {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO-15693 tag"}, - {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire samples as reader (enables carrier, sends inquiry)"}, - {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO-15693 tag"}, - {"slixdisable", CmdHF15SlixDisable, IfPm3Iso15693, "Disable privacy mode on SLIX ISO-15693 tag"}, - {"wrbl", CmdHF15Write, IfPm3Iso15693, "Write a block"}, - {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("afi") " -----------------------"}, - {"findafi", CmdHF15FindAfi, IfPm3Iso15693, "Brute force AFI of an ISO-15693 tag"}, - {"writeafi", CmdHF15WriteAfi, IfPm3Iso15693, "Writes the AFI on an ISO-15693 tag"}, - {"writedsfid", CmdHF15WriteDsfid, IfPm3Iso15693, "Writes the DSFID on an ISO-15693 tag"}, - {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("magic") " -----------------------"}, - {"csetuid", CmdHF15CSetUID, IfPm3Iso15693, "Set UID for magic card"}, + {"-----------", CmdHF15Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, + {"help", CmdHF15Help, AlwaysAvailable, "This help"}, + {"list", CmdHF15List, AlwaysAvailable, "List ISO-15693 history"}, + {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO-15693 from tag"}, + {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO-15693 tag, save to file"}, + {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, + {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO-15693 traffic"}, + {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, + {"rdbl", CmdHF15Readblock, IfPm3Iso15693, "Read a block"}, + {"rdmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple blocks"}, + {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO-15693 reader"}, + {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO-15693 tag"}, + {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire samples as reader (enables carrier, sends inquiry)"}, + {"eload", CmdHF15ELoad, IfPm3Iso15693, "Load image file into emulator to be used by 'sim' command"}, + {"esave", CmdHF15ESave, IfPm3Iso15693, "Save emulator memory into image file"}, + {"eview", CmdHF15EView, IfPm3Iso15693, "View emulator memory"}, + {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO-15693 tag"}, + {"slixwritepwd", CmdHF15SlixWritePassword, IfPm3Iso15693, "Writes a password on a SLIX ISO-15693 tag"}, + {"slixeasdisable", CmdHF15SlixEASDisable, IfPm3Iso15693, "Disable EAS mode on SLIX ISO-15693 tag"}, + {"slixeasenable", CmdHF15SlixEASEnable, IfPm3Iso15693, "Enable EAS mode on SLIX ISO-15693 tag"}, + {"slixprivacydisable", CmdHF15SlixDisable, IfPm3Iso15693, "Disable privacy mode on SLIX ISO-15693 tag"}, + {"slixprivacyenable", CmdHF15SlixEnable, IfPm3Iso15693, "Enable privacy mode on SLIX ISO-15693 tag"}, + {"passprotectafi", CmdHF15AFIPassProtect, IfPm3Iso15693, "Password protect AFI - Cannot be undone"}, + {"passprotecteas", CmdHF15EASPassProtect, IfPm3Iso15693, "Password protect EAS - Cannot be undone"}, + {"wrbl", CmdHF15Write, IfPm3Iso15693, "Write a block"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("afi") " -----------------------"}, + {"findafi", CmdHF15FindAfi, IfPm3Iso15693, "Brute force AFI of an ISO-15693 tag"}, + {"writeafi", CmdHF15WriteAfi, IfPm3Iso15693, "Writes the AFI on an ISO-15693 tag"}, + {"writedsfid", CmdHF15WriteDsfid, IfPm3Iso15693, "Writes the DSFID on an ISO-15693 tag"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("magic") " -----------------------"}, + {"csetuid", CmdHF15CSetUID, IfPm3Iso15693, "Set UID for magic card"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 83dc300db..f6714c5a2 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -40,6 +40,7 @@ #include "util.h" #include "fileutils.h" // laodFileJSONroot #include "crypto/libpcrypto.h" +#include "protocols.h" // ISO7816 APDU return codes const uint8_t PxSE_AID[] = {0xA0, 0x00, 0x00, 0x05, 0x07, 0x01, 0x00}; #define PxSE_AID_LENGTH 7 @@ -104,11 +105,11 @@ static int SelectAndPrintInfoFile(void) { uint16_t sw = 0; int res = CIPURSESelectFile(0x2ff7, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) return PM3_EAPDU_FAIL; res = CIPURSEReadBinary(0, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) return PM3_EAPDU_FAIL; if (len > 0) { @@ -151,7 +152,7 @@ static int CmdHFCipurseInfo(const char *Cmd) { bool mfExist = false; bool infoPrinted = false; int res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); - if (res == PM3_SUCCESS && sw == 0x9000) { + if (res == PM3_SUCCESS && sw == ISO7816_OK) { mfExist = true; PrintAndLogEx(INFO, _YELLOW_("MasterFile") " exist and can be selected."); @@ -161,7 +162,7 @@ static int CmdHFCipurseInfo(const char *Cmd) { for (int i = 0; i < ARRAYLEN(PxSE_AID_LIST); i++) { res = CIPURSESelectAID(false, true, (uint8_t *)PxSE_AID_LIST[i].aid, PxSE_AID_LENGTH, buf, sizeof(buf), &len, &sw); - if (res == PM3_SUCCESS && sw == 0x9000) { + if (res == PM3_SUCCESS && sw == ISO7816_OK) { mfExist = true; PrintAndLogEx(INFO, _CYAN_("PxSE") " exist: %s", PxSE_AID_LIST[i].name); if (len > 0) { @@ -179,7 +180,7 @@ static int CmdHFCipurseInfo(const char *Cmd) { } PrintAndLogEx(INFO, "Application `" _YELLOW_("AF F1") "` selected " _GREEN_("successfully")); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { if (sw == 0x0000) { PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000"); } else { @@ -362,7 +363,7 @@ static int SelectCommandEx(bool selectDefaultFile, bool useAID, uint8_t *aid, si if (useAID && aidLen > 0) { res = CIPURSESelectAID(true, true, aid, aidLen, buf, bufSize, len, sw); - if (res != 0 || *sw != 0x9000) { + if (res != 0 || *sw != ISO7816_OK) { if (verbose) { PrintAndLogEx(ERR, "Cipurse select application " _GREEN_("%s ") _RED_("error") ". Card returns 0x%04x", sprint_hex_inrow(aid, aidLen), *sw); } @@ -375,7 +376,7 @@ static int SelectCommandEx(bool selectDefaultFile, bool useAID, uint8_t *aid, si } else if (useFID) { res = CIPURSESelectFileEx(true, true, fileId, buf, bufSize, len, sw); - if (res != 0 || *sw != 0x9000) { + if (res != 0 || *sw != ISO7816_OK) { if (verbose) { PrintAndLogEx(ERR, "Cipurse select file 0x%04x ( %s )", fileId, _RED_("fail")); PrintAndLogEx(ERR, "Card returns 0x%04x", *sw); @@ -389,7 +390,7 @@ static int SelectCommandEx(bool selectDefaultFile, bool useAID, uint8_t *aid, si } else if (selectDefaultFile) { res = CIPURSESelectMFDefaultFileEx(true, true, buf, bufSize, len, sw); - if (res != 0 || *sw != 0x9000) { + if (res != 0 || *sw != ISO7816_OK) { if (verbose) { PrintAndLogEx(ERR, "Cipurse select default file " _RED_("error") ". Card returns 0x%04x", *sw); } @@ -402,7 +403,7 @@ static int SelectCommandEx(bool selectDefaultFile, bool useAID, uint8_t *aid, si } else { res = CIPURSESelect(true, true, buf, bufSize, len, sw); - if (res != 0 || *sw != 0x9000) { + if (res != 0 || *sw != ISO7816_OK) { if (verbose) { PrintAndLogEx(ERR, "Cipurse select default application " _RED_("error") ". Card returns 0x%04x", *sw); } @@ -419,7 +420,7 @@ static int SelectCommandEx(bool selectDefaultFile, bool useAID, uint8_t *aid, si } res = CIPURSESelectFileEx(false, true, childFileId, buf, bufSize, len, sw); - if (res != 0 || *sw != 0x9000) { + if (res != 0 || *sw != ISO7816_OK) { if (verbose) { PrintAndLogEx(ERR, "Select child file 0x%04x " _RED_("error") ". Card returns 0x%04x", childFileId, *sw); } @@ -487,7 +488,7 @@ static int CmdHFCipurseSelect(const char *Cmd) { size_t len = 0; uint16_t sw = 0; res = SelectCommandEx(selmfd, useAID, aid, aidLen, useFID, fileId, useChildFID, childFileId, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { DropField(); return PM3_ESOFT; } @@ -553,7 +554,7 @@ static int CmdHFCipurseAuth(const char *Cmd) { uint8_t buf[APDU_RES_LEN] = {0}; res = SelectCommand(selmfd, useAID, aid, aidLen, useFID, fileId, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { DropField(); return PM3_ESOFT; } @@ -635,7 +636,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) { uint8_t buf[APDU_RES_LEN] = {0}; res = CIPURSESelectAID(true, true, aid, aidLen, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse select application " _CYAN_("%s") " ( " _RED_("error") " ). Card returns 0x%04x", sprint_hex_inrow(aid, aidLen), sw); DropField(); return PM3_ESOFT; @@ -660,7 +661,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) { } res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { if (verbose == false) PrintAndLogEx(ERR, "File select ( " _RED_("error") " ). Card returns 0x%04x", sw); DropField(); @@ -671,7 +672,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) { PrintAndLogEx(INFO, "Select file 0x%x ( %s )", fileId, _GREEN_("ok")); res = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { if (verbose == false) PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); @@ -758,7 +759,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { uint8_t buf[APDU_RES_LEN] = {0}; res = CIPURSESelectAID(true, true, aid, aidLen, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse select application " _CYAN_("%s") " ( " _RED_("error") " ). Card returns 0x%04x", sprint_hex_inrow(aid, aidLen), sw); DropField(); return PM3_ESOFT; @@ -789,7 +790,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { } res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { if (verbose == false) PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); @@ -800,7 +801,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { PrintAndLogEx(INFO, "Select file 0x%x ( %s )", fileId, _GREEN_("ok")); res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { if (verbose == false) PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); @@ -812,7 +813,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { if (needCommit) { sw = 0; res = CIPURSECommitTransaction(&sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) PrintAndLogEx(WARNING, "Commit ( " _YELLOW_("fail") " ) Card returns 0x%04x", sw); if (verbose) @@ -883,7 +884,7 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { uint16_t sw = 0; res = SelectCommandEx(selmfd, useAID, aid, aidLen, useFID, fileId, useChildFID, childFileId, verbose, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); DropField(); return PM3_ESOFT; @@ -921,7 +922,7 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { } res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { if (verbose == false) PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); @@ -1020,7 +1021,7 @@ static int CmdHFCipurseWriteFileAttr(const char *Cmd) { uint16_t sw = 0; res = SelectCommandEx(selmfd, useAID, aid, aidLen, useFID, fileId, useChildFID, childFileId, verbose, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); DropField(); return PM3_ESOFT; @@ -1058,7 +1059,7 @@ static int CmdHFCipurseWriteFileAttr(const char *Cmd) { } res = CIPURSEUpdateFileAttributes(hdata, hdatalen, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { if (verbose == false) PrintAndLogEx(ERR, "File attributes update " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); @@ -1070,7 +1071,7 @@ static int CmdHFCipurseWriteFileAttr(const char *Cmd) { if (needCommit) { sw = 0; res = CIPURSECommitTransaction(&sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) PrintAndLogEx(WARNING, "Commit ( " _YELLOW_("fail") " ) Card returns 0x%04x", sw); if (verbose) @@ -1125,7 +1126,7 @@ static int CmdHFCipurseFormatAll(const char *Cmd) { uint16_t sw = 0; res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; @@ -1154,7 +1155,7 @@ static int CmdHFCipurseFormatAll(const char *Cmd) { } res = CIPURSEFormatAll(&sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Format " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; @@ -1239,14 +1240,14 @@ static int CmdHFCipurseCreateDGI(const char *Cmd) { if (useAID || useFID || selmfd) { res = SelectCommand(selmfd, useAID, aid, aidLen, useFID, fileId, verbose, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); DropField(); return PM3_ESOFT; } } else { res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; @@ -1277,7 +1278,7 @@ static int CmdHFCipurseCreateDGI(const char *Cmd) { } res = CIPURSECreateFile(hdata, hdatalen, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Create file command " _RED_("ERROR")); PrintAndLogEx(ERR, "0x%04x - %s", sw, GetSpecificAPDUCodeDesc(SelectAPDUCodeDescriptions, ARRAYLEN(SelectAPDUCodeDescriptions), sw)); @@ -1289,7 +1290,7 @@ static int CmdHFCipurseCreateDGI(const char *Cmd) { if (needCommit) { sw = 0; res = CIPURSECommitTransaction(&sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) PrintAndLogEx(WARNING, "Commit ( " _YELLOW_("fail") " ) Card returns 0x%04x", sw); if (verbose) @@ -1376,14 +1377,14 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { if (useChildFID) { res = SelectCommand(false, useAID, aid, aidLen, useFID, fileId, verbose, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Top level select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } } else { res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; @@ -1405,7 +1406,7 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { if (useChildFID) { res = CIPURSEDeleteFile(childFileId, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Delete child file " _CYAN_("%04x ") " %s", childFileId, _RED_("ERROR")); PrintAndLogEx(ERR, "0x%04x - %s", sw, @@ -1417,7 +1418,7 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { PrintAndLogEx(INFO, "Child file id " _CYAN_("%04x") " deleted " _GREEN_("succesfully"), childFileId); } else if (useFID) { res = CIPURSEDeleteFile(fileId, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Delete file " _CYAN_("%04x ") " %s", fileId, _RED_("ERROR")); PrintAndLogEx(ERR, "0x%04x - %s", sw, @@ -1429,7 +1430,7 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { PrintAndLogEx(INFO, "File id " _CYAN_("%04x") " deleted " _GREEN_("succesfully"), fileId); } else { res = CIPURSEDeleteFileAID(aid, aidLen, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Delete application " _CYAN_("%s ") " %s", sprint_hex_inrow(aid, aidLen), _RED_("ERROR")); PrintAndLogEx(ERR, "0x%04x - %s", sw, @@ -1444,7 +1445,7 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { if (needCommit) { sw = 0; res = CIPURSECommitTransaction(&sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) PrintAndLogEx(WARNING, "Commit ( " _YELLOW_("fail") " ) Card returns 0x%04x", sw); if (verbose) @@ -1588,14 +1589,14 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { if (useAID || useFID || selmfd) { res = SelectCommand(selmfd, useAID, aid, aidLen, useFID, fileId, verbose, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); DropField(); return PM3_ESOFT; } } else { res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; @@ -1626,7 +1627,7 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { } res = CIPURSEUpdateKey(encKeyId, newKeyId, keydata, sizeof(keydata), buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Update key command " _RED_("ERROR")); PrintAndLogEx(ERR, "0x%04x - %s", sw, GetSpecificAPDUCodeDesc(UAPDpdateKeyCodeDescriptions, ARRAYLEN(UAPDpdateKeyCodeDescriptions), sw)); @@ -1638,7 +1639,7 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { if (needCommit) { sw = 0; res = CIPURSECommitTransaction(&sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) PrintAndLogEx(WARNING, "Commit ( " _YELLOW_("fail") " ) Card returns 0x%04x", sw); if (verbose) @@ -1736,14 +1737,14 @@ static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { if (useAID || useFID || selmfd) { res = SelectCommand(selmfd, useAID, aid, aidLen, useFID, fileId, verbose, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); DropField(); return PM3_ESOFT; } } else { res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; @@ -1774,7 +1775,7 @@ static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { } res = CIPURSEUpdateKeyAttrib(trgKeyId, hdata[0], buf, sizeof(buf), &len, &sw); - if (res != 0 || sw != 0x9000) { + if (res != 0 || sw != ISO7816_OK) { PrintAndLogEx(ERR, "Update key attributes command " _RED_("ERROR")); PrintAndLogEx(ERR, "0x%04x - %s", sw, GetSpecificAPDUCodeDesc(UAPDpdateKeyAttrCodeDescriptions, ARRAYLEN(UAPDpdateKeyAttrCodeDescriptions), sw)); @@ -1786,7 +1787,7 @@ static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { if (needCommit) { sw = 0; res = CIPURSECommitTransaction(&sw); - if (res != 0 || sw != 0x9000) + if (res != 0 || sw != ISO7816_OK) PrintAndLogEx(WARNING, "Commit ( " _YELLOW_("fail") " ) Card returns 0x%04x", sw); if (verbose) @@ -1803,7 +1804,7 @@ bool CheckCardCipurse(void) { uint16_t sw = 0; int res = CIPURSESelect(true, false, buf, sizeof(buf), &len, &sw); - return (res == 0 && sw == 0x9000); + return (res == 0 && sw == ISO7816_OK); } static int CmdHFCipurseTest(const char *Cmd) { diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 44c116b16..c8edd2054 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -194,7 +194,7 @@ static bool emrtd_exchange_commands(sAPDU_t apdu, bool include_le, uint16_t le, return false; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(DEBUG, "Command failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return false; } @@ -716,7 +716,8 @@ static const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; static 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; + size_t offset; + int datalen = 0; // This is a hacky impl that just looks for the image header. I'll improve it eventually. // based on mrpkey.py @@ -738,6 +739,7 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const c 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); @@ -1343,7 +1345,7 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { } else if (mrz[0] == 'P') { PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport")); } else if (mrz[0] == 'A') { - PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("German Residency Permit")); + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Residency Permit")); } else { PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Unknown")); } @@ -1431,42 +1433,7 @@ static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) { return PM3_ESOFT; } - bool is_jpg = (data[offset] == 0xFF); - - size_t fn_len = strlen(dg_table[EF_DG2].filename) + 4 + 1; - char *fn = calloc(fn_len, sizeof(uint8_t)); - if (fn == NULL) - return PM3_EMALLOC; - - snprintf(fn, fn_len * sizeof(uint8_t), "%s.%s", dg_table[EF_DG2].filename, (is_jpg) ? "jpg" : "jp2"); - - PrintAndLogEx(DEBUG, "image filename `" _YELLOW_("%s") "`", fn); - - char *path; - if (searchHomeFilePath(&path, NULL, fn, false) != PM3_SUCCESS) { - free(fn); - return PM3_EFILE; - } - free(fn); - - // remove old file - if (fileExists(path)) { - PrintAndLogEx(DEBUG, "Delete old temp file `" _YELLOW_("%s") "`", path); - remove(path); - } - - // temp file. - PrintAndLogEx(DEBUG, "Save temp file `" _YELLOW_("%s") "`", path); - saveFile(path, "", data + offset, datalen); - - PrintAndLogEx(DEBUG, "view temp file `" _YELLOW_("%s") "`", path); - ShowPictureWindow(path); - msleep(500); - - // delete temp file - PrintAndLogEx(DEBUG, "Deleting temp file `" _YELLOW_("%s") "`", path); - remove(path); - //free(path); + ShowPictureWindow(data + offset, datalen); return PM3_SUCCESS; } @@ -1491,42 +1458,7 @@ static int emrtd_print_ef_dg5_info(uint8_t *data, size_t datalen) { return PM3_ESOFT; } - bool is_jpg = (data[offset] == 0xFF); - - size_t fn_len = strlen(dg_table[EF_DG5].filename) + 4 + 1; - char *fn = calloc(fn_len, sizeof(uint8_t)); - if (fn == NULL) - return PM3_EMALLOC; - - snprintf(fn, fn_len * sizeof(uint8_t), "%s.%s", dg_table[EF_DG5].filename, (is_jpg) ? "jpg" : "jp2"); - - PrintAndLogEx(DEBUG, "image filename `" _YELLOW_("%s") "`", fn); - - char *path; - if (searchHomeFilePath(&path, NULL, fn, false) != PM3_SUCCESS) { - free(fn); - return PM3_EFILE; - } - free(fn); - - // remove old file - if (fileExists(path)) { - PrintAndLogEx(DEBUG, "Delete old temp file `" _YELLOW_("%s") "`", path); - remove(path); - } - - // temp file. - PrintAndLogEx(DEBUG, "Save temp file `" _YELLOW_("%s") "`", path); - saveFile(path, "", data + offset, datalen); - - PrintAndLogEx(DEBUG, "view temp file `" _YELLOW_("%s") "`", path); - ShowPictureWindow(path); - msleep(500); - - // delete temp file - PrintAndLogEx(DEBUG, "Deleting temp file `" _YELLOW_("%s") "`", path); - remove(path); - //free(path); + ShowPictureWindow(data + offset, datalen); return PM3_SUCCESS; } @@ -1913,7 +1845,7 @@ static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } -int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, bool only_fast) { uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t resplen = 0; uint8_t ssc[8] = { 0x00 }; @@ -1999,7 +1931,7 @@ 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 && !dg->eac) { + if (((dg->fastdump && only_fast) || !only_fast) && !dg->pace && !dg->eac) { if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC)) { if (dg->parser != NULL) dg->parser(response, resplen); @@ -2025,14 +1957,16 @@ int infoHF_EMRTD_offline(const char *path) { uint8_t *data; size_t datalen = 0; char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { return PM3_EMALLOC; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_COM].filename); - if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) { - PrintAndLogEx(ERR, "Failed to read EF_COM."); + if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) && + (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS)) { + PrintAndLogEx(ERR, "Failed to read EF_COM"); free(filepath); return PM3_ESOFT; } @@ -2064,26 +1998,33 @@ int infoHF_EMRTD_offline(const char *path) { strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_CardAccess].filename); - if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { + if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) || + (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS)) { emrtd_print_ef_cardaccess_info(data, datalen); free(data); } else { - PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE."); + PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE"); } 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."); + if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) && + (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS)) { + PrintAndLogEx(ERR, "Failed to read EF_SOD"); free(filepath); return PM3_ESOFT; } + // coverity scan CID 395630, + if (data == NULL) { + 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."); + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail"); } free(data); @@ -2098,10 +2039,12 @@ int infoHF_EMRTD_offline(const char *path) { strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg->filename); - if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { + if ((loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) || + (loadFile_safeEx(filepath, ".bin", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS)) { // we won't halt on parsing errors - if (dg->parser != NULL) + if (dg->parser != NULL) { dg->parser(data, datalen); + } PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); // Check file hash @@ -2121,13 +2064,6 @@ int infoHF_EMRTD_offline(const char *path) { return PM3_SUCCESS; } -static void text_to_upper(uint8_t *data, int datalen) { - // Loop over text to make lowercase text uppercase - for (int i = 0; i < datalen; i++) { - data[i] = toupper(data[i]); - } -} - static bool validate_date(uint8_t *data, int datalen) { // Date has to be 6 chars if (datalen != 6) { @@ -2148,7 +2084,9 @@ static int CmdHFeMRTDDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd dump", "Dump all files on an eMRTD", - "hf emrtd dump" + "hf emrtd dump\n" + "hf emrtd dump --dir ../dump\n" + "hf emrtd dump -n 123456789 -d 19890101 -e 20250401" ); void *argtable[] = { @@ -2157,7 +2095,7 @@ static int CmdHFeMRTDDump(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_str0(NULL, "dir", "", "save dump to the given dirpath"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2173,7 +2111,7 @@ static int CmdHFeMRTDDump(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { BAC = false; } else { - text_to_upper(docnum, slen); + strn_upper((char *)docnum, slen); if (slen != 9) { // Pad to 9 with < memset(docnum + slen, '<', 9 - slen); @@ -2206,7 +2144,7 @@ static int CmdHFeMRTDDump(const char *Cmd) { error = true; } else { BAC = true; - text_to_upper(mrz, slen); + strn_upper((char *)mrz, slen); memcpy(docnum, &mrz[0], 9); memcpy(dob, &mrz[13], 6); memcpy(expiry, &mrz[21], 6); @@ -2246,7 +2184,10 @@ static int CmdHFeMRTDInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd info", "Display info about an eMRTD", - "hf emrtd info" + "hf emrtd info\n" + "hf emrtd info --dir ../dumps\n" + "hf emrtd info -n 123456789 -d 19890101 -e 20250401\n" + "hf emrtd info -n 123456789 -d 19890101 -e 20250401 -i" ); void *argtable[] = { @@ -2255,7 +2196,8 @@ static int CmdHFeMRTDInfo(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 (passports only)"), - arg_str0(NULL, "path", "", "display info from offline dump stored in dirpath"), + arg_str0(NULL, "dir", "", "display info from offline dump stored in dirpath"), + arg_lit0("i", "images", "show images"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -2271,7 +2213,7 @@ static int CmdHFeMRTDInfo(const char *Cmd) { if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { BAC = false; } else { - text_to_upper(docnum, slen); + strn_upper((char *)docnum, slen); if (slen != 9) { memset(docnum + slen, '<', 9 - slen); } @@ -2303,7 +2245,7 @@ static int CmdHFeMRTDInfo(const char *Cmd) { error = true; } else { BAC = true; - text_to_upper(mrz, slen); + strn_upper((char *)mrz, slen); memcpy(docnum, &mrz[0], 9); memcpy(dob, &mrz[13], 6); memcpy(expiry, &mrz[21], 6); @@ -2322,8 +2264,9 @@ static int CmdHFeMRTDInfo(const char *Cmd) { } uint8_t path[FILENAME_MAX] = { 0x00 }; bool is_offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0; + bool show_images = arg_get_lit(ctx, 6); CLIParserFree(ctx); - if ((! IfPm3Iso14443()) && (! is_offline)) { + if ((IfPm3Iso14443() == false) && (is_offline == false)) { PrintAndLogEx(WARNING, "Only offline mode is available"); error = true; } @@ -2337,7 +2280,7 @@ static int CmdHFeMRTDInfo(const char *Cmd) { if (g_debugMode >= 2) { SetAPDULogging(true); } - int res = infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); + int res = infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, !show_images); SetAPDULogging(restore_apdu_logging); return res; } diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 1b9588983..962501331 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -63,7 +63,7 @@ typedef struct emrtd_pacesdp_s { int CmdHFeMRTD(const char *Cmd); 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(char *documentnumber, char *dob, char *expiry, bool BAC_available, bool only_fast); int infoHF_EMRTD_offline(const char *path); #ifdef __cplusplus diff --git a/client/src/cmdhfepa.c b/client/src/cmdhfepa.c index c676cc2f5..66ce37d6b 100644 --- a/client/src/cmdhfepa.c +++ b/client/src/cmdhfepa.c @@ -103,9 +103,10 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { // perform the PACE protocol by replaying APDUs static int CmdHFEPAPACEReplay(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf epa preplay", + CLIParserInit(&ctx, "hf epa replay", "Perform PACE protocol by replaying given APDUs", - "hf epa preplay --mse 0022C1A4 --get 1068000000 --map 1086000002 --pka 1234ABCDEF --ma 1A2B3C4D"); + "hf epa replay --mse 0022C1A4 --get 1068000000 --map 1086000002 --pka 1234ABCDEF --ma 1A2B3C4D" + ); void *argtable[] = { arg_param_begin, @@ -207,10 +208,72 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { return PM3_SUCCESS; } +// perform the PACE protocol by replaying APDUs +static int CmdHFEPAPACESimulate(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf epa sim", + "Simulate PACE protocol with given password pwd of type pty.\n" + "The crypto is performed on pc or proxmark", + "hf epa sim --pwd 112233445566\n" + "hf epa sim --pc --pty 1 --pwd 112233445566" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit1(NULL, "pc", "perform crypto on PC"), + arg_str1(NULL, "pty", "", "type of password"), + arg_str1("p", "pwd", "", "password"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + +// bool use_pc = arg_get_lit(ctx, 1); +// uint8_t pwd_type = 0; + + int plen = 0; + uint8_t pwd[6] = {0}; + CLIGetHexWithReturn(ctx, 3, pwd, &plen); + + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Starting PACE simulation..."); + + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_EPA_PACE_SIMULATE, 0, 0, 0, pwd, plen); + + PacketResponseNG resp; + WaitForResponse(CMD_ACK, &resp); + + uint32_t *data = resp.data.asDwords; + + if (resp.oldarg[0] != 0) { + PrintAndLogEx(INFO, "\nPACE failed in step %u!", (uint32_t)resp.oldarg[0]); + PrintAndLogEx(INFO, "MSE Set AT: %u us", data[0]); + PrintAndLogEx(INFO, "GA Get Nonce: %u us", data[1]); + PrintAndLogEx(INFO, "GA Map Nonce: %u us", data[2]); + PrintAndLogEx(INFO, "GA Perform Key Agreement: %u us", data[3]); + PrintAndLogEx(INFO, "GA Mutual Authenticate: %u us", data[4]); + PrintAndLogEx(INFO, "----------------"); + } else { + PrintAndLogEx(INFO, "PACE successful!"); + PrintAndLogEx(INFO, "MSE Set AT: %u us", data[0]); + PrintAndLogEx(INFO, "GA Get Nonce: %u us", data[1]); + PrintAndLogEx(INFO, "GA Map Nonce: %u us", data[2]); + PrintAndLogEx(INFO, "GA Perform Key Agreement: %u us", data[3]); + PrintAndLogEx(INFO, "GA Mutual Authenticate: %u us", data[4]); + PrintAndLogEx(INFO, "----------------"); + } + + return PM3_SUCCESS; +} + + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"cnonces", CmdHFEPACollectPACENonces, IfPm3Iso14443, "Acquire encrypted PACE nonces of specific size"}, - {"preplay", CmdHFEPAPACEReplay, IfPm3Iso14443, "Perform PACE protocol by replaying given APDUs"}, + {"replay", CmdHFEPAPACEReplay, IfPm3Iso14443, "Perform PACE protocol by replaying given APDUs"}, + {"sim", CmdHFEPAPACESimulate, IfPm3Iso14443, "Simulate PACE protocol"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index c809633e1..191a481de 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -591,7 +591,7 @@ static int CmdHFFelicaAuthentication1(const char *Cmd) { arg_str0(NULL, "sn", "", "number of service, 1 byte"), arg_str0(NULL, "scl", "", "service code list, 2 bytes"), arg_str0("k", "key", "", "3des key, 16 bytes"), - arg_lit0("v", "verbose", "verbose helptext"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -702,7 +702,7 @@ static int CmdHFFelicaAuthentication1(const char *Cmd) { PrintAndLogEx(INFO, "Reader challenge (unencrypted): %s", sprint_hex(nonce, 8)); // Create M1c Challenge with 3DES (3 Keys = 24, 2 Keys = 16) - uint8_t master_key[24]; + uint8_t master_key[24] = {0}; mbedtls_des3_context des3_ctx; mbedtls_des3_init(&des3_ctx); @@ -804,7 +804,7 @@ static int CmdHFFelicaAuthentication2(const char *Cmd) { arg_str0("i", NULL, "", "set custom IDm"), arg_str0("c", "cc", "", "M3c card challenge, 8 bytes"), arg_str0("k", "key", "", "3des M3c decryption key, 16 bytes"), - arg_lit0("v", "verbose", "verbose helptext"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -970,7 +970,7 @@ static int CmdHFFelicaWritePlain(const char *Cmd) { arg_str0(NULL, "scl", "", "service code list"), arg_str0(NULL, "bn", "", "number of block"), arg_str0(NULL, "ble", "", "block list element (def 2|3 bytes)"), - arg_lit0("v", "verbose", "verbose helptext"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1136,7 +1136,7 @@ static int CmdHFFelicaReadPlain(const char *Cmd) { arg_str0(NULL, "scl", "", "service code list"), arg_str0(NULL, "bn", "", "number of block"), arg_str0(NULL, "ble", "", "block list element (def 2|3 bytes)"), - arg_lit0("v", "verbose", "verbose helptext"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1364,7 +1364,7 @@ static int CmdHFFelicaRequestSpecificationVersion(const char *Cmd) { arg_param_begin, arg_str0("i", NULL, "", "set custom IDm"), arg_str0("r", NULL, "", "set custom reserve"), - arg_lit0("v", "verbose", "verbose helptext"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1471,7 +1471,7 @@ static int CmdHFFelicaResetMode(const char *Cmd) { arg_param_begin, arg_str0("i", NULL, "", "set custom IDm"), arg_str0("r", NULL, "", "set custom reserve"), - arg_lit0("v", "verbose", "verbose helptext"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index 4609b93cb..64cc8a43f 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -44,6 +44,7 @@ #include "cmdtrace.h" #include "util.h" #include "fileutils.h" // laodFileJSONroot +#include "protocols.h" // ISO7816 APDU return codes #define DEF_FIDO_SIZE 2048 #define DEF_FIDO_PARAM_FILE "hf_fido2_defparams.json" @@ -84,7 +85,7 @@ static int CmdHFFidoInfo(const char *Cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { if (sw) PrintAndLogEx(INFO, "Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); else @@ -111,7 +112,7 @@ static int CmdHFFidoInfo(const char *Cmd) { if (res) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "FIDO2 version doesn't exist (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return PM3_SUCCESS; } @@ -262,7 +263,7 @@ static int CmdHFFidoRegister(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); json_decref(root); @@ -277,7 +278,7 @@ static int CmdHFFidoRegister(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "ERROR execute register command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return PM3_ESOFT; } @@ -413,7 +414,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("a", "apdu", "Show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_lit0("v", "verbose", "Verbose mode"), arg_rem("default mode:", "dont-enforce-user-presence-and-sign"), arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"), @@ -584,7 +585,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); json_decref(root); @@ -599,7 +600,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "ERROR execute authentication command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); json_decref(root); return PM3_ESOFT; @@ -639,7 +640,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res)); } } else { - PrintAndLogEx(SUCCESS, "Signature is (" _GREEN_("ok") " )"); + PrintAndLogEx(SUCCESS, "Signature is ( " _GREEN_("ok") " )"); } } else { PrintAndLogEx(WARNING, "No public key provided. can't check signature."); @@ -675,7 +676,7 @@ static int CmdHFFido2MakeCredential(const char *cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("a", "apdu", "Show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_litn("v", "verbose", 0, 2, "Verbose mode. vv - show full certificates data"), arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"), arg_lit0("c", "cbor", "Show CBOR decoded data"), @@ -724,7 +725,7 @@ static int CmdHFFido2MakeCredential(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); json_decref(root); @@ -752,7 +753,7 @@ static int CmdHFFido2MakeCredential(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "ERROR execute make credential command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); json_decref(root); return PM3_EFILE; @@ -794,7 +795,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("a", "apdu", "Show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_litn("v", "verbose", 0, 2, "Verbose mode. vv - show full certificates data"), arg_lit0("c", "cbor", "Show CBOR decoded data"), arg_lit0("l", "list", "Add CredentialId from json to allowList"), @@ -843,7 +844,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Can't select FIDO application. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); json_decref(root); @@ -871,7 +872,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "ERROR execute get assertion command. APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); json_decref(root); return PM3_ESOFT; diff --git a/client/src/cmdhffudan.c b/client/src/cmdhffudan.c new file mode 100644 index 000000000..502425f83 --- /dev/null +++ b/client/src/cmdhffudan.c @@ -0,0 +1,510 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency proximity cards from ISO14443A / Fudan commands +//----------------------------------------------------------------------------- + +#include "cmdhffudan.h" + +#include +#include +#include +#include "cliparser.h" +#include "cmdparser.h" // command_t +#include "comms.h" +#include "cmdhf14a.h" +#include "cmddata.h" +#include "mifare.h" // xiso +#include "cmdhf.h" // +#include "fileutils.h" // saveFile +#include "ui.h" +#include "commonutil.h" // MemLeToUint2byte +#include "protocols.h" // ISO14 defines +#include "crc16.h" // compute_crc +#include "util_posix.h" // msclock + +#define FUDAN_BLOCK_READ_RETRY 3 +#define MAX_FUDAN_BLOCK_SIZE 4 +#define MAX_FUDAN_05_BLOCKS 8 // 16 +#define MAX_FUDAN_08_BLOCKS 64 + +#ifndef AddCrc14A +# define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1) +#endif + + +// iceman: these types are quite unsure. +typedef enum { + FM11RF005M, + FM11RF008M, + FM11RF005SH, + FM11RF08SH, + FUDAN_NONE, +} fudan_type_t; + +static void fudan_print_blocks(uint16_t n, uint8_t *d) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "----+-------------+-----------------"); + PrintAndLogEx(INFO, "blk | data | ascii"); + PrintAndLogEx(INFO, "----+-------------+-----------------"); + for (uint16_t b = 0; b < n; b++) { + PrintAndLogEx(INFO, "%3d | %s ", b, sprint_hex_ascii(d + (b * MAX_FUDAN_BLOCK_SIZE), MAX_FUDAN_BLOCK_SIZE)); + } + PrintAndLogEx(INFO, "----+-------------+-----------------"); + PrintAndLogEx(NORMAL, ""); +} + +static char *GenerateFilename(iso14a_card_select_t *card, const char *prefix, const char *suffix) { + if (card == NULL) { + return NULL; + } + char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(card->uid) * 2 + 1, sizeof(uint8_t)); + strcpy(fptr, prefix); + FillFileNameByUID(fptr, card->uid, suffix, card->uidlen); + return fptr; +} + +static fudan_type_t fudan_detected(iso14a_card_select_t *card) { + + if ((card->sak & 0x0A) == 0x0A) { + + uint8_t atqa = MemLeToUint2byte(card->atqa); + if ((atqa & 0x0003) == 0x0003) { + // Uses Shanghai algo + // printTag("FM11RF005SH (FUDAN Shanghai Metro)"); + return FM11RF005SH; + } else if ((atqa & 0x0005) == 0x0005) { + // printTag("FM11RF005M (FUDAN MIFARE Classic clone)"); + return FM11RF005M; + } else if ((atqa & 0x0008) == 0x0008) { + // printTag("FM11RF008M (FUDAN MIFARE Classic clone)"); + return FM11RF008M; + } + + } else if ((card->sak & 0x53) == 0x53) { + // printTag("FM11RF08SH (FUDAN)"); + return FM11RF08SH; + } + return FUDAN_NONE; +} + +static int fudan_get_type(iso14a_card_select_t *card, bool verbose) { + + if (card == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); + return PM3_ESOFT; + } + + memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.oldarg[0]; + + if (select_status == 0) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); + DropField(); + return PM3_ESOFT; + } + + if (select_status == 3) { + if (verbose) { + PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); + PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card->atqa[1], card->atqa[0]); + } + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card->uid, card->uidlen)); + if (verbose) { + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card->atqa[1], card->atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card->sak, select_status); + + if (card->ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes + if (card->ats_len == card->ats[0] + 2) + PrintAndLogEx(SUCCESS, " ATS: " _GREEN_("%s"), sprint_hex(card->ats, card->ats[0])); + else { + PrintAndLogEx(SUCCESS, " ATS: [%d] " _GREEN_("%s"), card->ats_len, sprint_hex(card->ats, card->ats_len)); + } + } + } + return PM3_SUCCESS; +} + +int read_fudan_uid(bool loop, bool verbose) { + + do { + iso14a_card_select_t card; + + int res = fudan_get_type(&card, verbose); + + if (loop) { + if (res != PM3_SUCCESS) { + continue; + } + } else { + switch (res) { + case PM3_EFAILED: + case PM3_EINVARG: + return res; + case PM3_ETIMEOUT: + PrintAndLogEx(DEBUG, "command execution time out"); + return res; + case PM3_ESOFT: + PrintAndLogEx(DEBUG, "fudan card select failed"); + return PM3_ESOFT; + default: + break; + } + } + + + if (loop) { + res = handle_hf_plot(); + if (res != PM3_SUCCESS) { + break; + } + } + + // decoding code + if (loop == false) { + PrintAndLogEx(NORMAL, ""); + } + + } while (loop && kbd_enter_pressed() == false); + + + return PM3_SUCCESS; +} + +static int CmdHFFudanReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf fudan reader", + "Read a fudan tag", + "hf fudan reader\n" + "hf fudan reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool verbose = arg_get_lit(ctx, 1); + bool cm = arg_get_lit(ctx, 2); + + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + read_fudan_uid(cm, verbose); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHFFudanDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf fudan dump", + "Dump FUDAN tag to binary file\n" + "If no given, UID will be used as filename", + "hf fudan dump -f mydump --> dump using filename\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dump"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int datafnlen = 0; + char dataFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen); + CLIParserFree(ctx); + + // Select card to get UID/UIDLEN/ATQA/SAK information + // leaves the field on + iso14a_card_select_t card; + int res = fudan_get_type(&card, false); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to select a fudan card. Exiting..."); + DropField(); + return PM3_SUCCESS; + } + + // validations + fudan_type_t t = fudan_detected(&card); + if (t == FUDAN_NONE) { + PrintAndLogEx(FAILED, "failed to detect a fudan card. Exiting..."); + DropField(); + return PM3_SUCCESS; + } + + // detect card size + // 512b, 8kbits + uint8_t num_blocks = MAX_FUDAN_05_BLOCKS; + switch (t) { + case FM11RF008M: + num_blocks = MAX_FUDAN_08_BLOCKS; + break; + case FM11RF005SH: + case FM11RF005M: + case FM11RF08SH: + case FUDAN_NONE: + default: + break; + } + + uint8_t carddata[num_blocks * MAX_FUDAN_BLOCK_SIZE]; + + // + uint16_t flags = (ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS | ISO14A_RAW); + uint32_t argtimeout = 0; + uint32_t numbits = 0; + + PrintAndLogEx(SUCCESS, "." NOLF); + // dump memory + for (uint8_t b = 0; b < num_blocks; b++) { + + // read block + uint8_t cmd[4] = {ISO14443A_CMD_READBLOCK, b, 0x00, 0x00}; + AddCrc14A(cmd, 2); + + for (uint8_t tries = 0; tries < FUDAN_BLOCK_READ_RETRY; tries++) { + + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandOLD(CMD_HF_ISO14443A_READER, flags, sizeof(cmd) | ((uint32_t)(numbits << 16)), argtimeout, cmd, sizeof(cmd)); + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (resp.status == PM3_SUCCESS) { + uint8_t *data = resp.data.asBytes; + memcpy(carddata + (b * MAX_FUDAN_BLOCK_SIZE), data, MAX_FUDAN_BLOCK_SIZE); + PrintAndLogEx(NORMAL, "." NOLF); + break; + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "could not read block %2d", b); + } + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d", b); + } + } + + } + DropField(); + + PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); + + fudan_print_blocks(num_blocks, carddata); + + // create filename if none was given + if (strlen(dataFilename) < 1) { + char *fptr = GenerateFilename(&card, "hf-fudan-", "-dump"); + if (fptr == NULL) + return PM3_ESOFT; + + strcpy(dataFilename, fptr); + free(fptr); + } + + saveFile(dataFilename, ".bin", (uint8_t *)carddata, sizeof(carddata)); + saveFileEML(dataFilename, (uint8_t *)carddata, sizeof(carddata), MAX_FUDAN_BLOCK_SIZE); + + iso14a_mf_extdump_t xdump; + xdump.card_info = card; + xdump.dump = (uint8_t *)carddata; + xdump.dumplen = sizeof(carddata); + saveFileJSON(dataFilename, jsfFudan, (uint8_t *)&xdump, sizeof(xdump), NULL); + return PM3_SUCCESS; +} + +static int CmdHFFudanWrBl(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf fudan wrbl", + "Write fudan block with 4 hex bytes of data\n", + "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304" + ); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "blk", "", "block number"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_str0("d", "data", "", "bytes to write, 4 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int b = arg_get_int_def(ctx, 1, 1); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 2, key, &keylen); + + uint8_t block[MAX_FUDAN_BLOCK_SIZE] = {0x00}; + int blen = 0; + CLIGetHexWithReturn(ctx, 3, block, &blen); + CLIParserFree(ctx); + + if (blen != MAX_FUDAN_BLOCK_SIZE) { + PrintAndLogEx(WARNING, "block data must include 4 HEX bytes. Got %i", blen); + return PM3_EINVARG; + } + + if (b > 255) { + return PM3_EINVARG; + } + + PrintAndLogEx(SUCCESS, "Not implemented yet. Feel free to contribute!"); + + /* + + uint8_t blockno = (uint8_t)b; + + PrintAndLogEx(INFO, "Writing block no %d, key %s", blockno, sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); + + uint8_t data[26]; + memcpy(data, key, sizeof(key)); + memcpy(data + 10, block, sizeof(block)); + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, 0, 0, data, sizeof(data)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(FAILED, "Command execute timeout"); + return PM3_ETIMEOUT; + } + + uint8_t isok = resp.oldarg[0] & 0xff; + if (isok) { + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf fudan rdbl") "` to verify"); + } else { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + } + */ + return PM3_SUCCESS; +} + +static int CmdHFFudanRdBl(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf fudan rdbl", + "Read fudan block", + "hf fudan rdbl --blk 0 -k FFFFFFFFFFFF\n" + "hf fudan rdbl --blk 3 -v\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "blk", "", "block number"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int b = arg_get_int_def(ctx, 1, 0); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 2, key, &keylen); +// bool verbose = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (b > 255) { + return PM3_EINVARG; + } + + PrintAndLogEx(SUCCESS, "Not implemented yet. Feel free to contribute!"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + + +static int CmdHFFudanView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf fudan view", + "Print a FUDAN dump file (bin/eml/json)", + "hf fudan view -f hf-fudan-01020304-dump.bin" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + // read dump file + uint8_t *dump = NULL; + size_t bytes_read = 0; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MAX_FUDAN_BLOCK_SIZE * MAX_FUDAN_08_BLOCKS)); + if (res != PM3_SUCCESS) { + return res; + } + + uint16_t block_cnt = MIN(MAX_FUDAN_05_BLOCKS, (bytes_read / MAX_FUDAN_BLOCK_SIZE)); + + fudan_print_blocks(block_cnt, dump); + + free(dump); + return PM3_SUCCESS; +} + +static int CmdHelp(const char *Cmd); + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"reader", CmdHFFudanReader, IfPm3Iso14443a, "Act like a fudan reader"}, + {"dump", CmdHFFudanDump, IfPm3Iso14443a, "Dump FUDAN tag to binary file"}, + //{"sim", CmdHFFudanSim, IfPm3Iso14443a, "Simulate a fudan tag"}, + {"rdbl", CmdHFFudanRdBl, IfPm3Iso14443a, "Read a fudan tag"}, + {"view", CmdHFFudanView, AlwaysAvailable, "Display content from tag dump file"}, + {"wrbl", CmdHFFudanWrBl, IfPm3Iso14443a, "Write a fudan tag"}, + {NULL, NULL, 0, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFFudan(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhffudan.h b/client/src/cmdhffudan.h new file mode 100644 index 000000000..afdd618bd --- /dev/null +++ b/client/src/cmdhffudan.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency proximity cards from ISO14443A / FUDAN commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFFUDAN_H__ +#define CMDHFFUDAN_H__ + +#include "common.h" +#include "pm3_cmd.h" + +int CmdHFFudan(const char *Cmd); +int read_fudan_uid(bool loop, bool verbose); + +#endif + diff --git a/client/src/cmdhfgallagher.c b/client/src/cmdhfgallagher.c index 0872e8681..ff527a524 100644 --- a/client/src/cmdhfgallagher.c +++ b/client/src/cmdhfgallagher.c @@ -840,9 +840,10 @@ static int hfgal_read_card(uint32_t aid, uint8_t *site_key, bool verbose, bool q uint32_t current_aid = cad_aid_byte_to_uint(&cad[i + 3]); if (verbose) { - if (region_code > 0 || facility_code > 0) { - PrintAndLogEx(INFO, "Reading AID: " _YELLOW_("%06X") ", region: " _YELLOW_("%u") ", facility: " _YELLOW_("%u"), + if (region_code > 0 || facility_code > 0 || current_aid > 0) { + PrintAndLogEx(INFO, "Reading AID: " _YELLOW_("%06X") ", region: " _YELLOW_("%c") " ( " _YELLOW_("%u") " ), facility: " _YELLOW_("%u"), current_aid, + 'A' + region_code, region_code, facility_code ); @@ -860,11 +861,12 @@ static int hfgal_read_card(uint32_t aid, uint8_t *site_key, bool verbose, bool q } PM3_RET_IF_ERR_MAYBE_MSG(res, !quiet, "Failed reading card application credentials"); - PrintAndLogEx(SUCCESS, "Gallagher (AID %06X) - region: " _GREEN_("%u") + PrintAndLogEx(SUCCESS, "Gallagher (AID %06X) - region: " _GREEN_("%c") " ( " _GREEN_("%u") " )" ", facility: " _GREEN_("%u") ", card number: " _GREEN_("%u") ", issue level: " _GREEN_("%u"), current_aid, + 'A' + creds.region_code, creds.region_code, creds.facility_code, creds.card_number, @@ -1252,12 +1254,71 @@ static int CmdGallagherDiversify(const char *cmd) { return PM3_SUCCESS; } +static int CmdGallagherDecode(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf gallagher decode", + "Decode Gallagher credential block\n" + "Credential block can be specified with or without the bitwise inverse.", + "hf gallagher decode --data A3B4B0C151B0A31B" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1(NULL, "data", "", "Credential block (8 or 16 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, false); + + int data_len = 0; + uint8_t data_buf[16] = {0}; + CLIGetHexWithReturn(ctx, 1, data_buf, &data_len); + if (data_len != 8 && data_len != 16) { + PM3_RET_ERR_FREE(PM3_EINVARG, "--data must be 8 or 16 bytes"); + } + CLIParserFree(ctx); + + if (data_len == 16) { + // Check second half of file is the bitwise inverse of the first half + for (uint8_t i = 8; i < 16; i++) { + data_buf[i] ^= 0xFF; + } + + if (memcmp(data_buf, &data_buf[8], 8) != 0) { + PM3_RET_ERR(PM3_EFAILED, "Invalid cardholder data, last 8 bytes should be bitwise inverse of first 16 bytes. Received %s", + sprint_hex_inrow(data_buf, 16) + ); + } + } else { + for (uint8_t i = 0; i < 8; i++) { + data_buf[i + 8] = data_buf[i] ^ 0xFF; + } + PrintAndLogEx(INFO, "Full credential block with bitwise inverse: " _YELLOW_("%s"), sprint_hex_inrow(data_buf, 16)); + } + + GallagherCredentials_t creds = {0}; + gallagher_decode_creds(data_buf, &creds); + + PrintAndLogEx(SUCCESS, "Gallagher - region: " _GREEN_("%c") " ( " _GREEN_("%u") " )" + ", facility: " _GREEN_("%u") + ", card number: " _GREEN_("%u") + ", issue level: " _GREEN_("%u"), + 'A' + creds.region_code, + creds.region_code, + creds.facility_code, + creds.card_number, + creds.issue_level + ); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"reader", CmdGallagherReader, IfPm3Iso14443, "Read & decode all Gallagher credentials on a DESFire card"}, {"clone", CmdGallagherClone, IfPm3Iso14443, "Add Gallagher credentials to a DESFire card"}, {"delete", CmdGallagherDelete, IfPm3Iso14443, "Delete Gallagher credentials from a DESFire card"}, {"diversifykey", CmdGallagherDiversify, AlwaysAvailable, "Diversify Gallagher key"}, + {"decode", CmdGallagherDecode, AlwaysAvailable, "Decode Gallagher credential block"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index cf17a2505..b328436bb 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -37,7 +37,11 @@ #include "cmdsmartcard.h" // smart select fct #include "proxendian.h" #include "iclass_cmd.h" +#include "crypto/asn1utils.h" // ASN1 decoder +#include "preferences.h" + +#define PICOPASS_BLOCK_SIZE 8 #define NUM_CSNS 9 #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 @@ -55,12 +59,13 @@ static uint8_t empty[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static uint8_t zeros[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static int CmdHelp(const char *Cmd); +static void printIclassSIO(uint8_t *iclass_dump); static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }, - { 0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00 }, + { 0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90 }, { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, @@ -83,7 +88,7 @@ static int cmp_uint32(const void *a, const void *b) { bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t *tmac, uint8_t *key) { - iclass_prekey_t *prekey = calloc(ICLASS_KEYS_MAX, sizeof(iclass_prekey_t)); + iclass_prekey_t *prekey = calloc(ICLASS_KEYS_MAX * 2, sizeof(iclass_prekey_t)); if (prekey == NULL) { return false; } @@ -93,17 +98,20 @@ bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t * memcpy(ccnr + 8, rmac, 4); GenerateMacKeyFrom(csn, ccnr, false, false, (uint8_t *)iClass_Key_Table, ICLASS_KEYS_MAX, prekey); - qsort(prekey, ICLASS_KEYS_MAX, sizeof(iclass_prekey_t), cmp_uint32); + GenerateMacKeyFrom(csn, ccnr, false, true, (uint8_t *)iClass_Key_Table, ICLASS_KEYS_MAX, prekey + ICLASS_KEYS_MAX); + qsort(prekey, ICLASS_KEYS_MAX * 2, sizeof(iclass_prekey_t), cmp_uint32); iclass_prekey_t lookup; memcpy(lookup.mac, tmac, 4); // binsearch - iclass_prekey_t *item = (iclass_prekey_t *) bsearch(&lookup, prekey, ICLASS_KEYS_MAX, sizeof(iclass_prekey_t), cmp_uint32); + iclass_prekey_t *item = (iclass_prekey_t *) bsearch(&lookup, prekey, ICLASS_KEYS_MAX * 2, sizeof(iclass_prekey_t), cmp_uint32); if (item != NULL) { memcpy(key, item->key, 8); + free(prekey); return true; } + free(prekey); return false; } @@ -277,7 +285,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // get header from card PrintAndLogEx(INFO, "trying to read a card.."); - int res = read_iclass_csn(false, false); + int res = read_iclass_csn(false, false, false); if (res == PM3_SUCCESS) { cc = &iclass_last_known_card; // calc diversified key for selected card @@ -584,19 +592,19 @@ static void mem_app_config(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "------------------------- " _CYAN_("KeyAccess") " ------------------------"); PrintAndLogEx(INFO, " * Kd, Debit key, AA1 Kc, Credit key, AA2 *"); - uint8_t book = isset(mem, 0x20); - if (book) { - PrintAndLogEx(INFO, " Read A....... debit"); - PrintAndLogEx(INFO, " Read B....... credit"); - PrintAndLogEx(INFO, " Write A...... debit"); - PrintAndLogEx(INFO, " Write B...... credit"); + uint8_t keyAccess = isset(mem, 0x01); + if (keyAccess) { + PrintAndLogEx(INFO, " Read AA1....... debit"); + PrintAndLogEx(INFO, " Write AA1...... debit"); + PrintAndLogEx(INFO, " Read AA2....... credit"); + PrintAndLogEx(INFO, " Write AA2...... credit"); PrintAndLogEx(INFO, " Debit........ debit or credit"); PrintAndLogEx(INFO, " Credit....... credit"); } else { - PrintAndLogEx(INFO, " Read A....... debit or credit"); - PrintAndLogEx(INFO, " Read B....... debit or credit"); - PrintAndLogEx(INFO, " Write A...... credit"); - PrintAndLogEx(INFO, " Write B...... credit"); + PrintAndLogEx(INFO, " Read AA1....... debit or credit"); + PrintAndLogEx(INFO, " Write AA1...... credit"); + PrintAndLogEx(INFO, " Read AA2....... debit or credit"); + PrintAndLogEx(INFO, " Write AA2...... credit"); PrintAndLogEx(INFO, " Debit........ debit or credit"); PrintAndLogEx(INFO, " Credit....... credit"); } @@ -652,14 +660,19 @@ static int CmdHFiClassSniff(const char *Cmd) { bool jam_epurse_update = arg_get_lit(ctx, 1); CLIParserFree(ctx); - const uint8_t update_epurse_sequence[2] = {0x87, 0x02}; + if (jam_epurse_update) { + PrintAndLogEx(INFO, "Sniff with jam of iCLASS e-purse updates..."); + } struct { uint8_t jam_search_len; uint8_t jam_search_string[2]; } PACKED payload; + memset(&payload, 0, sizeof(payload)); + if (jam_epurse_update) { + const uint8_t update_epurse_sequence[2] = {0x87, 0x02}; payload.jam_search_len = sizeof(update_epurse_sequence); memcpy(payload.jam_search_string, update_epurse_sequence, sizeof(payload.jam_search_string)); } @@ -693,7 +706,7 @@ static int CmdHFiClassSim(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int sim_type = arg_get_int(ctx, 1); + int sim_type = arg_get_int_def(ctx, 1, 3); int csn_len = 0; uint8_t csn[8] = {0}; @@ -892,19 +905,25 @@ static int CmdHFiClassInfo(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool shallow_mod = arg_get_lit(ctx, 1); CLIParserFree(ctx); - return info_iclass(); + return info_iclass(shallow_mod); } -int read_iclass_csn(bool loop, bool verbose) { +int read_iclass_csn(bool loop, bool verbose, bool shallow_mod) { iclass_card_select_t payload = { .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) }; + if (shallow_mod) { + payload.flags |= FLAG_ICLASS_READER_SHALLOW_MOD; + } + int res = PM3_SUCCESS; do { @@ -960,30 +979,33 @@ static int CmdHFiClassReader(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool cm = arg_get_lit(ctx, 1); + bool shallow_mod = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (cm) { PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); } - return read_iclass_csn(cm, true); + return read_iclass_csn(cm, true, shallow_mod); } static int CmdHFiClassELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass eload", "Load emulator memory with data from (bin/eml/json) iCLASS dump file", - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml\n" + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -m\n" ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_lit0("m", "mem", "use RDV4 spiffs"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -998,8 +1020,39 @@ static int CmdHFiClassELoad(const char *Cmd) { return PM3_EINVARG; } + bool use_spiffs = arg_get_lit(ctx, 2); CLIParserFree(ctx); + // use RDV4 spiffs + if (use_spiffs && IfPm3Flash() == false) { + PrintAndLogEx(WARNING, "Device not compiled to support spiffs"); + return PM3_EINVARG; + } + + if (use_spiffs) { + + if (fnlen > 32) { + PrintAndLogEx(WARNING, "filename too long for spiffs, expected 32, got %u", fnlen); + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Loading file from spiffs to emulatore memory failed"); + return PM3_EFLASH; + } + + PrintAndLogEx(SUCCESS, "File transfered from spiffs to device emulator memory"); + return PM3_SUCCESS; + } + // read dump file uint8_t *dump = NULL; size_t bytes_read = 2048; @@ -1076,9 +1129,7 @@ static int CmdHFiClassESave(const char *Cmd) { FillFileNameByUID(fptr, dump, "-dump", 8); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, 8); - saveFileJSON(filename, jsfIclass, dump, bytes, NULL); + pm3_save_dump(filename, dump, bytes, jsfIclass, PICOPASS_BLOCK_SIZE); free(dump); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); @@ -1098,6 +1149,7 @@ static int CmdHFiClassEView(const char *Cmd) { arg_param_begin, arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0("z", "dense", "dense dump output style"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1105,6 +1157,7 @@ static int CmdHFiClassEView(const char *Cmd) { uint16_t blocks = 32; uint16_t bytes = arg_get_int_def(ctx, 1, 256); bool verbose = arg_get_lit(ctx, 2); + bool dense_output = g_session.dense_output || arg_get_lit(ctx, 3); blocks = bytes / 8; CLIParserFree(ctx); @@ -1139,11 +1192,45 @@ static int CmdHFiClassEView(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - printIclassDumpContents(dump, 1, blocks, bytes); + printIclassDumpContents(dump, 1, blocks, bytes, dense_output); + + if (verbose) { + printIclassSIO(dump); + } + free(dump); return PM3_SUCCESS; } +static void iclass_decode_credentials(uint8_t *data) { + BLOCK79ENCRYPTION encryption = (data[(6 * 8) + 7] & 0x03); + bool has_values = (memcmp(data + (8 * 7), empty, 8) != 0) && (memcmp(data + (8 * 7), zeros, 8) != 0); + if (has_values && encryption == None) { + + //todo: remove preamble/sentinel + uint32_t top = 0, mid = 0, bot = 0; + + PrintAndLogEx(INFO, "Block 7 decoder"); + + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, data + (8 * 7), 8, sizeof(hexstr) - 1, 0, 0, true); + hexstring_to_u96(&top, &mid, &bot, hexstr); + + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); + + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + } else { + PrintAndLogEx(INFO, "No credential found"); + } +} + static int CmdHFiClassDecrypt(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass decrypt", @@ -1167,6 +1254,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "d6", "decode as block 6"), + arg_lit0("z", "dense", "dense dump output style"), arg_param_end }; CLIExecWithReturn(clictx, Cmd, argtable, false); @@ -1190,6 +1278,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { bool verbose = arg_get_lit(clictx, 4); bool use_decode6 = arg_get_lit(clictx, 5); + bool dense_output = g_session.dense_output || arg_get_lit(clictx, 6); CLIParserFree(clictx); // sanity checks @@ -1296,21 +1385,36 @@ static int CmdHFiClassDecrypt(const char *Cmd) { //uint8_t numblocks4userid = GetNumberBlocksForUserId(decrypted + (6 * 8)); + bool decrypted_block789 = false; for (uint8_t blocknum = 0; blocknum < limit; ++blocknum) { uint16_t idx = blocknum * 8; memcpy(enc_data, decrypted + idx, 8); - if (aa1_encryption == RFU || aa1_encryption == None) - continue; + switch (aa1_encryption) { + // Right now, only 3DES is supported + case TRIPLEDES: + // Decrypt block 7,8,9 if configured. + if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, 8) != 0) { + if (use_sc) { + Decrypt(enc_data, decrypted + idx); + } else { + mbedtls_des3_crypt_ecb(&ctx, enc_data, decrypted + idx); + } + decrypted_block789 = true; + } + break; + case DES: + case RFU: + case None: + // Nothing to do for None anyway... + default: + continue; + } - // Decrypted block 7,8,9 if configured. - if (blocknum > 6 && blocknum <= 9 && memcmp(enc_data, empty, 8) != 0) { - if (use_sc) { - Decrypt(enc_data, decrypted + idx); - } else { - mbedtls_des3_crypt_ecb(&ctx, enc_data, decrypted + idx); - } + if (decrypted_block789) { + // Set the 2 last bits of block6 to 0 to mark the data as decrypted + decrypted[(6 * 8) + 7] &= 0xFC; } } @@ -1325,11 +1429,13 @@ static int CmdHFiClassDecrypt(const char *Cmd) { strcat(fptr, "hf-iclass-"); FillFileNameByUID(fptr, hdr->csn, "-dump-decrypted", sizeof(hdr->csn)); - saveFile(fptr, ".bin", decrypted, decryptedlen); - saveFileEML(fptr, decrypted, decryptedlen, 8); - saveFileJSON(fptr, jsfIclass, decrypted, decryptedlen, NULL); + pm3_save_dump(fptr, decrypted, decryptedlen, jsfIclass, PICOPASS_BLOCK_SIZE); - printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen); + printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen, dense_output); + + if (verbose) { + printIclassSIO(decrypted); + } PrintAndLogEx(NORMAL, ""); @@ -1342,33 +1448,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } // decode block 7-8-9 - has_values = (memcmp(decrypted + (8 * 7), empty, 8) != 0) && (memcmp(decrypted + (8 * 7), zeros, 8) != 0); - if (has_values) { - - //todo: remove preamble/sentinel - uint32_t top = 0, mid, bot; - mid = bytes_to_num(decrypted + (8 * 7), 4); - bot = bytes_to_num(decrypted + (8 * 7) + 4, 4); - - PrintAndLogEx(INFO, "Block 7 decoder"); - - char hexstr[16 + 1] = {0}; - hex_to_buffer((uint8_t *)hexstr, decrypted + (8 * 7), 8, sizeof(hexstr) - 1, 0, 0, true); - - char binstr[8 * 8 + 1] = {0}; - hextobinstring(binstr, hexstr); - size_t i = 0; - while (i < strlen(binstr) && binstr[i++] == '0'); - - PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), binstr + i); - - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr + i)); - HIDTryUnpack(&packed); - - } else { - PrintAndLogEx(INFO, "No credential found"); - } + iclass_decode_credentials(decrypted); // decode block 9 has_values = (memcmp(decrypted + (8 * 9), empty, 8) != 0) && (memcmp(decrypted + (8 * 9), zeros, 8) != 0); @@ -1495,12 +1575,16 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { return PM3_SUCCESS; } -static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { +static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose, bool shallow_mod) { iclass_card_select_t payload = { .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) }; + if (shallow_mod) { + payload.flags |= FLAG_ICLASS_READER_SHALLOW_MOD; + } + clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload, sizeof(iclass_card_select_t)); @@ -1554,6 +1638,9 @@ static int CmdHFiClassDump(const char *Cmd) { arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "raw, the key is interpreted as raw block 3/4"), arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0("z", "dense", "dense dump output style"), + arg_lit0(NULL, "force", "force unsecure card read"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1637,6 +1724,9 @@ static int CmdHFiClassDump(const char *Cmd) { bool elite = arg_get_lit(ctx, 6); bool rawkey = arg_get_lit(ctx, 7); bool use_replay = arg_get_lit(ctx, 8); + bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); + bool force = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); CLIParserFree(ctx); @@ -1655,6 +1745,11 @@ static int CmdHFiClassDump(const char *Cmd) { iclass_card_select_t payload_rdr = { .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) }; + + if (shallow_mod) { + payload_rdr.flags |= FLAG_ICLASS_READER_SHALLOW_MOD; + } + clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload_rdr, sizeof(iclass_card_select_t)); @@ -1703,6 +1798,12 @@ static int CmdHFiClassDump(const char *Cmd) { } } + // + if (force) { + pagemap = PICOPASS_NON_SECURE_PAGEMODE; + PrintAndLogEx(INFO, "Forcing NON SECURE PAGE dumping"); + } + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, "Dumping all available memory, block 3 - %u (0x%02x)", app_limit1, app_limit1); if (auth) { @@ -1728,6 +1829,7 @@ static int CmdHFiClassDump(const char *Cmd) { .req.use_replay = use_replay, .req.send_reply = true, .req.do_auth = auth, + .req.shallow_mod = shallow_mod, .end_block = app_limit1, }; memcpy(payload.req.key, key, 8); @@ -1737,7 +1839,7 @@ static int CmdHFiClassDump(const char *Cmd) { payload.start_block = 3; payload.req.do_auth = false; } else { - payload.start_block = 6; + payload.start_block = 5; } clearCommandBuffer(); @@ -1869,7 +1971,7 @@ write_dump: PrintAndLogEx(INFO, "Reading AA2 failed. dumping AA1 data to file"); // print the dump - printIclassDumpContents(tag_data, 1, (bytes_got / 8), bytes_got); + printIclassDumpContents(tag_data, 1, (bytes_got / 8), bytes_got, dense_output); // use CSN as filename if (filename[0] == 0) { @@ -1879,9 +1981,8 @@ write_dump: // save the dump to .bin file PrintAndLogEx(SUCCESS, "saving dump file - %u blocks read", bytes_got / 8); - saveFile(filename, ".bin", tag_data, bytes_got); - saveFileEML(filename, tag_data, bytes_got, 8); - saveFileJSON(filename, jsfIclass, tag_data, bytes_got, NULL); + + pm3_save_dump(filename, tag_data, bytes_got, jsfIclass, PICOPASS_BLOCK_SIZE); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt -f") "` to decrypt dump file"); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); @@ -1889,7 +1990,7 @@ write_dump: return PM3_SUCCESS; } -static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode) { +static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) { iclass_writeblock_req_t payload = { .req.use_raw = rawkey, @@ -1899,10 +2000,15 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bo .req.blockno = blockno, .req.send_reply = true, .req.do_auth = use_secure_pagemode, + .req.shallow_mod = shallow_mod, }; memcpy(payload.req.key, KEY, 8); memcpy(payload.data, bldata, sizeof(payload.data)); + if (replay) { + memcpy(payload.mac, macdata, sizeof(payload.mac)); + } + clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; @@ -1933,11 +2039,13 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), arg_int1("b", "block", "", "The block number to read"), arg_str1("d", "data", "", "data to write as 8 hex bytes"), + arg_str0("m", "mac", "", "replay mac data (4 hex bytes)"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0(NULL, "nr", "replay of NR/MAC"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1988,11 +2096,25 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { return PM3_EINVARG; } - bool use_credit_key = arg_get_lit(ctx, 5); - bool elite = arg_get_lit(ctx, 6); - bool rawkey = arg_get_lit(ctx, 7); - bool use_replay = arg_get_lit(ctx, 8); - bool verbose = arg_get_lit(ctx, 9); + int mac_len = 0; + uint8_t mac[4] = {0}; + CLIGetHexWithReturn(ctx, 5, mac, &mac_len); + + if (mac_len) { + if (mac_len != 4) { + PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + + bool use_credit_key = arg_get_lit(ctx, 6); + bool elite = arg_get_lit(ctx, 7); + bool rawkey = arg_get_lit(ctx, 8); + bool use_replay = arg_get_lit(ctx, 9); + bool verbose = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); CLIParserFree(ctx); @@ -2001,7 +2123,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { return PM3_EINVARG; } - int isok = iclass_write_block(blockno, data, key, use_credit_key, elite, rawkey, use_replay, verbose, auth); + int isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); switch (isok) { case PM3_SUCCESS: PrintAndLogEx(SUCCESS, "Wrote block %3d/0x%02X successful", blockno, blockno); @@ -2037,6 +2159,7 @@ static int CmdHFiClassRestore(const char *Cmd) { arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2086,6 +2209,7 @@ static int CmdHFiClassRestore(const char *Cmd) { bool elite = arg_get_lit(ctx, 7); bool rawkey = arg_get_lit(ctx, 8); bool verbose = arg_get_lit(ctx, 9); + bool shallow_mod = arg_get_lit(ctx, 10); CLIParserFree(ctx); @@ -2136,6 +2260,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true; + payload->req.shallow_mod = shallow_mod; memcpy(payload->req.key, key, 8); payload->item_cnt = (endblock - startblock + 1); @@ -2189,7 +2314,7 @@ static int CmdHFiClassRestore(const char *Cmd) { return resp.status; } -static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, bool auth, uint8_t *out) { +static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool replay, bool verbose, bool auth, bool shallow_mod, uint8_t *out) { iclass_auth_req_t payload = { .use_raw = rawkey, @@ -2199,6 +2324,7 @@ static int iclass_read_block(uint8_t *KEY, uint8_t blockno, uint8_t keyType, boo .blockno = blockno, .send_reply = true, .do_auth = auth, + .shallow_mod = shallow_mod, }; memcpy(payload.key, KEY, 8); @@ -2252,6 +2378,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0(NULL, "nr", "replay of NR/MAC"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2302,6 +2429,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { bool rawkey = arg_get_lit(ctx, 6); bool use_replay = arg_get_lit(ctx, 7); bool verbose = arg_get_lit(ctx, 8); + bool shallow_mod = arg_get_lit(ctx, 9); CLIParserFree(ctx); @@ -2321,7 +2449,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } uint8_t data[8] = {0}; - int res = iclass_read_block(key, blockno, keyType, elite, rawkey, use_replay, verbose, auth, data); + int res = iclass_read_block(key, blockno, keyType, elite, rawkey, use_replay, verbose, auth, shallow_mod, data); if (res != PM3_SUCCESS) return res; @@ -2363,23 +2491,21 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { if (memcmp(dec_data, empty, 8) != 0) { //todo: remove preamble/sentinel - - uint32_t top = 0, mid, bot; - mid = bytes_to_num(dec_data, 4); - bot = bytes_to_num(dec_data + 4, 4); + uint32_t top = 0, mid = 0, bot = 0; char hexstr[16 + 1] = {0}; hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); - char binstr[64 + 1] = {0}; - hextobinstring(binstr, hexstr); - size_t i = 0; - while (i < strlen(binstr) && binstr[i++] == '0'); + hexstring_to_u96(&top, &mid, &bot, hexstr); - i &= 0x3C; - PrintAndLogEx(SUCCESS, " bin : %s", binstr + i); + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, " bin : %s", pbin); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "------------------------------ " _CYAN_("wiegand") " -------------------------------"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr + i)); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); HIDTryUnpack(&packed); } else { PrintAndLogEx(INFO, "no credential found"); @@ -2438,7 +2564,61 @@ static int CmdHFiClass_loclass(const char *Cmd) { return bruteforceFileNoKeys(filename); } -void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize) { +static void detect_credential(uint8_t *data, bool *legacy, bool *se, bool *sr) { + bool r1 = !memcmp(data + (5 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); + + uint8_t pattern_se[] = {0x05, 0x00}; + bool r2 = byte_strstr(data + (6 * 8), 6 * 8, pattern_se, sizeof(pattern_se)) != -1; + + uint8_t pattern_sr[] = {0x05, 0x00, 0x05, 0x00}; + bool r3 = byte_strstr(data + (11 * 8), 6 * 8, pattern_sr, sizeof(pattern_sr)) != -1; + + *legacy = (r1) && (data[6 * 8] != 0x30); + *se = (r2) && (data[6 * 8] == 0x30); + *sr = (r3) && (data[10 * 8] == 0x30); + r1 = NULL, r2 = NULL, r3 = NULL; +} + +// print ASN1 decoded array in TLV view +static void printIclassSIO(uint8_t *iclass_dump) { + + bool isLegacy, isSE, isSR; + detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); + + int dlen = 0; + uint8_t *sio_start; + if (isSE) { + + sio_start = iclass_dump + (6 * 8); + uint8_t pattern_se[] = {0x05, 0x00}; + dlen = byte_strstr(sio_start, 8 * 8, pattern_se, sizeof(pattern_se)); + if (dlen == -1) { + return; + } + dlen += sizeof(pattern_se); + } else if (isSR) { + + sio_start = iclass_dump + (10 * 8); + uint8_t pattern_sr[] = {0x05, 0x00, 0x05, 0x00}; + dlen = byte_strstr(sio_start, 8 * 8, pattern_sr, sizeof(pattern_sr)); + if (dlen == -1) { + return; + } + dlen += sizeof(pattern_sr); + } else { + return; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------"); + print_hex_noascii_break(sio_start, dlen, 32); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - ASN1 TLV") " --------------------------"); + asn1_print(sio_start, dlen, " "); + PrintAndLogEx(NORMAL, ""); +} + +void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize, bool dense_output) { picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump; // picopass_ns_hdr_t *ns_hdr = (picopass_ns_hdr_t *)iclass_dump; @@ -2479,13 +2659,18 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e ); */ uint8_t pagemap = get_pagemap(hdr); - int i = startblock; + bool isLegacy = false, isSE = false, isSR = false; + if (filemaxblock >= 17) { + detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); + } + + int i = startblock; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Tag memory") " ---------------------------"); + PrintAndLogEx(INFO, "--------------------------- " _CYAN_("Tag memory") " ----------------------------"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, " block# | data | ascii |lck| info"); - PrintAndLogEx(INFO, "---------+-------------------------+----------+---+--------------"); + PrintAndLogEx(INFO, "---------+-------------------------+----------+---+----------------"); PrintAndLogEx(INFO, " 0/0x00 | " _GREEN_("%s") "| " _GREEN_("%s") " | | CSN " , sprint_hex(iclass_dump, 8) , sprint_ascii(iclass_dump, 8) @@ -2494,8 +2679,7 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e if (i != 1) PrintAndLogEx(INFO, " ......"); - uint8_t fb = (iclass_dump + (6 * 8))[0]; - + bool in_repeated_block = false; while (i <= endblock) { uint8_t *blk = iclass_dump + (i * 8); @@ -2537,25 +2721,21 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e const char *lockstr = (bl_lock) ? _RED_("x") : " "; + const char *block_info; + bool regular_print_block = false; if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { const char *info_nonks[] = {"CSN", "Config", "AIA", "User"}; - const char *s = info_nonks[3]; if (i < 3) { - s = info_nonks[i]; + block_info = info_nonks[i]; + } else { + block_info = info_nonks[3]; } - PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s " - , i - , i - , sprint_hex_ascii(blk, 8) - , lockstr - , s - ); - + regular_print_block = true; } else { const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; - if (i >= 6 && i <= 9 && fb != 0x30) { + if (i >= 6 && i <= 9 && isLegacy && isSE == false) { // legacy credential PrintAndLogEx(INFO, "%3d/0x%02X | " _YELLOW_("%s") "| " _YELLOW_("%s") " | %s | User / Cred " , i @@ -2564,9 +2744,18 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e , sprint_ascii(blk, 8) , lockstr ); - } else if (i >= 6 && i <= 11 && fb == 0x30) { + } else if (i >= 6 && i <= 12 && isSE) { // SIO credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO" + PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / SE" + , i + , i + , sprint_hex(blk, 8) + , sprint_ascii(blk, 8) + , lockstr + ); + } else if (i >= 10 && i <= 16 && isSR) { + // SIO credential + PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / SR" , i , i , sprint_hex(blk, 8) @@ -2574,18 +2763,53 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e , lockstr ); } else { - const char *s = info_ks[6]; if (i < 6) { - s = info_ks[i]; + block_info = info_ks[i]; + } else { + block_info = info_ks[6]; } - PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s ", i, i, sprint_hex_ascii(blk, 8), lockstr, s); + + regular_print_block = true; } } + + if (regular_print_block) { + // suppress repeating blocks, truncate as such that the first and last block with the same data is shown + // but the blocks in between are replaced with a single line of "*" if dense_output is enabled + if (dense_output && i > 6 && i < (endblock - 1) && !in_repeated_block && !memcmp(blk, blk - 8, 8) && + !memcmp(blk, blk + 8, 8) && !memcmp(blk, blk + 16, 8)) { + // we're in a user block that isn't the first user block nor last two user blocks, + // and the current block data is the same as the previous and next two block + in_repeated_block = true; + PrintAndLogEx(INFO, " ......"); + } else if (in_repeated_block && (memcmp(blk, blk + 8, 8) || i == endblock)) { + // in a repeating block, but the next block doesn't match anymore, or we're at the end block + in_repeated_block = false; + } + + if (in_repeated_block == false) { + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s ", + i, + i, + sprint_hex_ascii(blk, 8), + lockstr, + block_info); + } + } + i++; } - PrintAndLogEx(INFO, "---------+-------------------------+----------+---+--------------"); - PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential"); - PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO credential"); + PrintAndLogEx(INFO, "---------+-------------------------+----------+---+----------------"); + if (isLegacy) + PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential"); + + if (isSE) + PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SE credential"); + + if (isSR) + PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SR credential"); + PrintAndLogEx(NORMAL, ""); } @@ -2602,6 +2826,7 @@ static int CmdHFiClassView(const char *Cmd) { arg_int0(NULL, "first", "", "Begin printing from this block (default block 6)"), arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0("z", "dense", "dense dump output style"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2613,6 +2838,7 @@ static int CmdHFiClassView(const char *Cmd) { int startblock = arg_get_int_def(ctx, 2, 0); int endblock = arg_get_int_def(ctx, 3, 0); bool verbose = arg_get_lit(ctx, 4); + bool dense_output = g_session.dense_output || arg_get_lit(ctx, 5); CLIParserFree(ctx); @@ -2633,7 +2859,13 @@ static int CmdHFiClassView(const char *Cmd) { PrintAndLogEx(NORMAL, ""); print_picopass_header((picopass_hdr_t *) dump); print_picopass_info((picopass_hdr_t *) dump); - printIclassDumpContents(dump, startblock, endblock, bytes_read); + printIclassDumpContents(dump, startblock, endblock, bytes_read, dense_output); + iclass_decode_credentials(dump); + + if (verbose) { + printIclassSIO(dump); + } + free(dump); return PM3_SUCCESS; } @@ -2793,7 +3025,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { if (givenCSN == false) { uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (select_only(csn, CCNR, true) == false) { + if (select_only(csn, CCNR, true, false) == false) { DropField(); return PM3_ESOFT; } @@ -2991,6 +3223,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key (raw)"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3002,6 +3235,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { bool use_credit_key = arg_get_lit(ctx, 2); bool use_elite = arg_get_lit(ctx, 3); bool use_raw = arg_get_lit(ctx, 4); + bool shallow_mod = arg_get_lit(ctx, 5); CLIParserFree(ctx); @@ -3031,7 +3265,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { bool got_csn = false; for (uint8_t i = 0; i < ICLASS_AUTH_RETRY; i++) { - got_csn = select_only(CSN, CCNR, false); + got_csn = select_only(CSN, CCNR, false, shallow_mod); if (got_csn == false) PrintAndLogEx(WARNING, "one more try"); else @@ -3113,6 +3347,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { } packet->use_credit_key = use_credit_key; packet->count = curr_chunk_cnt; + packet->shallow_mod = shallow_mod; // copy chunk of pre calculated macs to packet memcpy(packet->items, (pre + chunk_offset), (4 * curr_chunk_cnt)); @@ -3457,7 +3692,6 @@ void GenerateMacKeyFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elit for (int i = 0; i < iclass_tc; i++) pthread_join(threads[i], NULL); - PrintAndLogEx(NORMAL, ""); } // print diversified keys @@ -3600,7 +3834,7 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass encode", - "Encode binary wiegand to block 7\n" + "Encode binary wiegand to block 7,8,9\n" "Use either --bin or --wiegand/--fc/--cn", "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337\n" "hf iclass encode --fc 31 --cn 337 --ki 0 -> FC 31 CN 337\n" @@ -3618,6 +3852,8 @@ static int CmdHFiClassEncode(const char *Cmd) { arg_u64_0(NULL, "fc", "", "facility code"), arg_u64_0(NULL, "cn", "", "card number"), arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0("v", NULL, "verbose (print encoded blocks)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3662,6 +3898,9 @@ static int CmdHFiClassEncode(const char *Cmd) { int format_len = 0; CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)format, sizeof(format), &format_len); + bool shallow_mod = arg_get_lit(ctx, 10); + bool verbose = arg_get_lit(ctx, 11); + CLIParserFree(ctx); if ((rawkey + elite) > 1) { @@ -3752,8 +3991,12 @@ static int CmdHFiClassEncode(const char *Cmd) { return PM3_ESOFT; } + // iceman: only for formats w length smaller than 37. + // Needs a check. + + // increase length to allow setting bit just above real data + packed.Length++; // Set sentinel bit - packed.Length++;// increase length to allow setting bit just above real data set_bit_by_position(&packed, true, 0); #ifdef HOST_LITTLE_ENDIAN @@ -3776,16 +4019,27 @@ static int CmdHFiClassEncode(const char *Cmd) { iclass_encrypt_block_data(credential + 24, enc_key); } + if (verbose) { + for (uint8_t i = 0; i < 4; i++) { + PrintAndLogEx(INFO, "Block %d/0x0%x -> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); + } + } + + if (!g_session.pm3_present) { + PrintAndLogEx(ERR, "Device offline\n"); + return PM3_EFAILED; + } + int isok = PM3_SUCCESS; // write for (uint8_t i = 0; i < 4; i++) { - isok = iclass_write_block(6 + i, credential + (i * 8), key, use_credit_key, elite, rawkey, false, false, auth); + isok = iclass_write_block(6 + i, credential + (i * 8), NULL, key, use_credit_key, elite, rawkey, false, false, auth, shallow_mod); switch (isok) { case PM3_SUCCESS: PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); break; default: - PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); + PrintAndLogEx(INFO, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); break; } } @@ -3950,11 +4204,16 @@ int CmdHFiClass(const char *Cmd) { // DESFIRE| | | //} -int info_iclass(void) { +int info_iclass(bool shallow_mod) { iclass_card_select_t payload = { .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) }; + + if (shallow_mod) { + payload.flags |= FLAG_ICLASS_READER_SHALLOW_MOD; + } + clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload, sizeof(iclass_card_select_t)); @@ -4049,4 +4308,3 @@ int info_iclass(void) { return PM3_SUCCESS; } - diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index f66a09a5e..db242d496 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -24,9 +24,9 @@ int CmdHFiClass(const char *Cmd); -int info_iclass(void); -int read_iclass_csn(bool loop, bool verbose); -void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize); +int info_iclass(bool shallow_mod); +int read_iclass_csn(bool loop, bool verbose, bool shallow_mod); +void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize, bool dense_output); void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite); void GenerateMacFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite, uint8_t *keys, uint32_t keycnt, iclass_premac_t *list); diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index bc0cdf89a..93ca6a00a 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -215,7 +215,7 @@ static void jooki_printEx(uint8_t *b64, uint8_t *iv, uint8_t tid, uint8_t fid, u PrintAndLogEx(INFO, "NDEF raw..... %s", sprint_hex_inrow(ndefmsg, sizeof(ndefmsg))); if (verbose) { - int res = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg)); + int res = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg), verbose); if (res != PM3_SUCCESS) { NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), verbose); } @@ -273,7 +273,7 @@ static int jooki_selftest(void) { jooki_create_ndef(b64, ndefmsg); PrintAndLogEx(INFO, "NDEF raw .... %s", sprint_hex(ndefmsg, sizeof(ndefmsg))); - int status = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg)); + int status = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true); if (status != PM3_SUCCESS) { status = NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true); } diff --git a/client/src/cmdhfksx6924.c b/client/src/cmdhfksx6924.c index d405459c9..7616ca766 100644 --- a/client/src/cmdhfksx6924.c +++ b/client/src/cmdhfksx6924.c @@ -46,6 +46,7 @@ #include "emv/tlv.h" #include "iso7816/apduinfo.h" #include "cmdhf14a.h" +#include "protocols.h" // ISO7816 APDU return codes static int CmdHelp(const char *Cmd); @@ -69,7 +70,7 @@ static int CmdHFKSX6924Balance(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("k", "keep", "keep field ON for next command"), - arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -102,7 +103,7 @@ static int CmdHFKSX6924Info(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("k", "keep", "keep field ON for next command"), - arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -126,7 +127,7 @@ static int CmdHFKSX6924Info(const char *Cmd) { return res; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { if (sw) { PrintAndLogEx(INFO, "Not a KS X 6924 card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); @@ -224,7 +225,7 @@ static int CmdHFKSX6924Select(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -245,15 +246,15 @@ static int CmdHFKSX6924Select(const char *Cmd) { static int CmdHFKSX6924Initialize(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf ksx6924 initialize", - "Perform transaction initialization (mpda)", - "hf ksx6924 initialize 000003e8 -> mpda\n"); + CLIParserInit(&ctx, "hf ksx6924 init", + "Perform transaction initialization with Mpda (Money of Purchase Transaction)", + "hf ksx6924 init 000003e8 -> Mpda\n"); void *argtable[] = { arg_param_begin, arg_lit0("k", "keep", "keep field ON for next command"), - arg_lit0("a", "apdu", "show APDU reqests and responses"), - arg_str1(NULL, NULL, "", NULL), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_str1(NULL, NULL, "", NULL), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -269,7 +270,7 @@ static int CmdHFKSX6924Initialize(const char *Cmd) { SetAPDULogging(APDULogging); if (datalen != 4) { - PrintAndLogEx(WARNING, "Mpda parameter must be 4 byte long (eg: 000003e8)"); + PrintAndLogEx(WARNING, "Mpda parameter must be 4 bytes long (eg: 000003e8)"); return PM3_EINVARG; } @@ -278,15 +279,23 @@ static int CmdHFKSX6924Initialize(const char *Cmd) { goto end; } - PrintAndLogEx(SUCCESS, "Initialize Card : Mpda -> %02X %02X %02X %02X", data[0], data[1], data[2], data[3]); - - uint8_t response[25] = {0}; - if (KSX6924InitializeCard(data[0], data[1], data[2], data[3], response)) { - PrintAndLogEx(SUCCESS, "Response : %s", sprint_hex(response, sizeof(response))); - } else { - PrintAndLogEx(FAILED, "Initialize Card Error"); + uint8_t resp[APDU_RES_LEN] = {0}; + size_t resp_len = 0; + if (KSX6924InitializeCard(data[0], data[1], data[2], data[3], resp, &resp_len) == false) { + goto end; } + uint8_t *r = resp; + struct ksx6924_initialize_card_response initCardResponse; + bool ret = KSX6924ParseInitializeCardResponse(r, resp_len, &initCardResponse); + + if (!ret) { + PrintAndLogEx(FAILED, "Error parsing KS X 6924 initialize card response"); + goto end; + } + + KSX6924PrintInitializeCardResponse(&initCardResponse); + end: if (keep == false) { DropField(); @@ -305,7 +314,7 @@ static int CmdHFKSX6924PRec(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("k", "keep", "keep field ON for next command"), - arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), arg_str1(NULL, NULL, "", NULL), arg_param_end }; @@ -348,11 +357,11 @@ end: static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"balance", CmdHFKSX6924Balance, IfPm3Iso14443a, "Get current purse balance"}, - {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Get info about a KS X 6924 (T-Money, Snapper+) transit card"}, - {"initialize", CmdHFKSX6924Initialize, IfPm3Iso14443a, "Perform transaction initialization (Mpda)"}, - {"prec", CmdHFKSX6924PRec, IfPm3Iso14443a, "Send proprietary get record command (CLA=90, INS=4C)"}, {"select", CmdHFKSX6924Select, IfPm3Iso14443a, "Select application, and leave field up"}, + {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Get info about a KS X 6924 (T-Money, Snapper+) transit card"}, + {"balance", CmdHFKSX6924Balance, IfPm3Iso14443a, "Get current purse balance"}, + {"init", CmdHFKSX6924Initialize, IfPm3Iso14443a, "Perform transaction initialization with Mpda"}, + {"prec", CmdHFKSX6924PRec, IfPm3Iso14443a, "Send proprietary get record command (CLA=90, INS=4C)"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index f501dcb91..02a43e0aa 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -34,6 +34,7 @@ static int CmdHelp(const char *Cmd); #define LEGIC_PRIME_MIM256 256 #define LEGIC_PRIME_MIM1024 1024 #define LEGIC_BLOCK_SIZE 8 +#define LEGIC_PACKET_SIZE (PM3_CMD_DATA_SIZE - sizeof(legic_packet_t)) static bool legic_xor(uint8_t *data, uint16_t cardsize) { @@ -56,54 +57,28 @@ static bool legic_xor(uint8_t *data, uint16_t cardsize) { return true; } -/* - * Output BigBuf and deobfuscate LEGIC RF tag data. - * This is based on information given in the talk held - * by Henryk Ploetz and Karsten Nohl at 26c3 - */ -static int CmdLegicInfo(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf legic info", - "Gets information from a LEGIC Prime tag like systemarea, user areas, etc", - "hf legic info"); - - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - +static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buffer) { int i = 0, k = 0, segmentNum = 0, segment_len = 0, segment_flag = 0; int crc = 0, wrp = 0, wrc = 0; uint8_t stamp_len = 0; - uint16_t datalen = 0; char token_type[6] = {0, 0, 0, 0, 0, 0}; int dcf = 0; int bIsSegmented = 0; + int return_value = PM3_SUCCESS; - // tagtype - legic_card_select_t card; - if (legic_get_type(&card) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Failed to identify tagtype"); - return PM3_ESOFT; + if (!(card_size == LEGIC_PRIME_MIM22 || card_size == LEGIC_PRIME_MIM256 || card_size == LEGIC_PRIME_MIM1024)) { + PrintAndLogEx(FAILED, "Bytebuffer is not any known legic card size! (MIM22, MIM256, MIM1024)"); + return_value = PM3_EFAILED; + return PM3_EFAILED; } - PrintAndLogEx(SUCCESS, "Reading full tag memory of " _YELLOW_("%d") " bytes...", card.cardsize); - - // allocate receiver buffer - uint8_t *data = calloc(card.cardsize, sizeof(uint8_t)); + // copy input buffer into newly allocated buffer, because the existing code mutates the data inside. + uint8_t *data = calloc(card_size, sizeof(uint8_t)); if (!data) { PrintAndLogEx(WARNING, "Cannot allocate memory"); return PM3_EMALLOC; } - - int status = legic_read_mem(0, card.cardsize, 0x55, data, &datalen); - if (status != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Failed reading memory"); - free(data); - return status; - } + memcpy(data, input_buffer, card_size); // Output CDF System area (9 bytes) plus remaining header area (12 bytes) crc = data[4]; @@ -216,9 +191,10 @@ static int CmdLegicInfo(const char *Cmd) { uint32_t segCalcCRC = 0; uint32_t segCRC = 0; - // Not Data card? - if (dcf > 60000) + // Not a data card by dcf or too small to contain data (MIM22)? + if (dcf > 60000 || card_size == LEGIC_PRIME_MIM22) { goto out; + } PrintAndLogEx(SUCCESS, _CYAN_("ADF: User Area")); PrintAndLogEx(NORMAL, "------------------------------------------------------"); @@ -230,6 +206,13 @@ static int CmdLegicInfo(const char *Cmd) { // decode segments for (segmentNum = 1; segmentNum < 128; segmentNum++) { + // for decoding the segment header we need at least 4 bytes left in buffer + if ((i + 4) > card_size) { + PrintAndLogEx(FAILED, "Cannot read segment header, because the input buffer is too small. " + "Please check that the data is correct and properly aligned. "); + return_value = PM3_EOUTOFBOUND; + goto out; + } segment_len = ((data[i + 1] ^ crc) & 0x0f) * 256 + (data[i] ^ crc); segment_flag = ((data[i + 1] ^ crc) & 0xf0) >> 4; wrp = (data[i + 2] ^ crc); @@ -276,6 +259,14 @@ static int CmdLegicInfo(const char *Cmd) { i += 5; + // for printing the complete segment we need at least wrc + wrp_len + remain_seg_payload_len bytes + if ((i + wrc + wrp_len + remain_seg_payload_len) > card_size) { + PrintAndLogEx(FAILED, "Cannot read segment body, because the input buffer is too small. " + "Please check that the data is correct and properly aligned. "); + return_value = PM3_EOUTOFBOUND; + goto out; + } + if (hasWRC) { PrintAndLogEx(SUCCESS, "\nWRC protected area: (I %d | K %d| WRC %d)", i, k, wrc); PrintAndLogEx(NORMAL, "\nrow | data"); @@ -329,7 +320,6 @@ static int CmdLegicInfo(const char *Cmd) { } // end for loop } else { - // Data start point on unsegmented cards i = 8; @@ -339,7 +329,7 @@ static int CmdLegicInfo(const char *Cmd) { bool hasWRC = (wrc > 0); bool hasWRP = (wrp > wrc); int wrp_len = (wrp - wrc); - int remain_seg_payload_len = (card.cardsize - 22 - wrp); + int remain_seg_payload_len = (card_size - 22 - wrp); PrintAndLogEx(SUCCESS, "Unsegmented card - WRP: %02u, WRC: %02u, RD: %01u", wrp, @@ -347,6 +337,14 @@ static int CmdLegicInfo(const char *Cmd) { (data[7] & 0x80) >> 7 ); + // for printing the complete segment we need at least wrc + wrp_len + remain_seg_payload_len bytes + if ((i + wrc + wrp_len + remain_seg_payload_len) > card_size) { + PrintAndLogEx(FAILED, "Cannot read segment body, because the input buffer is too small. " + "Please check that the data is correct and properly aligned. "); + return_value = PM3_EOUTOFBOUND; + goto out; + } + if (hasWRC) { PrintAndLogEx(SUCCESS, "WRC protected area: (I %d | WRC %d)", i, wrc); PrintAndLogEx(NORMAL, "\nrow | data"); @@ -385,6 +383,55 @@ static int CmdLegicInfo(const char *Cmd) { } out: + free(data); + return (return_value); +} + +/* + * Output BigBuf and deobfuscate LEGIC RF tag data. + * This is based on information given in the talk held + * by Henryk Ploetz and Karsten Nohl at 26c3 + */ +static int CmdLegicInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic info", + "Gets information from a LEGIC Prime tag like systemarea, user areas, etc", + "hf legic info"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + uint16_t datalen = 0; + + // tagtype + legic_card_select_t card; + if (legic_get_type(&card) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Failed to identify tagtype"); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Reading full tag memory of " _YELLOW_("%d") " bytes...", card.cardsize); + + // allocate receiver buffer + uint8_t *data = calloc(card.cardsize, sizeof(uint8_t)); + if (!data) { + PrintAndLogEx(WARNING, "Cannot allocate memory"); + return PM3_EMALLOC; + } + + int status = legic_read_mem(0, card.cardsize, 0x55, data, &datalen); + if (status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Failed reading memory"); + free(data); + return status; + } + + decode_and_print_memory(card.cardsize, data); + free(data); return PM3_SUCCESS; } @@ -737,9 +784,9 @@ void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { // fast push mode g_conn.block_after_ACK = true; - for (size_t i = offset; i < numofbytes; i += (PM3_CMD_DATA_SIZE - sizeof(legic_packet_t))) { + for (size_t i = offset; i < numofbytes; i += LEGIC_PACKET_SIZE) { - size_t len = MIN((numofbytes - i), (PM3_CMD_DATA_SIZE - sizeof(legic_packet_t))); + size_t len = MIN((numofbytes - i), LEGIC_PACKET_SIZE); if (len == numofbytes - i) { // Disable fast mode on last packet g_conn.block_after_ACK = false; @@ -943,9 +990,9 @@ static int CmdLegicRestore(const char *Cmd) { // transfer to device PacketResponseNG resp; // 7 = skip UID bytes and MCC - for (size_t i = 7; i < bytes_read; i += PM3_CMD_DATA_SIZE) { + for (size_t i = 7; i < bytes_read; i += LEGIC_PACKET_SIZE) { - size_t len = MIN((bytes_read - i), PM3_CMD_DATA_SIZE); + size_t len = MIN((bytes_read - i), LEGIC_PACKET_SIZE); if (len == bytes_read - i) { // Disable fast mode on last packet g_conn.block_after_ACK = false; @@ -1173,6 +1220,41 @@ static int CmdLegicEView(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLegicEInfo(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic einfo", + "It decodes and displays emulator memory", + "hf legic einfo\n" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + size_t card_size = LEGIC_PRIME_MIM256; + + uint8_t *dump = calloc(card_size, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "downloading emulator memory"); + if (GetFromDevice(BIG_BUF_EML, dump, card_size, 0, NULL, 0, NULL, 2500, false) == false) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(dump); + return PM3_ETIMEOUT; + } + + decode_and_print_memory(card_size, dump); + + free(dump); + return PM3_SUCCESS; +} + static int CmdLegicWipe(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic wipe", @@ -1209,11 +1291,11 @@ static int CmdLegicWipe(const char *Cmd) { // transfer to device PacketResponseNG resp; - for (size_t i = 7; i < card.cardsize; i += PM3_CMD_DATA_SIZE) { + for (size_t i = 7; i < card.cardsize; i += LEGIC_PACKET_SIZE) { PrintAndLogEx(NORMAL, "." NOLF); - size_t len = MIN((card.cardsize - i), PM3_CMD_DATA_SIZE); + size_t len = MIN((card.cardsize - i), LEGIC_PACKET_SIZE); if (len == card.cardsize - i) { // Disable fast mode on last packet g_conn.block_after_ACK = false; @@ -1285,6 +1367,10 @@ static int CmdLegicView(const char *Cmd) { PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); print_hex_break(dump, bytes_read, 16); + + PrintAndLogEx(NORMAL, ""); + decode_and_print_memory(bytes_read, dump); + free(dump); return PM3_SUCCESS; } @@ -1305,9 +1391,10 @@ static command_t CommandTable[] = { {"eload", CmdLegicELoad, IfPm3Legicrf, "Load binary dump to emulator memory"}, {"esave", CmdLegicESave, IfPm3Legicrf, "Save emulator memory to binary file"}, {"eview", CmdLegicEView, IfPm3Legicrf, "View emulator memory"}, + {"einfo", CmdLegicEInfo, IfPm3Legicrf, "Display deobfuscated and decoded emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("utils") " ---------------------"}, {"crc", CmdLegicCalcCrc, AlwaysAvailable, "Calculate Legic CRC over given bytes"}, - {"view", CmdLegicView, AlwaysAvailable, "Display content from tag dump file"}, + {"view", CmdLegicView, AlwaysAvailable, "Display deobfuscated and decoded content from tag dump file"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 89acdb0e1..7207e50f9 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -176,20 +176,35 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i if (is_response == false) { if ((gs_ntag_i2c_state == 1) && (cmdsize == 6) && (memcmp(cmd + 1, "\x00\x00\x00", 3) == 0)) { - snprintf(exp, size, "SECTOR(%d)", cmd[0]); + snprintf(exp, size, "SECTOR(" _MAGENTA_("%d") ")", cmd[0]); gs_ntag_i2c_state = 0; return PM3_SUCCESS; } if (cmdsize >= 7 && cmd[0] == ECP_HEADER) { - // Second byte of ECP frame indicates its version - // Version 0x01 payload is 7 bytes long (including crc) - // Version 0x02 payload is 15 bytes long (including crc) + // Byte 0 is a header + // Byte 1 indicates format version + // Version 0x01 format is 7 bytes long (including crc) + // Version 0x02 format is at least 7 bytes long (including crc). First 4 bits of byte 2 define extra payload length if (cmd[1] == 0x01 && cmdsize == 7) { snprintf(exp, size, "ECP1"); return PM3_SUCCESS; - } else if (cmd[1] == 0x02 && cmdsize == 15) { - snprintf(exp, size, "ECP2"); + } else if (cmd[1] == 0x02 && cmdsize == (cmd[2] & 0x0f) + 7) { + // Byte 3 is the reader type + switch (cmd[3]) { + case 0x01: + snprintf(exp, size, "ECP2 (Transit)"); + break; + case 0x02: + snprintf(exp, size, "ECP2 (Access)"); + break; + case 0x03: + snprintf(exp, size, "ECP2 (Identity)"); + break; + default: + snprintf(exp, size, "ECP2"); + break; + } return PM3_SUCCESS; } } @@ -245,10 +260,10 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "REQA"); break; case ISO14443A_CMD_READBLOCK: - snprintf(exp, size, "READBLOCK(%d)", cmd[1]); + snprintf(exp, size, "READBLOCK(" _MAGENTA_("%d") ")", cmd[1]); break; case ISO14443A_CMD_WRITEBLOCK: - snprintf(exp, size, "WRITEBLOCK(%d)", cmd[1]); + snprintf(exp, size, "WRITEBLOCK(" _MAGENTA_("%d") ")", cmd[1]); break; case ISO14443A_CMD_HALT: snprintf(exp, size, "HALT"); @@ -264,10 +279,10 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "OPTIONAL TIMESLOT"); break; case MIFARE_CMD_INC: - snprintf(exp, size, "INC(%d)", cmd[1]); + snprintf(exp, size, "INC(" _MAGENTA_("%d") ")", cmd[1]); break; case MIFARE_CMD_DEC: - snprintf(exp, size, "DEC(%d)", cmd[1]); + snprintf(exp, size, "DEC(" _MAGENTA_("%d") ")", cmd[1]); break; case MIFARE_CMD_RESTORE: @@ -278,7 +293,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "SELECT SECTOR"); gs_ntag_i2c_state = 1; } else { - snprintf(exp, size, "RESTORE(%d)", cmd[1]); + snprintf(exp, size, "RESTORE(" _MAGENTA_("%d") ")", cmd[1]); } } else { return PM3_ESOFT; @@ -286,11 +301,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i break; case MIFARE_CMD_TRANSFER: - snprintf(exp, size, "TRANSFER(%d)", cmd[1]); + snprintf(exp, size, "TRANSFER(" _MAGENTA_("%d") ")", cmd[1]); break; case MIFARE_AUTH_KEYA: { if (cmdsize > 3) { - snprintf(exp, size, "AUTH-A(%d)", cmd[1]); + snprintf(exp, size, "AUTH-A(" _MAGENTA_("%d") ")", cmd[1]); MifareAuthState = masNt; } else { // case MIFARE_ULEV1_VERSION : both 0x60. @@ -300,7 +315,18 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i } case MIFARE_AUTH_KEYB: { MifareAuthState = masNt; - snprintf(exp, size, "AUTH-B(%d)", cmd[1]); + snprintf(exp, size, "AUTH-B(" _MAGENTA_("%d") ")", cmd[1]); + break; + } + case MIFARE_MAGIC_GDM_AUTH_KEY: { + if (cmdsize > 3) { + snprintf(exp, size, "MAGIC AUTH (" _MAGENTA_("%d") ")", cmd[1]); + MifareAuthState = masNt; + } + break; + } + case MIFARE_MAGIC_GDM_WRITEBLOCK: { + snprintf(exp, size, "MAGIC WRITEBLOCK(" _MAGENTA_("%d") ")", cmd[1]); break; } case MIFARE_MAGICWUPC1: @@ -323,7 +349,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i // buffer too small to print the full key, // so we give just a hint that one of the default keys was used // snprintf(exp, size, "AUTH-2 KEY: " _YELLOW_("%s"), sprint_hex(gs_mfuc_key, 16)); - snprintf(exp, size, "AUTH-2 KEY: " _YELLOW_("%02x%02x%02x%02x..."), gs_mfuc_key[0], gs_mfuc_key[1], gs_mfuc_key[2], gs_mfuc_key[3]); + snprintf(exp, size, "AUTH-2 KEY: " _GREEN_("%02X%02X%02X%02X..."), gs_mfuc_key[0], gs_mfuc_key[1], gs_mfuc_key[2], gs_mfuc_key[3]); } else { snprintf(exp, size, "AUTH-2"); } @@ -334,36 +360,36 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i break; case MIFARE_ULEV1_AUTH: if (cmdsize == 7) - snprintf(exp, size, "PWD-AUTH KEY: " _YELLOW_("0x%02x%02x%02x%02x"), cmd[1], cmd[2], cmd[3], cmd[4]); + snprintf(exp, size, "PWD-AUTH KEY: " _GREEN_("0x%02X%02X%02X%02X"), cmd[1], cmd[2], cmd[3], cmd[4]); else snprintf(exp, size, "PWD-AUTH"); break; case MIFARE_ULEV1_FASTREAD : { if (cmdsize >= 3 && cmd[2] <= 0xE6) - snprintf(exp, size, "READ RANGE (%d-%d)", cmd[1], cmd[2]); + snprintf(exp, size, "READ RANGE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]); else // outside limits, useful for some tags... - snprintf(exp, size, "READ RANGE (%d-%d) (?)", cmd[1], cmd[2]); + snprintf(exp, size, "READ RANGE (" _MAGENTA_("%d-%d") ") (?)", cmd[1], cmd[2]); break; } case MIFARE_ULC_WRITE : { if (cmd[1] < 0x21) - snprintf(exp, size, "WRITEBLOCK(%d)", cmd[1]); + snprintf(exp, size, "WRITEBLOCK(" _MAGENTA_("%d") ")", cmd[1]); else // outside limits, useful for some tags... - snprintf(exp, size, "WRITEBLOCK(%d) (?)", cmd[1]); + snprintf(exp, size, "WRITEBLOCK(" _MAGENTA_("%d") ") (?)", cmd[1]); break; } case MIFARE_ULEV1_READ_CNT : { if (cmd[1] < 5) - snprintf(exp, size, "READ CNT(%d)", cmd[1]); + snprintf(exp, size, "READ CNT(" _MAGENTA_("%d") ")", cmd[1]); else snprintf(exp, size, "?"); break; } case MIFARE_ULEV1_INCR_CNT : { if (cmd[1] < 5) - snprintf(exp, size, "INCR(%d)", cmd[1]); + snprintf(exp, size, "INCR(" _MAGENTA_("%d") ")", cmd[1]); else snprintf(exp, size, "?"); break; @@ -372,7 +398,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "READ SIG"); break; case MIFARE_ULEV1_CHECKTEAR: - snprintf(exp, size, "CHK TEARING(%d)", cmd[1]); + snprintf(exp, size, "CHK TEARING(" _MAGENTA_("%d") ")", cmd[1]); break; case MIFARE_ULEV1_VCSL: snprintf(exp, size, "VCSL"); @@ -391,7 +417,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i } case NTAG_I2C_FASTWRITE: if (cmdsize == 69) - snprintf(exp, size, "FAST WRITE (%d - %d)", cmd[1], cmd[2]); + snprintf(exp, size, "FAST WRITE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]); else snprintf(exp, size, "?"); @@ -464,10 +490,17 @@ void annotateIclass(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool curr_state = PICO_NONE; break; case ICLASS_CMD_CHECK: - snprintf(exp, size, "CHECK"); curr_state = PICO_AUTH_MACS; memcpy(rmac, cmd + 1, 4); memcpy(tmac, cmd + 5, 4); + + uint8_t key[8]; + if (check_known_default(csn, epurse, rmac, tmac, key)) { + snprintf(exp, size, "CHECK ( %s )", sprint_hex_inrow(key, 8)); + } else { + snprintf(exp, size, "CHECK"); + } + break; case ICLASS_CMD_READ4: snprintf(exp, size, "READ4(%d)", cmd[1]); @@ -516,11 +549,7 @@ void annotateIclass(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool } else if (curr_state == PICO_AUTH_EPURSE) { memcpy(epurse, cmd, 8); } else if (curr_state == PICO_AUTH_MACS) { - - uint8_t key[8]; - if (check_known_default(csn, epurse, rmac, tmac, key)) { - snprintf(exp, size, "( " _GREEN_("%s") " )", sprint_hex_inrow(key, 8)); - } + snprintf(exp, size, _GREEN_("CHECK SUCCESS")); curr_state = PICO_NONE; } } @@ -868,7 +897,7 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "R-block NACK(%d)", (cmd[0] & 0x01)); } // I-block 000xCN1x - else if ((cmd[0] & 0xC0) == 0x00) { + else if (((cmd[0] & 0xC0) == 0x00) && (cmdsize > 2)) { // PCB [CID] [NAD] [INF] CRC CRC int pos = 1; @@ -881,18 +910,40 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { for (uint8_t i = 0; i < 2; i++, pos++) { bool found_annotation = true; + uint8_t *data = cmd + pos + 1; + // if the byte prior to the command is 90 the command is wrapped, so data starts 3 bytes later + if (i > 0 && cmd[pos - 1] == 0x90) { + data += 3; + } + uint8_t data_size = 0; + if (cmdsize > (data - cmd)) { + data_size = cmdsize - (data - cmd); + } + switch (cmd[pos]) { case MFDES_CREATE_APPLICATION: - snprintf(exp, size, "CREATE APPLICATION"); + if (data_size >= 3) { + snprintf(exp, size, "CREATE APPLICATION (appId %06x)", MemLeToUint3byte(data)); + } else { + snprintf(exp, size, "CREATE APPLICATION"); + } break; case MFDES_DELETE_APPLICATION: - snprintf(exp, size, "DELETE APPLICATION"); + if (data_size >= 3) { + snprintf(exp, size, "DELETE APPLICATION (appId %06x)", MemLeToUint3byte(data)); + } else { + snprintf(exp, size, "DELETE APPLICATION"); + } break; case MFDES_GET_APPLICATION_IDS: snprintf(exp, size, "GET APPLICATION IDS"); break; case MFDES_SELECT_APPLICATION: - snprintf(exp, size, "SELECT APPLICATION"); + if (data_size >= 3) { + snprintf(exp, size, "SELECT APPLICATION (appId %06x)", MemLeToUint3byte(data)); + } else { + snprintf(exp, size, "SELECT APPLICATION"); + } break; case MFDES_FORMAT_PICC: snprintf(exp, size, "FORMAT PICC"); @@ -901,31 +952,67 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "GET VERSION"); break; case MFDES_READ_DATA: - snprintf(exp, size, "READ DATA"); + if (data_size >= 7) { + snprintf(exp, size, "READ DATA (fileId %02x, offset %u, len %u)", data[0], MemLeToUint3byte(data + 1), MemLeToUint3byte(data + 4)); + } else { + snprintf(exp, size, "READ DATA"); + } break; case MFDES_WRITE_DATA: - snprintf(exp, size, "WRITE DATA"); + if (data_size >= 7) { + snprintf(exp, size, "WRITE DATA (fileId %02x, offset %u, len %u)", data[0], MemLeToUint3byte(data + 1), MemLeToUint3byte(data + 4)); + } else { + snprintf(exp, size, "WRITE DATA"); + } break; case MFDES_GET_VALUE: - snprintf(exp, size, "GET VALUE"); + if (data_size >= 1) { + snprintf(exp, size, "GET VALUE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "GET VALUE"); + } break; case MFDES_CREDIT: - snprintf(exp, size, "CREDIT"); + if (data_size >= 1) { + snprintf(exp, size, "CREDIT (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREDIT"); + } break; case MFDES_DEBIT: - snprintf(exp, size, "DEBIT"); + if (data_size >= 1) { + snprintf(exp, size, "DEBIT (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "DEBIT"); + } break; case MFDES_LIMITED_CREDIT: - snprintf(exp, size, "LIMITED CREDIT"); + if (data_size >= 1) { + snprintf(exp, size, "LIMITED CREDIT (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "LIMITED CREDIT"); + } break; case MFDES_WRITE_RECORD: - snprintf(exp, size, "WRITE RECORD"); + if (data_size >= 7) { + snprintf(exp, size, "WRITE RECORD (fileId %02x, offset %u, len %u)", data[0], MemLeToUint3byte(data + 1), MemLeToUint3byte(data + 4)); + } else { + snprintf(exp, size, "WRITE RECORD"); + } break; case MFDES_READ_RECORDS: - snprintf(exp, size, "READ RECORDS"); + if (data_size >= 7) { + snprintf(exp, size, "READ RECORDS (fileId %02x, offset %u, len %u)", data[0], MemLeToUint3byte(data + 1), MemLeToUint3byte(data + 4)); + } else { + snprintf(exp, size, "READ RECORDS"); + } break; case MFDES_CLEAR_RECORD_FILE: - snprintf(exp, size, "CLEAR RECORD FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CLEAR RECORD FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CLEAR RECORD FILE"); + } break; case MFDES_COMMIT_TRANSACTION: snprintf(exp, size, "COMMIT TRANSACTION"); @@ -946,57 +1033,87 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "GET ISOFILE IDS"); break; case MFDES_GET_FILE_SETTINGS: - snprintf(exp, size, "GET FILE SETTINGS"); + if (data_size >= 1) { + snprintf(exp, size, "GET FILE SETTINGS (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "GET FILE SETTINGS"); + } break; case MFDES_CHANGE_FILE_SETTINGS: - snprintf(exp, size, "CHANGE FILE SETTINGS"); + if (data_size >= 1) { + snprintf(exp, size, "CHANGE FILE SETTINGS (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CHANGE FILE SETTINGS"); + } break; case MFDES_CREATE_STD_DATA_FILE: - snprintf(exp, size, "CREATE STD DATA FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CREATE STD DATA FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREATE STD DATA FILE"); + } break; case MFDES_CREATE_BACKUP_DATA_FILE: - snprintf(exp, size, "CREATE BACKUP DATA FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CREATE BACKUP DATA FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREATE BACKUP DATA FILE"); + } break; case MFDES_CREATE_VALUE_FILE: - snprintf(exp, size, "CREATE VALUE FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CREATE VALUE FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREATE VALUE FILE"); + } break; case MFDES_CREATE_LINEAR_RECORD_FILE: - snprintf(exp, size, "CREATE LINEAR RECORD FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CREATE LINEAR RECORD FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREATE LINEAR RECORD FILE"); + } break; case MFDES_CREATE_CYCLIC_RECORD_FILE: - snprintf(exp, size, "CREATE CYCLIC RECORD FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CREATE CYCLIC RECORD FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREATE CYCLIC RECORD FILE"); + } break; case MFDES_CREATE_TRANS_MAC_FILE: - snprintf(exp, size, "CREATE TRANSACTION MAC FILE"); + if (data_size >= 1) { + snprintf(exp, size, "CREATE TRANSACTION MAC FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "CREATE TRANSACTION MAC FILE"); + } break; case MFDES_DELETE_FILE: - snprintf(exp, size, "DELETE FILE"); + if (data_size >= 1) { + snprintf(exp, size, "DELETE FILE (fileId %02x)", data[0]); + } else { + snprintf(exp, size, "DELETE FILE"); + } break; case MFDES_AUTHENTICATE: - if (cmdsize > 6) { - //Assume wrapped - snprintf(exp, size, "AUTH NATIVE (keyNo %d)", cmd[pos + 4]); + if (data_size >= 1) { + snprintf(exp, size, "AUTH NATIVE (keyNo %u)", data[0]); } else { - //Assume unwrapped - snprintf(exp, size, "AUTH NATIVE (keyNo %d)", cmd[pos + 1]); + snprintf(exp, size, "AUTH NATIVE"); } break; // AUTHENTICATE_NATIVE case MFDES_AUTHENTICATE_ISO: - if (cmdsize > 6) { - //Assume wrapped - snprintf(exp, size, "AUTH ISO (keyNo %d)", cmd[pos + 4]); + if (data_size >= 1) { + snprintf(exp, size, "AUTH ISO (keyNo %u)", data[0]); } else { - //Assume unwrapped - snprintf(exp, size, "AUTH ISO (keyNo %d)", cmd[pos + 1]); + snprintf(exp, size, "AUTH ISO"); } break; // AUTHENTICATE_STANDARD case MFDES_AUTHENTICATE_AES: - if (cmdsize > 6) { - //Assume wrapped - snprintf(exp, size, "AUTH AES (keyNo %d)", cmd[pos + 4]); + if (data_size >= 1) { + snprintf(exp, size, "AUTH AES (keyNo %u)", data[0]); } else { - //Assume unwrapped - snprintf(exp, size, "AUTH AES (keyNo %d)", cmd[pos + 1]); + snprintf(exp, size, "AUTH AES"); } break; case MFDES_AUTHENTICATE_EV2F: @@ -1012,10 +1129,18 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "GET KEY SETTINGS"); break; case MFDES_CHANGE_KEY: - snprintf(exp, size, "CHANGE KEY"); + if (data_size >= 1) { + snprintf(exp, size, "CHANGE KEY (keyNo %u)", data[0]); + } else { + snprintf(exp, size, "CHANGE KEY"); + } break; case MFDES_GET_KEY_VERSION: - snprintf(exp, size, "GET KEY VERSION"); + if (data_size >= 1) { + snprintf(exp, size, "GET KEY VERSION (keyNo %u)", data[0]); + } else { + snprintf(exp, size, "GET KEY VERSION"); + } break; case MFDES_ADDITIONAL_FRAME: snprintf(exp, size, "AUTH FRAME / NEXT FRAME"); @@ -1456,6 +1581,10 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, // get UID if (MifareAuthState == masNone) { + if (isResponse && cmdsize == 5) { + ClearAuthData(); + AuthData.uid = bytes_to_num(&cmd[0], 4); + } if (cmdsize == 9 && cmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && cmd[1] == 0x70) { ClearAuthData(); AuthData.uid = bytes_to_num(&cmd[2], 4); diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index feed30cf3..b5a8d1206 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -201,6 +201,31 @@ static int lto_rdbl(uint8_t blk, uint8_t *block_response, uint8_t *block_cnt_res return PM3_SUCCESS; } +/* +static int lto_rdbl_ext(uint16_t blk, uint8_t *block_response, uint8_t *block_cnt_response, bool verbose) { + + if (blk && 0x) { + blk &= 0xFE; + } + + uint16_t resp_len = 18; + uint8_t rdbl_ext_cmd[] = {0x21 , blk & 0xFF, (blk >> 8) & 0xFF}; + uint8_t rdbl_cnt_cmd[] = {0x80}; + + int status = lto_send_cmd_raw(rdbl_ext_cmd, sizeof(rdbl_ext_cmd), block_response, &resp_len, true, false, verbose); + if (status == PM3_ETIMEOUT || status == PM3_ESOFT) { + return PM3_EWRONGANSWER; // READ BLOCK failed + } + + status = lto_send_cmd_raw(rdbl_cnt_cmd, sizeof(rdbl_cnt_cmd), block_cnt_response, &resp_len, false, false, verbose); + if (status == PM3_ETIMEOUT || status == PM3_ESOFT) { + return PM3_EWRONGANSWER; // READ BLOCK CONTINUE failed + } + + return PM3_SUCCESS; +} +*/ + static int CmdHfLTOInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf lto info", @@ -360,6 +385,65 @@ static void lto_print_mmi(uint8_t *d) { // - Application Specific Data (1056b) // - Suspended Append Writes (128b) +static int CmdHFLTOReader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf lto reader", + "Act as a LTO-CM reader. Look for LTO-CM tags until Enter or the pm3 button is pressed", + "hf lto reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + return reader_lto(cm, true); +} + +int reader_lto(bool loop, bool verbose) { + + int ret = PM3_SUCCESS; + + do { + uint8_t serial[5] = {0}; + uint8_t serial_len = sizeof(serial); + uint8_t type_info[2] = {0}; + + lto_switch_off_field(); + lto_switch_on_field(); + clearCommandBuffer(); + + ret = lto_select(serial, serial_len, type_info, verbose); + if (loop) { + if (ret != PM3_SUCCESS) { + continue; + } + } + + if (ret == PM3_SUCCESS) { + + if (loop == false) { + PrintAndLogEx(NORMAL, ""); + } + + PrintAndLogEx(INFO, "UID......... " _GREEN_("%s"), sprint_hex_inrow(serial, sizeof(serial))); + } + + } while (loop && kbd_enter_pressed() == false); + + lto_switch_off_field(); + return ret; +} + int infoLTO(bool verbose) { clearCommandBuffer(); @@ -372,6 +456,7 @@ int infoLTO(bool verbose) { int ret_val = lto_select(serial_number, serial_len, type_info, verbose); if (verbose == false) { if (ret_val == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "UID......... " _YELLOW_("%s"), sprint_hex_inrow(serial_number, sizeof(serial_number))); } lto_switch_off_field(); @@ -776,12 +861,13 @@ static int CmdHfLTRestore(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"dump", CmdHfLTODump, IfPm3Iso14443a, "Dump LTO-CM tag to file"}, - {"restore", CmdHfLTRestore, IfPm3Iso14443a, "Restore dump file to LTO-CM tag"}, - {"info", CmdHfLTOInfo, IfPm3Iso14443a, "Tag information"}, - {"rdbl", CmdHfLTOReadBlock, IfPm3Iso14443a, "Read block"}, - {"wrbl", CmdHfLTOWriteBlock, IfPm3Iso14443a, "Write block"}, + {"dump", CmdHfLTODump, IfPm3Iso14443a, "Dump LTO-CM tag to file"}, + {"info", CmdHfLTOInfo, IfPm3Iso14443a, "Tag information"}, {"list", CmdHfLTOList, AlwaysAvailable, "List LTO-CM history"}, + {"rdbl", CmdHfLTOReadBlock, IfPm3Iso14443a, "Read block"}, + {"reader", CmdHFLTOReader, IfPm3Iso14443a, "Act like a LTO-CM reader"}, + {"restore", CmdHfLTRestore, IfPm3Iso14443a, "Restore dump file to LTO-CM tag"}, + {"wrbl", CmdHfLTOWriteBlock, IfPm3Iso14443a, "Write block"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhflto.h b/client/src/cmdhflto.h index beade8fa9..3c9b310a4 100644 --- a/client/src/cmdhflto.h +++ b/client/src/cmdhflto.h @@ -21,6 +21,7 @@ #include "common.h" +int reader_lto(bool loop, bool verbose); int infoLTO(bool verbose); int dumpLTO(uint8_t *dump, bool verbose); int restoreLTO(uint8_t *dump, bool verbose); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index b98e7bc1f..adcb39d13 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -19,32 +19,41 @@ #include "cmdhfmf.h" #include -#include "cmdparser.h" // command_t -#include "commonutil.h" // ARRAYLEN -#include "comms.h" // clearCommandBuffer +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // clearCommandBuffer #include "fileutils.h" #include "cmdtrace.h" -#include "mifare/mifaredefault.h" // mifare default key array -#include "cliparser.h" // argtable -#include "hardnested_bf_core.h" // SetSIMDInstr +#include "mifare/mifaredefault.h" // mifare default key array +#include "cliparser.h" // argtable +#include "hardnested_bf_core.h" // SetSIMDInstr #include "mifare/mad.h" #include "nfc/ndef.h" #include "protocols.h" -#include "util_posix.h" // msclock +#include "util_posix.h" // msclock #include "cmdhfmfhard.h" -#include "crapto1/crapto1.h" // prng_successor -#include "cmdhf14a.h" // exchange APDU +#include "crapto1/crapto1.h" // prng_successor +#include "cmdhf14a.h" // exchange APDU #include "crypto/libpcrypto.h" +#include "wiegand_formats.h" +#include "wiegand_formatutils.h" -#define MIFARE_4K_MAXBLOCK 256 -#define MIFARE_2K_MAXBLOCK 128 -#define MIFARE_1K_MAXBLOCK 64 -#define MIFARE_MINI_MAXBLOCK 20 +#define MIFARE_4K_MAXBLOCK 256 +#define MIFARE_2K_MAXBLOCK 128 +#define MIFARE_1K_MAXBLOCK 64 +#define MIFARE_MINI_MAXBLOCK 20 -#define MIFARE_MINI_MAXSECTOR 5 -#define MIFARE_1K_MAXSECTOR 16 -#define MIFARE_2K_MAXSECTOR 32 -#define MIFARE_4K_MAXSECTOR 40 +#define MIFARE_4K_MAXSECTOR 40 +#define MIFARE_2K_MAXSECTOR 32 +#define MIFARE_1K_MAXSECTOR 16 +#define MIFARE_MINI_MAXSECTOR 5 + +#define MIFARE_4K_MAX_BYTES 4096 +#define MIFARE_2K_MAX_BYTES 2048 +#define MIFARE_1K_MAX_BYTES 1024 +#define MIFARE_MINI_MAX_BYTES 320 + +#define MIFARE_KEY_SIZE 6 static int CmdHelp(const char *Cmd); @@ -113,14 +122,14 @@ static int GetHFMF14AUID(uint8_t *uid, int *uidlen) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { PrintAndLogEx(WARNING, "iso14443a card select failed"); DropField(); - return 0; + return PM3_ERFTRANS; } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t)); *uidlen = card.uidlen; - return 1; + return PM3_SUCCESS; } static char *GenerateFilename(const char *prefix, const char *suffix) { @@ -131,8 +140,8 @@ static char *GenerateFilename(const char *prefix, const char *suffix) { int uidlen = 0; char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t)); - GetHFMF14AUID(uid, &uidlen); - if (!uidlen) { + int res = GetHFMF14AUID(uid, &uidlen); + if (res != PM3_SUCCESS || !uidlen) { PrintAndLogEx(WARNING, "No tag found."); free(fptr); return NULL; @@ -172,7 +181,7 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) { PrintAndLogEx(INFO, " # | Access rights"); PrintAndLogEx(INFO, "----+-----------------------------------------------------------------"); - if (! mfValidateAccessConditions(&data[6])) { + if (mfValidateAccessConditions(&data[6]) == false) { PrintAndLogEx(WARNING, _RED_("Invalid Access Conditions")); } @@ -232,15 +241,15 @@ static bool mfc_value(const uint8_t *d, int32_t *val) { if (val) { *val = a; } - return (val_checks); + return val_checks; } -static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { + +static void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { if (blockno == 0) { PrintAndLogEx(INFO, "%3d | " _RED_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } else if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } else { - int32_t value = 0; if (verbose && mfc_value(d, &value)) { PrintAndLogEx(INFO, "%3d | " _CYAN_("%s") " %"PRIi32, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value); @@ -250,15 +259,37 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { } } +static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { + uint8_t sectorno = mfSectorNum(blockno); + + char secstr[6] = " "; + if (mfFirstBlockOfSector(sectorno) == blockno) { + sprintf(secstr, " %3d ", sectorno); + } + + if (blockno == 0) { + PrintAndLogEx(INFO, "%s| %3d | " _RED_("%s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } else if (mfIsSectorTrailer(blockno)) { + PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } else { + int32_t value = 0; + if (verbose && mfc_value(d, &value)) { + PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value); + } else { + PrintAndLogEx(INFO, "%s| %3d | %s ", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } + } +} + static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - PrintAndLogEx(INFO, "blk | data | ascii"); - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, " sec | blk | data | ascii"); + PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); for (uint16_t i = 0; i < n; i++) { mf_print_block(i, d + (i * MFBLOCK_SIZE), verbose); } - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); PrintAndLogEx(HINT, _CYAN_("cyan") " = value block with decoded value"); // MAD detection @@ -295,9 +326,9 @@ static int mf_print_keys(uint16_t n, uint8_t *d) { for (uint16_t i = 0; i < n; i++) { if (mfIsSectorTrailer(i)) { e_sector[mfSectorNum(i)].foundKey[0] = 1; - e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), 6); + e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); e_sector[mfSectorNum(i)].foundKey[1] = 1; - e_sector[mfSectorNum(i)].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, 6); + e_sector[mfSectorNum(i)].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, MIFARE_KEY_SIZE); } } printKeyTable(sectors, e_sector); @@ -334,6 +365,274 @@ static void mf_print_sector_hdr(uint8_t sector) { PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); } +static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, uint8_t *block) { + + uint8_t data[26]; + memcpy(data, key, MFKEY_SIZE); + memcpy(data + 10, block, MFBLOCK_SIZE); + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(FAILED, "Command execute timeout"); + return false; + } + + return (resp.oldarg[0] & 0xff); +} + +static void mf_analyse_acl(uint16_t n, uint8_t *d) { + + for (uint16_t b = 3; b < n; b++) { + if (mfIsSectorTrailer(b) == false) { + continue; + } + + uint8_t block[MFBLOCK_SIZE] = {0x00}; + memcpy(block, d + (b * MFBLOCK_SIZE), MFBLOCK_SIZE); + + // ensure access right isn't messed up. + if (mfValidateAccessConditions(&block[6]) == false) { + PrintAndLogEx(WARNING, "Invalid Access Conditions on sector " _YELLOW_("%u"), mfSectorNum(b)); + } + + // Warn if ACL is strict read-only + uint8_t bar = mfNumBlocksPerSector(mfSectorNum(b)); + for (uint8_t foo = 0; foo < bar; foo++) { + if (mfReadOnlyAccessConditions(foo, &block[6])) { + PrintAndLogEx(WARNING, _YELLOW_("s%u / b%u") " - Strict ReadOnly Access Conditions detected", mfSectorNum(b), b - bar + 1 + foo); + } + } + } +} + +/* + Sector trailer sanity checks. + Warn if ACL is strict read-only, or invalid ACL. +*/ +static int mf_analyse_st_block(uint8_t blockno, uint8_t *block, bool force) { + + if (mfIsSectorTrailer(blockno) == false) { + return PM3_SUCCESS; + } + + PrintAndLogEx(INFO, "Sector trailer (ST) write detected"); + + // ensure access right isn't messed up. + if (mfValidateAccessConditions(&block[6]) == false) { + PrintAndLogEx(WARNING, "Invalid Access Conditions detected, replacing with default values"); + memcpy(block + 6, "\xFF\x07\x80\x69", 4); + } + + bool ro_detected = false; + uint8_t bar = mfNumBlocksPerSector(mfSectorNum(blockno)); + for (uint8_t foo = 0; foo < bar; foo++) { + if (mfReadOnlyAccessConditions(foo, &block[6])) { + PrintAndLogEx(WARNING, "Strict ReadOnly Access Conditions on block " _YELLOW_("%u") " detected", blockno - bar + 1 + foo); + ro_detected = true; + } + } + if (ro_detected) { + if (force) { + PrintAndLogEx(WARNING, " --force override, continuing..."); + } else { + PrintAndLogEx(INFO, "Exiting, please run `" _YELLOW_("hf mf acl -d %s") "` to understand", sprint_hex_inrow(&block[6], 3)); + PrintAndLogEx(INFO, "Use `" _YELLOW_("--force") "` to override and write this data"); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(SUCCESS, "ST passed checks, continuing..."); + } + + return PM3_SUCCESS; +} + +/* Reads data from tag + * @param card: (output) card info + * @param carddata: (output) card data + * @param numSectors: size of the card + * @param keyFileName: filename containing keys or NULL. +*/ +static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t numSectors, char *keyfn){ + + // Select card to get UID/UIDLEN/ATQA/SAK information + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + uint64_t select_status = resp.oldarg[0]; + if (select_status == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + return PM3_SUCCESS; + } + + // store card info + memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + char *fptr = NULL; + if (keyfn == NULL || keyfn[0] == '\0') { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) + return PM3_ESOFT; + + keyfn = fptr ; + } + + PrintAndLogEx(INFO, "Using... %s", keyfn); + + size_t alen = 0, blen = 0; + uint8_t *keyA, *keyB; + if (loadFileBinaryKey(keyfn, "", (void**)&keyA, (void**)&keyB, &alen, &blen) != PM3_SUCCESS) { + free(fptr); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Reading sector access bits..."); + PrintAndLogEx(INFO, "." NOLF); + + uint8_t rights[40][4] = {0}; + + mf_readblock_t payload; + uint8_t current_key; + for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) { + current_key = MF_KEY_A; + for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + + payload.blockno = mfFirstBlockOfSector(sectorNo) + mfNumBlocksPerSector(sectorNo) - 1; + payload.keytype = current_key; + + memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); + + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) { + + uint8_t *data = resp.data.asBytes; + if (resp.status == PM3_SUCCESS) { + rights[sectorNo][0] = ((data[7] & 0x10) >> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0 + rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1 + rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2 + rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer + break; + } else if (tries == (MIFARE_SECTOR_RETRY / 2)) { // after half unsuccessful tries, give key B a go + PrintAndLogEx(WARNING, "\ntrying with key B instead..."); + current_key = MF_KEY_B; + PrintAndLogEx(INFO, "." NOLF); + } else if (tries == (MIFARE_SECTOR_RETRY - 1)) { // on last try set defaults + PrintAndLogEx(FAILED, "\ncould not get access rights for sector %2d. Trying with defaults...", sectorNo); + rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; + rights[sectorNo][3] = 0x01; + } + } else { + PrintAndLogEx(FAILED, "\ncommand execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo); + rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; + rights[sectorNo][3] = 0x01; + } + } + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Finished reading sector access bits"); + PrintAndLogEx(INFO, "Dumping all blocks from card..."); + + for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) { + for (uint8_t blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) { + bool received = false; + current_key = MF_KEY_A; + uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5; + if (rights[sectorNo][data_area] == 0x07) { // no key would work + PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d, skipping", sectorNo, blockNo); + continue; + } + + for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { + if (mfIsSectorTrailer(blockNo)) { + + // sector trailer. At least the Access Conditions can always be read with key A. + payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; + payload.keytype = current_key; + memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); + received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } else { + // data block. Check if it can be read with key A or key B + if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { + // only key B would work + payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; + payload.keytype = MF_KEY_B; + memcpy(payload.key, keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); + received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } else { + // key A would work + payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; + payload.keytype = current_key; + memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); + received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } + } + + if (received) { + if (resp.status == PM3_SUCCESS) { + // break the re-try loop + break; + } + if ((current_key == MF_KEY_A) && (tries == (MIFARE_SECTOR_RETRY / 2))) { + // Half the tries failed with key A. Swap for key B + current_key = MF_KEY_B; + + // clear out keyA since it failed. + memset(keyA + (sectorNo * MIFARE_KEY_SIZE), 0x00, MIFARE_KEY_SIZE); + } + } + } + + if (received) { + + if (resp.status == PM3_SUCCESS) { + + uint8_t *data = resp.data.asBytes; + + if (mfIsSectorTrailer(blockNo)) { + // sector trailer. Fill in the keys. + memcpy(data , keyA + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + memcpy(data + 10, keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + } + + memcpy(carddata + (MFBLOCK_SIZE * (mfFirstBlockOfSector(sectorNo) + blockNo)), data, MFBLOCK_SIZE); + PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo); + } else { + PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo); + } + } else { + PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo); + } + } + } + + free(fptr); + free(keyA); + free(keyB); + + PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); + return PM3_SUCCESS ; +} + static int CmdHF14AMfAcl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf acl", @@ -365,7 +664,7 @@ static int CmdHF14AMfAcl(const char *Cmd) { if (memcmp(acl, "\xFF\x07\x80", 3) == 0) { PrintAndLogEx(INFO, "ACL... " _GREEN_("%s") " (transport configuration)", sprint_hex(acl, sizeof(acl))); } - if (! mfValidateAccessConditions(acl)) { + if (mfValidateAccessConditions(acl) == false) { PrintAndLogEx(ERR, _RED_("Invalid Access Conditions, NEVER write these on a card!")); } PrintAndLogEx(NORMAL, ""); @@ -411,9 +710,9 @@ static int CmdHF14AMfDarkside(const char *Cmd) { t1 = msclock() - t1; switch (isOK) { - case -1 : - PrintAndLogEx(WARNING, "button pressed, aborted"); - return PM3_ESOFT; + case PM3_EOPABORTED: + PrintAndLogEx(WARNING, "button pressed or aborted via keyboard. aborted"); + return PM3_EOPABORTED; case -2 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)"); return PM3_ESOFT; @@ -424,9 +723,6 @@ static int CmdHF14AMfDarkside(const char *Cmd) { PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour"); return PM3_ESOFT; - case PM3_EOPABORTED : - PrintAndLogEx(WARNING, "aborted via keyboard"); - return PM3_EOPABORTED; default : PrintAndLogEx(SUCCESS, "found valid key: "_GREEN_("%012" PRIx64), key); break; @@ -444,7 +740,10 @@ static int CmdHF14AMfWrBl(const char *Cmd) { "Sector 0 / Block 0 - Manufacturer block\n" "When writing to block 0 you must use a VALID block 0 data (UID, BCC, SAK, ATQA)\n" "Writing an invalid block 0 means rendering your Magic GEN2 card undetectable. \n" - "Look in the magic_cards_notes.md file for help to resolve it.", + "Look in the magic_cards_notes.md file for help to resolve it.\n" + " \n" + "`--force` param is used to override warnings like bad ACL and BLOCK 0 writes.\n" + " if not specified, it will exit if detected", "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { @@ -452,10 +751,9 @@ static int CmdHF14AMfWrBl(const char *Cmd) { arg_int1(NULL, "blk", "", "block number"), arg_lit0("a", NULL, "input key type is key A (def)"), arg_lit0("b", NULL, "input key type is key B"), - arg_lit0(NULL, "force", "enforce block0 writes"), + arg_lit0(NULL, "force", "override warnings"), arg_str0("k", "key", "", "key, 6 hex bytes"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), - arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -490,6 +788,8 @@ static int CmdHF14AMfWrBl(const char *Cmd) { if (b > 255) { return PM3_EINVARG; } + + // BLOCK 0 detection if (b == 0 && force == false) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Targeting Sector 0 / Block 0 - Manufacturer block"); @@ -501,6 +801,10 @@ static int CmdHF14AMfWrBl(const char *Cmd) { uint8_t blockno = (uint8_t)b; + if (mf_analyse_st_block(blockno, block, force) != PM3_SUCCESS) { + return PM3_EINVARG; + } + PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); @@ -516,10 +820,12 @@ static int CmdHF14AMfWrBl(const char *Cmd) { return PM3_ETIMEOUT; } - uint8_t isok = resp.oldarg[0] & 0xff; - if (isok) { + int status = resp.oldarg[0]; + if (status) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify"); + } else if (status == PM3_ETEAROFF) { + return status; } else { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); // suggest the opposite keytype than what was used. @@ -573,7 +879,7 @@ static int CmdHF14AMfRdBl(const char *Cmd) { uint8_t sector = mfSectorNum(blockno); mf_print_sector_hdr(sector); - mf_print_block(blockno, data, verbose); + mf_print_block_one(blockno, data, verbose); if (verbose) { decode_print_st(blockno, data); } @@ -636,7 +942,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { mf_print_sector_hdr(sector); for (int i = 0; i < blocks; i++) { - mf_print_block(start + i, data + (i * MFBLOCK_SIZE), verbose); + mf_print_block_one(start + i, data + (i * MFBLOCK_SIZE), verbose); } if (verbose) { @@ -706,6 +1012,7 @@ static int CmdHF14AMfDump(const char *Cmd) { arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -722,7 +1029,7 @@ static int CmdHF14AMfDump(const char *Cmd) { bool m1 = arg_get_lit(ctx, 4); bool m2 = arg_get_lit(ctx, 5); bool m4 = arg_get_lit(ctx, 6); - + bool nosave = arg_get_lit(ctx, 7); CLIParserFree(ctx); uint64_t t1 = msclock(); @@ -736,232 +1043,68 @@ static int CmdHF14AMfDump(const char *Cmd) { } uint8_t numSectors = MIFARE_1K_MAXSECTOR; + uint16_t bytes = MIFARE_1K_MAX_BYTES; if (m0) { numSectors = MIFARE_MINI_MAXSECTOR; + bytes = MIFARE_MINI_MAX_BYTES; } else if (m1) { numSectors = MIFARE_1K_MAXSECTOR; + bytes = MIFARE_1K_MAX_BYTES; } else if (m2) { numSectors = MIFARE_2K_MAXSECTOR; - } else if (m4) { + bytes = MIFARE_2K_MAX_BYTES; + } else if (m4) { numSectors = MIFARE_4K_MAXSECTOR; + bytes = MIFARE_4K_MAX_BYTES; } else { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } - uint8_t sectorNo, blockNo; - uint8_t keyA[40][6]; - uint8_t keyB[40][6]; - uint8_t rights[40][4]; - uint8_t carddata[256][16]; - - FILE *f; - PacketResponseNG resp; - - char *fptr; - - // Select card to get UID/UIDLEN/ATQA/SAK information - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); - return PM3_ETIMEOUT; + // read card + iso14a_card_select_t card ; + uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t)); + if (mem == NULL) { + PrintAndLogEx(ERR, "failed to allocate memory"); + return PM3_EMALLOC; } - - uint64_t select_status = resp.oldarg[0]; - if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); - return select_status; - } - - // store card info - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - - if (keyFilename[0] == 0x00) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) - return PM3_ESOFT; - - strcpy(keyFilename, fptr); - free(fptr); - } - - if ((f = fopen(keyFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); - return PM3_EFILE; - } - - PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); - - // Read keys A from file - size_t bytes_read; - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - bytes_read = fread(keyA[sectorNo], 1, 6, f); - if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error."); - fclose(f); - return PM3_EFILE; - } - } - - // Read keys B from file - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - bytes_read = fread(keyB[sectorNo], 1, 6, f); - if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error."); - fclose(f); - return PM3_EFILE; - } - } - - fclose(f); - - PrintAndLogEx(INFO, "Reading sector access bits..."); - PrintAndLogEx(INFO, "." NOLF); - - uint8_t tries; - mf_readblock_t payload; - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { - PrintAndLogEx(NORMAL, "." NOLF); - fflush(stdout); - - payload.blockno = mfFirstBlockOfSector(sectorNo) + mfNumBlocksPerSector(sectorNo) - 1; - payload.keytype = MF_KEY_A; - memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - - if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) { - - uint8_t *data = resp.data.asBytes; - if (resp.status == PM3_SUCCESS) { - rights[sectorNo][0] = ((data[7] & 0x10) >> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0 - rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1 - rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2 - rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer - break; - } else if (tries == 2) { // on last try set defaults - PrintAndLogEx(FAILED, "\ncould not get access rights for sector %2d. Trying with defaults...", sectorNo); - rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; - rights[sectorNo][3] = 0x01; - } - } else { - PrintAndLogEx(FAILED, "\ncommand execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo); - rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; - rights[sectorNo][3] = 0x01; - } - } - } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Finished reading sector access bits"); - PrintAndLogEx(INFO, "Dumping all blocks from card..."); - - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - for (blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) { - bool received = false; - - for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { - if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A. - - payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; - payload.keytype = MF_KEY_A; - memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); - } else { // data block. Check if it can be read with key A or key B - uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5; - if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work - - payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; - payload.keytype = 1; - memcpy(payload.key, keyB[sectorNo], sizeof(payload.key)); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); - } else if (rights[sectorNo][data_area] == 0x07) { // no key would work - PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d", sectorNo, blockNo); - // where do you want to go?? Next sector or block? - break; - } else { // key A would work - - payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; - payload.keytype = MF_KEY_A; - memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); - } - } - if (received) { - if (resp.status == PM3_SUCCESS) { - // break the re-try loop - break; - } - } - } - - if (received) { - uint8_t *data = resp.data.asBytes; - if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. Fill in the keys. - data[0] = (keyA[sectorNo][0]); - data[1] = (keyA[sectorNo][1]); - data[2] = (keyA[sectorNo][2]); - data[3] = (keyA[sectorNo][3]); - data[4] = (keyA[sectorNo][4]); - data[5] = (keyA[sectorNo][5]); - - data[10] = (keyB[sectorNo][0]); - data[11] = (keyB[sectorNo][1]); - data[12] = (keyB[sectorNo][2]); - data[13] = (keyB[sectorNo][3]); - data[14] = (keyB[sectorNo][4]); - data[15] = (keyB[sectorNo][5]); - } - if (resp.status == PM3_SUCCESS) { - memcpy(carddata[mfFirstBlockOfSector(sectorNo) + blockNo], data, 16); - PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo); - } else { - PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo); - break; - } - } else { - PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo); - break; - } - } + int res = mfc_read_tag(&card, mem, numSectors, keyFilename); + if (res != PM3_SUCCESS) { + free(mem); + return res; } PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); - PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks"); + // Skip saving card data to file + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + free(mem); + return PM3_SUCCESS; + } + // Save to file if (strlen(dataFilename) < 1) { - fptr = GenerateFilename("hf-mf-", "-dump"); - if (fptr == NULL) + char *fptr = GenerateFilename("hf-mf-", "-dump"); + if (fptr == NULL) { + free(mem); return PM3_ESOFT; + } strcpy(dataFilename, fptr); free(fptr); } - uint16_t bytes = 16 * (mfFirstBlockOfSector(numSectors - 1) + mfNumBlocksPerSector(numSectors - 1)); - - saveFile(dataFilename, ".bin", (uint8_t *)carddata, bytes); - saveFileEML(dataFilename, (uint8_t *)carddata, bytes, MFBLOCK_SIZE); + saveFile(dataFilename, ".bin", mem, bytes); + saveFileEML(dataFilename, mem, bytes, MFBLOCK_SIZE); iso14a_mf_extdump_t xdump; xdump.card_info = card; - xdump.dump = (uint8_t *)carddata; + xdump.dump = mem; xdump.dumplen = bytes; saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + free(mem); return PM3_SUCCESS; } @@ -976,9 +1119,11 @@ static int CmdHF14AMfRestore(const char *Cmd) { "If access rights in dump file is all zeros, it will be replaced with default values\n" "\n" "`--uid` param is used for filename templates `hf-mf--dump.bin` and `hf-mf--key.bin.\n" - " If not specified, it will read the card uid instead.\n" + " if not specified, it will read the card uid instead.\n" " `--ka` param you can indicate that the key file should be used for authentication instead.\n" - " if so we also try both B/A keys", + " if so we also try both B/A keys\n" + "`--force` param is used to override warnings and allow bad ACL block writes.\n" + " if not specified, it will skip blocks with bad ACL.\n", "hf mf restore\n" "hf mf restore --1k --uid 04010203\n" "hf mf restore --1k --uid 04010203 -k hf-mf-AABBCCDD-key.bin\n" @@ -995,6 +1140,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), arg_str0("k", "kfn", "", "key filename"), arg_lit0(NULL, "ka", "use specified keyfile to authenticate"), + arg_lit0(NULL, "force", "override warnings"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1017,6 +1163,8 @@ static int CmdHF14AMfRestore(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)keyfilename, FILE_PATH_SIZE, &keyfnlen); bool use_keyfile_for_auth = arg_get_lit(ctx, 8); + bool force = arg_get_lit(ctx, 9); + CLIParserFree(ctx); // validations @@ -1062,7 +1210,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { if (fptr == NULL) return PM3_ESOFT; - strcpy(keyfilename, fptr); + strncpy(keyfilename, fptr, sizeof(keyfilename) - 1); free(fptr); } @@ -1120,7 +1268,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", datafilename); - // main loop for restoreing. + // main loop for restoring. // a bit more complicated than needed // this is because of two things. // 1. we are setting keys from a key file or using the existing ones in the dump @@ -1130,7 +1278,6 @@ static int CmdHF14AMfRestore(const char *Cmd) { for (uint8_t b = 0; b < mfNumBlocksPerSector(s); b++) { uint8_t bldata[MFBLOCK_SIZE] = {0x00}; - memcpy(bldata, dump, MFBLOCK_SIZE); // if sector trailer @@ -1153,10 +1300,23 @@ static int CmdHF14AMfRestore(const char *Cmd) { } // ensure access right isn't messed up. - if (! mfValidateAccessConditions(&bldata[6])) { - PrintAndLogEx(WARNING, "Invalid Access Conditions on sector %i, replacing by default values", s); + if (mfValidateAccessConditions(&bldata[6]) == false) { + PrintAndLogEx(WARNING, "Invalid Access Conditions on sector %i, replacing with default values", s); memcpy(bldata + 6, "\xFF\x07\x80\x69", 4); } + + // Warn if ACL is strict read-only + for (uint8_t foo = 0; foo < mfNumBlocksPerSector(s); foo++) { + if (mfReadOnlyAccessConditions(foo, &bldata[6])) { + PrintAndLogEx(WARNING, "Strict ReadOnly Access Conditions on block " _YELLOW_("%u") " detected", foo); + + // if --force isn't used, skip writing this block + if (force == false) { + PrintAndLogEx(INFO, "Skipping, use `" _YELLOW_("--force") "` to override and write this data"); + continue; + } + } + } } if (bytes_read) { @@ -1368,7 +1528,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't PrintAndLogEx(ERR, "Command execute timeout\n"); break; case PM3_EOPABORTED: - PrintAndLogEx(WARNING, "Button pressed. Aborted.\n"); + PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); break; case PM3_EFAILED: PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); @@ -1376,6 +1536,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't case PM3_ESOFT: PrintAndLogEx(FAILED, "No valid key found"); break; + case PM3_ESTATIC_NONCE: + PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + break; case PM3_SUCCESS: key64 = bytes_to_num(keyBlock, 6); @@ -1400,9 +1563,10 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } return PM3_SUCCESS; default : - PrintAndLogEx(ERR, "Unknown error.\n"); + PrintAndLogEx(ERR, "Unknown error\n"); } return PM3_SUCCESS; + } else { // ------------------------------------ multiple sectors working uint64_t t1 = msclock(); @@ -1447,15 +1611,18 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't PrintAndLogEx(ERR, "Command execute timeout\n"); break; case PM3_EOPABORTED: - PrintAndLogEx(WARNING, "button pressed. Aborted.\n"); + PrintAndLogEx(WARNING, "button pressed. Aborted\n"); break; case PM3_EFAILED : - PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); + PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable)\n"); break; case PM3_ESOFT: //key not found calibrate = false; continue; + case PM3_ESTATIC_NONCE: + PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + break; case PM3_SUCCESS: calibrate = false; e_sector[sectorNo].foundKey[trgKeyType] = 1; @@ -1464,7 +1631,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); continue; default : - PrintAndLogEx(ERR, "Unknown error.\n"); + PrintAndLogEx(ERR, "Unknown error\n"); } free(e_sector); return PM3_ESOFT; @@ -1547,7 +1714,7 @@ jumptoend: PrintAndLogEx(ERR, "Failed to save keys to file"); free(e_sector); free(fptr); - return PM3_ESOFT; + return PM3_EFILE; } free(fptr); } @@ -1605,7 +1772,6 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { bool transferToEml = arg_get_lit(ctx, 9); bool createDumpFile = arg_get_lit(ctx, 10); - CLIParserFree(ctx); //validations @@ -1660,7 +1826,8 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { uint64_t t1 = msclock(); e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return PM3_EMALLOC; + if (e_sector == NULL) + return PM3_EMALLOC; // add our known key e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1; @@ -1796,7 +1963,7 @@ jumptoend: PrintAndLogEx(ERR, "Failed to save keys to file"); free(e_sector); free(fptr); - return PM3_ESOFT; + return PM3_EFILE; } free(fptr); } @@ -1810,19 +1977,22 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf hardnested", "Nested attack for hardened MIFARE Classic cards.\n" + "if card is EV1, command can detect and use known key see example below\n" + " \n" "`--i` set type of SIMD instructions. Without this flag programs autodetect it.\n" " or \n" " hf mf hardnested -r --tk [known target key]\n" "Add the known target key to check if it is present in the remaining key space\n" " hf mf hardnested --blk 0 -a -k A0A1A2A3A4A5 --tblk 4 --ta --tk FFFFFFFFFFFF\n" , + "hf mf hardnested --tblk 4 --ta --> works for MFC EV1\n" "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta\n" "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -w\n" "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -f nonces.bin -w -s\n" "hf mf hardnested -r\n" "hf mf hardnested -r --tk a0a1a2a3a4a5\n" "hf mf hardnested -t --tk a0a1a2a3a4a5\n" - "hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk FFFFFFFFFFFF" + "hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk FFFFFFFFFFFF\n" ); void *argtable[] = { @@ -1945,7 +2115,7 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { SetSIMDInstr(SIMD_NONE); - bool know_target_key = (trg_keylen); + bool known_target_key = (trg_keylen); if (nonce_file_read) { char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); @@ -1958,8 +2128,9 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { if (nonce_file_write) { char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); - if (fptr == NULL) - return 1; + if (fptr == NULL) { + return PM3_EFILE; + } strncpy(filename, fptr, FILE_PATH_SIZE - 1); free(fptr); } @@ -1968,7 +2139,15 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { snprintf(filename, FILE_PATH_SIZE, "hf-mf-%s-nonces.bin", uid); } - if (know_target_key == false && nonce_file_read == false) { + // detect MFC EV1 Signature + if (detect_mfc_ev1_signature() && keylen == 0) { + PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected"); + blockno = 69; + keytype = MF_KEY_B; + memcpy(key, g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b)); + } + + if (known_target_key == false && nonce_file_read == false) { // check if tag doesn't have static nonce if (detect_classic_static_nonce() == NONCE_STATIC) { @@ -1989,7 +2168,7 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { trg_blockno, (trg_keytype == MF_KEY_B) ? 'B' : 'A', trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5], - know_target_key ? "" : " (not set)" + known_target_key ? "" : " (not set)" ); PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", @@ -1997,7 +2176,7 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { tests); uint64_t foundkey = 0; - int16_t isOK = mfnestedhard(blockno, keytype, key, trg_blockno, trg_keytype, know_target_key ? trg_key : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); + int16_t isOK = mfnestedhard(blockno, keytype, key, trg_blockno, trg_keytype, known_target_key ? trg_key : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); if ((tests == 0) && IfPm3Iso14443a()) { DropField(); @@ -2005,18 +2184,20 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { if (isOK) { switch (isOK) { - case 1 : - PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n"); + case PM3_ETIMEOUT : + PrintAndLogEx(ERR, "Error: No response from Proxmark3\n"); break; - case 2 : - PrintAndLogEx(NORMAL, "Button pressed. Aborted.\n"); + case PM3_EOPABORTED: + PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); + break; + case PM3_ESTATIC_NONCE: + PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); break; default : break; } - return 2; } - return 0; + return isOK; } static int CmdHF14AMfAutoPWN(const char *Cmd) { @@ -2039,7 +2220,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { arg_lit0("a", NULL, "Input key A (def)"), arg_lit0("b", NULL, "Input key B"), arg_str0("f", "file", "", "filename of dictionary"), - arg_lit0("s", "slow", "Slower acquisition (required by some non standard cards)"), + arg_lit0(NULL, "slow", "Slower acquisition (required by some non standard cards)"), arg_lit0("l", "legacy", "legacy mode (use the slow `hf mf chk`)"), arg_lit0("v", "verbose", "verbose output (statistics)"), @@ -2074,7 +2255,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { return PM3_EINVARG; } - bool know_target_key = (keylen == 6); + bool known_key = (keylen == 6); uint8_t sectorno = arg_get_u32_def(ctx, 2, 0); @@ -2192,10 +2373,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { int bytes; // Settings int prng_type = PM3_EUNDEF; - int has_staticnonce; uint8_t num_found_keys = 0; -// ------------------------------ + // ------------------------------ // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); @@ -2209,13 +2389,21 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); - return select_status; + return PM3_ECARDEXCHANGE; } // store card info iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + // detect MFC EV1 Signature + bool is_ev1 = detect_mfc_ev1_signature(); + if (is_ev1) { + // hidden sectors on MFC EV1 + sector_cnt += 2; + } + // create/initialize key storage structure uint32_t e_sector_size = sector_cnt > sectorno ? sector_cnt : sectorno + 1; res = initSectorTable(&e_sector, e_sector_size); @@ -2224,20 +2412,40 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { return PM3_EMALLOC; } + if (is_ev1) { + PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected"); + // Store the keys + e_sector[16].Key[MF_KEY_A] = bytes_to_num((uint8_t *)g_mifare_signature_key_a, sizeof(g_mifare_signature_key_a)); + e_sector[16].foundKey[MF_KEY_A] = 'D'; + + e_sector[17].Key[MF_KEY_A] = bytes_to_num((uint8_t *)g_mifare_signature_key_a, sizeof(g_mifare_signature_key_a)); + e_sector[17].foundKey[MF_KEY_A] = 'D'; + e_sector[17].Key[MF_KEY_B] = bytes_to_num((uint8_t *)g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b)); + e_sector[17].foundKey[MF_KEY_B] = 'D'; + + // use found key if not supplied + if (known_key == false) { + known_key = true; + sectorno = 17; + keytype = MF_KEY_B; + memcpy(key, g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b)); + } + } + // read uid to generate a filename for the key file char *fptr = GenerateFilename("hf-mf-", "-key.bin"); // check if tag doesn't have static nonce - has_staticnonce = detect_classic_static_nonce(); + int has_staticnonce = detect_classic_static_nonce(); // card prng type (weak=1 / hard=0 / select/card comm error = negative value) if (has_staticnonce == NONCE_NORMAL) { prng_type = detect_classic_prng(); if (prng_type < 0) { - PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error"); + PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%u)", prng_type); free(e_sector); free(fptr); - return prng_type; + return PM3_ESOFT; } } @@ -2245,7 +2453,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); - PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), know_target_key ? "True" : "False"); + PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False"); PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex(key, sizeof(key))); @@ -2267,7 +2475,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint64_t t1 = msclock(); // check the user supplied key - if (know_target_key == false) { + if (known_key == false) { PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); } else { if (verbose) { @@ -2287,7 +2495,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { ++num_found_keys; } else { - know_target_key = false; + known_key = false; PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector"_RED_("%3d") " key type "_RED_("%c") " key " _RED_("%s"), sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', @@ -2305,9 +2513,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { e_sector[i].foundKey[j] = 'U'; // If the user supplied secctor / keytype was wrong --> just be nice and correct it ;) - if (know_target_key == false) { + if (known_key == false) { num_to_bytes(e_sector[i].Key[j], 6, key); - know_target_key = true; + known_key = true; sectorno = i; keytype = j; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", @@ -2430,9 +2638,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { num_to_bytes(e_sector[i].Key[j], 6, tmp_key); // Store valid credentials for the nested / hardnested attack if none exist - if (know_target_key == false) { + if (known_key == false) { num_to_bytes(e_sector[i].Key[j], 6, key); - know_target_key = true; + known_key = true; sectorno = i; keytype = j; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", @@ -2452,7 +2660,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Check if at least one sector key was found - if (know_target_key == false) { + if (known_key == false) { + // Check if the darkside attack can be used if (prng_type && has_staticnonce != NONCE_STATIC) { if (verbose) { @@ -2461,8 +2670,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { isOK = mfDarkside(mfFirstBlockOfSector(sectorno), keytype + 0x60, &key64); switch (isOK) { - case -1 : - PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); + case PM3_EOPABORTED : + PrintAndLogEx(WARNING, "\nButton pressed or aborted via keyboard"); goto noValidKeyFound; case -2 : PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); @@ -2474,9 +2683,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(FAILED, "\nCard is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour."); goto noValidKeyFound; - case -5 : - PrintAndLogEx(WARNING, "\naborted via keyboard."); - goto noValidKeyFound; default : PrintAndLogEx(SUCCESS, "\nFound valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); break; @@ -2492,6 +2698,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { key64 ); } else { + noValidKeyFound: PrintAndLogEx(FAILED, "No usable key was found!"); free(keyBlock); @@ -2561,7 +2768,7 @@ noValidKeyFound: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) goto skipReadBKey; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) goto skipReadBKey; if (resp.status != PM3_SUCCESS) goto skipReadBKey; @@ -2613,13 +2820,13 @@ tryNested: PrintAndLogEx(ERR, "\nError: No response from Proxmark3."); free(e_sector); free(fptr); - return PM3_ESOFT; + return isOK; } case PM3_EOPABORTED: { PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); free(e_sector); free(fptr); - return PM3_EOPABORTED; + return isOK; } case PM3_EFAILED: { PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is probably not predictable)."); @@ -2640,6 +2847,11 @@ tryNested: } break; } + case PM3_ESTATIC_NONCE: + PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + free(e_sector); + free(fptr); + return isOK; case PM3_SUCCESS: { calibrate = false; e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); @@ -2650,7 +2862,7 @@ tryNested: PrintAndLogEx(ERR, "unknown Error.\n"); free(e_sector); free(fptr); - return PM3_ESOFT; + return isOK; } } @@ -2668,14 +2880,18 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack DropField(); if (isOK) { switch (isOK) { - case 1: { + case PM3_ETIMEOUT: { PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); break; } - case 2: { + case PM3_EOPABORTED: { PrintAndLogEx(NORMAL, "\nButton pressed, user aborted"); break; } + case PM3_ESTATIC_NONCE: { + PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted\n"); + break; + } default: { break; } @@ -2707,13 +2923,13 @@ tryStaticnested: PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); free(e_sector); free(fptr); - return PM3_ESOFT; + return isOK; } case PM3_EOPABORTED: { PrintAndLogEx(WARNING, "\nButton pressed, user aborted"); free(e_sector); free(fptr); - return PM3_EOPABORTED; + return isOK; } case PM3_SUCCESS: { e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); @@ -2799,7 +3015,8 @@ all_found: free(fptr); return PM3_ESOFT; } - strcpy(filename, fptr); + + strncpy(filename, fptr, sizeof(filename) - 1); free(fptr); saveFile(filename, ".bin", dump, bytes); @@ -3421,18 +3638,22 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo static int CmdHF14AMfSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf sim", - "Simulate MIFARE Classic card", + "Simulate MIFARE Classic family type based upon\n" + "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" + "from emulator memory. See `hf mf eload` first.\n" + "The UID from emulator memory will be used if not specified.", "hf mf sim --mini --> MIFARE Mini\n" "hf mf sim --1k --> MIFARE Classic 1k (default)\n" "hf mf sim --1k -u 0a0a0a0a --> MIFARE Classic 1k with 4b UID\n" "hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n" "hf mf sim --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n" "hf mf sim --2k --> MIFARE 2k\n" - "hf mf sim --4k --> MIFARE 4k"); + "hf mf sim --4k --> MIFARE 4k" + ); void *argtable[] = { arg_param_begin, - arg_str0("u", "uid", "", "UID 4,7 or 10bytes. If not specified, the UID 4b/7b from emulator memory will be used"), + arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), @@ -3508,8 +3729,6 @@ static int CmdHF14AMfSim(const char *Cmd) { } CLIParserFree(ctx); - nonces_t data[1]; - sector_t *k_sector = NULL; //Validations @@ -3600,7 +3819,7 @@ static int CmdHF14AMfSim(const char *Cmd) { if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) continue; if (!(flags & FLAG_NR_AR_ATTACK)) break; if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) break; - + nonces_t data[1]; memcpy(data, resp.data.asBytes, sizeof(data)); readerAttack(k_sector, k_sectorsCount, data[0], setEmulatorMem, verbose); } @@ -3647,6 +3866,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { void printKeyTable(uint8_t sectorscnt, sector_t *e_sector) { return printKeyTableEx(sectorscnt, e_sector, 0); } + void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { char strA[12 + 1] = {0}; char strB[12 + 1] = {0}; @@ -3741,7 +3961,7 @@ static int CmdHF14AMfEGetBlk(const char *Cmd) { uint8_t sector = mfSectorNum(blockno); mf_print_sector_hdr(sector); - mf_print_block(blockno, data, verbose); + mf_print_block_one(blockno, data, verbose); } if (verbose) { decode_print_st(blockno, data); @@ -3783,7 +4003,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { for (int i = 0; i < blocks; i++) { int res = mfEmlGetMem(data, start + i, 1); if (res == PM3_SUCCESS) { - mf_print_block(start + i, data, verbose); + mf_print_block_one(start + i, data, verbose); } } if (verbose) { @@ -3866,6 +4086,7 @@ int CmdHF14AMfELoad(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "ul", "MIFARE Ultralight family"), + arg_lit0("m", "mem", "use RDV4 spiffs"), arg_int0("q", "qty", "", "manually set number of blocks (overrides)"), arg_param_end }; @@ -3881,7 +4102,8 @@ int CmdHF14AMfELoad(const char *Cmd) { bool m4 = arg_get_lit(ctx, 5); bool mu = arg_get_lit(ctx, 6); - int numblks = arg_get_int_def(ctx, 7, -1); + bool use_spiffs = arg_get_lit(ctx, 7); + int numblks = arg_get_int_def(ctx, 8, -1); CLIParserFree(ctx); @@ -3921,6 +4143,36 @@ int CmdHF14AMfELoad(const char *Cmd) { PrintAndLogEx(INFO, "overriding number of blocks, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); } + // use RDV4 spiffs + if (use_spiffs && IfPm3Flash() == false) { + PrintAndLogEx(WARNING, "Device not compiled to support spiffs"); + return PM3_EINVARG; + } + + if (use_spiffs) { + + if (fnlen > 32) { + PrintAndLogEx(WARNING, "filename too long for spiffs, expected 32, got %u", fnlen); + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Loading file from spiffs to emulator memory failed"); + return PM3_EFLASH; + } + + PrintAndLogEx(SUCCESS, "File transfered from spiffs to device emulator memory"); + return PM3_SUCCESS; + } + uint8_t *data = NULL; size_t bytes_read = 0; int res = pm3_load_dump(filename, (void **)&data, &bytes_read, (block_width * block_cnt + hdr_len)); @@ -4180,7 +4432,7 @@ static int CmdHF14AMfECFill(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf ecfill", "Dump card and transfer the data to emulator memory.\n" - "Keys must be laid in the emulator memory", + "Keys must be in the emulator memory", "hf mf ecfill --> use key type A\n" "hf mf ecfill --4k -b --> target 4K card with key type B" ); @@ -4699,7 +4951,7 @@ static int CmdHF14AMfCGetBlk(const char *Cmd) { uint8_t sector = mfSectorNum(blockno); mf_print_sector_hdr(sector); - mf_print_block(blockno, data, verbose); + mf_print_block_one(blockno, data, verbose); if (verbose) { decode_print_st(blockno, data); @@ -4752,7 +5004,7 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res); return PM3_ESOFT; } - mf_print_block(start + i, data, verbose); + mf_print_block_one(start + i, data, verbose); } if (verbose) { decode_print_st(start + blocks - 1, data); @@ -4777,7 +5029,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), - arg_lit0(NULL, "emu", "from emulator memory"), + arg_lit0(NULL, "emu", "to emulator memory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -4842,7 +5094,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); - return select_status; + return PM3_SUCCESS; } // store card info @@ -4993,7 +5245,7 @@ static int CmdHF14AMfCView(const char *Cmd) { if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); - return select_status; + return PM3_ERFTRANS; } iso14a_card_select_t card; @@ -5143,7 +5395,7 @@ static int CmdHf14AMfSetMod(const char *Cmd) { if (resp.status == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Change ( " _GREEN_("ok") " )"); else - PrintAndLogEx(FAILED, "Change (" _GREEN_("fail") " )"); + PrintAndLogEx(FAILED, "Change ( " _RED_("fail") " )"); return resp.status; } @@ -5202,7 +5454,7 @@ static int CmdHF14AMfice(const char *Cmd) { fptr = GenerateFilename("hf-mf-", "-nonces.bin"); if (fptr == NULL) return PM3_EFILE; - strcpy(filename, fptr); + strncpy(filename, fptr, sizeof(filename) - 1); free(fptr); } @@ -5383,10 +5635,53 @@ static int CmdHF14AMfMAD(const char *Cmd) { MADPrintHeader(); bool haveMAD2 = false; MAD1DecodeAndPrint(dump, swapmad, verbose, &haveMAD2); + + int sector = DetectHID(dump, 0x484d); + if (sector > -1) { + + // decode it + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, _CYAN_("HID PACS detected")); + + uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0}; + memcpy(pacs_sector, dump + (sector * 4 * 16), sizeof(pacs_sector)); + + if (pacs_sector[16] == 0x02) { + + PrintAndLogEx(SUCCESS, "Raw...... " _GREEN_("%s"), sprint_hex_inrow(pacs_sector + 24, 8)); + + //todo: remove preamble/sentinel + uint32_t top = 0, mid = 0, bot = 0; + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, pacs_sector + 24, 8, sizeof(hexstr) - 1, 0, 0, true); + hexstring_to_u96(&top, &mid, &bot, hexstr); + + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin); + + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + } + } + + sector = DetectHID(dump, 0x4910); + if (sector > -1) { + // decode it + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, _CYAN_("VIGIK PACS detected")); + } free(dump); return PM3_SUCCESS; } + if (g_session.pm3_present == false) + return PM3_ENOTTY; + uint8_t sector0[16 * 4] = {0}; uint8_t sector10[16 * 4] = {0}; @@ -5547,9 +5842,9 @@ int CmdHFMFNDEFRead(const char *Cmd) { CLIParserFree(ctx); - uint16_t ndefAID = 0xe103; + uint16_t ndef_aid = NDEF_MFC_AID; if (aidlen == 2) - ndefAID = (aid[0] << 8) + aid[1]; + ndef_aid = (aid[0] << 8) + aid[1]; uint8_t ndefkey[6] = {0}; memcpy(ndefkey, g_mifare_ndef_key, 6); @@ -5557,15 +5852,15 @@ int CmdHFMFNDEFRead(const char *Cmd) { memcpy(ndefkey, key, 6); } - uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; + uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; + uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; uint8_t data[4096] = {0}; int datalen = 0; if (verbose) PrintAndLogEx(INFO, "reading MAD v1 sector"); - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; @@ -5582,7 +5877,7 @@ int CmdHFMFNDEFRead(const char *Cmd) { if (verbose) PrintAndLogEx(INFO, "reading MAD v2 sector"); - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); return PM3_ESOFT; @@ -5598,23 +5893,23 @@ int CmdHFMFNDEFRead(const char *Cmd) { } PrintAndLogEx(INFO, "reading data from tag"); - for (int i = 0; i < madlen; i++) { - if (ndefAID == mad[i]) { - uint8_t vsector[16 * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { + for (int i = 1; i <= madlen; i++) { + if (ndef_aid == mad[i]) { + uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; + if (mfReadSector(i, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { PrintAndLogEx(ERR, "error, reading sector %d ", i + 1); return PM3_ESOFT; } - memcpy(&data[datalen], vsector, 16 * 3); - datalen += 16 * 3; + memcpy(&data[datalen], vsector, MFBLOCK_SIZE * 3); + datalen += MFBLOCK_SIZE * 3; PrintAndLogEx(INPLACE, "%d", i); } } PrintAndLogEx(NORMAL, ""); - if (!datalen) { + if (datalen == 0) { PrintAndLogEx(WARNING, "no NDEF data"); return PM3_SUCCESS; } @@ -5628,13 +5923,483 @@ int CmdHFMFNDEFRead(const char *Cmd) { if (fnlen != 0) { saveFile(filename, ".bin", data, datalen); } - NDEFDecodeAndPrint(data, datalen, verbose); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -vv`") " for more details"); + res = NDEFDecodeAndPrint(data, datalen, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); + res = NDEFRecordsDecodeAndPrint(data, datalen, verbose); + } + + if (verbose == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -v`") " for more details"); + } else { + if (verbose2 == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -vv`") " for more details"); + } + } return PM3_SUCCESS; } -static int CmdHFMFPersonalize(const char *cmd) { +// https://www.nxp.com/docs/en/application-note/AN1305.pdf +int CmdHFMFNDEFFormat(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ndefformat", + "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF)\n" + "If no given, UID will be used as filename. \n" + "It will try default keys and MAD keys to detect if tag is already formatted in order to write.\n" + "\n" + "If not, it will try finding a key file based on your UID. ie, if you ran autopwn before", + "hf mf ndefformat\n" + // "hf mf ndefformat --mini --> MIFARE Mini\n" + "hf mf ndefformat --1k --> MIFARE Classic 1k\n" + // "hf mf ndefformat --2k --> MIFARE 2k\n" + // "hf mf ndefformat --4k --> MIFARE 4k\n" + "hf mf ndefformat --keys hf-mf-01020304-key.bin --> MIFARE 1k with keys from specified file\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "keys", "", "filename of keys"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int keyfnlen = 0; + char keyFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); + + bool m0 = arg_get_lit(ctx, 2); + bool m1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); + + CLIParserFree(ctx); + + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + uint8_t numSectors = MIFARE_1K_MAXSECTOR; + + if (m0) { + numSectors = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + numSectors = MIFARE_1K_MAXSECTOR; + } else if (m2) { + numSectors = MIFARE_2K_MAXSECTOR; + } else if (m4) { + numSectors = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + + if (g_session.pm3_present == false) + return PM3_ENOTTY; + + // Select card to get UID/UIDLEN/ATQA/SAK information + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + uint64_t select_status = resp.oldarg[0]; + if (select_status == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + return PM3_SUCCESS; + } + + DropField(); + + + // init keys to default key + uint8_t keyA[MIFARE_4K_MAXSECTOR][MFKEY_SIZE]; + uint8_t keyB[MIFARE_4K_MAXSECTOR][MFKEY_SIZE]; + + for (uint8_t i = 0; i < MIFARE_4K_MAXSECTOR; i++) { + memcpy(keyA[i], g_mifare_default_key, sizeof(g_mifare_default_key)); + memcpy(keyB[i], g_mifare_default_key, sizeof(g_mifare_default_key)); + } + + // test if MAD key is used + uint64_t key64 = 0; + + // check if we can authenticate to sector + if (mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64) == PM3_SUCCESS) { + + // if used, assume KEY A is MAD/NDEF set. + memcpy(keyA[0], g_mifare_mad_key, sizeof(g_mifare_mad_key)); + memcpy(keyB[0], g_mifare_mad_key_b, sizeof(g_mifare_mad_key_b)); + for (uint8_t i = 1; i < MIFARE_4K_MAXSECTOR; i++) { + memcpy(keyA[i], g_mifare_ndef_key, sizeof(g_mifare_ndef_key)); + } + } + + // Do we have a keyfile based from UID? + if (keyfnlen == 0) { + char *fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr) { + strncpy(keyFilename, fptr, sizeof(keyFilename) - 1); + } + free(fptr); + DropField(); + } + + // load key file if exist + if (strlen(keyFilename)) { + + FILE *f; + if ((f = fopen(keyFilename, "rb")) == NULL) { + // PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); + goto skipfile; + } + + PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); + + // Read keys A from file + size_t bytes_read; + for (uint8_t i = 0; i < numSectors; i++) { + bytes_read = fread(keyA[i], 1, MFKEY_SIZE, f); + if (bytes_read != MFKEY_SIZE) { + PrintAndLogEx(ERR, "File reading error."); + fclose(f); + return PM3_EFILE; + } + } + + // Read keys B from file + for (uint8_t i = 0; i < numSectors; i++) { + bytes_read = fread(keyB[i], 1, MFKEY_SIZE, f); + if (bytes_read != MFKEY_SIZE) { + PrintAndLogEx(ERR, "File reading error."); + fclose(f); + return PM3_EFILE; + } + } + + fclose(f); + } + +skipfile: + ; + + uint8_t firstblocks[8][16] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x14, 0x01, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 }, + { 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1, 0x03, 0xE1 }, + { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A }, + { 0x03, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0x7F, 0x07, 0x88, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, + }; + + // main loop + for (int i = 0; i < numSectors; i++) { + for (int j = 0; j < mfNumBlocksPerSector(j); j++) { + + uint8_t b = (mfFirstBlockOfSector(i) + j); + uint8_t block[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + switch (b) { + case 0: + continue; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + memcpy(block, firstblocks[b], MFBLOCK_SIZE); + break; + default: { + if (mfIsSectorTrailer(j)) { + // ST NDEF + memcpy(block, firstblocks[7], MFBLOCK_SIZE); + } + break; + } + + } + + // write to card, try B key first + if (mf_write_block(keyB[i], MF_KEY_B, b, block) == 0) { + // try A key, + if (mf_write_block(keyA[i], MF_KEY_A, b, block) == 0) { + return PM3_EFAILED; + } + } + PrintAndLogEx(INPLACE, "Formatting block %u", b); + } + } + + PrintAndLogEx(NORMAL, ""); + + return PM3_SUCCESS; +} + +int CmdHFMFNDEFWrite(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ndefwrite", + "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.\n", + "hf mf ndefwrite -d 0300FE -> write empty record to tag\n" + "hf mf ndefwrite -f myfilename\n" + "hf mf ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", NULL, "", "raw NDEF hex bytes"), + arg_str0("f", "file", "", "write raw NDEF file to tag"), + arg_lit0("p", NULL, "fix NDEF record headers / terminator block if missing"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t raw[4096] = {0}; + int rawlen; + CLIGetHexWithReturn(ctx, 1, raw, &rawlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool fix_msg = arg_get_lit(ctx, 3); + + bool m0 = arg_get_lit(ctx, 4); + bool m1 = arg_get_lit(ctx, 5); + bool m2 = arg_get_lit(ctx, 6); + bool m4 = arg_get_lit(ctx, 7); + bool verbose = arg_get_lit(ctx, 8); + + CLIParserFree(ctx); + + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + uint8_t numSectors = MIFARE_1K_MAXSECTOR; + + if (m0) { + numSectors = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + numSectors = MIFARE_1K_MAXSECTOR; + } else if (m2) { + numSectors = MIFARE_2K_MAXSECTOR; + } else if (m4) { + numSectors = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + if (verbose) { + PrintAndLogEx(INFO, "Number of sectors selected: %u", numSectors); + } + + if (g_session.pm3_present == false) { + PrintAndLogEx(FAILED, "No Proxmark3 device present"); + return PM3_ENOTTY; + } + + if ((rawlen && fnlen) || (rawlen == 0 && fnlen == 0)) { + PrintAndLogEx(WARNING, "Please specify either raw hex or filename"); + return PM3_EINVARG; + } + + // test if MAD key is used + uint64_t key64 = 0; + + // check if we can authenticate to sector + int res = mfCheckKeys(0, MF_KEY_A, true, 1, (uint8_t *)g_mifare_mad_key, &key64); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Sector 0 failed to authenticate with MAD default key"); + PrintAndLogEx(HINT, "Verify that the tag NDEF formatted"); + return res; + } + + // NDEF for MIFARE CLASSIC has different memory size available. + + int32_t bytes = rawlen; + + // read dump file + if (fnlen) { + uint8_t *dump = NULL; + size_t bytes_read = 0; + res = pm3_load_dump(filename, (void **)&dump, &bytes_read, sizeof(raw)); + if (res != PM3_SUCCESS) { + return res; + } + memcpy(raw, dump, bytes_read); + bytes = bytes_read; + free(dump); + } + + // Has raw bytes ndef message header?bytes + switch (raw[0]) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0xFD: + case 0xFE: + break; + default: { + if (fix_msg == false) { + PrintAndLogEx(WARNING, "raw NDEF message doesn't have a proper header, continuing..."); + } else { + if (bytes + 2 > sizeof(raw)) { + PrintAndLogEx(WARNING, "no room for header, exiting..."); + return PM3_EMALLOC; + } + uint8_t tmp_raw[4096]; + memcpy(tmp_raw, raw, sizeof(tmp_raw)); + raw[0] = 0x03; + raw[1] = bytes; + memcpy(raw + 2, tmp_raw, sizeof(raw) - 2); + bytes += 2; + PrintAndLogEx(SUCCESS, "Added generic message header (0x03)"); + } + } + } + + // Has raw bytes ndef a terminator block? + if (raw[bytes - 1] != 0xFE) { + if (fix_msg == false) { + PrintAndLogEx(WARNING, "raw NDEF message doesn't have a terminator block, continuing..."); + } else { + + if (bytes + 1 > sizeof(raw)) { + PrintAndLogEx(WARNING, "no room for terminator block, exiting..."); + return PM3_EMALLOC; + } + raw[bytes] = 0xFE; + bytes++; + PrintAndLogEx(SUCCESS, "Added terminator block (0xFE)"); + } + } + + if (verbose) { + PrintAndLogEx(INFO, "Num of Bytes... %u", bytes); + print_buffer(raw, bytes, 0); + } + + // read MAD Sector 0, block1,2 + uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { + PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); + return PM3_ESOFT; + } + + // decode MAD + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + res = MADDecode(sector0, NULL, mad, &madlen, false); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "can't decode MAD"); + return res; + } + + // how much memory do I have available ? + // Skip sector 0 since its used for MAD + uint8_t freemem[MIFARE_4K_MAXSECTOR] = {0}; + uint16_t sum = 0; + uint8_t block_no = 0; + for (uint8_t i = 1; i < (madlen & 0xFF); i++) { + + freemem[i] = (mad[i] == NDEF_MFC_AID); + + if (freemem[i]) { + + if (block_no == 0) { + block_no = mfFirstBlockOfSector(i); + } + + if (verbose) { + PrintAndLogEx(INFO, "Sector %u is NDEF formatted", i); + } + sum += (MFBLOCK_SIZE * 3); + } + } + + if (verbose) { + PrintAndLogEx(INFO, "Total avail ndef mem... %u", sum); + PrintAndLogEx(INFO, "First block............ %u", block_no); + } + + if (sum < bytes) { + PrintAndLogEx(WARNING, "Raw NDEF message is larger than available NDEF formatted memory"); + return PM3_EINVARG; + } + + // main loop - write blocks + uint8_t *ptr_raw = raw; + while (bytes > 0) { + + uint8_t block[MFBLOCK_SIZE] = { 0x00 }; + + if (bytes < MFBLOCK_SIZE) { + memcpy(block, ptr_raw, bytes); + } else { + memcpy(block, ptr_raw, MFBLOCK_SIZE); + ptr_raw += MFBLOCK_SIZE; + } + + // write to card, try B key first + if (mf_write_block(g_mifare_default_key, MF_KEY_B, block_no, block) == 0) { + + // try A key, + if (mf_write_block(g_mifare_ndef_key, MF_KEY_A, block_no, block) == 0) { + return PM3_EFAILED; + } + } + + PrintAndLogEx(INPLACE, "%u", block_no); + + // find next available block + block_no++; + if (mfIsSectorTrailer(block_no)) { + block_no++; + + // skip sectors which isn't ndef formatted + while (freemem[mfSectorNum(block_no)] == 0) { + block_no++; + } + } + + bytes -= MFBLOCK_SIZE; + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int CmdHFMFPersonalize(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf personalize", "Personalize the UID of a MIFARE Classic EV1 card. This is only possible \n" @@ -5657,7 +6422,7 @@ static int CmdHFMFPersonalize(const char *cmd) { arg_lit0(NULL, "f3", "UIDF3, single size NUID"), arg_param_end }; - CLIExecWithReturn(ctx, cmd, argtable, true); + CLIExecWithReturn(ctx, Cmd, argtable, true); bool use_a = arg_get_lit(ctx, 1); bool use_b = arg_get_lit(ctx, 2); @@ -5845,135 +6610,238 @@ static int CmdHf14AGen3Freeze(const char *Cmd) { } static int CmdHf14AMfSuperCard(const char *Cmd) { - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf supercard", "Extract info from a `super card`", - "hf mf supercard"); + "hf mf supercard -> recover key\n" + "hf mf supercard -r -> reset card\n" + "hf mf supercard -u 11223344 -> change UID\n"); void *argtable[] = { arg_param_begin, - arg_lit0("r", "reset", "reset card"), + arg_lit0("r", "reset", "Reset card"), + arg_str0("u", "uid", "", "New UID (4 hex bytes)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool reset_card = arg_get_lit(ctx, 1); + uint8_t uid[4]; + int uidlen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), uid, sizeof(uid), &uidlen); CLIParserFree(ctx); - bool activate_field = true; - bool keep_field_on = true; - int res = 0; + if (res || (!res && uidlen && uidlen != sizeof(uid))) { + PrintAndLogEx(ERR, "UID must include 8 HEX symbols"); + return PM3_EINVARG; + } - if (reset_card) { + #define SUPER_MAX_TRACES 7 - keep_field_on = false; - uint8_t response[6]; - int resplen = 0; + uint8_t trace = 0; + uint8_t traces[SUPER_MAX_TRACES][16]; - // --------------- RESET CARD ---------------- - uint8_t aRESET[] = { 0x00, 0xa6, 0xc0, 0x00 }; - res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response), &resplen); + // read 7 traces from super card + for (trace = 0; trace < SUPER_MAX_TRACES; trace++) { + + uint8_t data[] = {0x30, 0x00 + trace}; + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data)); + if (WaitForResponseTimeout(CMD_ACK, NULL, 1500) == false) { + break; + } + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + break; + } + + uint16_t len = resp.oldarg[0] & 0xFFFF; + if (len != 18) { + break; // Not trace data + } + + memcpy(&traces[trace], resp.data.asBytes, len - 2); + } + + // Super card generation 2 + if (trace == SUPER_MAX_TRACES) { + + // no reset on super card generation 2. + if (uidlen || reset_card) { + PrintAndLogEx(FAILED, "Not supported on this card"); + return PM3_SUCCESS; + } + + // recover key from collected traces + for (trace = 0; trace < SUPER_MAX_TRACES; trace++) { + uint8_t *trace_data = traces[trace]; + nonces_t data; + + // first + uint16_t NT0 = (trace_data[6] << 8) | trace_data[7]; + data.cuid = bytes_to_num(trace_data, 4); + data.nonce = prng_successor(NT0, 31); + data.nr = bytes_to_num(trace_data + 8, 4); + data.ar = bytes_to_num(trace_data + 12, 4); + data.at = 0; + + // second + for (uint8_t s_strace = trace + 1; s_strace < 7; s_strace++) { + uint8_t *s_trace_data = traces[s_strace]; + if (mfSectorNum(s_trace_data[5]) == mfSectorNum(trace_data[5])) { + NT0 = (s_trace_data[6] << 8) | s_trace_data[7]; + data.nonce2 = prng_successor(NT0, 31); + data.nr2 = bytes_to_num(s_trace_data + 8, 4); + data.ar2 = bytes_to_num(s_trace_data + 12, 4); + data.sector = mfSectorNum(trace_data[5]); + data.keytype = trace_data[4]; + data.state = FIRST; + + uint64_t key64 = -1; + if (mfkey32_moebius(&data, &key64)) { + PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]", sprint_hex_inrow(trace_data, 4), data.sector, (data.keytype == 0x60) ? 'A' : 'B', key64); + break; + } + } + } + } + + } else { + + // Super card generation 1 + + // Commands: + // a0 - set UID + // b0 - read traces + // c0 - clear card + bool activate_field = true; + bool keep_field_on = true; + + // change UID on a super card generation 1 + if (uidlen) { + keep_field_on = false; + uint8_t response[6]; + int resplen = 0; + + // --------------- CHANGE UID ---------------- + uint8_t aCHANGE[] = {0x00, 0xa6, 0xa0, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0x00}; + memcpy(aCHANGE + 5, uid, uidlen); + res = ExchangeAPDU14a(aCHANGE, sizeof(aCHANGE), activate_field, keep_field_on, response, sizeof(response), + &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Super card UID change [ " _RED_("fail") " ]"); + DropField(); + return res; + } + + PrintAndLogEx(SUCCESS, "Super card UID change ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; + } + + // reset a super card generation 1 + if (reset_card) { + keep_field_on = false; + uint8_t response[6]; + int resplen = 0; + + // --------------- RESET CARD ---------------- + uint8_t aRESET[] = {0x00, 0xa6, 0xc0, 0x00}; + res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response), + &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]"); + DropField(); + return res; + } + PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; + } + + + uint8_t responseA[22]; + uint8_t responseB[22]; + int respAlen = 0; + int respBlen = 0; + + // --------------- First ---------------- + uint8_t aFIRST[] = {0x00, 0xa6, 0xb0, 0x00, 0x10}; + res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]"); DropField(); return res; } - PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )"); - return PM3_SUCCESS; - } + // --------------- Second ---------------- + activate_field = false; + keep_field_on = false; - uint8_t responseA[22]; - uint8_t responseB[22]; - int respAlen = 0; - int respBlen = 0; + uint8_t aSECOND[] = {0x00, 0xa6, 0xb0, 0x01, 0x10}; + res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } - // --------------- First ---------------- - uint8_t aFIRST[] = { 0x00, 0xa6, 0xb0, 0x00, 0x10 }; - res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen); - if (res != PM3_SUCCESS) { - DropField(); - return res; - } + uint8_t outA[16] = {0}; + uint8_t outB[16] = {0}; - // --------------- Second ---------------- - activate_field = false; - keep_field_on = false; + uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + for (uint8_t i = 0; i < 16; i += 8) { + des_decrypt(outA + i, responseA + i, key); + des_decrypt(outB + i, responseB + i, key); + } - uint8_t aSECOND[] = { 0x00, 0xa6, 0xb0, 0x01, 0x10 }; - res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen); - if (res != PM3_SUCCESS) { - DropField(); - return res; - } + PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen)); + PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA))); + PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen)); + PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB))); -// uint8_t inA[] = { 0x72, 0xD7, 0xF4, 0x3E, 0xFD, 0xAB, 0xF2, 0x35, 0xFD, 0x49, 0xEE, 0xDC, 0x44, 0x95, 0x43, 0xC4}; -// uint8_t inB[] = { 0xF0, 0xA2, 0x67, 0x6A, 0x04, 0x6A, 0x72, 0x12, 0x76, 0xA4, 0x1D, 0x02, 0x1F, 0xEA, 0x20, 0x85}; + if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { + PrintAndLogEx(INFO, "No trace recorded"); + return PM3_SUCCESS; + } - uint8_t outA[16] = {0}; - uint8_t outB[16] = {0}; + // second trace? + if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { + PrintAndLogEx(INFO, "Only one trace recorded"); + return PM3_SUCCESS; + } - uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - for (uint8_t i = 0; i < 16; i += 8) { - des_decrypt(outA + i, responseA + i, key); - des_decrypt(outB + i, responseB + i, key); - } + nonces_t data; - PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen)); - PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA))); - PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen)); - PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB))); + // first + uint16_t NT0 = (outA[6] << 8) | outA[7]; + data.cuid = bytes_to_num(outA, 4); + data.nonce = prng_successor(NT0, 31); + data.nr = bytes_to_num(outA + 8, 4); + data.ar = bytes_to_num(outA + 12, 4); + data.at = 0; - if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { - PrintAndLogEx(INFO, "No trace recorded"); - return PM3_SUCCESS; - } + // second + NT0 = (outB[6] << 8) | outB[7]; + data.nonce2 = prng_successor(NT0, 31); + data.nr2 = bytes_to_num(outB + 8, 4); + data.ar2 = bytes_to_num(outB + 12, 4); + data.sector = mfSectorNum(outA[5]); + data.keytype = outA[4]; + data.state = FIRST; - // second trace? - if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { - PrintAndLogEx(INFO, "Only one trace recorded"); - return PM3_SUCCESS; - } + PrintAndLogEx(DEBUG, "A Sector %02x", data.sector); + PrintAndLogEx(DEBUG, "A NT %08x", data.nonce); + PrintAndLogEx(DEBUG, "A NR %08x", data.nr); + PrintAndLogEx(DEBUG, "A AR %08x", data.ar); + PrintAndLogEx(DEBUG, ""); + PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2); + PrintAndLogEx(DEBUG, "B NR %08x", data.nr2); + PrintAndLogEx(DEBUG, "B AR %08x", data.ar2); - nonces_t data; - - // first - uint16_t NT0 = (outA[6] << 8) | outA[7]; - data.cuid = bytes_to_num(outA, 4); - data.nonce = prng_successor(NT0, 31); - data.nr = bytes_to_num(outA + 8, 4); - data.ar = bytes_to_num(outA + 12, 4); - data.at = 0; - - // second - NT0 = (outB[6] << 8) | outB[7]; - data.nonce2 = prng_successor(NT0, 31);; - data.nr2 = bytes_to_num(outB + 8, 4); - data.ar2 = bytes_to_num(outB + 12, 4); - data.sector = mfSectorNum(outA[5]); - data.keytype = outA[4]; - data.state = FIRST; - - PrintAndLogEx(DEBUG, "A Sector %02x", data.sector); - PrintAndLogEx(DEBUG, "A NT %08x", data.nonce); - PrintAndLogEx(DEBUG, "A NR %08x", data.nr); - PrintAndLogEx(DEBUG, "A AR %08x", data.ar); - PrintAndLogEx(DEBUG, ""); - PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2); - PrintAndLogEx(DEBUG, "B NR %08x", data.nr2); - PrintAndLogEx(DEBUG, "B AR %08x", data.ar2); - - uint64_t key64 = -1; - res = mfkey32_moebius(&data, &key64); - - if (res) { - PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%12" PRIX64) " ]" - , sprint_hex_inrow(outA, 4) - , data.sector - , (data.keytype == 0x60) ? 'A' : 'B' - , key64); - } else { - PrintAndLogEx(FAILED, "failed to recover any key"); + uint64_t key64 = -1; + if (mfkey32_moebius(&data, &key64)) { + PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%012" PRIX64) " ]", sprint_hex_inrow(outA, 4), data.sector, (data.keytype == 0x60) ? 'A' : 'B', key64); + } else { + PrintAndLogEx(FAILED, "failed to recover any key"); + } } return PM3_SUCCESS; } @@ -6011,7 +6879,7 @@ static int CmdHF14AMfWipe(const char *Cmd) { if (fptr == NULL) return PM3_ESOFT; - strcpy(keyFilename, fptr); + strncpy(keyFilename, fptr, sizeof(keyFilename) - 1); free(fptr); } @@ -6155,11 +7023,11 @@ static int CmdHF14AMfView(const char *Cmd) { } uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); - if (bytes_read == 320) + if (bytes_read == MIFARE_MINI_MAX_BYTES) block_cnt = MIFARE_MINI_MAXBLOCK; - else if (bytes_read == 2048) + else if (bytes_read == MIFARE_2K_MAX_BYTES) block_cnt = MIFARE_2K_MAXBLOCK; - else if (bytes_read == 4096) + else if (bytes_read == MIFARE_4K_MAX_BYTES) block_cnt = MIFARE_4K_MAXBLOCK; if (verbose) { @@ -6171,12 +7039,376 @@ static int CmdHF14AMfView(const char *Cmd) { if (verbose) { mf_print_keys(block_cnt, dump); + mf_analyse_acl(block_cnt, dump); + } + + int sector = DetectHID(dump, 0x4910); + if (sector > -1) { + // decode it + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, _CYAN_("VIGIK PACS detected")); + + // decode MAD + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + res = MADDecode(dump, NULL, mad, &madlen, false); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "can't decode MAD"); + return res; + } + + typedef union UDATA { + uint8_t *bytes; + mfc_vigik_t *vigik; + } UDATA; + // allocate memory + UDATA d; + d.bytes = calloc(bytes_read, sizeof(uint8_t)); + if (d.bytes == NULL) { + return PM3_EMALLOC; + } + uint16_t dlen = 0; + + // vigik struture sector 0 + uint8_t *pdump = dump; + + memcpy(d.bytes + dlen, pdump, MFBLOCK_SIZE * 3); + dlen += MFBLOCK_SIZE * 3; + pdump += (MFBLOCK_SIZE * 4); // skip sectortrailer + + // extract memory from MAD sectors + for (int i = 0; i <= madlen; i++) { + if (0x4910 == mad[i] || 0x4916 == mad[i]) { + memcpy(d.bytes + dlen, pdump, MFBLOCK_SIZE * 3); + dlen += MFBLOCK_SIZE * 3; + } + + pdump += (MFBLOCK_SIZE * 4); // skip sectortrailer + } + +// convert_mfc_2_arr(pdump, bytes_read, d, &dlen); + vigik_annotate(d.vigik); + free(d.bytes); } free(dump); return PM3_SUCCESS; } +// Read block from Gen4 GTU card +static int CmdHF14AGen4GetBlk(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ggetblk", + "Get block data from magic gen4 GTU card.", + "hf mf ggetblk --blk 0 --> get block 0 (manufacturer)\n" + "hf mf ggetblk --blk 3 -v --> get block 3, decode sector trailer\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("b", "blk", "", "block number"), + arg_lit0("v", "verbose", "verbose output"), + arg_str0("p", "pwd", "", "password 4bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, false); + int b = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len); + CLIParserFree(ctx); + + //validate args + if (b > MIFARE_4K_MAXBLOCK) { + return PM3_EINVARG; + } + + if (pwd_len != 4 && pwd_len != 0) { + PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + uint8_t blockno = (uint8_t)b; + uint8_t data[16] = {0}; + + PrintAndLogEx(NORMAL, "Block: %x", blockno) ; + + int res = mfG4GetBlock(pwd, blockno, data, MAGIC_INIT | MAGIC_OFF); + if (res) { + PrintAndLogEx(ERR, "Can't read block. error=%d", res); + return PM3_ESOFT; + } + + uint8_t sector = mfSectorNum(blockno); + mf_print_sector_hdr(sector); + mf_print_block_one(blockno, data, verbose); + + if (verbose) { + decode_print_st(blockno, data); + } else { + PrintAndLogEx(NORMAL, ""); + } + + return PM3_SUCCESS; +} + +// Load dump to Gen4 GTU card +static int CmdHF14AGen4Load(const char *cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gload", + "Load magic gen4 gtu card with data from (bin/eml/json) dump file\n" + "or from emulator memory.", + "hf mf gload --emu\n" + "hf mf gload -f hf-mf-01020304.eml\n" + "hf mf gload -p AABBCCDD --4k -v -f hf-mf-01020304-dump.bin\n" + "\n" + "Card must be configured beforehand with `script run hf_mf_ultimatecard`.\n" + "Blocks are 16 bytes long." + ); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_str0("p", "pwd", "", "password 4bytes"), + arg_lit0("v", "verbose", "verbose output"), + arg_str0("f", "file", "", "filename of dump"), + arg_lit0(NULL, "emu", "from emulator memory"), + arg_int0(NULL, "start", "", "index of block to start writing (default 0)"), + arg_int0(NULL, "end", "", "index of block to end writing (default last block)"), + arg_param_end + }; + + CLIExecWithReturn(ctx, cmd, argtable, false); + bool m0 = arg_get_lit(ctx, 1); + bool m1 = arg_get_lit(ctx, 2); + bool m2 = arg_get_lit(ctx, 3); + bool m4 = arg_get_lit(ctx, 4); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len); + + bool verbose = arg_get_lit(ctx, 6); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool fill_from_emulator = arg_get_lit(ctx, 8); + + int start = arg_get_int_def(ctx, 9, 0); + int end = arg_get_int_def(ctx, 10, -1); + + CLIParserFree(ctx); + + // validations + if (pwd_len != 4 && pwd_len != 0) { + PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + char s[6]; + memset(s, 0, sizeof(s)); + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + strncpy(s, "Mini", 5); + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + strncpy(s, "1K", 3); + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + strncpy(s, "2K", 3); + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + strncpy(s, "4K", 3); + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + if (fill_from_emulator && (fnlen != 0)) { + PrintAndLogEx(WARNING, "Please specify file or emulator memory, but not both"); + return PM3_EINVARG; + } + + if (!fill_from_emulator && (fnlen == 0)) { + PrintAndLogEx(WARNING, "Please specify file or emulator memory"); + return PM3_EINVARG; + } + + if (end == -1) { + end = block_cnt - 1; + } + + if (start < 0 || end < 0) { + PrintAndLogEx(WARNING, "start and end must be positive integers"); + return PM3_EINVARG ; + } + + if (start > end) { + PrintAndLogEx(WARNING, "start cannot be more than end"); + return PM3_EINVARG ; + } + + if (start >= block_cnt) { + PrintAndLogEx(WARNING, "Last block for Mifare %s is %d. Start is too high.", s, block_cnt - 1) ; + return PM3_EINVARG ; + } + + if (end >= block_cnt) { + PrintAndLogEx(WARNING, "Last block for Mifare %s is %d. End is too high.", s, block_cnt - 1) ; + return PM3_EINVARG ; + } + + uint8_t *data = NULL; + size_t bytes_read = 0; + + if (fill_from_emulator) { + data = calloc(block_cnt * MFBLOCK_SIZE, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "downloading emulator memory"); + if (GetFromDevice(BIG_BUF_EML, data, block_cnt * MFBLOCK_SIZE, 0, NULL, 0, NULL, 2500, false) == false) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(data); + return PM3_ETIMEOUT; + } + + } else { + // read from file + int res = pm3_load_dump(filename, (void **)&data, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); + if (res != PM3_SUCCESS) { + return res; + } + + // check file size corresponds to card size. + if (bytes_read != (block_cnt * MFBLOCK_SIZE)) { + PrintAndLogEx(ERR, "File content error. Read %zu bytes, expected %i", bytes_read, block_cnt * MFBLOCK_SIZE); + if (data != NULL) free(data); + return PM3_EFILE; + } + } + + if (verbose) { + if (fnlen != 0) { + PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); + PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); + } else { + PrintAndLogEx(INFO, "Read %d blocks from emulator memory", block_cnt); + } + } + + PrintAndLogEx(INFO, "Copying to magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s); + PrintAndLogEx(INFO, "Starting block: %d. Ending block: %d.", start, end); + + // copy to card + for (uint16_t blockno = start; blockno <= end; blockno++) { + + // 4k writes can be long, so we split status each 64 block boundary. + if (blockno % 64 == 0 || blockno == start) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + + // write block + uint8_t flags = 0 ; + if (blockno == start) flags |= MAGIC_INIT ; + if (blockno == end) flags |= MAGIC_OFF ; + + int res = mfG4SetBlock(pwd, blockno, data + (blockno * MFBLOCK_SIZE), flags); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Can't set magic card block: %d. error=%d", blockno, res); + PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + free(data); + return PM3_ESOFT; + } + } + PrintAndLogEx(NORMAL, "\n"); + + if (data != NULL) free(data); + + PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from %s", end - start + 1, + (fill_from_emulator ? "emulator memory" : "file")); + PrintAndLogEx(INFO, "Done!"); + return PM3_SUCCESS; +} + +// Write block to Gen4 GTU card +static int CmdHF14AGen4SetBlk(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gsetblk", + "Set block data on a magic gen4 GTU card", + "hf mf gsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("b", "blk", "", "block number"), + arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), + arg_str0("p", "pwd", "", "password 4bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, false); + + int b = arg_get_int_def(ctx, 1, -1); + + uint8_t data[MFBLOCK_SIZE] = {0x00}; + int datalen = 0; + CLIGetHexWithReturn(ctx, 2, data, &datalen); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len); + + CLIParserFree(ctx); + + // validations + if (pwd_len != 4 && pwd_len != 0) { + PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + + if (b < 0 || b >= MIFARE_4K_MAXBLOCK) { + PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b); + return PM3_EINVARG; + } + + if (datalen != MFBLOCK_SIZE) { + PrintAndLogEx(FAILED, "expected 16 bytes data, got %i", datalen); + return PM3_EINVARG; + } + + // write block + PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data))); + + uint8_t blockno = (uint8_t)b; + int res = mfG4SetBlock(pwd, blockno, data, MAGIC_INIT | MAGIC_OFF); + if (res) { + PrintAndLogEx(ERR, "Can't write block. error=%d", res); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + static int CmdHF14AGen4View(const char *Cmd) { CLIParserContext *ctx; @@ -6241,6 +7473,121 @@ static int CmdHF14AGen4View(const char *Cmd) { return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "View magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s); + + // reserve memory + uint16_t bytes = block_cnt * MFBLOCK_SIZE; + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + for (uint16_t i = 0; i < block_cnt; i++) { + + // 4k READs can be long, so we split status each 64 blocks. + if (i % 64 == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + + uint8_t flags = 0 ; + if (i == 0) flags |= MAGIC_INIT ; + if (i + 1 == block_cnt) flags |= MAGIC_OFF ; + + int res = mfG4GetBlock(pwd, i, dump + (i * MFBLOCK_SIZE), flags); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Can't get magic card block: %u. error=%d", i, res); + PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + free(dump); + return PM3_ESOFT; + } + } + + PrintAndLogEx(NORMAL, ""); + mf_print_blocks(block_cnt, dump, verbose); + + if (verbose) { + mf_print_keys(block_cnt, dump); + } + + free(dump); + return PM3_SUCCESS; +} + +// save contents of Gent4 GTU card to file / emulator +static int CmdHF14AGen4Save(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gsave", + "Save `magic gen4 gtu` card memory into three files (BIN/EML/JSON)" + "or into emulator memory", + "hf mf gsave\n" + "hf mf gsave --4k\n" + "hf mf gsave -p DEADBEEF -f hf-mf-01020304.json" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_str0("p", "pwd", "", "password 4bytes"), + arg_str0("f", "file", "", "filename of dump"), + arg_lit0(NULL, "emu", "to emulator memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool m0 = arg_get_lit(ctx, 1); + bool m1 = arg_get_lit(ctx, 2); + bool m2 = arg_get_lit(ctx, 3); + bool m4 = arg_get_lit(ctx, 4); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len); + + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool fill_emulator = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + // validations + if (pwd_len != 4 && pwd_len != 0) { + PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + char s[6]; + memset(s, 0, sizeof(s)); + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + strncpy(s, "Mini", 5); + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + strncpy(s, "1K", 3); + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + strncpy(s, "2K", 3); + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + strncpy(s, "4K", 3); + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + PrintAndLogEx(SUCCESS, "Dumping magic gen4 GTU MIFARE Classic " _GREEN_("%s") " card memory", s); PrintAndLogEx(INFO, "." NOLF); // Select card to get UID/UIDLEN information @@ -6259,12 +7606,12 @@ static int CmdHF14AGen4View(const char *Cmd) { 3: proprietary Anticollision */ uint64_t select_status = resp.oldarg[0]; - if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); - return select_status; + return PM3_SUCCESS; } + // store card info iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); @@ -6278,27 +7625,277 @@ static int CmdHF14AGen4View(const char *Cmd) { for (uint16_t i = 0; i < block_cnt; i++) { - if (mfG4GetBlock(pwd, i, dump + (i * MFBLOCK_SIZE)) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Can't get magic card block: %u", i); + // 4k READs can be long, so we split status each 64 blocks. + if (i % 64 == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + + uint8_t flags = 0 ; + if (i == 0) flags |= MAGIC_INIT ; + if (i + 1 == block_cnt) flags |= MAGIC_OFF ; + + int res = mfG4GetBlock(pwd, i, dump + (i * MFBLOCK_SIZE), flags); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Can't get magic card block: %u. error=%d", i, res); PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); free(dump); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "." NOLF); - fflush(stdout); } PrintAndLogEx(NORMAL, ""); - mf_print_blocks(block_cnt, dump, verbose); - if (verbose) { - mf_print_keys(block_cnt, dump); + if (fill_emulator) { + PrintAndLogEx(INFO, "uploading to emulator memory" NOLF); + // fast push mode + g_conn.block_after_ACK = true; + + size_t offset = 0; + int cnt = 0; + uint16_t bytes_left = bytes ; + + while (bytes_left > 0 && cnt < block_cnt) { + // 4k writes can be long, so we split status each 64 blocks. + if (cnt % 64 == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + + if (bytes_left == MFBLOCK_SIZE) { + // Disable fast mode on last packet + g_conn.block_after_ACK = false; + } + + if (mfEmlSetMem_xt(dump + offset, cnt, 1, MFBLOCK_SIZE) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); + free(dump); + return PM3_ESOFT; + } + + cnt++; + offset += MFBLOCK_SIZE; + bytes_left -= MFBLOCK_SIZE; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", bytes); } + // user supplied filename? + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(fptr, sizeof(filename), "hf-mf-"); + FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); + } + + saveFile(filename, ".bin", dump, bytes); + saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); + iso14a_mf_extdump_t xdump; + xdump.card_info = card; + xdump.dump = dump; + xdump.dumplen = bytes; + saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + free(dump); return PM3_SUCCESS; } +static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gdmcfg", + "Get configuration data from magic gen4 GDM card.", + "hf mf gdmcfg\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "key 6 bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); + CLIParserFree(ctx); + + // validate args + if (keylen != 6 && keylen != 0) { + PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen); + return PM3_EINVARG; + } + + struct p { + uint8_t key[6]; + } PACKED payload; + memcpy(payload.key, key, sizeof(payload.key)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_CONFIG, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_CONFIG, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + uint8_t *d = resp.data.asBytes; + PrintAndLogEx(SUCCESS, "config... %s", sprint_hex(d, resp.length)); + PrintAndLogEx(NORMAL, ""); + } + + return resp.status; +} + +static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gdmsetcfg", + "Set configuration data on a magic gen4 GDM card", + "hf mf gdmsetcfg -d 850000000000000000005A5A00000008" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to write, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t block[MFBLOCK_SIZE] = {0x00}; + int blen = 0; + CLIGetHexWithReturn(ctx, 1, block, &blen); + CLIParserFree(ctx); + + if (blen != MFBLOCK_SIZE) { + PrintAndLogEx(WARNING, "expected %u HEX bytes. got %i", MFBLOCK_SIZE, blen); + return PM3_EINVARG; + } + + struct p { + uint8_t data[MFBLOCK_SIZE]; + } PACKED payload; + + memcpy(payload.data, block, sizeof(payload.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRCFG, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRCFG, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf gdmcfg") "` to verify"); + } else { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + } + return PM3_SUCCESS; +} + +static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gdmsetblk", + "Set block data on a magic gen4 GDM card\n" + "`--force` param is used to override warnings like bad ACL writes.\n" + " if not specified, it will exit if detected", + "hf mf gdmsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" + ); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "blk", "", "block number"), + arg_lit0("a", NULL, "input key type is key A (def)"), + arg_lit0("b", NULL, "input key type is key B"), + arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_lit0(NULL, "force", "override warnings"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int b = arg_get_int_def(ctx, 1, 1); + + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 3)) { + keytype = MF_KEY_B;; + } + + uint8_t block[MFBLOCK_SIZE] = {0x00}; + int blen = 0; + CLIGetHexWithReturn(ctx, 4, block, &blen); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 5, key, &keylen); + + bool force = arg_get_lit(ctx, 6); + CLIParserFree(ctx); + + if (blen != MFBLOCK_SIZE) { + PrintAndLogEx(WARNING, "expected %u HEX bytes. got %i", MFBLOCK_SIZE, blen); + return PM3_EINVARG; + } + + if (b < 0 || b >= MIFARE_4K_MAXBLOCK) { + PrintAndLogEx(FAILED, "target block number out-of-range, got %i", b); + return PM3_EINVARG; + } + + if (keylen != 6 && keylen != 0) { + PrintAndLogEx(FAILED, "Must specify 6 bytes, got " _YELLOW_("%u"), keylen); + return PM3_EINVARG; + } + + uint8_t blockno = (uint8_t)b; + + if (mf_analyse_st_block(blockno, block, force) != PM3_SUCCESS) { + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); + + struct p { + uint8_t blockno; + uint8_t keytype; + uint8_t key[6]; + uint8_t data[MFBLOCK_SIZE]; // data to be written + } PACKED payload; + + payload.blockno = blockno; + payload.keytype = keytype; + memcpy(payload.key, key, sizeof(payload.key)); + memcpy(payload.data, block, sizeof(payload.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRBL, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf rdbl") "` to verify"); + } else if (resp.status == PM3_ETEAROFF) { + return resp.status; + } else { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype `" _YELLOW_("hf mf gdmsetblk -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a'); + } + return PM3_SUCCESS; +} + static int CmdHF14AMfValue(const char *Cmd) { CLIParserContext *ctx; @@ -6308,24 +7905,30 @@ static int CmdHF14AMfValue(const char *Cmd) { "hf mf value --blk 16 -k FFFFFFFFFFFF --inc 10\n" "hf mf value --blk 16 -k FFFFFFFFFFFF -b --dec 10\n" "hf mf value --blk 16 -k FFFFFFFFFFFF -b --get\n" + "hf mf value --blk 16 -k FFFFFFFFFFFF --res --transfer 30 --tk FFFFFFFFFFFF --> transfer block 16 value to block 30 (even if block can't be incremented by ACL)\n" "hf mf value --get -d 87D612007829EDFF87D6120011EE11EE\n" - ); + ); void *argtable[] = { - arg_param_begin, - arg_str0("k", "key", "", "key, 6 hex bytes"), - arg_lit0("a", NULL, "input key type is key A (def)"), - arg_lit0("b", NULL, "input key type is key B"), - arg_u64_0(NULL, "inc", "", "Incremenet value by X (0 - 2147483647)"), - arg_u64_0(NULL, "dec", "", "Dcrement value by X (0 - 2147483647)"), - arg_u64_0(NULL, "set", "", "Set value to X (-2147483647 - 2147483647)"), - arg_lit0(NULL, "get", "Get value from block"), - arg_int0(NULL, "blk", "", "block number"), - arg_str0("d", "data", "", "block data to extract values from (16 hex bytes)"), - arg_param_end + arg_param_begin, + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_lit0("a", NULL, "input key type is key A (def)"), + arg_lit0("b", NULL, "input key type is key B"), + arg_u64_0(NULL, "inc", "", "Increment value by X (0 - 2147483647)"), + arg_u64_0(NULL, "dec", "", "Decrement value by X (0 - 2147483647)"), + arg_u64_0(NULL, "set", "", "Set value to X (-2147483647 - 2147483647)"), + arg_u64_0(NULL, "transfer", "", "Transfer value to other block (after inc/dec/restore)"), + arg_str0(NULL, "tkey", "", "transfer key, 6 hex bytes (if transfer is preformed to other sector)"), + arg_lit0(NULL, "ta", "transfer key type is key A (def)"), + arg_lit0(NULL, "tb", "transfer key type is key B"), + arg_lit0(NULL, "get", "Get value from block"), + arg_lit0(NULL, "res", "Restore (copy value to card buffer, should be used with --transfer)"), + arg_int0(NULL, "blk", "", "block number"), + arg_str0("d", "data", "", "block data to extract values from (16 hex bytes)"), + arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 8, 1); + uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { @@ -6336,32 +7939,45 @@ static int CmdHF14AMfValue(const char *Cmd) { keytype = MF_KEY_B;; } + uint8_t transferkeytype = MF_KEY_A; + if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 10)) { + keytype = MF_KEY_B;; + } + int keylen = 0; uint8_t key[6] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); + int transferkeylen = 0; + uint8_t transferkey[6] = {0}; + CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen); + /* Value /Value Value BLK /BLK BLK /BLK 00000000 FFFFFFFF 00000000 10 EF 10 EF - BLK is used to referece where the backup come from, I suspect its just the current block for the actual value ? + BLK is used to reference where the backup come from, I suspect it's just the current block for the actual value ? increment and decrement are an unsigned value set value is a signed value - We are getting signed and/or bigger values to allow a defult to be set meaning users did not supply that option. + We are getting signed and/or bigger values to allow a default to be set meaning users did not supply that option. */ int64_t incval = (int64_t)arg_get_u64_def(ctx, 4, -1); // Inc by -1 is invalid, so not set. - int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Inc by -1 is invalid, so not set. + int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Dec by -1 is invalid, so not set. int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF); // out of bounds (for int32) so not set - bool getval = arg_get_lit(ctx, 7); - uint8_t block[MFBLOCK_SIZE] = {0x00}; + int64_t trnval = (int64_t)arg_get_u64_def(ctx, 7, -1); // block to transfer to + bool getval = arg_get_lit(ctx, 11); + bool resval = arg_get_lit(ctx, 12); int dlen = 0; uint8_t data[16] = {0}; - CLIGetHexWithReturn(ctx, 9, data, &dlen); + CLIGetHexWithReturn(ctx, 14, data, &dlen); CLIParserFree(ctx); - uint8_t action = 3; // 0 Increment, 1 - Decrement, 2 - Set, 3 - Get, 4 - Decode from data + uint8_t action = 4; // 0 Increment, 1 - Decrement, 2 - Restore, 3 - Set, 4 - Get, 5 - Decode from data uint32_t value = 0; - uint8_t isok = true; // Need to check we only have 1 of inc/dec/set and get the value from the selected option int optionsprovided = 0; @@ -6388,7 +8004,7 @@ static int CmdHF14AMfValue(const char *Cmd) { if (setval != 0x7FFFFFFFFFFFFFFF) { optionsprovided++; - action = 2; + action = 3; if ((setval < -2147483647) || (setval > 2147483647)) { PrintAndLogEx(WARNING, "set value must be between -2147483647 and 2147483647. Got %lli", setval); return PM3_EINVARG; @@ -6396,9 +8012,19 @@ static int CmdHF14AMfValue(const char *Cmd) { value = (uint32_t)setval; } + if (resval) { + if (trnval == -1) { + PrintAndLogEx(WARNING, "You can't use restore without using transfer"); + return PM3_EINVARG; + } + + optionsprovided++; + action = 2; + } + if (dlen != 0) { optionsprovided++; - action = 4; + action = 5; if (dlen != 16) { PrintAndLogEx(WARNING, "date length must be 16 hex bytes long, got %d", dlen); return PM3_EINVARG; @@ -6410,36 +8036,60 @@ static int CmdHF14AMfValue(const char *Cmd) { return PM3_EINVARG; } - // dont want to write value data and break something - if ((blockno == 0) || (mfIsSectorTrailer(blockno))) { - PrintAndLogEx(WARNING, "invlaid block number, should be a data block "); + if (trnval != -1 && action > 2) { + PrintAndLogEx(WARNING, "You can't use transfer without using --inc, --dec or --res"); return PM3_EINVARG; } - if (action < 3) { - if (action <= 1) { // increment/decrement value + if (trnval != -1 && transferkeylen == 0 && mfSectorNum(trnval) != mfSectorNum(blockno)) { + PrintAndLogEx(WARNING, "Transfer is preformed to other sector, but no key for new sector provided"); + return PM3_EINVARG; + } + + // don't want to write value data and break something + if ((blockno == 0) || (mfIsSectorTrailer(blockno)) || (trnval == 0) || (trnval != -1 && mfIsSectorTrailer(trnval))) { + PrintAndLogEx(WARNING, "invalid block number, should be a data block"); + return PM3_EINVARG; + } + + if (action < 4) { + uint8_t isok = true; + if (g_session.pm3_present == false) + return PM3_ENOTTY; + // 0 Increment, 1 - Decrement, 2 - Restore, 3 - Set, 4 - Get, 5 - Decode from data + if (action <= 2) { // increment/decrement/restore value + uint8_t block[MFBLOCK_SIZE] = {0x00}; memcpy(block, (uint8_t *)&value, 4); - uint8_t cmddata[26]; + uint8_t cmddata[34]; memcpy(cmddata, key, sizeof(key)); // Key == 6 data went to 10, so lets offset 9 for inc/dec if (action == 0) - PrintAndLogEx(INFO, "value increment by : %d", value); - else - PrintAndLogEx(INFO, "value decrement by : %d", value); + PrintAndLogEx(INFO, "Value incremented by : %d", value); + if (action == 1) + PrintAndLogEx(INFO, "Value decremented by : %d", value); - PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); - - cmddata[9] = action; // 00 if increment, 01 if decrement. - memcpy(cmddata + 10, block, sizeof(block)); + cmddata[9] = action; // 00 if increment, 01 if decrement, 02 if restore + if (trnval != -1) { + cmddata[10] = trnval; // transfer to block + memcpy(cmddata + 27, transferkey, sizeof(transferkey)); + if (mfSectorNum(trnval) != mfSectorNum(blockno)) + cmddata[33] = 1; // should send nested auth + PrintAndLogEx(INFO, "Transfer block no %d to block %d", blockno, trnval); + } else { + cmddata[10] = 0; + PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + } + memcpy(cmddata + 11, block, sizeof(block)); clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_VALUE, blockno, keytype, 0, cmddata, sizeof(cmddata)); + SendCommandMIX(CMD_HF_MIFARE_VALUE, blockno, keytype, transferkeytype, cmddata, sizeof(cmddata)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(FAILED, "Command execute timeout"); return PM3_ETIMEOUT; } - isok = resp.oldarg[0] & 0xff; + + isok = resp.oldarg[0] & 0xff; } else { // set value // To set a value block (or setup) we can use the normal mifare classic write block // So build the command options can call CMD_HF_MIFARE_WRITEBL @@ -6464,7 +8114,8 @@ static int CmdHF14AMfValue(const char *Cmd) { PrintAndLogEx(FAILED, "Command execute timeout"); return PM3_ETIMEOUT; } - isok = resp.oldarg[0] & 0xff; + + isok = resp.oldarg[0] & 0xff; } if (isok) { @@ -6480,10 +8131,17 @@ static int CmdHF14AMfValue(const char *Cmd) { int32_t readvalue; int res = -1; - if (action == 4) { - res = PM3_SUCCESS; // alread have data from command line + if (action == 5) { + res = PM3_SUCCESS; // already have data from command line } else { - res = mfReadBlock(blockno, keytype, key, data); + if (trnval == -1) { + res = mfReadBlock(blockno, keytype, key, data); + } else { + if (mfSectorNum(trnval) != mfSectorNum(blockno)) + res = mfReadBlock(trnval, transferkeytype, transferkey, data); + else + res = mfReadBlock(trnval, keytype, key, data); + } } if (res == PM3_SUCCESS) { @@ -6520,12 +8178,11 @@ static command_t CommandTable[] = { {"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"}, {"acl", CmdHF14AMfAcl, AlwaysAvailable, "Decode and print MIFARE Classic access rights bytes"}, {"dump", CmdHF14AMfDump, IfPm3Iso14443a, "Dump MIFARE Classic tag to binary file"}, - {"mad", CmdHF14AMfMAD, IfPm3Iso14443a, "Checks and prints MAD"}, - {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"}, + {"mad", CmdHF14AMfMAD, AlwaysAvailable, "Checks and prints MAD"}, {"personalize", CmdHFMFPersonalize, IfPm3Iso14443a, "Personalize UID (MIFARE Classic EV1 only)"}, {"rdbl", CmdHF14AMfRdBl, IfPm3Iso14443a, "Read MIFARE Classic block"}, {"rdsc", CmdHF14AMfRdSc, IfPm3Iso14443a, "Read MIFARE Classic sector"}, - {"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to BLANK tag"}, + {"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to tag"}, {"setmod", CmdHf14AMfSetMod, IfPm3Iso14443a, "Set MIFARE Classic EV1 load modulation strength"}, {"value", CmdHF14AMfValue, AlwaysAvailable, "Value blocks"}, {"view", CmdHF14AMfView, AlwaysAvailable, "Display content from tag dump file"}, @@ -6556,10 +8213,22 @@ static command_t CommandTable[] = { {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GTU") " --------------------------"}, + {"ggetblk", CmdHF14AGen4GetBlk, IfPm3Iso14443a, "Read block from card"}, + {"gload", CmdHF14AGen4Load, IfPm3Iso14443a, "Load dump to card"}, + {"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"}, + {"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"}, -// {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, + {"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"}, + {"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"}, + {"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, + {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, + {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, + {"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, {NULL, NULL, NULL, NULL} + }; static int CmdHelp(const char *Cmd) { diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index 32b6a909a..94610421b 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -21,12 +21,14 @@ #include "common.h" #include "mifare/mfkey.h" -#include "mifare/mifarehost.h" // struct +#include "mifare/mifarehost.h" // structs int CmdHFMF(const char *Cmd); -int CmdHF14AMfELoad(const char *Cmd); // used by cmd hf mfu eload -int CmdHF14AMfDbg(const char *Cmd); // used by cmd hf mfu dbg -int CmdHFMFNDEFRead(const char *Cmd); +int CmdHF14AMfELoad(const char *Cmd); // used by "hf mfu eload" +int CmdHF14AMfDbg(const char *Cmd); // used by "hf mfu dbg" +int CmdHFMFNDEFRead(const char *Cmd); // used by "nfc mf cread" +int CmdHFMFNDEFFormat(const char *Cmd); // used by "nfc mf cformat" +int CmdHFMFNDEFWrite(const char *Cmd); // used by "nfc mf cwrite" void showSectorTable(sector_t *k_sector, uint8_t k_sectorsCount); void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, bool setEmulatorMem, bool verbose); diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index af8726298..b2cd1f7a5 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -18,12 +18,10 @@ // Code heavily modified by B.Kerler :) #include "cmdhfmfdes.h" - #include #include - -#include "commonutil.h" // ARRAYLEN -#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "cmdparser.h" // command_t #include "comms.h" #include "ui.h" #include "cmdhf14a.h" @@ -32,16 +30,16 @@ #include "protocols.h" #include "cmdtrace.h" #include "cliparser.h" -#include "iso7816/apduinfo.h" // APDU manipulation / errorcodes +#include "iso7816/apduinfo.h" // APDU manipulation / errorcodes #include "iso7816/iso7816core.h" // APDU logging -#include "util_posix.h" // msleep +#include "util_posix.h" // msleep #include "mifare/desfirecore.h" #include "mifare/desfiretest.h" #include "mifare/desfiresecurechan.h" -#include "mifare/mifaredefault.h" // default keys +#include "mifare/mifaredefault.h" // default keys #include "crapto1/crapto1.h" #include "fileutils.h" -#include "nfc/ndef.h" // NDEF +//#include "nfc/ndef.h" // NDEF #include "mifare/mad.h" #include "mifare/mifaredefault.h" #include "generator.h" @@ -51,7 +49,7 @@ #define MAX_KEY_LEN 24 #define MAX_KEYS_LIST_LEN 1024 -#define status(x) ( ((uint16_t)(0x91<<8)) + (uint16_t)x ) +#define status(x) ( ((uint16_t)(0x91 << 8)) + (uint16_t)x ) /* static uint8_t desdefaultkeys[3][8] = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //Official {0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47}, @@ -149,6 +147,15 @@ typedef enum { NTAG413DNA, } nxp_cardtype_t; +typedef enum { + DESFIRE_UNKNOWN_PROD = 0, + DESFIRE_PHYSICAL, + DESFIRE_LIGHT_PHYSICAL, + DESFIRE_MICROCONTROLLER, + DESFIRE_JAVACARD, + DESFIRE_HCE, +} nxp_producttype_t; + typedef struct dfname { uint8_t aid[3]; uint8_t fid[2]; @@ -298,6 +305,41 @@ static nxp_cardtype_t getCardType(uint8_t major, uint8_t minor) { return DESFIRE_UNKNOWN; } +// ref: https://www.nxp.com/docs/en/application-note/AN12343.pdf p7 +static nxp_producttype_t getProductType(const uint8_t *versionhw) { + + uint8_t product = versionhw[2]; + + if (product == 0x01) + return DESFIRE_PHYSICAL; + if (product == 0x08) + return DESFIRE_LIGHT_PHYSICAL; + if (product == 0x81 || product == 0x83) + return DESFIRE_MICROCONTROLLER; + if (product == 0x91) + return DESFIRE_JAVACARD; + if (product == 0xA1) + return DESFIRE_HCE; + return DESFIRE_UNKNOWN_PROD; +} + +static const char *getProductTypeStr(const uint8_t *versionhw) { + + uint8_t product = versionhw[2]; + + if (product == 0x01) + return "MIFARE DESFire native IC (physical card)"; + if (product == 0x08) + return "MIFARE DESFire Light native IC (physical card)"; + if (product == 0x81 || product == 0x83) + return "MIFARE DESFire implementation on microcontroller (physical card)"; + if (product == 0x91) + return "MIFARE DESFire applet on Java card / secure element"; + if (product == 0xA1) + return "MIFARE DESFire HCE (MIFARE 2GO)"; + return "UNKNOWN PROD"; +} + static int mfdes_get_info(mfdes_info_res_t *info) { SendCommandNG(CMD_HF_DESFIRE_INFO, NULL, 0); PacketResponseNG resp; @@ -354,6 +396,7 @@ static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signat {"MIFARE Plus Ev1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, {"MIFARE Plus EvX", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}, {"DESFire Ev2 XL", "04CD5D45E50B1502F0BA4656FF37669597E7E183251150F9574CC8DA56BF01C7ABE019E29FEA48F9CE22C3EA4029A765E1BC95A89543BAD1BC"}, + {"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, }; @@ -627,6 +670,12 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(info.uid, info.uidlen)); PrintAndLogEx(SUCCESS, " Batch number: " _GREEN_("%s"), sprint_hex(info.details + 7, 5)); PrintAndLogEx(SUCCESS, " Production date: week " _GREEN_("%02x") " / " _GREEN_("20%02x"), info.details[12], info.details[13]); + + nxp_producttype_t prodtype = getProductType(info.versionHW); + if (prodtype != DESFIRE_UNKNOWN_PROD) { + PrintAndLogEx(SUCCESS, " Product type: %s", getProductTypeStr(info.versionHW)); + } + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); PrintAndLogEx(INFO, " raw: %s", sprint_hex_inrow(info.versionHW, sizeof(info.versionHW))); @@ -646,7 +695,6 @@ static int CmdHF14ADesInfo(const char *Cmd) { PrintAndLogEx(INFO, " Version: " _YELLOW_("%d.%d"), info.versionSW[3], info.versionSW[4]); PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(info.versionSW[5])); PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(info.versionSW[6], false)); - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--------------------------------- " _CYAN_("Card capabilities") " ---------------------------------"); uint8_t major = info.versionSW[3]; @@ -1725,18 +1773,20 @@ static int CmdHF14aDesMAD(const char *Cmd) { } } + PrintAndLogEx(SUCCESS, _CYAN_("Issuer")); + if (foundFFFFFF) { res = DesfireSelectAIDHexNoFieldOn(&dctx, 0xffffff); if (res == PM3_SUCCESS) { uint32_t madver = 0; res = DesfireValueFileOperations(&dctx, 0x00, MFDES_GET_VALUE, &madver); if (res != PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "MAD version : " _RED_("n/a")); + PrintAndLogEx(SUCCESS, "MAD version... " _RED_("n/a")); } else { if (madver == 3) - PrintAndLogEx(SUCCESS, "MAD version : " _GREEN_("3")); + PrintAndLogEx(SUCCESS, "MAD version... " _GREEN_("3")); else - PrintAndLogEx(WARNING, "MAD version : " _YELLOW_("%d"), madver); + PrintAndLogEx(WARNING, "MAD version... " _YELLOW_("%d"), madver); } uint8_t data[250] = {0}; @@ -1744,10 +1794,10 @@ static int CmdHF14aDesMAD(const char *Cmd) { res = DesfireReadFile(&dctx, 01, 0x000000, 0, data, &datalen); if (res != PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Card Holder : " _RED_("n/a")); + PrintAndLogEx(SUCCESS, "Card Holder... " _RED_("n/a")); } else { if (datalen > 0) { - PrintAndLogEx(SUCCESS, "Card Holder : "); + PrintAndLogEx(SUCCESS, "Card Holder... "); if (verbose) { print_buffer_with_offset(data, datalen, 0, true); PrintAndLogEx(NORMAL, ""); @@ -1755,7 +1805,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { MADCardHolderInfoDecode(data, datalen, verbose); PrintAndLogEx(NORMAL, ""); } else { - PrintAndLogEx(SUCCESS, "Card Holder : " _YELLOW_("none")); + PrintAndLogEx(SUCCESS, "Card Holder... " _YELLOW_("none")); } } @@ -1779,16 +1829,41 @@ static int CmdHF14aDesMAD(const char *Cmd) { } size_t madappcount = 0; - PrintAndLogEx(SUCCESS, "Applications : "); + PrintAndLogEx(SUCCESS, ""); + PrintAndLogEx(SUCCESS, _CYAN_("Applications")); for (int i = 0; i < PICCInfo.appCount; i++) { if ((AppList[i].appNum & 0xf00000) == 0xf00000) { DesfirePrintMADAID(AppList[i].appNum, verbose); + + // read file 0, 1, 2 + res = DesfireSelectAIDHexNoFieldOn(&dctx, AppList[i].appNum); + if (res == PM3_SUCCESS) { + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + res = DesfireGetFileIDList(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetFileIDList command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (buflen > 0) { + for (int j = 0; j < buflen; j++) { + PrintAndLogEx(INFO, " File ID... %02x", buf[j]); + } + } + } + madappcount++; } } - if (madappcount == 0) + if (madappcount == 0) { PrintAndLogEx(SUCCESS, "There is no MAD applications on the card"); + DropField(); + return PM3_SUCCESS; + } DropField(); return PM3_SUCCESS; @@ -2122,9 +2197,9 @@ static int CmdHF14ADesSetConfiguration(const char *Cmd) { "\n" "hf mfdes setconfig --param 03 --data 0428 -> set SAK\n" "hf mfdes setconfig --param 02 --data 0875778102637264 -> set ATS (first byte - length)\n" - "hf mfdes setconfig --isoid df01 -t aes -s ev2 --param 05 --data 00000000020000000000 -> set LRP mode enable for Desfire Light\n" - "hf mfdes setconfig --isoid df01 -t aes -s ev2 --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light\n" - "hf mfdes setconfig --isoid df01 -t aes -s lrp --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light via lrp"); + "hf mfdes setconfig --isoid df01 -t aes --schann ev2 --param 05 --data 00000000020000000000 -> set LRP mode enable for Desfire Light\n" + "hf mfdes setconfig --isoid df01 -t aes --schann ev2 --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light\n" + "hf mfdes setconfig --isoid df01 -t aes --schann lrp --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light via lrp"); void *argtable[] = { arg_param_begin, @@ -2231,7 +2306,7 @@ static int CmdHF14ADesChangeKey(const char *Cmd) { "but for APP keys crypto algorithm is set by createapp command and can't be changed wo application delete\n" "\n" "hf mfdes changekey --aid 123456 -> execute with default factory setup. change des key 0 in the app 123456 from 00..00 to 00..00\n" - "hf mfdes changekey --isoid df01 -t aes -s lrp --newkeyno 01 -> change key 01 via lrp channel" + "hf mfdes changekey --isoid df01 -t aes --schann lrp --newkeyno 01 -> change key 01 via lrp channel" "hf mfdes changekey -t des --newalgo aes --newkey 11223344556677889900112233445566 --newver a5 -> change card master key to AES one\n" "hf mfdes changekey --aid 123456 -t aes --key 00000000000000000000000000000000 --newkey 11223344556677889900112233445566 -> change app master key\n" "hf mfdes changekey --aid 123456 -t des -n 0 --newkeyno 1 --oldkey 5555555555555555 --newkey 1122334455667788 -> change key 1 with auth from key 0\n" @@ -2632,7 +2707,7 @@ static int CmdHF14ADesGetUID(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes getuid", "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Any card's key needs to be provided. ", "hf mfdes getuid -> execute with default factory setup\n" - "hf mfdes getuid --isoid df01 -t aes -s lrp -> for desfire lights default settings"); + "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings"); void *argtable[] = { arg_param_begin, @@ -3301,7 +3376,7 @@ static int CmdHF14ADesGetFileISOIDs(const char *Cmd) { "hf mfdes getfileisoids --aid 123456 -> execute with defaults from `default` command\n" "hf mfdes getfileisoids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup\n" "hf mfdes getfileisoids --isoid df01 -> get iso file ids from Desfire Light with factory card settings\n" - "hf mfdes getfileisoids --isoid df01 -s lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication"); + "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication"); void *argtable[] = { arg_param_begin, @@ -3441,6 +3516,7 @@ static int CmdHF14ADesGetFileSettings(const char *Cmd) { DesfirePrintFileSettings(buf, buflen); + PrintAndLogEx(NORMAL, ""); DropField(); return PM3_SUCCESS; } @@ -3497,7 +3573,6 @@ static int DesfireCreateFileParameters( bool userawfrights = false; if (frightsid) { if (CLIGetUint32Hex(ctx, frightsid, 0xeeee, &frights, &userawfrights, 2, "File rights must have 2 bytes length")) { - CLIParserFree(ctx); return PM3_EINVARG; } } @@ -3543,7 +3618,7 @@ static int CmdHF14ADesChFileSettings(const char *Cmd) { "hf mfdes chfilesettings --aid 123456 --fid 01 --amode plain --rrights free --wrights free --rwrights free --chrights key0 -> change file settings app=123456, file=01 with defaults from `default` command\n" "hf mfdes chfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 --rawdata 00EEEE -> execute with default factory setup\n" "hf mfdes chfilesettings --aid 123456 --fid 01 --rawdata 810000021f112f22 -> change file settings with additional rights for keys 1 and 2\n" - "hf mfdes chfilesettings --isoid df01 --fid 00 --amode plain --rawrights eee0 -s lrp -t aes -> change file settings via lrp channel"); + "hf mfdes chfilesettings --isoid df01 --fid 00 --amode plain --rawrights eee0 --schann lrp -t aes -> change file settings via lrp channel"); void *argtable[] = { arg_param_begin, @@ -4068,8 +4143,8 @@ static int CmdHF14ADesCreateTrMACFile(const char *Cmd) { "hf mfdes createmacfile --aid 123456 --fid 01 --rawrights 0FF0 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file with parameters. Rights from default. Authentication with defaults from `default` command\n" "hf mfdes createmacfile --aid 123456 --fid 01 --amode plain --rrights free --wrights deny --rwrights free --chrights key0 --mackey 00112233445566778899aabbccddeeff -> create file app=123456, file=01, with key, and mentioned rights with defaults from `default` command\n" "hf mfdes createmacfile -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup. key and keyver == 0x00..00\n" - "hf mfdes createmacfile --isoid df01 --fid 0f -s lrp -t aes --rawrights 0FF0 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel\n" - "hf mfdes createmacfile --isoid df01 --fid 0f -s lrp -t aes --rawrights 0F10 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel with CommitReaderID command enable"); + "hf mfdes createmacfile --isoid df01 --fid 0f --schann lrp -t aes --rawrights 0FF0 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel\n" + "hf mfdes createmacfile --isoid df01 --fid 0f --schann lrp -t aes --rawrights 0F10 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel with CommitReaderID command enable"); void *argtable[] = { arg_param_begin, @@ -4186,7 +4261,7 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes deletefile", "Delete file from application. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes deletefile --aid 123456 --fid 01 -> delete file for: app=123456, file=01 with defaults from `default` command\n" - "hf mfdes deletefile --isoid df01 --fid 0f -s lrp -t aes -> delete file for lrp channel"); + "hf mfdes deletefile --isoid df01 --fid 0f --schann lrp -t aes -> delete file for lrp channel"); void *argtable[] = { arg_param_begin, @@ -4232,7 +4307,7 @@ static int CmdHF14ADesDeleteFile(const char *Cmd) { CLIParserFree(ctx); if (fnum > 0x1F) { - PrintAndLogEx(ERR, "File number range is invalid (exp 0 - 31), got %d", fnum); + PrintAndLogEx(ERR, "File number range is invalid (exp 0x00 - 0x1f), got 0x%02x", fnum); return PM3_EINVARG; } @@ -4263,8 +4338,8 @@ static int CmdHF14ADesValueOperations(const char *Cmd) { "hf mfdes value --aid 123456 --fid 01 -> get value app=123456, file=01 with defaults from `default` command\n" "hf mfdes value --aid 123456 --fid 01 --op credit -d 00000001 -> credit value app=123456, file=01 with defaults from `default` command\n" "hf mfdes value -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> get value with default factory setup\n" - "hf mfdes val --isoid df01 --fid 03 -s lrp -t aes -n 1 --op credit --d 00000001 -m encrypt -> credit value in the lrp encrypted mode\n" - "hf mfdes val --isoid df01 --fid 03 -s lrp -t aes -n 1 --op get -m plain -> get value in plain (nevertheless of mode) works for desfire light (look SetConfiguration option 0x09)"); + "hf mfdes val --isoid df01 --fid 03 --schann lrp -t aes -n 1 --op credit --d 00000001 -m encrypt -> credit value in the lrp encrypted mode\n" + "hf mfdes val --isoid df01 --fid 03 --schann lrp -t aes -n 1 --op get -m plain -> get value in plain (nevertheless of mode) works for desfire light (look SetConfiguration option 0x09)"); void *argtable[] = { arg_param_begin, @@ -4436,7 +4511,7 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes clearrecfile", "Clear record file. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "hf mfdes clearrecfile --aid 123456 --fid 01 -> clear record file for: app=123456, file=01 with defaults from `default` command\n" - "hf mfdes clearrecfile --isoid df01 --fid 01 -s lrp -t aes -n 3 -> clear record file for lrp channel with key number 3"); + "hf mfdes clearrecfile --isoid df01 --fid 01 --schann lrp -t aes -n 3 -> clear record file for lrp channel with key number 3"); void *argtable[] = { arg_param_begin, @@ -4482,7 +4557,7 @@ static int CmdHF14ADesClearRecordFile(const char *Cmd) { CLIParserFree(ctx); if (fnum > 0x1F) { - PrintAndLogEx(ERR, "File number range is invalid (exp 0 - 31), got %d", fnum); + PrintAndLogEx(ERR, "File number range is invalid (exp 0x00 - 0x1f), got 0x%02x", fnum); return PM3_EINVARG; } @@ -4768,14 +4843,17 @@ static int DesfileReadFileAndPrint(DesfireContext_t *dctx, } } - if (resplen > 0) { + if (resplen > 0 && reclen > 0) { size_t reccount = resplen / reclen; PrintAndLogEx(SUCCESS, "Read %zu bytes from file 0x%02x from record %d record count %zu record length %zu", resplen, fnum, offset, reccount, reclen); - if (reccount > 1) + if (reccount > 1) { PrintAndLogEx(SUCCESS, "Lastest record at the bottom."); + } + for (int i = 0; i < reccount; i++) { - if (i != 0) + if (i != 0) { PrintAndLogEx(SUCCESS, "Record %zu", reccount - (i + offset + 1)); + } print_buffer_with_offset(&resp[i * reclen], reclen, offset, (i == 0)); } } else { @@ -4835,8 +4913,8 @@ static int CmdHF14ADesReadData(const char *Cmd) { "hf mfdes read --isoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands\n" "hf mfdes read --isoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000001 -> get one record (number 5) from file 1100 via iso commands\n" "hf mfdes read --isoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000000 -> get all record (from 5 to 1) from file 1100 via iso commands\n" - "hf mfdes read --isoid df01 --fid 00 -s lrp -t aes --length 000010 -> read via lrp channel\n" - "hf mfdes read --isoid df01 --fid 00 -s ev2 -t aes --length 000010 --isochain -> read Desfire Light via ev2 channel"); + "hf mfdes read --isoid df01 --fid 00 --schann lrp -t aes --length 000010 -> read via lrp channel\n" + "hf mfdes read --isoid df01 --fid 00 --schann ev2 -t aes --length 000010 --isochain -> read Desfire Light via ev2 channel"); void *argtable[] = { arg_param_begin, @@ -4914,7 +4992,7 @@ static int CmdHF14ADesReadData(const char *Cmd) { CLIParserFree(ctx); if (fnum > 0x1F) { - PrintAndLogEx(ERR, "File number range is invalid (exp 0 - 31), got %d", fnum); + PrintAndLogEx(ERR, "File number range is invalid (exp 0x00 - 0x1f), got 0x%02x", fnum); return PM3_EINVARG; } @@ -4995,7 +5073,7 @@ static int CmdHF14ADesWriteData(const char *Cmd) { "Write data from file. Key needs to be provided or flag --no-auth set (depend on file settings).", "In the mode with CommitReaderID to decode previous reader id command needs to read transaction counter via dump/read command and specify --trkey\n" "\n" - "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write file: app=123456, file=01, offset=0, get file type from card. use default channel settings from `default` command\n" + "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> AID 123456, file=01, offset=0, get file type from card. use default channel settings from `default` command\n" "hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --0ffset 000100 -> write data to std file with offset 0x100\n" "hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --commit -> write data to backup file with commit\n" "hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 -> increment value file\n" @@ -5007,7 +5085,7 @@ static int CmdHF14ADesWriteData(const char *Cmd) { "hf mfdes write --isoid 1234 --fileisoid 1000 --type data -c iso -d 01020304 -> write data to std/backup file via iso commandset\n" "hf mfdes write --isoid 1234 --fileisoid 2000 --type record -c iso -d 01020304 -> send record to record file via iso commandset\n" "hf mfdes write --aid 123456 --fid 01 -d 01020304 --readerid 010203 -> write data to file with CommitReaderID command before write and CommitTransaction after write\n" - "hf mfdes write --isoid df01 --fid 04 -d 01020304 --trkey 00112233445566778899aabbccddeeff --readerid 5532 -t aes -s lrp -> advanced CommitReaderID via lrp channel sample"); + "hf mfdes write --isoid df01 --fid 04 -d 01020304 --trkey 00112233445566778899aabbccddeeff --readerid 5532 -t aes --schann lrp -> advanced CommitReaderID via lrp channel sample"); void *argtable[] = { arg_param_begin, @@ -5113,7 +5191,7 @@ static int CmdHF14ADesWriteData(const char *Cmd) { CLIParserFree(ctx); if (fnum > 0x1F) { - PrintAndLogEx(ERR, "File number range is invalid (exp 0 - 31), got %d", fnum); + PrintAndLogEx(ERR, "File number range is invalid (exp 0x00 - 0x1f), got 0x%02x", fnum); return PM3_EINVARG; } @@ -5343,9 +5421,10 @@ static int CmdHF14ADesWriteData(const char *Cmd) { static int CmdHF14ADesLsFiles(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes lsfiles", - "Show file list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", - "hf mfdes lsfiles --aid 123456 -> show file list for: app=123456 with defaults from `default` command" - "hf mfdes lsfiles --isoid df01 --no-auth -> show files from desfire light"); + "This commands List files inside application AID / ISOID.\n" + "Master key needs to be provided or flag --no-auth set (depend on cards settings).", + "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds\n" + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light"); void *argtable[] = { arg_param_begin, @@ -5484,7 +5563,7 @@ static int CmdHF14ADesDump(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes dump", "For each application show fil list and then file content. Key needs to be provided for authentication or flag --no-auth set (depend on cards settings).", "hf mfdes dump --aid 123456 -> show file dump for: app=123456 with channel defaults from `default` command/n" - "hf mfdes dump --isoid df01 -s lrp -t aes --length 000090 -> lrp default settings with length limit"); + "hf mfdes dump --isoid df01 --schann lrp -t aes --length 000090 -> lrp default settings with length limit"); void *argtable[] = { arg_param_begin, diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index a8455deab..4cb7873af 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -1043,7 +1043,7 @@ static int read_nonce_file(char *filename) { if (filename == NULL) { PrintAndLogEx(WARNING, "Filename is NULL"); - return 1; + return PM3_EINVARG; } FILE *fnonces = NULL; char progress_text[80] = ""; @@ -1052,7 +1052,7 @@ static int read_nonce_file(char *filename) { num_acquired_nonces = 0; if ((fnonces = fopen(filename, "rb")) == NULL) { PrintAndLogEx(WARNING, "Could not open file " _YELLOW_("%s"), filename); - return 1; + return PM3_EFILE; } snprintf(progress_text, 80, "Reading nonces from file " _YELLOW_("%s"), filename); @@ -1061,7 +1061,7 @@ static int read_nonce_file(char *filename) { if (bytes_read != 6) { PrintAndLogEx(ERR, "File reading error."); fclose(fnonces); - return 1; + return PM3_EFILE; } cuid = bytes_to_num(read_buf, 4); uint8_t trgBlockNo = bytes_to_num(read_buf + 4, 1); @@ -1095,7 +1095,7 @@ static int read_nonce_file(char *filename) { } if (got_match == false) { PrintAndLogEx(FAILED, "No match for the First_Byte_Sum (%u), is the card a genuine MFC Ev1? ", first_byte_Sum); - return 1; + return PM3_ESOFT; } return PM3_SUCCESS; } @@ -1417,7 +1417,7 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (WaitForResponseTimeout(CMD_ACK, &resp, 3000) == false) { DropField(); - return 1; + return PM3_ETIMEOUT; } // error during nested_hard @@ -1432,7 +1432,7 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if ((fnonces = fopen(filename, "wb")) == NULL) { PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), filename); DropField(); - return 3; + return PM3_EFILE; } snprintf(progress_text, 80, "Writing acquired nonces to binary file " _YELLOW_("%s"), filename); @@ -1481,7 +1481,10 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (got_match == false) { PrintAndLogEx(FAILED, "No match for the First_Byte_Sum (%u), is the card a genuine MFC Ev1? ", first_byte_Sum); - return 4; + if (nonce_file_write) { + fclose(fnonces); + } + return PM3_EWRONGANSWER; } hardnested_stage |= CHECK_2ND_BYTES; @@ -1515,7 +1518,7 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ fclose(fnonces); } DropField(); - return 1; + return PM3_ETIMEOUT; } // error during nested_hard @@ -2250,8 +2253,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc init_nonce_memory(); update_reduction_rate(0.0, true); - if (simulate_acquire_nonces() != PM3_SUCCESS) { - return 3; + int res = simulate_acquire_nonces(); + if (res != PM3_SUCCESS) { + return res; } set_test_state(best_first_bytes[0]); @@ -2310,7 +2314,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_statelist_cache(); free_candidates_memory(candidates); candidates = NULL; - if (!key_found) { + if (key_found == false) { // update the statistics nonces[best_first_bytes[0]].sum_a8_guess[j].prob = 0; nonces[best_first_bytes[0]].sum_a8_guess[j].num_states = 0; @@ -2341,7 +2345,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_part_sum_bitarrays(); } fclose(fstats); + } else { + start_time = msclock(); print_progress_header(); snprintf(progress_text, sizeof(progress_text), "Brute force benchmark: %1.0f million (2^%1.1f) keys/s", brute_force_per_second / 1000000, log(brute_force_per_second) / log(2.0)); @@ -2353,30 +2359,32 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc init_nonce_memory(); update_reduction_rate(0.0, true); + int res; if (nonce_file_read) { // use pre-acquired data from file nonces.bin - if (read_nonce_file(filename) != 0) { + res = read_nonce_file(filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); free_bitarray(all_bitflips_bitarray[ODD_STATE]); free_bitarray(all_bitflips_bitarray[EVEN_STATE]); free_sum_bitarrays(); free_part_sum_bitarrays(); - return 3; + return res; } hardnested_stage = CHECK_1ST_BYTES | CHECK_2ND_BYTES; update_nonce_data(false); float brute_force_depth; shrink_key_space(&brute_force_depth); } else { // acquire nonces. - uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow, filename); - if (is_OK != 0) { + res = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow, filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); free_bitarray(all_bitflips_bitarray[ODD_STATE]); free_bitarray(all_bitflips_bitarray[EVEN_STATE]); free_sum_bitarrays(); free_part_sum_bitarrays(); - return is_OK; + return res; } } @@ -2437,7 +2445,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_statelist_cache(); free_candidates_memory(candidates); candidates = NULL; - if (!key_found) { + if (key_found == false) { // update the statistics nonces[best_first_bytes[0]].sum_a8_guess[j].prob = 0; nonces[best_first_bytes[0]].sum_a8_guess[j].num_states = 0; @@ -2453,5 +2461,5 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_sum_bitarrays(); free_part_sum_bitarrays(); } - return 0; + return PM3_SUCCESS; } diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index dcd73fb35..aa7480953 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -65,9 +65,9 @@ static char *getCardSizeStr(uint8_t fsize) { // is LSB set? if (fsize & 1) - snprintf(retStr, sizeof(buf), "0x%02X (" _GREEN_("%d - %d bytes") ")", fsize, usize, lsize); + snprintf(retStr, sizeof(buf), "0x%02X ( " _GREEN_("%d - %d bytes") " )", fsize, usize, lsize); else - snprintf(retStr, sizeof(buf), "0x%02X (" _GREEN_("%d bytes") ")", fsize, lsize); + snprintf(retStr, sizeof(buf), "0x%02X ( " _GREEN_("%d bytes") " )", fsize, lsize); return buf; } @@ -77,14 +77,14 @@ static char *getProtocolStr(uint8_t id, bool hw) { char *retStr = buf; if (id == 0x04) { - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("ISO 14443-3 MIFARE, 14443-4") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("ISO 14443-3 MIFARE, 14443-4") " )", id); } else if (id == 0x05) { if (hw) - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("ISO 14443-2, 14443-3") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("ISO 14443-2, 14443-3") " )", id); else - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("ISO 14443-3, 14443-4") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("ISO 14443-3, 14443-4") " )", id); } else { - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("Unknown") ")", id); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("Unknown") " )", id); } return buf; } @@ -95,19 +95,19 @@ static char *getVersionStr(uint8_t major, uint8_t minor) { char *retStr = buf; if (major == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire MF3ICD40") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire MF3ICD40") " )", major, minor); else if (major == 0x01 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV1") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV1") " )", major, minor); else if (major == 0x12 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV2") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV2") " )", major, minor); else if (major == 0x33 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire EV3") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire EV3") " )", major, minor); else if (major == 0x30 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("DESFire Light") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor); else if (major == 0x11 && minor == 0x00) - snprintf(retStr, sizeof(buf), "%x.%x (" _GREEN_("Plus EV1") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV1") " )", major, minor); else - snprintf(retStr, sizeof(buf), "%x.%x (" _YELLOW_("Unknown") ")", major, minor); + snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor); return buf; } @@ -118,16 +118,16 @@ static char *getTypeStr(uint8_t type) { switch (type) { case 1: - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("DESFire") ")", type); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("DESFire") " )", type); break; case 2: - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("Plus") ")", type); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("Plus") " )", type); break; case 3: - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("Ultralight") ")", type); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("Ultralight") " )", type); break; case 4: - snprintf(retStr, sizeof(buf), "0x%02X (" _YELLOW_("NTAG") ")", type); + snprintf(retStr, sizeof(buf), "0x%02X ( " _YELLOW_("NTAG") " )", type); break; default: break; @@ -171,7 +171,8 @@ static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature #define PUBLIC_PLUS_ECDA_KEYLEN 57 const ecdsa_publickey_t nxp_plus_public_keys[] = { {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, - {"MIFARE Plus Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"} + {"MIFARE Plus Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}, + {"MIFARE Plus Trojka", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"} }; uint8_t i; @@ -461,7 +462,7 @@ static int CmdHFMFPWritePerso(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "Verbose mode"), + arg_lit0("v", "verbose", "Verbose output"), arg_str1(NULL, "ki", "", " Key number, 2 hex bytes"), arg_str0(NULL, "key", "", " Key, 16 hex bytes"), arg_param_end @@ -513,8 +514,8 @@ static int CmdHFMFPWritePerso(const char *Cmd) { PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return PM3_ESOFT; } - PrintAndLogEx(INFO, "Write (" _GREEN_("ok") " )"); + PrintAndLogEx(INFO, "Write ( " _GREEN_("ok") " )"); return PM3_SUCCESS; } @@ -713,18 +714,18 @@ static int CmdHFMFPRdbl(const char *Cmd) { } if (blockn > 255) { - PrintAndLogEx(ERR, " must be in range [0..255] instead of: %d", blockn); + PrintAndLogEx(ERR, " must be in range [0..255] got: %d", blockn); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", keylen); return PM3_EINVARG; } // 3 blocks - wo iso14443-4 chaining if (blocksCount > 3) { - PrintAndLogEx(ERR, "blocks count must be less than 3 instead of: %d", blocksCount); + PrintAndLogEx(ERR, "blocks count must be less than 3. got: %d", blocksCount); return PM3_EINVARG; } @@ -823,12 +824,12 @@ static int CmdHFMFPRdsc(const char *Cmd) { } if (sectorNum > 39) { - PrintAndLogEx(ERR, " must be in range [0..39] instead of: %d", sectorNum); + PrintAndLogEx(ERR, " must be in range [0..39] got: %d", sectorNum); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", keylen); return PM3_EINVARG; } @@ -925,17 +926,17 @@ static int CmdHFMFPWrbl(const char *Cmd) { } if (blockNum > 255) { - PrintAndLogEx(ERR, " must be in range [0..255] instead of: %d", blockNum); + PrintAndLogEx(ERR, " must be in range [0..255] got: %d", blockNum); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", keylen); return PM3_EINVARG; } if (datainlen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", datainlen); + PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", datainlen); return PM3_EINVARG; } @@ -1372,7 +1373,7 @@ static int CmdHFMFPMAD(const char *Cmd) { if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector0, verbose)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); - return 2; + return PM3_ESOFT; } PrintAndLogEx(NORMAL, ""); @@ -1392,7 +1393,7 @@ static int CmdHFMFPMAD(const char *Cmd) { if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); - return 2; + return PM3_ESOFT; } MAD2DecodeAndPrint(sector10, swapmad, verbose); @@ -1403,7 +1404,7 @@ static int CmdHFMFPMAD(const char *Cmd) { size_t madlen = 0; if (MADDecode(sector0, sector10, mad, &madlen, swapmad)) { PrintAndLogEx(ERR, "can't decode MAD"); - return 10; + return PM3_EWRONGANSWER; } // copy default NDEF key diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 38c2b7d4a..963786ebc 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -65,7 +65,7 @@ static uint8_t default_3des_keys[][16] = { static uint8_t default_pwd_pack[][4] = { {0xFF, 0xFF, 0xFF, 0xFF}, // PACK 0x00,0x00 -- factory default - {0x4E, 0x45, 0x78, 0x54}, + {0x4E, 0x45, 0x78, 0x54}, // NExT }; static uint32_t UL_TYPES_ARRAY[] = { @@ -392,6 +392,12 @@ static int ul_auth_select(iso14a_card_select_t *card, TagTypeUL_t tagtype, bool return PM3_SUCCESS; } +static int ntagtt_getTamperStatus(uint8_t *response, uint16_t responseLength) { + uint8_t cmd[] = {NTAGTT_CMD_READ_TT, 0x00}; + int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); + return len; +} + static int ulev1_getVersion(uint8_t *response, uint16_t responseLength) { uint8_t cmd[] = {MIFARE_ULEV1_VERSION}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); @@ -437,7 +443,7 @@ static int ul_fudan_check(void) { if (!ul_select(&card)) return UL_ERROR; - uint8_t cmd[4] = {0x30, 0x00, 0x02, 0xa7}; //wrong crc on purpose should be 0xa8 + uint8_t cmd[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa7}; //wrong crc on purpose should be 0xa8 clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 4, 0, cmd, sizeof(cmd)); PacketResponseNG resp; @@ -606,14 +612,13 @@ static int ndef_print_CC(uint8_t *data) { uint8_t mlrule = (data[3] & 0x06) >> 1; uint8_t mbread = (data[3] & 0x01); - PrintAndLogEx(SUCCESS, " Additional feature information"); - PrintAndLogEx(SUCCESS, " %02X", data[3]); - PrintAndLogEx(SUCCESS, " 00000000"); - PrintAndLogEx(SUCCESS, " xxx - %02X: RFU ( %s )", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(SUCCESS, " x - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support"); - PrintAndLogEx(SUCCESS, " x - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support"); - PrintAndLogEx(SUCCESS, " xx - %02X: RFU ( %s )", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(SUCCESS, " x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support"); + PrintAndLogEx(SUCCESS, " %02X: Additional feature information", data[3]); + PrintAndLogEx(SUCCESS, " %s", sprint_bin(&data[3], 1)); + PrintAndLogEx(SUCCESS, " xxx..... - %02X: RFU ( %s )", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(SUCCESS, " ...x.... - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support"); + PrintAndLogEx(SUCCESS, " ....x... - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support"); + PrintAndLogEx(SUCCESS, " .....xx. - %02X: RFU ( %s )", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(SUCCESS, " .......x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support"); return PM3_SUCCESS; } @@ -749,8 +754,75 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: %s", startPage, startPage, sprint_hex(data, 4)); - if ((tagtype & (NTAG_213_F | NTAG_213_TT | NTAG_216_F))) { - uint8_t mirror_conf = (data[0] & 0xC0); + //NTAG213TT has different ASCII mirroring options and config bytes interpretation from other ulev1 class tags + if (tagtype & NTAG_213_TT) { + uint8_t mirror_conf = ((data[0] & 0xE0) >> 5); + uint8_t mirror_byte = ((data[0] & 0x18) >> 3); + uint8_t mirror_page = data[2]; + + switch (mirror_conf) { + case 0: + PrintAndLogEx(INFO, " - no ASCII mirror"); + break; + case 1: + PrintAndLogEx(INFO, " - UID ASCII mirror"); + break; + case 2: + PrintAndLogEx(INFO, " - NFC counter ASCII mirror"); + break; + case 3: + PrintAndLogEx(INFO, " - UID and NFC counter ASCII mirror"); + break; + case 4: + PrintAndLogEx(INFO, " - tag tamper ASCII mirror"); + break; + case 5: + PrintAndLogEx(INFO, " - UID and tag tamper ASCII mirror"); + break; + case 6: + PrintAndLogEx(INFO, " - NFC counter and tag tamper ASCII mirror"); + break; + case 7: + PrintAndLogEx(INFO, " - UID, NFC counter, and tag tamper ASCII mirror"); + break; + default: + break; + } + + if (mirror_conf) { + uint8_t mirror_user_mem_start_byte = (4 * (mirror_page - 4)) + mirror_byte; + uint8_t bytes_required_for_mirror_data = 0; + + switch (mirror_conf) { + case 1: + bytes_required_for_mirror_data = 14; + break; + case 2: + bytes_required_for_mirror_data = 6; + break; + case 3: + bytes_required_for_mirror_data = 8; + break; + case 4: + bytes_required_for_mirror_data = 21; + break; + case 5: + bytes_required_for_mirror_data = 23; + break; + case 6: + bytes_required_for_mirror_data = 15; + break; + case 7: + bytes_required_for_mirror_data = 30; + break; + default: + break; + } + PrintAndLogEx(INFO, " mirror start page %02X | byte pos %02X - %s", mirror_page, mirror_byte, (mirror_page >= 0x4 && ((mirror_user_mem_start_byte + bytes_required_for_mirror_data) <= 144)) ? _GREEN_("OK") : _YELLOW_("Invalid value")); + } + + } else if (tagtype & (NTAG_213_F | NTAG_216_F)) { + uint8_t mirror_conf = ((data[0] & 0xC0) >> 6); uint8_t mirror_byte = (data[0] & 0x30); bool sleep_en = (data[0] & 0x08); strg_mod_en = (data[0] & 0x04); @@ -792,7 +864,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st break; } // valid mirror start page and byte position within start page. - if ((tagtype & NTAG_213_F) || (tagtype & NTAG_213_TT)) { + if (tagtype & NTAG_213_F) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0x24) ? "OK" : "Invalid value"); break;} @@ -823,6 +895,35 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st else PrintAndLogEx(INFO, " - pages don't need authentication"); + uint8_t tt_enabled = 0; + uint8_t tt_message[4] = {0x00}; + uint8_t tt_msg_resp_len = 0; + uint8_t tt_status_resp[5] = {0x00}; + + if (tagtype & NTAG_213_TT) { + tt_enabled = (data[1] & 0x02); + tt_msg_resp_len = ul_read(45, tt_message, 4); + + PrintAndLogEx(INFO, " - tamper detection feature is %s" + , (tt_enabled) ? _GREEN_("ENABLED") : "disabled" + ); + + switch (data[1] & 0x06) { + case 0x00: + PrintAndLogEx(INFO, " - tamper message is unlocked and read/write enabled"); + break; + case 0x02: + PrintAndLogEx(INFO, " - tamper message is reversibly read/write locked in memory while the tamper feature is enabled"); + break; + case 0x04: + case 0x06: + PrintAndLogEx(INFO, " - tamper message is permanently read/write locked in memory"); + break; + default: + break; + } + } + PrintAndLogEx(INFO, " cfg1 [%u/0x%02X]: %s", startPage + 1, startPage + 1, sprint_hex(data + 4, 4)); if (authlim == 0) PrintAndLogEx(INFO, " - " _GREEN_("Unlimited password attempts")); @@ -838,6 +939,55 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " PWD [%u/0x%02X]: %s- (cannot be read)", startPage + 2, startPage + 2, sprint_hex(data + 8, 4)); PrintAndLogEx(INFO, " PACK [%u/0x%02X]: %s - (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 12, 2)); PrintAndLogEx(INFO, " RFU [%u/0x%02X]: %s- (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 14, 2)); + + if (tagtype & NTAG_213_TT) { + if (data[1] & 0x06) { + PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s- (cannot be read)", sprint_hex(tt_message, tt_msg_resp_len)); + PrintAndLogEx(INFO, " - tamper message is masked in memory"); + } else { + PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s", sprint_hex(tt_message, tt_msg_resp_len)); + PrintAndLogEx(INFO, " - tamper message is %s and is readable/writablbe in memory", sprint_hex(tt_message, tt_msg_resp_len)); + } + } + + //The NTAG213TT only returns meaningful information for the fields below if the tamper feature is enabled + if ((tagtype & NTAG_213_TT) && tt_enabled) { + + uint8_t tt_status_len = ntagtt_getTamperStatus(tt_status_resp, 5); + + if (tt_status_len != 5) { + PrintAndLogEx(WARNING, "Error sending the READ_TT_STATUS command to tag\n"); + return PM3_ESOFT; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tamper Status")); + PrintAndLogEx(INFO, " READ_TT_STATUS: %s", sprint_hex(tt_status_resp, 5)); + + PrintAndLogEx(INFO, " Tamper status result from this power-up:"); + switch (tt_status_resp[4]) { + case 0x43: + PrintAndLogEx(INFO, " - Tamper loop was detcted as closed during this power-up"); + break; + case 0x4F: + PrintAndLogEx(INFO, " - Tamper loop was detected as open during this power-up"); + break; + case 0x49: + PrintAndLogEx(INFO, " - Tamper loop measurement was not enabled or not valid during this power-up"); + break; + default: + break; + } + + PrintAndLogEx(INFO, " Tamper detection permanent memory:"); + if ((tt_status_resp[0] | tt_status_resp [1] | tt_status_resp[2] | tt_status_resp[3]) == 0x00) + + PrintAndLogEx(INFO, " - Tamper loop has never been detected as open during power-up"); + else { + PrintAndLogEx(INFO, " - Tamper loop was detected as open during power-up at least once"); + PrintAndLogEx(INFO, " - Tamper message returned by READ_TT_STATUS command: %s", sprint_hex(tt_status_resp, 4)); + } + } return PM3_SUCCESS; } @@ -1145,6 +1295,46 @@ Lego Dimensions, E1 10 12 00 01 03 A0 0C 34 03 13 D1 01 0F 54 02 65 6E */ +typedef struct { + const char *desc; + uint8_t mpos; + uint8_t mlen; + const char *match; + uint32_t (*otp)(const uint8_t *uid); + const char *hint; +} mfu_otp_identify_t; + +static mfu_otp_identify_t mfu_otp_ident_table[] = { + { "SALTO", 12, 4, "534C544F", ul_c_otpgenA, NULL }, +// { "SAFLOK", 12, 4, NULL, ul_c_otpgenB, NULL }, +// { "VINGCARD", 12, 4, NULL, ul_c_otpgenC, NULL }, +// { "DORMA KABA", 12, 4, NULL, ul_c_otpgenD, NULL }, + { NULL, 0, 0, NULL, NULL, NULL } +}; + +static mfu_otp_identify_t *mfu_match_otp_fingerprint(uint8_t *data) { + uint8_t i = 0; + do { + int ml = 0; + uint8_t mtmp[40] = {0}; + + // static or dynamic created OTP to fingerprint. + if (mfu_otp_ident_table[i].match) { + param_gethex_to_eol(mfu_otp_ident_table[i].match, 0, mtmp, sizeof(mtmp), &ml); + } else { + uint32_t otp = mfu_otp_ident_table[i].otp(data); + num_to_bytes(otp, 4, mtmp); + } + + bool m2 = (memcmp(mtmp, data + mfu_otp_ident_table[i].mpos, mfu_otp_ident_table[i].mlen) == 0); + if (m2) { + PrintAndLogEx(DEBUG, "(fingerprint) found %s", mfu_otp_ident_table[i].desc); + return &mfu_otp_ident_table[i]; + } + } while (mfu_otp_ident_table[++i].desc); + return NULL; +} + typedef struct { const char *desc; const char *version; @@ -1185,7 +1375,7 @@ static mfu_identify_t mfu_ident_table[] = { "Snackworld", "0004040101000B03", 9, 7, "483000E1100600", NULL, NULL, - "hf mfu dump -k %08x" + "hf mfu dump -k" }, { "Amiibo", "0004040201001103", @@ -1325,15 +1515,36 @@ static int mfu_fingerprint(TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authke if (item) { PrintAndLogEx(SUCCESS, "Found " _GREEN_("%s"), item->desc); - if (item->Pwd) { + if (item->hint) { + if (item->Pwd) { + char s[40] = {0}; + snprintf(s, sizeof(s), item->hint, item->Pwd(uid)); + PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", s); + } else { + PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", item->hint); + } + } + } + } + + // OTP checks + mfu_otp_identify_t *item = mfu_match_otp_fingerprint(data); + if (item) { + PrintAndLogEx(SUCCESS, "Found " _GREEN_("%s"), item->desc); + + if (item->hint) { + if (item->otp) { char s[40] = {0}; - snprintf(s, sizeof(s), item->hint, item->Pwd(uid)); + snprintf(s, sizeof(s), item->hint, item->otp(uid)); PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", s); } else { PrintAndLogEx(HINT, "Use `" _YELLOW_("%s") "`", item->hint); } } } + // + + out: free(data); @@ -1412,6 +1623,7 @@ uint32_t GetHF14AMfU_Type(void) { else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = NTAG_210; break; } else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = NTAG_212; break; } else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } //Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) @@ -1696,7 +1908,6 @@ static int CmdHF14AMfUInfo(const char *Cmd) { uint8_t startconfigblock = 0; uint8_t ulev1_conf[16] = {0x00}; - // config blocks always are last 4 pages for (uint8_t i = 0; i < ARRAYLEN(UL_TYPES_ARRAY); i++) { if (tagtype & UL_TYPES_ARRAY[i]) { startconfigblock = UL_MEMORY_ARRAY[i] - 3; @@ -2468,6 +2679,142 @@ static void wait4response(uint8_t b) { } } +// +//Configure tamper feature of NTAG 213TT +// +int CmdHF14MfUTamper(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu tamper", + "Set the congiguration of the NTAG 213TT tamper feature\n" + "Supports:\n" + "NTAG 213TT\n", + "hf mfu tamper -e -> enable tamper feature\n" + "hf mfu tamper -d -> disable tamper feature\n" + "hf mfu tamper -m 0A0A0A0A -> set the tamper message to 0A0A0A0A\n" + "hf mfu tamper --lockmessage -> permanently lock the tamper message and mask it from memory\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("e", "enable", "Enable the tamper feature"), + arg_lit0("d", "disable", "Disable the tamper feature"), + arg_str0("m", "message", "", "Set the tamper message (4 bytes)"), + arg_lit0(NULL, "lockmessage", "Permanently lock the tamper message and mask it from memory (does not lock tamper feature itself)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int tt_cfg_page = 41; + int tt_msg_page = 45; + int msg_len = 0; + uint8_t msg_data[4] = {0x00}; + CLIGetHexWithReturn(ctx, 3, msg_data, &msg_len); + bool use_msg = (msg_len > 0); + + if (use_msg && msg_len != 4) { + PrintAndLogEx(WARNING, "The tamper message must be 4 hex bytes if provided"); + DropField(); + CLIParserFree(ctx); + return PM3_ESOFT; + } + + bool lock_msg = arg_get_lit(ctx, 4); + bool enable = arg_get_lit(ctx, 1); + bool disable = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + TagTypeUL_t tagtype = GetHF14AMfU_Type(); + if (tagtype == UL_ERROR) { + PrintAndLogEx(WARNING, "Tag type not detected"); + DropField(); + return PM3_ESOFT; + } + if (tagtype != NTAG_213_TT) { + PrintAndLogEx(WARNING, "Tag type not NTAG 213TT"); + DropField(); + return PM3_ESOFT; + } + + DropField(); + iso14a_card_select_t card; + + if (enable && disable) { + PrintAndLogEx(WARNING, "You can only select one of the options enable/disable tamper feature"); + DropField(); + return PM3_ESOFT; + } + + if (use_msg) { + if (ul_select(&card) == false) { + DropField(); + return UL_ERROR; + } + PrintAndLogEx(INFO, "Trying to write tamper message\n"); + SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, tt_msg_page, 0, 0, msg_data, 4); + + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.oldarg[0] & 0xff; + if (!isOK) + PrintAndLogEx(WARNING, "Failed to write tamper message"); + else + PrintAndLogEx(SUCCESS, "Tamper message written successfully"); + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + } + + if (enable | disable | lock_msg) { + + if (ul_select(&card) == false) { + PrintAndLogEx(ERR, "Unable to select tag"); + DropField(); + return UL_ERROR; + } + + uint8_t cfg_page[4] = {0x00}; + uint8_t cmd[] = {ISO14443A_CMD_READBLOCK, tt_cfg_page}; + int status = ul_send_cmd_raw(cmd, sizeof(cmd), cfg_page, 4); + DropField(); + + if (status <= 0) { + PrintAndLogEx(WARNING, "Problem reading current config from tag"); + DropField(); + return PM3_ESOFT; + } + + if (enable) { + cfg_page[1] = cfg_page[1] | 0x02; + PrintAndLogEx(INFO, "Enabling tamper feature"); + } + if (disable) { + cfg_page[1] = cfg_page[1] & 0xFD; + PrintAndLogEx(INFO, "Disabling tamper feature"); + } + if (lock_msg) { + cfg_page[1] = cfg_page[1] | 0x04; + PrintAndLogEx(INFO, "Locking tamper message"); + } + + SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, tt_cfg_page, 0, 0, cfg_page, 4); + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.oldarg[0] & 0xff; + if (!isOK) + PrintAndLogEx(WARNING, "Failed to write tamper configuration"); + else + PrintAndLogEx(SUCCESS, "Tamper configuration written successfully"); + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + } + + DropField(); + return PM3_SUCCESS; +} + // // Restore dump file onto tag // @@ -2523,7 +2870,7 @@ static int CmdHF14AMfURestore(const char *Cmd) { if (fnlen == 0) { char *fptr = GenerateFilename("hf-mfu-", "-dump.bin"); if (fptr != NULL) { - strcpy(filename, fptr); + strncpy(filename, fptr, sizeof(filename) - 1); } else { snprintf(filename, sizeof(filename), "dumpdata.bin"); } @@ -2716,8 +3063,10 @@ static int CmdHF14AMfUeLoad(const char *Cmd) { int res = CmdHF14AMfELoad(nc); free(nc); + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfu sim -t 7`") " to simulate an Amiibo."); return res; } + // // Simulate tag // @@ -2727,9 +3076,11 @@ static int CmdHF14AMfUSim(const char *Cmd) { "Simulate MIFARE Ultralight family type based upon\n" "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" "from emulator memory. See `hf mfu eload` first. \n" + "The UID from emulator memory will be used if not specified.\n" "See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight\n" - "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> AMIIBO (NTAG 215), pack 0x8080" + "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo\n" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" ); void *argtable[] = { @@ -3240,11 +3591,12 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { } PrintAndLogEx(INFO, "------------------.---------------"); - PrintAndLogEx(INFO, " Using UID : " _YELLOW_("%s"), sprint_hex(uid, 7)); + PrintAndLogEx(INFO, " Using UID 4b: " _YELLOW_("%s"), sprint_hex(uid, 4)); + PrintAndLogEx(INFO, " Using UID 7b: " _YELLOW_("%s"), sprint_hex(uid, 7)); PrintAndLogEx(INFO, "----------------------------------"); PrintAndLogEx(INFO, " algo | pwd | pack"); PrintAndLogEx(INFO, "-----------------+----------+-----"); - PrintAndLogEx(INFO, " EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid)); + PrintAndLogEx(INFO, " Transport EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid)); PrintAndLogEx(INFO, " Amiibo | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid)); PrintAndLogEx(INFO, " Lego Dimension | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid)); PrintAndLogEx(INFO, " XYZ 3D printer | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid)); @@ -3252,6 +3604,9 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { PrintAndLogEx(INFO, " NTAG tools | %08X | %04X", ul_ev1_pwdgenF(uid), ul_ev1_packgen_def(uid)); PrintAndLogEx(INFO, "-----------------+----------+-----"); PrintAndLogEx(INFO, " Vingcard algo"); + PrintAndLogEx(INFO, " Saflok algo"); + PrintAndLogEx(INFO, " SALTO algo"); + PrintAndLogEx(INFO, " Dorma Kaba algo"); PrintAndLogEx(INFO, "----------------------------------"); return PM3_SUCCESS; } @@ -3955,6 +4310,7 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { arg_str0("k", "key", "Replace default key for NDEF", NULL), arg_lit0("l", NULL, "Swap entered key's endianness"), arg_str0("f", "file", "", "Save raw NDEF to file"), + arg_litn("v", "verbose", 0, 2, "show technical data"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3963,6 +4319,7 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool verbose = arg_get_lit(ctx, 4); CLIParserFree(ctx); switch (keylen) { @@ -4024,6 +4381,9 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { } } + // The following read will read in blocks of 16 bytes. + // ensure maxsize is rounded up to a multiple of 16 + maxsize = maxsize + (16 - (maxsize % 16)); // allocate mem uint8_t *records = calloc(maxsize, sizeof(uint8_t)); if (records == NULL) { @@ -4046,9 +4406,9 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { if (fnlen != 0) { saveFile(filename, ".bin", records, (size_t)maxsize); } - status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize); + status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize, verbose); if (status != PM3_SUCCESS) { - status = NDEFDecodeAndPrint(records, (size_t)maxsize, true); + status = NDEFDecodeAndPrint(records, (size_t)maxsize, verbose); } char *jooki = strstr((char *)records, "s.jooki.rocks/s/?s="); @@ -4090,41 +4450,135 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { return status; } -static int CmdHF14AMfuEView(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfu eview", - "It displays emulator memory", - "hf mfu eview" - ); +// utility function. Retrieves emulator memory +static int GetMfuDumpFromEMul(mfu_dump_t **buf) { - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - uint16_t blocks = MFU_MAX_BLOCKS; - uint16_t bytes = MFU_MAX_BYTES + MFU_DUMP_PREFIX_LENGTH; - - uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + mfu_dump_t *dump = calloc(1, sizeof(mfu_dump_t)); if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } PrintAndLogEx(INFO, "downloading from emulator memory"); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF_EML, (uint8_t *)dump, MFU_MAX_BYTES + MFU_DUMP_PREFIX_LENGTH, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } - printMFUdumpEx((mfu_dump_t *)dump, blocks, 0); + *buf = dump ; + return PM3_SUCCESS ; +} + +static int CmdHF14AMfuEView(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu eview", + "Displays emulator memory\n" + "By default number of pages shown depends on defined tag type.\n" + "You can override this with option --end.", + "hf mfu eview\n" + "hf mfu eview --end 255 -> dumps whole memory" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("e", "end", "", "index of last block"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + int end = arg_get_int_def(ctx, 1, -1); + CLIParserFree(ctx); + + bool override_end = (end != -1) ; + + if (override_end && (end < 0 || end > MFU_MAX_BLOCKS)) { + PrintAndLogEx(WARNING, "Invalid value for end:%d. Must be be positive integer < %d.", end, MFU_MAX_BLOCKS); + return PM3_EINVARG ; + } + + mfu_dump_t *dump ; + int res = GetMfuDumpFromEMul(&dump) ; + if (res != PM3_SUCCESS) { + return res ; + } + + if (override_end) { + ++end ; + } else { + end = dump->pages ; + } + + printMFUdumpEx(dump, end, 0); free(dump); return PM3_SUCCESS; } +static int CmdHF14AMfuESave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfu esave", + "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/eml/json)\n" + "By default number of pages saved depends on defined tag type.\n" + "You can override this with option --end.", + "hf mfu esave\n" + "hf mfu esave --end 255 -> saves whole memory\n" + "hf mfu esave -f hf-mfu-04010203040506-dump.json" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("e", "end", "", "index of last block"), + arg_str0("f", "file", "", "filename of dump"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + int end = arg_get_int_def(ctx, 1, -1); + + char filename[FILE_PATH_SIZE]; + int fnlen = 0 ; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + bool override_end = (end != -1) ; + + if (override_end && (end < 0 || end > MFU_MAX_BLOCKS)) { + PrintAndLogEx(WARNING, "Invalid value for end:%d. Must be be positive integer <= %d.", end, MFU_MAX_BLOCKS); + return PM3_EINVARG ; + } + + // get dump from memory + mfu_dump_t *dump ; + int res = GetMfuDumpFromEMul(&dump) ; + if (res != PM3_SUCCESS) { + return res ; + } + + // initialize filename + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + uint8_t uid[7] = {0}; + memcpy(uid, (uint8_t *) & (dump->data), 3); + memcpy(uid + 3, (uint8_t *) & (dump->data) + 4, 4); + strcat(filename, "hf-mfu-"); + FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); + } + + if (override_end) { + end ++ ; + } else { + end = dump->pages ; + } + + // save dump. Last block contains PACK + RFU + uint16_t datalen = (end + 1) * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; + res = pm3_save_dump(filename, (uint8_t *)dump, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); + + free(dump); + return res; +} + static int CmdHF14AMfuView(const char *Cmd) { CLIParserContext *ctx; @@ -4238,8 +4692,10 @@ static command_t CommandTable[] = { {"restore", CmdHF14AMfURestore, IfPm3Iso14443a, "Restore a dump onto a MFU MAGIC tag"}, {"view", CmdHF14AMfuView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdHF14AMfUWrBl, IfPm3Iso14443a, "Write block"}, + {"tamper", CmdHF14MfUTamper, IfPm3Iso14443a, "Cofigure the tamper feature on an NTAG 213TT"}, {"---------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"}, - {"eload", CmdHF14AMfUeLoad, IfPm3Iso14443a, "Load Ultralight .eml dump file into emulator memory"}, + {"eload", CmdHF14AMfUeLoad, IfPm3Iso14443a, "Load Ultralight dump file into emulator memory"}, + {"esave", CmdHF14AMfuESave, IfPm3Iso14443a, "Save Ultralight dump file from emulator memory"}, {"eview", CmdHF14AMfuEView, IfPm3Iso14443a, "View emulator memory"}, {"sim", CmdHF14AMfUSim, IfPm3Iso14443a, "Simulate MIFARE Ultralight from emulator memory"}, {"---------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic") " ----------------------------"}, diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index 568e3c4a9..1ed652e79 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -50,6 +50,7 @@ int trace_mfuc_try_default_3des_keys(uint8_t **correct_key, int state, uint8_t ( int CmdHFMFUltra(const char *Cmd); int CmdHF14MfuNDEFRead(const char *Cmd); +int CmdHF14MfUTamper(const char *Cmd); uint16_t ul_ev1_packgen_VCNEW(uint8_t *uid, uint32_t pwd); uint32_t ul_ev1_otpgenA(uint8_t *uid); diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 350df87f5..896157be4 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -30,6 +30,7 @@ #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "crypto/asn1utils.h" // ASN1 decode / print #include "commonutil.h" // get_sw +#include "protocols.h" // ISO7816 APDU return codes static int CmdHelp(const char *Cmd); @@ -55,7 +56,7 @@ static int seos_select(void) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting SEOS applet aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -75,7 +76,7 @@ static int seos_select(void) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting ADF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index 54c432ab9..86dfe0584 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -31,6 +31,7 @@ #include "nfc/ndef.h" // NDEFRecordsDecodeAndPrint #include "cmdnfc.h" // print_type4_cc_info #include "commonutil.h" // get_sw +#include "protocols.h" // ISO7816 APDU return codes #define TIMEOUT 2000 @@ -46,7 +47,7 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) { PrintAndLogEx(SUCCESS, "------------ " _CYAN_("ST System file") " ------------"); uint16_t len = (d[0] << 8 | d[1]); - PrintAndLogEx(SUCCESS, " len %u bytes (" _GREEN_("0x%04X") ")", len, len); + PrintAndLogEx(SUCCESS, " len %u bytes ( " _GREEN_("0x%04X") " )", len, len); if (d[2] == 0x80) { PrintAndLogEx(SUCCESS, " ST reserved ( 0x%02X )", d[2]); @@ -98,7 +99,7 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) { PrintAndLogEx(SUCCESS, " Device# " _YELLOW_("%s"), sprint_hex_inrow(d + 10, 5)); uint16_t mem = (d[0xF] << 8 | d[0x10]); - PrintAndLogEx(SUCCESS, " Memory Size - 1 %u bytes (" _GREEN_("0x%04X") ")", mem, mem); + PrintAndLogEx(SUCCESS, " Memory Size - 1 %u bytes ( " _GREEN_("0x%04X") " )", mem, mem); PrintAndLogEx(SUCCESS, " IC Reference code %u ( 0x%02X )", d[0x12], d[0x12]); @@ -136,7 +137,7 @@ static int infoHFST25TA(void) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -156,7 +157,7 @@ static int infoHFST25TA(void) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting CC file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -172,7 +173,7 @@ static int infoHFST25TA(void) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading CC file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -192,7 +193,7 @@ static int infoHFST25TA(void) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting system file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -210,7 +211,7 @@ static int infoHFST25TA(void) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading system file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -287,6 +288,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { arg_param_begin, arg_str0("p", "pwd", "", "16 byte read password"), arg_str0("f", "file", "", "save raw NDEF to file"), + arg_litn("v", "verbose", 0, 2, "show technical data"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -294,6 +296,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool verbose = arg_get_lit(ctx, 3); CLIParserFree(ctx); if (pwdlen == 0) { @@ -327,7 +330,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -347,7 +350,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -376,7 +379,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Verify password failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -395,7 +398,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -404,7 +407,7 @@ int CmdHFST25TANdefRead(const char *Cmd) { if (fnlen != 0) { saveFile(filename, ".bin", response + 2, resplen - 4); } - NDEFRecordsDecodeAndPrint(response + 2, resplen - 4); + NDEFRecordsDecodeAndPrint(response + 2, resplen - 4, verbose); return PM3_SUCCESS; } @@ -493,7 +496,7 @@ static int CmdHFST25TAProtect(const char *Cmd) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -513,7 +516,7 @@ static int CmdHFST25TAProtect(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -532,7 +535,7 @@ static int CmdHFST25TAProtect(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Verify password failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -551,7 +554,7 @@ static int CmdHFST25TAProtect(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "changing protection failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -637,7 +640,7 @@ static int CmdHFST25TAPwd(const char *Cmd) { } uint16_t sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -657,7 +660,7 @@ static int CmdHFST25TAPwd(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Selecting NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -676,7 +679,7 @@ static int CmdHFST25TAPwd(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "Verify password failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; @@ -697,7 +700,7 @@ static int CmdHFST25TAPwd(const char *Cmd) { } sw = get_sw(response, resplen); - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "password change failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); DropField(); return PM3_ESOFT; diff --git a/client/src/cmdhftesla.c b/client/src/cmdhftesla.c new file mode 100644 index 000000000..e1687a170 --- /dev/null +++ b/client/src/cmdhftesla.c @@ -0,0 +1,274 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency ISO14443A / TESLA commands +//----------------------------------------------------------------------------- + +#include "cmdhftesla.h" +#include +#include +#include "cmdparser.h" // command_t +#include "comms.h" // clearCommandBuffer +#include "cmdtrace.h" +#include "cliparser.h" +#include "cmdhf14a.h" +#include "protocols.h" // definitions of ISO14A/7816 protocol +#include "iso7816/apduinfo.h" // GetAPDUCodeDescription +#include "commonutil.h" // get_sw +#include "protocols.h" // ISO7816 APDU return co-des +#include "ui.h" +#include "cmdhf14a.h" // apdu chaining + +#define TIMEOUT 2000 + +static int CmdHelp(const char *Cmd); + +/** + * 0x80 0x00 0x00 0x00 - get interface object + 0x80 0x01 0x00 0x00 - load data from storage + 0x80 0x02 KEY_INDEX 0x00 - initialize key pair + 0x80 0x03 KEY_INDEX 0x00 - generate key pair + 0x80 0x04 KEY_INDEX 0x00 - get public key + 0x80 0x05 CRT_INDEX 0x00 - load certificate + 0x80 0x06 CRT_INDEX 0x00 - get certificate + 0x80 0x07 0x00 0x00 - get version + 0x80 0x08 0x00 0x00 - confirm prepersonalization + 0x80 0x10 KEY_INDEX 0x00 - sign challenge + 0x80 0x11 KEY_INDEX 0x00 - dh key exchange + +*/ + +// TESLA +static int info_hf_tesla(void) { + + bool activate_field = true; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + // --------------- Select TESLA application ---------------- + uint8_t aSELECT_AID[80]; + int aSELECT_AID_n = 0; + param_gethex_to_eol("00a404000a7465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + activate_field = false; + uint16_t sw = get_sw(response, resplen); + + if ((resplen < 2) || (sw != ISO7816_OK)) { + + param_gethex_to_eol("00a404000af465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } + + if ((resplen < 2) || (sw != ISO7816_OK)) { + PrintAndLogEx(ERR, "Selecting TESLA aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + // DropField(); + // return PM3_ESOFT; + } + + + keep_field_on = true; + + + // --------------- ECDH public key file reading ---------------- + uint8_t pk[3][65] = {{0}}; + + for (uint8_t i = 0; i < 3; i++) { + + uint8_t aSELECT_PK[5] = {0x80, 0x04, i, 0x00, 0x00}; + res = ExchangeAPDU14a(aSELECT_PK, sizeof(aSELECT_PK), activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + continue; + } + + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + memcpy(pk[i], response, resplen - 2); + } + } + + uint8_t aREAD_FORM_FACTOR[30]; + int aREAD_FORM_FACTOR_n = 0; + param_gethex_to_eol("80140000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n); + res = ExchangeAPDU14a(aREAD_FORM_FACTOR, aREAD_FORM_FACTOR_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + sw = get_sw(response, resplen); + if (sw != ISO7816_OK) { + PrintAndLogEx(ERR, "reading FORM FACTOR file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + DropField(); + return PM3_ESOFT; + } + + // store form factor for later + uint8_t form_factor[resplen - 2]; + memcpy(form_factor, response, sizeof(form_factor)); + + uint8_t aREAD_VERSION[30]; + int aREAD_VERSION_n = 0; + param_gethex_to_eol("80170000", 0, aREAD_VERSION, sizeof(aREAD_VERSION), &aREAD_VERSION_n); + res = ExchangeAPDU14a(aREAD_VERSION, aREAD_VERSION_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t version[resplen - 2]; + + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + // store version for later + memcpy(version, response, sizeof(version)); + } + + // --------------- CERT reading ---------------- + Set_apdu_in_framing(true); + for (uint8_t i = 0; i < 5; i++) { + + uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x00, 0x00, 0xFF}; + int aSELECT_CERT_n = 7; + + res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, PM3_CMD_DATA_SIZE, &resplen); + if (res != PM3_SUCCESS) { + continue; + } + + sw = get_sw(response, resplen); + + if (sw == ISO7816_OK) { + // save CERT for later + uint8_t cert[515] = {0}; + memcpy(cert, response, resplen - 2); + + PrintAndLogEx(INFO, "CERT # %i", i); + PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert, resplen - 2)); + } + } + Set_apdu_in_framing(false); + + uint8_t aAUTH[90]; + int aAUTH_n = 0; + // vehicle public key , 16 byte CHALLENGE + // 00112233445566778899AABBCCDDEEFF + // 0x51 = 81 dec +// param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); + param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); + res = ExchangeAPDU14a(aAUTH, aAUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t auth[resplen - 2]; + + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + // store CHALLENGE for later + memcpy(auth, response, sizeof(auth)); + } + + keep_field_on = false; + DropField(); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "PUBLIC KEY"); + for (int i = 0; i < 3; i++) { + PrintAndLogEx(INFO, "%d - %s", i, sprint_hex_inrow(pk[i], 65)); + } + PrintAndLogEx(INFO, "Form factor... %s " NOLF, sprint_hex_inrow(form_factor, sizeof(form_factor))); + + uint16_t form_factor_value = MemBeToUint2byte(form_factor); + + switch (form_factor_value) { + case 0x0001: + PrintAndLogEx(NORMAL, "( card )"); + break; + case 0x0022: + PrintAndLogEx(NORMAL, "( fob )"); + break; + case 0x0031: + PrintAndLogEx(NORMAL, "( phone app )"); + break; + default: + PrintAndLogEx(NORMAL, "( unknown )"); + break; + } + + if (sizeof(version) > 0) { + PrintAndLogEx(INFO, "Version....... %s", sprint_hex_inrow(version, sizeof(version))); + } + + PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth))); + + PrintAndLogEx(INFO, "Fingerprint"); + if ((memcmp(pk[0], pk[1], 65) == 0)) { + PrintAndLogEx(INFO, " GaussKey detected"); + } + // + return PM3_SUCCESS; +} + +// menu command to get and print all info known about any known ST25TA tag +static int CmdHFTeslaInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf telsa info", + "Get info about TESLA Key tag", + "hf tesla info" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + return info_hf_tesla(); +} + +static int CmdHFTeslaList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf tesla", "7816"); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"info", CmdHFTeslaInfo, IfPm3Iso14443a, "Tag information"}, + {"list", CmdHFTeslaList, AlwaysAvailable, "List ISO 14443A/7816 history"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFTESLA(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhftesla.h b/client/src/cmdhftesla.h new file mode 100644 index 000000000..923ac5fb1 --- /dev/null +++ b/client/src/cmdhftesla.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency ISO14443A / TESLA commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFTESLA_H__ +#define CMDHFTESLA_H__ + +#include "common.h" + +int CmdHFTESLA(const char *Cmd); + +#endif diff --git a/client/src/cmdhftexkom.c b/client/src/cmdhftexkom.c new file mode 100644 index 000000000..2b54480ca --- /dev/null +++ b/client/src/cmdhftexkom.c @@ -0,0 +1,949 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency proximity cards from TEXCOM commands +//----------------------------------------------------------------------------- + +#include "cmdhftexkom.h" + +#include +#include +#include +#include "cliparser.h" +#include "cmdparser.h" // command_t +#include "comms.h" +#include "ui.h" +#include "cmdhf14a.h" +#include "cmddata.h" +#include "graph.h" + +#define TEXKOM_NOISE_THRESHOLD (10) + +static inline uint32_t GetGraphBuffer(uint32_t indx) { + if (g_GraphBuffer[indx] < -128) + return 0; + else + return g_GraphBuffer[indx] + 128; +} + +static uint32_t TexkomAVGField(void) { + if (g_GraphTraceLen == 0) + return 0; + + uint64_t vsum = 0; + for (uint32_t i = 0; i < g_GraphTraceLen; i++) + vsum += GetGraphBuffer(i); + + return vsum / g_GraphTraceLen; +} + +static uint32_t TexkomSearchStart(uint32_t indx, uint32_t threshold) { + // one bit length = 27, minimal noise = 60 + uint32_t lownoisectr = 0; + for (uint32_t i = indx; i < g_GraphTraceLen; i++) { + if (lownoisectr > 60) { + if (GetGraphBuffer(i) > threshold) + return i; + } else { + if (GetGraphBuffer(i) > threshold) + lownoisectr = 0; + else + lownoisectr++; + } + } + + return 0; +} + +static uint32_t TexkomSearchLength(uint32_t indx, uint32_t threshold) { + // one bit length = 27, minimal noise = 60 + uint32_t lownoisectr = 0; + uint32_t datalen = 0; + for (uint32_t i = indx; i < g_GraphTraceLen; i++) { + if (lownoisectr > 60) { + break; + } else { + if (GetGraphBuffer(i) > threshold) { + lownoisectr = 0; + datalen = i - indx + 27; + } else { + lownoisectr++; + } + } + } + + return datalen; +} + +static uint32_t TexkomSearchMax(uint32_t indx, uint32_t len) { + uint32_t res = 0; + + for (uint32_t i = 0; i < len; i++) { + if (i + indx > g_GraphTraceLen) + break; + + if (GetGraphBuffer(indx + i) > res) + res = GetGraphBuffer(indx + i); + } + + return res; +} + +static bool TexkomCorrelate(uint32_t indx, uint32_t threshold) { + if (indx < 2 || indx + 2 > g_GraphTraceLen) + return false; + + uint32_t g1 = GetGraphBuffer(indx - 2); + uint32_t g2 = GetGraphBuffer(indx - 1); + uint32_t g3 = GetGraphBuffer(indx); + uint32_t g4 = GetGraphBuffer(indx + 1); + uint32_t g5 = GetGraphBuffer(indx + 2); + + return ( + (g3 > threshold) && + (g3 >= g2) && (g3 >= g1) && (g3 > g4) && (g3 > g5) + ); +} + +static bool TexkomCalculateMaxMin(const uint32_t *data, uint32_t len, uint32_t *dmax, uint32_t *dmin) { + *dmax = 0; + *dmin = 0xffffffff; + for (size_t i = 0; i < len; i++) { + if (data[i] > *dmax) + *dmax = data[i]; + if (data[i] < *dmin) + *dmin = data[i]; + } + + return (*dmax != 0) && (*dmin != 0xffffffff) && (*dmax > *dmin); +} + +static bool TexkomCalculateBitLengths(uint32_t *data, uint32_t len, uint32_t *hi, uint32_t *low, uint32_t *lmax, uint32_t *lmin) { + *hi = 0; + *low = 0; + + uint32_t dmax = 0; + uint32_t dmin = 0xffffffff; + if (!TexkomCalculateMaxMin(data, len, &dmax, &dmin)) + return false; + + uint32_t dmiddle = (dmax + dmin) / 2; + uint32_t sumhi = 0; + uint32_t lenhi = 0; + uint32_t sumlow = 0; + uint32_t lenlow = 0; + for (size_t i = 0; i < len; i++) { + if (data[i] > dmiddle) { + sumhi += data[i]; + lenhi++; + } else { + sumlow += data[i]; + lenlow++; + } + } + + if (lenhi) + *hi = sumhi / lenhi; + + if (lenlow) + *low = sumlow / lenlow; + + if (lmax != NULL) + *lmax = dmax; + if (lmin != NULL) + *lmin = dmin; + + return (*hi != 0) && (*low != 0) && (*hi > *low); +} + +static inline bool TexcomCalculateBit(uint32_t data, uint32_t bitlen, uint32_t threshold) { + return + (data < (bitlen + threshold)) && + (data > (bitlen - threshold)); +} + +// code from https://github.com/li0ard/crclib/blob/main/index.js +static uint8_t TexcomTK13CRC(const uint8_t *data) { + uint8_t crc = 0; + uint8_t indx = 0; + while (indx < 4) { + crc = crc ^ data[indx++]; + + for (uint8_t i = 0; i < 8; i++) + if (crc & 0x80) { + crc = 0x31 ^ (crc << 1); + } else + crc <<= 1; + }; + + return crc; +} + +static uint8_t MMBITCRC(const uint8_t *data) { + return + (((data[0] & 0x0f) ^ ((data[0] >> 4) & 0x0f) ^ + (data[1] & 0x0f) ^ ((data[1] >> 4) & 0x0f) ^ + (data[2] & 0x0f) ^ ((data[2] >> 4) & 0x0f) + ) ^ 0x0f + ) & 0x0f; +} + +static unsigned char dallas_crc8(const unsigned char *data, const unsigned int size) { + unsigned char crc = 0; + for (unsigned int i = 0; i < size; ++i) { + unsigned char inbyte = data[i]; + for (unsigned char j = 0; j < 8; ++j) { + unsigned char mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} + +// code from https://github.com/li0ard/crclib/blob/main/index.js +static uint8_t TexcomTK17CRC(uint8_t *data) { + uint8_t ddata[8] = {0x00, 0x00, 0x00, data[0], data[1], data[2], data[3], 0x00}; + + return dallas_crc8(ddata, 7); +} + +static bool TexcomTK13Decode(uint32_t *implengths, uint32_t implengthslen, char *bitstring, char *cbitstring, bool verbose) { + bitstring[0] = 0; + cbitstring[0] = 0; + if (implengthslen == 0) + return false; + + uint32_t hilength = 0; + uint32_t lowlength = 0; + if (!TexkomCalculateBitLengths(implengths, implengthslen, &hilength, &lowlength, NULL, NULL)) + return false; + + uint32_t threshold = (hilength - lowlength) / 3 + 1; + //PrintAndLogEx(WARNING, "--- hi: %d, low: %d, threshold: %d", hilength, lowlength, threshold); + + bool biterror = false; + for (uint32_t i = 0; i < implengthslen; i++) { + if (TexcomCalculateBit(implengths[i], hilength, threshold)) + strcat(bitstring, "1"); + else if (TexcomCalculateBit(implengths[i], lowlength, threshold)) + strcat(bitstring, "0"); + else { + //PrintAndLogEx(INFO, "ERROR string [%zu]: %s, bit: %d, blen: %d", strlen(bitstring), bitstring, i, implengths[i]); + + biterror = true; + break; + } + } + + if (biterror || strlen(bitstring) == 0) + return false; + + if (verbose) + PrintAndLogEx(INFO, "raw bit string [%zu]: %s", strlen(bitstring), bitstring); + + // add trailing impulse (some tags just ignore it) + if (strlen(bitstring) % 2 != 0) { + if (bitstring[strlen(bitstring) - 1] == '1') + strcat(bitstring, "0"); + else + strcat(bitstring, "1"); + } + + for (uint32_t i = 0; i < strlen(bitstring); i = i + 2) { + if (bitstring[i] == bitstring[i + 1]) { + cbitstring[0] = 0; + if (verbose) + PrintAndLogEx(WARNING, "Raw bit string have error at offset %d.", i); + break; + } + if (bitstring[i] == '1') + strcat(cbitstring, "1"); + else + strcat(cbitstring, "0"); + } + + if (strlen(cbitstring) == 0) + return false; + + if (verbose) + PrintAndLogEx(INFO, "bit string [%zu]: %s", strlen(cbitstring), cbitstring); + + return ((strlen(cbitstring) == 64) && (strncmp(cbitstring, "1111111111111111", 16) == 0)); +} + +// general decode of the very bad signal. maybe here will be some of tk-13 old badges +static bool TexcomTK15Decode(uint32_t *implengths, uint32_t implengthslen, char *bitstring, char *cbitstring, bool verbose) { + bitstring[0] = 0; + cbitstring[0] = 0; + if (implengthslen == 0) + return false; + + bool biterror = false; + for (uint32_t i = 0; i < implengthslen / 2; i++) { + if (implengths[i * 2] == implengths[i * 2 + 1]) { + biterror = true; + break; + } else if (implengths[i * 2] > implengths[i * 2 + 1]) { + strcat(bitstring, "10"); + strcat(cbitstring, "1"); + } else { + strcat(bitstring, "01"); + strcat(cbitstring, "0"); + } + } + + if (implengthslen > 2 && implengthslen % 2 != 0) { + int lastimplen = implengths[implengthslen - 1]; + bool prevbit = (implengths[implengthslen - 3] > implengths[implengthslen - 2]); + bool thesamebit = (abs(lastimplen - (int)implengths[implengthslen - 3]) < abs(lastimplen - (int)implengths[implengthslen - 2])); + + if (prevbit ^ (!thesamebit)) { + strcat(bitstring, "10"); + strcat(cbitstring, "1"); + } else { + strcat(bitstring, "01"); + strcat(cbitstring, "0"); + } + } + + if (biterror || strlen(bitstring) == 0 || strlen(cbitstring) == 0) + return false; + + if (verbose) { + PrintAndLogEx(INFO, "raw bit string [%zu]: %s", strlen(bitstring), bitstring); + PrintAndLogEx(INFO, "bit string [%zu]: %s", strlen(cbitstring), cbitstring); + } + + return ((strlen(cbitstring) == 64) && (strncmp(cbitstring, "1111111111111111", 16) == 0)); +} + +static inline int TexcomTK17Get2Bits(uint32_t len1, uint32_t len2) { + uint32_t xlen = (len2 * 100) / (len1 + len2); + if (xlen < 10 || xlen > 90) + return TK17WrongBit; + if (xlen < 30) + return TK17Bit00; + if (xlen < 50) + return TK17Bit10; + if (xlen < 70) + return TK17Bit01; + return TK17Bit11; +} + +static bool TexcomTK17Decode(uint32_t *implengths, uint32_t implengthslen, char *bitstring, char *cbitstring, bool verbose) { + bitstring[0] = 0; + cbitstring[0] = 0; + if (implengthslen == 0) + return false; + + for (uint32_t i = 0; i < implengthslen; i = i + 2) { + int dbit = TexcomTK17Get2Bits(implengths[i], implengths[i + 1]); + if (dbit == TK17WrongBit) + return false; + + switch (dbit) { + case TK17Bit00: + strcat(bitstring, "00"); + break; + case TK17Bit01: + strcat(bitstring, "01"); + break; + case TK17Bit10: + strcat(bitstring, "10"); + break; + case TK17Bit11: + strcat(bitstring, "11"); + break; + default: + return false; + } + } + + if (verbose) + PrintAndLogEx(INFO, "TK17 raw bit string [%zu]: %s", strlen(bitstring), bitstring); + + for (uint32_t i = 0; i < 8; i++) { + memcpy(&cbitstring[i * 8 + 0], &bitstring[i * 8 + 6], 2); + memcpy(&cbitstring[i * 8 + 2], &bitstring[i * 8 + 4], 2); + memcpy(&cbitstring[i * 8 + 4], &bitstring[i * 8 + 2], 2); + memcpy(&cbitstring[i * 8 + 6], &bitstring[i * 8 + 0], 2); + } + + if (verbose) + PrintAndLogEx(INFO, "TK17 bit string [%zu]: %s", strlen(cbitstring), cbitstring); + + return (strlen(bitstring) == 64) && (strncmp(cbitstring, "1111111111111111", 16) == 0); +} + +static bool TexcomGeneralDecode(uint32_t *implengths, uint32_t implengthslen, char *bitstring, bool verbose) { + uint32_t hilength = 0; + uint32_t lowlength = 0; + if (!TexkomCalculateBitLengths(implengths, implengthslen, &hilength, &lowlength, NULL, NULL)) + return false; + + uint32_t threshold = (hilength - lowlength) / 3 + 1; + + bitstring[0] = 0; + bool biterror = false; + for (uint32_t i = 0; i < implengthslen; i++) { + if (TexcomCalculateBit(implengths[i], hilength, threshold)) + strcat(bitstring, "1"); + else if (TexcomCalculateBit(implengths[i], lowlength, threshold)) + strcat(bitstring, "0"); + else { + if (verbose) { + PrintAndLogEx(INFO, "ERROR string [%zu]: %s, bit: %d, blen: %d", strlen(bitstring), bitstring, i, implengths[i]); + + PrintAndLogEx(INFO, "Length array:"); + for (uint32_t j = 0; j < implengthslen; j++) { + PrintAndLogEx(NORMAL, "%u, " NOLF, implengths[j]); + } + PrintAndLogEx(NORMAL, ""); + } + + biterror = true; + break; + } + } + if (verbose) + PrintAndLogEx(INFO, "General raw bit string [%zu]: %s", strlen(bitstring), bitstring); + + return (!biterror && strlen(bitstring) > 0); +} + +static void TexcomReverseCode(const uint8_t *code, int length, uint8_t *reverse_code) { + for (int i = 0; i < length; i++) { + reverse_code[i] = code[(length - 1) - i]; + } +}; + +static int texkom_get_type(texkom_card_select_t *card, bool verbose) { + + if (card == NULL) { + return PM3_EINVARG; + } + + // get samples from tag. + + uint32_t samplesCount = 30000; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ACQ_RAW_ADC, (uint8_t *)&samplesCount, sizeof(uint32_t)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ACQ_RAW_ADC, &resp, 2500) == false) { + if (verbose) { + PrintAndLogEx(WARNING, "command execution time out"); + } + return PM3_ETIMEOUT; + } + + uint32_t size = (resp.data.asDwords[0]); + if (size > 0) { + if (getSamples(samplesCount, false) != PM3_SUCCESS) { + if (verbose) + PrintAndLogEx(ERR, "Get samples error"); + + return PM3_EFAILED; + }; + } + + // decode samples to 8 bytes + char bitstring[256] = {0}; + char cbitstring[128] = {0}; + char genbitstring[256] = {0}; + int found = TexkomModError; + uint32_t sindx = 0; + + while (sindx < samplesCount - 5) { + + sindx = TexkomSearchStart(sindx, TEXKOM_NOISE_THRESHOLD); + if (sindx == 0 || sindx > samplesCount - 5) { + if (TexkomAVGField() > 30 && verbose) { + PrintAndLogEx(WARNING, "Too noisy environment. Try to move the tag from the antenna a bit."); + } + break; + } + + uint32_t slen = TexkomSearchLength(sindx, TEXKOM_NOISE_THRESHOLD); + if (slen == 0) { + continue; + } + + uint32_t maxlvl = TexkomSearchMax(sindx, 1760); + if (maxlvl < TEXKOM_NOISE_THRESHOLD) { + sindx += 1700; + continue; + } + + uint32_t noiselvl = maxlvl / 5; + if (noiselvl < TEXKOM_NOISE_THRESHOLD) { + noiselvl = TEXKOM_NOISE_THRESHOLD; + } + + uint32_t implengths[256] = {}; + uint32_t implengthslen = 0; + uint32_t impulseindx = 0; + uint32_t impulsecnt = 0; + for (uint32_t i = 0; i < slen; i++) { + if (TexkomCorrelate(sindx + i, noiselvl)) { + impulsecnt++; + + if (impulseindx != 0) { + if (implengthslen < 256) { + implengths[implengthslen++] = sindx + i - impulseindx; + } + } + impulseindx = sindx + i; + } + } + + // check if it TK-17 modulation + // 65 impulses and 64 intervals (1 interval = 2 bits, interval length encoding) that represents 128 bit of card code + if (impulsecnt == 65) { + if (TexcomTK17Decode(implengths, implengthslen, bitstring, cbitstring, verbose)) { + found = TexkomModTK17; + break; + } + } + + // check if it TK-13 or TK-15 modulation + // it have 127 or 128 impulses and 128 double-intervals that represents 128 bit of card code + if (impulsecnt == 127 || impulsecnt == 128) { + if (TexcomTK13Decode(implengths, implengthslen, bitstring, cbitstring, verbose)) { + found = TexkomModTK13; + break; + } else if (TexcomTK15Decode(implengths, implengthslen, bitstring, cbitstring, verbose)) { + found = TexkomModTK15; + break; + } + } + + // general decoding. it thought that there is 2 types of intervals "long" (1) and "short" (0) + // and tries to decode sequence. shows only raw data + if (verbose) + TexcomGeneralDecode(implengths, implengthslen, genbitstring, verbose); + } + + if (found != TexkomModError) { + + for (uint32_t i = 0; i < strlen(cbitstring); i++) { + card->tcode[i / 8] = (card->tcode[i / 8] << 1) | ((cbitstring[i] == '1') ? 1 : 0); + } + + TexcomReverseCode(card->tcode, sizeof(card->tcode), card->rtcode); + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + +int read_texkom_uid(bool loop, bool verbose) { + + do { + texkom_card_select_t card; + + int res = texkom_get_type(&card, verbose); + + if (loop) { + if (res != PM3_SUCCESS) { + continue; + } + } else { + switch (res) { + case PM3_EFAILED: + case PM3_EINVARG: + return res; + case PM3_ETIMEOUT: + if (verbose) { + PrintAndLogEx(WARNING, "command execution time out"); + } + return res; + case PM3_ESOFT: + if (verbose) { + PrintAndLogEx(WARNING, "texkom card select failed"); + } + return PM3_ESOFT; + default: + break; + } + } + + // decoding code + if (card.tcode[0] == 0xff && card.tcode[1] == 0xff) { + + if (loop == false) { + PrintAndLogEx(NORMAL, ""); + } + + bool crc = (TexcomTK13CRC(&card.tcode[3]) == card.tcode[7]); + + if (card.tcode[2] == 0x63) { + PrintAndLogEx(INFO, "TYPE..... TK13"); + PrintAndLogEx(INFO, "UID...... " _GREEN_("%s"), sprint_hex(&card.tcode[3], 4)); + if (verbose) { + PrintAndLogEx(INFO, "CRC...... %s", (crc) ? _GREEN_("ok") : _RED_("fail")); + } + } else if (card.tcode[2] == 0xCA) { + PrintAndLogEx(INFO, "TYPE..... TK17"); + PrintAndLogEx(INFO, "UID...... " _GREEN_("%s"), sprint_hex(&card.tcode[3], 4)); + if (verbose) { + PrintAndLogEx(INFO, "CRC...... %s", (crc) ? _GREEN_("ok") : _RED_("fail")); + } + } else if (card.tcode[2] == 0xFF && card.tcode[3] == 0xFF) { + PrintAndLogEx(INFO, "TYPE..... MMBIT"); + PrintAndLogEx(INFO, "UID...... " _GREEN_("%s"), sprint_hex(&card.tcode[4], 3)); + crc = (MMBITCRC(&card.tcode[4]) == card.tcode[7] >> 4); + if (verbose) { + PrintAndLogEx(INFO, "CRC...... %s", (crc) ? _GREEN_("ok") : _RED_("fail")); + } + } + if (verbose) { + PrintAndLogEx(INFO, "Raw... %s", sprint_hex(card.tcode, 8)); + PrintAndLogEx(INFO, "Raw Reversed... %s", sprint_hex(card.rtcode, 8)); + } + } + + } while (loop && kbd_enter_pressed() == false); + + return PM3_SUCCESS; +} + +static int CmdHFTexkomReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf texkom reader", + "Read a texkom tag", + "hf texkom reader\n" + "hf texkom reader -@ -> continuous reader mode" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("1", NULL, "Use data from Graphbuffer"), + arg_lit0("v", "verbose", "Verbose scan and output"), + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool gbuffer = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + bool cm = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + return read_texkom_uid(cm, verbose); + } + + uint32_t samplesCount = 30000; + if (gbuffer) { + samplesCount = g_GraphTraceLen; + } else { + clearCommandBuffer(); + SendCommandNG(CMD_HF_ACQ_RAW_ADC, (uint8_t *)&samplesCount, sizeof(uint32_t)); + + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_HF_ACQ_RAW_ADC, &resp, 2500)) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + + uint32_t size = (resp.data.asDwords[0]); + if (size > 0) { + if (getSamples(samplesCount, false) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Get samples error"); + return PM3_EFAILED; + }; + } + } + + char bitstring[256] = {0}; + char cbitstring[128] = {0}; + char genbitstring[256] = {0}; + int codefound = TexkomModError; + uint32_t sindx = 0; + while (sindx < samplesCount - 5) { + sindx = TexkomSearchStart(sindx, TEXKOM_NOISE_THRESHOLD); + if (sindx == 0 || sindx > samplesCount - 5) { + if (TexkomAVGField() > 30) + PrintAndLogEx(WARNING, "Too noisy environment. Try to move the tag from the antenna a bit."); + break; + } + + uint32_t slen = TexkomSearchLength(sindx, TEXKOM_NOISE_THRESHOLD); + if (slen == 0) + continue; + + uint32_t maxlvl = TexkomSearchMax(sindx, 1760); + if (maxlvl < TEXKOM_NOISE_THRESHOLD) { + sindx += 1700; + continue; + } + + uint32_t noiselvl = maxlvl / 5; + if (noiselvl < TEXKOM_NOISE_THRESHOLD) + noiselvl = TEXKOM_NOISE_THRESHOLD; + + //PrintAndLogEx(WARNING, "--- indx: %d, len: %d, max: %d, noise: %d", sindx, slen, maxlvl, noiselvl); + + uint32_t implengths[256] = {}; + uint32_t implengthslen = 0; + uint32_t impulseindx = 0; + uint32_t impulsecnt = 0; + for (uint32_t i = 0; i < slen; i++) { + if (TexkomCorrelate(sindx + i, noiselvl)) { + impulsecnt++; + + if (impulseindx != 0) { + if (implengthslen < 256) + implengths[implengthslen++] = sindx + i - impulseindx; + } + impulseindx = sindx + i; + } + } + //PrintAndLogEx(WARNING, "--- impulses: %d, lenarray: %d, [%d,%d]", impulsecnt, implengthslen, implengths[0], implengths[1]); + + // check if it TK-17 modulation + // 65 impulses and 64 intervals (1 interval = 2 bits, interval length encoding) that represents 128 bit of card code + if (impulsecnt == 65) { + if (TexcomTK17Decode(implengths, implengthslen, bitstring, cbitstring, verbose)) { + codefound = TexkomModTK17; + break; + } + } + + // check if it TK-13 modulation + // it have 127 or 128 impulses and 128 double-intervals that represents 128 bit of card code + if (impulsecnt == 127 || impulsecnt == 128) { + if (TexcomTK13Decode(implengths, implengthslen, bitstring, cbitstring, verbose)) { + codefound = TexkomModTK13; + break; + } else if (TexcomTK15Decode(implengths, implengthslen, bitstring, cbitstring, verbose)) { + codefound = TexkomModTK15; + break; + } + } + + // general decoding. it thought that there is 2 types of intervals "long" (1) and "short" (0) + // and tries to decode sequence. shows only raw data + if (verbose) + TexcomGeneralDecode(implengths, implengthslen, genbitstring, verbose); + } + + if (codefound != TexkomModError) { + uint8_t tcode[8] = {0}; + for (uint32_t i = 0; i < strlen(cbitstring); i++) { + tcode[i / 8] = (tcode[i / 8] << 1) | ((cbitstring[i] == '1') ? 1 : 0); + } + + uint8_t rtcode[8] = {0}; + TexcomReverseCode(tcode, 8, rtcode); + + if (verbose) { + PrintAndLogEx(INFO, "Hex code: %s", sprint_hex(tcode, 8)); + PrintAndLogEx(INFO, "Hex code reversed: %s", sprint_hex(rtcode, 8)); + } + + if (tcode[0] == 0xff && tcode[1] == 0xff) { + // decoding code + + if (verbose == false) { + PrintAndLogEx(INFO, "Texkom: %s", sprint_hex(tcode, 8)); + PrintAndLogEx(INFO, "Texkom duplicator: %s", sprint_hex(rtcode, 8)); + } + + if (codefound == TexkomModTK13) + PrintAndLogEx(INFO, "modulation: TK13"); + else if (codefound == TexkomModTK15) + PrintAndLogEx(INFO, "modulation: TK15"); + else if (codefound == TexkomModTK17) + PrintAndLogEx(INFO, "modulation: TK17"); + else + PrintAndLogEx(INFO, "modulation: unknown"); + + if (tcode[2] == 0x63) { + // TK13 and TK15. differs only by timings. TK15 has impulse 0 and 1 lengths very close to each other. + if (codefound == TexkomModTK13) + PrintAndLogEx(INFO, "type : TK13"); + else if (codefound == TexkomModTK15) + PrintAndLogEx(INFO, "type : TK15"); + else + PrintAndLogEx(WARNING, " mod type: WRONG"); + + PrintAndLogEx(INFO, "uid : %s", sprint_hex(&tcode[3], 4)); + + if (TexcomTK13CRC(&tcode[3]) == tcode[7]) + PrintAndLogEx(INFO, "crc : OK"); + else + PrintAndLogEx(WARNING, "crc : WRONG"); + + } else if (tcode[2] == 0xFF && tcode[3] == 0xFF) { + // MMBIT + if (codefound != TexkomModTK13 && codefound != TexkomModTK15) { + PrintAndLogEx(WARNING, " mod type: WRONG"); + } + PrintAndLogEx(INFO, "type : MMBIT"); + PrintAndLogEx(INFO, "uid : %s", sprint_hex(&tcode[4], 3)); + + if (MMBITCRC(&tcode[4]) == tcode[7] >> 4) + PrintAndLogEx(INFO, "crc : OK"); + else + PrintAndLogEx(WARNING, "crc : WRONG"); + } else if (tcode[2] == 0xCA) { + // TK17 + if (codefound != TexkomModTK17) { + PrintAndLogEx(WARNING, " mod type: WRONG"); + } + PrintAndLogEx(INFO, "type : TK17"); + PrintAndLogEx(INFO, "uid : %s", sprint_hex(&tcode[3], 4)); + + if (TexcomTK17CRC(&tcode[3]) == tcode[7]) + PrintAndLogEx(INFO, "crc : OK"); + else + PrintAndLogEx(WARNING, "crc : WRONG"); + + } else { + PrintAndLogEx(INFO, "type : unknown"); + PrintAndLogEx(INFO, "uid : %s (maybe)", sprint_hex(&tcode[3], 4)); + } + } else { + PrintAndLogEx(ERR, "Code have no preamble FFFF: %s", sprint_hex(tcode, 8)); + } + } else { + if (strlen(genbitstring) > 0) + PrintAndLogEx(INFO, "General decoding bitstring: %s", genbitstring); + if (strlen(bitstring) > 0) + PrintAndLogEx(INFO, "last raw bit string [%zu]: %s", strlen(bitstring), bitstring); + if (strlen(cbitstring) > 0) + PrintAndLogEx(INFO, "last bit string [%zu]: %s", strlen(cbitstring), cbitstring); + + PrintAndLogEx(ERR, "Texkom card is not found"); + } + + return PM3_SUCCESS; +} + +static int CmdHFTexkomSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf texkom sim", + "Simulate a texkom tag", + "hf texkom sim \r\n" + "hf texkom sim --raw FFFF638C7DC45553 -> simulate TK13 tag with id 8C7DC455\r\n" + "hf texkom sim --tk17 --raw FFFFCA17F31EC512 -> simulate TK17 tag with id 17F31EC5\r\n" + "hf texkom sim --id 8C7DC455 -> simulate TK13 tag with id 8C7DC455\r\n" + "hf texkom sim --id 8C7DC455 --tk17 -> simulate TK17 tag with id 17F31EC5"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "Verbose work"), + arg_lit0("t", "tk17", "Use TK-17 modulation (TK-13 by default)"), + arg_str0(NULL, "raw", "", "Raw data for texkom card, 8 bytes. Manual modulation select."), + arg_str0(NULL, "id", "", "Raw data for texkom card, 8 bytes. Manual modulation select."), + arg_int0(NULL, "timeout", "", "Simulation timeout in the ms. If not specified or 0 - infinite. Command can be skipped by pressing the button"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + // + struct p { + uint8_t data[8]; + uint8_t modulation; + uint32_t timeout; + } PACKED payload = {}; + + bool verbose = arg_get_lit(ctx, 1); + payload.modulation = 0; // tk-13 + if (arg_get_lit(ctx, 2)) + payload.modulation = 1; //tk-17 + + uint8_t rawdata[250] = {0}; + int rawdatalen = 0; + CLIGetHexWithReturn(ctx, 3, rawdata, &rawdatalen); + + uint8_t iddata[250] = {0}; + int iddatalen = 0; + CLIGetHexWithReturn(ctx, 4, iddata, &iddatalen); + + payload.timeout = arg_get_int_def(ctx, 5, 0); + + CLIParserFree(ctx); + + if (rawdatalen == 0 && iddatalen == 0) { + PrintAndLogEx(ERR, " or must be specified to simulate"); + return PM3_EINVARG; + } + + if (iddatalen > 0 && iddatalen != 4) { + PrintAndLogEx(ERR, " must be 4 bytes long instead of: %d", iddatalen); + return PM3_EINVARG; + } + + if (iddatalen == 4) { + rawdata[0] = 0xff; + rawdata[1] = 0xff; + rawdata[2] = (payload.modulation == 0) ? 0x63 : 0xCA; + memcpy(&rawdata[3], iddata, 4); + rawdata[7] = (payload.modulation == 0) ? TexcomTK13CRC(iddata) : TexcomTK17CRC(iddata); + rawdatalen = 8; + } + + if (rawdatalen > 0 && rawdatalen != 8) { + PrintAndLogEx(ERR, " must be 8 bytes long instead of: %d", rawdatalen); + return PM3_EINVARG; + } + + memcpy(payload.data, rawdata, 8); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_TEXKOM_SIMULATE, (uint8_t *)&payload, sizeof(payload)); + + if (payload.timeout > 0 && payload.timeout < 2800) { + PrintAndLogEx(INFO, "simulate command started"); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_TEXKOM_SIMULATE, &resp, 3000) == false) { + if (verbose) { + PrintAndLogEx(WARNING, "(hf texkom simulate) command execution time out"); + } + return PM3_ETIMEOUT; + } + PrintAndLogEx(INFO, "simulate command execution done"); + } else { + PrintAndLogEx(INFO, "simulate command started..."); + } + + return PM3_SUCCESS; +} + +static int CmdHelp(const char *Cmd); + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"reader", CmdHFTexkomReader, IfPm3Iso14443a, "Act like a Texkom reader"}, + {"sim", CmdHFTexkomSim, IfPm3Iso14443a, "Simulate a Texkom tag"}, + //{"write", CmdHFTexkomWrite, IfPm3Iso14443a, "Write a Texkom tag"}, + {NULL, NULL, 0, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFTexkom(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhftexkom.h b/client/src/cmdhftexkom.h new file mode 100644 index 000000000..079a2e4fb --- /dev/null +++ b/client/src/cmdhftexkom.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency proximity cards from TEXCOM commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFTEXCOM_H__ +#define CMDHFTEXCOM_H__ + +#include "common.h" +#include "pm3_cmd.h" + +enum TK17Bits { + TK17WrongBit, + TK17Bit00, + TK17Bit01, + TK17Bit10, + TK17Bit11 +}; + +enum TexkomModulation { + TexkomModError, + TexkomModTK13, + TexkomModTK15, + TexkomModTK17 +}; + +typedef struct { + uint8_t tcode[8]; + uint8_t rtcode[8]; + uint8_t tagtype; +} PACKED texkom_card_select_t; + + +int CmdHFTexkom(const char *Cmd); +int read_texkom_uid(bool loop, bool verbose); +#endif diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index 778471b99..6311b50ed 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -33,25 +33,12 @@ #include "nfc/ndef.h" #include "fileutils.h" // saveFile -#define TOPAZ_STATIC_MEMORY (0x0f * 8) // 15 blocks with 8 Bytes each -// a struct to describe a memory area which contains lock bits and the corresponding lockable memory area -typedef struct dynamic_lock_area { - struct dynamic_lock_area *next; - uint16_t byte_offset; // the address of the lock bits - uint16_t size_in_bits; - uint16_t first_locked_byte; // the address of the lockable area - uint16_t bytes_locked_per_bit; -} dynamic_lock_area_t; +#ifndef AddCrc14B +# define AddCrc14B(data, len) compute_crc(CRC_14443_B, (data), (len), (data)+(len), (data)+(len)+1) +#endif -static struct { - uint8_t HR01[2]; - uint8_t uid[7]; - uint16_t size; - uint8_t data_blocks[TOPAZ_STATIC_MEMORY / 8][8]; // this memory is always there - uint8_t *dynamic_memory; // this memory can be there - dynamic_lock_area_t *dynamic_lock_areas; // lock area descriptors -} topaz_tag; +static topaz_tag_t topaz_tag; static void topaz_switch_on_field(void) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, 0, 0, NULL, 0); @@ -88,10 +75,7 @@ static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint // calculate CRC bytes and send topaz command, returns the length of the response (0 in case of error) static int topaz_send_cmd(uint8_t *cmd, uint8_t len, uint8_t *response, uint16_t *response_len, bool verbose) { if (len > 1) { - uint8_t b1, b2; - compute_crc(CRC_14443_B, cmd, len - 2, &b1, &b2); - cmd[len - 2] = b1; - cmd[len - 1] = b2; + AddCrc14B(cmd, len - 2); } return topaz_send_cmd_raw(cmd, len, response, response_len, verbose); @@ -126,7 +110,8 @@ static int topaz_select(uint8_t *atqa, uint8_t atqa_len, uint8_t *rid_response, // read all of the static memory of a selected Topaz tag. static int topaz_rall(uint8_t *uid, uint8_t *response) { - uint16_t resp_len = 0; + + uint16_t resp_len = 124; uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; memcpy(&rall_cmd[3], uid, 4); @@ -139,30 +124,68 @@ static int topaz_rall(uint8_t *uid, uint8_t *response) { } // read a block (8 Bytes) of a selected Topaz tag. -static int topaz_read_block(uint8_t *uid, uint8_t blockno, uint8_t *block_data) { - uint16_t resp_len = 0; - uint8_t read8_cmd[] = {TOPAZ_READ8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t read8_response[11] = {0}; +static int topaz_read_block(uint8_t blockno, uint8_t *block_data) { + uint8_t atqa[2] = {0}; + uint8_t rid_response[8] = {0}; + int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true); + if (res != PM3_SUCCESS) { + return res; + } - read8_cmd[1] = blockno; - memcpy(&read8_cmd[10], uid, 4); + if (atqa[1] != 0x0c && atqa[0] != 0x00) { + return res; + } - if (topaz_send_cmd(read8_cmd, sizeof(read8_cmd), read8_response, &resp_len, true) == PM3_ETIMEOUT) { + uint8_t *uid_echo = &rid_response[2]; + uint8_t rall_response[124] = {0}; + + res = topaz_rall(uid_echo, rall_response); + if (res == PM3_ESOFT) { + return res; + } + + uint8_t read8_cmd[] = {TOPAZ_READ8, blockno, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(&read8_cmd[10], uid_echo, 4); + + uint16_t resp_len = 11; + uint8_t response[11] = {0}; + + if (topaz_send_cmd(read8_cmd, sizeof(read8_cmd), response, &resp_len, true) == PM3_ETIMEOUT) { topaz_switch_off_field(); return PM3_ESOFT; // READ8 failed } - memcpy(block_data, &read8_response[1], 8); + memcpy(block_data, &response[1], 8); return PM3_SUCCESS; } // read a segment (16 blocks = 128 Bytes) of a selected Topaz tag. Works only for tags with dynamic memory. -static int topaz_read_segment(uint8_t *uid, uint8_t segno, uint8_t *segment_data) { - uint16_t resp_len = 0; +static int topaz_read_segment(uint8_t segno, uint8_t *segment_data) { + + uint8_t atqa[2] = {0}; + uint8_t rid_response[8] = {0}; + int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true); + if (res != PM3_SUCCESS) { + return res; + } + + if (atqa[1] != 0x0c && atqa[0] != 0x00) { + return res; + } + + uint8_t *uid_echo = &rid_response[2]; + uint8_t rall_response[124] = {0}; + + res = topaz_rall(uid_echo, rall_response); + if (res == PM3_ESOFT) { + return res; + } + + uint16_t resp_len = 131; uint8_t rseg_cmd[] = {TOPAZ_RSEG, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t rseg_response[131]; rseg_cmd[1] = segno << 4; - memcpy(&rseg_cmd[10], uid, 4); + memcpy(&rseg_cmd[10], uid_echo, 4); if (topaz_send_cmd(rseg_cmd, sizeof(rseg_cmd), rseg_response, &resp_len, true) == PM3_ETIMEOUT) { topaz_switch_off_field(); @@ -172,6 +195,55 @@ static int topaz_read_segment(uint8_t *uid, uint8_t segno, uint8_t *segment_data return PM3_SUCCESS; } +// write a block (8 Bytes) of a selected Topaz tag. +static int topaz_write_erase8_block(uint8_t blockno, uint8_t *block_data) { + + uint8_t atqa[2] = {0}; + uint8_t rid_response[8] = {0}; + int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true); + if (res != PM3_SUCCESS) { + return res; + } + + if (atqa[1] != 0x0c && atqa[0] != 0x00) { + return res; + } + + uint8_t *uid_echo = &rid_response[2]; + uint8_t rall_response[124] = {0}; + + res = topaz_rall(uid_echo, rall_response); + if (res == PM3_ESOFT) { + return res; + } + + uint8_t wr8_cmd[] = {TOPAZ_WRITE_E8, blockno, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(wr8_cmd + 10, uid_echo, 4); + memcpy(wr8_cmd + 2, block_data, 8); + + uint16_t resp_len = 11; + uint8_t response[11] = {0}; + + if (topaz_send_cmd(wr8_cmd, sizeof(wr8_cmd), response, &resp_len, true) == PM3_ETIMEOUT) { + topaz_switch_off_field(); + return PM3_ESOFT; // WriteErase 8bytes failed + } + + if (resp_len != 11) { + return PM3_EFAILED; + } + + if (blockno != response[0]) { + return PM3_EFAILED; + } + + if (memcmp(block_data, response + 1, 8) == 0) { + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + + // search for the lock area descriptor for the lockable area including byteno static dynamic_lock_area_t *get_dynamic_lock_area(uint16_t byteno) { dynamic_lock_area_t *lock_area; @@ -220,23 +292,113 @@ static bool topaz_byte_is_locked(uint16_t byteno) { } } -// read and print the Capability Container -static int topaz_print_CC(uint8_t *data) { - if (data[0] != 0xe1) { +static int topaz_set_cc_dynamic(const uint8_t *data) { + + if (data[0] != 0xE1) { topaz_tag.size = TOPAZ_STATIC_MEMORY; + PrintAndLogEx(WARNING, "No Type 1 NDEF capability container found"); return PM3_ESOFT; // no NDEF message } - PrintAndLogEx(SUCCESS, "Capability Container: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); - PrintAndLogEx(SUCCESS, " %02x: NDEF Magic Number", data[0]); - PrintAndLogEx(SUCCESS, " %02x: version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f); + // setting of dynamic memory and allocation of such memory. uint16_t memsize = (data[2] + 1) * 8; topaz_tag.size = memsize; topaz_tag.dynamic_memory = calloc(memsize - TOPAZ_STATIC_MEMORY, sizeof(uint8_t)); - PrintAndLogEx(SUCCESS, " %02x: Physical Memory Size of this tag: %d bytes", data[2], memsize); - PrintAndLogEx(SUCCESS, " %02x: %s / %s", data[3], - (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", - (data[3] & 0x0F) == 0 ? "Write access granted without any security" : (data[3] & 0x0F) == 0x0F ? "No write access granted at all" : "(RFU)"); + if (topaz_tag.dynamic_memory == NULL) { + return PM3_EMALLOC; + } + return PM3_SUCCESS; +} + +// read and print the Capability Container +static int topaz_print_CC(uint8_t *data) { + + if (data[0] != 0xE1) { + topaz_tag.size = TOPAZ_STATIC_MEMORY; + PrintAndLogEx(WARNING, "No Type 1 NDEF capability container found"); + return PM3_ESOFT; // no NDEF message + } + + +//NFC Forum Type 1,2,3,4 +// +// 4 has 1.1 (11) + +// b7, b6 major version +// b5, b4 minor version +// b3, b2 read +// 00 always, 01 rfu, 10 proprietary, 11 rfu +// b1, b0 write +// 00 always, 01 rfo, 10 proprietary, 11 never + +// vs + +// NFC Forum Type 2 docs, where +// b7654 = major version +// b3219 = minor version + +// CC write / read access +// (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", +// (data[3] & 0x0F) == 0 ? "Write access granted without any security" : (data[3] & 0x0F) == 0x0F ? "No write access granted at all" : "(RFU)"); + uint8_t cc_major = (data[1] & 0xF0) >> 4; + uint8_t cc_minor = (data[1] & 0x00); + + uint8_t cc_write = data[3] & 0x0F; + uint8_t cc_read = (data[3] & 0xF0) >> 4; + + const char *wStr; + switch (cc_write) { + case 0: + wStr = "Write access granted without any security"; + break; + case 0xF: + wStr = "No write access"; + break; + default: + wStr = "RFU"; + break; + } + const char *rStr; + switch (cc_read) { + case 0: + rStr = "Read access granted without any security"; + break; + default: + rStr = "RFU"; + break; + } + + PrintAndLogEx(SUCCESS, "Capability Container: %s", sprint_hex(data, 4)); + PrintAndLogEx(SUCCESS, " %02X: NDEF Magic Number", data[0]); + +// PrintAndLogEx(SUCCESS, " %02X : version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0F); + PrintAndLogEx(SUCCESS, " %02X: version %d.%d supported by tag", data[1], cc_major, cc_minor); + PrintAndLogEx(SUCCESS, " : %s / %s", rStr, wStr); + + PrintAndLogEx(SUCCESS, " %02X: Physical Memory Size: %d bytes", data[2], (data[2] + 1) * 8); + if (data[2] == 0x0E) + PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 120); + else if (data[2] == 0x1F) + PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 256); + else if (data[2] == 0xFF) + PrintAndLogEx(SUCCESS, " %02X: NDEF Memory Size: %d bytes", data[2], 2048); + + uint8_t msb3 = (data[3] & 0xE0) >> 5; + uint8_t sf = (data[3] & 0x10) >> 4; + uint8_t lb = (data[3] & 0x08) >> 3; + uint8_t mlrule = (data[3] & 0x06) >> 1; + uint8_t mbread = (data[3] & 0x01); + + PrintAndLogEx(SUCCESS, " %02X: Additional feature information", data[3]); + PrintAndLogEx(SUCCESS, " ^^"); + PrintAndLogEx(SUCCESS, " %s", sprint_bin(&data[3], 1)); + PrintAndLogEx(SUCCESS, " xxx..... - %02X: RFU ( %s )", msb3, (msb3 == 0) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(SUCCESS, " ...x.... - %02X: %s special frame", sf, (sf) ? "support" : "don\'t support"); + PrintAndLogEx(SUCCESS, " ....x... - %02X: %s lock block", lb, (lb) ? "support" : "don\'t support"); + PrintAndLogEx(SUCCESS, " .....xx. - %02X: RFU ( %s )", mlrule, (mlrule == 0) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(SUCCESS, " .......x - %02X: IC %s multiple block reads", mbread, (mbread) ? "support" : "don\'t support"); + PrintAndLogEx(SUCCESS, ""); + return PM3_SUCCESS; } @@ -295,8 +457,10 @@ static void topaz_print_control_TLVs(uint8_t *memory) { uint16_t next_lockable_byte = 0x0f * 8; // first byte after static memory area while (*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { + // all Lock Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) get_TLV(&TLV_ptr, &TLV_type, &TLV_length, &TLV_value); + if (TLV_type == 0x01) { // a Lock Control TLV uint8_t pages_addr = TLV_value[0] >> 4; uint8_t byte_offset = TLV_value[0] & 0x0f; @@ -305,13 +469,14 @@ static void topaz_print_control_TLVs(uint8_t *memory) { uint16_t bytes_per_page = 1 << (TLV_value[2] & 0x0f); uint16_t bytes_locked_per_bit = 1 << (TLV_value[2] >> 4); uint16_t area_start = pages_addr * bytes_per_page + byte_offset; - PrintAndLogEx(SUCCESS, "Lock Area of %d bits at byte offset 0x%04x. Each Lock Bit locks %d bytes.", - size_in_bits, - area_start, - bytes_locked_per_bit); + + PrintAndLogEx(SUCCESS, "Lock Area of " _YELLOW_("%d") " bits at byte offset " _YELLOW_("0x%04x"), size_in_bits, area_start); + PrintAndLogEx(SUCCESS, "Each lock bit locks " _YELLOW_("%d") " bytes", bytes_locked_per_bit); + lock_TLV_present = true; dynamic_lock_area_t *old = topaz_tag.dynamic_lock_areas; dynamic_lock_area_t *new; + if (old == NULL) { new = topaz_tag.dynamic_lock_areas = (dynamic_lock_area_t *) calloc(sizeof(dynamic_lock_area_t), sizeof(uint8_t)); } else { @@ -321,38 +486,45 @@ static void topaz_print_control_TLVs(uint8_t *memory) { new = old->next = (dynamic_lock_area_t *) calloc(sizeof(dynamic_lock_area_t), sizeof(uint8_t)); } new->next = NULL; + if (area_start <= next_lockable_byte) { // lock areas are not lockable next_lockable_byte += size_in_bytes; } + new->first_locked_byte = next_lockable_byte; new->byte_offset = area_start; new->size_in_bits = size_in_bits; new->bytes_locked_per_bit = bytes_locked_per_bit; next_lockable_byte += size_in_bits * bytes_locked_per_bit; } + if (TLV_type == 0x02) { // a Reserved Memory Control TLV uint8_t pages_addr = TLV_value[0] >> 4; uint8_t byte_offset = TLV_value[0] & 0x0f; uint16_t size_in_bytes = TLV_value[1] ? TLV_value[1] : 256; uint8_t bytes_per_page = 1 << (TLV_value[2] & 0x0f); uint16_t area_start = pages_addr * bytes_per_page + byte_offset; - PrintAndLogEx(SUCCESS, "Reserved Memory of %d bytes at byte offset 0x%02x.", + + PrintAndLogEx(SUCCESS, "Reserved Memory... " _GREEN_("%d") " bytes at byte offset " _YELLOW_("0x%02x"), size_in_bytes, area_start); + reserved_memory_control_TLV_present = true; - adjust_lock_areas(area_start, size_in_bytes); // reserved memory areas are not lockable + + // reserved memory areas are not lockable + adjust_lock_areas(area_start, size_in_bytes); if (area_start <= next_lockable_byte) { next_lockable_byte += size_in_bytes; } } } - if (!lock_TLV_present) { + if (lock_TLV_present == false) { PrintAndLogEx(SUCCESS, "(No Lock Control TLV present)"); } - if (!reserved_memory_control_TLV_present) { + if (reserved_memory_control_TLV_present == false) { PrintAndLogEx(SUCCESS, "(No Reserved Memory Control TLV present)"); } } @@ -360,15 +532,15 @@ static void topaz_print_control_TLVs(uint8_t *memory) { // read all of the dynamic memory static int topaz_read_dynamic_data(void) { // first read the remaining block of segment 0 - if (topaz_read_block(topaz_tag.uid, 0x0F, &topaz_tag.dynamic_memory[0]) == PM3_ESOFT) { + if (topaz_read_block(0x0F, &topaz_tag.dynamic_memory[0]) == PM3_ESOFT) { PrintAndLogEx(ERR, "Error while reading dynamic memory block " _YELLOW_("%02x") ". Aborting...", 0x0F); return PM3_ESOFT; } // read the remaining segments - uint8_t max_segment = topaz_tag.size / 128 - 1; - for (uint8_t segment = 1; segment <= max_segment; segment++) { - if (topaz_read_segment(topaz_tag.uid, segment, &topaz_tag.dynamic_memory[(segment - 1) * 128 + 8]) == PM3_ESOFT) { + uint8_t max = topaz_tag.size / 128 - 1; + for (uint8_t segment = 1; segment <= max; segment++) { + if (topaz_read_segment(segment, &topaz_tag.dynamic_memory[(segment - 1) * 128 + 8]) == PM3_ESOFT) { PrintAndLogEx(ERR, "Error while reading dynamic memory block " _YELLOW_("%02x") ". Aborting...", segment); return PM3_ESOFT; } @@ -378,23 +550,31 @@ static int topaz_read_dynamic_data(void) { // read and print the dynamic memory static void topaz_print_dynamic_data(void) { - if (topaz_tag.size > TOPAZ_STATIC_MEMORY) { - PrintAndLogEx(SUCCESS, "Dynamic Data blocks:"); - if (topaz_read_dynamic_data() == 0) { - PrintAndLogEx(NORMAL, "block# | offset | Data | Locked(y/n)"); - PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------"); - char line[80]; - for (uint16_t blockno = 0x0F; blockno < topaz_tag.size / 8; blockno++) { - uint8_t *block_data = &topaz_tag.dynamic_memory[(blockno - 0x0F) * 8]; - char lockbits[9]; - for (uint16_t j = 0; j < 8; j++) { - int offset = 3 * j; - snprintf(line + offset, sizeof(line) - offset, "%02x ", block_data[j]); - lockbits[j] = topaz_byte_is_locked(blockno * 8 + j) ? 'y' : 'n'; - } - lockbits[8] = '\0'; - PrintAndLogEx(NORMAL, " 0x%02x | 0x%04x | %s| %-3s", blockno, blockno * 8, line, lockbits); + + if (topaz_tag.size <= TOPAZ_STATIC_MEMORY) { + return; + } + + PrintAndLogEx(SUCCESS, "Dynamic Data blocks:"); + + if (topaz_read_dynamic_data() == PM3_SUCCESS) { + + PrintAndLogEx(SUCCESS, "block# | Data |lck"); + PrintAndLogEx(SUCCESS, "-------+-------------------------+-------------"); + + char line[80]; + for (uint16_t blockno = 0x0F; blockno < topaz_tag.size / 8; blockno++) { + + uint8_t *block_data = &topaz_tag.dynamic_memory[(blockno - 0x0F) * 8]; + char lockbits[9]; + for (uint16_t j = 0; j < 8; j++) { + int offset = 3 * j; + snprintf(line + offset, sizeof(line) - offset, "%02x ", block_data[j]); + lockbits[j] = topaz_byte_is_locked(blockno * 8 + j) ? 'y' : 'n'; } + lockbits[8] = '\0'; + + PrintAndLogEx(SUCCESS, " 0x%02x | %s| %-3s", blockno, line, lockbits); } } } @@ -403,24 +583,69 @@ static void topaz_print_lifecycle_state(uint8_t *data) { // to be done } +static void printTopazDumpContents(topaz_tag_t *dump) { + + // uses a global var for all + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Static data blocks :"); + PrintAndLogEx(SUCCESS, "block# | data |lck| info"); + PrintAndLogEx(SUCCESS, "------------+-------------------------+---+------------"); + + const char *block_info; + const char *topaz_ks[] = { "uid", "user", "rfu", "lock / otp" }; + + for (uint8_t i = 0; i <= 0x0C; i++) { + + if (i == 0) + block_info = topaz_ks[i]; + else + block_info = topaz_ks[1]; + + const char *lockstr = (topaz_byte_is_locked(i * 8)) ? _RED_("x") : " "; + + PrintAndLogEx(SUCCESS, " %3u / 0x%02x | %s| %s | %s", + i, + i, + sprint_hex(&dump->data_blocks[i][0], 8), + lockstr, + block_info + ); + } + + PrintAndLogEx(SUCCESS, " %3u / 0x%02x | %s| | %s", 0x0D, 0x0D, sprint_hex(&dump->data_blocks[0x0D][0], 8), topaz_ks[2]); + PrintAndLogEx(SUCCESS, " %3u / 0x%02x | %s| | %s", 0x0E, 0x0E, sprint_hex(&dump->data_blocks[0x0E][0], 8), topaz_ks[3]); + PrintAndLogEx(SUCCESS, "------------+-------------------------+---+------------"); + PrintAndLogEx(NORMAL, ""); +} + static int CmdHFTopazReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz reader", "Read UID from Topaz tags", - "hf topaz reader"); + "hf topaz reader\n" + "hf topaz reader -@ -> Continuous mode\n" + ); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "verbose output"), + arg_lit0("@", NULL, "optional - continuous reader mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); - + bool cm = arg_get_lit(ctx, 2); CLIParserFree(ctx); - return readTopazUid(verbose); + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + int res = readTopazUid(cm, verbose); + + topaz_switch_off_field(); + return res; } // read a Topaz tag and print some useful information @@ -447,47 +672,36 @@ int CmdHFTopazInfo(const char *Cmd) { CLIParserFree(ctx); - int status = readTopazUid(verbose); - if (status != PM3_SUCCESS) + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + + int status = readTopazUid(false, verbose); + if (status != PM3_SUCCESS) { return status; - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Static Data blocks " _YELLOW_("0x00") " to " _YELLOW_("0x0C")":"); - PrintAndLogEx(NORMAL, "block# | offset | Data | Locked"); - PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------"); - char line[80]; - for (uint16_t i = 0; i <= 0x0c; i++) { - char lockbits[9]; - for (uint16_t j = 0; j < 8; j++) { - int offset = 3 * j; - snprintf(line + offset, sizeof(line) - offset, "%02x ", topaz_tag.data_blocks[i][j] /*rall_response[2 + 8*i + j]*/); - lockbits[j] = topaz_byte_is_locked(i * 8 + j) ? 'y' : 'n'; - } - lockbits[8] = '\0'; - PrintAndLogEx(NORMAL, " 0x%02x | 0x%02x | %s| %-3s", i, i * 8, line, lockbits); } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Static Reserved block " _YELLOW_("0x0D")":"); - for (uint16_t j = 0; j < 8; j++) { - int offset = 3 * j; - snprintf(line + offset, sizeof(line) - offset, "%02x ", topaz_tag.data_blocks[0x0d][j]); - } - PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------"); - PrintAndLogEx(NORMAL, " 0x%02x | 0x%02x | %s| %-3s", 0x0d, 0x0d * 8, line, "n/a"); - PrintAndLogEx(NORMAL, ""); - - PrintAndLogEx(SUCCESS, "Static Lockbits and OTP Bytes:"); - for (uint16_t j = 0; j < 8; j++) { - int offset = 3 * j; - snprintf(line + offset, sizeof(line) - offset, "%02x ", topaz_tag.data_blocks[0x0e][j]); - } - PrintAndLogEx(NORMAL, "-------+--------+-------------------------+------------"); - PrintAndLogEx(NORMAL, " 0x%02x | 0x%02x | %s| %-3s", 0x0e, 0x0e * 8, line, "n/a"); + PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(topaz_tag.uid[6])); + + // ToDo: CRC check + PrintAndLogEx(SUCCESS, " HR: " _GREEN_("%02X %02X"), topaz_tag.HR01[0], topaz_tag.HR01[1]); + PrintAndLogEx(SUCCESS, " ^^"); + + PrintAndLogEx(SUCCESS, " %s", sprint_bin(topaz_tag.HR01, 1)); + PrintAndLogEx(SUCCESS, " ...x.... - %s / %s", + (topaz_tag.HR01[0] & 0xF0) == 0x10 ? _GREEN_("TOPAZ tag") : "", + (topaz_tag.HR01[0] & 0xF0) == 0x10 ? _GREEN_("Type 1 NDEF") : "" + ); + PrintAndLogEx(SUCCESS, " .......x - %s memory map", ((topaz_tag.HR01[0] & 0x0F) == 0x01) ? "Static" : "Dynamic"); + PrintAndLogEx(SUCCESS, ""); + PrintAndLogEx(SUCCESS, " Lock bytes... %02X%02X", + topaz_tag.data_blocks[0x0e][0], + topaz_tag.data_blocks[0x0e][1] + ); + + PrintAndLogEx(SUCCESS, " OTP.......... %s", sprint_hex(&topaz_tag.data_blocks[0x0e][2], 6)); PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("NDEF configuration") " ---------------------------"); status = topaz_print_CC(&topaz_tag.data_blocks[1][0]); - if (status == PM3_ESOFT) { PrintAndLogEx(SUCCESS, "No NDEF message data present"); topaz_switch_off_field(); @@ -498,16 +712,6 @@ int CmdHFTopazInfo(const char *Cmd) { topaz_print_control_TLVs(&topaz_tag.data_blocks[1][4]); PrintAndLogEx(NORMAL, ""); - topaz_print_dynamic_data(); - - topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]); - - if (fnlen != 0) { - saveFile(filename, ".bin", &topaz_tag.data_blocks[1][0], TOPAZ_STATIC_MEMORY); - } - NDEFDecodeAndPrint(&topaz_tag.data_blocks[1][0], TOPAZ_STATIC_MEMORY, true); - - PrintAndLogEx(INFO, "-------------------------------------------------------------"); topaz_switch_off_field(); return PM3_SUCCESS; } @@ -528,11 +732,11 @@ static int CmdHFTopazSim(const char *Cmd) { return PM3_SUCCESS; } -static int CmdHFTopazCmdRaw(const char *Cmd) { +static int CmdHFTopazRaw(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz raw", "Send raw hex data to Topaz tags", - "hf topaz raw -> Not yet implemented"); + "hf topaz raw"); void *argtable[] = { arg_param_begin, @@ -540,8 +744,7 @@ static int CmdHFTopazCmdRaw(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - - PrintAndLogEx(INFO, "not yet implemented. Use hf 14 raw with option -T."); + PrintAndLogEx(INFO, "not yet implemented. Use `hf 14 raw --topaz` meanwhile"); return PM3_SUCCESS; } @@ -568,16 +771,216 @@ static int CmdHFTopazSniff(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFTopazDump(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf topaz dump", + "Dump TOPAZ tag to binary file\n" + "If no given, UID will be used as filename", + "hf topaz dump\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dump"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + int status = readTopazUid(false, false); + if (status != PM3_SUCCESS) { + return status; + } + printTopazDumpContents(&topaz_tag); + + bool set_dynamic = false; + if (topaz_set_cc_dynamic(&topaz_tag.data_blocks[1][0]) == PM3_SUCCESS) { + set_dynamic = true; + + topaz_print_dynamic_data(); + + topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]); + + NDEFDecodeAndPrint(&topaz_tag.data_blocks[1][4], + (topaz_tag.HR01[0] == 1) ? (12 * 8) : 476 + , true + ); + } + + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + topaz_switch_off_field(); + + // user supplied filename? + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + strcat(filename, "hf-topaz-"); + FillFileNameByUID(filename, topaz_tag.uid, "-dump", sizeof(topaz_tag.uid)); + } + + if (topaz_tag.size) + pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t) + topaz_tag.size, jsfTopaz, TOPAZ_BLOCK_SIZE); + else + pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t), jsfTopaz, TOPAZ_BLOCK_SIZE); + + if (set_dynamic) { + free(topaz_tag.dynamic_memory); + } + + return PM3_SUCCESS; +} + +static int CmdHFTopazView(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf topaz view", + "Print a Topaz tag dump file (bin/eml/json)", + "hf topaz view -f hf-topaz-04010203-dump.bin"); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + // read dump file + topaz_tag_t *dump = NULL; + size_t bytes_read = TOPAZ_MAX_SIZE; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, sizeof(topaz_tag_t) + TOPAZ_MAX_SIZE); + if (res != PM3_SUCCESS) { + return res; + } + if (bytes_read < sizeof(topaz_tag_t)) { + free(dump); + return PM3_EFAILED; + } + printTopazDumpContents(dump); + + if (topaz_set_cc_dynamic(&topaz_tag.data_blocks[1][0]) == PM3_SUCCESS) { + + topaz_print_dynamic_data(); + + topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]); + + NDEFDecodeAndPrint(&topaz_tag.data_blocks[1][4], + (topaz_tag.HR01[0] == 1) ? (12 * 8) : 476 + , true + ); + + PrintAndLogEx(INFO, "%s", sprint_hex(&topaz_tag.data_blocks[1][4], (12 * 8))); + + free(topaz_tag.dynamic_memory); + } + + free(dump); + return PM3_SUCCESS; +} + +// Read single block +static int CmdHFTopazRdBl(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf topaz rdbl", + "Read a block", + "hf topaz rdbl -b 7\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1("b", "block", "", "Block number to write"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int blockno = arg_get_int_def(ctx, 1, -1); + CLIParserFree(ctx); + + if (blockno < 0) { + PrintAndLogEx(WARNING, "Wrong block number"); + return PM3_EINVARG; + } + + // send read block + uint8_t data[8] = {0}; + int res = topaz_read_block(blockno, data); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, sizeof(data))); + } + + topaz_switch_off_field(); + return res; +} + +// Write single block +static int CmdHFTopazWrBl(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf topaz wrbl", + "Write a block", + "hf topaz wrbl -b 7 -d 1122334455667788\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1("b", "block", "", "Block number to write"), + arg_str1("d", "data", "", "Block data (8 hex bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int blockno = arg_get_int_def(ctx, 1, -1); + + int dlen = 0; + uint8_t data[8] = {0x00}; + CLIGetHexWithReturn(ctx, 2, data, &dlen); + + CLIParserFree(ctx); + + if (blockno < 0) { + PrintAndLogEx(WARNING, "Wrong block number"); + return PM3_EINVARG; + } + + if (dlen != 8) { + PrintAndLogEx(WARNING, "Wrong data length. Expect 8, got %d", dlen); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, dlen)); + + // send write Block + int res = topaz_write_erase8_block(blockno, data); + + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + } else { + PrintAndLogEx(WARNING, "Write ( " _RED_("fail") " )"); + } + + topaz_switch_off_field(); + return res; +} + static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHFTopazList, AlwaysAvailable, "List Topaz history"}, - {"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"}, - {"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"}, - {"sim", CmdHFTopazSim, IfPm3Iso14443a, " -- Simulate Topaz tag"}, - {"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"}, - {"raw", CmdHFTopazCmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"dump", CmdHFTopazDump, IfPm3Iso14443a, "Dump TOPAZ family tag to file"}, + {"list", CmdHFTopazList, AlwaysAvailable, "List Topaz history"}, + {"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"}, + {"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"}, + {"sim", CmdHFTopazSim, IfPm3Iso14443a, "Simulate Topaz tag"}, + {"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"}, + {"raw", CmdHFTopazRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, + {"rdbl", CmdHFTopazRdBl, IfPm3Iso14443a, "Read block"}, + {"view", CmdHFTopazView, AlwaysAvailable, "Display content from tag dump file"}, + {"wrbl", CmdHFTopazWrBl, IfPm3Iso14443a, "Write block"}, {NULL, NULL, 0, NULL} }; @@ -592,72 +995,82 @@ int CmdHFTopaz(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -int readTopazUid(bool verbose) { +int readTopazUid(bool loop, bool verbose) { - uint8_t atqa[2] = {0}; - uint8_t rid_response[8] = {0}; - uint8_t *uid_echo = &rid_response[2]; - uint8_t rall_response[124] = {0}; + int res = PM3_SUCCESS; - int status = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), verbose); - if (status == PM3_ESOFT) { - if (verbose) PrintAndLogEx(ERR, "Error: couldn't receive ATQA"); - return PM3_ESOFT; - } + do { + uint8_t atqa[2] = {0}; + uint8_t rid_response[8] = {0}; + uint8_t *uid_echo = &rid_response[2]; + uint8_t rall_response[124] = {0}; - if (atqa[1] != 0x0c && atqa[0] != 0x00) { - if (verbose) PrintAndLogEx(ERR, "Tag doesn't support the Topaz protocol."); - topaz_switch_off_field(); - return PM3_ESOFT; - } + int status = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), verbose); + if (status == PM3_ESOFT) { + if (verbose) PrintAndLogEx(ERR, "Error: couldn't receive ATQA"); - if (status == PM3_EWRONGANSWER) { - if (verbose) PrintAndLogEx(ERR, "Error: tag didn't answer to RID"); - topaz_switch_off_field(); - return PM3_ESOFT; - } + if (loop) { + continue; + } - status = topaz_rall(uid_echo, rall_response); - if (status == PM3_ESOFT) { - PrintAndLogEx(ERR, "Error: tag didn't answer to RALL"); - topaz_switch_off_field(); - return PM3_ESOFT; - } + res = status; + break; + } - memcpy(topaz_tag.uid, rall_response + 2, 7); - memcpy(topaz_tag.data_blocks, rall_response + 2, 0x0f * 8); + if (atqa[1] != 0x0c && atqa[0] != 0x00) { + if (verbose) PrintAndLogEx(ERR, "Tag doesn't support the Topaz protocol."); - // printing - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(SUCCESS, " UID: %02x %02x %02x %02x %02x %02x %02x", - topaz_tag.uid[6], - topaz_tag.uid[5], - topaz_tag.uid[4], - topaz_tag.uid[3], - topaz_tag.uid[2], - topaz_tag.uid[1], - topaz_tag.uid[0]); + if (loop) { + continue; + } - PrintAndLogEx(SUCCESS, " UID[6] (Manufacturer Byte) = " _YELLOW_("%02x")", Manufacturer: " _YELLOW_("%s"), - topaz_tag.uid[6], - getTagInfo(topaz_tag.uid[6]) - ); + res = PM3_ESOFT; + break; + } - PrintAndLogEx(SUCCESS, " ATQA: %02x %02x", atqa[1], atqa[0]); + if (status == PM3_EWRONGANSWER) { + if (verbose) PrintAndLogEx(ERR, "Error: tag didn't answer to RID"); - topaz_tag.HR01[0] = rid_response[0]; - topaz_tag.HR01[1] = rid_response[1]; + if (loop) { + continue; + } + res = status; + break; + } - // ToDo: CRC check - PrintAndLogEx(SUCCESS, " HR0: %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", - rid_response[0], - (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", - (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", - (rid_response[0] & 0x0F) == 0x01 ? "static" : "dynamic"); + status = topaz_rall(uid_echo, rall_response); + if (status == PM3_ESOFT) { + PrintAndLogEx(ERR, "Error: tag didn't answer to RALL"); - PrintAndLogEx(SUCCESS, " HR1: %02x", rid_response[1]); + if (loop) { + continue; + } + + res = status; + break; + } + + memcpy(topaz_tag.uid, rall_response + 2, 7); + memcpy(topaz_tag.data_blocks, rall_response + 2, 0x0F * 8); + + // printing + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%02X %02X %02X %02X %02X %02X %02X"), + topaz_tag.uid[6], + topaz_tag.uid[5], + topaz_tag.uid[4], + topaz_tag.uid[3], + topaz_tag.uid[2], + topaz_tag.uid[1], + topaz_tag.uid[0]); + + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), atqa[1], atqa[0]); + + topaz_tag.HR01[0] = rid_response[0]; + topaz_tag.HR01[1] = rid_response[1]; + + } while (loop && kbd_enter_pressed() == false); topaz_switch_off_field(); - return PM3_SUCCESS; + return res; } diff --git a/client/src/cmdhftopaz.h b/client/src/cmdhftopaz.h index 499436107..19dfbb2f9 100644 --- a/client/src/cmdhftopaz.h +++ b/client/src/cmdhftopaz.h @@ -21,8 +21,32 @@ #include "common.h" + +#define TOPAZ_STATIC_MEMORY (0x0F * 8) // 15 blocks with 8 Bytes each +#define TOPAZ_BLOCK_SIZE 8 +#define TOPAZ_MAX_SIZE 512 + +// a struct to describe a memory area which contains lock bits and the corresponding lockable memory area +typedef struct dynamic_lock_area_s { + struct dynamic_lock_area_s *next; + uint16_t byte_offset; // the address of the lock bits + uint16_t size_in_bits; + uint16_t first_locked_byte; // the address of the lockable area + uint16_t bytes_locked_per_bit; +} dynamic_lock_area_t; + +typedef struct topaz_tag_s { + uint8_t HR01[2]; + uint8_t uid[7]; + uint16_t size; + uint8_t data_blocks[TOPAZ_STATIC_MEMORY / 8][8]; // this memory is always there + uint8_t *dynamic_memory; // this memory can be there + dynamic_lock_area_t *dynamic_lock_areas; // lock area descriptors +} topaz_tag_t; + + + int CmdHFTopaz(const char *Cmd); int CmdHFTopazInfo(const char *Cmd); - -int readTopazUid(bool verbose); +int readTopazUid(bool loop, bool verbose); #endif diff --git a/client/src/cmdhfwaveshare.c b/client/src/cmdhfwaveshare.c index ae2f74582..8df62bcc1 100644 --- a/client/src/cmdhfwaveshare.c +++ b/client/src/cmdhfwaveshare.c @@ -660,7 +660,6 @@ static int start_drawing_1in54B(uint8_t model_nr, uint8_t *black, uint8_t *red) } static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { - uint8_t progress; uint8_t step0[2] = {0xcd, 0x0d}; uint8_t step1[3] = {0xcd, 0x00, 10}; // select e-paper type and reset e-paper // 4 :2.13inch e-Paper @@ -687,7 +686,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { // uint8_t step13[2]={0xcd,0x0b}; // Judge whether the power supply is turned off successfully // uint8_t step14[2]={0xcd,0x0c}; // The end of the transmission uint8_t rx[20]; - uint16_t actrxlen[20], i; + uint16_t actrxlen[20]; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); @@ -713,17 +712,17 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { return PM3_ESOFT; } - if ((card.uidlen != 7) || ((memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "WSDZ10m", 7) != 0))) { + if ((card.uidlen != 7) || ((memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "FSTN11m", 7) != 0) && (memcmp(card.uid, "WSDZ10m", 7) != 0))) { PrintAndLogEx(WARNING, "Card doesn't look like Waveshare tag"); DropField(); return PM3_ESOFT; } - if (((model_nr != M1in54B) && (memcmp(card.uid, "FSTN10m", 7) == 0))) { + if (((model_nr != M1in54B) && ((memcmp(card.uid, "FSTN10m", 7) == 0) || (memcmp(card.uid, "FSTN11m", 7) == 0)))) { PrintAndLogEx(WARNING, "Card is a Waveshare tag 1.54\", not %s", models[model_nr].desc); DropField(); return PM3_ESOFT; } - if (((model_nr == M1in54B) && (memcmp(card.uid, "FSTN10m", 7) != 0))) { + if (((model_nr == M1in54B) && (memcmp(card.uid, "FSTN10m", 7) != 0) && (memcmp(card.uid, "FSTN11m", 7) != 0))) { PrintAndLogEx(WARNING, "Card is not a Waveshare tag 1.54\", check your model number"); DropField(); return PM3_ESOFT; @@ -812,6 +811,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { } // 1.54B Data transfer is complete and wait for refresh } else { + uint8_t progress; PrintAndLogEx(DEBUG, "Step5: e-paper config2"); ret = transceive_blocking(step5, 2, rx, 20, actrxlen, true); // cd 05 if (ret != PM3_SUCCESS) { @@ -831,7 +831,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { } PrintAndLogEx(DEBUG, "Step8: Start data transfer"); if (model_nr == M2in13) { // 2.13inch - for (i = 0; i < 250; i++) { + for (uint16_t i = 0; i < 250; i++) { read_black(i, step8, model_nr, black); ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08 if (ret != PM3_SUCCESS) { @@ -841,7 +841,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { PrintAndLogEx(INPLACE, "Progress: %d %%", progress); } } else if (model_nr == M2in9) { - for (i = 0; i < 296; i++) { + for (uint16_t i = 0; i < 296; i++) { read_black(i, step8, model_nr, black); ret = transceive_blocking(step8, 19, rx, 20, actrxlen, true); // cd 08 if (ret != PM3_SUCCESS) { @@ -851,7 +851,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { PrintAndLogEx(INPLACE, "Progress: %d %%", progress); } } else if (model_nr == M4in2) { //4.2inch - for (i = 0; i < 150; i++) { + for (uint16_t i = 0; i < 150; i++) { read_black(i, step8, model_nr, black); ret = transceive_blocking(step8, 103, rx, 20, actrxlen, true); // cd 08 if (ret != PM3_SUCCESS) { @@ -861,7 +861,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { PrintAndLogEx(INPLACE, "Progress: %d %%", progress); } } else if (model_nr == M7in5) { //7.5inch - for (i = 0; i < 400; i++) { + for (uint16_t i = 0; i < 400; i++) { read_black(i, step8, model_nr, black); ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08 if (ret != PM3_SUCCESS) { @@ -872,7 +872,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { msleep(6); } } else if (model_nr == M2in13B) { //2.13inch B - for (i = 0; i < 26; i++) { + for (uint16_t i = 0; i < 26; i++) { read_black(i, step8, model_nr, black); ret = transceive_blocking(step8, 109, rx, 20, actrxlen, false); // cd 08 if (ret != PM3_SUCCESS) { @@ -883,7 +883,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { } } else if (model_nr == M7in5HD) { //7.5HD - for (i = 0; i < 484; i++) { + for (uint16_t i = 0; i < 484; i++) { read_black(i, step8, model_nr, black); //memset(&step8[3], 0xf0, 120); ret = transceive_blocking(step8, 123, rx, 20, actrxlen, true); // cd 08 @@ -899,7 +899,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { return ret; } } else if (model_nr == M2in7) { //2.7inch - for (i = 0; i < 48; i++) { + for (uint16_t i = 0; i < 48; i++) { //read_black(i,step8, model_nr, black); memset(&step8[3], 0xFF, sizeof(step8) - 3); ret = transceive_blocking(step8, 124, rx, 20, actrxlen, true); // cd 08 @@ -925,7 +925,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { } PrintAndLogEx(DEBUG, "Step9b"); if (model_nr == M2in7) { - for (i = 0; i < 48; i++) { + for (uint16_t i = 0; i < 48; i++) { read_black(i, step13, model_nr, black); ret = transceive_blocking(step13, 124, rx, 20, actrxlen, true); //CD 19 if (ret != PM3_SUCCESS) { @@ -935,7 +935,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { PrintAndLogEx(INPLACE, "Progress: %d %%", progress); } } else if (model_nr == M2in13B) { - for (i = 0; i < 26; i++) { + for (uint16_t i = 0; i < 26; i++) { read_red(i, step13, model_nr, red); //memset(&step13[3], 0xfE, 106); ret = transceive_blocking(step13, 109, rx, 20, actrxlen, false); diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c new file mode 100644 index 000000000..fec69855e --- /dev/null +++ b/client/src/cmdhfxerox.c @@ -0,0 +1,815 @@ +//----------------------------------------------------------------------------- +// High frequency Xerox commands (ISO14443B) +//----------------------------------------------------------------------------- + +#include "cmdhfxerox.h" + +#include "fileutils.h" + +#include "cmdparser.h" // command_t +#include "cliparser.h" +#include "comms.h" +#include "iso14b.h" +#include "crc16.h" + +#define TIMEOUT 2000 + + +#define c2l(c,l) (l = ((unsigned long)(*((c)++))), \ + l |= ((unsigned long)(*((c)++))) << 8L, \ + l |= ((unsigned long)(*((c)++))) << 16L, \ + l |= ((unsigned long)(*((c)++))) << 24L) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c += n; \ + l1 = l2 = 0; \ + switch (n) { \ + case 8: l2 = ((unsigned long)(*(--(c)))) << 24L; \ + case 7: l2 |= ((unsigned long)(*(--(c)))) << 16L; \ + case 6: l2 |= ((unsigned long)(*(--(c)))) << 8L; \ + case 5: l2 |= ((unsigned long)(*(--(c)))); \ + case 4: l1 = ((unsigned long)(*(--(c)))) << 24L; \ + case 3: l1 |= ((unsigned long)(*(--(c)))) << 16L; \ + case 2: l1 |= ((unsigned long)(*(--(c)))) << 8L; \ + case 1: l1 |= ((unsigned long)(*(--(c)))); \ + } \ + } + +#define l2c(l,c) (*((c)++) = (uint8_t)(((l)) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 8L) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 16L) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 24L) & 0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c += n; \ + switch (n) { \ + case 8: *(--(c)) = (uint8_t)(((l2) >> 24L) & 0xff); \ + case 7: *(--(c)) = (uint8_t)(((l2) >> 16L) & 0xff); \ + case 6: *(--(c)) = (uint8_t)(((l2) >> 8L) & 0xff); \ + case 5: *(--(c)) = (uint8_t)(((l2)) & 0xff); \ + case 4: *(--(c)) = (uint8_t)(((l1) >> 24L) & 0xff); \ + case 3: *(--(c)) = (uint8_t)(((l1) >> 16L) & 0xff); \ + case 2: *(--(c)) = (uint8_t)(((l1) >> 8L) & 0xff); \ + case 1: *(--(c)) = (uint8_t)(((l1)) & 0xff); \ + } \ + } + +/* NOTE - c is not incremented as per n2l */ +#define n2ln(c,l1,l2,n) { \ + c += n; \ + l1 = l2 = 0; \ + switch (n) { \ + case 8: l2 = ((unsigned long)(*(--(c)))); \ + case 7: l2 |= ((unsigned long)(*(--(c)))) << 8; \ + case 6: l2 |= ((unsigned long)(*(--(c)))) << 16; \ + case 5: l2 |= ((unsigned long)(*(--(c)))) << 24; \ + case 4: l1 = ((unsigned long)(*(--(c)))); \ + case 3: l1 |= ((unsigned long)(*(--(c)))) << 8; \ + case 2: l1 |= ((unsigned long)(*(--(c)))) << 16; \ + case 1: l1 |= ((unsigned long)(*(--(c)))) << 24; \ + } \ + } + +/* NOTE - c is not incremented as per l2n */ +#define l2nn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c)) = (uint8_t)(((l2)) & 0xff); \ + case 7: *(--(c)) = (uint8_t)(((l2) >> 8) & 0xff); \ + case 6: *(--(c)) = (uint8_t)(((l2) >> 16) & 0xff); \ + case 5: *(--(c)) = (uint8_t)(((l2) >> 24) & 0xff); \ + case 4: *(--(c)) = (uint8_t)(((l1)) & 0xff); \ + case 3: *(--(c)) = (uint8_t)(((l1) >> 8) & 0xff); \ + case 2: *(--(c)) = (uint8_t)(((l1) >> 16) & 0xff); \ + case 1: *(--(c)) = (uint8_t)(((l1) >> 24) & 0xff); \ + } \ + } + +#define n2l(c,l) (l = ((unsigned long)(*((c)++))) << 24L, \ + l |= ((unsigned long)(*((c)++))) << 16L, \ + l |= ((unsigned long)(*((c)++))) << 8L, \ + l |= ((unsigned long)(*((c)++)))) + +#define l2n(l,c) (*((c)++) = (uint8_t)(((l) >> 24L) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 16L) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 8L) & 0xff), \ + *((c)++) = (uint8_t)(((l)) & 0xff)) + +#define C_RC2(n) \ + t = (x0 + (x1 & ~x3) + (x2 & x3) + *(p0++)) & 0xffff; \ + x0 = (t << 1) | (t >> 15); \ + t = (x1 + (x2 & ~x0) + (x3 & x0) + *(p0++)) & 0xffff; \ + x1 = (t << 2) | (t >> 14); \ + t = (x2 + (x3 & ~x1) + (x0 & x1) + *(p0++)) & 0xffff; \ + x2 = (t << 3) | (t >> 13); \ + t = (x3 + (x0 & ~x2) + (x1 & x2) + *(p0++)) & 0xffff; \ + x3 = (t << 5) | (t >> 11); + +#define RC2_ENCRYPT 1 +#define RC2_DECRYPT 0 + +typedef unsigned int RC2_INT; + +typedef struct rc2_key_st { + RC2_INT data[64]; +} RC2_KEY; + +static const uint8_t lut[256] = { + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, + 0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, + 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5, + 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, + 0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, + 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f, + 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, + 0xbc, 0x94, 0x43, 0x03, 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, + 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x08, 0xe8, 0xea, 0xde, + 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, + 0x04, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, + 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85, + 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, + 0x67, 0x6c, 0xba, 0xc9, 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, + 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0x0d, 0x38, 0x34, 0x1b, + 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, + 0xfe, 0x7f, 0xc1, 0xad, +}; + +static const uint8_t var_list[] = {0x1c, 0x1e, 0x20, 0x26, 0x28, 0x2a, 0x2c, 0x2e}; + + +static int CmdHelp(const char *Cmd); +void RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits); +void RC2_encrypt(unsigned long *d, RC2_KEY *key); +void RC2_decrypt(unsigned long *d, RC2_KEY *key); +void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, RC2_KEY *ks, unsigned char *iv, int encrypt); + + +void RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits) { + int i, j; + unsigned char *k; + RC2_INT *ki; + unsigned int c, d; + + k = (unsigned char *) & (key->data[0]); + *k = 0; /* for if there is a zero length key */ + + if (len > 128) + len = 128; + + if (bits <= 0) + bits = 1024; + + if (bits > 1024) + bits = 1024; + + for (i = 0; i < len; i++) + k[i] = data[i]; + + /* expand table */ + d = k[len - 1]; + j = 0; + for (i = len; i < 128; i++, j++) { + d = lut[(k[j] + d) & 0xff]; + k[i] = d; + } + + /* hmm.... key reduction to 'bits' bits */ + + j = (bits + 7) >> 3; + i = 128 - j; + c = (0xff >> (-bits & 0x07)); + + d = lut[k[i] & c]; + k[i] = d; + while (i--) { + d = lut[k[i + j] ^ d]; + k[i] = d; + } + + /* copy from bytes into RC2_INT's */ + ki = &(key->data[63]); + for (i = 127; i >= 0; i -= 2) + * (ki--) = ((k[i] << 8) | k[i - 1]) & 0xffff; +} + +void RC2_encrypt(unsigned long *d, RC2_KEY *key) { + int i, n; + register RC2_INT *p0, *p1; + register RC2_INT x0, x1, x2, x3; + unsigned long l; + + l = d[0]; + x0 = (RC2_INT)l & 0xffff; + x1 = (RC2_INT)(l >> 16L); + l = d[1]; + x2 = (RC2_INT)l & 0xffff; + x3 = (RC2_INT)(l >> 16L); + + n = 3; + i = 5; + + p0 = p1 = &(key->data[0]); + for (;;) { + register RC2_INT t = (x0 + (x1 & ~x3) + (x2 & x3) + * (p0++)) & 0xffff; + x0 = (t << 1) | (t >> 15); + t = (x1 + (x2 & ~x0) + (x3 & x0) + * (p0++)) & 0xffff; + x1 = (t << 2) | (t >> 14); + t = (x2 + (x3 & ~x1) + (x0 & x1) + * (p0++)) & 0xffff; + x2 = (t << 3) | (t >> 13); + t = (x3 + (x0 & ~x2) + (x1 & x2) + * (p0++)) & 0xffff; + x3 = (t << 5) | (t >> 11); + + if (--i == 0) { + if (--n == 0) break; + i = (n == 2) ? 6 : 5; + + x0 += p1[x3 & 0x3f]; + x1 += p1[x0 & 0x3f]; + x2 += p1[x1 & 0x3f]; + x3 += p1[x2 & 0x3f]; + } + } + + d[0] = (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L); + d[1] = (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L); +} + +void RC2_decrypt(unsigned long *d, RC2_KEY *key) { + int i, n; + register RC2_INT *p0, *p1; + register RC2_INT x0, x1, x2, x3; + unsigned long l; + + l = d[0]; + x0 = (RC2_INT)l & 0xffff; + x1 = (RC2_INT)(l >> 16L); + l = d[1]; + x2 = (RC2_INT)l & 0xffff; + x3 = (RC2_INT)(l >> 16L); + + n = 3; + i = 5; + + p0 = &(key->data[63]); + p1 = &(key->data[0]); + for (;;) { + register RC2_INT t = ((x3 << 11) | (x3 >> 5)) & 0xffff; + x3 = (t - (x0 & ~x2) - (x1 & x2) - * (p0--)) & 0xffff; + t = ((x2 << 13) | (x2 >> 3)) & 0xffff; + x2 = (t - (x3 & ~x1) - (x0 & x1) - * (p0--)) & 0xffff; + t = ((x1 << 14) | (x1 >> 2)) & 0xffff; + x1 = (t - (x2 & ~x0) - (x3 & x0) - * (p0--)) & 0xffff; + t = ((x0 << 15) | (x0 >> 1)) & 0xffff; + x0 = (t - (x1 & ~x3) - (x2 & x3) - * (p0--)) & 0xffff; + + if (--i == 0) { + if (--n == 0) + break; + + i = (n == 2) ? 6 : 5; + + x3 = (x3 - p1[x2 & 0x3f]) & 0xffff; + x2 = (x2 - p1[x1 & 0x3f]) & 0xffff; + x1 = (x1 - p1[x0 & 0x3f]) & 0xffff; + x0 = (x0 - p1[x3 & 0x3f]) & 0xffff; + } + } + + d[0] = (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L); + d[1] = (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L); +} + +void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + RC2_KEY *ks, unsigned char *iv, int encrypt) { + register unsigned long tin0, tin1; + register unsigned long tout0, tout1, xor0, xor1; + register long l = length; + unsigned long tin[2]; + + if (encrypt) { + + c2l(iv, tout0); + c2l(iv, tout1); + iv -= 8; + + for (l -= 8; l >= 0; l -= 8) { + c2l(in, tin0); + c2l(in, tin1); + tin0 ^= tout0; + tin1 ^= tout1; + tin[0] = tin0; + tin[1] = tin1; + RC2_encrypt(tin, ks); + tout0 = tin[0]; + l2c(tout0, out); + tout1 = tin[1]; + l2c(tout1, out); + } + + if (l != -8) { + c2ln(in, tin0, tin1, l + 8); + tin0 ^= tout0; + tin1 ^= tout1; + tin[0] = tin0; + tin[1] = tin1; + RC2_encrypt(tin, ks); + tout0 = tin[0]; + l2c(tout0, out); + tout1 = tin[1]; + l2c(tout1, out); + } + + l2c(tout0, iv); + l2c(tout1, iv); + + } else { + + c2l(iv, xor0); + c2l(iv, xor1); + iv -= 8; + + for (l -= 8; l >= 0; l -= 8) { + c2l(in, tin0); + tin[0] = tin0; + c2l(in, tin1); + tin[1] = tin1; + RC2_decrypt(tin, ks); + tout0 = tin[0] ^ xor0; + tout1 = tin[1] ^ xor1; + l2c(tout0, out); + l2c(tout1, out); + xor0 = tin0; + xor1 = tin1; + } + + if (l != -8) { + c2l(in, tin0); + tin[0] = tin0; + c2l(in, tin1); + tin[1] = tin1; + RC2_decrypt(tin, ks); + tout0 = tin[0] ^ xor0; + tout1 = tin[1] ^ xor1; + l2cn(tout0, tout1, out, l + 8); + xor0 = tin0; + xor1 = tin1; + } + + l2c(xor0, iv); + l2c(xor1, iv); + } + tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0; + tin[0] = tin[1] = 0; +} + +static int switch_off_field(void) { + SetISODEPState(ISODEP_INACTIVE); + iso14b_raw_cmd_t packet = { + .flags = ISO14B_DISCONNECT, + .timeout = 0, + .rawlen = 0, + }; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); + return PM3_SUCCESS; +} + +static int findXerox(iso14b_card_select_t *card, bool disconnect) { + + if (card == NULL) + return PM3_EINVARG; + + int8_t retry = 3; + while (retry--) { + + iso14b_raw_cmd_t packet = { + .flags = (ISO14B_CONNECT | ISO14B_SELECT_XRX | (disconnect ? ISO14B_DISCONNECT : 0)), + .timeout = 0, + .rawlen = 0, + }; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + + if (resp.oldarg[0] == 0) { + memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + } + return resp.oldarg[0]; + } + } // retry + +// switch_off_field(); + PrintAndLogEx(FAILED, "command execution timeout"); + return PM3_ESOFT; +} + +static uint8_t info_blocks[] = { 0x15, 0x16, 0x17, 0x18, 0x22 }; +static const char *c_type[] = { "drum", "yellow", "magenta", "cyan", "black" }; + +static inline char dec_digit(uint8_t dig) { + return (dig <= 9) ? dig + '0' : '?'; +} + +static void gen_pn(const uint8_t *data, char *pn) { + pn[0] = dec_digit(data[0] >> 4); + pn[1] = dec_digit(data[0] & 0xF); + pn[2] = dec_digit(data[1] >> 4); + + char sym = ((data[1] & 0xF) << 4) | (data[2] >> 4); + pn[3] = (sym >= 'A' && sym <= 'Z') ? sym : '?'; + + pn[4] = dec_digit(data[2] & 0xF); + pn[5] = dec_digit(data[3] >> 4); + pn[6] = dec_digit(data[3] & 0xF); + pn[7] = dec_digit(data[4] >> 4); + pn[8] = dec_digit(data[4] & 0xF); + pn[9] = '-'; + pn[10] = dec_digit(data[5] >> 4); + pn[11] = dec_digit(data[5] & 0xF); + pn[12] = 0; +} + +int read_xerox_uid(bool loop, bool verbose) { + + do { + iso14b_card_select_t card; + int status = findXerox(&card, true); + + if (loop) { + if (status != PM3_SUCCESS) { + continue; + } + } else { + + if (status == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + } else { + return PM3_ESOFT; + } + } + + } while (loop && kbd_enter_pressed() == false); + + return PM3_SUCCESS; +} + +static int CmdHFXeroxReader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf xerox reader", + "Act as a 14443B reader to identify a tag", + "hf xerox reader\n" + "hf xerox reader -@ \n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + bool cm = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + return read_xerox_uid(cm, verbose); +} + +static int CmdHFXeroxInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf xerox info", + "Tag information for ISO/IEC 14443 type B / XEROX based tags", + "hf xerox info" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + iso14b_card_select_t card; + int status = findXerox(&card, false); + if (status != PM3_SUCCESS) { + switch_off_field(); + if (verbose) { + PrintAndLogEx(FAILED, "Fuji/Xerox tag select failed"); + } + return PM3_ERFTRANS; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); + if (packet == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + int blocknum = 0; + uint8_t data[sizeof(info_blocks) * 4] = {0}; + + // set up the read command + packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); + packet->rawlen = 11; + packet->raw[0] = 0x02; + packet->raw[1] = 0x20; // set command: read mem + memcpy(packet->raw + 2, card.uid, 8); // store uid + + for (int retry = 0; (retry < 5 && blocknum < sizeof(info_blocks)); retry++) { + + packet->raw[10] = info_blocks[blocknum]; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + /* + PrintAndLogEx(INFO, "%X %X %X %X %X %I64X %I64X %I64X %X %X %X %c", + resp.cmd, resp.length, resp.magic, resp.status, resp.crc, resp.oldarg[0], resp.oldarg[1], resp.oldarg[2], + resp.data.asBytes[0], resp.data.asBytes[1], resp.data.asBytes[2], resp.ng ? 't' : 'f'); + */ + + // 14b raw command send data_len instead of status + if (/*resp.status != 0 ||*/ resp.length < 7) { + PrintAndLogEx(FAILED, "retrying one more time"); + continue; + } + + uint8_t *recv = resp.data.asBytes; + + if (check_crc(CRC_14443_B, recv, 7) == false) { + PrintAndLogEx(FAILED, "crc fail, retrying one more time"); + continue; + } + + if (recv[0] != 2) { + PrintAndLogEx(FAILED, "Tag returned Error %x %x", recv[0], recv[1]); + break; + } + + memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + + retry = 0; + blocknum++; + } + } + + switch_off_field(); + free(packet); + + if (blocknum != sizeof(info_blocks)) { + PrintAndLogEx(FAILED, "Fuji/Xerox tag read failed"); + return PM3_ERFTRANS; + } + + char pn[13]; + gen_pn(data, pn); + PrintAndLogEx(SUCCESS, " PartNo : %s", pn); + PrintAndLogEx(SUCCESS, " Date : %02d.%02d.%02d", data[8], data[9], data[10]); + PrintAndLogEx(SUCCESS, " Serial : %d", (data[14] << 16) | (data[13] << 8) | data[12]); + PrintAndLogEx(SUCCESS, " Type : %s", (data[18] <= 4) ? c_type[data[18]] : "Unknown"); + + return PM3_SUCCESS; +} + +static int CmdHFXeroxDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf xerox dump", + "Dump all memory from a Fuji/Xerox tag", + "hf xerox dump\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename to save dump to"), + arg_lit0("d", "decrypt", "decrypt secret blocks"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool decrypt = arg_get_lit(ctx, 2); + + CLIParserFree(ctx); + + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); + if (packet == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + iso14b_card_select_t card; + int status = findXerox(&card, false); // remain RF on + if (status != PM3_SUCCESS) { + free(packet); + switch_off_field(); + return PM3_ERFTRANS; + } + + PrintAndLogEx(INFO, "Reading memory from tag UID " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + + int blocknum = 1; // block 0 all zeros + uint8_t data[256 * 4] = {0}; + + // set up the read command + packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); + packet->rawlen = 11; + packet->raw[0] = 0x02; + memcpy(packet->raw + 2, card.uid, 8); // store uid + + PrintAndLogEx(INFO, "." NOLF); + + for (int retry = 0; (retry < 5 && blocknum < 0x100); retry++) { + + packet->raw[1] = (blocknum < 12) ? 0x30 : 0x20; // set command: read ext mem or read mem + packet->raw[10] = blocknum & 0xFF; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + /* + PrintAndLogEx(INFO, "%X %X %X %X %X %I64X %I64X %I64X %X %X %X %c", + resp.cmd, resp.length, resp.magic, resp.status, resp.crc, resp.oldarg[0], resp.oldarg[1], resp.oldarg[2], + resp.data.asBytes[0], resp.data.asBytes[1], resp.data.asBytes[2], resp.ng ? 't' : 'f'); + */ + if (/*resp.status != 0 ||*/ resp.length < 7) { // 14b raw command send data_len instead of status + PrintAndLogEx(FAILED, "retrying one more time"); + continue; + } + + uint8_t *recv = resp.data.asBytes; + + if (check_crc(CRC_14443_B, recv, 7) == false) { + PrintAndLogEx(FAILED, "crc fail, retrying one more time"); + continue; + } + + if (recv[0] != 2) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Tag returned Error %x %x", recv[0], recv[1]); + break; + } + + memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + + retry = 0; + blocknum++; + + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); +// PrintAndLogEx(INPLACE, "blk %3d", blocknum); + } + } + + switch_off_field(); + + free(packet); + + PrintAndLogEx(NORMAL, ""); + + if (blocknum != 0x100) + PrintAndLogEx(FAILED, "dump failed at block %d", blocknum); + + if (decrypt) { + PrintAndLogEx(INFO, "Decrypting secret blocks..."); + + RC2_KEY exp_key; + uint8_t k1[8], iv[8], k2[8], decr[8]; + + k1[0] = data[8]; + k1[1] = data[5]; + k1[2] = data[6]; + k1[3] = data[7]; + k1[4] = data[0x18 * 4 + 0]; + k1[5] = data[0x18 * 4 + 1]; + k1[6] = data[0x22 * 4 + 0]; + k1[7] = 0; + + RC2_set_key(&exp_key, 8, k1, 64); + + memset(iv, 0, sizeof(iv)); + iv[0] = k1[6]; + iv[1] = k1[7]; + iv[2] = 1; + + RC2_cbc_encrypt(k1, k2, 8, &exp_key, iv, RC2_ENCRYPT); + + memcpy(k1, k2, sizeof(k1)); + + k1[2] = k2[3] ^ data[0x22 * 4 + 0]; + k1[3] = k2[4] ^ data[0x22 * 4 + 1]; // first_key[7]; + k1[5] = k2[1] ^ 0x01; // 01 = crypto method? rfid[23][2] + + RC2_set_key(&exp_key, 8, k1, 64); + + for (int n = 0; n < sizeof(var_list); n++) { + + uint8_t dadr = var_list[n]; + + if (dadr + 1 >= blocknum) { + PrintAndLogEx(INFO, "secret block %02X skipped.", dadr); + continue; + } + + memset(iv, 0, sizeof(iv)); + iv[0] = dadr; + + RC2_cbc_encrypt(&data[dadr * 4], decr, 8, &exp_key, iv, RC2_DECRYPT); + + memcpy(&data[dadr * 4], decr, 8); + + int b; + uint16_t cs, csd; + + // calc checksum + for (b = 0, cs = 0; b < sizeof(decr) - 2; b += 2) cs += decr[b] | (decr[b + 1] << 8); + cs = ~cs; + csd = (decr[7] << 8) | decr[6]; + + if (cs != csd) { + PrintAndLogEx(FAILED, "secret block %02X checksum failed.", dadr); + } + } + } + + PrintAndLogEx(INFO, "block# | data | ascii"); + PrintAndLogEx(INFO, "---------+--------------+----------"); + + for (int i = 0; i < blocknum; i++) { + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s", + i, + i, + sprint_hex(data + (i * 4), 4), + sprint_ascii(data + (i * 4), 4) + ); + } + PrintAndLogEx(INFO, "---------+--------------+----------"); + PrintAndLogEx(NORMAL, ""); + + if (0 == filename[0]) { // generate filename from uid + /* + PrintAndLogEx(INFO, "Using UID as filename"); + + sprintf(filename, "hf-xerox-%02X%02X%02X%02X%02X%02X%02X%02X-dump%s", + card.uid[7],card.uid[6],card.uid[5],card.uid[4],card.uid[3],card.uid[2],card.uid[1],card.uid[0], + decrypt ? "-dec" : ""); + */ + char *fptr = filename; + PrintAndLogEx(INFO, "Using UID as filename"); + fptr += snprintf(fptr, sizeof(filename), "hf-xerox-"); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), decrypt ? "-dump-dec" : "-dump", card.uidlen); + } + + size_t datalen = blocknum * 4; + saveFile(filename, ".bin", data, datalen); + saveFileEML(filename, data, datalen, 4); +// saveFileJSON(filename, jsf15, data, datalen, NULL); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Short info on Fuji/Xerox tag"}, + {"reader", CmdHFXeroxReader, IfPm3Iso14443b, "Act like a Fuji/Xerox reader"}, + {"dump", CmdHFXeroxDump, IfPm3Iso14443b, "Read all memory pages of an Fuji/Xerox tag, save to file"}, +// {"rdbl", CmdHFXeroxRdBl, IfPm3Iso14443b, "Read Fuji/Xerox block"}, +// {"wrbl", CmdHFXeroxWrBl, IfPm3Iso14443b, "Write Fuji/Xerox block"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFXerox(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhfxerox.h b/client/src/cmdhfxerox.h new file mode 100644 index 000000000..6e3d55635 --- /dev/null +++ b/client/src/cmdhfxerox.h @@ -0,0 +1,13 @@ +//----------------------------------------------------------------------------- +// High frequency Xerox commands (ISO14443B) +//----------------------------------------------------------------------------- + +#ifndef CMDHFXEROX_H__ +#define CMDHFXEROX_H__ + +#include "common.h" + +int CmdHFXerox(const char *Cmd); +int read_xerox_uid(bool loop, bool verbose); + +#endif diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 9831135f5..3ddd98189 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -133,7 +133,7 @@ static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) { break; } - PrintAndLogEx(NORMAL, " Memory.... " _YELLOW_("%u") " Kb ( " _YELLOW_("%2.0f%%") " used )" + PrintAndLogEx(NORMAL, " Memory.... " _YELLOW_("%u") " KB ( " _YELLOW_("%2.0f%%") " used )" , mem_avail , mem_avail == 0 ? 0.0f : (float)mem_used / (mem_avail * 1024) * 100 ); @@ -592,12 +592,12 @@ static int CmdLCD(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); int r_len = 0; uint8_t raw[1] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &r_len); - int j = arg_get_int(ctx, 2); + int j = arg_get_int_def(ctx, 2, 1); + CLIParserFree(ctx); if (j < 1) { PrintAndLogEx(WARNING, "Count must be larger than zero"); return PM3_EINVARG; @@ -763,7 +763,7 @@ static int CmdStandalone(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t arg = arg_get_u32(ctx, 1); + uint8_t arg = arg_get_u32_def(ctx, 1, 1); CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_STANDALONE, (uint8_t *)&arg, sizeof(arg)); @@ -938,7 +938,7 @@ static int CmdPing(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint32_t len = arg_get_u32(ctx, 1); + uint32_t len = arg_get_u32_def(ctx, 1, 32); CLIParserFree(ctx); if (len > PM3_CMD_DATA_SIZE) @@ -1298,6 +1298,10 @@ void pm3_version(bool verbose, bool oneliner) { PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); } else { PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("PM3 GENERIC")); + if (IfPm3Flash()) { + PrintAndLogEx(NORMAL, " external flash............ %s", _GREEN_("present")); + } + if (IfPm3FpcUsartHost()) { PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", _GREEN_("present")); } diff --git a/client/src/cmdlfawid.c b/client/src/cmdlfawid.c index 06b9c520c..5d735a650 100644 --- a/client/src/cmdlfawid.c +++ b/client/src/cmdlfawid.c @@ -274,7 +274,8 @@ int demodAWID(bool verbose) { } free(bits); - PrintAndLogEx(DEBUG, "DEBUG: AWID idx: %d, Len: %zu Printing DemodBuffer:", idx, size); + PrintAndLogEx(DEBUG, "DEBUG: AWID idx: %d, Len: %zu", idx, size); + PrintAndLogEx(DEBUG, "DEBUG: Printing DemodBuffer:"); if (g_debugMode) { printDemodBuff(0, false, false, true); printDemodBuff(0, false, false, false); @@ -487,7 +488,7 @@ static int CmdAWIDBrute(const char *Cmd) { arg_u64_1(NULL, "fc", "", "8|16bit value facility code"), arg_u64_0(NULL, "cn", "", "optional - card number to start with, max 65535"), arg_u64_0(NULL, "delay", "", "optional - delay betweens attempts in ms. Default 1000ms"), - arg_lit0("v", "verbose", "verbose logging, show all tries"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index ef9caf959..29ad61bc9 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -229,6 +229,8 @@ void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type) { uint8_t sebury2 = (id >> 16) & 0x7F; uint32_t sebury3 = id & 0x7FFFFF; PrintAndLogEx(SUCCESS, "Pattern Sebury : %d %d %d [0x%X 0x%X 0x%X]", sebury1, sebury2, sebury3, sebury1, sebury2, sebury3); + PrintAndLogEx(SUCCESS, "VD / ID : %03" PRIu64 " / %010" PRIu64, (id >> 32LL) & 0xFFFF, (id & 0xFFFFFFFF)); + PrintAndLogEx(INFO, "------------------------------------------------"); } } @@ -672,11 +674,6 @@ static int CmdEM410xClone(const char *Cmd) { return PM3_EINVARG; } - char cardtype[16] = {"T55x7"}; - if (q5) { - snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); - } - PrintAndLogEx(SUCCESS, "Preparing to clone EM4102 to " _YELLOW_("%s") " tag with EM Tag ID " _GREEN_("%010" PRIX64) " (RF/%d)", q5 ? "Q5/T5555" : (em ? "EM4305/4469" : "T55x7"), id, clk); struct { diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 6eb2bb837..c21540f45 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -41,6 +41,7 @@ #include "generator.h" #include "cliparser.h" #include "cmdhw.h" +#include "util.h" //////////////// 4205 / 4305 commands @@ -1264,7 +1265,8 @@ int CmdEM4x05Info(const char *Cmd) { // read word 1 (serial #) doesn't need pwd // continue if failed, .. non blocking fail. - em4x05_read_word_ext(EM_SERIAL_BLOCK, 0, false, &serial); + int res = em4x05_read_word_ext(EM_SERIAL_BLOCK, 0, false, &serial); + (void)res; printEM4x05info(block0, serial); @@ -1996,8 +1998,7 @@ int CmdEM4x05Sniff(const char *Cmd) { const char *cmdText; char dataText[100]; char blkAddr[4]; - char bits[80]; - int i, bitidx; + int i; int ZeroWidth; // 32-42 "1" is 32 int CycleWidth; size_t pulseSamples; @@ -2018,13 +2019,15 @@ int CmdEM4x05Sniff(const char *Cmd) { PrintAndLogEx(SUCCESS, "offset | Command | Data | blk | raw"); PrintAndLogEx(SUCCESS, "-------+-------------+----------+-----+------------------------------------------------------------"); + smartbuf bits = { 0 }; + bits.ptr = malloc(EM4X05_BITS_BUFSIZE); + bits.size = EM4X05_BITS_BUFSIZE; + bits.idx = 0; size_t idx = 0; // loop though sample buffer while (idx < g_GraphTraceLen) { - bool eop = false; bool haveData = false; bool pwd = false; - uint32_t tmpValue; idx = em4x05_Sniff_GetNextBitStart(idx, g_GraphTraceLen, g_GraphBuffer, &pulseSamples); size_t pktOffset = idx; @@ -2037,9 +2040,10 @@ int CmdEM4x05Sniff(const char *Cmd) { if (ZeroWidth <= 50) { pktOffset -= ZeroWidth; - memset(bits, 0x00, sizeof(bits)); - bitidx = 0; + memset(bits.ptr, 0, bits.size); + bits.idx = 0; + bool eop = false; while ((idx < g_GraphTraceLen) && !eop) { CycleWidth = idx; idx = em4x05_Sniff_GetNextBitStart(idx, g_GraphTraceLen, g_GraphBuffer, &pulseSamples); @@ -2047,7 +2051,7 @@ int CmdEM4x05Sniff(const char *Cmd) { CycleWidth = idx - CycleWidth; if ((CycleWidth > 300) || (CycleWidth < (ZeroWidth - 5))) { // to long or too short eop = true; - bits[bitidx++] = '0'; // Append last zero from the last bit find + sb_append_char(&bits, '0'); // Append last zero from the last bit find cmdText = ""; // EM4305 command lengths @@ -2059,76 +2063,77 @@ int CmdEM4x05Sniff(const char *Cmd) { // -> disable 1010 11111111 0 11111111 0 11111111 0 11111111 0 00000000 0 // Check to see if we got the leading 0 - if (((strncmp(bits, "00011", 5) == 0) && (bitidx == 50)) || - ((strncmp(bits, "00101", 5) == 0) && (bitidx == 57)) || - ((strncmp(bits, "01001", 5) == 0) && (bitidx == 12)) || - ((strncmp(bits, "01100", 5) == 0) && (bitidx == 50)) || - ((strncmp(bits, "01010", 5) == 0) && (bitidx == 50))) { - memmove(bits, &bits[1], bitidx - 1); - bitidx--; + if (((strncmp(bits.ptr, "00011", 5) == 0) && (bits.idx == 50)) || + ((strncmp(bits.ptr, "00101", 5) == 0) && (bits.idx == 57)) || + ((strncmp(bits.ptr, "01001", 5) == 0) && (bits.idx == 12)) || + ((strncmp(bits.ptr, "01100", 5) == 0) && (bits.idx == 50)) || + ((strncmp(bits.ptr, "01010", 5) == 0) && (bits.idx == 50))) { + memmove(bits.ptr, &bits.ptr[1], bits.idx - 1); + bits.idx--; PrintAndLogEx(INFO, "Trim leading 0"); } - bits[bitidx] = 0; + sb_append_char(&bits, 0); + bits.idx--; // logon - if ((strncmp(bits, "0011", 4) == 0) && (bitidx == 49)) { + if ((strncmp(bits.ptr, "0011", 4) == 0) && (bits.idx == 49)) { haveData = true; pwd = true; cmdText = "Logon"; strncpy(blkAddr, " ", sizeof(blkAddr)); - tmpValue = em4x05_Sniff_GetBlock(&bits[4], fwd); + uint32_t tmpValue = em4x05_Sniff_GetBlock(&bits.ptr[4], fwd); snprintf(dataText, sizeof(dataText), "%08X", tmpValue); } // write - if ((strncmp(bits, "0101", 4) == 0) && (bitidx == 56)) { + if ((strncmp(bits.ptr, "0101", 4) == 0) && (bits.idx == 56)) { haveData = true; cmdText = "Write"; - tmpValue = (bits[4] - '0') + ((bits[5] - '0') << 1) + ((bits[6] - '0') << 2) + ((bits[7] - '0') << 3); + uint32_t tmpValue = (bits.ptr[4] - '0') + ((bits.ptr[5] - '0') << 1) + ((bits.ptr[6] - '0') << 2) + ((bits.ptr[7] - '0') << 3); snprintf(blkAddr, sizeof(blkAddr), "%u", tmpValue); if (tmpValue == 2) { pwd = true; } - tmpValue = em4x05_Sniff_GetBlock(&bits[11], fwd); + tmpValue = em4x05_Sniff_GetBlock(&bits.ptr[11], fwd); snprintf(dataText, sizeof(dataText), "%08X", tmpValue); } // read - if ((strncmp(bits, "1001", 4) == 0) && (bitidx == 11)) { + if ((strncmp(bits.ptr, "1001", 4) == 0) && (bits.idx == 11)) { haveData = true; pwd = false; cmdText = "Read"; - tmpValue = (bits[4] - '0') + ((bits[5] - '0') << 1) + ((bits[6] - '0') << 2) + ((bits[7] - '0') << 3); + uint32_t tmpValue = (bits.ptr[4] - '0') + ((bits.ptr[5] - '0') << 1) + ((bits.ptr[6] - '0') << 2) + ((bits.ptr[7] - '0') << 3); snprintf(blkAddr, sizeof(blkAddr), "%u", tmpValue); strncpy(dataText, " ", sizeof(dataText)); } // protect - if ((strncmp(bits, "1100", 4) == 0) && (bitidx == 49)) { + if ((strncmp(bits.ptr, "1100", 4) == 0) && (bits.idx == 49)) { haveData = true; pwd = false; cmdText = "Protect"; strncpy(blkAddr, " ", sizeof(blkAddr)); - tmpValue = em4x05_Sniff_GetBlock(&bits[11], fwd); + uint32_t tmpValue = em4x05_Sniff_GetBlock(&bits.ptr[11], fwd); snprintf(dataText, sizeof(dataText), "%08X", tmpValue); } // disable - if ((strncmp(bits, "1010", 4) == 0) && (bitidx == 49)) { + if ((strncmp(bits.ptr, "1010", 4) == 0) && (bits.idx == 49)) { haveData = true; pwd = false; cmdText = "Disable"; strncpy(blkAddr, " ", sizeof(blkAddr)); - tmpValue = em4x05_Sniff_GetBlock(&bits[11], fwd); + uint32_t tmpValue = em4x05_Sniff_GetBlock(&bits.ptr[11], fwd); snprintf(dataText, sizeof(dataText), "%08X", tmpValue); } // bits[bitidx] = 0; } else { i = (CycleWidth - ZeroWidth) / 28; - bits[bitidx++] = '0'; + sb_append_char(&bits, '0'); for (int ii = 0; ii < i; ii++) { - bits[bitidx++] = '1'; + sb_append_char(&bits, '1'); } } } @@ -2139,11 +2144,13 @@ int CmdEM4x05Sniff(const char *Cmd) { // Print results if (haveData) { //&& (minWidth > 1) && (maxWidth > minWidth)){ if (pwd) - PrintAndLogEx(SUCCESS, "%6zu | %-10s | " _YELLOW_("%8s")" | " _YELLOW_("%3s")" | %s", pktOffset, cmdText, dataText, blkAddr, bits); + PrintAndLogEx(SUCCESS, "%6zu | %-10s | " _YELLOW_("%8s")" | " _YELLOW_("%3s")" | %s", pktOffset, cmdText, dataText, blkAddr, bits.ptr); else - PrintAndLogEx(SUCCESS, "%6zu | %-10s | " _GREEN_("%8s")" | " _GREEN_("%3s")" | %s", pktOffset, cmdText, dataText, blkAddr, bits); + PrintAndLogEx(SUCCESS, "%6zu | %-10s | " _GREEN_("%8s")" | " _GREEN_("%3s")" | %s", pktOffset, cmdText, dataText, blkAddr, bits.ptr); } } + free(bits.ptr); + bits.ptr = NULL; // footer PrintAndLogEx(SUCCESS, "---------------------------------------------------------------------------------------------------"); diff --git a/client/src/cmdlfem4x05.h b/client/src/cmdlfem4x05.h index 19c3dd4f2..ed472a803 100644 --- a/client/src/cmdlfem4x05.h +++ b/client/src/cmdlfem4x05.h @@ -27,6 +27,8 @@ #define EM4305_PROT2_BLOCK 15 #define EM4469_PROT_BLOCK 3 +#define EM4X05_BITS_BUFSIZE 128 + // config blocks #define EM4305_DEFAULT_CONFIG_BLOCK (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_MANCHESTER | EM4x05_SET_NUM_BLOCKS(4) ) // ASK/MAN , data rate 32, 4 data blocks //#define EM4305_DEFAULT_CONFIG_BLOCK (EM4x05_SET_BITRATE(32) | EM4x05_MODULATION_BIPHASE | EM4x05_SET_NUM_BLOCKS(4) ) // ASK/BIPHASE , data rate 32, 4 data blocks diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 8f6db24a3..9b5bac06b 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -555,7 +555,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { return PM3_ESOFT; uint8_t *data = resp.data.asBytes; - em4x50_word_t words[EM4X50_NO_WORDS]; + em4x50_word_t words[EM4X50_NO_WORDS] = {0}; prepare_result(data, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); if (out != NULL) @@ -628,7 +628,7 @@ int CmdEM4x50Info(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), - arg_lit0("v", "verbose", "additional output of data section"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -939,7 +939,7 @@ int CmdEM4x50WritePwd(const char *Cmd) { return PM3_SUCCESS; if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Writing password (" _RED_("failed") ")"); + PrintAndLogEx(FAILED, "Writing password ( " _RED_("fail") " )"); return PM3_EFAILED; } diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index e0087e230..d7bc3811a 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -165,7 +165,7 @@ int CmdEM4x70Write(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 write", "Write EM4x70\n", - "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n" + "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n" "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\n" ); @@ -181,7 +181,7 @@ int CmdEM4x70Write(const char *Cmd) { etd.parity = arg_get_lit(ctx, 1); - int addr = arg_get_int(ctx, 2); + int addr = arg_get_int_def(ctx, 2, 1); int word_len = 0; uint8_t word[2] = {0x0}; @@ -190,17 +190,17 @@ int CmdEM4x70Write(const char *Cmd) { CLIParserFree(ctx); if (addr < 0 || addr >= EM4X70_NUM_BLOCKS) { - PrintAndLogEx(FAILED, "block has to be within range [0, 15]"); + PrintAndLogEx(FAILED, "block has to be within range [0, 15] got: %d", addr); return PM3_EINVARG; } if (word_len != 2) { - PrintAndLogEx(FAILED, "word/data length must be 2 bytes instead of %d", word_len); + PrintAndLogEx(FAILED, "word/data length must be 2 bytes. got: %d", word_len); return PM3_EINVARG; } etd.address = (uint8_t) addr; - etd.word = BYTES2UINT16(word);; + etd.word = BYTES2UINT16(word); clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -220,6 +220,105 @@ int CmdEM4x70Write(const char *Cmd) { return PM3_ESOFT; } +int CmdEM4x70Brute(const char *Cmd) { + + // From paper "Dismantling Megamos Crypto", Roel Verdult, Flavio D. Garcia and Barıs¸ Ege. + // Partial Key-Update Attack (optimized version) + em4x70_data_t etd = {0}; + + CLIParserContext *ctx; + + CLIParserInit(&ctx, "lf em 4x70 brute", + "Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70\n" + "This attack does NOT write anything to the tag.\n" + "Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'.\n" + "After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'\n", + "lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 --> bruteforcing key bits k95...k80\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "par", "Add parity bit when sending commands"), + arg_int1("b", "block", "", "block/word address, dec"), + arg_str1(NULL, "rnd", "", "Random 56-bit"), + arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), + arg_str0("s", "start", "", "Start bruteforce enumeration from this key value"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + etd.parity = arg_get_lit(ctx, 1); + + int addr = arg_get_int_def(ctx, 2, 0); + if (addr < 7 || addr > 9) { + PrintAndLogEx(FAILED, "block has to be within range [7, 9] got: %d", addr); + CLIParserFree(ctx); + return PM3_EINVARG; + } + etd.address = (uint8_t) addr; + + int rnd_len = 7; + CLIGetHexWithReturn(ctx, 3, etd.rnd, &rnd_len); + + int frnd_len = 4; + CLIGetHexWithReturn(ctx, 4, etd.frnd, &frnd_len); + + uint32_t start_key = 0; + int res = arg_get_u32_hexstr_def_nlen(ctx, 5, 0, &start_key, 2, true); + if (res == 2) { + PrintAndLogEx(WARNING, "start key parameter must be in range [0, FFFF]"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + etd.start_key = start_key; + + CLIParserFree(ctx); + + if (rnd_len != 7) { + PrintAndLogEx(FAILED, "Random number length must be 7 bytes instead of %d", rnd_len); + return PM3_EINVARG; + } + + if (frnd_len != 4) { + PrintAndLogEx(FAILED, "F(RN) length must be 4 bytes instead of %d", frnd_len); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit"); + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandNG(CMD_LF_EM4X70_BRUTE, (uint8_t *)&etd, sizeof(etd)); + + uint32_t timeout = 0; + for (;;) { + + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + break; + } + + if (WaitForResponseTimeout(CMD_LF_EM4X70_BRUTE, &resp, TIMEOUT)) { + if (resp.status) { + // Response is 16-bit partial key + PrintAndLogEx(INFO, "Partial Key Response: %02X %02X", resp.data.asBytes[0], resp.data.asBytes[1]); + return PM3_SUCCESS; + } + break; + } + + // should be done in about 60 minutes. + if (timeout > ((60 * 60000) / TIMEOUT)) { + PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); + break; + } + timeout++; + } + + PrintAndLogEx(FAILED, "Bruteforce of partial key " _RED_("failed")); + return PM3_ESOFT; +} + int CmdEM4x70Unlock(const char *Cmd) { // send pin code to device, unlocking it for writing @@ -452,6 +551,7 @@ int CmdEM4x70WriteKey(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"brute", CmdEM4x70Brute, IfPm3EM4x70, "Bruteforce EM4X70 to find partial Crypt Key"}, {"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"}, {"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"}, {"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"}, diff --git a/client/src/cmdlfem4x70.h b/client/src/cmdlfem4x70.h index 5d46af3ca..12599713b 100644 --- a/client/src/cmdlfem4x70.h +++ b/client/src/cmdlfem4x70.h @@ -26,6 +26,7 @@ int CmdLFEM4X70(const char *Cmd); int CmdEM4x70Info(const char *Cmd); int CmdEM4x70Write(const char *Cmd); +int CmdEM4x70Brute(const char *Cmd); int CmdEM4x70Unlock(const char *Cmd); int CmdEM4x70Auth(const char *Cmd); int CmdEM4x70WritePIN(const char *Cmd); diff --git a/client/src/cmdlfguard.c b/client/src/cmdlfguard.c index 02bd52eb8..4ba98d903 100644 --- a/client/src/cmdlfguard.c +++ b/client/src/cmdlfguard.c @@ -86,9 +86,9 @@ static int demod_guard_raw(uint8_t *raw, uint8_t rlen) { } if (unknown) - PrintAndLogEx(SUCCESS, "G-Prox-II - Unknown len: " _GREEN_("%u") ", Raw: %s", fmtlen, sprint_hex_inrow(raw, rlen)); + PrintAndLogEx(SUCCESS, "G-Prox-II - Unknown len: " _GREEN_("%u") "xor: " _GREEN_("%u")", Raw: %s", fmtlen, xorKey, sprint_hex_inrow(raw, rlen)); else - PrintAndLogEx(SUCCESS, "G-Prox-II - len: " _GREEN_("%u")" FC: " _GREEN_("%u") " Card: " _GREEN_("%u") ", Raw: %s", fmtlen, FC, Card, sprint_hex_inrow(raw, rlen)); + PrintAndLogEx(SUCCESS, "G-Prox-II - Len: " _GREEN_("%u")" FC: " _GREEN_("%u") " Card: " _GREEN_("%u") "xor: " _GREEN_("%u")", Raw: %s", fmtlen, FC, Card, xorKey, sprint_hex_inrow(raw, rlen)); return PM3_SUCCESS; } @@ -142,9 +142,11 @@ int demodGuard(bool verbose) { // get key and then get all 8 bytes of payload decoded xorKey = (uint8_t)bytebits_to_byteLSBF(bits_no_spacer, 8); + PrintAndLogEx(DEBUG, "DEBUG: gProxII xorKey: %u", xorKey); + for (size_t idx = 0; idx < 8; idx++) { plain[idx] = ((uint8_t)bytebits_to_byteLSBF(bits_no_spacer + 8 + (idx * 8), 8)) ^ xorKey; - PrintAndLogEx(DEBUG, "DEBUG: gProxII byte %zu after xor: %02x", idx, plain[idx]); + PrintAndLogEx(DEBUG, "DEBUG: gProxII byte %zu after xor: %02x (%02x before xor)", idx, plain[idx], bytebits_to_byteLSBF(bits_no_spacer + 8 + (idx * 8), 8)); } setDemodBuff(g_DemodBuffer, 96, preambleIndex); @@ -161,6 +163,12 @@ int demodGuard(bool verbose) { bool unknown = false; switch (fmtLen) { case 36: + PrintAndLogEx(DEBUG, "DEBUG: FC 1: %x", (plain[3] & 0x7F) << 7); + PrintAndLogEx(DEBUG, "DEBUG: FC 2: %x", plain[4] >> 1); + PrintAndLogEx(DEBUG, "DEBUG: Card 1: %x", (plain[4] & 1) << 19); + PrintAndLogEx(DEBUG, "DEBUG: Card 2: %x", plain[5] << 11); + PrintAndLogEx(DEBUG, "DEBUG: Card 3: %x", plain[6] << 3); + PrintAndLogEx(DEBUG, "DEBUG: Card 4: %x", (plain[7] & 0xE0) >> 5); FC = ((plain[3] & 0x7F) << 7) | (plain[4] >> 1); Card = ((plain[4] & 1) << 19) | (plain[5] << 11) | (plain[6] << 3) | ((plain[7] & 0xE0) >> 5); break; @@ -172,10 +180,10 @@ int demodGuard(bool verbose) { unknown = true; break; } - if (!unknown) - PrintAndLogEx(SUCCESS, "G-Prox-II - len: " _GREEN_("%u")" FC: " _GREEN_("%u") " Card: " _GREEN_("%u") ", Raw: %08x%08x%08x", fmtLen, FC, Card, raw1, raw2, raw3); + if (unknown) + PrintAndLogEx(SUCCESS, "G-Prox-II - Unknown len: " _GREEN_("%u") " xor: " _GREEN_("%u")", Raw: %08x%08x%08x ", fmtLen, xorKey, raw1, raw2, raw3); else - PrintAndLogEx(SUCCESS, "G-Prox-II - Unknown len: " _GREEN_("%u") ", Raw: %08x%08x%08x", fmtLen, raw1, raw2, raw3); + PrintAndLogEx(SUCCESS, "G-Prox-II - Len: " _GREEN_("%u")" FC: " _GREEN_("%u") " Card: " _GREEN_("%u") " xor: " _GREEN_("%u") ", Raw: %08x%08x%08x", fmtLen, FC, Card, xorKey, raw1, raw2, raw3); return PM3_SUCCESS; } @@ -243,16 +251,17 @@ static int CmdGuardReader(const char *Cmd) { static int CmdGuardClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf gproxii clone", - "clone a Guardall tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" + "Clone a Guardall tag to a T55x7, Q5/T5555 or EM4305/4469 tag.\n" "The facility-code is 8-bit and the card number is 20-bit. Larger values are truncated.\n" "Currently work only on 26 | 36 bit format", - "lf gproxii clone --fmt 26 --fc 123 --cn 1337 -> encode for T55x7 tag\n" - "lf gproxii clone --fmt 26 --fc 123 --cn 1337 --q5 -> encode for Q5/T5555 tag\n" - "lf gproxii clone --fmt 26 --fc 123 --cn 1337 --em -> encode for EM4305/4469" + "lf gproxii clone --xor 141 --fmt 26 --fc 123 --cn 1337 -> encode for T55x7 tag\n" + "lf gproxii clone --xor 141 --fmt 26 --fc 123 --cn 1337 --q5 -> encode for Q5/T5555 tag\n" + "lf gproxii clone --xor 141 --fmt 26 --fc 123 --cn 1337 --em -> encode for EM4305/4469" ); void *argtable[] = { arg_param_begin, + arg_u64_1(NULL, "xor", "", "8-bit xor value (installation dependant)"), arg_u64_1(NULL, "fmt", "", "format length 26|32|36|40"), arg_u64_1(NULL, "fc", "", "8-bit value facility code"), arg_u64_1(NULL, "cn", "", "16-bit value card number"), @@ -262,11 +271,13 @@ static int CmdGuardClone(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint32_t fmtlen = arg_get_u32_def(ctx, 1, 0); - uint32_t fc = arg_get_u32_def(ctx, 2, 0); - uint32_t cn = arg_get_u32_def(ctx, 3, 0); - bool q5 = arg_get_lit(ctx, 4); - bool em = arg_get_lit(ctx, 5); + uint32_t xorval = arg_get_u32_def(ctx, 1, 0); + uint32_t fmtlen = arg_get_u32_def(ctx, 2, 0); + uint32_t fc = arg_get_u32_def(ctx, 3, 0); + uint32_t cn = arg_get_u32_def(ctx, 4, 0); + + bool q5 = arg_get_lit(ctx, 5); + bool em = arg_get_lit(ctx, 6); CLIParserFree(ctx); if (q5 && em) { @@ -280,7 +291,7 @@ static int CmdGuardClone(const char *Cmd) { //GuardProxII - compat mode, ASK/Biphase, data rate 64, 3 data blocks uint8_t *bs = calloc(96, sizeof(uint8_t)); - if (getGuardBits(fmtlen, facilitycode, cardnumber, bs) != PM3_SUCCESS) { + if (getGuardBits(xorval, fmtlen, facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); free(bs); return PM3_ESOFT; @@ -306,10 +317,11 @@ static int CmdGuardClone(const char *Cmd) { free(bs); - PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u") + PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u") " xorKey: " _GREEN_("%u") , cardtype , facilitycode , cardnumber + , xorval ); print_blocks(blocks, ARRAYLEN(blocks)); @@ -332,11 +344,12 @@ static int CmdGuardSim(const char *Cmd) { "Simulation runs until the button is pressed or another USB command is issued.\n" "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated.\n" "Currently work only on 26 | 36 bit format", - "lf gproxii sim --fmt 26 --fc 123 --cn 1337\n" + "lf gproxii sim --xor 141 --fmt 26 --fc 123 --cn 1337\n" ); void *argtable[] = { arg_param_begin, + arg_u64_1(NULL, "xor", "", "8-bit xor value (installation dependant)"), arg_u64_1(NULL, "fmt", "", "format length 26|32|36|40"), arg_u64_1(NULL, "fc", "", "8-bit value facility code"), arg_u64_1(NULL, "cn", "", "16-bit value card number"), @@ -344,9 +357,10 @@ static int CmdGuardSim(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint32_t fmtlen = arg_get_u32_def(ctx, 1, 0); - uint32_t fc = arg_get_u32_def(ctx, 2, 0); - uint32_t cn = arg_get_u32_def(ctx, 3, 0); + uint32_t xorval = arg_get_u32_def(ctx, 1, 0); + uint32_t fmtlen = arg_get_u32_def(ctx, 2, 0); + uint32_t fc = arg_get_u32_def(ctx, 3, 0); + uint32_t cn = arg_get_u32_def(ctx, 4, 0); CLIParserFree(ctx); fmtlen &= 0x7F; @@ -356,12 +370,13 @@ static int CmdGuardSim(const char *Cmd) { uint8_t bs[96]; memset(bs, 0x00, sizeof(bs)); - if (getGuardBits(fmtlen, facilitycode, cardnumber, bs) != PM3_SUCCESS) { + if (getGuardBits(xorval, fmtlen, facilitycode, cardnumber, bs) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Error with tag bitstream generation."); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u") + PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u") + , xorval , facilitycode , cardnumber ); @@ -435,9 +450,8 @@ int detectGProxII(uint8_t *bits, size_t *size) { } // Works for 26bits. -int getGuardBits(uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8_t *guardBits) { +int getGuardBits(uint8_t xorKey, uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8_t *guardBits) { - uint8_t xorKey = 0x66; uint8_t i; uint8_t pre[96]; uint8_t rawbytes[12]; @@ -448,7 +462,6 @@ int getGuardBits(uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8_t *guardBits) { switch (fmtlen) { case 32: { rawbytes[1] = (32 << 2); - break; } case 36: { @@ -456,6 +469,7 @@ int getGuardBits(uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8_t *guardBits) { // Get wiegand from FacilityCode 14bits, CardNumber 20bits uint8_t wiegand[36]; memset(wiegand, 0x00, sizeof(wiegand)); + num_to_bytebits(fc, 14, wiegand); num_to_bytebits(cn, 20, wiegand + 14); diff --git a/client/src/cmdlfguard.h b/client/src/cmdlfguard.h index c68c0d477..cfdf739a4 100644 --- a/client/src/cmdlfguard.h +++ b/client/src/cmdlfguard.h @@ -23,5 +23,5 @@ int CmdLFGuard(const char *Cmd); int detectGProxII(uint8_t *bits, size_t *size); int demodGuard(bool verbose); -int getGuardBits(uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8_t *guardBits); +int getGuardBits(uint8_t xorKey, uint8_t fmtlen, uint32_t fc, uint32_t cn, uint8_t *guardBits); #endif diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c old mode 100644 new mode 100755 index d25c06811..d197a6dcb --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -80,11 +80,14 @@ static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, boo ); } - lf_hidsim_t payload; - payload.hi2 = packed.Top; - payload.hi = packed.Mid; - payload.lo = packed.Bot; - payload.longFMT = (packed.Mid > 0xFFF); + lf_hidsim_t payload = { + .EM = false, + .Q5 = false, + .hi2 = packed.Top, + .hi = packed.Mid, + .lo = packed.Bot, + .longFMT = (packed.Mid > 0xFFF) + }; clearCommandBuffer(); @@ -472,26 +475,27 @@ static int CmdHIDClone(const char *Cmd) { static int CmdHIDBrute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hid brute", - "Enables bruteforce of HID readers with specified facility code.\n" - "This is a attack against reader. if cardnumber is given, it starts with it and goes up / down one step\n" - "if cardnumber is not given, it starts with 1 and goes up to 65535", - "lf hid brute -w H10301 --fc 224\n" - "lf hid brute -w H10301 --fc 21 -d 2000\n" - "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000\n" - "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000 --up\n" + "Enables bruteforce of HID readers with specified facility code or card number. This is an attack against the reader.\n" + "If the field being bruteforced is provided, it starts with it and goes up / down one step while maintaining other supplied values.\n" + "If the field being bruteforced is not provided, it will iterate through the full range while maintaining other supplied values.", + "lf hid brute -w H10301 --field fc --fc 224 --cn 6278\n" + "lf hid brute -w H10301 --field cn --fc 21 -d 2000\n" + "lf hid brute -v -w H10301 --field cn --fc 21 --cn 200 -d 2000\n" + "lf hid brute -v -w H10301 --field fc --fc 21 --cn 200 -d 2000 --up\n" ); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "verbose logging, show all tries"), + arg_lit0("v", "verbose", "verbose output"), arg_str1("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), - arg_u64_0(NULL, "fc", "", "facility code"), - arg_u64_0(NULL, "cn", "", "card number to start with"), - arg_u64_0("i", "issue", "", "issue level"), - arg_u64_0("o", "oem", "", "OEM code"), - arg_u64_0("d", "delay", "", "delay betweens attempts in ms. Default 1000ms"), - arg_lit0(NULL, "up", "direction to increment card number. (default is both directions)"), - arg_lit0(NULL, "down", "direction to decrement card number. (default is both directions)"), + arg_str1(NULL, "field", "", "field to bruteforce"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number"), + arg_u64_0("i", "issue", "", "issue level"), + arg_u64_0("o", "oem", "", "OEM code"), + arg_u64_0("d", "delay", "", "delay betweens attempts in ms. (def is 1000)"), + arg_lit0(NULL, "up", "direction to increment field value. (def is both directions)"), + arg_lit0(NULL, "down", "direction to decrement field value. (def is both directions)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -509,60 +513,71 @@ static int CmdHIDBrute(const char *Cmd) { return PM3_EINVARG; } - wiegand_card_t cn_hi, cn_low; - memset(&cn_hi, 0, sizeof(wiegand_card_t)); + wiegand_card_t card_hi, card_low; + memset(&card_hi, 0, sizeof(wiegand_card_t)); - cn_hi.FacilityCode = arg_get_u32_def(ctx, 3, 0); - cn_hi.CardNumber = arg_get_u32_def(ctx, 4, 0); - cn_hi.IssueLevel = arg_get_u32_def(ctx, 5, 0); - cn_hi.OEM = arg_get_u32_def(ctx, 6, 0); + char field[3] = {0}; + int field_len = 0; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)field, sizeof(field), &field_len); - uint32_t delay = arg_get_u32_def(ctx, 7, 1000); + card_hi.FacilityCode = arg_get_u32_def(ctx, 4, 0); + card_hi.CardNumber = arg_get_u32_def(ctx, 5, 0); + card_hi.IssueLevel = arg_get_u32_def(ctx, 6, 0); + card_hi.OEM = arg_get_u32_def(ctx, 7, 0); + + uint32_t delay = arg_get_u32_def(ctx, 8, 1000); int direction = 0; - if (arg_get_lit(ctx, 8) && arg_get_lit(ctx, 9)) { + if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) { direction = 0; - } else if (arg_get_lit(ctx, 8)) { - direction = 1; } else if (arg_get_lit(ctx, 9)) { + direction = 1; + } else if (arg_get_lit(ctx, 10)) { direction = 2; } CLIParserFree(ctx); if (verbose) { - PrintAndLogEx(INFO, "Wiegand format#.. %i", format_idx); - PrintAndLogEx(INFO, "OEM#............. %u", cn_hi.OEM); - PrintAndLogEx(INFO, "ISSUE#........... %u", cn_hi.IssueLevel); - PrintAndLogEx(INFO, "Facility#........ %u", cn_hi.FacilityCode); - PrintAndLogEx(INFO, "Card#............ %" PRIu64, cn_hi.CardNumber); + PrintAndLogEx(INFO, "Wiegand format... %i", format_idx); + PrintAndLogEx(INFO, "OEM.............. %u", card_hi.OEM); + PrintAndLogEx(INFO, "ISSUE............ %u", card_hi.IssueLevel); + PrintAndLogEx(INFO, "Facility code.... %u", card_hi.FacilityCode); + PrintAndLogEx(INFO, "Card number...... %" PRIu64, card_hi.CardNumber); + PrintAndLogEx(INFO, "Delay............ " _YELLOW_("%d"), delay); + if (strcmp(field, "fc") == 0) { + PrintAndLogEx(INFO, "Field............ " _YELLOW_("fc")); + } else if (strcmp(field, "cn") == 0) { + PrintAndLogEx(INFO, "Field............ " _YELLOW_("cn")); + } switch (direction) { case 0: - PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("BOTH") " delay " _YELLOW_("%d"), delay); + PrintAndLogEx(INFO, "Direction........ " _YELLOW_("both")); break; case 1: - PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("UP") " delay " _YELLOW_("%d"), delay); + PrintAndLogEx(INFO, "Direction........ " _YELLOW_("up")); break; case 2: - PrintAndLogEx(INFO, "Brute-forcing direction: " _YELLOW_("DOWN") " delay " _YELLOW_("%d"), delay); + PrintAndLogEx(INFO, "Direction........ " _YELLOW_("down")); break; default: break; } } - PrintAndLogEx(INFO, "Started brute-forcing HID Prox reader"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Started bruteforcing HID Prox reader"); PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(NORMAL, ""); // copy values to low. - cn_low = cn_hi; + card_low = card_hi; // main loop - // iceman: could add options for bruteforcing OEM, ISSUE or FC as well.. bool exitloop = false; bool fin_hi, fin_low; fin_hi = fin_low = false; do { - if (!g_session.pm3_present) { + if (g_session.pm3_present == false) { PrintAndLogEx(WARNING, "Device offline\n"); return PM3_ENODATA; } @@ -573,26 +588,42 @@ static int CmdHIDBrute(const char *Cmd) { } // do one up - if (direction != 2) { - if (cn_hi.CardNumber < 0xFFFF) { - cn_hi.CardNumber++; - if (sendTry(format_idx, &cn_hi, delay, verbose) != PM3_SUCCESS) { - return PM3_ESOFT; + if (direction != 2 && fin_hi != true) { + if (sendTry(format_idx, &card_hi, delay, verbose) != PM3_SUCCESS) { + return PM3_ESOFT; + } + if (strcmp(field, "fc") == 0) { + if (card_hi.FacilityCode < 0xFF) { + card_hi.FacilityCode++; + } else { + fin_hi = true; + } + } else if (strcmp(field, "cn") == 0) { + if (card_hi.CardNumber < 0xFFFF) { + card_hi.CardNumber++; + } else { + fin_hi = true; } - } else { - fin_hi = true; } } // do one down - if (direction != 1) { - if (cn_low.CardNumber > 0) { - cn_low.CardNumber--; - if (sendTry(format_idx, &cn_low, delay, verbose) != PM3_SUCCESS) { - return PM3_ESOFT; + if (direction != 1 && fin_low != true) { + if (sendTry(format_idx, &card_low, delay, verbose) != PM3_SUCCESS) { + return PM3_ESOFT; + } + if (strcmp(field, "fc") == 0) { + if (card_low.FacilityCode > 0) { + card_low.FacilityCode--; + } else { + fin_low = true; + } + } else if (strcmp(field, "cn") == 0) { + if (card_low.CardNumber > 0) { + card_low.CardNumber--; + } else { + fin_low = true; } - } else { - fin_low = true; } } @@ -614,7 +645,8 @@ static int CmdHIDBrute(const char *Cmd) { } while (exitloop == false); - PrintAndLogEx(INFO, "Brute forcing finished"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Bruteforcing finished"); return PM3_SUCCESS; } @@ -624,7 +656,7 @@ static command_t CommandTable[] = { {"reader", CmdHIDReader, IfPm3Lf, "attempt to read and extract tag data"}, {"clone", CmdHIDClone, IfPm3Lf, "clone HID tag to T55x7"}, {"sim", CmdHIDSim, IfPm3Lf, "simulate HID tag"}, - {"brute", CmdHIDBrute, IfPm3Lf, "bruteforce card number against reader"}, + {"brute", CmdHIDBrute, IfPm3Lf, "bruteforce facility code or card number against reader"}, {"watch", CmdHIDWatch, IfPm3Lf, "continuously watch for cards. Reader mode"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 4f891ffda..66fb3ea42 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -581,24 +581,24 @@ static int CmdLFHitagReader(const char *Cmd) { if (s01) { cmd = CMD_LF_HITAGS_READ; htf = RHTSF_CHALLENGE; - memcpy(htd.auth.NrAr, nrar, sizeof(nrar)); + memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); } if (s02) { cmd = CMD_LF_HITAGS_READ; htf = RHTSF_KEY; - memcpy(htd.crypto.key, key, sizeof(key)); + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); } if (h21) { htf = RHT2F_PASSWORD; - memcpy(htd.pwd.password, key, 4); + memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); } if (h22) { htf = RHT2F_AUTHENTICATE; - memcpy(htd.auth.NrAr, nrar, sizeof(nrar)); + memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); } if (h23) { htf = RHT2F_CRYPTO; - memcpy(htd.crypto.key, key, sizeof(key)); + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); } if (h25) { htf = RHT2F_TEST_AUTH_ATTEMPTS; @@ -627,6 +627,9 @@ static int CmdLFHitagReader(const char *Cmd) { // block3, 1 byte printHitag2Configuration(data[4 * 3]); + + // print data + print_hex_break(data, 48, 4); } return PM3_SUCCESS; } @@ -767,22 +770,22 @@ static int CmdLFHitagWriter(const char *Cmd) { if (s03) { htf = WHTSF_CHALLENGE; - memcpy(htd.auth.NrAr, nrar, sizeof(nrar)); + memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); memcpy(htd.auth.data, data, sizeof(data)); } if (s04) { htf = WHTSF_KEY; - memcpy(htd.crypto.key, key, sizeof(key)); + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); memcpy(htd.crypto.data, data, sizeof(data)); } if (h24) { htf = WHT2F_CRYPTO; - memcpy(htd.pwd.password, key, 4); + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); memcpy(htd.crypto.data, data, sizeof(data)); } if (h27) { htf = WHT2F_PASSWORD; - memcpy(htd.pwd.password, key, 4); + memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); memcpy(htd.crypto.data, data, sizeof(data)); } @@ -820,17 +823,14 @@ static int CmdLFHitag2Dump(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t filename[FILE_PATH_SIZE] = {0}; + int fnlen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 1), filename, sizeof(filename), &fnlen); - if (res != 0) { - CLIParserFree(ctx); - return PM3_EINVARG; - } + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); uint8_t key[6]; int keylen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, sizeof(key), &keylen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, sizeof(key), &keylen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; @@ -844,22 +844,49 @@ static int CmdLFHitag2Dump(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(WARNING, "to be implemented..."); + hitag_function htf; + hitag_data htd; + memset(&htd, 0, sizeof(htd)); - /* - PrintAndLogEx(SUCCESS, "Dumping tag memory..."); + if (keylen == 6) { + htf = RHT2F_CRYPTO; + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); + PrintAndLogEx(INFO, "Authenticating in crypto mode"); + } else { + htf = RHT2F_PASSWORD; + memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); + PrintAndLogEx(INFO, "Authenticating in password mode"); + } - clearCommandBuffer(); - //SendCommandNG(CMD_LF_HITAG_DUMP, &htd, sizeof(htd)); - PacketResponseNG resp; - uint8_t *data = resp.data.asBytes; - if (fnlen < 1) { - char *fptr = filename + snprintf(filename, sizeof(filename), "lf-hitag-"); - FillFileNameByUID(fptr, data, "-dump", 4); - } + uint16_t cmd = CMD_LF_HITAG_READER; + clearCommandBuffer(); + SendCommandMIX(cmd, htf, 0, 0, &htd, sizeof(htd)); + PacketResponseNG resp; - pm3_save_dump(filename, data, 48, jsfHitag, 4); - */ + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + if (resp.oldarg[0] == false) { + PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); + return PM3_ESOFT; + } + uint8_t *data = resp.data.asBytes; + + if (data == NULL) + return PM3_ESOFT; + + PrintAndLogEx(SUCCESS, "Dumping tag memory..."); + + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(filename, sizeof(filename), "lf-hitag-"); + FillFileNameByUID(fptr, data, "-dump", 4); + } + + print_hex_break(data, 48, 4); + + pm3_save_dump(filename, data, 48, jsfHitag, 4); return PM3_SUCCESS; } @@ -918,7 +945,6 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, } - void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) { } diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index 9b174c74a..fa59894e1 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -37,6 +37,8 @@ #include "cliparser.h" #include "cmdlfem4x05.h" // EM defines #include "parity.h" // parity +#include "util_posix.h" + #define INDALA_ARR_LEN 64 static int CmdHelp(const char *Cmd); @@ -121,6 +123,72 @@ static void decodeHeden2L(uint8_t *bits) { PrintAndLogEx(SUCCESS, " Heden-2L | %u", cardnumber); } +// sending three times. Didn't seem to break the previous sim? +static int sendPing(void) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + SendCommandNG(CMD_PING, NULL, 0); + clearCommandBuffer(); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_PING, &resp, 1000) == false) { + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + +static int sendTry(uint8_t fc, uint16_t cn, uint32_t delay, bool fmt4041x, bool verbose) { + + // convert to fc / cn to binarray + uint8_t bs[64]; + memset(bs, 0x00, sizeof(bs)); + + // Bitstream generation, format select + int res; + if (fmt4041x) { + res = getIndalaBits4041x(fc, cn, bs); + } else { + res = getIndalaBits(fc, cn, bs); + } + + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Error with tag bitstream generation."); + return res; + } + + if (verbose) { + + uint8_t raw[8]; + raw[0] = bytebits_to_byte(bs, 8); + raw[1] = bytebits_to_byte(bs + 8, 8); + raw[2] = bytebits_to_byte(bs + 16, 8); + raw[3] = bytebits_to_byte(bs + 24, 8); + raw[4] = bytebits_to_byte(bs + 32, 8); + raw[5] = bytebits_to_byte(bs + 40, 8); + raw[6] = bytebits_to_byte(bs + 48, 8); + raw[7] = bytebits_to_byte(bs + 56, 8); + + PrintAndLogEx(INFO, "Trying FC: " _YELLOW_("%u") " CN: " _YELLOW_("%u") " Raw: " _YELLOW_("%s") + , fc + , cn + , sprint_hex_inrow(raw, sizeof(raw)) + ); + } + + // indala PSK, clock 32, carrier 0 + lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); + payload->carrier = 2; + payload->invert = 0; + payload->clock = 32; + memcpy(payload->data, bs, sizeof(bs)); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + sizeof(bs)); + free(payload); + + msleep(delay); + return sendPing(); +} + + // Indala 26 bit decode // by marshmellow, martinbeier // optional arguments - same as PSKDemod (clock & invert & maxerr) @@ -336,7 +404,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { uint8_t data[MAX_GRAPH_TRACE_LEN] = {0}; size_t datasize = getFromGraphBuf(data); - uint8_t rawbits[4096]; + uint8_t rawbits[4096] = {0}; int rawbit = 0; int worst = 0, worstPos = 0; @@ -564,6 +632,8 @@ static int CmdIndalaSim(const char *Cmd) { "Enables simulation of Indala card with specified facility code and card number.\n" "Simulation runs until the button is pressed or another USB command is issued.", "lf indala sim --heden 888\n" + "lf indala sim --fc 123 --cn 1337 \n" + "lf indala sim --fc 123 --cn 1337 --4041x\n" "lf indala sim --raw a0000000a0002021\n" "lf indala sim --raw 80000001b23523a6c2e31eba3cbee4afb3c6ad1fcf649393928c14e5" ); @@ -572,8 +642,12 @@ static int CmdIndalaSim(const char *Cmd) { arg_param_begin, arg_str0("r", "raw", "", "raw bytes"), arg_int0(NULL, "heden", "", "Cardnumber for Heden 2L format"), + arg_int0(NULL, "fc", "", "Facility code (26 bit H10301 format)"), + arg_int0(NULL, "cn", "", "Card number (26 bit H10301 format)"), + arg_lit0(NULL, "4041x", "Optional - specify Indala 4041X format, must use with fc and cn"), arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, false); // raw param @@ -584,17 +658,34 @@ static int CmdIndalaSim(const char *Cmd) { bool is_long_uid = (raw_len == 28); + bool fmt4041x = arg_get_lit(ctx, 5); + + int32_t cardnumber; - bool got_cn = false; + uint8_t fc = 0; + uint16_t cn = 0; + bool got_cn = false, got_26 = false; + if (is_long_uid == false) { // Heden param cardnumber = arg_get_int_def(ctx, 2, -1); got_cn = (cardnumber != -1); + + // 26b FC/CN param + fc = arg_get_int_def(ctx, 3, 0); + cn = arg_get_int_def(ctx, 4, 0); + got_26 = (fc != 0 && cn != 0); } CLIParserFree(ctx); + if ((got_26 == false) && fmt4041x) { + PrintAndLogEx(FAILED, "You must specify a facility code and card number when using 4041X format"); + return PM3_EINVARG; + } + + // if HEDEN fmt? if (got_cn) { encodeHeden2L(raw, cardnumber); raw_len = 8; @@ -604,17 +695,47 @@ static int CmdIndalaSim(const char *Cmd) { uint8_t bs[224]; memset(bs, 0x00, sizeof(bs)); + // if RAW, copy to bitstream uint8_t counter = 0; for (int32_t i = 0; i < raw_len; i++) { - uint8_t tmp = raw[i]; - bs[counter++] = (tmp >> 7) & 1; - bs[counter++] = (tmp >> 6) & 1; - bs[counter++] = (tmp >> 5) & 1; - bs[counter++] = (tmp >> 4) & 1; - bs[counter++] = (tmp >> 3) & 1; - bs[counter++] = (tmp >> 2) & 1; - bs[counter++] = (tmp >> 1) & 1; - bs[counter++] = tmp & 1; + uint8_t b = raw[i]; + bs[counter++] = (b >> 7) & 1; + bs[counter++] = (b >> 6) & 1; + bs[counter++] = (b >> 5) & 1; + bs[counter++] = (b >> 4) & 1; + bs[counter++] = (b >> 3) & 1; + bs[counter++] = (b >> 2) & 1; + bs[counter++] = (b >> 1) & 1; + bs[counter++] = b & 1; + } + + counter = (raw_len * 8); + + // HEDEN + + // FC / CN not HEDEN. + if (raw_len == 0 && got_26) { + // Bitstream generation, format select + int res = PM3_ESOFT; + if (fmt4041x) { + res = getIndalaBits4041x(fc, cn, bs); + } else { + res = getIndalaBits(fc, cn, bs); + } + + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Error with tag bitstream generation."); + return res; + } + + counter = INDALA_ARR_LEN; + + PrintAndLogEx(SUCCESS, "Simulating " _YELLOW_("64 bit") " Indala FC " _YELLOW_("%u") " CN " _YELLOW_("%u"), fc, cn); + } else { + PrintAndLogEx(SUCCESS, "Simulating " _YELLOW_("%s") " Indala raw " _YELLOW_("%s") + , (is_long_uid) ? "224 bit" : "64 bit" + , sprint_hex_inrow(raw, counter) + ); } // a0 00 00 00 bd 98 9a 11 @@ -623,10 +744,7 @@ static int CmdIndalaSim(const char *Cmd) { // It has to send either 64bits (8bytes) or 224bits (28bytes). Zero padding needed if not. // lf simpsk -1 -c 32 --fc 2 -d 0102030405060708 - PrintAndLogEx(SUCCESS, "Simulating " _YELLOW_("%s") " Indala raw " _YELLOW_("%s") - , (is_long_uid) ? "224 bit" : "64 bit" - , sprint_hex_inrow(raw, raw_len) - ); + PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command"); // indala PSK, clock 32, carrier 0 @@ -634,34 +752,31 @@ static int CmdIndalaSim(const char *Cmd) { payload->carrier = 2; payload->invert = 0; payload->clock = 32; - memcpy(payload->data, bs, raw_len * 8); + memcpy(payload->data, bs, counter); clearCommandBuffer(); - SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + (raw_len * 8)); + SendCommandNG(CMD_LF_PSK_SIMULATE, (uint8_t *)payload, sizeof(lf_psksim_t) + counter); free(payload); PacketResponseNG resp; WaitForResponse(CMD_LF_PSK_SIMULATE, &resp); PrintAndLogEx(INFO, "Done"); - if (resp.status != PM3_EOPABORTED) + if (resp.status != PM3_EOPABORTED) { return resp.status; + } return PM3_SUCCESS; } static int CmdIndalaClone(const char *Cmd) { - int32_t cardnumber; - uint8_t fc = 0; - uint16_t cn = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "lf indala clone", "clone Indala UID to T55x7 or Q5/T5555 tag using different known formats\n" _RED_("\nWarning, encoding with FC/CN doesn't always work"), - "lf indala clone --heden 888 --> use Heden 2L format\n" - "lf indala clone --fc 123 --cn 1337 --> use standard 26b format\n" - "lf indala clone --fc 123 --cn 1337 --4041x --> use 4041x format\n" + "lf indala clone --heden 888\n" + "lf indala clone --fc 123 --cn 1337\n" + "lf indala clone --fc 123 --cn 1337 --4041x\n" "lf indala clone -r a0000000a0002021\n" "lf indala clone -r 80000001b23523a6c2e31eba3cbee4afb3c6ad1fcf649393928c14e5"); @@ -683,11 +798,16 @@ static int CmdIndalaClone(const char *Cmd) { CLIGetHexWithReturn(ctx, 1, raw, &raw_len); bool is_long_uid = (raw_len == 28); + bool q5 = arg_get_lit(ctx, 5); bool em = arg_get_lit(ctx, 6); bool fmt4041x = arg_get_lit(ctx, 7); + int32_t cardnumber; + uint8_t fc = 0; + uint16_t cn = 0; bool got_cn = false, got_26 = false; + if (is_long_uid == false) { // Heden param @@ -706,7 +826,7 @@ static int CmdIndalaClone(const char *Cmd) { return PM3_EINVARG; } - if ((!got_26) && fmt4041x) { + if ((got_26 == false) && fmt4041x) { PrintAndLogEx(FAILED, "You must specify a facility code and card number when using 4041X format"); return PM3_EINVARG; } @@ -823,8 +943,145 @@ static int CmdIndalaClone(const char *Cmd) { return res; } +static int CmdIndalaBrute(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf indala brute", + "Enables bruteforce of INDALA readers with specified facility code.\n" + "This is a attack against reader. if cardnumber is given, it starts with it and goes up / down one step\n" + "if cardnumber is not given, it starts with 1 and goes up to 65535", + "lf indala brute --fc 224\n" + "lf indala brute --fc 21 -d 2000\n" + "lf indala brute -v --fc 21 --cn 200 -d 2000\n" + "lf indala brute -v --fc 21 --cn 200 -d 2000 --up\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number to start with"), + arg_u64_0("d", "delay", "", "delay betweens attempts in ms. Default 1000ms"), + arg_lit0(NULL, "up", "direction to increment card number. (default is both directions)"), + arg_lit0(NULL, "down", "direction to decrement card number. (default is both directions)"), + arg_lit0(NULL, "4041x", "specify Indala 4041X format"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool verbose = arg_get_lit(ctx, 1); + + uint32_t fc = arg_get_u32_def(ctx, 2, 0); + uint32_t cn = arg_get_u32_def(ctx, 3, 0); + + uint32_t delay = arg_get_u32_def(ctx, 4, 1000); + + int direction = 0; + if (arg_get_lit(ctx, 5) && arg_get_lit(ctx, 6)) { + direction = 0; + } else if (arg_get_lit(ctx, 5)) { + direction = 1; + } else if (arg_get_lit(ctx, 6)) { + direction = 2; + } + + bool fmt4041x = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + if (verbose) { + PrintAndLogEx(INFO, "Wiegand format... " _YELLOW_("%s"), (fmt4041x) ? "4041x" : "Standard"); + PrintAndLogEx(INFO, "Facility code.... " _YELLOW_("%u"), fc); + PrintAndLogEx(INFO, "Card number...... " _YELLOW_("%u"), cn); + PrintAndLogEx(INFO, "Delay............ " _YELLOW_("%d"), delay); + switch (direction) { + case 0: + PrintAndLogEx(INFO, "Direction........ " _YELLOW_("BOTH")); + break; + case 1: + PrintAndLogEx(INFO, "Direction........ " _YELLOW_("UP")); + break; + case 2: + PrintAndLogEx(INFO, "Direction........ " _YELLOW_("DOWN")); + break; + default: + break; + } + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Started brute-forcing INDALA Prox reader"); + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(NORMAL, ""); + + // main loop + // iceman: could add options for bruteforcing FC as well.. + uint8_t fc_hi = fc; + uint8_t fc_low = fc; + uint16_t cn_hi = cn; + uint16_t cn_low = cn; + + bool exitloop = false; + bool fin_hi, fin_low; + fin_hi = fin_low = false; + do { + + if (g_session.pm3_present == false) { + PrintAndLogEx(WARNING, "Device offline\n"); + return PM3_ENODATA; + } + + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "aborted via keyboard!"); + return sendPing(); + } + + // do one up + if (direction != 2) { + if (cn_hi < 0xFFFF) { + if (sendTry(fc_hi, cn_hi, delay, fmt4041x, verbose) != PM3_SUCCESS) { + return PM3_ESOFT; + } + cn_hi++; + } else { + fin_hi = true; + } + } + + // do one down + if (direction != 1) { + if (cn_low > 0) { + cn_low--; + if (sendTry(fc_low, cn_low, delay, fmt4041x, verbose) != PM3_SUCCESS) { + return PM3_ESOFT; + } + } else { + fin_low = true; + } + } + + switch (direction) { + case 0: + if (fin_hi && fin_low) { + exitloop = true; + } + break; + case 1: + exitloop = fin_hi; + break; + case 2: + exitloop = fin_low; + break; + default: + break; + } + + } while (exitloop == false); + + PrintAndLogEx(INFO, "Brute forcing finished"); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"brute", CmdIndalaBrute, IfPm3Lf, "Demodulate an Indala tag (PSK1) from the GraphBuffer"}, {"demod", CmdIndalaDemod, AlwaysAvailable, "Demodulate an Indala tag (PSK1) from the GraphBuffer"}, {"altdemod", CmdIndalaDemodAlt, AlwaysAvailable, "Alternative method to demodulate samples for Indala 64 bit UID (option '224' for 224 bit)"}, {"reader", CmdIndalaReader, IfPm3Lf, "Read an Indala tag from the antenna"}, diff --git a/client/src/cmdlfparadox.c b/client/src/cmdlfparadox.c index 328bda02c..a2fa50f11 100644 --- a/client/src/cmdlfparadox.c +++ b/client/src/cmdlfparadox.c @@ -99,7 +99,7 @@ int demodParadox(bool verbose) { } uint32_t hi2 = 0, hi = 0, lo = 0; - uint8_t error = 0; + uint8_t errors = 0; // Remove manchester encoding from FSK bits, skip pre for (uint32_t i = idx + PARADOX_PREAMBLE_LEN; i < (idx + 96); i += 2) { @@ -107,7 +107,7 @@ int demodParadox(bool verbose) { // not manchester data if (bits[i] == bits[i + 1]) { PrintAndLogEx(WARNING, "Error Manchester at %u", i); - error++; + errors++; } hi2 = (hi2 << 1) | (hi >> 31); @@ -119,6 +119,10 @@ int demodParadox(bool verbose) { } } + if (errors) { + PrintAndLogEx(WARNING, "Total Manchester Errors... %u", errors); + } + setDemodBuff(bits, size, idx); setClockGrid(50, wave_idx + (idx * 50)); @@ -226,6 +230,7 @@ static int CmdParadoxClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf paradox clone", "clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", + "lf paradox clone --fc 96 --cn 40426 -> encode for T55x7 tag with fc and cn\n" "lf paradox clone --raw 0f55555695596a6a9999a59a -> encode for T55x7 tag\n" "lf paradox clone --raw 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag\n" "lf paradox clone --raw 0f55555695596a6a9999a59a --em -> encode for EM4305/4469" @@ -234,6 +239,8 @@ static int CmdParadoxClone(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("r", "raw", "", "raw hex data. 12 bytes max"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end @@ -245,8 +252,10 @@ static int CmdParadoxClone(const char *Cmd) { uint8_t raw[12] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); - bool q5 = arg_get_lit(ctx, 2); - bool em = arg_get_lit(ctx, 3); + uint32_t fc = arg_get_u32_def(ctx, 2, 0); + uint32_t cn = arg_get_u32_def(ctx, 3, 0); + bool q5 = arg_get_lit(ctx, 4); + bool em = arg_get_lit(ctx, 5); CLIParserFree(ctx); if (q5 && em) { @@ -254,14 +263,56 @@ static int CmdParadoxClone(const char *Cmd) { return PM3_EINVARG; } - if (raw_len != 12) { - PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); - return PM3_EINVARG; - } + uint32_t blocks[4] = {0}; - uint32_t blocks[4]; - for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { - blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + if (raw_len != 0) { + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + return PM3_EINVARG; + } + + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); + } + } else { + uint8_t manchester[13] = { 0x00 }; // check size needed + uint32_t t1; + + manchester[0] = 0x0F; // preamble + manchester[1] = 0x05; // Leading zeros - Note: from this byte on, is part of the CRC calculation + manchester[2] = 0x55; // Leading zeros its 4 bits out for the CRC, so we need too move + manchester[3] = 0x55; // Leading zeros back 4 bits once we have the crc (done below) + + // add FC + t1 = manchesterEncode2Bytes(fc); + manchester[4] = (t1 >> 8) & 0xFF; + manchester[5] = t1 & 0xFF; + + // add cn + t1 = manchesterEncode2Bytes(cn); + manchester[6] = (t1 >> 24) & 0xFF; + manchester[7] = (t1 >> 16) & 0xFF; + manchester[8] = (t1 >> 8) & 0xFF; + manchester[9] = t1 & 0xFF; + + uint8_t crc = (CRC8Maxim(manchester + 1, 9) ^ 0x6) & 0xFF; + + // add crc + t1 = manchesterEncode2Bytes(crc); + manchester[10] = (t1 >> 8) & 0xFF; + manchester[11] = t1 & 0xFF; + + // move left 4 bits left 4 bits - Now that we have the CRC we need to re-align the data. + for (int i = 1; i < 12; i++) + manchester[i] = (manchester[i] << 4) + (manchester[i + 1] >> 4); + + // Add trailing 1010 (11) + manchester[11] |= (1 << 3); + manchester[11] |= (1 << 1); + + // move into tag blocks + for (int i = 0; i < 12; i++) + blocks[1 + (i / 4)] += (manchester[i] << (8 * (3 - i % 4))); } // Paradox - FSK2a, data rate 50, 3 data blocks diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 2af8f18b7..f1cb773fb 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -2235,7 +2235,7 @@ static int CmdT55xxDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, sizeof(filename), &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); uint8_t override = arg_get_lit(ctx, 2) ? 1 : 0; @@ -2341,7 +2341,7 @@ static int CmdT55xxRestore(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, sizeof(filename), &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool usepwd = false; uint32_t password = 0; @@ -2362,7 +2362,7 @@ static int CmdT55xxRestore(const char *Cmd) { } // read dump file - uint8_t *dump = NULL; + uint32_t *dump = NULL; size_t bytes_read = 0; res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (T55x7_BLOCK_COUNT * 4)); if (res != PM3_SUCCESS) { @@ -2387,11 +2387,10 @@ static int CmdT55xxRestore(const char *Cmd) { snprintf(pwdopt, sizeof(pwdopt), "-p %08X", password); } - uint32_t *data = (uint32_t *) dump; uint8_t idx; // Restore endien for writing to card for (idx = 0; idx < 12; idx++) { - data[idx] = BSWAP_32(data[idx]); + dump[idx] = BSWAP_32(dump[idx]); } // Have data ready, lets write @@ -2400,12 +2399,12 @@ static int CmdT55xxRestore(const char *Cmd) { // write blocks 1..3 page 1 // update downlink mode (if needed) and write b 0 downlink_mode = 0; - if ((((data[11] >> 28) & 0xF) == 6) || (((data[11] >> 28) & 0xF) == 9)) - downlink_mode = (data[11] >> 10) & 3; + if ((((dump[11] >> 28) & 0xF) == 6) || (((dump[11] >> 28) & 0xF) == 9)) + downlink_mode = (dump[11] >> 10) & 3; // write out blocks 1-7 page 0 for (idx = 1; idx <= 7; idx++) { - snprintf(wcmd, sizeof(wcmd), "-b %d -d %08X %s", idx, data[idx], pwdopt); + snprintf(wcmd, sizeof(wcmd), "-b %d -d %08X %s", idx, dump[idx], pwdopt); if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); @@ -2414,12 +2413,12 @@ static int CmdT55xxRestore(const char *Cmd) { // if password was set on the "blank" update as we may have just changed it if (usepwd) { - snprintf(pwdopt, sizeof(pwdopt), "-p %08X", data[7]); + snprintf(pwdopt, sizeof(pwdopt), "-p %08X", dump[7]); } // write out blocks 1-3 page 1 for (idx = 9; idx <= 11; idx++) { - snprintf(wcmd, sizeof(wcmd), "-b %d --pg1 -d %08X %s", idx - 8, data[idx], pwdopt); + snprintf(wcmd, sizeof(wcmd), "-b %d --pg1 -d %08X %s", idx - 8, dump[idx], pwdopt); if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); @@ -2430,7 +2429,7 @@ static int CmdT55xxRestore(const char *Cmd) { config.downlink_mode = downlink_mode; // Write the page 0 config - snprintf(wcmd, sizeof(wcmd), "-b 0 -d %08X %s", data[0], pwdopt); + snprintf(wcmd, sizeof(wcmd), "-b 0 -d %08X %s", dump[0], pwdopt); if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Warning: error writing blk 0"); } @@ -3035,7 +3034,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, sizeof(filename), &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); // White cloner password based on EM4100 ID bool use_calc_password = false; @@ -4012,7 +4011,6 @@ static int CmdT55xxSniff(const char *Cmd) { size_t idx = 0; uint32_t usedPassword, blockData; int pulseSamples = 0, pulseIdx = 0; - const char *modeText; char pwdText[100]; char dataText[100]; int pulseBuffer[80] = { 0 }; // max should be 73 +/- - Holds Pulse widths @@ -4027,8 +4025,8 @@ static int CmdT55xxSniff(const char *Cmd) { // Headings PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, _CYAN_("T55xx command detection")); - PrintAndLogEx(SUCCESS, "Downlink mode | password | Data | blk | page | 0 | 1 | raw"); - PrintAndLogEx(SUCCESS, "----------------------+----------+----------+-----+------+-----+-----+-------------------------------------------------------------------------------"); + PrintAndLogEx(SUCCESS, "Downlink mode | password | Data | blk | page | 0 | 1 | raw"); + PrintAndLogEx(SUCCESS, "------------------------+------------+----------+-----+------+-----+-----+-------------------------------------------------------------------------------"); idx = 0; // loop though sample buffer @@ -4038,7 +4036,7 @@ static int CmdT55xxSniff(const char *Cmd) { int maxWidth = 0; data[0] = 0; bool have_data = false; - modeText = "Default"; + const char *modeText = "Default"; strncpy(pwdText, " ", sizeof(pwdText)); strncpy(dataText, " ", sizeof(dataText)); @@ -4164,11 +4162,15 @@ static int CmdT55xxSniff(const char *Cmd) { } have_data = true; modeText = "Default pwd write"; - snprintf(pwdText, sizeof(pwdText), "%08X", usedPassword); + snprintf(pwdText, sizeof(pwdText), " %08X", usedPassword); snprintf(dataText, sizeof(dataText), "%08X", blockData); } - // Default Write (or password read ??) + // Default Write or password read ??? + // the most confusing command. + // if the token is with a password - all is OK, + // if not - read command with a password will lead to write the shifted password to the memory and: + // IF the most bit of the data is `1` ----> IT LEADS TO LOCK this block of the memory if (dataLen == 38) { t55sniff_trim_samples(pulseBuffer, &pulseIdx, 38); @@ -4180,6 +4182,12 @@ static int CmdT55xxSniff(const char *Cmd) { if (data[i] == '1') blockData |= 1; } + for (uint8_t i = 2; i <= 33; i++) { + usedPassword <<= 1; + if (data[i] == '1') { + usedPassword |= 1; + } + } blockAddr = 0; for (uint8_t i = 35; i <= 37; i++) { blockAddr <<= 1; @@ -4187,7 +4195,8 @@ static int CmdT55xxSniff(const char *Cmd) { blockAddr |= 1; } have_data = true; - modeText = "Default write"; + modeText = "Default write/pwd read"; + snprintf(pwdText, sizeof(pwdText), "[%08X]", usedPassword); snprintf(dataText, sizeof(dataText), "%08X", blockData); } } @@ -4224,7 +4233,7 @@ static int CmdT55xxSniff(const char *Cmd) { } have_data = true; modeText = "Leading 0 pwd write"; - snprintf(pwdText, sizeof(pwdText), "%08X", usedPassword); + snprintf(pwdText, sizeof(pwdText), " %08X", usedPassword); snprintf(dataText, sizeof(dataText), "%08X", blockData); } } @@ -4234,9 +4243,9 @@ static int CmdT55xxSniff(const char *Cmd) { // Print results if (have_data) { if (blockAddr == 7) - PrintAndLogEx(SUCCESS, "%-20s | "_GREEN_("%8s")" | "_YELLOW_("%8s")" | "_YELLOW_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s", modeText, pwdText, dataText, blockAddr, page, minWidth, maxWidth, data); + PrintAndLogEx(SUCCESS, "%-22s | "_GREEN_("%10s")" | "_YELLOW_("%8s")" | "_YELLOW_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s", modeText, pwdText, dataText, blockAddr, page, minWidth, maxWidth, data); else - PrintAndLogEx(SUCCESS, "%-20s | "_GREEN_("%8s")" | "_GREEN_("%8s")" | "_GREEN_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s", modeText, pwdText, dataText, blockAddr, page, minWidth, maxWidth, data); + PrintAndLogEx(SUCCESS, "%-22s | "_GREEN_("%10s")" | "_GREEN_("%8s")" | "_GREEN_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s", modeText, pwdText, dataText, blockAddr, page, minWidth, maxWidth, data); } } diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index 8c11f7bc5..a9c2cb474 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -39,6 +39,7 @@ #include "cmdanalyse.h" #include "emv/cmdemv.h" // EMV #include "cmdflashmem.h" // rdv40 flashmem commands +#include "cmdpiv.h" #include "cmdsmartcard.h" // rdv40 smart card ISO7816 commands #include "cmdusart.h" // rdv40 FPC USART commands #include "cmdwiegand.h" // wiegand commands @@ -111,7 +112,7 @@ static int lf_search_plus(const char *Cmd) { // Try to change config! uint32_t d; d = config.divisor = default_divisor[i]; - PrintAndLogEx(INFO, "--> trying (" _GREEN_("%d.%02d kHz")")", 12000 / (d + 1), ((1200000 + (d + 1) / 2) / (d + 1)) - ((12000 / (d + 1)) * 100)); + PrintAndLogEx(INFO, "--> trying ( " _GREEN_("%d.%02d kHz")" )", 12000 / (d + 1), ((1200000 + (d + 1) / 2) / (d + 1)) - ((12000 / (d + 1)) * 100)); retval = lf_config(&config); if (retval != PM3_SUCCESS) @@ -249,7 +250,7 @@ static int CmdMsleep(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "msleep", "Sleep for given amount of milliseconds", - "msleep 100" + "msleep -t 100" ); void *argtable[] = { @@ -299,15 +300,23 @@ static int CmdClear(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "clear", "Clear the Proxmark3 client terminal screen", - "clear" + "clear -> clear the terminal screen\n" + "clear -b -> clear the terminal screen and the scrollback buffer" ); void *argtable[] = { arg_param_begin, + arg_lit0("b", "back", "also clear the scrollback buffer"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool scrollback = arg_get_lit(ctx, 1); CLIParserFree(ctx); - PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ ""); + + if (!scrollback) + PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ ""); + else + PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ _CLEAR_SCROLLBACK_ ""); + return PM3_SUCCESS; } @@ -324,6 +333,7 @@ static command_t CommandTable[] = { {"lf", CmdLF, AlwaysAvailable, "{ Low frequency commands... }"}, {"mem", CmdFlashMem, IfPm3Flash, "{ Flash memory manipulation... }"}, {"nfc", CmdNFC, AlwaysAvailable, "{ NFC commands... }"}, + {"piv", CmdPIV, AlwaysAvailable, "{ PIV commands... }"}, {"reveng", CmdRev, AlwaysAvailable, "{ CRC calculations from RevEng software... }"}, {"smart", CmdSmartcard, AlwaysAvailable, "{ Smart card ISO-7816 commands... }"}, {"script", CmdScript, AlwaysAvailable, "{ Scripting commands... }"}, diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index a3f6ffd05..df87ee6f8 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -107,14 +107,14 @@ static int CmdNfcDecode(const char *Cmd) { uint8_t *dump = NULL; size_t bytes_read = 4096; res = pm3_load_dump(filename, (void **)&dump, &bytes_read, 4096); - if (res != PM3_SUCCESS) { + if (res != PM3_SUCCESS || dump == NULL) { return res; } res = NDEFDecodeAndPrint(dump, bytes_read, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); - res = NDEFRecordsDecodeAndPrint(dump, bytes_read); + res = NDEFRecordsDecodeAndPrint(dump, bytes_read, verbose); } free(dump); @@ -123,7 +123,7 @@ static int CmdNfcDecode(const char *Cmd) { res = NDEFDecodeAndPrint(data, datalen, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); - res = NDEFRecordsDecodeAndPrint(data, datalen); + res = NDEFRecordsDecodeAndPrint(data, datalen, verbose); } } return res; @@ -138,6 +138,7 @@ static int CmdNFCType1Help(const char *Cmd); static command_t CommandNFCType1Table[] = { {"--------", CmdNFCType1Help, AlwaysAvailable, "-------------- " _CYAN_("NFC Forum Tag Type 1") " ---------------"}, +// {"format", CmdNFCType1Format, IfPm3Iso14443a, "format ISO-14443-a tag as NFC Tag"}, {"read", CmdNFCType1Read, IfPm3Iso14443a, "read NFC Forum Tag Type 1"}, // {"write", CmdNFCType1Write, IfPm3Iso14443a, "write NFC Forum Tag Type 1"}, {"--------", CmdNFCType1Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, @@ -165,6 +166,7 @@ static int CmdNFCType2Help(const char *Cmd); static command_t CommandNFCType2Table[] = { {"--------", CmdNFCType2Help, AlwaysAvailable, "-------------- " _CYAN_("NFC Forum Tag Type 2") " ---------------"}, +// {"format", CmdNFCType2Format, IfPm3Iso14443a, "format ISO-14443-a tag as NFC Tag"}, {"read", CmdNFCType2Read, IfPm3Iso14443a, "read NFC Forum Tag Type 2"}, // {"write", CmdNFCType2Write, IfPm3Iso14443a, "write NFC Forum Tag Type 2"}, {"--------", CmdNFCType2Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, @@ -193,8 +195,9 @@ static int CmdNFCType3Help(const char *Cmd); static command_t CommandNFCType3Table[] = { {"--------", CmdNFCType3Help, AlwaysAvailable, "-------------- " _CYAN_("NFC Forum Tag Type 3") " ---------------"}, +// {"format", CmdNFCType3Format, IfPm3Felica, "format FeliCa tag as NFC Tag"}, {"read", CmdNFCType3Read, IfPm3Felica, "read NFC Forum Tag Type 3"}, - {"write", CmdNFCType3Write, IfPm3Felica, "write NFC Forum Tag Type 3"}, +// {"write", CmdNFCType3Write, IfPm3Felica, "write NFC Forum Tag Type 3"}, {"--------", CmdNFCType3Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdNFCType3Help, AlwaysAvailable, "This help"}, {NULL, NULL, NULL, NULL} @@ -220,19 +223,28 @@ static int CmdNFCST25TARead(const char *Cmd) { return CmdHFST25TANdefRead(Cmd); } +static int CmdNFCType4AFormat(const char *Cmd) { + return CmdHF14ANdefFormat(Cmd); +} + +static int CmdNFCType4AWrite(const char *Cmd) { + return CmdHF14ANdefWrite(Cmd); +} + static int CmdNFCType4AHelp(const char *Cmd); static command_t CommandNFCType4ATable[] = { - {"--------", CmdNFCType4AHelp, AlwaysAvailable, "--------- " _CYAN_("NFC Forum Tag Type 4 ISO14443A") " ----------"}, - {"read", CmdNFCType4ARead, IfPm3Iso14443a, "read NFC Forum Tag Type 4 A"}, -// {"write", CmdNFCType4AWrite, IfPm3Iso14443a, "write NFC Forum Tag Type 4 A"}, + {"--------", CmdNFCType4AHelp, AlwaysAvailable, "--------- " _CYAN_("NFC Forum Tag Type 4 ISO14443A") " ----------"}, + {"format", CmdNFCType4AFormat, IfPm3Iso14443a, "format ISO-14443-a tag as NFC Tag"}, + {"read", CmdNFCType4ARead, IfPm3Iso14443a, "read NFC Forum Tag Type 4 A"}, + {"write", CmdNFCType4AWrite, IfPm3Iso14443a, "write NFC Forum Tag Type 4 A"}, // {"mfdesread", CmdNFCMFDESRead, IfPm3Iso14443a, "read NDEF from MIFARE DESfire"}, // hf mfdes ndefread // {"mfdesformat", CmdNFCMFDESFormat, IfPm3Iso14443a, "format MIFARE DESfire as NFC Forum Tag Type 4"}, - {"st25taread", CmdNFCST25TARead, IfPm3Iso14443a, "read ST25TA as NFC Forum Tag Type 4"}, + {"st25taread", CmdNFCST25TARead, IfPm3Iso14443a, "read ST25TA as NFC Forum Tag Type 4"}, - {"--------", CmdNFCType4AHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, - {"help", CmdNFCType4AHelp, AlwaysAvailable, "This help"}, + {"--------", CmdNFCType4AHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, + {"help", CmdNFCType4AHelp, AlwaysAvailable, "This help"}, {NULL, NULL, NULL, NULL} }; @@ -256,6 +268,7 @@ static int CmdNFCType4BHelp(const char *Cmd); static command_t CommandNFCType4BTable[] = { {"--------", CmdNFCType4BHelp, AlwaysAvailable, "--------- " _CYAN_("NFC Forum Tag Type 4 ISO14443B") " -------------"}, +// {"format", CmdNFCType4BFormat, IfPm3Iso14443b, "format ISO-14443-b tag as NFC Tag"}, {"read", CmdNFCType4BRead, IfPm3Iso14443b, "read NFC Forum Tag Type 4 B"}, // {"write", CmdNFCType4BWrite, IfPm3Iso14443b, "write NFC Forum Tag Type 4 B"}, {"--------", CmdNFCType4BHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, @@ -284,8 +297,9 @@ static int CmdNFCType5Help(const char *Cmd); static command_t CommandNFCType5Table[] = { {"--------", CmdNFCType5Help, AlwaysAvailable, "-------------- " _CYAN_("NFC Forum Tag Type 5") " ---------------"}, +// {"format", CmdNFCType5Format, IfPm3Iso15693, "format ISO-15693 tag as NFC Tag"}, {"read", CmdNFCType5Read, IfPm3Iso15693, "read NFC Forum Tag Type 5"}, - {"write", CmdNFCType5Write, IfPm3Iso15693, "write NFC Forum Tag Type 5"}, +// {"write", CmdNFCType5Write, IfPm3Iso15693, "write NFC Forum Tag Type 5"}, {"--------", CmdNFCType5Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdNFCType5Help, AlwaysAvailable, "This help"}, {NULL, NULL, NULL, NULL} @@ -307,6 +321,15 @@ static int CmdNFCMFCRead(const char *Cmd) { return CmdHFMFNDEFRead(Cmd); } +static int CmdNFCMFCFormat(const char *Cmd) { + return CmdHFMFNDEFFormat(Cmd); +} + +static int CmdNFCMFCWrite(const char *Cmd) { + return CmdHFMFNDEFWrite(Cmd); +} + + static int CmdNFCMFPRead(const char *Cmd) { return CmdHFMFPNDEFRead(Cmd); } @@ -316,9 +339,9 @@ static int CmdNFCMFHelp(const char *Cmd); static command_t CommandMFTable[] = { {"--------", CmdNFCMFHelp, AlwaysAvailable, "--------- " _CYAN_("NFC Type MIFARE Classic/Plus Tag") " --------"}, + {"cformat", CmdNFCMFCFormat, IfPm3Iso14443a, "format MIFARE Classic Tag as NFC Tag"}, {"cread", CmdNFCMFCRead, IfPm3Iso14443a, "read NFC Type MIFARE Classic Tag"}, -// {"cwrite", CmdNFCMFCWrite, IfPm3Iso14443a, "write NFC Type MIFARE Classic Tag"}, -// {"cformat", CmdNFCMFCFormat, IfPm3Iso14443a, "format MIFARE Classic Tag as NFC Tag"}, + {"cwrite", CmdNFCMFCWrite, IfPm3Iso14443a, "write NFC Type MIFARE Classic Tag"}, {"pread", CmdNFCMFPRead, IfPm3Iso14443a, "read NFC Type MIFARE Plus Tag"}, {"--------", CmdNFCMFHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdNFCMFHelp, AlwaysAvailable, "This help"}, @@ -387,6 +410,7 @@ static command_t CommandTable[] = { {"--------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"decode", CmdNfcDecode, AlwaysAvailable, "Decode NDEF records"}, +// {"encode", CmdNfcEncode, AlwaysAvailable, "Encode NDEF records"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 96c5f0fb3..fe7243c5e 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -25,6 +25,9 @@ #include "comms.h" #include "util_posix.h" // msleep + +#define MAX_PM3_INPUT_ARGS_LENGTH 4096 + bool AlwaysAvailable(void) { return true; } @@ -36,54 +39,55 @@ bool IfPm3Present(void) { } bool IfPm3Rdv4Fw(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; - return (g_pm3_capabilities.compiled_with_flash) || (g_pm3_capabilities.compiled_with_smartcard); + return (g_pm3_capabilities.is_rdv4); } bool IfPm3Flash(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; - if (!g_pm3_capabilities.compiled_with_flash) + if (g_pm3_capabilities.compiled_with_flash == false) return false; return g_pm3_capabilities.hw_available_flash; } bool IfPm3Smartcard(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; - if (!g_pm3_capabilities.compiled_with_smartcard) + if (g_pm3_capabilities.compiled_with_smartcard == false) return false; return g_pm3_capabilities.hw_available_smartcard; } bool IfPm3FpcUsart(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_fpc_usart; } bool IfPm3FpcUsartHost(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_fpc_usart_host; } bool IfPm3FpcUsartHostFromUsb(void) { // true if FPC USART Host support and if talking from USB-CDC interface - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; - if (!g_pm3_capabilities.compiled_with_fpc_usart_host) + if (g_pm3_capabilities.compiled_with_fpc_usart_host == false) return false; return !g_conn.send_via_fpc_usart; } bool IfPm3FpcUsartDevFromUsb(void) { // true if FPC USART developer support and if talking from USB-CDC interface - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; - if (!g_pm3_capabilities.compiled_with_fpc_usart_dev) + if (g_pm3_capabilities.compiled_with_fpc_usart_dev == false) return false; + return !g_conn.send_via_fpc_usart; } @@ -93,103 +97,101 @@ bool IfPm3FpcUsartFromUsb(void) { } bool IfPm3Lf(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_lf; } bool IfPm3Hitag(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_hitag; } bool IfPm3EM4x50(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_em4x50; } bool IfPm3EM4x70(void) { - - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_em4x70; } bool IfPm3Hfsniff(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_hfsniff; } bool IfPm3Hfplot(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_hfplot; } bool IfPm3Iso14443a(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_iso14443a; } bool IfPm3Iso14443b(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_iso14443b; } bool IfPm3Iso14443(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_iso14443a || g_pm3_capabilities.compiled_with_iso14443b; } bool IfPm3Iso15693(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_iso15693; } bool IfPm3Felica(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_felica; } bool IfPm3Legicrf(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_legicrf; } bool IfPm3Iclass(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_iclass; } bool IfPm3NfcBarcode(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_nfcbarcode; } bool IfPm3Lcd(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_lcd; } bool IfPm3Zx8211(void) { - if (!IfPm3Present()) + if (IfPm3Present() == false) return false; return g_pm3_capabilities.compiled_with_zx8211; } - void CmdsHelp(const command_t Commands[]) { if (Commands[0].Name == NULL) return; int i = 0; @@ -259,7 +261,7 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { } - char cmd_name[128]; + char cmd_name[128] = {0}; memset(cmd_name, 0, sizeof(cmd_name)); int len = 0; @@ -275,8 +277,10 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { // find args, check for -h / --help int tmplen = len; - while (Cmd[tmplen] == ' ') + while (Cmd[tmplen] == ' ') { ++tmplen; + } + bool request_help = (strcmp(Cmd + tmplen, "-h") == 0) || (strcmp(Cmd + tmplen, "--help") == 0); int i = 0; @@ -305,12 +309,15 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { matches++; } } - if (matches == 1) i = last_match; + if (matches == 1) { + i = last_match; + } } if (Commands[i].Name) { - while (Cmd[len] == ' ') + while (Cmd[len] == ' ') { ++len; + } return Commands[i].Parse(Cmd + len); } else { // show help for selected hierarchy or if command not recognised @@ -320,7 +327,7 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { return PM3_SUCCESS; } -static char pparent[512] = {0}; +static char pparent[MAX_PM3_INPUT_ARGS_LENGTH] = {0}; static char *parent = pparent; void dumpCommandsRecursive(const command_t cmds[], int markdown, bool full_help) { @@ -346,11 +353,13 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown, bool full_help) const char *cmd_offline = "N"; - if (cmds[i].IsAvailable()) + if (cmds[i].IsAvailable()) { cmd_offline = "Y"; - if (markdown) + } + + if (markdown) { PrintAndLogEx(NORMAL, "|`%s%-*s`|%-*s|`%s`", parent, w_cmd - (int)strlen(parent) - 2, cmds[i].Name, w_off, cmd_offline, cmds[i].Help); - else if (full_help) { + } else if (full_help) { PrintAndLogEx(NORMAL, "---------------------------------------------------------------------------------------"); PrintAndLogEx(NORMAL, _RED_("%s%-*s\n") "available offline: %s", parent, w_cmd - (int)strlen(parent), cmds[i].Name, cmds[i].IsAvailable() ? _GREEN_("yes") : _RED_("no")); cmds[i].Parse("--help"); @@ -374,15 +383,17 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown, bool full_help) } else { PrintAndLogEx(NORMAL, "### %s%s\n\n %s\n", parent, cmds[i].Name, cmds[i].Help); } - char currentparent[512] = {0}; + + char currentparent[MAX_PM3_INPUT_ARGS_LENGTH] = {0}; snprintf(currentparent, sizeof currentparent, "%s%s ", parent, cmds[i].Name); + char *old_parent = parent; parent = currentparent; // This is what causes the recursion, since commands Parse-implementation // in turn calls the CmdsParse above. - if (markdown) + if (markdown) { cmds[i].Parse("XX_internal_command_dump_markdown_XX"); - else if (full_help) { + } else if (full_help) { cmds[i].Parse("XX_internal_command_dump_full_XX"); } else { cmds[i].Parse("XX_internal_command_dump_XX"); diff --git a/client/src/cmdpiv.c b/client/src/cmdpiv.c new file mode 100644 index 000000000..8264c5936 --- /dev/null +++ b/client/src/cmdpiv.c @@ -0,0 +1,992 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// PIV commands +//----------------------------------------------------------------------------- + +#include "cmdpiv.h" + +#include "comms.h" // DropField +#include "cmdsmartcard.h" // smart_select +#include "cmdtrace.h" +#include "cliparser.h" +#include "cmdparser.h" +#include "commonutil.h" // Mem[LB]eToUintXByte +#include "emv/tlv.h" +#include "proxmark3.h" +#include "cmdhf14a.h" +#include "fileutils.h" +#include "crypto/asn1utils.h" +#include "protocols.h" + +static int CmdHelp(const char *Cmd); + +static uint8_t PIV_APPLET[9] = "\xA0\x00\x00\x03\x08\x00\x00\x10\x00"; + +enum piv_condition_t { + PIV_MANDATORY, + PIV_CONDITIONAL, + PIV_OPTIONAL, + PIV_INVALID = 0xff, +}; + +struct piv_container { + uint32_t id; + const uint8_t *tlv_tag; // tag is between 1 and 3 bytes. + size_t len; // length of the hex-form if the tag (i.e. twice the byte size) for pretty printing + enum piv_condition_t cond; + const char *name; +}; + +#define PIV_TAG_ID(x) ((const uint8_t *)(x)) +#define PIV_CONTAINER_FINISH { (~0), NULL, 0, PIV_INVALID, NULL } + +// Source: SP800-73-4, Annex A +// https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-73-4.pdf +static const struct piv_container PIV_CONTAINERS[] = { + {0xDB00, PIV_TAG_ID("\x5F\xC1\x07"), 3, PIV_MANDATORY, "Card Capability Container"}, + {0x3000, PIV_TAG_ID("\x5F\xC1\x02"), 3, PIV_MANDATORY, "Card Holder Unique Identifier"}, + {0x0101, PIV_TAG_ID("\x5F\xC1\x05"), 3, PIV_MANDATORY, "X.509 Certificate for PIV Authentication (key ref 9A)"}, + {0x6010, PIV_TAG_ID("\x5F\xC1\x03"), 3, PIV_MANDATORY, "Cardholder Fingerprints"}, + {0x9000, PIV_TAG_ID("\x5F\xC1\x06"), 3, PIV_MANDATORY, "Security Object"}, + {0x6030, PIV_TAG_ID("\x5F\xC1\x08"), 3, PIV_MANDATORY, "Cardholder Facial Image"}, + {0x0500, PIV_TAG_ID("\x5F\xC1\x01"), 3, PIV_MANDATORY, "X.509 Certificate for Card Authentication (key ref 9E)"}, + {0x0100, PIV_TAG_ID("\x5F\xC1\x0A"), 3, PIV_CONDITIONAL, "X.509 Certificate for Digital Signature (key ref 9C)"}, + {0x0102, PIV_TAG_ID("\x5F\xC1\x0B"), 3, PIV_CONDITIONAL, "X.509 Certificate for Key Management (key ref 9D)"}, + {0x3001, PIV_TAG_ID("\x5F\xC1\x09"), 3, PIV_OPTIONAL, "Printed Information"}, + {0x6050, PIV_TAG_ID("\x7E"), 1, PIV_OPTIONAL, "Discovery Object"}, + {0x6060, PIV_TAG_ID("\x5F\xC1\x0C"), 3, PIV_OPTIONAL, "Key History Object"}, + {0x1001, PIV_TAG_ID("\x5F\xC1\x0D"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 1 (key ref 82)"}, + {0x1002, PIV_TAG_ID("\x5F\xC1\x0E"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 2 (key ref 83)"}, + {0x1003, PIV_TAG_ID("\x5F\xC1\x0F"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 3 (key ref 84)"}, + {0x1004, PIV_TAG_ID("\x5F\xC1\x10"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 4 (key ref 85)"}, + {0x1005, PIV_TAG_ID("\x5F\xC1\x11"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 5 (key ref 86)"}, + {0x1006, PIV_TAG_ID("\x5F\xC1\x12"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 6 (key ref 87)"}, + {0x1007, PIV_TAG_ID("\x5F\xC1\x13"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 7 (key ref 88)"}, + {0x1008, PIV_TAG_ID("\x5F\xC1\x14"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 8 (key ref 89)"}, + {0x1009, PIV_TAG_ID("\x5F\xC1\x15"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 9 (key ref 8A)"}, + {0x100A, PIV_TAG_ID("\x5F\xC1\x16"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 10 (key ref 8B)"}, + {0x100B, PIV_TAG_ID("\x5F\xC1\x17"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 11 (key ref 8C)"}, + {0x100C, PIV_TAG_ID("\x5F\xC1\x18"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 12 (key ref 8D)"}, + {0x100D, PIV_TAG_ID("\x5F\xC1\x19"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 13 (key ref 8E)"}, + {0x100E, PIV_TAG_ID("\x5F\xC1\x1A"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 14 (key ref 8F)"}, + {0x100F, PIV_TAG_ID("\x5F\xC1\x1B"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 15 (key ref 90)"}, + {0x1010, PIV_TAG_ID("\x5F\xC1\x1C"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 16 (key ref 91)"}, + {0x1011, PIV_TAG_ID("\x5F\xC1\x1D"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 17 (key ref 92)"}, + {0x1012, PIV_TAG_ID("\x5F\xC1\x1E"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 18 (key ref 93)"}, + {0x1013, PIV_TAG_ID("\x5F\xC1\x1F"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 19 (key ref 94)"}, + {0x1014, PIV_TAG_ID("\x5F\xC1\x20"), 3, PIV_OPTIONAL, "Retired X.509 Certificate for Key Management 20 (key ref 95)"}, + {0x1015, PIV_TAG_ID("\x5F\xC1\x21"), 3, PIV_OPTIONAL, "Cardholder Iris Images"}, + {0x1016, PIV_TAG_ID("\x7F\x61"), 2, PIV_OPTIONAL, "Biometric Information Templates Group Template"}, + {0x1017, PIV_TAG_ID("\x5F\xC1\x22"), 3, PIV_OPTIONAL, "Secure Messaging Certificate Signer"}, + {0x1018, PIV_TAG_ID("\x5F\xC1\x23"), 3, PIV_OPTIONAL, "Pairing Code Reference Data Container"}, + PIV_CONTAINER_FINISH, +}; + +enum piv_tag_t { + PIV_TAG_GENERIC, + PIV_TAG_HEXDUMP, + PIV_TAG_STRING, + PIV_TAG_PRINTSTR, + PIV_TAG_NUMERIC, + PIV_TAG_YYYYMMDD, + PIV_TAG_ENUM, + PIV_TAG_TLV, + PIV_TAG_GUID, + PIV_TAG_CERT, + PIV_TAG_FASCN, +}; + +struct piv_tag { + tlv_tag_t tag; + const char *name; + enum piv_tag_t type; + const void *data; +}; + +struct piv_tag_enum { + unsigned long value; + const char *name; +}; + +#define PIV_ENUM_FINISH { (~0), NULL } + +// From table 6-2 in SP800-78 specification +static const struct piv_tag_enum PIV_CRYPTO_ALG[] = { + {0x00, "3 Key 3DES - ECB"}, + {0x03, "3 Key 3DES - ECB"}, // Not a typo, 2 identifiers for the same algorithm + {0x06, "RSA 1024 bit"}, + {0x07, "RSA 2048 bit"}, + {0x08, "AES-128 ECB"}, + {0x0A, "AES-192 ECB"}, + {0x0C, "AES-256 ECB"}, + {0x11, "ECC P-256"}, + {0x14, "ECC P-384"}, + {0x27, "Cipher Suite 2"}, + {0x2E, "Cipher Suite 7"}, + PIV_ENUM_FINISH, +}; + +static const struct piv_tag_enum PIV_CERT_INFO[] = { + {0x00, "Uncompressed"}, + {0x01, "GZIP Compressed"}, + PIV_ENUM_FINISH, +}; + +static const struct piv_tag piv_tags[] = { + { 0x00, "Unknown ???", PIV_TAG_HEXDUMP, NULL }, + { 0x01, "Name", PIV_TAG_PRINTSTR, NULL }, + { 0x02, "Employee Affiliation", PIV_TAG_PRINTSTR, NULL }, + { 0x04, "Expiry Date", PIV_TAG_PRINTSTR, NULL }, + { 0x05, "Agency Card Serial Number", PIV_TAG_PRINTSTR, NULL }, + { 0x06, "Issuer identification", PIV_TAG_PRINTSTR, NULL }, + { 0x07, "Organization Affiliation (Line 1)", PIV_TAG_PRINTSTR, NULL }, + { 0x08, "Organization Affiliation (Line 2)", PIV_TAG_PRINTSTR, NULL }, + + { 0x30, "FASC-N", PIV_TAG_FASCN, NULL }, + { 0x32, "Organizational Identifier [deprecated]", PIV_TAG_HEXDUMP, NULL }, + { 0x33, "DUNS [deprecated]", PIV_TAG_HEXDUMP, NULL }, + { 0x34, "GUID", PIV_TAG_GUID, NULL }, + { 0x35, "Expiry Date", PIV_TAG_YYYYMMDD, NULL }, + { 0x36, "Cardholder UUID", PIV_TAG_GUID, NULL }, + { 0x3d, "Authentication Key Map", PIV_TAG_HEXDUMP, NULL }, + { 0x3e, "Issuer Asymmetric Signature", PIV_TAG_CERT, NULL }, + + { 0x4f, "Application Identifier (AID)", PIV_TAG_STRING, NULL }, + + { 0x50, "Application Label", PIV_TAG_PRINTSTR, NULL }, + { 0x53, "Discretionary data (or template)", PIV_TAG_TLV, NULL }, + { 0x5f2f, "PIN Usage Policy", PIV_TAG_HEXDUMP, NULL }, + { 0x5f50, "Issuer URL", PIV_TAG_PRINTSTR, NULL }, + + { 0x61, "Application Property Template", PIV_TAG_GENERIC, NULL }, + + { 0x70, "Certificate", PIV_TAG_CERT, NULL }, + { 0x71, "CertInfo", PIV_TAG_ENUM, PIV_CERT_INFO }, + { 0x72, "MSCUID [deprecated]", PIV_TAG_HEXDUMP, NULL }, + { 0x79, "Coexistent tag allocation authority", PIV_TAG_HEXDUMP, NULL }, + { 0x7f21, "Intermediate CVC", PIV_TAG_HEXDUMP, NULL }, + { 0x7f60, "Biometric Information Template", PIV_TAG_GENERIC, NULL }, + + { 0x80, "Cryptographic algorithm identifier", PIV_TAG_ENUM, PIV_CRYPTO_ALG }, + + { 0x99, "Pairing Code", PIV_TAG_PRINTSTR, NULL }, + + { 0xac, "Cryptographic algorithms supported", PIV_TAG_GENERIC, NULL }, + + { 0xb4, "Security Object Buffer (deprecated)", PIV_TAG_GENERIC, NULL }, + { 0xba, "Mapping of DG to Container ID", PIV_TAG_HEXDUMP, NULL }, + { 0xbb, "Security Object", PIV_TAG_CERT, NULL }, + { 0xbc, "Fingerprint I & II or Image for Visual Verification", PIV_TAG_GENERIC, NULL }, + + { 0xc1, "keysWithOnCardCerts", PIV_TAG_NUMERIC, NULL }, + { 0xc2, "keysWithOffCardCerts", PIV_TAG_NUMERIC, NULL }, + + { 0xe3, "Extended Application CardURL [deprecated]", PIV_TAG_GENERIC, NULL }, + { 0xee, "Buffer Length [deprecated]", PIV_TAG_NUMERIC, NULL }, + + { 0xf0, "Card Identifier", PIV_TAG_STRING, NULL }, + { 0xf1, "Capability Container version number", PIV_TAG_NUMERIC, NULL }, + { 0xf2, "Capability Grammar version number", PIV_TAG_NUMERIC, NULL }, + { 0xf3, "Application Card URL", PIV_TAG_PRINTSTR, NULL }, + { 0xf4, "PKCS#15", PIV_TAG_NUMERIC, NULL }, + { 0xf5, "Registered Data Model Number", PIV_TAG_NUMERIC, NULL }, + { 0xf6, "Access Control Rule Table", PIV_TAG_HEXDUMP, NULL }, + { 0xf7, "Card APDUs", PIV_TAG_GENERIC, NULL }, + { 0xfa, "Redirection Tag", PIV_TAG_GENERIC, NULL }, + { 0xfb, "Capability Tuples (CT)", PIV_TAG_GENERIC, NULL }, + { 0xfc, "Status Tuples (ST)", PIV_TAG_GENERIC, NULL }, + { 0xfd, "Next CCC", PIV_TAG_GENERIC, NULL }, + { 0xfe, "Error Detection Code", PIV_TAG_GENERIC, NULL }, +}; + +struct guid { + uint32_t part1; + uint16_t part2; + uint16_t part3; + uint8_t data[8]; +}; + +static void parse_guid(const uint8_t *data, struct guid *guid) { + if (guid == NULL) { + return; + } + size_t ofs = 0; + guid->part1 = MemBeToUint4byte(&data[ofs]); + ofs += sizeof(uint32_t); + guid->part2 = MemBeToUint2byte(&data[ofs]); + ofs += sizeof(uint16_t); + guid->part3 = MemBeToUint2byte(&data[ofs]); + ofs += sizeof(uint16_t); + for (size_t i = 0; i < sizeof(guid->data); i++) { + guid->data[i] = data[ofs + i]; + } +} + +static void piv_print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf); +static bool piv_tag_dump(const struct tlv *tlv, int level); + +static void PrintChannel(Iso7816CommandChannel channel) { + switch (channel) { + case CC_CONTACTLESS: + PrintAndLogEx(INFO, "Selected channel... " _GREEN_("CONTACTLESS (T=CL)")); + break; + case CC_CONTACT: + PrintAndLogEx(INFO, "Selected channel... " _GREEN_("CONTACT")); + break; + } +} + +static int piv_sort_tag(tlv_tag_t tag) { + return (int)(tag >= 0x100 ? tag : tag << 8); +} + +static int piv_tlv_compare(const void *a, const void *b) { + const struct tlv *tlv = a; + const struct piv_tag *tag = b; + + return piv_sort_tag(tlv->tag) - (piv_sort_tag(tag->tag)); +} + +static const struct piv_tag *piv_get_tag(const struct tlv *tlv) { + const struct piv_tag *tag = bsearch(tlv, piv_tags, ARRAYLEN(piv_tags), + sizeof(piv_tags[0]), piv_tlv_compare); + return tag != NULL ? tag : &piv_tags[0]; +} + +static unsigned long piv_value_numeric(const struct tlv *tlv, unsigned start, unsigned end) { + unsigned long ret = 0; + int i; + + if (end > tlv->len * 2) + return ret; + if (start >= end) + return ret; + + if (start & 1) { + ret += tlv->value[start / 2] & 0xf; + i = start + 1; + } else + i = start; + + for (; i < end - 1; i += 2) { + ret *= 10; + ret += tlv->value[i / 2] >> 4; + ret *= 10; + ret += tlv->value[i / 2] & 0xf; + } + + if (end & 1) { + ret *= 10; + ret += tlv->value[end / 2] >> 4; + } + + return ret; +} + +static void piv_tag_dump_yyyymmdd(const struct tlv *tlv, const struct piv_tag *tag, int level) { + bool is_printable = true; + for (size_t i = 0; i < tlv->len; i++) { + if ((tlv->value[i] < 0x30) || (tlv->value[i] > 0x39)) { + is_printable = false; + break; + } + } + if (is_printable) { + PrintAndLogEx(NORMAL, " " _YELLOW_("%c%c%c%c.%c%c.%c%c"), + tlv->value[0], tlv->value[1], tlv->value[2], tlv->value[3], + tlv->value[4], tlv->value[5], + tlv->value[6], tlv->value[7] + ); + } else { + PrintAndLogEx(NORMAL, " " _YELLOW_("%04lu.%02lu.%02lu"), + piv_value_numeric(tlv, 0, 4), + piv_value_numeric(tlv, 4, 6), + piv_value_numeric(tlv, 6, 8) + ); + } +} + +static void piv_tag_dump_enum(const struct tlv *tlv, const struct piv_tag *tag, int level) { + const struct piv_tag_enum *values = tag->data; + for (size_t i = 0; values[i].name != NULL; i++) { + if (values[i].value == tlv->value[0]) { + PrintAndLogEx(NORMAL, " %u - '" _YELLOW_("%s")"'", + tlv->value[0], values[i].name); + return; + } + } + PrintAndLogEx(NORMAL, " %u - " _RED_("Unknown??"), tlv->value[0]); +} + +static void piv_tag_dump_tlv(const struct tlv *tlv, const struct piv_tag *tag, int level) { + // We don't use parsing methods because we need to discard constructed tags + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + + while (left) { + struct tlv sub_tlv; + //const struct piv_tag *sub_tag; + + if (!tlv_parse_tl(&buf, &left, &sub_tlv)) { + PrintAndLogEx(INFO, "%*sInvalid Tag-Len", (level * 4), " "); + continue; + } + sub_tlv.value = buf; + piv_tag_dump(&sub_tlv, level + 1); + buf += sub_tlv.len; + left -= sub_tlv.len; + } + +} + +static void piv_print_cert(const uint8_t *buf, const size_t len, int level) { + char prefix[256] = {0}; + PrintAndLogEx(NORMAL, ""); + snprintf(prefix, sizeof(prefix), "%*s", 4 * level, " "); + // TODO: when mbedTLS has a new release with the PCKS7 parser, we can replace the generic ASN.1 print + // The pull request has been merged end of Nov 2022. + asn1_print((uint8_t *) buf, len, prefix); +} + +static void piv_print_fascn(const uint8_t *buf, const size_t len, int level) { + const char *encoded[32] = { + _RED_("?"), // 0b00000 + "0", // 0b00001 + "8", // 0b00010 + _RED_("?"), // 0b00011 + "4", // 0b00100 + _RED_("?"), // 0b00101 + _RED_("?"), // 0b00110 + _RED_("?"), // 0b00111 + "2", // 0b01000 + _RED_("?"), // 0b01001 + _RED_("?"), // 0b01010 + _RED_("?"), // 0b01011 + _RED_("?"), // 0b01100 + "6", // 0b01101 + _RED_("?"), // 0b01110 + _RED_("?"), // 0b01111 + "1", // 0b10000 + _RED_("?"), // 0b10001 + _RED_("?"), // 0b10010 + "9", // 0b10011 + _RED_("?"), // 0b10100 + "5", // 0b10101 + _GREEN_(" FS "), // 0b10110 + _RED_("?"), // 0b10111 + _RED_("?"), // 0b11000 + "3", // 0b11001 + _YELLOW_("SS "), // 0b11010 + _RED_("?"), // 0b11011 + "7", // 0b11100 + _RED_("?"), // 0b11101 + _RED_("?"), // 0b11110 + _YELLOW_(" ES"), // 0b11111 + }; + const uint8_t cycle[8] = {5, 2, 7, 4, 1, 6, 3, 8}; + + PrintAndLogEx(INFO, "%*s" NOLF, 4 * level, " "); + // Buffer is 40 bytes but last byte is LRC that we process separately + for (int i = 0; i < 39; i++) { + uint8_t tmp = buf[(5 * i) >> 3]; + uint8_t rot = cycle[i & 7]; + // rotate left to get the bits in place + tmp = (tmp << rot) | (tmp >> (8 - rot)); + // append bits from next byte if needed + if (rot < 5) { + uint8_t tmp2 = buf[(5 * (i + 1)) >> 3]; + tmp2 = (tmp2 << rot) | (tmp2 >> (8 - rot)); + tmp &= 0x1f << rot; + tmp |= tmp2 & ((1 << rot) - 1); + } + PrintAndLogEx(NORMAL, "%s" NOLF, encoded[tmp & 0x1f]); + } + uint8_t lrc = buf[24] & 0x1f; + PrintAndLogEx(NORMAL, " LRC=[" _YELLOW_("%02x") "]", lrc); +} + +static bool piv_tag_dump(const struct tlv *tlv, int level) { + if (tlv == NULL) { + PrintAndLogEx(FAILED, "NULL"); + return false; + } + + const struct piv_tag *tag = piv_get_tag(tlv); + + PrintAndLogEx(INFO, "%*s--%2x[%02zx] '%s':" NOLF, (level * 4), " ", tlv->tag, tlv->len, tag->name); + + switch (tag->type) { + case PIV_TAG_GENERIC: + PrintAndLogEx(NORMAL, ""); + break; + case PIV_TAG_HEXDUMP: + PrintAndLogEx(NORMAL, ""); + print_buffer(tlv->value, tlv->len, level + 1); + break; + case PIV_TAG_STRING: + PrintAndLogEx(NORMAL, " '" _YELLOW_("%s")"'", sprint_hex_inrow(tlv->value, tlv->len)); + break; + case PIV_TAG_NUMERIC: + PrintAndLogEx(NORMAL, " " _YELLOW_("%lu"), piv_value_numeric(tlv, 0, tlv->len * 2)); + break; + case PIV_TAG_YYYYMMDD: + piv_tag_dump_yyyymmdd(tlv, tag, level); + break; + case PIV_TAG_ENUM: + piv_tag_dump_enum(tlv, tag, level + 1); + break; + case PIV_TAG_TLV: + PrintAndLogEx(NORMAL, ""); + piv_tag_dump_tlv(tlv, tag, level); + break; + case PIV_TAG_PRINTSTR: + PrintAndLogEx(NORMAL, " '" NOLF); + for (size_t i = 0; i < tlv->len; i++) { + PrintAndLogEx(NORMAL, _YELLOW_("%c") NOLF, tlv->value[i]); + } + PrintAndLogEx(NORMAL, "'"); + break; + case PIV_TAG_GUID: + if (tlv->len != 16) { + PrintAndLogEx(NORMAL, _RED_("")); + } else { + struct guid guid = {0}; + parse_guid(tlv->value, &guid); + PrintAndLogEx(NORMAL, " " _YELLOW_("{%08x-%04x-%04x-") NOLF, guid.part1, guid.part2, guid.part3); + for (size_t i = 0; i < 8; i++) { + PrintAndLogEx(NORMAL, _YELLOW_("%02x") NOLF, guid.data[i]); + } + PrintAndLogEx(NORMAL, _YELLOW_("}")); + } + break; + case PIV_TAG_CERT: + piv_print_cert(tlv->value, tlv->len, level + 2); + break; + case PIV_TAG_FASCN: + PrintAndLogEx(NORMAL, " '" _YELLOW_("%s")"'", sprint_hex_inrow(tlv->value, tlv->len)); + if (tlv->len == 25) { + piv_print_fascn(tlv->value, tlv->len, level + 2); + } + break; + }; + + return true; +} + +static void piv_print_cb(void *data, const struct tlv *tlv, int level, bool is_leaf) { + piv_tag_dump(tlv, level); + if (is_leaf) { + print_buffer(tlv->value, tlv->len, level); + } +} + +static void PrintTLV(const struct tlvdb *tlvdb) { + if (tlvdb) { + tlvdb_visit(tlvdb, piv_print_cb, NULL, 0); + } +} + +static void PrintTLVFromBuffer(const uint8_t *buf, size_t len) { + if (buf == NULL || len == 0) { + return; + } + struct tlvdb_root *root = calloc(1, sizeof(*root) + len); + if (root == NULL) { + return; + } + root->len = len; + memcpy(root->buf, buf, len); + if (tlvdb_parse_root_multi(root) == true) { + PrintTLV(&(root->db)); + } else { + PrintAndLogEx(WARNING, "TLV ERROR: Can't parse buffer as TLV tree."); + } + tlvdb_root_free(root); +} + +static int PivGetData(Iso7816CommandChannel channel, const uint8_t tag[], size_t tag_len, bool verbose, struct tlvdb_root **result, uint16_t *sw) { + uint8_t apdu_data[5] = {0x5c, 0x00}; + + *result = NULL; + *sw = 0; + + if (tag_len < 1 || tag_len > 3) { + return PM3_EINVARG; + } + + apdu_data[1] = tag_len; + memcpy(&apdu_data[2], tag, tag_len); + + sAPDU_t apdu = { + .CLA = 0x00, + .INS = 0xCB, + .P1 = 0x3F, + .P2 = 0xFF, + .Lc = tag_len + 2, + .data = apdu_data + }; + + // Answer can be chained. Let's use a dynamically allocated buffer. + size_t capacity = PM3_CMD_DATA_SIZE; + struct tlvdb_root *root = calloc(1, sizeof(*root) + capacity); + + if (root == NULL) { + return PM3_EMALLOC; + } + root->len = 0; + + size_t more_data = 0; + do { + size_t received = 0; + int res = Iso7816ExchangeEx(channel, false, true, apdu, (more_data != 0), more_data, &(root->buf[root->len]), capacity - root->len, &received, sw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Sending APDU failed with code %d", res); + free(root); + return res; + } + root->len += received; + if (((*sw) & 0xff00) == 0x6100) { + // More data + more_data = (*sw) & 0xff; + if (more_data == 0x00 || more_data > MAX_APDU_SIZE) { + more_data = MAX_APDU_SIZE; + } + apdu.CLA = 0x00; + apdu.INS = 0xC0; + apdu.P1 = 0x00; + apdu.P2 = 0x00; + apdu.Lc = 0; + apdu.data = NULL; + if ((capacity - root->len) < PM3_CMD_DATA_SIZE) { + PrintAndLogEx(DEBUG, "Adding more capacity to buffer..."); + capacity += PM3_CMD_DATA_SIZE; + struct tlvdb_root *new_root = realloc(root, sizeof(*root) + capacity); + if (new_root == NULL) { + PrintAndLogEx(FAILED, "Running out of memory while re-allocating buffer"); + //free(root); + tlvdb_root_free(root); + return PM3_EMALLOC; + } + root = new_root; + } + } + if ((*sw) == ISO7816_OK) { + more_data = 0; + } + } while (more_data > 0); + + // Now we can try parse the TLV and return it + *result = root; + if (*sw == ISO7816_OK && tlvdb_parse_root(root) == true) { + return PM3_SUCCESS; + } + if (verbose == true) { + PrintAndLogEx(WARNING, "Couldn't parse TLV answer."); + } + return PM3_SUCCESS; +} + +static int PivGetDataByCidAndPrint(Iso7816CommandChannel channel, const struct piv_container *cid, bool decodeTLV, bool verbose) { + struct tlvdb_root *root = NULL; + + if (cid == NULL) { + return PM3_SUCCESS; + } + + PrintAndLogEx(INFO, "Getting %s [" _GREEN_("%s") "]", cid->name, sprint_hex_inrow(cid->tlv_tag, cid->len)); + + uint16_t sw = 0; + + if (PivGetData(channel, cid->tlv_tag, cid->len, verbose, &root, &sw) == PM3_SUCCESS) { + switch (sw) { + case ISO7816_OK: + if (decodeTLV == true) { + PrintTLV(&(root->db)); + } else { + print_buffer(root->buf, root->len, 0); + } + break; + case ISO7816_FILE_NOT_FOUND: + PrintAndLogEx(FAILED, "Container not found."); + break; + case ISO7816_SECURITY_STATUS_NOT_SATISFIED: + PrintAndLogEx(WARNING, "Security conditions not met."); + break; + default: + if (verbose == true) { + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + } + break; + } + tlvdb_root_free(root); + } + return PM3_SUCCESS; +} + +static int PivGetDataByTagAndPrint(Iso7816CommandChannel channel, const uint8_t tag[], size_t tag_len, bool decodeTLV, bool verbose) { + int idx = 0; + + for (; PIV_CONTAINERS[idx].len != 0; idx++) { + if ((tag_len == PIV_CONTAINERS[idx].len) && (memcmp(tag, PIV_CONTAINERS[idx].tlv_tag, tag_len) == 0)) { + break; + } + } + if (PIV_CONTAINERS[idx].len == 0) { + struct piv_container cid = {0x00, tag, tag_len, PIV_OPTIONAL, "Getting unknown contained ID"}; + return PivGetDataByCidAndPrint(channel, &cid, decodeTLV, verbose); + } + return PivGetDataByCidAndPrint(channel, &(PIV_CONTAINERS[idx]), decodeTLV, verbose); +} + +static int PivAuthenticateSign(Iso7816CommandChannel channel, uint8_t alg_id, uint8_t key_id, uint8_t nonce[], size_t nonce_len, void **result, bool decodeTLV, bool verbose) { + const size_t MAX_NONCE_LEN = 0x7a; + if (nonce_len > MAX_NONCE_LEN) { + if (verbose == true) { + PrintAndLogEx(WARNING, "Nonce cannot exceed %zu bytes. Got %zu bytes.", MAX_NONCE_LEN, nonce_len); + } + return PM3_EINVARG; + } + uint8_t apdu_buf[APDU_RES_LEN] = {0x7c, nonce_len + 4, 0x82, 0x00, 0x81, nonce_len}; + memcpy(&apdu_buf[6], nonce, nonce_len); + sAPDU_t apdu = { + 0x00, 0x87, alg_id, key_id, + 6 + nonce_len, apdu_buf + }; + + uint16_t sw = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + int res = Iso7816ExchangeEx(channel, false, true, apdu, false, 0, buf, APDU_RES_LEN, &len, &sw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Sending APDU failed with code %d", res); + return res; + } + if (sw != ISO7816_OK) { + if (verbose == true) { + PrintAndLogEx(INFO, "Unexpected APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + } + return PM3_EFAILED; + } + if (verbose == true) { + if (decodeTLV == true) { + PrintTLVFromBuffer(buf, len); + } else { + print_buffer(buf, len, 0); + } + } + return PM3_SUCCESS; +} + +static int PivSelect(Iso7816CommandChannel channel, bool activateField, bool leaveFieldOn, bool decodeTLV, bool silent, uint8_t applet[], size_t appletLen) { + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + + int res = Iso7816Select(channel, activateField, leaveFieldOn, applet, appletLen, buf, sizeof(buf), &len, &sw); + if ((sw != 0) && (silent == false)) { + PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + } + + if (res != PM3_SUCCESS || sw != ISO7816_OK) { + PrintAndLogEx(FAILED, "Applet selection failed. Card is not a PIV card."); + return res; + } + + if (silent == false) { + if (decodeTLV == true) { + PrintTLVFromBuffer(buf, len); + } else { + print_buffer(buf, len, 0); + } + } + return PM3_SUCCESS; +} + +static int CmdPIVSelect(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "piv select", + "Executes select applet command", + "piv select -s -> select card, select applet\n" + "piv select -st --aid a00000030800001000 -> select card, select applet a00000030800001000, show result in TLV\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "Activate field and select applet"), + arg_lit0("kK", "keep", "Keep field for next command"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_str0(NULL, "aid", "", "Applet ID to select. By default A0000003080000100 will be used"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool activateField = arg_get_lit(ctx, 1); + bool leaveSignalON = arg_get_lit(ctx, 2); + bool APDULogging = arg_get_lit(ctx, 3); + bool decodeTLV = arg_get_lit(ctx, 4); + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 5)) { + channel = CC_CONTACT; + } + + PrintChannel(channel); + + uint8_t applet_id[APDU_AID_LEN] = {0}; + int aid_len = 0; + CLIGetHexWithReturn(ctx, 6, applet_id, &aid_len); + if (aid_len == 0) { + memcpy(applet_id, PIV_APPLET, sizeof(PIV_APPLET)); + aid_len = sizeof(PIV_APPLET); + } + + CLIParserFree(ctx); + + SetAPDULogging(APDULogging); + + return PivSelect(channel, activateField, leaveSignalON, decodeTLV, false, applet_id, aid_len); +} + +static int CmdPIVGetData(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "piv getdata", + "Get a data container of a given tag", + "piv getdata -s 5fc102 -> select card, select applet, get card holder unique identifer\n" + "piv getdata -st 5fc102 -> select card, select applet, get card holder unique identifer, show result in TLV\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "Activate field and select applet"), + arg_lit0("kK", "keep", "Keep field for next command"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_str0(NULL, "aid", "", "Applet ID to select. By default A0000003080000100 will be used"), + arg_str1(NULL, NULL, "", "Tag ID to read, between 1 and 3 bytes."), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool activateField = arg_get_lit(ctx, 1); + bool leaveSignalON = arg_get_lit(ctx, 2); + bool APDULogging = arg_get_lit(ctx, 3); + bool decodeTLV = arg_get_lit(ctx, 4); + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 5)) + channel = CC_CONTACT; + PrintChannel(channel); + + uint8_t applet_id[APDU_AID_LEN] = {0}; + int aid_len = 0; + CLIGetHexWithReturn(ctx, 6, applet_id, &aid_len); + if (aid_len == 0) { + memcpy(applet_id, PIV_APPLET, sizeof(PIV_APPLET)); + aid_len = sizeof(PIV_APPLET); + } + + uint8_t tag[4] = {0}; + int tag_len = 0; + CLIGetHexWithReturn(ctx, 7, tag, &tag_len); + + CLIParserFree(ctx); + + if ((tag_len < 1) || (tag_len > 3)) { + PrintAndLogEx(WARNING, "Tag should be between 1 and 3 bytes. Got %i", tag_len); + return PM3_EINVARG; + } + + SetAPDULogging(APDULogging); + + int res = 0; + if (activateField == true) { + res = PivSelect(channel, activateField, true, decodeTLV, true, applet_id, aid_len); + if (res != PM3_SUCCESS) { + if (leaveSignalON == false) { + DropFieldEx(channel); + } + return res; + } + } + res = PivGetDataByTagAndPrint(channel, tag, tag_len, decodeTLV, false); + if (leaveSignalON == false) { + DropFieldEx(channel); + } + return res; +} + +static int CmdPIVAuthenticateSign(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "piv sign", + "Send a nonce and ask the PIV card to sign it", + "piv sign -sk -> select card, select applet, sign a NULL nonce\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "Activate field and select applet"), + arg_lit0("kK", "keep", "Keep field for next command"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_str0(NULL, "aid", "", "Applet ID to select. By default A0000003080000100 will be used"), + arg_str1(NULL, "nonce", "", "Nonce to sign."), + arg_int0(NULL, "slot", "", "Slot number. Default will be 0x9E (card auth cert)."), + arg_int0(NULL, "alg", "", "Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool activateField = arg_get_lit(ctx, 1); + bool leaveSignalON = arg_get_lit(ctx, 2); + bool APDULogging = arg_get_lit(ctx, 3); + bool decodeTLV = arg_get_lit(ctx, 4); + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 5)) + channel = CC_CONTACT; + PrintChannel(channel); + + uint8_t applet_id[APDU_AID_LEN] = {0}; + int aid_len = 0; + CLIGetHexWithReturn(ctx, 6, applet_id, &aid_len); + if (aid_len == 0) { + memcpy(applet_id, PIV_APPLET, sizeof(PIV_APPLET)); + aid_len = sizeof(PIV_APPLET); + } + + uint8_t nonce[APDU_RES_LEN] = {0}; + int nonce_len = 0; + CLIGetHexWithReturn(ctx, 7, nonce, &nonce_len); + + int key_slot = arg_get_int_def(ctx, 8, 0x9e); + int alg_id = arg_get_int_def(ctx, 9, 0x11); + + CLIParserFree(ctx); + + if (key_slot > 0xff) { + PrintAndLogEx(FAILED, "Key slot must fit on 1 byte."); + return PM3_EINVARG; + } + if (alg_id > 0xff) { + PrintAndLogEx(FAILED, "Algorithm ID must fit on 1 byte"); + return PM3_EINVARG; + } + + SetAPDULogging(APDULogging); + + int res = 0; + if (activateField == true) { + res = PivSelect(channel, activateField, true, decodeTLV, true, applet_id, aid_len); + if (res != PM3_SUCCESS) { + if (leaveSignalON == false) { + DropFieldEx(channel); + } + return res; + } + } + res = PivAuthenticateSign(channel, (uint8_t)(alg_id & 0xff), (uint8_t)(key_slot & 0xff), nonce, nonce_len, NULL, decodeTLV, true); + if (leaveSignalON == false) { + DropFieldEx(channel); + } + return res; +} + +static int CmdPIVScan(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "piv scan", + "Scan a PIV card for known containers", + "piv scan -s -> select card, select applet and run scan\n" + "piv scan -st --aid a00000030800001000 -> select card, select applet a00000030800001000, show result of the scan in TLV\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("sS", "select", "Activate field and select applet"), + arg_lit0("kK", "keep", "Keep field for next command"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), + arg_lit0("tT", "tlv", "TLV decode results"), + arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_str0(NULL, "aid", "", "Applet ID to select. By default A0000003080000100 will be used"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool activateField = arg_get_lit(ctx, 1); + bool leaveSignalON = arg_get_lit(ctx, 2); + bool APDULogging = arg_get_lit(ctx, 3); + bool decodeTLV = arg_get_lit(ctx, 4); + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 5)) + channel = CC_CONTACT; + PrintChannel(channel); + + uint8_t applet_id[APDU_AID_LEN] = {0}; + int aid_len = 0; + CLIGetHexWithReturn(ctx, 6, applet_id, &aid_len); + if (aid_len == 0) { + memcpy(applet_id, PIV_APPLET, sizeof(PIV_APPLET)); + aid_len = sizeof(PIV_APPLET); + } + + CLIParserFree(ctx); + + SetAPDULogging(APDULogging); + if (aid_len == 0) { + memcpy(applet_id, PIV_APPLET, sizeof(PIV_APPLET)); + aid_len = sizeof(PIV_APPLET); + } + if (activateField == true) { + int res = PivSelect(channel, activateField, true, decodeTLV, true, applet_id, aid_len); + if (res != PM3_SUCCESS) { + if (leaveSignalON == false) { + DropFieldEx(channel); + } + return res; + } + } + + for (int i = 0; PIV_CONTAINERS[i].len != 0; i++) { + PivGetDataByCidAndPrint(channel, &(PIV_CONTAINERS[i]), decodeTLV, false); + PrintAndLogEx(NORMAL, ""); + } + if (leaveSignalON == false) { + DropFieldEx(channel); + } + return PM3_SUCCESS; +} + +static int CmdPIVList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "piv", "7816"); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"select", CmdPIVSelect, IfPm3Iso14443, "Select the PIV applet"}, + {"getdata", CmdPIVGetData, IfPm3Iso14443, "Gets a container on a PIV card"}, + {"authsign", CmdPIVAuthenticateSign, IfPm3Iso14443, "Authenticate with the card"}, + {"scan", CmdPIVScan, IfPm3Iso14443, "Scan PIV card for known containers"}, + {"list", CmdPIVList, AlwaysAvailable, "List ISO7816 history"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdPIV(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} + diff --git a/client/src/cmdpiv.h b/client/src/cmdpiv.h new file mode 100644 index 000000000..13b5fffe9 --- /dev/null +++ b/client/src/cmdpiv.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// PIV commands +//----------------------------------------------------------------------------- + +#ifndef CMDPIV_H__ +#define CMDPIV_H__ + +#include "common.h" + +int CmdPIV(const char *Cmd); + +#endif diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 96d1f3637..0abab9b01 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -147,7 +147,7 @@ static int split(char *str, char **arr) { return word_cnt; } -static void set_python_path(char *path) { +static void set_python_path(const char *path) { PyObject *syspath = PySys_GetObject("path"); if (syspath == 0) { PrintAndLogEx(WARNING, "Python failed to getobject"); @@ -164,37 +164,44 @@ static void set_python_path(char *path) { } static void set_python_paths(void) { - //--add to the LUA_PATH (package.path in lua) - // so we can load scripts from various places: + // Prepending to sys.path so we can load scripts from various places. + // This means the following directories are in reverse order of + // priority for search python modules. + + // Allow current working directory because it seems that's what users want. + // But put it with lower search priority than the typical pm3 scripts directories + // but still with a higher priority than the pip installed libraries to mimic + // Python interpreter behavior. That should be confusing the users the least. + set_python_path("."); const char *exec_path = get_my_executable_directory(); if (exec_path != NULL) { - // from the ./luascripts/ directory + // from the ./pyscripts/ directory char scripts_path[strlen(exec_path) + strlen(PYTHON_SCRIPTS_SUBDIR) + strlen(PYTHON_LIBRARIES_WILDCARD) + 1]; strcpy(scripts_path, exec_path); strcat(scripts_path, PYTHON_SCRIPTS_SUBDIR); -// strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD); + // strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD); set_python_path(scripts_path); } const char *user_path = get_my_user_directory(); if (user_path != NULL) { - // from the $HOME/.proxmark3/luascripts/ directory + // from the $HOME/.proxmark3/pyscripts/ directory char scripts_path[strlen(user_path) + strlen(PM3_USER_DIRECTORY) + strlen(PYTHON_SCRIPTS_SUBDIR) + strlen(PYTHON_LIBRARIES_WILDCARD) + 1]; strcpy(scripts_path, user_path); strcat(scripts_path, PM3_USER_DIRECTORY); strcat(scripts_path, PYTHON_SCRIPTS_SUBDIR); -// strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD); + // strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD); set_python_path(scripts_path); } if (exec_path != NULL) { - // from the $PREFIX/share/proxmark3/luascripts/ directory + // from the $PREFIX/share/proxmark3/pyscripts/ directory char scripts_path[strlen(exec_path) + strlen(PM3_SHARE_RELPATH) + strlen(PYTHON_SCRIPTS_SUBDIR) + strlen(PYTHON_LIBRARIES_WILDCARD) + 1]; strcpy(scripts_path, exec_path); strcat(scripts_path, PM3_SHARE_RELPATH); strcat(scripts_path, PYTHON_SCRIPTS_SUBDIR); -// strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD); + // strcat(scripts_path, PYTHON_LIBRARIES_WILDCARD); set_python_path(scripts_path); } } @@ -399,35 +406,55 @@ static int CmdScriptRun(const char *Cmd) { PrintAndLogEx(SUCCESS, "executing python " _YELLOW_("%s"), script_path); PrintAndLogEx(SUCCESS, "args " _YELLOW_("'%s'"), arguments); - wchar_t *program = Py_DecodeLocale(filename, NULL); - if (program == NULL) { - PrintAndLogEx(ERR, "could not decode " _YELLOW_("%s"), filename); - free(script_path); - return PM3_ESOFT; - } - - // optional but recommended - Py_SetProgramName(program); #ifdef HAVE_PYTHON_SWIG // hook Proxmark3 API PyImport_AppendInittab("_pm3", PyInit__pm3); #endif +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 10 Py_Initialize(); +#else + PyConfig py_conf; + // We need to use Python mode instead of isolated to avoid breaking stuff. + PyConfig_InitPythonConfig(&py_conf); + // Let's still make things bit safer by being as close as possible to isolated mode. + py_conf.configure_c_stdio = -1; + py_conf.faulthandler = 0; + py_conf.use_hash_seed = 0; + py_conf.install_signal_handlers = 0; + py_conf.parse_argv = 0; + py_conf.user_site_directory = 1; + py_conf.use_environment = 0; +#endif //int argc, char ** argv char *argv[128]; - int argc = split(arguments, argv); + argv[0] = filename; + int argc = split(arguments, &argv[1]); +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 10 wchar_t *py_args[argc + 1]; - py_args[0] = Py_DecodeLocale(filename, NULL); - for (int i = 0; i < argc; i++) { - py_args[i + 1] = Py_DecodeLocale(argv[i], NULL); + for (int i = 0; i <= argc; i++) { + py_args[i] = Py_DecodeLocale(argv[i], NULL); } PySys_SetArgv(argc + 1, py_args); +#else + // The following line will implicitly pre-initialize Python + PyConfig_SetBytesArgv(&py_conf, argc + 1, argv); + + // We disallowed in py_conf environment variables interfering with python interpreter's behavior. + // Let's manually enable the ones we truly need. + // This is required by Proxspace to work with an isolated Python configuration + PyConfig_SetBytesString(&py_conf, &py_conf.home, getenv("PYTHONHOME")); + // This is required for allowing `import pm3` in python scripts + PyConfig_SetBytesString(&py_conf, &py_conf.pythonpath_env, getenv("PYTHONPATH")); + + Py_InitializeFromConfig(&py_conf); // clean up + PyConfig_Clear(&py_conf); +#endif for (int i = 0; i < argc; ++i) { - free(argv[i]); + free(argv[i + 1]); } // setup search paths. @@ -440,8 +467,13 @@ static int CmdScriptRun(const char *Cmd) { return PM3_ESOFT; } int ret = Pm3PyRun_SimpleFileNoExit(f, filename); +#if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 10 + // Py_DecodeLocale() allocates memory that needs to be free'd + for (int i = 0; i < argc + 1; i++) { + PyMem_RawFree(py_args[i]); + } +#endif Py_Finalize(); - PyMem_RawFree(program); free(script_path); if (ret) { PrintAndLogEx(WARNING, "\nfinished " _YELLOW_("%s") " with exception", filename); diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 3e273fc07..9c273739d 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -317,6 +317,7 @@ static int smart_wait(uint8_t *out, int maxoutlen, bool verbose) { static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { int datalen = smart_wait(out, maxoutlen, verbose); + int totallen = datalen; bool needGetData = false; if (datalen < 2) { @@ -327,8 +328,26 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { needGetData = true; } - if (needGetData) { + if (needGetData == true) { + // Don't discard data we already received except the SW code. + // If we only received 1 byte, this is the echo of INS, we discard it. + totallen -= 2; + if (totallen == 1) { + totallen = 0; + } + int ofs = totallen; + maxoutlen -= totallen; + PrintAndLogEx(DEBUG, "Keeping data (%d bytes): %s", ofs, sprint_hex(out, ofs)); + int len = out[datalen - 1]; + if (len == 0 || len > MAX_APDU_SIZE) { + // Cap the data length or the smartcard may send us a buffer we can't handle + len = MAX_APDU_SIZE; + } + if (maxoutlen < len) { + // We don't have enough buffer to hold the next part + goto out; + } if (verbose) PrintAndLogEx(INFO, "Requesting " _YELLOW_("0x%02X") " bytes response", len); @@ -342,7 +361,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + sizeof(cmd_getresp)); free(payload); - datalen = smart_wait(out, maxoutlen, verbose); + datalen = smart_wait(&out[ofs], maxoutlen, verbose); if (datalen < 2) { goto out; @@ -352,7 +371,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { if (datalen != len + 2) { // data with ACK if (datalen == len + 2 + 1) { // 2 - response, 1 - ACK - if (out[0] != ISO7816_GET_RESPONSE) { + if (out[ofs] != ISO7816_GET_RESPONSE) { if (verbose) { PrintAndLogEx(ERR, "GetResponse ACK error. len 0x%x | data[0] %02X", len, out[0]); } @@ -361,7 +380,8 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { } datalen--; - memmove(out, &out[1], datalen); + memmove(&out[ofs], &out[ofs + 1], datalen); + totallen += datalen; } else { // wrong length if (verbose) { @@ -372,7 +392,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { } out: - return datalen; + return totallen; } static int smart_response(uint8_t *out, int maxoutlen) { @@ -501,8 +521,8 @@ static int CmdSmartUpgrade(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "smart upgrade", - "Upgrade RDV4.0 sim module firmware", - "smart upgrade -f sim011.bin" + "Upgrade RDV4 sim module firmware", + "smart upgrade -f sim013.bin" ); void *argtable[] = { @@ -513,7 +533,7 @@ static int CmdSmartUpgrade(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, sizeof(filename), &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); char *bin_extension = filename; @@ -670,7 +690,7 @@ static int CmdSmartInfo(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "verbose"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -699,7 +719,7 @@ static int CmdSmartInfo(const char *Cmd) { // print header PrintAndLogEx(INFO, "--- " _CYAN_("Smartcard Information") " ---------"); - PrintAndLogEx(INFO, "ISO7618-3 ATR... %s", sprint_hex(card.atr, card.atr_len)); + PrintAndLogEx(INFO, "ISO7816-3 ATR... %s", sprint_hex(card.atr, card.atr_len)); // convert bytes to str. char *hexstr = calloc((card.atr_len << 1) + 1, sizeof(uint8_t)); if (hexstr == NULL) { @@ -748,7 +768,7 @@ static int CmdSmartReader(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "verbose"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdsmartcard.h b/client/src/cmdsmartcard.h index 65e6ba1c1..6ed0a2842 100644 --- a/client/src/cmdsmartcard.h +++ b/client/src/cmdsmartcard.h @@ -22,6 +22,11 @@ #include "common.h" #include "pm3_cmd.h" // structs +// On ARM side, ISO7816_MAX_FRAME is set to 260 +// This means we can receive a full short APDU (256 bytes) of data and have enough room for +// SW status code and surrounding metadata without creating a buffer overflow. +#define MAX_APDU_SIZE 256 + int CmdSmartcard(const char *Cmd); bool smart_select(bool verbose, smart_card_atr_t *atr); diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 1ef307cf6..d3096307a 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -485,8 +485,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr uint32_t end_of_transmission_timestamp = 0; uint8_t topaz_reader_command[9]; char explanation[40] = {0}; - uint8_t mfData[32] = {0}; - size_t mfDataLen = 0; tracelog_hdr_t *first_hdr = (tracelog_hdr_t *)(trace); tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos); @@ -573,7 +571,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr //2 Not crc-command //--- Draw the data column - char line[18][120] = {{0}}; + char line[18][160] = {{0}}; if (data_len == 0) { if (protocol == ICLASS && duration == 2048) { @@ -633,7 +631,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } - if (markCRCBytes) { + + uint8_t crc_format_string_offset = 0; + if (markCRCBytes && data_len > 2) { //CRC-command if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAGS)) && (data_len > 1)) { // Note that UID REQUEST response has no CRC, but we don't know @@ -644,18 +644,46 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr (*pos2) = ']'; (*(pos2 + 1)) = '\0'; } else { + if (crcStatus == 0 || crcStatus == 1) { - char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4) - 1; - (*pos1) = '['; - char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4) - 1; - (*pos2) = ']'; - (*(pos2 + 1)) = '\0'; + + char *pos1 = line[(data_len - 2) / 18]; + pos1 += (((data_len - 2) % 18) * 4) - 1; + + (*(pos1 + 6 + 1)) = '\0'; + + char *cb_str = str_dup(pos1 + 1); + + if (hdr->isResponse) { + if (g_session.supports_colors) { + if (crcStatus == 0) { + snprintf(pos1, 24, " " _RED_("%s") " ", cb_str); + } else { + snprintf(pos1, 24, " " _GREEN_("%s") " ", cb_str); + } + crc_format_string_offset = 9; + } else { + snprintf(pos1, 9, "[%s]", cb_str); + } + } else { + if (g_session.supports_colors) { + if (crcStatus == 0) { + snprintf(pos1, 24, AEND " " _RED_("%s") " ", cb_str); + } else { + snprintf(pos1, 24, AEND " " _GREEN_("%s") " ", cb_str); + } + crc_format_string_offset = 13; + } else { + snprintf(pos1, 9, "[%s]", cb_str); + } + } + free(cb_str); } } } // Draw the CRC column - const char *crcstrings[] = { "!crc", " ok ", " ", "A ok", "B ok" }; + const char *crcstrings[] = { _RED_(" !! "), _GREEN_(" ok "), " ", _GREEN_("A ok"), _GREEN_("B ok") }; const char *crc = crcstrings[crcStatus]; // mark short bytes (less than 8 Bit + Parity) @@ -777,17 +805,19 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr if (hdr->isResponse) { // tag row if (use_us) { - PrintAndLogEx(NORMAL, " %10.1f | %10.1f | Tag |%-72s | %s| %s", + PrintAndLogEx(NORMAL, " %10.1f | %10.1f | Tag |%-*s | %s| %s", (float)time1 / 13.56, (float)time2 / 13.56, + 72 + crc_format_string_offset, line[j], (j == num_lines - 1) ? crc : " ", (j == num_lines - 1) ? explanation : "" ); } else { - PrintAndLogEx(NORMAL, " %10u | %10u | Tag |%-72s | %s| %s", - (hdr->timestamp - first_hdr->timestamp), - (end_of_transmission_timestamp - first_hdr->timestamp), + PrintAndLogEx(NORMAL, " %10u | %10u | Tag |%-*s | %s| %s", + time1, + time2, + 72 + crc_format_string_offset, line[j], (j == num_lines - 1) ? crc : " ", (j == num_lines - 1) ? explanation : "" @@ -797,18 +827,20 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr // reader row if (use_us) { PrintAndLogEx(NORMAL, - _YELLOW_(" %10.1f") " | " _YELLOW_("%10.1f") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + _YELLOW_(" %10.1f") " | " _YELLOW_("%10.1f") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), (float)time1 / 13.56, (float)time2 / 13.56, + 72 + crc_format_string_offset, line[j], (j == num_lines - 1) ? crc : " ", (j == num_lines - 1) ? explanation : "" ); } else { PrintAndLogEx(NORMAL, - _YELLOW_(" %10u") " | " _YELLOW_("%10u") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), - (hdr->timestamp - first_hdr->timestamp), - (end_of_transmission_timestamp - first_hdr->timestamp), + _YELLOW_(" %10u") " | " _YELLOW_("%10u") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + time1, + time2, + 72 + crc_format_string_offset, line[j], (j == num_lines - 1) ? crc : " ", (j == num_lines - 1) ? explanation : "" @@ -819,13 +851,15 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } else { if (hdr->isResponse) { - PrintAndLogEx(NORMAL, " | | |%-72s | %s| %s", + PrintAndLogEx(NORMAL, " | | |%-*s | %s| %s", + 72 + crc_format_string_offset, line[j], (j == num_lines - 1) ? crc : " ", (j == num_lines - 1) ? explanation : "" ); } else { - PrintAndLogEx(NORMAL, " | | |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + PrintAndLogEx(NORMAL, " | | |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + 72 + crc_format_string_offset, line[j], (j == num_lines - 1) ? crc : " ", (j == num_lines - 1) ? explanation : "" @@ -835,13 +869,16 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } if (protocol == PROTO_MIFARE) { + uint8_t mfData[32] = {0}; + size_t mfDataLen = 0; if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen, mfDicKeys, mfDicKeysCount)) { memset(explanation, 0x00, sizeof(explanation)); annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen, hdr->isResponse); uint8_t crcc = iso14443A_CRC_check(hdr->isResponse, mfData, mfDataLen); - PrintAndLogEx(NORMAL, " | | * |%-72s | %-4s| %s", + PrintAndLogEx(NORMAL, " | | * |%-*s | %-4s| %s", + 72 + crc_format_string_offset, sprint_hex_inrow_spaces(mfData, mfDataLen, 2), - (crcc == 0 ? "!crc" : (crcc == 1 ? " ok " : " ")), + (crcc == 0 ? _RED_(" !! ") : (crcc == 1 ? _GREEN_(" ok ") : " ")), explanation); } } @@ -854,11 +891,26 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos); - PrintAndLogEx(NORMAL, " %10u | %10u | %s |fdt (Frame Delay Time): " _YELLOW_("%d"), - (end_of_transmission_timestamp - first_hdr->timestamp), - (next_hdr->timestamp - first_hdr->timestamp), - " ", - (next_hdr->timestamp - end_of_transmission_timestamp)); + uint32_t time1 = end_of_transmission_timestamp - first_hdr->timestamp; + uint32_t time2 = next_hdr->timestamp - first_hdr->timestamp; + if (prev_eot) { + time1 = 0; + time2 = next_hdr->timestamp - end_of_transmission_timestamp; + } + + if (use_us) { + PrintAndLogEx(NORMAL, " %10.1f | %10.1f | %s |fdt (Frame Delay Time): " _YELLOW_("%.1f"), + (float)time1 / 13.56, + (float)time2 / 13.56, + " ", + (float)(next_hdr->timestamp - end_of_transmission_timestamp) / 13.56); + } else { + PrintAndLogEx(NORMAL, " %10u | %10u | %s |fdt (Frame Delay Time): " _YELLOW_("%d"), + time1, + time2, + " ", + (next_hdr->timestamp - end_of_transmission_timestamp)); + } } return tracepos; @@ -1053,8 +1105,8 @@ int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol) protocol); char example[200] = {0}; snprintf(example, sizeof(example) - 1, - "%s list -f -> show frame delay times\n" - "%s list -1 -> use trace buffer ", + "%s list --frame -> show frame delay times\n" + "%s list -1 -> use trace buffer ", alias, alias); char fullalias[100] = {0}; snprintf(fullalias, sizeof(fullalias) - 1, "%s list", alias); @@ -1063,20 +1115,20 @@ int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol) void *argtable[] = { arg_param_begin, arg_lit0("1", "buffer", "use data from trace buffer"), - arg_lit0("f", NULL, "show frame delay times"), + arg_lit0(NULL, "frame", "show frame delay times"), arg_lit0("c", NULL, "mark CRC bytes"), arg_lit0("r", NULL, "show relative times (gap and duration)"), arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"), arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n" " or to import into Wireshark using encapsulation type \"ISO 14443\""), - arg_str0(NULL, "dict", "", "use dictionary keys file"), + arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); char args[128] = {0}; - snprintf(args, sizeof(args), "-t %s ", protocol); + snprintf(args, sizeof(args), "-c -t %s ", protocol); strncat(args, Cmd, sizeof(args) - strlen(args) - 1); return CmdTraceList(args); } @@ -1106,22 +1158,22 @@ int CmdTraceList(const char *Cmd) { "trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") "\n" "trace list -t topaz -> interpret as " _YELLOW_("Topaz") "\n" "\n" - "trace list -t mf --dict -> use dictionary keys file\n" - "trace list -t 14a -f -> show frame delay times\n" - "trace list -t 14a -1 -> use trace buffer " + "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file\n" + "trace list -t 14a --frame -> show frame delay times\n" + "trace list -t 14a -1 -> use trace buffer " ); void *argtable[] = { arg_param_begin, arg_lit0("1", "buffer", "use data from trace buffer"), - arg_lit0("f", NULL, "show frame delay times"), + arg_lit0(NULL, "frame", "show frame delay times"), arg_lit0("c", NULL, "mark CRC bytes"), arg_lit0("r", NULL, "show relative times (gap and duration)"), arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"), arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n" " or to import into Wireshark using encapsulation type \"ISO 14443\""), arg_str0("t", "type", NULL, "protocol to annotate the trace"), - arg_str0(NULL, "dict", "", "use dictionary keys file"), + arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index fe6148f4e..0973a9beb 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -158,6 +158,7 @@ int CmdWiegandDecode(const char *Cmd) { PrintAndLogEx(ERR, "empty input"); return PM3_EINVARG; } + wiegand_message_t packed = initialize_message_object(top, mid, bot, blen); HIDTryUnpack(&packed); return PM3_SUCCESS; diff --git a/client/src/comms.c b/client/src/comms.c index 535b95a61..a8ed8be4c 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -834,7 +834,7 @@ bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint3 return false; } case FPGA_MEM: { - SendCommandMIX(CMD_FPGAMEM_DOWNLOAD, start_index, bytes, 0, NULL, 0); + SendCommandNG(CMD_FPGAMEM_DOWNLOAD, NULL, 0); return dl_it(dest, bytes, response, ms_timeout, show_warning, CMD_FPGAMEM_DOWNLOADED); } } @@ -856,8 +856,10 @@ static bool dl_it(uint8_t *dest, uint32_t bytes, PacketResponseNG *response, siz if (response->cmd == CMD_ACK) return true; - // Spiffs download is converted to NG, - if (response->cmd == CMD_SPIFFS_DOWNLOAD) + if (response->cmd == CMD_SPIFFS_DOWNLOAD && response->status == PM3_EMALLOC) + return false; + // Spiffs // fpgamem-plot download is converted to NG, + if (response->cmd == CMD_SPIFFS_DOWNLOAD || response->cmd == CMD_FPGAMEM_DOWNLOAD) return true; // sample_buf is a array pointer, located in data.c diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index 4bab148be..5fcabe20a 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -26,6 +26,7 @@ #include #include #include +#include "mbedtls/bignum.h" // big num #include #include "emv/emv_tags.h" #include "emv/emvjson.h" @@ -44,6 +45,7 @@ enum asn1_tag_t { ASN1_TAG_STR_TIME, ASN1_TAG_OBJECT_ID, ASN1_TAG_HEX, + ASN1_TAG_BIT_STRING, }; struct asn1_tag { @@ -62,7 +64,7 @@ static const struct asn1_tag asn1_tags[] = { // ASN.1 { 0x01, "BOOLEAN", ASN1_TAG_BOOLEAN }, { 0x02, "INTEGER", ASN1_TAG_INTEGER }, - { 0x03, "BIT STRING", ASN1_TAG_GENERIC }, + { 0x03, "BIT STRING", ASN1_TAG_BIT_STRING }, { 0x04, "OCTET STRING", ASN1_TAG_OCTET_STRING }, { 0x05, "NULL", ASN1_TAG_GENERIC }, { 0x06, "OBJECT IDENTIFIER", ASN1_TAG_OBJECT_ID }, @@ -96,7 +98,6 @@ static const struct asn1_tag asn1_tags[] = { { 0xa5, "[5]", ASN1_TAG_GENERIC }, }; - static int asn1_sort_tag(tlv_tag_t tag) { return (int)(tag >= 0x100 ? tag : tag << 8); } @@ -109,9 +110,7 @@ static int asn1_tlv_compare(const void *a, const void *b) { } static const struct asn1_tag *asn1_get_tag(const struct tlv *tlv) { - struct asn1_tag *tag = bsearch(tlv, asn1_tags, ARRAYLEN(asn1_tags), - sizeof(asn1_tags[0]), asn1_tlv_compare); - + struct asn1_tag *tag = bsearch(tlv, asn1_tags, ARRAYLEN(asn1_tags), sizeof(asn1_tags[0]), asn1_tlv_compare); return tag ? tag : &asn1_tags[0]; } @@ -119,43 +118,55 @@ static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag int len = tlv->len; *needdump = false; - int startindx = longyear ? 4 : 2; + int startidx = longyear ? 4 : 2; if (len > 4) { PrintAndLogEx(NORMAL, " value: '" NOLF); while (true) { + // year - if (longyear == false) - PrintAndLogEx(NORMAL, "20" NOLF); + if (longyear == false) { + int short_year = (tlv->value[0] - '0') * 10 + (tlv->value[1] - '0'); + if (short_year >= 0 && short_year <= 99) { + if (short_year > 50) { + PrintAndLogEx(NORMAL, "19" NOLF); + } else { + PrintAndLogEx(NORMAL, "20" NOLF); + } + } + } - PrintAndLogEx(NORMAL, "%s-" NOLF, sprint_hex(tlv->value, startindx)); - - if (len < startindx + 2) + PrintAndLogEx(NORMAL, "%.*s-" NOLF, startidx, tlv->value); + if (len < startidx + 2) break; // month - PrintAndLogEx(NORMAL, "%02x%02x-" NOLF, tlv->value[startindx], tlv->value[startindx + 1]); - if (len < startindx + 4) + PrintAndLogEx(NORMAL, "%.*s-" NOLF, 2, tlv->value + startidx); + if (len < startidx + 4) break; // day - PrintAndLogEx(NORMAL, "%02x%02x " NOLF, tlv->value[startindx + 2], tlv->value[startindx + 3]); - if (len < startindx + 6) + PrintAndLogEx(NORMAL, "%.*s " NOLF, 2, tlv->value + startidx + 2); + if (len < startidx + 6) break; + // hour - PrintAndLogEx(NORMAL, "%02x%02x:" NOLF, tlv->value[startindx + 4], tlv->value[startindx + 5]); - if (len < startindx + 8) + PrintAndLogEx(NORMAL, "%.*s:" NOLF, 2, tlv->value + startidx + 4); + if (len < startidx + 8) break; + // min - PrintAndLogEx(NORMAL, "%02x%02x:" NOLF, tlv->value[startindx + 6], tlv->value[startindx + 7]); - if (len < startindx + 10) + PrintAndLogEx(NORMAL, "%.*s:" NOLF, 2, tlv->value + startidx + 6); + if (len < startidx + 10) break; + // sec - PrintAndLogEx(NORMAL, "%02x%02x" NOLF, tlv->value[startindx + 8], tlv->value[startindx + 9]); - if (len < startindx + 11) + PrintAndLogEx(NORMAL, "%.*s" NOLF, 2, tlv->value + startidx + 8); + if (len < startidx + 12) break; + // time zone - PrintAndLogEx(NORMAL, " zone: %.*s" NOLF, len - 10 - (longyear ? 4 : 2), &tlv->value[startindx + 10]); + PrintAndLogEx(NORMAL, " zone: UTC %.*s" NOLF, len - startidx - 10, tlv->value + startidx + 10); break; } PrintAndLogEx(NORMAL, "'"); @@ -166,57 +177,72 @@ static void asn1_tag_dump_str_time(const struct tlv *tlv, const struct asn1_tag } static void asn1_tag_dump_string(const struct tlv *tlv, const struct asn1_tag *tag, int level) { - PrintAndLogEx(NORMAL, " value: '%s'", sprint_hex(tlv->value, tlv->len)); + PrintAndLogEx(NORMAL, " value: '" _GREEN_("%.*s") "' hex: '%s'", (int)tlv->len, tlv->value, sprint_hex(tlv->value, tlv->len)); +} + +static void asn1_tag_dump_bitstring(const struct tlv *tlv, const struct asn1_tag *tag, int level) { + + size_t len = tlv->len; + size_t n = (len * 8); + bool skip = false; + + if (tlv->value[0] == 0) { + n -= 8; + len--; + skip = true; + } + + uint8_t *d = calloc(n, sizeof(uint8_t)); + if (d == NULL) { + return; + } + + if (skip) + bytes_to_bytebits(tlv->value + 1, len, d); + else + bytes_to_bytebits(tlv->value, len, d); + + level++; + PrintAndLogEx(NORMAL, " (%zu bit)", n); + PrintAndLogEx(INFO, "%*s" NOLF, 1 + (level * 4), ""); + + for (int i = 0; i < n; i++) { + + char c = d[i]; + if (c < 2) { + c += '0'; + } else { + goto out; + } + + PrintAndLogEx(NORMAL, "%c" NOLF, c); + + if (((i + 1) % 64) == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "%*s" NOLF, 1 + (level * 4), ""); + } + } + +out: + free(d); + PrintAndLogEx(NORMAL, ""); } static void asn1_tag_dump_hex(const struct tlv *tlv, const struct asn1_tag *tag, int level) { PrintAndLogEx(NORMAL, " value: '%s'", sprint_hex_inrow(tlv->value, tlv->len)); } -static void asn1_tag_dump_octet_string(const struct tlv *tlv, const struct asn1_tag *tag, int level, bool *needdump) { - *needdump = false; - for (size_t i = 0; i < tlv->len; i++) - if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])) { - *needdump = true; - break; +static void asn1_tag_dump_octet_string(const struct tlv *tlv, const struct asn1_tag *tag, int level) { + /* + for (size_t i = 0; i < tlv->len; i++) { + if (!isspace(tlv->value[i]) && !isprint(tlv->value[i])) { + *needdump = true; + break; + } } - - if (*needdump) { - PrintAndLogEx(NORMAL, ""); - } else { - PrintAndLogEx(NORMAL, " " NOLF); - asn1_tag_dump_string(tlv, tag, level); - } -} - -static unsigned long asn1_value_integer(const struct tlv *tlv, unsigned start, unsigned end) { - unsigned long ret = 0; - unsigned i; - - if (end > tlv->len * 2) - return ret; - if (start >= end) - return ret; - - if (start & 1) { - ret += tlv->value[start / 2] & 0xf; - i = start + 1; - } else - i = start; - - for (; i < end - 1; i += 2) { - ret = ret << 4; // was: ret*=10 - ret += tlv->value[i / 2] >> 4; - ret = ret << 4; // was: ret*=10 - ret += tlv->value[i / 2] & 0xf; - } - - if (end & 1) { - ret = ret << 4; // was: ret*=10 - ret += tlv->value[end / 2] >> 4; - } - - return ret; + */ + PrintAndLogEx(NORMAL, " " NOLF); + asn1_tag_dump_string(tlv, tag, level); } static void asn1_tag_dump_boolean(const struct tlv *tlv, const struct asn1_tag *tag, int level) { @@ -229,17 +255,34 @@ static void asn1_tag_dump_boolean(const struct tlv *tlv, const struct asn1_tag * } static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag *tag, int level) { - PrintAndLogEx(NORMAL, "%*s" NOLF, (level * 4), " "); - if (tlv->len == 4) { - int32_t val = 0; - for (size_t i = 0; i < tlv->len; i++) { - val = (val << 8) + tlv->value[i]; - } - PrintAndLogEx(NORMAL, " value: %d (0x%08X)", val, val); + + size_t n = (tlv->len * 2); + char *hex = calloc(n + 1, sizeof(uint8_t)); + if (hex == NULL) { return; } - uint32_t val = asn1_value_integer(tlv, 0, tlv->len * 2); - PrintAndLogEx(NORMAL, " value: %" PRIu32 " (0x%X)", val, val); + + hex_to_buffer((uint8_t *)hex, tlv->value, tlv->len, tlv->len, 0, 0, false); + + // results for MPI actions + bool ret = false; + + // container of big number + mbedtls_mpi N; + mbedtls_mpi_init(&N); + + MBEDTLS_MPI_CHK(mbedtls_mpi_read_string(&N, 16, hex)); + + char s[600] = {0}; + size_t slen = 0; + MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, 10, s, sizeof(s), &slen)); + if (slen > 0) { + PrintAndLogEx(NORMAL, "%*s value: %s", (level), "", s); + } + +cleanup: + mbedtls_mpi_free(&N); + free(hex); } static char *asn1_oid_description(const char *oid, bool with_group_desc) { @@ -293,7 +336,7 @@ static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag char pstr[300]; mbedtls_oid_get_numeric_string(pstr, sizeof(pstr), &asn1_buf); - PrintAndLogEx(INFO, "%*s %s" NOLF, (level * 4), " ", pstr); + PrintAndLogEx(NORMAL, "%*s %s" NOLF, (level), " ", pstr); char *jsondesc = asn1_oid_description(pstr, true); if (jsondesc) { @@ -353,7 +396,8 @@ bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) { *candump = false; break; case ASN1_TAG_OCTET_STRING: - asn1_tag_dump_octet_string(tlv, tag, level, candump); + asn1_tag_dump_octet_string(tlv, tag, level); + *candump = false; break; case ASN1_TAG_BOOLEAN: asn1_tag_dump_boolean(tlv, tag, level); @@ -377,6 +421,10 @@ bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) { asn1_tag_dump_hex(tlv, tag, level); *candump = false; break; + case ASN1_TAG_BIT_STRING: + asn1_tag_dump_bitstring(tlv, tag, level); + *candump = false; + break; }; return true; diff --git a/client/src/crypto/asn1utils.c b/client/src/crypto/asn1utils.c index f54a6b451..4618c5028 100644 --- a/client/src/crypto/asn1utils.c +++ b/client/src/crypto/asn1utils.c @@ -20,19 +20,30 @@ #include #include #include -#include "ui.h" // Print... +#include // memcpy +#include "ui.h" // Print... #include "emv/tlv.h" #include "asn1dump.h" #include "util.h" + int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *rval, uint8_t *sval) { - if (!signature || !signaturelen || !rval || !sval) + + if (!signature || !signaturelen || !rval || !sval) { return PM3_EINVARG; + } + + uint8_t *p = calloc(sizeof(uint8_t), signaturelen); + if (p == NULL) { + return PM3_EMALLOC; + } + + memcpy(p, signature, signaturelen); + uint8_t *p_tmp = p; + const uint8_t *end = p + signaturelen; int res = PM3_SUCCESS; - unsigned char *p = signature; - const unsigned char *end = p + signaturelen; - size_t len; + size_t len = 0; mbedtls_mpi xmpi; if ((res = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) == 0) { @@ -61,11 +72,16 @@ int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *r goto exit; // check size - if (end != p) + if (end != p) { + free(p_tmp); + end = NULL; return PM3_ESOFT; + } } exit: + free(p_tmp); + end = NULL; return res; } diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index a855b672f..71277d87b 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -17,11 +17,9 @@ //----------------------------------------------------------------------------- #include "cmdemv.h" - #include - -#include "comms.h" // DropField -#include "cmdsmartcard.h" // smart_select +#include "comms.h" // DropField +#include "cmdsmartcard.h" // smart_select #include "cmdtrace.h" #include "emvjson.h" #include "test/cryptotest.h" @@ -35,6 +33,7 @@ #include "ui.h" #include "emv_tags.h" #include "fileutils.h" +#include "protocols.h" // ISO7816 APDU return codes static int CmdHelp(const char *Cmd); @@ -140,7 +139,7 @@ static int CmdEMVSearch(const char *Cmd) { arg_param_begin, arg_lit0("sS", "select", "Activate field and select card"), arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end @@ -196,7 +195,7 @@ static int CmdEMVPPSE(const char *Cmd) { arg_lit0("kK", "keep", "Keep field ON for next command"), arg_lit0("1", "pse", "PSE (1PAY.SYS.DDF01) mode"), arg_lit0("2", "ppse", "PPSE (2PAY.SYS.DDF01) mode (def)"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end @@ -255,7 +254,7 @@ static int CmdEMVGPO(const char *Cmd) { arg_lit0("kK", "keep", "Keep field ON for next command"), arg_lit0("pP", "params", "Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters"), arg_lit0("mM", "make", "Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_strx0(NULL, NULL, "", "PDOLdata/PDOL"), @@ -362,7 +361,7 @@ static int CmdEMVReadRecord(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_strx1(NULL, NULL, "", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), arg_lit0("pP", "params", "Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters"), arg_lit0("mM", "make", "Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_strx1(NULL, NULL, "", "CDOLdata/CDOL"), @@ -544,7 +543,7 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; @@ -598,7 +597,7 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { arg_lit0("kK", "keep", "Keep field ON for next command"), arg_lit0("pP", "params", "Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters"), arg_lit0("mM", "make", "Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results of selected applets"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_strx1(NULL, NULL, "", "DDOLdata/DDOL"), @@ -823,7 +822,7 @@ static int CmdEMVExec(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("sS", "select", "Activate field and select card"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results"), arg_lit0("jJ", "jload", "Load transaction parameters from `emv_defparams.json` file"), arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE"), @@ -1448,7 +1447,7 @@ static int CmdEMVScan(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("tT", "tlv", "TLV decode results"), arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"), arg_lit0("jJ", "jload", "Load transaction parameters from `emv_defparams.json` file"), @@ -1564,7 +1563,7 @@ static int CmdEMVScan(const char *Cmd) { PrintAndLogEx(INFO, "PPSE"); res = EMVSelectPSE(channel, true, true, 2, buf, sizeof(buf), &len, &sw); - if (!res && sw == 0x9000) { + if (!res && sw == ISO7816_OK) { if (decodeTLV) TLVPrintFromBuffer(buf, len); @@ -1872,7 +1871,7 @@ static int CmdEMVRoca(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("tT", "selftest", "Self test"), - arg_lit0("aA", "apdu", "Show APDU reqests and responses"), + arg_lit0("aA", "apdu", "Show APDU requests and responses"), arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 17aa2610e..0628fcf76 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -17,13 +17,11 @@ //----------------------------------------------------------------------------- #include "emvcore.h" - #include - -#include "commonutil.h" // ARRAYLEN -#include "comms.h" // DropField +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // DropField #include "cmdparser.h" -#include "cmdsmartcard.h" // ExchangeAPDUSC +#include "cmdsmartcard.h" // ExchangeAPDUSC #include "ui.h" #include "cmdhf14a.h" #include "cmdhf14b.h" @@ -31,6 +29,7 @@ #include "emv_tags.h" #include "emvjson.h" #include "util_posix.h" +#include "protocols.h" // ISO7816 APDU return codes // Got from here. Thanks! // https://eftlab.co.uk/index.php/site-map/knowledge-base/211-emv-aid-rid-pix @@ -178,7 +177,7 @@ bool TLVPrintFromBuffer(uint8_t *data, int datalen) { } void TLVPrintFromTLVLev(struct tlvdb *tlv, int level) { - if (!tlv) + if (tlv == NULL) return; tlvdb_visit(tlv, emv_print_cb, NULL, level); @@ -194,15 +193,16 @@ void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { PrintAndLogEx(INFO, "|------------------+--------+-------------------------|"); struct tlvdb *ttmp = tlvdb_find(tlv, 0x6f); - if (!ttmp) + if (ttmp == NULL) PrintAndLogEx(INFO, "| none |"); while (ttmp) { const struct tlv *tgAID = tlvdb_get_inchild(ttmp, 0x84, NULL); const struct tlv *tgName = tlvdb_get_inchild(ttmp, 0x50, NULL); const struct tlv *tgPrio = tlvdb_get_inchild(ttmp, 0x87, NULL); - if (!tgAID) + if (!tgAID) { break; + } PrintAndLogEx(INFO, "| %s| %s | %s|", sprint_hex_inrow_ex(tgAID->value, tgAID->len, 17), (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", @@ -382,8 +382,6 @@ static int EMVCheckAID(Iso7816CommandChannel channel, bool decodeTLV, struct tlv int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv) { uint8_t data[APDU_RES_LEN] = {0}; size_t datalen = 0; - uint8_t sfidata[0x11][APDU_RES_LEN]; - size_t sfidatalen[0x11] = {0}; uint16_t sw = 0; int res; const char *PSE_or_PPSE = PSENum == 1 ? "PSE" : "PPSE"; @@ -392,7 +390,7 @@ int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFi res = EMVSelectPSE(channel, ActivateField, true, PSENum, data, sizeof(data), &datalen, &sw); if (!res) { - if (sw != 0x9000) { + if (sw != ISO7816_OK) { PrintAndLogEx(FAILED, "Select PSE error. APDU error: %04x.", sw); return 1; } @@ -403,6 +401,8 @@ int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFi // PSE/PPSE with SFI struct tlvdb *tsfi = tlvdb_find_path(t, (tlv_tag_t[]) {0x6f, 0xa5, 0x88, 0x00}); if (tsfi) { + uint8_t sfidata[0x11][APDU_RES_LEN]; + size_t sfidatalen[0x11] = {0}; uint8_t sfin = 0; tlv_get_uint8(tlvdb_get_tlv(tsfi), &sfin); PrintAndLogEx(INFO, "* PPSE get SFI: 0x%02x.", sfin); @@ -419,7 +419,7 @@ int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFi } // error catch! - if (sw != 0x9000) { + if (sw != ISO7816_OK) { sfidatalen[ui] = 0; PrintAndLogEx(FAILED, "PPSE get Error. APDU error: %04x.", sw); break; @@ -670,10 +670,6 @@ static const unsigned char default_ddol_value[] = {0x9f, 0x37, 0x04}; static struct tlv default_ddol_tlv = {.tag = 0x9f49, .len = 3, .value = default_ddol_value }; int trDDA(Iso7816CommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - struct emv_pk *pk = get_ca_pk(tlv); if (!pk) { PrintAndLogEx(ERR, "Error: Key not found, exiting"); @@ -768,6 +764,9 @@ int trDDA(Iso7816CommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { tlvdb_free(atc_db); } else { + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); if (dac_db) { const struct tlv *dac_tlv = tlvdb_get(dac_db, 0x9f45, NULL); diff --git a/client/src/emv/test/crypto_test.c b/client/src/emv/test/crypto_test.c index 332203ad1..ab67a2081 100644 --- a/client/src/emv/test/crypto_test.c +++ b/client/src/emv/test/crypto_test.c @@ -72,7 +72,7 @@ static int test_genkey(unsigned int keylength, unsigned char *msg, size_t msg_le } free(tmp2); - PrintAndLogEx(SUCCESS, "passed (" _GREEN_("%" PRIu64) " ms)", msclock() - ms); + PrintAndLogEx(SUCCESS, "passed ( " _GREEN_("%" PRIu64) " ms )", msclock() - ms); free_tmp: free(tmp); diff --git a/client/src/emv/tlv.c b/client/src/emv/tlv.c index f22bfea4c..737300fe0 100644 --- a/client/src/emv/tlv.c +++ b/client/src/emv/tlv.c @@ -44,19 +44,6 @@ // const typeof( ((type *)0)->member ) *__mptr = (ptr); // (type *)( (char *)__mptr - offsetof(type,member) );}) -struct tlvdb { - struct tlv tag; - struct tlvdb *next; - struct tlvdb *parent; - struct tlvdb *children; -}; - -struct tlvdb_root { - struct tlvdb db; - size_t len; - unsigned char buf[0]; -}; - static tlv_tag_t tlv_parse_tag(const unsigned char **buf, size_t *len) { tlv_tag_t tag; @@ -210,8 +197,7 @@ struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len) { return &root->db; err: - tlvdb_free(&root->db); - + tlvdb_root_free(root); return NULL; } @@ -220,22 +206,32 @@ struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) { const unsigned char *tmp; size_t left; - if (!len || !buf) + if (len == 0 || buf == NULL) { return NULL; + } root = calloc(1, sizeof(*root) + len); + if (root == NULL) { + return NULL; + } + root->len = len; memcpy(root->buf, buf, len); tmp = root->buf; left = len; - if (!tlvdb_parse_one(&root->db, NULL, &tmp, &left)) + if (tlvdb_parse_one(&root->db, NULL, &tmp, &left) == false) { goto err; + } while (left != 0) { struct tlvdb *db = calloc(1, sizeof(*db)); - if (!tlvdb_parse_one(db, NULL, &tmp, &left)) { + if (db == NULL) { + goto err; + } + + if (tlvdb_parse_one(db, NULL, &tmp, &left) == false) { free(db); goto err; } @@ -246,11 +242,53 @@ struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len) { return &root->db; err: - tlvdb_free(&root->db); - + tlvdb_root_free(root); return NULL; } +bool tlvdb_parse_root(struct tlvdb_root *root) { + if (root == NULL || root->len == 0) { + return false; + } + + const uint8_t *tmp; + size_t left; + + tmp = root->buf; + left = root->len; + if (tlvdb_parse_one(&root->db, NULL, &tmp, &left) == true) { + if (left == 0) { + return true; + } + } + return false; +} + +bool tlvdb_parse_root_multi(struct tlvdb_root *root) { + if (root == NULL || root->len == 0) { + return false; + } + + const uint8_t *tmp; + size_t left; + + tmp = root->buf; + left = root->len; + if (tlvdb_parse_one(&root->db, NULL, &tmp, &left) == true) { + while (left > 0) { + struct tlvdb *db = calloc(1, sizeof(*db)); + if (tlvdb_parse_one(db, NULL, &tmp, &left) == true) { + tlvdb_add(&root->db, db); + } else { + free(db); + return false; + } + } + return true; + } + return false; +} + struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value) { struct tlvdb_root *root = calloc(1, sizeof(*root) + len); @@ -281,8 +319,9 @@ struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *val void tlvdb_free(struct tlvdb *tlvdb) { struct tlvdb *next = NULL; - if (!tlvdb) + if (tlvdb == NULL) { return; + } for (; tlvdb; tlvdb = next) { next = tlvdb->next; @@ -291,37 +330,58 @@ void tlvdb_free(struct tlvdb *tlvdb) { } } +void tlvdb_root_free(struct tlvdb_root *root) { + if (root == NULL) { + return; + } + if (root->db.children) { + tlvdb_free(root->db.children); + root->db.children = NULL; + } + if (root->db.next) { + tlvdb_free(root->db.next); + root->db.next = NULL; + } + free(root); +} + struct tlvdb *tlvdb_find_next(struct tlvdb *tlvdb, tlv_tag_t tag) { - if (!tlvdb) + if (tlvdb == NULL) { return NULL; + } return tlvdb_find(tlvdb->next, tag); } struct tlvdb *tlvdb_find(struct tlvdb *tlvdb, tlv_tag_t tag) { - if (!tlvdb) + if (tlvdb == NULL) { return NULL; + } for (; tlvdb; tlvdb = tlvdb->next) { - if (tlvdb->tag.tag == tag) + if (tlvdb->tag.tag == tag) { return tlvdb; + } } return NULL; } struct tlvdb *tlvdb_find_full(struct tlvdb *tlvdb, tlv_tag_t tag) { - if (!tlvdb) + if (tlvdb == NULL) { return NULL; + } for (; tlvdb; tlvdb = tlvdb->next) { - if (tlvdb->tag.tag == tag) + if (tlvdb->tag.tag == tag) { return tlvdb; + } if (tlvdb->children) { struct tlvdb *ch = tlvdb_find_full(tlvdb->children, tag); - if (ch) + if (ch) { return ch; + } } } @@ -344,12 +404,14 @@ struct tlvdb *tlvdb_find_path(struct tlvdb *tlvdb, tlv_tag_t tag[]) { } void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) { - if (tlvdb == other) + if (tlvdb == other) { return; + } while (tlvdb->next) { - if (tlvdb->next == other) + if (tlvdb->next == other) { return; + } tlvdb = tlvdb->next; } @@ -358,17 +420,21 @@ void tlvdb_add(struct tlvdb *tlvdb, struct tlvdb *other) { } void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, const unsigned char *value, struct tlvdb **tlvdb_elm) { + struct tlvdb *telm = tlvdb_find_full(tlvdb, tag); if (telm == NULL) { // new tlv element struct tlvdb *elm = tlvdb_fixed(tag, len, value); tlvdb_add(tlvdb, elm); - if (tlvdb_elm) + if (tlvdb_elm) { *tlvdb_elm = elm; + } + } else { // the same tlv structure - if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len)) + if (telm->tag.tag == tag && telm->tag.len == len && !memcmp(telm->tag.value, value, len)) { return; + } // replace tlv element struct tlvdb *tnewelm = tlvdb_fixed(tag, len, value); @@ -387,8 +453,9 @@ void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, // elm in root struct tlvdb *celm = tlvdb; // elm in child list of node - if (telm->parent && telm->parent->children) + if (telm->parent && telm->parent->children) { celm = telm->parent->children; + } // find previous element for (; celm; celm = celm->next) { @@ -408,7 +475,8 @@ void tlvdb_change_or_add_node_ex(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, *tlvdb_elm = tnewelm; tnewelm_linked = true; } - if (! tnewelm_linked) { + + if (!tnewelm_linked) { tlvdb_free(tnewelm); } } @@ -423,8 +491,9 @@ void tlvdb_change_or_add_node(struct tlvdb *tlvdb, tlv_tag_t tag, size_t len, co void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) { struct tlvdb *next = NULL; - if (!tlvdb) + if (tlvdb == NULL) { return; + } for (; tlvdb; tlvdb = next) { next = tlvdb->next; @@ -434,12 +503,14 @@ void tlvdb_visit(const struct tlvdb *tlvdb, tlv_cb cb, void *data, int level) { } static const struct tlvdb *tlvdb_next(const struct tlvdb *tlvdb) { - if (tlvdb->children) + if (tlvdb->children) { return tlvdb->children; + } while (tlvdb) { - if (tlvdb->next) + if (tlvdb->next) { return tlvdb->next; + } tlvdb = tlvdb->parent; } @@ -455,8 +526,9 @@ const struct tlv *tlvdb_get(const struct tlvdb *tlvdb, tlv_tag_t tag, const stru while (tlvdb) { - if (tlvdb->tag.tag == tag) + if (tlvdb->tag.tag == tag) { return &tlvdb->tag; + } tlvdb = tlvdb_next(tlvdb); } @@ -523,11 +595,13 @@ bool tlv_is_constructed(const struct tlv *tlv) { } bool tlv_equal(const struct tlv *a, const struct tlv *b) { - if (!a && !b) + if (a == NULL && b == NULL) { return true; + } - if (!a || !b) + if (a == NULL || b == NULL) { return false; + } return a->tag == b->tag && a->len == b->len && !memcmp(a->value, b->value, a->len); } @@ -552,8 +626,9 @@ bool tlvdb_get_uint8(struct tlvdb *tlvRoot, tlv_tag_t tag, uint8_t *value) { bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value) { *value = 0; if (etlv) { - if (etlv->len == 0) + if (etlv->len == 0) { return true; + } if (etlv->len == 1) { *value = etlv->value[0]; @@ -566,8 +641,9 @@ bool tlv_get_uint8(const struct tlv *etlv, uint8_t *value) { bool tlv_get_int(const struct tlv *etlv, int *value) { *value = 0; if (etlv) { - if (etlv->len == 0) + if (etlv->len == 0) { return true; + } if (etlv->len <= 4) { for (int i = 0; i < etlv->len; i++) { diff --git a/client/src/emv/tlv.h b/client/src/emv/tlv.h index 50d046659..39f1bcacd 100644 --- a/client/src/emv/tlv.h +++ b/client/src/emv/tlv.h @@ -31,14 +31,31 @@ struct tlv { const unsigned char *value; }; -struct tlvdb; +struct tlvdb { + struct tlv tag; + struct tlvdb *next; + struct tlvdb *parent; + struct tlvdb *children; +}; + +struct tlvdb_root { + struct tlvdb db; + size_t len; + unsigned char buf[0]; +}; + typedef void (*tlv_cb)(void *data, const struct tlv *tlv, int level, bool is_leaf); struct tlvdb *tlvdb_fixed(tlv_tag_t tag, size_t len, const unsigned char *value); struct tlvdb *tlvdb_external(tlv_tag_t tag, size_t len, const unsigned char *value); struct tlvdb *tlvdb_parse(const unsigned char *buf, size_t len); struct tlvdb *tlvdb_parse_multi(const unsigned char *buf, size_t len); + +bool tlvdb_parse_root(struct tlvdb_root *root); +bool tlvdb_parse_root_multi(struct tlvdb_root *root); + void tlvdb_free(struct tlvdb *tlvdb); +void tlvdb_root_free(struct tlvdb_root *root); struct tlvdb *tlvdb_elm_get_next(struct tlvdb *tlvdb); struct tlvdb *tlvdb_elm_get_children(struct tlvdb *tlvdb); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 026193702..8a6049d3c 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -28,6 +28,7 @@ #include "util.h" #include "cmdhficlass.h" // pagemap #include "protocols.h" // iclass defines +#include "cmdhftopaz.h" // TOPAZ defines #ifdef _WIN32 #include "scandir.h" @@ -213,18 +214,23 @@ bool create_path(const char *dirname) { } */ -bool setDefaultPath(savePaths_t pathIndex, const char *Path) { +bool setDefaultPath(savePaths_t pathIndex, const char *path) { if (pathIndex < spItemCount) { - if ((Path == NULL) && (g_session.defaultPaths[pathIndex] != NULL)) { + + if ((path == NULL) && (g_session.defaultPaths[pathIndex] != NULL)) { free(g_session.defaultPaths[pathIndex]); g_session.defaultPaths[pathIndex] = NULL; } - if (Path != NULL) { - g_session.defaultPaths[pathIndex] = (char *)realloc(g_session.defaultPaths[pathIndex], strlen(Path) + 1); - strcpy(g_session.defaultPaths[pathIndex], Path); + if (path == NULL) { + return false; } + + size_t len = strlen(path); + + g_session.defaultPaths[pathIndex] = (char *)realloc(g_session.defaultPaths[pathIndex], len + 1); + strcpy(g_session.defaultPaths[pathIndex], path); return true; } return false; @@ -233,54 +239,77 @@ bool setDefaultPath(savePaths_t pathIndex, const char *Path) { static char *filenamemcopy(const char *preferredName, const char *suffix) { if (preferredName == NULL) return NULL; if (suffix == NULL) return NULL; + char *fileName = (char *) calloc(strlen(preferredName) + strlen(suffix) + 1, sizeof(uint8_t)); - if (fileName == NULL) + if (fileName == NULL) { return NULL; + } + strcpy(fileName, preferredName); - if (str_endswith(fileName, suffix)) + if (str_endswith(fileName, suffix)) { return fileName; + } + strcat(fileName, suffix); return fileName; } +static size_t path_size(savePaths_t a) { + if (a == spItemCount) { + return 0; + } + return strlen(g_session.defaultPaths[a]); +} + char *newfilenamemcopy(const char *preferredName, const char *suffix) { - if (preferredName == NULL) return NULL; - if (suffix == NULL) return NULL; + if (preferredName == NULL || suffix == NULL) { + return NULL; + } uint16_t p_namelen = strlen(preferredName); if (str_endswith(preferredName, suffix)) p_namelen -= strlen(suffix); - const size_t fileNameLen = p_namelen + strlen(suffix) + 1 + 10; - const size_t fileNameSize = fileNameLen * sizeof(uint8_t); + // 10: room for filenum to ensure new filename + const size_t len = p_namelen + strlen(suffix) + 1 + 10; - char *fileName = (char *) calloc(fileNameLen, sizeof(uint8_t)); // 10: room for filenum to ensure new filename + int foobar = path_size(spDefault); + (void) foobar; + + char *fileName = (char *) calloc(len, sizeof(uint8_t)); if (fileName == NULL) { return NULL; } - int num = 1; - snprintf(fileName, fileNameSize, "%.*s%s", p_namelen, preferredName, suffix); + + snprintf(fileName, len, "%.*s%s", p_namelen, preferredName, suffix); while (fileExists(fileName)) { - snprintf(fileName, fileNameSize, "%.*s-%d%s", p_namelen, preferredName, num, suffix); + snprintf(fileName, len, "%.*s-%d%s", p_namelen, preferredName, num, suffix); num++; } + + PrintAndLogEx(INFO, "FILE PATH: %s", fileName); return fileName; } int saveFile(const char *preferredName, const char *suffix, const void *data, size_t datalen) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } + char *fileName = newfilenamemcopy(preferredName, suffix); - if (fileName == NULL) return PM3_EMALLOC; + if (fileName == NULL) { + return PM3_EMALLOC; + } - /* We should have a valid filename now, e.g. dumpdata-3.bin */ + // We should have a valid filename now, e.g. dumpdata-3.bin - /*Opening file for writing in binary mode*/ + // Opening file for writing in binary mode FILE *f = fopen(fileName, "wb"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); free(fileName); return PM3_EFILE; } @@ -294,20 +323,25 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t blocksize) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } + char *fileName = newfilenamemcopy(preferredName, ".eml"); - if (fileName == NULL) return PM3_EMALLOC; + if (fileName == NULL) { + return PM3_EMALLOC; + } int retval = PM3_SUCCESS; int blocks = datalen / blocksize; uint16_t currblock = 1; - /* We should have a valid filename now, e.g. dumpdata-3.bin */ + // We should have a valid filename now, e.g. dumpdata-3.bin - /*Opening file for writing in text mode*/ + // Opening file for writing in text mode FILE *f = fopen(fileName, "w+"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); retval = PM3_EFILE; goto out; } @@ -342,10 +376,14 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s } int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *)) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } char *fileName = newfilenamemcopy(preferredName, ".json"); - if (fileName == NULL) return PM3_EMALLOC; + if (fileName == NULL) { + return PM3_EMALLOC; + } int retval = PM3_SUCCESS; @@ -396,6 +434,19 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, } break; } + case jsfFudan: { + iso14a_mf_extdump_t *xdump = (iso14a_mf_extdump_t *)(void *) data; + JsonSaveStr(root, "FileType", "fudan"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1); + for (size_t i = 0; i < (xdump->dumplen / 4); i++) { + char path[PATH_MAX_LENGTH] = {0}; + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 4], 4); + } + break; + } case jsfMfuMemory: { JsonSaveStr(root, "FileType", "mfu"); @@ -619,6 +670,24 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, (*callback)(root); break; } + case jsfTopaz: { + topaz_tag_t *tag = (topaz_tag_t *)(void *) data; + JsonSaveStr(root, "FileType", "topaz"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", tag->uid, sizeof(tag->uid)); + JsonSaveBufAsHexCompact(root, "$.Card.H0R1", tag->HR01, sizeof(tag->HR01)); + JsonSaveBufAsHexCompact(root, "$.Card.Size", (uint8_t *) & (tag->size), 2); + for (size_t i = 0; i < TOPAZ_STATIC_MEMORY / 8; i++) { + char path[PATH_MAX_LENGTH] = {0}; + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &tag->data_blocks[i][0], TOPAZ_BLOCK_SIZE); + } + + // ICEMAN todo: add dynamic memory. + // uint16_z Size + // uint8_t *dynamic_memory; + + break; + } default: break; } @@ -672,9 +741,15 @@ int saveFileJSONrootEx(const char *preferredName, void *root, size_t flags, bool int saveFileWAVE(const char *preferredName, const int *data, size_t datalen) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } + char *fileName = newfilenamemcopy(preferredName, ".wav"); - if (fileName == NULL) return PM3_EMALLOC; + if (fileName == NULL) { + return PM3_EMALLOC; + } + int retval = PM3_SUCCESS; struct wave_info_t wave_info = { @@ -695,15 +770,18 @@ int saveFileWAVE(const char *preferredName, const int *data, size_t datalen) { FILE *wave_file = fopen(fileName, "wb"); if (!wave_file) { - PrintAndLogEx(WARNING, "file not found or locked. "_YELLOW_("'%s'"), fileName); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); retval = PM3_EFILE; goto out; } + fwrite(&wave_info, sizeof(wave_info), 1, wave_file); + for (int i = 0; i < datalen; i++) { uint8_t sample = data[i] + 128; fwrite(&sample, 1, 1, wave_file); } + fclose(wave_file); PrintAndLogEx(SUCCESS, "saved " _YELLOW_("%zu") " bytes to wave file " _YELLOW_("'%s'"), 2 * datalen, fileName); @@ -715,21 +793,27 @@ out: int saveFilePM3(const char *preferredName, int *data, size_t datalen) { - if (data == NULL) return PM3_EINVARG; + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } + char *fileName = newfilenamemcopy(preferredName, ".pm3"); - if (fileName == NULL) return PM3_EMALLOC; + if (fileName == NULL) { + return PM3_EMALLOC; + } int retval = PM3_SUCCESS; FILE *f = fopen(fileName, "w"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. "_YELLOW_("'%s'"), fileName); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); retval = PM3_EFILE; goto out; } - for (uint32_t i = 0; i < datalen; i++) + for (uint32_t i = 0; i < datalen; i++) { fprintf(f, "%d\n", data[i]); + } fflush(f); fclose(f); @@ -855,7 +939,7 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, FILE *f = fopen(path, "rb"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; } @@ -966,7 +1050,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { FILE *f = fopen(path, "r"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; } @@ -1078,27 +1162,57 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz goto out; } - uint8_t *udata = (uint8_t *)data; + typedef union UDATA { + void *v; + uint8_t *bytes; + mfu_dump_t *mfu; + topaz_tag_t *topaz; + } UDATA; + UDATA udata = (UDATA)data; char ctype[100] = {0}; JsonLoadStr(root, "$.FileType", ctype); if (!strcmp(ctype, "raw")) { - JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); + JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); } if (!strcmp(ctype, "mfcard")) { size_t sptr = 0; for (int i = 0; i < 256; i++) { + char blocks[30] = {0}; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + + size_t len = 0; + uint8_t block[16]; + JsonLoadBufAsHex(root, blocks, block, 16, &len); + if (!len) + break; + if (sptr + 16 > maxdatalen) { retval = PM3_EMALLOC; goto out; } + memcpy(&udata.bytes[sptr], block, 16); + sptr += len; + } + + *datalen = sptr; + } + + if (!strcmp(ctype, "fudan")) { + size_t sptr = 0; + for (int i = 0; i < 256; i++) { + if (sptr + 4 > maxdatalen) { + retval = PM3_EMALLOC; + goto out; + } + char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata[sptr], 16, &len); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); if (!len) break; @@ -1110,18 +1224,16 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz if (!strcmp(ctype, "mfu")) { - mfu_dump_t *mem = (mfu_dump_t *)udata; - - JsonLoadBufAsHex(root, "$.Card.Version", mem->version, sizeof(mem->version), datalen); - JsonLoadBufAsHex(root, "$.Card.TBO_0", mem->tbo, sizeof(mem->tbo), datalen); - JsonLoadBufAsHex(root, "$.Card.TBO_1", mem->tbo1, sizeof(mem->tbo1), datalen); - JsonLoadBufAsHex(root, "$.Card.Signature", mem->signature, sizeof(mem->signature), datalen); - JsonLoadBufAsHex(root, "$.Card.Counter0", &mem->counter_tearing[0][0], 3, datalen); - JsonLoadBufAsHex(root, "$.Card.Tearing0", &mem->counter_tearing[0][3], 1, datalen); - JsonLoadBufAsHex(root, "$.Card.Counter1", &mem->counter_tearing[1][0], 3, datalen); - JsonLoadBufAsHex(root, "$.Card.Tearing1", &mem->counter_tearing[1][3], 1, datalen); - JsonLoadBufAsHex(root, "$.Card.Counter2", &mem->counter_tearing[2][0], 3, datalen); - JsonLoadBufAsHex(root, "$.Card.Tearing2", &mem->counter_tearing[2][3], 1, datalen); + JsonLoadBufAsHex(root, "$.Card.Version", udata.mfu->version, sizeof(udata.mfu->version), datalen); + JsonLoadBufAsHex(root, "$.Card.TBO_0", udata.mfu->tbo, sizeof(udata.mfu->tbo), datalen); + JsonLoadBufAsHex(root, "$.Card.TBO_1", udata.mfu->tbo1, sizeof(udata.mfu->tbo1), datalen); + JsonLoadBufAsHex(root, "$.Card.Signature", udata.mfu->signature, sizeof(udata.mfu->signature), datalen); + JsonLoadBufAsHex(root, "$.Card.Counter0", &udata.mfu->counter_tearing[0][0], 3, datalen); + JsonLoadBufAsHex(root, "$.Card.Tearing0", &udata.mfu->counter_tearing[0][3], 1, datalen); + JsonLoadBufAsHex(root, "$.Card.Counter1", &udata.mfu->counter_tearing[1][0], 3, datalen); + JsonLoadBufAsHex(root, "$.Card.Tearing1", &udata.mfu->counter_tearing[1][3], 1, datalen); + JsonLoadBufAsHex(root, "$.Card.Counter2", &udata.mfu->counter_tearing[2][0], 3, datalen); + JsonLoadBufAsHex(root, "$.Card.Tearing2", &udata.mfu->counter_tearing[2][3], 1, datalen); *datalen = MFU_DUMP_PREFIX_LENGTH; size_t sptr = 0; @@ -1135,15 +1247,15 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); size_t len = 0; - JsonLoadBufAsHex(root, blocks, &mem->data[sptr], MFU_BLOCK_SIZE, &len); + JsonLoadBufAsHex(root, blocks, &udata.mfu->data[sptr], MFU_BLOCK_SIZE, &len); if (!len) break; sptr += len; - mem->pages++; + udata.mfu->pages++; } // remove one, since pages indicates a index rather than number of available pages - --mem->pages; + --udata.mfu->pages; *datalen += sptr; } @@ -1160,7 +1272,7 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata[sptr], 4, &len); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); if (!len) break; @@ -1182,7 +1294,7 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata[sptr], 8, &len); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); if (!len) break; @@ -1203,7 +1315,7 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata[sptr], 4, &len); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); if (!len) break; @@ -1224,7 +1336,7 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata[sptr], 4, &len); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); if (!len) break; @@ -1234,13 +1346,44 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz } if (!strcmp(ctype, "15693")) { - JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); + JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); } if (!strcmp(ctype, "legic")) { - JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); + JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); } + if (!strcmp(ctype, "topaz")) { + + JsonLoadBufAsHex(root, "$.Card.UID", udata.topaz->uid, sizeof(udata.topaz->uid), datalen); + JsonLoadBufAsHex(root, "$.Card.HR01", udata.topaz->HR01, sizeof(udata.topaz->HR01), datalen); + JsonLoadBufAsHex(root, "$.Card.Size", (uint8_t *) & (udata.topaz->size), 2, datalen); + + size_t sptr = 0; + for (int i = 0; i < (TOPAZ_STATIC_MEMORY / 8); i++) { + + if (sptr + TOPAZ_BLOCK_SIZE > maxdatalen) { + retval = PM3_EMALLOC; + goto out; + } + + char blocks[30] = {0}; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + + size_t len = 0; + JsonLoadBufAsHex(root, blocks, &udata.topaz->data_blocks[sptr][0], TOPAZ_BLOCK_SIZE, &len); + if (!len) + break; + + sptr += len; + + // ICEMAN todo: add dynamic memory. + // uint16_z Size + // uint8_t *dynamic_memory; + } + + *datalen += sptr; + } out: if (callback != NULL) { @@ -1284,6 +1427,7 @@ int loadFileJSONroot(const char *preferredName, void **proot, bool verbose) { return retval; } +// iceman: todo - move all unsafe functions like this from client source. int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint32_t *keycnt) { // t5577 == 4 bytes // mifare == 6 bytes @@ -1320,7 +1464,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale FILE *f = fopen(path, "r"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); retval = PM3_EFILE; goto out; } @@ -1423,7 +1567,7 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key FILE *f = fopen(path, "r"); if (!f) { - PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); retval = PM3_EFILE; goto out; } @@ -1476,6 +1620,57 @@ out: return retval; } +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "rb"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize <= 0) { + PrintAndLogEx(FAILED, "error, when getting filesize"); + fclose(f); + return PM3_EFILE; + } + + // Half is KEY A, half is KEY B + fsize /= 2; + + *keya = calloc(fsize, sizeof(uint8_t)); + if (*keya == NULL) { + PrintAndLogEx(FAILED, "error, cannot allocate memory"); + fclose(f); + return PM3_EMALLOC; + } + + *alen = fread(*keya, 1, fsize, f); + + *keyb = calloc(fsize, sizeof(uint8_t)); + if (*keyb == NULL) { + PrintAndLogEx(FAILED, "error, cannot allocate memory"); + fclose(f); + return PM3_EMALLOC; + } + + *blen = fread(*keyb, 1, fsize, f); + fclose(f); + return PM3_SUCCESS; +} + mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { mfu_df_e retval = MFU_DF_UNKNOWN; @@ -1774,14 +1969,18 @@ static int searchFinalFile(char **foundpath, const char *pm3dir, const char *sea (strcmp(PYTHON_SCRIPTS_SUBDIR, pm3dir) == 0) || (strcmp(RESOURCES_SUBDIR, pm3dir) == 0))) { char *path = calloc(strlen(exec_path) + strlen(pm3dir) + strlen(filename) + 1, sizeof(char)); - if (path == NULL) + if (path == NULL) { goto out; + } + strcpy(path, exec_path); strcat(path, pm3dir); strcat(path, filename); + if ((g_debugMode == 2) && (!silent)) { PrintAndLogEx(INFO, "Searching %s", path); } + if (fileExists(path)) { free(filename); *foundpath = path; @@ -1899,6 +2098,9 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl return PM3_EMALLOC; } res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL); + if (res != PM3_SUCCESS) { + free(*pdump); + } break; } case DICTIONARY: { @@ -1908,8 +2110,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl } if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", fn); - free(*pdump); + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fn); return PM3_EFILE; } @@ -1917,6 +2118,12 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl } int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft, size_t blocksize) { + + if (d == NULL || n == 0) { + PrintAndLogEx(INFO, "No data to save, skipping..."); + return PM3_EINVARG; + } + saveFile(fn, ".bin", d, n); saveFileEML(fn, d, n, blocksize); saveFileJSON(fn, jsft, d, n, NULL); diff --git a/client/src/fileutils.h b/client/src/fileutils.h index c440c7c26..18e0fbd7e 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -47,6 +47,8 @@ typedef enum { jsfEM4x69, jsfEM4x50, jsfFido, + jsfFudan, + jsfTopaz, } JSONFileType; typedef enum { @@ -58,7 +60,9 @@ typedef enum { int fileExists(const char *filename); //bool create_path(const char *dirname); -bool setDefaultPath(savePaths_t pathIndex, const char *Path); // set a path in the path list g_session.defaultPaths + +// set a path in the path list g_session.defaultPaths +bool setDefaultPath(savePaths_t pathIndex, const char *path); char *newfilenamemcopy(const char *preferredName, const char *suffix); @@ -232,6 +236,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale */ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint32_t *keycnt); +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen); typedef enum { MFU_DF_UNKNOWN, @@ -272,5 +277,20 @@ DumpFileType_t getfiletype(const char *filename); */ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen); + +/** STUB + * @brief Utility function to save data to three file files (BIN/EML/JSON). + * It also tries to save according to user preferences set dump folder paths. + * E.g. dumpdata.bin + * E.g. dumpdata.eml + * E.g. dumpdata.json + + * @param fn + * @param d The binary data to write to the file + * @param n the length of the data + * @param jsft json format type for the different memory cards (MFC, MFUL, LEGIC, 14B, 15, ICLASS etc) + * @param blocksize + * @return PM3_SUCCESS if OK + */ int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft, size_t blocksize); #endif // FILEUTILS_H diff --git a/client/src/flash.c b/client/src/flash.c index ad627ffb6..20729b2da 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -489,7 +489,7 @@ static void flash_suggest_update_bootloader(void) { PrintAndLogEx(ERR, "------------- " _CYAN_("Follow these steps") " -------------------"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, " 1) ./pm3-flash-bootrom"); - PrintAndLogEx(ERR, " 2) ./pm3-flash-all"); + PrintAndLogEx(ERR, " 2) ./pm3-flash-fullimage"); PrintAndLogEx(ERR, " 3) ./pm3"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---------------------------------------------------"); @@ -595,6 +595,11 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t return PM3_SUCCESS; } +// Reboot into bootloader +int flash_reboot_bootloader(char *serial_port_name) { + return enter_bootloader(serial_port_name); +} + static int write_block(uint32_t address, uint8_t *data, uint32_t length) { uint8_t block_buf[BLOCK_SIZE]; memset(block_buf, 0xFF, BLOCK_SIZE); @@ -626,6 +631,7 @@ static const char ice[] = _RED_(" . .. .. . . .. ... . . . . . .. . ") "\n...................................................................\n" "...................................................................\n" + "...................................................................\n" ; // Write a file's segments to Flash diff --git a/client/src/flash.h b/client/src/flash.h index 5c2535620..fa5694dbd 100644 --- a/client/src/flash.h +++ b/client/src/flash.h @@ -44,6 +44,7 @@ typedef struct { int flash_load(flash_file_t *ctx, bool force); int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size); int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed); +int flash_reboot_bootloader(char *serial_port_name); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); diff --git a/client/src/graph.c b/client/src/graph.c index 5914c8585..851e49d5a 100644 --- a/client/src/graph.c +++ b/client/src/graph.c @@ -29,21 +29,39 @@ int g_GraphBuffer[MAX_GRAPH_TRACE_LEN]; size_t g_GraphTraceLen; /* write a manchester bit to the graph -TODO, verfy that this doesn't overflow buffer (iceman) */ void AppendGraph(bool redraw, uint16_t clock, int bit) { - uint8_t half = clock / 2; + uint16_t half = clock / 2; + uint16_t end = clock; uint16_t i; + + // overflow/underflow safe checks ... Assumptions: + // _Assert(g_GraphTraceLen >= 0); + // _Assert(g_GraphTraceLen <= MAX_GRAPH_TRACE_LEN); + // If this occurs, allow partial rendering, up to the last sample... + if ((MAX_GRAPH_TRACE_LEN - g_GraphTraceLen) < half) { + PrintAndLogEx(DEBUG, "WARNING: AppendGraph() - Request exceeds max graph length"); + end = MAX_GRAPH_TRACE_LEN - g_GraphTraceLen; + half = end; + } + if ((MAX_GRAPH_TRACE_LEN - g_GraphTraceLen) < end) { + PrintAndLogEx(DEBUG, "WARNING: AppendGraph() - Request exceeds max graph length"); + end = MAX_GRAPH_TRACE_LEN - g_GraphTraceLen; + } + //set first half the clock bit (all 1's or 0's for a 0 or 1 bit) - for (i = 0; i < half; ++i) + for (i = 0; i < half; ++i) { g_GraphBuffer[g_GraphTraceLen++] = bit; + } //set second half of the clock bit (all 0's or 1's for a 0 or 1 bit) - for (; i < clock; ++i) + for (; i < end; ++i) { g_GraphBuffer[g_GraphTraceLen++] = bit ^ 1; + } - if (redraw) + if (redraw) { RepaintGraphWindow(); + } } // clear out our graph window diff --git a/client/src/iso7816/apduinfo.c b/client/src/iso7816/apduinfo.c index b2d879b78..b7d77aca9 100644 --- a/client/src/iso7816/apduinfo.c +++ b/client/src/iso7816/apduinfo.c @@ -440,7 +440,8 @@ int APDUDecode(uint8_t *data, int len, APDU_t *apdu) { int APDUEncode(APDU_t *apdu, uint8_t *data, int *len) { if (len) *len = 0; - + if (apdu == NULL) + return 1; if (apdu->le > 0x10000) return 1; diff --git a/client/src/iso7816/iso7816core.c b/client/src/iso7816/iso7816core.c index 5db7d32aa..7e0dd2c7f 100644 --- a/client/src/iso7816/iso7816core.c +++ b/client/src/iso7816/iso7816core.c @@ -17,9 +17,7 @@ //----------------------------------------------------------------------------- #include "iso7816core.h" - #include - #include "commonutil.h" // ARRAYLEN #include "comms.h" // DropField #include "cmdparser.h" @@ -29,6 +27,7 @@ #include "cmdhf14b.h" #include "iso14b.h" // iso14b_raw_cmd_t #include "util_posix.h" +#include "protocols.h" // ISO7816 APDU return codes //iceman: this logging setting, should be unified with client debug etc. static bool APDULogging = false; @@ -170,7 +169,7 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l *sw = isw; } - if (isw != 0x9000) { + if (isw != ISO7816_OK) { if (APDULogging) { if (*sw >> 8 == 0x61) { PrintAndLogEx(ERR, "APDU chaining len %02x", *sw & 0xFF); diff --git a/client/src/ksx6924/ksx6924core.c b/client/src/ksx6924/ksx6924core.c index 2748ea044..307573f74 100644 --- a/client/src/ksx6924/ksx6924core.c +++ b/client/src/ksx6924/ksx6924core.c @@ -38,6 +38,7 @@ #include "util.h" #include "comms.h" // clearCommandBuffer #include "commonutil.h" // ntohl (pm3 version) +#include "protocols.h" // ISO7816 APDU return codes // Date type. This is the actual on-card format. typedef struct { @@ -66,6 +67,16 @@ typedef struct { uint8_t rfu[8]; } PACKED _ksx6924_internal_purse_info_t; +typedef struct { + uint8_t ALGep; + uint8_t VKep; + uint8_t BALep[4]; // uint32_t big-endian + uint8_t IDcenter; + uint8_t IDep[8]; // bcd + uint8_t NTep[4]; + uint8_t Sign1[4]; +} PACKED _ksx6924_initialize_card_response_t; + // Declares a structure for simple enums. #define MAKE_ENUM_TYPE(KEY_TYPE) \ struct _ksx6924_enum_ ## KEY_TYPE { \ @@ -88,7 +99,7 @@ typedef struct { KEY_TYPE key, const char* defaultValue) { \ struct _ksx6924_enum_ ## KEY_TYPE *r = bsearch( \ &key, KSX6924_ENUM_ ## NAME, \ - sizeof(KSX6924_ENUM_ ## NAME) / sizeof(KSX6924_ENUM_ ## NAME [0]), \ + ARRAYLEN(KSX6924_ENUM_ ## NAME), \ sizeof(KSX6924_ENUM_ ## NAME [0]), \ _ksx6924_ ## KEY_TYPE ## _enum_compare); \ if (r == NULL) { \ @@ -114,17 +125,17 @@ MAKE_ENUM_CONST(Alg, uint8_t, // KSX6924LookupTMoneyIDCenter MAKE_ENUM_CONST(TMoneyIDCenter, uint8_t, -{ 0x00, "reserved" }, +{ 0x00, "Reserved" }, { 0x01, "Korea Financial Telecommunications and Clearings Institute" }, { 0x02, "A-Cash" }, { 0x03, "Mybi" }, - +{ 0x04, "Reserved" }, { 0x05, "V-Cash" }, { 0x06, "Mondex Korea" }, { 0x07, "Korea Expressway Corporation" }, -{ 0x08, "Korea Smart Card Corporation" }, +{ 0x08, "Tmoney Co., Ltd." }, { 0x09, "KORAIL Networks" }, - +{ 0x0a, "Reserved" }, { 0x0b, "EB Card Corporation" }, { 0x0c, "Seoul Bus Transport Association" }, { 0x0d, "Cardnet" }, @@ -165,17 +176,17 @@ MAKE_ENUM_CONST(TMoneyTCode, uint8_t, // KSX6924LookupTMoneyCCode MAKE_ENUM_CONST(TMoneyCCode, uint8_t, { 0x00, "None" }, -{ 0x01, "KB Kookmin Bank" }, -{ 0x02, "Nonghyup Bank" }, +{ 0x01, "KB Card" }, +{ 0x02, "NH Card" }, { 0x03, "Lotte Card" }, { 0x04, "BC Card" }, { 0x05, "Samsung Card" }, -{ 0x06, "Shinhan Bank" }, +{ 0x06, "Shinhan Card" }, { 0x07, "Citibank Korea" }, { 0x08, "Korea Exchange Bank" }, -{ 0x09, "Woori" }, +{ 0x09, "Woori Card" }, { 0x0a, "Hana SK Card" }, -{ 0x0b, "Hyundai Capital Services" }, +{ 0x0b, "Hyundai Card" }, ); static const char *KSX6924_UNKNOWN = "Unknown"; @@ -392,7 +403,7 @@ bool KSX6924TrySelect(void) { return false; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { if (sw) { PrintAndLogEx(FAILED, "Not a KS X 6924 card! APDU response: %04x - %s", @@ -434,7 +445,7 @@ bool KSX6924GetBalance(uint32_t *result) { return false; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { return false; } @@ -446,7 +457,7 @@ bool KSX6924GetBalance(uint32_t *result) { /** * Perform transaction initialization. */ -bool KSX6924InitializeCard(uint8_t mpda1, uint8_t mpda2, uint8_t mpda3, uint8_t mpda4, uint8_t *result) { +bool KSX6924InitializeCard(uint8_t mpda1, uint8_t mpda2, uint8_t mpda3, uint8_t mpda4, uint8_t *result, size_t *result_len) { if (result == NULL) { return false; @@ -466,15 +477,74 @@ bool KSX6924InitializeCard(uint8_t mpda1, uint8_t mpda2, uint8_t mpda3, uint8_t return false; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { return false; } //*result = ntohl(*(uint32_t*)(arr)); memcpy(result, arr, rlen + 2); // skip 2 sw bytes + memcpy(result_len, &rlen, sizeof(size_t)); return true; } +/** + * Parses Initialize Card response + */ +bool KSX6924ParseInitializeCardResponse(const uint8_t *initCardResponse, size_t resp_len, struct ksx6924_initialize_card_response *ret) { + + memset(ret, 0, sizeof(struct ksx6924_initialize_card_response)); + + if (resp_len != sizeof(_ksx6924_initialize_card_response_t)) { + // Invalid size! + PrintAndLogEx(FAILED, "Expected %ld bytes, got %ld\n", sizeof(_ksx6924_initialize_card_response_t), resp_len); + return false; + } + + const _ksx6924_initialize_card_response_t *internalInitCardResponse = (const _ksx6924_initialize_card_response_t *)initCardResponse; + + // Simple copies + ret->ALGep = internalInitCardResponse->ALGep; + ret->VKep = internalInitCardResponse->VKep; + ret->IDcenter = internalInitCardResponse->IDcenter; + + // Fields that need rewriting + hex_to_buffer(ret->IDep, internalInitCardResponse->IDep, + sizeof(internalInitCardResponse->IDep), + sizeof(ret->IDep) - 1, + 0, // min_str_len + 0, // spaces_between + false // uppercase + ); + + ret->BALep = MemBeToUint4byte((uint8_t *)internalInitCardResponse->BALep); + ret->NTep = MemBeToUint4byte((uint8_t *)internalInitCardResponse->NTep); + + memcpy(&ret->Sign1, &internalInitCardResponse->Sign1, 4); + + // TODO + return true; +}; + +/** + * Prints out a Initialize Card response + */ +void KSX6924PrintInitializeCardResponse(const struct ksx6924_initialize_card_response *response) { + + if (response == NULL) { + return; + } + + PrintAndLogEx(INFO, "--- " _CYAN_("KS X 6924 Initialize Card Response") " ---------------------------"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, " ALGep (Algorithm Identifier)........ %02x ( %s )", response->ALGep, KSX6924LookupAlg(response->ALGep, KSX6924_UNKNOWN)); + PrintAndLogEx(INFO, " VKep (Version of Key) .............. %02x", response->VKep); + PrintAndLogEx(INFO, " BALep (Balance...................... %" PRIu32, response->BALep); + PrintAndLogEx(INFO, " IDcenter (Issuer ID) ............... %02x ( %s )", response->IDcenter, KSX6924LookupTMoneyIDCenter(response->IDcenter, KSX6924_UNKNOWN)); + PrintAndLogEx(INFO, " IDep (Card number) ................. %s", response->IDep); + PrintAndLogEx(INFO, " NTep (Number of Transaction + 1) ... %" PRIu32, response->NTep); + PrintAndLogEx(INFO, " Sign1 .............................. %s", sprint_hex(response->Sign1, sizeof(response->Sign1))); + PrintAndLogEx(INFO, ""); +} /** * Issues a proprietary "get record" command (CLA=90, INS=4C). @@ -505,7 +575,7 @@ bool KSX6924ProprietaryGetRecord(uint8_t id, uint8_t *result, size_t result_len) return false; } - if (sw != 0x9000) { + if (sw != ISO7816_OK) { return false; } diff --git a/client/src/ksx6924/ksx6924core.h b/client/src/ksx6924/ksx6924core.h index c207628c8..96f597ec1 100644 --- a/client/src/ksx6924/ksx6924core.h +++ b/client/src/ksx6924/ksx6924core.h @@ -53,6 +53,18 @@ struct ksx6924_purse_info { uint8_t rfu[8]; }; +// Convenience structure for representing purse information. Actual on-card +// format is in _ksx6924_initialize_card_response_t. +struct ksx6924_initialize_card_response { + uint8_t ALGep; + uint8_t VKep; + uint32_t BALep; + uint8_t IDcenter; + uint8_t IDep[17]; // hex digits + null terminator + uint32_t NTep; + uint8_t Sign1[4]; +}; + // Get card type description const char *KSX6924LookupCardType(uint8_t key, const char *defaultValue); @@ -94,7 +106,13 @@ bool KSX6924TrySelect(void); bool KSX6924GetBalance(uint32_t *result); // Perform transaction initialization. -bool KSX6924InitializeCard(uint8_t mpda1, uint8_t mpda2, uint8_t mpda3, uint8_t mpda4, uint8_t *result); +bool KSX6924InitializeCard(uint8_t mpda1, uint8_t mpda2, uint8_t mpda3, uint8_t mpda4, uint8_t *result, size_t *result_len); + +// Parses Initialize Card response +bool KSX6924ParseInitializeCardResponse(const uint8_t *initCardResponse, size_t resp_len, struct ksx6924_initialize_card_response *ret); + +// Prints out a Initialize Card response +void KSX6924PrintInitializeCardResponse(const struct ksx6924_initialize_card_response *response); // Proprietary get record command. Function unknown. // result must be 10 bytes long. diff --git a/client/src/loclass/cipher.c b/client/src/loclass/cipher.c index b69230667..974eb5060 100644 --- a/client/src/loclass/cipher.c +++ b/client/src/loclass/cipher.c @@ -290,9 +290,9 @@ int testMAC(void) { doMAC(cc_nr, div_key, calculated_mac); if (memcmp(calculated_mac, correct_MAC, 4) == 0) { - PrintAndLogEx(SUCCESS, " MAC calculation (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " MAC calculation ( %s )", _GREEN_("ok")); } else { - PrintAndLogEx(FAILED, " MAC calculation (%s)", _RED_("failed")); + PrintAndLogEx(FAILED, " MAC calculation ( %s )", _RED_("fail")); printarr(" Calculated_MAC", calculated_mac, 4); printarr(" Correct_MAC ", correct_MAC, 4); return PM3_ESOFT; diff --git a/client/src/loclass/cipherutils.c b/client/src/loclass/cipherutils.c index eaeee3019..f3572b823 100644 --- a/client/src/loclass/cipherutils.c +++ b/client/src/loclass/cipherutils.c @@ -198,9 +198,9 @@ static int testBitStream(void) { } if (memcmp(input, output, sizeof(input)) == 0) { - PrintAndLogEx(SUCCESS, " Bitstream test 1 (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Bitstream test 1 ( %s )", _GREEN_("ok")); } else { - PrintAndLogEx(FAILED, " Bitstream test 1 (%s)", _RED_("failed")); + PrintAndLogEx(FAILED, " Bitstream test 1 ( %s )", _RED_("fail")); uint8_t i; for (i = 0 ; i < ARRAYLEN(input) ; i++) { PrintAndLogEx(NORMAL, " IN %02x, OUT %02x", input[i], output[i]); @@ -228,9 +228,9 @@ static int testReversedBitstream(void) { } if (memcmp(input, output, sizeof(input)) == 0) { - PrintAndLogEx(SUCCESS, " Bitstream test 2 (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Bitstream test 2 ( %s )", _GREEN_("ok")); } else { - PrintAndLogEx(FAILED, " Bitstream test 2 (%s)", _RED_("failed")); + PrintAndLogEx(FAILED, " Bitstream test 2 ( %s )", _RED_("fail")); uint8_t i; for (i = 0 ; i < ARRAYLEN(input) ; i++) { PrintAndLogEx(NORMAL, " IN %02x, MIDDLE: %02x, OUT %02x", input[i], reverse[i], output[i]); diff --git a/client/src/loclass/cipherutils.h b/client/src/loclass/cipherutils.h index aeba9f33d..160945f0d 100644 --- a/client/src/loclass/cipherutils.h +++ b/client/src/loclass/cipherutils.h @@ -42,14 +42,14 @@ typedef struct { uint8_t *buffer; - uint8_t numbits; - uint8_t position; + uint32_t numbits; + uint32_t position; } BitstreamIn_t; typedef struct { uint8_t *buffer; - uint8_t numbits; - uint8_t position; + uint32_t numbits; + uint32_t position; } BitstreamOut_t; bool headBit(BitstreamIn_t *stream); diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index ec13ca76e..5d2be50e6 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -707,7 +707,7 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { PrintAndLogEx(SUCCESS, "----- " _CYAN_("High security custom key (Kcus)") " -----"); PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); PrintAndLogEx(SUCCESS, "iCLASS format " _GREEN_("%s"), sprint_hex(key64, 8)); - PrintAndLogEx(SUCCESS, "Key verified (" _GREEN_("ok") ")"); + PrintAndLogEx(SUCCESS, "Key verified ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -758,7 +758,7 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { first16bytes[i] = keytable[i] & 0xFF; if ((keytable[i] & LOCLASS_CRACKED) != LOCLASS_CRACKED) { - PrintAndLogEx(WARNING, "Warning: we are missing byte %d, custom key calculation will fail...", i); + PrintAndLogEx(WARNING, "Warning: we are missing byte " _RED_("%d") " , custom key calculation will fail...", i); return PM3_ESOFT; } } @@ -849,7 +849,7 @@ static int _test_iclass_key_permutation(void) { return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, " Iclass key permutation (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Iclass key permutation ( %s )", _GREEN_("ok")); return PM3_SUCCESS; } @@ -892,17 +892,17 @@ int testElite(bool slowtests) { hash2(k_cus, keytable); printarr_human_readable("---------------------- Hash2 ----------------------", keytable, sizeof(keytable)); if (keytable[3] == 0xA1 && keytable[0x30] == 0xA3 && keytable[0x6F] == 0x95) { - PrintAndLogEx(SUCCESS, " hash2 (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " hash2 ( %s )", _GREEN_("ok")); } int res = PM3_SUCCESS; PrintAndLogEx(INFO, "Testing hash1..."); res += _testHash1(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 (%s)", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Testing key diversification..."); res += _test_iclass_key_permutation(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification (%s)", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); if (slowtests) res += _testBruteforce(); diff --git a/client/src/mifare/aiddesfire.c b/client/src/mifare/aiddesfire.c index 0e768daf9..7732a3649 100644 --- a/client/src/mifare/aiddesfire.c +++ b/client/src/mifare/aiddesfire.c @@ -333,7 +333,7 @@ int AIDDFDecodeAndPrint(uint8_t aid[3]) { open_aiddf_file(&df_known_aids, false); char fmt[80]; - snprintf(fmt, sizeof(fmt), " DF AID Function %02X%02X%02X :" _YELLOW_("%s"), aid[2], aid[1], aid[0], "%s"); + snprintf(fmt, sizeof(fmt), " DF AID Function... %02X%02X%02X :" _YELLOW_("%s"), aid[2], aid[1], aid[0], "%s"); print_aiddf_description(df_known_aids, aid, fmt, false); close_aiddf_file(df_known_aids); return PM3_SUCCESS; diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index a89ac2a7a..9c3326f83 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -32,9 +32,9 @@ #include "aes.h" #include "ui.h" #include "crc.h" -#include "crc16.h" // crc16 ccitt +#include "crc16.h" // crc16 ccitt #include "crc32.h" -#include "protocols.h" +#include "protocols.h" // ISO7816 APDU return codes #include "cmdhf14a.h" #include "iso7816/apduinfo.h" // APDU manipulation / errorcodes #include "iso7816/iso7816core.h" // APDU logging @@ -470,7 +470,7 @@ static int DESFIRESendApduEx(bool activate_field, sAPDU_t apdu, uint16_t le, uin if (sw) *sw = isw; - if (isw != 0x9000 && + if (isw != ISO7816_OK && isw != DESFIRE_GET_ISO_STATUS(MFDES_S_OPERATION_OK) && isw != DESFIRE_GET_ISO_STATUS(MFDES_S_SIGNATURE) && isw != DESFIRE_GET_ISO_STATUS(MFDES_S_ADDITIONAL_FRAME) && @@ -942,7 +942,7 @@ void DesfirePrintMADAID(uint32_t appid, bool verbose) { if (appid == 0xffffff) PrintAndLogEx(SUCCESS, " Card issuer information application"); else - MADDFDecodeAndPrint(short_aid); + MADDFDecodeAndPrint(short_aid, verbose); } } @@ -953,7 +953,7 @@ void DesfirePrintAIDFunctions(uint32_t appid) { uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4); PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid); PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8)); - MADDFDecodeAndPrint(short_aid); + MADDFDecodeAndPrint(short_aid, false); } else { AIDDFDecodeAndPrint(aid); } @@ -997,7 +997,7 @@ int DesfireSelectAndAuthenticateEx(DesfireContext_t *dctx, DesfireSecureChannel if (isosw) dctx->cmdSet = DCCISO; - if (!noauth) { + if (noauth == false) { res = DesfireAuthenticate(dctx, secureChannel, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res)); @@ -1655,7 +1655,7 @@ static bool DesfireCheckISOAuthCmd(DesfireISOSelectWay way, uint32_t appID, char uint8_t p2 = ((app_level) ? 0x80 : 0x00) | keyNum; res = DesfireExchangeISO(false, &dctx, (sAPDU_t) {0x00, ISO7816_EXTERNAL_AUTHENTICATION, p1, p2, rndlen * 2, piccrnd}, 0, resp, &resplen, &sw); DropField(); - return (sw == 0x9000 || sw == 0x6982); + return (sw == ISO7816_OK || sw == ISO7816_SECURITY_STATUS_NOT_SATISFIED); } void DesfireCheckAuthCommands(DesfireISOSelectWay way, uint32_t appID, char *dfname, uint8_t keyNum, AuthCommandsChk_t *authCmdCheck) { @@ -1746,7 +1746,12 @@ int DesfireFillAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS ap int indx = AppListSearchAID(DesfireAIDByteToUint(&buf[i * 24 + 1]), appList, PICCInfo->appCount); if (indx >= 0) { appList[indx].appISONum = MemLeToUint2byte(&buf[i * 24 + 1 + 3]); - memcpy(appList[indx].appDFName, &buf[i * 24 + 1 + 5], strnlen((char *)&buf[i * 24 + 1 + 5], 16)); + memcpy( + appList[indx].appDFName, + &buf[i * 24 + 1 + 5], + // strnlen((char *)&buf[i * 24 + 1 + 5], 16) + 16 + ); } } } @@ -1822,12 +1827,14 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("Applications list") " ---------------------------------"); for (int i = 0; i < PICCInfo->appCount; i++) { - PrintAndLogEx(SUCCESS, _CYAN_("Application number: 0x%02x") " iso id: " _GREEN_("0x%04x") " name: " _GREEN_("%s"), appList[i].appNum, appList[i].appISONum, appList[i].appDFName); + PrintAndLogEx(SUCCESS, _CYAN_("Application number: 0x%02X"), appList[i].appNum); + PrintAndLogEx(SUCCESS, " ISO id.... " _GREEN_("0x%04X"), appList[i].appISONum); + PrintAndLogEx(SUCCESS, " DF name... " _GREEN_("%s") " ( %s)", appList[i].appDFName, sprint_hex((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName))); DesfirePrintAIDFunctions(appList[i].appNum); if (PICCInfo->authCmdCheck.checked) { - PrintAndLogEx(SUCCESS, "Auth commands: " NOLF); + PrintAndLogEx(SUCCESS, "Auth commands: "); DesfireCheckAuthCommandsPrint(&appList[i].authCmdCheck); PrintAndLogEx(SUCCESS, ""); } @@ -2294,9 +2301,9 @@ static const char *GetDesfireKeyType(uint8_t keytype) { } const char *GetDesfireAccessRightStr(uint8_t right) { - static char int_access_str[200]; if (right <= 0x0d) { + static char int_access_str[200]; snprintf(int_access_str, sizeof(int_access_str), "key 0x%02x", right); return int_access_str; } @@ -2360,10 +2367,10 @@ void DesfireDecodeFileAcessMode(const uint8_t *mode, uint8_t *r, uint8_t *w, uin void DesfirePrintAccessRight(uint8_t *data) { uint8_t r = 0, w = 0, rw = 0, ch = 0; DesfireDecodeFileAcessMode(data, &r, &w, &rw, &ch); - PrintAndLogEx(SUCCESS, "read : %s", GetDesfireAccessRightStr(r)); - PrintAndLogEx(SUCCESS, "write : %s", GetDesfireAccessRightStr(w)); - PrintAndLogEx(SUCCESS, "readwrite: %s", GetDesfireAccessRightStr(rw)); - PrintAndLogEx(SUCCESS, "change : %s", GetDesfireAccessRightStr(ch)); + PrintAndLogEx(SUCCESS, " read......... %s", GetDesfireAccessRightStr(r)); + PrintAndLogEx(SUCCESS, " write........ %s", GetDesfireAccessRightStr(w)); + PrintAndLogEx(SUCCESS, " read/write... %s", GetDesfireAccessRightStr(rw)); + PrintAndLogEx(SUCCESS, " change....... %s", GetDesfireAccessRightStr(ch)); } void DesfireFillFileSettings(uint8_t *data, size_t datalen, FileSettings_t *fsettings) { @@ -2430,22 +2437,31 @@ static void DesfirePrintShortFileTypeSettings(FileSettings_t *fsettings) { switch (fsettings->fileType) { case 0x00: case 0x01: { - PrintAndLogEx(NORMAL, "size: %d [0x%x] " NOLF, fsettings->fileSize, fsettings->fileSize); + PrintAndLogEx(NORMAL, "Size " _YELLOW_("%d") " / " _YELLOW_("0x%X") NOLF, fsettings->fileSize, fsettings->fileSize); break; } case 0x02: { - PrintAndLogEx(NORMAL, "value [%d .. %d] lim cred: 0x%02x (%d [0x%x]) " NOLF, - fsettings->lowerLimit, fsettings->upperLimit, fsettings->limitedCredit, fsettings->value, fsettings->value); + PrintAndLogEx(NORMAL, "Value [%d .. %d] lim cred: 0x%02x (%d [0x%x]) " NOLF, + fsettings->lowerLimit, + fsettings->upperLimit, + fsettings->limitedCredit, + fsettings->value, + fsettings->value + ); break; } case 0x03: case 0x04: { - PrintAndLogEx(NORMAL, "record count %d/%d size: %d [0x%x]b " NOLF, - fsettings->curRecordCount, fsettings->maxRecordCount, fsettings->recordSize, fsettings->recordSize); + PrintAndLogEx(NORMAL, "Rec cnt %d/%d size: %d [0x%x]b " NOLF, + fsettings->curRecordCount, + fsettings->maxRecordCount, + fsettings->recordSize, + fsettings->recordSize + ); break; } case 0x05: { - PrintAndLogEx(NORMAL, "key type: 0x%02x version: 0x%02x " NOLF, fsettings->keyType, fsettings->keyVersion); + PrintAndLogEx(NORMAL, "Key type: 0x%02x ver: 0x%02x " NOLF, fsettings->keyType, fsettings->keyVersion); break; } default: { @@ -2469,8 +2485,8 @@ void DesfirePrintFileSettingsOneLine(FileSettings_t *fsettings) { void DesfirePrintFileSettingsTable(bool printheader, uint8_t id, bool isoidavail, uint16_t isoid, FileSettings_t *fsettings) { if (printheader) { - PrintAndLogEx(SUCCESS, " ID |ISO ID| File type | Mode | Rights: raw, r w rw ch | File settings "); - PrintAndLogEx(SUCCESS, "------------------------------------------------------------------------------------------------------------"); + PrintAndLogEx(SUCCESS, " ID |ISO ID| File type | Mode | Rights: raw, r w rw ch | File settings"); + PrintAndLogEx(SUCCESS, "----------------------------------------------------------------------------------------------------------"); } PrintAndLogEx(SUCCESS, " " _GREEN_("%02x") " |" NOLF, id); if (isoidavail) { @@ -2482,10 +2498,10 @@ void DesfirePrintFileSettingsTable(bool printheader, uint8_t id, bool isoidavail PrintAndLogEx(NORMAL, " |" NOLF); } - PrintAndLogEx(NORMAL, "0x%02x " _CYAN_("%-15s") " |" NOLF, fsettings->fileType, GetDesfireFileType(fsettings->fileType)); + PrintAndLogEx(NORMAL, " 0x%02x " _CYAN_("%-15s") " |" NOLF, fsettings->fileType, GetDesfireFileType(fsettings->fileType)); PrintAndLogEx(NORMAL, " %-5s |" NOLF, GetDesfireCommunicationMode(fsettings->fileCommMode)); - PrintAndLogEx(NORMAL, "%04x, %-4s %-4s %-4s %-4s |" NOLF, + PrintAndLogEx(NORMAL, " %04x, %-4s %-4s %-4s %-4s |" NOLF, fsettings->rawAccessRights, GetDesfireAccessRightShortStr(fsettings->rAccess), GetDesfireAccessRightShortStr(fsettings->wAccess), @@ -2539,15 +2555,12 @@ void DesfirePrintFileSettingsExtended(FileSettings_t *fsettings) { PrintAndLogEx(NORMAL, "change: %s)", GetDesfireAccessRightStr(fsettings->chAccess)); } - static void DesfirePrintFileSettDynPart(uint8_t filetype, uint8_t *data, size_t datalen, uint8_t *dynlen, bool create) { switch (filetype) { case 0x00: case 0x01: { int filesize = MemLeToUint3byte(&data[0]); - - PrintAndLogEx(INFO, "File size : %d (0x%X) bytes", filesize, filesize); - + PrintAndLogEx(INFO, "File size (bytes)... " _YELLOW_("%d") " / " _YELLOW_("0x%X"), filesize, filesize); *dynlen = 3; break; } @@ -2557,15 +2570,23 @@ static void DesfirePrintFileSettDynPart(uint8_t filetype, uint8_t *data, size_t int value = MemLeToUint4byte(&data[8]); uint8_t limited_credit_enabled = data[12]; - PrintAndLogEx(INFO, "Lower limit : %d (0x%08X)", lowerlimit, lowerlimit); - PrintAndLogEx(INFO, "Upper limit : %d (0x%08X)", upperlimit, upperlimit); + PrintAndLogEx(INFO, "Lower limit... %d / 0x%08X", lowerlimit, lowerlimit); + PrintAndLogEx(INFO, "Upper limit... %d / 0x%08X", upperlimit, upperlimit); if (create) { - PrintAndLogEx(INFO, "Value : %d (0x%08X)", value, value); - PrintAndLogEx(INFO, "Limited credit : [%d - %s]", limited_credit_enabled, ((limited_credit_enabled & 1) != 0) ? "enabled" : "disabled"); + PrintAndLogEx(INFO, "Value............ %d / 0x%08X", value, value); + PrintAndLogEx(INFO, "Limited credit... %d - %s" + , limited_credit_enabled + , ((limited_credit_enabled & 1) != 0) ? "enabled" : "disabled" + ); } else { - PrintAndLogEx(INFO, "Limited credit : [%d - %s] %d (0x%08X)", limited_credit_enabled, ((limited_credit_enabled & 1) != 0) ? "enabled" : "disabled", value, value); + PrintAndLogEx(INFO, "Limited credit... %d - %s %d (0x%08X)" + , limited_credit_enabled + , ((limited_credit_enabled & 1) != 0) ? "enabled" : "disabled" + , value + , value + ); } - PrintAndLogEx(INFO, "GetValue access : %s", ((limited_credit_enabled & 0x02) != 0) ? "Free" : "Not Free"); + PrintAndLogEx(INFO, "GetValue access... %s", ((limited_credit_enabled & 0x02) != 0) ? "Free" : "Not Free"); *dynlen = 13; break; @@ -2575,28 +2596,28 @@ static void DesfirePrintFileSettDynPart(uint8_t filetype, uint8_t *data, size_t uint32_t recordsize = MemLeToUint3byte(&data[0]); uint32_t maxrecords = MemLeToUint3byte(&data[3]); uint32_t currentrecord = 0; - if (!create) + if (create == false) currentrecord = MemLeToUint3byte(&data[6]); - PrintAndLogEx(INFO, "Record size : %d (0x%X) bytes", recordsize, recordsize); - PrintAndLogEx(INFO, "Max num records : %d (0x%X)", maxrecords, maxrecords); - PrintAndLogEx(INFO, "Total size : %d (0x%X) bytes", recordsize * maxrecords, recordsize * maxrecords); - if (!create) - PrintAndLogEx(INFO, "Curr num records : %d (0x%X)", currentrecord, currentrecord); + PrintAndLogEx(INFO, "Record size....... %d / 0x%X bytes", recordsize, recordsize); + PrintAndLogEx(INFO, "Max num records... %d / 0x%X", maxrecords, maxrecords); + PrintAndLogEx(INFO, "Total size........ %d / 0x%X bytes", recordsize * maxrecords, recordsize * maxrecords); + if (create == false) + PrintAndLogEx(INFO, "Curr num records... %d / 0x%X", currentrecord, currentrecord); *dynlen = (create) ? 6 : 9; break; } case 0x05: { - PrintAndLogEx(INFO, "Key type [0x%02x] : %s", data[0], GetDesfireKeyType(data[0])); + PrintAndLogEx(INFO, "Key type [0x%02x] ... %s", data[0], GetDesfireKeyType(data[0])); *dynlen = 1; if (create) { - PrintAndLogEx(INFO, "Key : %s", sprint_hex(&data[1], 16)); + PrintAndLogEx(INFO, "Key... %s", sprint_hex(&data[1], 16)); *dynlen += 16; } - PrintAndLogEx(INFO, "Key version : %d (0x%X)", data[*dynlen], data[*dynlen]); + PrintAndLogEx(INFO, "Key version... %d / 0x%X", data[*dynlen], data[*dynlen]); (*dynlen)++; break; } @@ -2608,28 +2629,29 @@ static void DesfirePrintFileSettDynPart(uint8_t filetype, uint8_t *data, size_t void DesfirePrintFileSettings(uint8_t *data, size_t len) { if (len < 6) { - PrintAndLogEx(ERR, "Wrong file settings length: %zu", len); + PrintAndLogEx(ERR, "Wrong file settings length, expected 6> got %zu ", len); return; } uint8_t filetype = data[0]; PrintAndLogEx(INFO, "---- " _CYAN_("File settings") " ----"); - PrintAndLogEx(SUCCESS, "File type [0x%02x] : %s file", filetype, GetDesfireFileType(filetype)); - PrintAndLogEx(SUCCESS, "File comm mode : %s", GetDesfireCommunicationMode(data[1] & 0x03)); + PrintAndLogEx(SUCCESS, "File type " _YELLOW_("0x%02x") " ..... %s file", filetype, GetDesfireFileType(filetype)); + PrintAndLogEx(SUCCESS, "File comm mode...... %s", GetDesfireCommunicationMode(data[1] & 0x03)); bool addaccess = false; if (filetype != 0x05) { addaccess = ((data[1] & 0x80) != 0); - PrintAndLogEx(SUCCESS, "Additional access: %s", (addaccess) ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Additional access... %s", (addaccess) ? "Yes" : "No"); } - PrintAndLogEx(SUCCESS, "Access rights : %04x", MemLeToUint2byte(&data[2])); - DesfirePrintAccessRight(&data[2]); //2 bytes + + PrintAndLogEx(SUCCESS, "Access rights....... %04x", MemLeToUint2byte(&data[2])); + DesfirePrintAccessRight(&data[2]); // 2 bytes uint8_t reclen = 0; DesfirePrintFileSettDynPart(filetype, &data[4], len - 4, &reclen, false); reclen += 4; // static part if (addaccess && filetype != 0x05 && reclen > 0 && len > reclen && len == reclen + data[reclen] * 2) { - PrintAndLogEx(SUCCESS, "Add access records: %d", data[reclen]); + PrintAndLogEx(SUCCESS, "Add access records... %d", data[reclen]); for (int i = 0; i < data[reclen] * 2; i += 2) { PrintAndLogEx(SUCCESS, "Add access rights : [%d] %04x", i / 2, MemLeToUint2byte(&data[reclen + 1 + i])); DesfirePrintAccessRight(&data[reclen + 1 + i]); @@ -2667,11 +2689,11 @@ void DesfirePrintCreateFileSettings(uint8_t filetype, uint8_t *data, size_t len) PrintAndLogEx(INFO, "---- " _CYAN_("Create file settings") " ----"); PrintAndLogEx(SUCCESS, "File type : %s", ftyperec->text); - PrintAndLogEx(SUCCESS, "File number : 0x%02x (%d)", data[0], data[0]); + PrintAndLogEx(SUCCESS, "File number : 0x%02X (%d)", data[0], data[0]); size_t xlen = 1; if (ftyperec->mayHaveISOfid) { if (isoidpresent) { - PrintAndLogEx(SUCCESS, "File ISO number : 0x%04x", MemLeToUint2byte(&data[xlen])); + PrintAndLogEx(SUCCESS, "File ISO number : 0x%04X", MemLeToUint2byte(&data[xlen])); xlen += 2; } else { PrintAndLogEx(SUCCESS, "File ISO number : n/a"); @@ -2683,7 +2705,7 @@ void DesfirePrintCreateFileSettings(uint8_t filetype, uint8_t *data, size_t len) PrintAndLogEx(SUCCESS, "Additional access: %s", (addaccess) ? "Yes" : "No"); xlen++; - PrintAndLogEx(SUCCESS, "Access rights : %04x", MemLeToUint2byte(&data[xlen])); + PrintAndLogEx(SUCCESS, "Access rights : %04X", MemLeToUint2byte(&data[xlen])); DesfirePrintAccessRight(&data[xlen]); xlen += 2; @@ -2837,7 +2859,7 @@ int DesfireISOSelectEx(DesfireContext_t *dctx, bool fieldon, DesfireISOSelectCon size_t xresplen = 0; uint16_t sw = 0; int res = DesfireExchangeISO(fieldon, dctx, (sAPDU_t) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; if (resp != NULL && resplen != NULL) { @@ -2863,7 +2885,7 @@ int DesfireISOSelectDF(DesfireContext_t *dctx, char *dfname, uint8_t *resp, size int DesfireISOGetChallenge(DesfireContext_t *dctx, DesfireCryptoAlgorithm keytype, uint8_t *resp, size_t *resplen) { uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_GET_CHALLENGE, 0x00, 0x00, 0x00, NULL}, DesfireGetRndLenForKey(keytype), resp, resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; @@ -2878,7 +2900,7 @@ int DesfireISOExternalAuth(DesfireContext_t *dctx, bool app_level, uint8_t keynu uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_EXTERNAL_AUTHENTICATION, p1, p2, DesfireGetRndLenForKey(keytype) * 2, data}, 0, resp, &resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; @@ -2891,7 +2913,7 @@ int DesfireISOInternalAuth(DesfireContext_t *dctx, bool app_level, uint8_t keynu uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_INTERNAL_AUTHENTICATION, p1, p2, keylen, data}, keylen * 2, resp, resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; @@ -2907,7 +2929,7 @@ int DesfireISOReadBinary(DesfireContext_t *dctx, bool use_file_id, uint8_t filei uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_READ_BINARY, p1, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; @@ -2926,7 +2948,7 @@ int DesfireISOUpdateBinary(DesfireContext_t *dctx, bool use_file_id, uint8_t fil uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_UPDATE_BINARY, p1, p2, datalen, data}, 0, resp, &resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; @@ -2937,7 +2959,7 @@ int DesfireISOReadRecords(DesfireContext_t *dctx, uint8_t recordnum, bool read_a uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_READ_RECORDS, recordnum, p2, 0, NULL}, (length == 0) ? APDU_INCLUDE_LE_00 : length, resp, resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; @@ -2951,7 +2973,7 @@ int DesfireISOAppendRecord(DesfireContext_t *dctx, uint8_t fileid, uint8_t *data uint16_t sw = 0; int res = DesfireExchangeISO(false, dctx, (sAPDU_t) {0x00, ISO7816_APPEND_RECORD, 0x00, p2, datalen, data}, 0, resp, &resplen, &sw); - if (res == PM3_SUCCESS && sw != 0x9000) + if (res == PM3_SUCCESS && sw != ISO7816_OK) return PM3_ESOFT; return res; diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index f1d5c0b55..023f3cbf6 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -125,6 +125,7 @@ static int print_aid_description(json_t *root, uint16_t aid, char *fmt, bool ver PrintAndLogEx(INFO, fmt, " (unknown)"); return PM3_ENODATA; } + const char *vmad = mad_json_get_str(elm, "mad"); const char *application = mad_json_get_str(elm, "application"); const char *company = mad_json_get_str(elm, "company"); @@ -132,7 +133,7 @@ static int print_aid_description(json_t *root, uint16_t aid, char *fmt, bool ver const char *integrator = mad_json_get_str(elm, "system_integrator"); if (application && company) { - size_t result_len = 4 + strlen(application) + strlen(company); + size_t result_len = 6 + strlen(application) + strlen(company); char result[result_len]; snprintf(result, result_len, " %s [%s]", application, company); PrintAndLogEx(INFO, fmt, result); @@ -187,7 +188,7 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) if (sector0 == NULL) return PM3_EINVARG; - uint8_t GPB = sector0[3 * 16 + 9]; + uint8_t GPB = sector0[(3 * 16) + 9]; if (verbose) PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x"), "GPB", GPB); @@ -213,7 +214,7 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) int res = madCRCCheck(sector0, true, 1); if (verbose && res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x") " (%s)", "CRC8", sector0[16], _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x") " ( %s )", "CRC8", sector0[16], _GREEN_("ok")); if (mad_ver == 2 && sector10) { int res2 = madCRCCheck(sector10, true, 2); @@ -221,7 +222,7 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) res = res2; if (verbose && !res2) - PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x") " (%s)", "CRC8", sector10[0], _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x") " ( %s )", "CRC8", sector10[0], _GREEN_("ok")); } // MA (multi-application card) @@ -316,7 +317,7 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA int ibs = MADInfoByteDecode(sector, swapmad, 1, verbose); - if (ibs) { + if (ibs > 0) { PrintAndLogEx(SUCCESS, "Card publisher sector " _MAGENTA_("0x%02x"), ibs); } else { PrintAndLogEx(WARNING, "Card publisher " _RED_("not") " present " _YELLOW_("0x%02x"), ibs); @@ -353,13 +354,13 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { int res = madCRCCheck(sector, true, 2); if (verbose) { if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "CRC8 (%s)", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "CRC8 ( %s )", _GREEN_("ok")); else - PrintAndLogEx(WARNING, "CRC8 (%s)", _RED_("fail")); + PrintAndLogEx(WARNING, "CRC8 ( %s )", _RED_("fail")); } int ibs = MADInfoByteDecode(sector, swapmad, 2, verbose); - if (ibs) { + if (ibs > 0) { PrintAndLogEx(SUCCESS, "Card publisher sector " _MAGENTA_("0x%02x"), ibs); } else { PrintAndLogEx(WARNING, "Card publisher " _RED_("not") " present " _YELLOW_("0x%02x"), ibs); @@ -389,12 +390,12 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { return PM3_SUCCESS; } -int MADDFDecodeAndPrint(uint32_t short_aid) { +int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) { open_mad_file(&mad_known_aids, false); - char fmt[50]; + char fmt[128]; snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X :" _YELLOW_("%s"), short_aid, "%s"); - print_aid_description(mad_known_aids, short_aid, fmt, false); + print_aid_description(mad_known_aids, short_aid, fmt, verbose); close_mad_file(mad_known_aids); return PM3_SUCCESS; } @@ -405,3 +406,18 @@ bool HasMADKey(uint8_t *d) { return (memcmp(d + (3 * MFBLOCK_SIZE), g_mifare_mad_key, sizeof(g_mifare_mad_key)) == 0); } + +int DetectHID(uint8_t *d, uint16_t manufacture) { + if (d == NULL) + return -1; + + // find HID + for (int i = 1; i < 16; i++) { + uint16_t aid = madGetAID(d, false, 1, i); + if (aid == manufacture) { + return i; + } + } + + return -1; +} diff --git a/client/src/mifare/mad.h b/client/src/mifare/mad.h index cf951a0b7..e1ebec62c 100644 --- a/client/src/mifare/mad.h +++ b/client/src/mifare/mad.h @@ -25,8 +25,9 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2); int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad); int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2); int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose); -int MADDFDecodeAndPrint(uint32_t short_aid); +int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose); int MADCardHolderInfoDecode(uint8_t *data, size_t datalen, bool verbose); void MADPrintHeader(void); bool HasMADKey(uint8_t *d); +int DetectHID(uint8_t *d, uint16_t manufacture); #endif // _MAD_H_ diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index 166408223..1ac6b6fed 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -74,22 +74,37 @@ AccessConditions_t MFAccessConditionsTrailer[] = { }; bool mfValidateAccessConditions(const uint8_t *data) { - uint8_t ndata1 = (data[0]) & 0x0f; - uint8_t ndata2 = (data[0] >> 4) & 0x0f; - uint8_t ndata3 = (data[1]) & 0x0f; - uint8_t data1 = (data[1] >> 4) & 0x0f; - uint8_t data2 = (data[2]) & 0x0f; - uint8_t data3 = (data[2] >> 4) & 0x0f; + uint8_t nd1 = NIBBLE_LOW(data[0]); + uint8_t nd2 = NIBBLE_HIGH(data[0]); + uint8_t nd3 = NIBBLE_LOW(data[1]); + uint8_t d1 = NIBBLE_HIGH(data[1]); + uint8_t d2 = NIBBLE_LOW(data[2]); + uint8_t d3 = NIBBLE_HIGH(data[2]); - return ((ndata1 == (data1 ^ 0xF)) && (ndata2 == (data2 ^ 0xF)) && (ndata3 == (data3 ^ 0xF))); + return ((nd1 == (d1 ^ 0xF)) && (nd2 == (d2 ^ 0xF)) && (nd3 == (d3 ^ 0xF))); +} +bool mfReadOnlyAccessConditions(uint8_t blockn, const uint8_t *data) { + + uint8_t d1 = NIBBLE_HIGH(data[1]) >> blockn; + uint8_t d2 = NIBBLE_LOW(data[2]) >> blockn; + uint8_t d3 = NIBBLE_HIGH(data[2]) >> blockn; + uint8_t cond = (d1 & 0x01) << 2 | (d2 & 0x01) << 1 | (d3 & 0x01); + + if (blockn == 3) { + if ((cond == 0x02) || (cond == 0x06) || (cond == 0x07)) return true; + } else { + if ((cond == 0x02) || (cond == 0x05)) return true; + } + return false; } -const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data) { - uint8_t data1 = ((data[1] >> 4) & 0x0f) >> blockn; - uint8_t data2 = ((data[2]) & 0x0f) >> blockn; - uint8_t data3 = ((data[2] >> 4) & 0x0f) >> blockn; - uint8_t cond = (data1 & 0x01) << 2 | (data2 & 0x01) << 1 | (data3 & 0x01); +const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data) { + uint8_t d1 = NIBBLE_HIGH(data[1]) >> blockn; + uint8_t d2 = NIBBLE_LOW(data[2]) >> blockn; + uint8_t d3 = NIBBLE_HIGH(data[2]) >> blockn; + + uint8_t cond = (d1 & 0x01) << 2 | (d2 & 0x01) << 1 | (d3 & 0x01); if (blockn == 3) { for (int i = 0; i < ARRAYLEN(MFAccessConditionsTrailer); i++) @@ -198,7 +213,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti if (res) { if (!silentMode) PrintAndLogEx(ERR, "Exchange raw error: %d", res); if (dropFieldIfError) DropField(); - return 2; + return PM3_ERFTRANS; } if (verbose) @@ -207,19 +222,19 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti if (datalen < 1) { if (!silentMode) PrintAndLogEx(ERR, "Card response wrong length: %d", datalen); if (dropFieldIfError) DropField(); - return 3; + return PM3_EWRONGANSWER; } if (data[0] != 0x90) { if (!silentMode) PrintAndLogEx(ERR, "Card response error: %02x %s", data[0], mfpGetErrorDescription(data[0])); if (dropFieldIfError) DropField(); - return 3; + return PM3_EWRONGANSWER; } if (datalen != 19) { // code 1b + 16b + crc 2b if (!silentMode) PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen); if (dropFieldIfError) DropField(); - return 3; + return PM3_EWRONGANSWER; } aes_decode(NULL, key, &data[1], RndB, 16); @@ -242,7 +257,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti if (res) { if (!silentMode) PrintAndLogEx(ERR, "Exchange raw error: %d", res); if (dropFieldIfError) DropField(); - return 4; + return PM3_ERFTRANS; } if (verbose) @@ -262,7 +277,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti PrintAndLogEx(ERR, "RndA card: %s", sprint_hex(&raw[4], 16)); } if (dropFieldIfError) DropField(); - return 5; + return PM3_EWRONGANSWER; } if (verbose) { @@ -319,7 +334,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti if (verbose) PrintAndLogEx(INFO, "Authentication OK"); - return 0; + return PM3_SUCCESS; } static int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { diff --git a/client/src/mifare/mifare4.h b/client/src/mifare/mifare4.h index 7905f3ab6..489add9f3 100644 --- a/client/src/mifare/mifare4.h +++ b/client/src/mifare/mifare4.h @@ -71,6 +71,7 @@ int MFPGetSignature(bool activateField, bool leaveSignalON, uint8_t *dataout, in int MFPGetVersion(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); bool mfValidateAccessConditions(const uint8_t *data); +bool mfReadOnlyAccessConditions(uint8_t blockn, const uint8_t *data); const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data); uint8_t mfNumBlocksPerSector(uint8_t sectorNo); diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index 752679f33..7ce83e29f 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -21,6 +21,7 @@ #include "common.h" +#define MFKEY_SIZE 6 #define MFBLOCK_SIZE 16 static const uint64_t g_mifare_default_keys[] = { @@ -28,6 +29,7 @@ static const uint64_t g_mifare_default_keys[] = { 0x000000000000, // Blank key 0xa0a1a2a3a4a5, // NFCForum MAD key 0xd3f7d3f7d3f7, // NDEF public key + 0x4b791bea7bcc, // MFC EV1 Signature B 0xb0b1b2b3b4b5, 0xaabbccddeeff, 0x1a2b3c4d5e6f, @@ -45,7 +47,7 @@ static const uint64_t g_mifare_default_keys[] = { 0x0000014b5c31, 0xb578f38a5c61, 0x96a301bce267, - 0xfc00018778f7, + 0xfc00018778f7, // Public Transport 0x6471a5ef2d1a, // SimonsVoss 0x4E3552426B32, // ID06 0x6A1987C40A21, // Salto @@ -66,9 +68,18 @@ static const uint64_t g_mifare_default_keys[] = { 0x857464D3AAD1, // HTC Eindhoven key 0x08B386463229, // troika 0xe00000000000, // icopy + 0x199404281970, // NSP A + 0x199404281998, // NSP B }; +static const uint8_t g_mifare_default_key[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const uint8_t g_mifare_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}; +static const uint8_t g_mifare_mad_key_b[] = {0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A}; + +// 16 key B D01AFEEB890A +static const uint8_t g_mifare_signature_key_a[] = {0x5C, 0x8F, 0xF9, 0x99, 0x0D, 0xA2}; +static const uint8_t g_mifare_signature_key_b[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; + static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7}; static const uint8_t g_mifarep_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 3ac048958..70e601431 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -35,6 +35,11 @@ #include "util_posix.h" // msclock #include "cmdparser.h" // detection of flash capabilities #include "cmdflashmemspiffs.h" // upload to flash mem +#include "mifaredefault.h" // default keys +#include "protocol_vigik.h" // VIGIK struct +#include "crypto/libpcrypto.h" +#include "util.h" // xor +#include "mbedtls/sha1.h" // SHA1 int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -80,7 +85,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_READER, &resp, 2000)) { if (resp.status == PM3_EOPABORTED) { - return -1; + return resp.status; } struct p { @@ -435,7 +440,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_NESTED, (uint8_t *)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_HF_MIFARE_NESTED, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_NESTED, &resp, 2000) == false) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); return PM3_ETIMEOUT; } @@ -635,104 +640,104 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl memcpy(&statelists[1].nt_enc, package->nt_b, sizeof(package->nt_b)); memcpy(&statelists[1].ks1, package->ks_b, sizeof(package->ks_b)); - // calc keys - pthread_t thread_id[2]; - - // create and run worker threads - for (uint8_t i = 0; i < 2; i++) - pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); - - // wait for threads to terminate: - for (uint8_t i = 0; i < 2; i++) - pthread_join(thread_id[i], (void *)&statelists[i].head.slhead); - - // the first 16 Bits of the cryptostate already contain part of our key. - // Create the intersection of the two lists based on these 16 Bits and - // roll back the cryptostate - p1 = p3 = statelists[0].head.slhead; - p2 = p4 = statelists[1].head.slhead; - - while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { - if (Compare16Bits(p1, p2) == 0) { - - struct Crypto1State savestate; - savestate = *p1; - while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { - *p3 = *p1; - lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0); - p3++; - p1++; - } - savestate = *p2; - while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) { - *p4 = *p2; - lfsr_rollback_word(p4, statelists[1].nt_enc ^ statelists[1].uid, 0); - p4++; - p2++; - } - } else { - while (Compare16Bits(p1, p2) == -1) p1++; - while (Compare16Bits(p1, p2) == 1) p2++; - } - } - - p3->odd = -1; - p3->even = -1; - p4->odd = -1; - p4->even = -1; - statelists[0].len = p3 - statelists[0].head.slhead; - statelists[1].len = p4 - statelists[1].head.slhead; - statelists[0].tail.sltail = --p3; - statelists[1].tail.sltail = --p4; - - // the statelists now contain possible keys. The key we are searching for must be in the - // intersection of both lists - qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64); - qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64); - // Create the intersection - statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); - - -/* - - memcpy(&uid, package->cuid, sizeof(package->cuid)); - - statelists[0].blockNo = package->block; - statelists[0].keyType = package->keytype; - statelists[0].uid = uid; - - memcpy(&statelists[0].nt_enc, package->nt, sizeof(package->nt)); - memcpy(&statelists[0].ks1, package->ks, sizeof(package->ks)); - // calc keys - pthread_t t; + pthread_t thread_id[2]; - // create and run worker thread - pthread_create(&t, NULL, nested_worker_thread, &statelists[0]); + // create and run worker threads + for (uint8_t i = 0; i < 2; i++) + pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); - // wait for thread to terminate: - pthread_join(t, (void *)&statelists[0].head.slhead); + // wait for threads to terminate: + for (uint8_t i = 0; i < 2; i++) + pthread_join(thread_id[i], (void *)&statelists[i].head.slhead); // the first 16 Bits of the cryptostate already contain part of our key. + // Create the intersection of the two lists based on these 16 Bits and + // roll back the cryptostate p1 = p3 = statelists[0].head.slhead; + p2 = p4 = statelists[1].head.slhead; - // create key candidates. - while (p1 <= statelists[0].tail.sltail) { - struct Crypto1State savestate; - savestate = *p1; - while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { - *p3 = *p1; - lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0); - p3++; - p1++; + while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { + if (Compare16Bits(p1, p2) == 0) { + + struct Crypto1State savestate; + savestate = *p1; + while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { + *p3 = *p1; + lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0); + p3++; + p1++; + } + savestate = *p2; + while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) { + *p4 = *p2; + lfsr_rollback_word(p4, statelists[1].nt_enc ^ statelists[1].uid, 0); + p4++; + p2++; + } + } else { + while (Compare16Bits(p1, p2) == -1) p1++; + while (Compare16Bits(p1, p2) == 1) p2++; } } p3->odd = -1; p3->even = -1; + p4->odd = -1; + p4->even = -1; statelists[0].len = p3 - statelists[0].head.slhead; + statelists[1].len = p4 - statelists[1].head.slhead; statelists[0].tail.sltail = --p3; -*/ + statelists[1].tail.sltail = --p4; + + // the statelists now contain possible keys. The key we are searching for must be in the + // intersection of both lists + qsort(statelists[0].head.keyhead, statelists[0].len, sizeof(uint64_t), compare_uint64); + qsort(statelists[1].head.keyhead, statelists[1].len, sizeof(uint64_t), compare_uint64); + // Create the intersection + statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + + + /* + + memcpy(&uid, package->cuid, sizeof(package->cuid)); + + statelists[0].blockNo = package->block; + statelists[0].keyType = package->keytype; + statelists[0].uid = uid; + + memcpy(&statelists[0].nt_enc, package->nt, sizeof(package->nt)); + memcpy(&statelists[0].ks1, package->ks, sizeof(package->ks)); + + // calc keys + pthread_t t; + + // create and run worker thread + pthread_create(&t, NULL, nested_worker_thread, &statelists[0]); + + // wait for thread to terminate: + pthread_join(t, (void *)&statelists[0].head.slhead); + + // the first 16 Bits of the cryptostate already contain part of our key. + p1 = p3 = statelists[0].head.slhead; + + // create key candidates. + while (p1 <= statelists[0].tail.sltail) { + struct Crypto1State savestate; + savestate = *p1; + while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { + *p3 = *p1; + lfsr_rollback_word(p3, statelists[0].nt_enc ^ statelists[0].uid, 0); + p3++; + p1++; + } + } + + p3->odd = -1; + p3->even = -1; + statelists[0].len = p3 - statelists[0].head.slhead; + statelists[0].tail.sltail = --p3; + */ uint32_t keycnt = statelists[0].len; if (keycnt == 0) goto out; @@ -753,16 +758,16 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl // used for mfCheckKeys_file, which needs a header mem = calloc((maxkeysinblock * 6) + 5, sizeof(uint8_t)); - if (mem == NULL) { - free(statelists[0].head.slhead); - return PM3_EMALLOC; - } + if (mem == NULL) { + free(statelists[0].head.slhead); + return PM3_EMALLOC; + } - mem[0] = statelists[0].keyType; - mem[1] = statelists[0].blockNo; - mem[2] = 1; - mem[3] = ((max_keys_chunk >> 8) & 0xFF); - mem[4] = (max_keys_chunk & 0xFF); + mem[0] = statelists[0].keyType; + mem[1] = statelists[0].blockNo; + mem[2] = 1; + mem[3] = ((max_keys_chunk >> 8) & 0xFF); + mem[4] = (max_keys_chunk & 0xFF); p_keyblock = mem + 5; } else { @@ -854,15 +859,15 @@ out: ); free(statelists[0].head.slhead); - free(statelists[1].head.slhead); + free(statelists[1].head.slhead); return PM3_ESOFT; } // MIFARE -int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) { +int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6); + SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, (uint8_t *)key, 6); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; @@ -880,7 +885,7 @@ int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data) return PM3_SUCCESS; } -int mfReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t *data) { +int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { mf_readblock_t payload = { .blockno = blockNo, .keytype = keyType @@ -1034,7 +1039,8 @@ int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t * } int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { - uint8_t block0[16] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xAF}; + uint8_t block0[16] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D}; + // uint8_t block0[16] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09}; uint8_t blockD[16] = {0x00}; uint8_t blockK[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x08, 0x77, 0x8F, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t params = MAGIC_SINGLE; @@ -1157,13 +1163,15 @@ int mfGen3Freeze(void) { } } -int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data) { +int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { struct p { uint8_t blockno; uint8_t pwd[4]; + uint8_t workFlags; } PACKED payload; payload.blockno = blockno; memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + payload.workFlags = workFlags; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_G4_RDBL, (uint8_t *)&payload, sizeof(payload)); @@ -1180,6 +1188,32 @@ int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data) { return PM3_SUCCESS; } +int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { + struct p { + uint8_t blockno; + uint8_t pwd[4]; + uint8_t data[16]; + uint8_t workFlags; + } PACKED payload; + payload.blockno = blockno; + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + memcpy(payload.data, data, sizeof(payload.data)); + payload.workFlags = workFlags; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_WRBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) { + return PM3_EUNDEF; + } + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + // variables uint32_t cuid = 0; // uid part used for crypto1. @@ -1270,6 +1304,8 @@ int detect_classic_nackbug(bool verbose) { if (verbose) PrintAndLogEx(SUCCESS, "press pm3-button on the Proxmark3 device to abort both Proxmark3 and client.\n"); + PrintAndLogEx(INFO, "." NOLF); + while (true) { PrintAndLogEx(NORMAL, "." NOLF); @@ -1376,11 +1412,20 @@ int detect_mf_magic(bool is_mfc) { case MAGIC_GEN_3: PrintAndLogEx(SUCCESS, "Magic capabilities : possibly " _GREEN_("Gen 3 / APDU")); break; + case MAGIC_GEN_4GTU: + PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GTU")); + break; + case MAGIC_GEN_4GDM: + PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM")); + break; case MAGIC_GEN_UNFUSED: PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Write Once / FUID")); break; - case MAGIC_SUPER: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("super card")); + case MAGIC_SUPER_GEN1: + PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Super card (") _CYAN_("Gen 1") _GREEN_(")")); + break; + case MAGIC_SUPER_GEN2: + PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Super card (") _CYAN_("Gen 2") _GREEN_(")")); break; case MAGIC_NTAG21X: PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("NTAG21x")); @@ -1391,18 +1436,348 @@ int detect_mf_magic(bool is_mfc) { return isGeneration; } -int detect_mfc_ev1_signature(uint8_t *signature) { +bool detect_mfc_ev1_signature(void) { + uint64_t key = 0; + int res = mfCheckKeys(69, MF_KEY_B, false, 1, (uint8_t *)g_mifare_signature_key_b, &key); + return (res == PM3_SUCCESS); +} + +int read_mfc_ev1_signature(uint8_t *signature) { if (signature == NULL) { return PM3_EINVARG; } uint8_t sign[32] = {0}; - uint8_t key[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; - int res = mfReadBlock(69, 1, key, sign); + int res = mfReadBlock(69, MF_KEY_B, g_mifare_signature_key_b, sign); if (res == PM3_SUCCESS) { - res = mfReadBlock(70, 1, key, sign + 16); + res = mfReadBlock(70, MF_KEY_B, g_mifare_signature_key_b, sign + 16); if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } } return res; } + +int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) { + if (in == NULL || out == NULL) + return PM3_EINVARG; + + uint8_t blockno = 0; + while (ilen) { + + if (mfIsSectorTrailer(blockno) == false) { + memcpy(out, in, MFBLOCK_SIZE); + } + blockno++; + out += MFBLOCK_SIZE; + in += MFBLOCK_SIZE; + ilen -= MFBLOCK_SIZE; + *olen += MFBLOCK_SIZE; + } + return PM3_SUCCESS; +} + +static const vigik_pk_t vigik_rsa_pk[] = { + {"La Poste Service Universel", 0x07AA, "AB9953CBFCCD9375B6C028ADBAB7584BED15B9CA037FADED9765996F9EA1AB983F3041C90DA3A198804FF90D5D872A96A4988F91F2243B821E01C5021E3ED4E1BA83B7CFECAB0E766D8563164DE0B2412AE4E6EA63804DF5C19C7AA78DC14F608294D732D7C8C67A88C6F84C0F2E3FAFAE34084349E11AB5953AC68729D07715"}, + {"La Poste Service Universel", 0x07AA, "1577D02987C63A95B51AE149430834AEAF3F2E0F4CF8C6887AC6C8D732D79482604FC18DA77A9CC1F54D8063EAE6E42A41B2E04D1663856D760EABECCFB783BAE1D43E1E02C5011E823B24F2918F98A4962A875D0DF94F8098A1A30DC941303F98ABA19E6F996597EDAD7F03CAB915ED4B58B7BAAD28C0B67593CDFCCB5399AB"}, + + {"La Poste Autres Services", 0x07AB, "A6D99B8D902893B04F3F8DE56CB6BF24338FEE897C1BCE6DFD4EBD05B7B1A07FD2EB564BB4F7D35DBFE0A42966C2C137AD156E3DAB62904592BCA20C0BC7B8B1E261EF82D53F52D203843566305A49A22062DECC38C2FE3864CAD08E79219487651E2F79F1C9392B48CAFE1BFFAFF4802AE451E7A283E55A4026AD1E82DF1A15"}, + {"La Poste Autres Services", 0x07AB, "151adf821ead26405ae583a2e751e42a80f4afff1bfeca482b39c9f1792f1e65879421798ed0ca6438fec238ccde6220a2495a3066358403d2523fd582ef61e2b1b8c70b0ca2bc92459062ab3d6e15ad37c1c26629a4e0bf5dd3f7b44b56ebd27fa0b1b705bd4efd6dce1b7c89ee8f3324bfb66ce58d3f4fb09328908d9bd9a6"}, + + {"France Telecom", 0x07AC, "C44DBCD92F9DCF42F4902A87335DBB35D2FF530CDB09814CFA1F4B95A1BD018D099BC6AB69F667B4922AE1ED826E72951AA3E0EAAA7D49A695F04F8CDAAE2D18D10D25BD529CBB05ABF070DC7C041EC35C2BA7F58CC4C349983CC6E11A5CBE828FB8ECBC26F08E1094A6B44C8953C8E1BAFD214DF3E69F430A98CCC75C03669D"}, + {"France Telecom", 0x07AC, "9d66035cc7cc980a439fe6f34d21fdbae1c853894cb4a694108ef026bcecb88f82be5c1ae1c63c9849c3c48cf5a72b5cc31e047cdc70f0ab05bb9c52bd250dd1182daeda8c4ff095a6497daaeae0a31a95726e82ede12a92b467f669abc69b098d01bda1954b1ffa4c8109db0c53ffd235bb5d33872a90f442cf9d2fd9bc4dc4"}, + + {"EDF-GDF", 0x07AD, "B35193DBD2F88A21CDCFFF4BF84F7FC036A991A363DCB3E802407A5E5879DC2127EECFC520779E79E911394882482C87D09A88B0711CBC2973B77FFDAE40EA0001F595072708C558B484AB89D02BCBCB971FF1B80371C0BE30CB13661078078BB68EBCCA524B9DD55EBF7D47D9355AFC95511350CC1103A5DEE847868848B235"}, + {"EDF-GDF", 0x07AD, "35b248888647e8dea50311cc50135195fc5a35d9477dbf5ed59d4b52cabc8eb68b0778106613cb30bec07103b8f11f97cbcb2bd089ab84b458c508270795f50100ea40aefd7fb77329bc1c71b0889ad0872c4882483911e9799e7720c5cfee2721dc79585e7a4002e8b3dc63a391a936c07f4ff84bffcfcd218af8d2db9351b3"}, + {NULL, 0, NULL} +}; + +const char *vigik_get_service(uint16_t service_code) { + for (int i = 0; i < ARRAYLEN(vigik_rsa_pk); ++i) + if (service_code == vigik_rsa_pk[i].code) + return vigik_rsa_pk[i].desc; + + //No match, return default + return vigik_rsa_pk[ARRAYLEN(vigik_rsa_pk) - 1].desc; +} + + +int vigik_verify(mfc_vigik_t *d) { +#define PUBLIC_VIGIK_KEYLEN 128 + + // iso9796 + // Exponent V = 2 + // n = The public modulus n is the product of the secret prime factors p and q. Its length is 1024 bits. + + if (g_debugMode == DEBUG) { + PrintAndLogEx(INFO, "Raw"); + print_hex_noascii_break((uint8_t *)d, sizeof(*d) - sizeof(d->rsa_signature), MFBLOCK_SIZE * 2); + + PrintAndLogEx(INFO, "Raw signature"); + print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2); + } + + /* + int dl = 0; + + param_gethex_to_eol("1C07D46DA3849326D24B3468BD76673F4F3C41827DC413E81E4F3C7804FAC727213059B21D047510D6432448643A92EBFC67FBEDDAB468D13D948B172F5EBC79A0E3FEFDFAF4E81FC7108E070F1E3CD0", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl); + + param_gethex_to_eol("1AB86FE0C17FFFFE4379D5E15A4B2FAFFEFCFA0F1F3F7FA03E7DDDF1E3C78FFFB1F0E23F7FFF51584771C5C18307FEA36CA74E60AA6B0409ACA66A9EC155F4E9112345708A2B8457E722608EE1157408", 0, signature, PUBLIC_VIGIK_KEYLEN, &dl); + signature_len = dl; + */ + + uint8_t rev_sig[128]; + reverse_array_copy(d->rsa_signature, sizeof(d->rsa_signature), rev_sig); + + PrintAndLogEx(INFO, "Raw signature reverse"); + print_hex_noascii_break(rev_sig, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2); + + // t = 0xBC = Implicitly known + // t = 0xCC = look at byte before to determine hash function + // uint8_t T[] = {0x33, 0xCC}; + + // Success decrypt would mean 0x4b BB ... BB BA padding + // padding, message, hash, 8 bits or 16 bits + + // signature = h( C || M1 || h(M2) ) + // 1024 - 786 - 160 - 16 -1 + // salt C + // message M = 96 bytes, 768 bits + // sha1 hash H = 20 bytes, 160 bits + // padding = 20 bytes, 96 bits + + uint8_t i; + bool is_valid = false; + + for (i = 0; i < ARRAYLEN(vigik_rsa_pk); i++) { + if (vigik_rsa_pk[i].desc == NULL) { + break; + } + + mbedtls_mpi RN, E; + mbedtls_mpi_init(&RN); + + // exponent 2 = even + mbedtls_mpi_init(&E); + mbedtls_mpi_add_int(&E, &E, 2); + + int dl = 0; + uint8_t n[PUBLIC_VIGIK_KEYLEN]; + memset(n, 0, sizeof(n)); + param_gethex_to_eol(vigik_rsa_pk[i].n, 0, n, PUBLIC_VIGIK_KEYLEN, &dl); + + // convert + mbedtls_mpi N, s, sqr, res; + mbedtls_mpi_init(&N); + mbedtls_mpi_init(&s); + mbedtls_mpi_init(&sqr); + mbedtls_mpi_init(&res); + + mbedtls_mpi_read_binary(&N, (const unsigned char *)n, PUBLIC_VIGIK_KEYLEN); + + //mbedtls_mpi_read_binary(&s, (const unsigned char*)signature, signature_len); + mbedtls_mpi_read_binary(&s, (const unsigned char *)rev_sig, sizeof(d->rsa_signature)); + + // check is sign < (N/2) + + mbedtls_mpi n_2; + mbedtls_mpi_init(&n_2); + mbedtls_mpi_copy(&n_2, &N); + mbedtls_mpi_shift_r(&n_2, 1); + bool is_less = (mbedtls_mpi_cmp_mpi(&s, &n_2) > 0) ? false : true; + PrintAndLogEx(DEBUG, "z < (N/2) ..... %s", (is_less) ? _GREEN_("YES") : _RED_("NO")); + mbedtls_mpi_free(&n_2); + + + if (is_less) { + mbedtls_mpi_exp_mod(&sqr, &s, &E, &N, &RN); + } else { + continue; + } + + /* + if v is even and + ⎯ if J* mod 8 = 1, then f* = n–J*. + ⎯ if J* mod 8 = 4, then f* = J*, + ⎯ if J* mod 8 = 6, then f* = 2J*, + ⎯ if J* mod 8 = 7, then f* = 2(n–J*), + */ + uint8_t b2 = mbedtls_mpi_get_bit(&sqr, 2); + uint8_t b1 = mbedtls_mpi_get_bit(&sqr, 1); + uint8_t b0 = mbedtls_mpi_get_bit(&sqr, 0); + uint8_t lsb = (b2 << 2) | (b1 << 1) | b0; + + /* + //1 + mbedtls_mpi_sub_mpi(&res, &N, &sqr); + mbedtls_mpi_write_file( "[=] 1... ", &res, 16, NULL ); + // 4 + mbedtls_mpi_copy(&res, &sqr); + mbedtls_mpi_write_file( "[=] 4... ", &res, 16, NULL ); + // 6 + mbedtls_mpi_mul_int(&res, &sqr, 2); + mbedtls_mpi_write_file( "[=] 6... ", &res, 16, NULL ); + // 7 + mbedtls_mpi foo; + mbedtls_mpi_init(&foo); + mbedtls_mpi_sub_mpi(&foo, &N, &sqr); + mbedtls_mpi_mul_int(&res, &foo, 2); + mbedtls_mpi_free(&foo); + mbedtls_mpi_write_file( "[=] 7... ", &res, 16, NULL ); + */ + + switch (lsb) { + case 1: { + mbedtls_mpi_sub_mpi(&res, &N, &sqr); + break; + } + case 4: { + mbedtls_mpi_copy(&res, &sqr); + break; + } + case 6: { + mbedtls_mpi_mul_int(&res, &sqr, 2); + break; + } + case 7: { + mbedtls_mpi foo2; + mbedtls_mpi_init(&foo2); + mbedtls_mpi_sub_mpi(&foo2, &N, &sqr); + mbedtls_mpi_mul_int(&res, &foo2, 2); + mbedtls_mpi_free(&foo2); + break; + } + default: { + continue; + } + } + + PrintAndLogEx(DEBUG, "LSB............ " _GREEN_("%u"), lsb); + if (g_debugMode == DEBUG) { + mbedtls_mpi_write_file("[=] N.............. ", &N, 16, NULL); + mbedtls_mpi_write_file("[=] signature...... ", &s, 16, NULL); + mbedtls_mpi_write_file("[=] square mod n... ", &sqr, 16, NULL); + mbedtls_mpi_write_file("[=] n-fs........... ", &res, 16, NULL); + } + + + uint8_t nfs[128] = {0}; + mbedtls_mpi_write_binary(&res, nfs, sizeof(nfs)); + + // xor 0xDC01 + int count_zero = 0; + for (int x = 0; x < sizeof(nfs); x += 2) { + nfs[x] ^= 0xDC; + nfs[x + 1] ^= 0x01; + + if (nfs[x] == 0x00) + count_zero++; + if (nfs[x + 1] == 0x00) + count_zero++; + } + + if (count_zero > 10) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Message XORED"); + print_hex_noascii_break(nfs, sizeof(nfs), 32); + PrintAndLogEx(INFO, "\n"); + is_valid = true; + break; + } + + /* + if (bar == 0) { + typedef struct vigik_rsa_s { + uint8_t rsa[127]; + uint8_t hash; + } vigik_rsa_t; + + vigik_rsa_t ts; + memcpy(&ts, nfs, sizeof(ts)); + + if ( ts.hash == 0xCC ) { + PrintAndLogEx(INFO, "Hash byte... 0x%02X", ts.hash); + switch(ts.rsa[126]) { + case 0x11: + PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1"); + break; + case 0x22: + PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - RIPEMD"); + break; + case 0x33: + PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - SHA1"); + break; + default: + PrintAndLogEx(INFO, "Hash algo ( 0x%02X ) - " _RED_("err")); + break; + } + } else if ( ts.hash == 0xBC) { + PrintAndLogEx(INFO, "Hash byte... 0x%02X - " _GREEN_("implict"), ts.hash); + } else { + PrintAndLogEx(INFO, "Hash byte... 0x%02x - " _RED_("err"), ts.hash); + } + + PrintAndLogEx(INFO, "Message w padding"); + print_hex_noascii_break(ts.rsa, sizeof(ts.rsa) - 20, 32); + } + */ + + mbedtls_mpi_free(&N); + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&res); + mbedtls_mpi_free(&RN); + mbedtls_mpi_free(&E); + } + + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); + PrintAndLogEx(INFO, "RSA: 1024bit"); + + if (is_valid == false || i == ARRAYLEN(vigik_rsa_pk)) { + PrintAndLogEx(INFO, "Signature:"); + print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2); + PrintAndLogEx(SUCCESS, "Signature verification: " _RED_("failed")); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Signature public key name: " _YELLOW_("%s"), vigik_rsa_pk[i].desc); + PrintAndLogEx(INFO, "Signature public key value:"); + PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n); + PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 64); + PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 128); + PrintAndLogEx(INFO, "%.64s", vigik_rsa_pk[i].n + 192); + + PrintAndLogEx(INFO, "Signature:"); + print_hex_noascii_break(d->rsa_signature, sizeof(d->rsa_signature), MFBLOCK_SIZE * 2); + + PrintAndLogEx(SUCCESS, "Signature verification: " _GREEN_("successful")); + + return PM3_SUCCESS; +} + +int vigik_annotate(mfc_vigik_t *d) { + if (d == NULL) + return PM3_EINVARG; + + PrintAndLogEx(INFO, "Manufacture......... %s", sprint_hex(d->b0, sizeof(d->b0))); + PrintAndLogEx(INFO, "MAD................. %s", sprint_hex(d->mad, sizeof(d->mad))); + PrintAndLogEx(INFO, "Counters............ %u", d->counters); + PrintAndLogEx(INFO, "rtf................. %s", sprint_hex(d->rtf, sizeof(d->rtf))); + PrintAndLogEx(INFO, "Service code........ 0x%08x / %u - " _YELLOW_("%s"), d->service_code, d->service_code, vigik_get_service(d->service_code)); + PrintAndLogEx(INFO, "Info flag........... %u -", d->info_flag); // , sprint_bin(d->info_flag, 1)); + PrintAndLogEx(INFO, "Key version......... %u", d->key_version); + PrintAndLogEx(INFO, "PTR Counter......... %u", d->ptr_counter); + PrintAndLogEx(INFO, "Counter num......... %u", d->counter_num); + PrintAndLogEx(INFO, "Slot access date.... %s", sprint_hex(d->slot_access_date, sizeof(d->slot_access_date))); + PrintAndLogEx(INFO, "Slot dst duration... %u", d->slot_dst_duration); + PrintAndLogEx(INFO, "Other Slots......... %s", sprint_hex(d->other_slots, sizeof(d->other_slots))); + PrintAndLogEx(INFO, "Services counter.... %u", d->services_counter); + PrintAndLogEx(INFO, "Loading date........ %s", sprint_hex(d->loading_date, sizeof(d->loading_date))); + PrintAndLogEx(INFO, "Reserved null....... %u", d->reserved_null); + PrintAndLogEx(INFO, "----------------------------------------------------------------"); + PrintAndLogEx(INFO, ""); + vigik_verify(d); + PrintAndLogEx(INFO, "----------------------------------------------------------------"); + PrintAndLogEx(INFO, ""); + return PM3_SUCCESS; + +} diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 8db9f36bb..ccefbbd40 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -23,6 +23,7 @@ #include "common.h" #include "util.h" // FILE_PATH_SIZE +#include "protocol_vigik.h" #define MIFARE_SECTOR_RETRY 10 @@ -79,8 +80,8 @@ int mfCheckKeys_file(uint8_t *destfn, uint64_t *key); int mfKeyBrute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t *resultkey); -int mfReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data); -int mfReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t *data); +int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data); +int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data); int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); @@ -95,7 +96,8 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); int mfGen3Freeze(void); -int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data); +int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); +int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); @@ -103,7 +105,15 @@ int detect_classic_prng(void); int detect_classic_nackbug(bool verbose); int detect_mf_magic(bool is_mfc); int detect_classic_static_nonce(void); -int detect_mfc_ev1_signature(uint8_t *signature); +bool detect_mfc_ev1_signature(void); +int read_mfc_ev1_signature(uint8_t *signature); + void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted); + +// remove all sector trailers in a MFC dump +int convert_mfc_2_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen); +const char *vigik_get_service(uint16_t service_code); +int vigik_verify(mfc_vigik_t *d); +int vigik_annotate(mfc_vigik_t *d); #endif diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index f4d07ba7d..b995a3e19 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -40,6 +40,7 @@ #define NDEF_XVCARDTEXT "text/x-vcard" + static const char *TypeNameFormat_s[] = { "Empty Record", "Well Known Record", @@ -112,12 +113,12 @@ static const char *URI_s[] = { "urn:nfc:" // 0x23 }; -static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen); -static int ndefDecodePayload(NDEFHeader_t *ndef); +static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, bool verbose); +static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose); static uint16_t ndefTLVGetLength(const uint8_t *data, size_t *indx) { uint16_t len = 0; - if (data[0] == 0xff) { + if (data[0] == 0xFF) { len = (data[1] << 8) + data[2]; *indx += 3; } else { @@ -165,23 +166,25 @@ static int ndefDecodeHeader(uint8_t *data, size_t datalen, NDEFHeader_t *header) return PM3_SUCCESS; } -static int ndefPrintHeader(NDEFHeader_t *header) { - PrintAndLogEx(INFO, _CYAN_("Header info")); +static int ndefPrintHeader(NDEFHeader_t *header, bool verbose) { - PrintAndLogEx(SUCCESS, " %s ....... Message begin", STRBOOL(header->MessageBegin)); - PrintAndLogEx(SUCCESS, " %s ...... Message end", STRBOOL(header->MessageEnd)); - PrintAndLogEx(SUCCESS, " %s ..... Chunk flag", STRBOOL(header->ChunkFlag)); - PrintAndLogEx(SUCCESS, " %s .... Short record bit", STRBOOL(header->ShortRecordBit)); - PrintAndLogEx(SUCCESS, " %s ... ID Len present", STRBOOL(header->IDLenPresent)); - PrintAndLogEx(SUCCESS, ""); + if (verbose) { + PrintAndLogEx(INFO, _CYAN_("Header info")); + PrintAndLogEx(SUCCESS, " %s ....... Message begin", STRBOOL(header->MessageBegin)); + PrintAndLogEx(SUCCESS, " %s ...... Message end", STRBOOL(header->MessageEnd)); + PrintAndLogEx(SUCCESS, " %s ..... Chunk flag", STRBOOL(header->ChunkFlag)); + PrintAndLogEx(SUCCESS, " %s .... Short record bit", STRBOOL(header->ShortRecordBit)); + PrintAndLogEx(SUCCESS, " %s ... ID Len present", STRBOOL(header->IDLenPresent)); + PrintAndLogEx(SUCCESS, ""); - PrintAndLogEx(SUCCESS, " Header length...... %zu", header->len); - PrintAndLogEx(SUCCESS, " Type length........ %zu", header->TypeLen); - PrintAndLogEx(SUCCESS, " Payload length..... %zu", header->PayloadLen); - PrintAndLogEx(SUCCESS, " ID length.......... %zu", header->IDLen); - PrintAndLogEx(SUCCESS, " Record length...... %zu", header->RecLen); + PrintAndLogEx(SUCCESS, " Header length...... %zu", header->len); + PrintAndLogEx(SUCCESS, " Type length........ %zu", header->TypeLen); + PrintAndLogEx(SUCCESS, " Payload length..... %zu", header->PayloadLen); + PrintAndLogEx(SUCCESS, " ID length.......... %zu", header->IDLen); - PrintAndLogEx(SUCCESS, " Type name format... [ 0x%02x ] " _YELLOW_("%s"), header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]); + PrintAndLogEx(SUCCESS, " Type name format... [ 0x%02x ] " _YELLOW_("%s"), header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]); + PrintAndLogEx(SUCCESS, " Record length...... %zu", header->RecLen); + } return PM3_SUCCESS; } @@ -482,26 +485,27 @@ static int ndefDecodePayloadSmartPoster(uint8_t *ndef, size_t ndeflen, bool prin return res; } - if (verbose) { - ndefPrintHeader(&NDEFHeader); - } + ndefPrintHeader(&NDEFHeader, verbose); if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) { - ndefDecodePayload(&NDEFHeader); + ndefDecodePayload(&NDEFHeader, verbose); } - if (NDEFHeader.TypeLen) { - PrintAndLogEx(INFO, "Type data"); - print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); - } - if (NDEFHeader.IDLen) { - PrintAndLogEx(INFO, "ID data"); - print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1); - } - if (NDEFHeader.PayloadLen) { - PrintAndLogEx(INFO, "Payload data"); - print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); + if (verbose) { + if (NDEFHeader.TypeLen) { + PrintAndLogEx(INFO, "Type data"); + print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); + } + if (NDEFHeader.IDLen) { + PrintAndLogEx(INFO, "ID data"); + print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1); + } + if (NDEFHeader.PayloadLen) { + PrintAndLogEx(INFO, "Payload data"); + print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); + } } + // recursive if (NDEFHeader.MessageEnd == false) { ndefDecodePayloadSmartPoster(ndef + NDEFHeader.RecLen, ndeflen - NDEFHeader.RecLen, false, false); @@ -824,7 +828,7 @@ static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { return PM3_SUCCESS; } -static int ndefDecodePayload(NDEFHeader_t *ndef) { +static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { PrintAndLogEx(INFO, ""); switch (ndef->TypeNameFormat) { @@ -867,7 +871,7 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) { } if (!strncmp((char *)ndef->Type, "Sp", ndef->TypeLen)) { - ndefDecodePayloadSmartPoster(ndef->Payload, ndef->PayloadLen, true, false); + ndefDecodePayloadSmartPoster(ndef->Payload, ndef->PayloadLen, true, verbose); } if (!strncmp((char *)ndef->Type, "Di", ndef->TypeLen)) { @@ -946,36 +950,39 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) { return PM3_SUCCESS; } -static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) { +static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, bool verbose) { NDEFHeader_t NDEFHeader = {0}; int res = ndefDecodeHeader(ndefRecord, ndefRecordLen, &NDEFHeader); if (res != PM3_SUCCESS) return res; - ndefPrintHeader(&NDEFHeader); + ndefPrintHeader(&NDEFHeader, verbose); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, _CYAN_("Payload info")); - if (NDEFHeader.TypeLen) { - PrintAndLogEx(INFO, "Type data"); - print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); - } - if (NDEFHeader.IDLen) { - PrintAndLogEx(INFO, "ID data"); - print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1); - } - if (NDEFHeader.PayloadLen) { - PrintAndLogEx(INFO, "Payload data"); - print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); + if (verbose) { + if (NDEFHeader.TypeLen) { + PrintAndLogEx(INFO, "Type data"); + print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); + } + if (NDEFHeader.IDLen) { + PrintAndLogEx(INFO, "ID data"); + print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1); + } + if (NDEFHeader.PayloadLen) { + PrintAndLogEx(INFO, "Payload data"); + print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); + } } + if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) { - ndefDecodePayload(&NDEFHeader); + ndefDecodePayload(&NDEFHeader, verbose); } return PM3_SUCCESS; } -int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) { +int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, bool verbose) { bool firstRec = true; size_t len = 0; size_t counter = 0; @@ -1004,7 +1011,7 @@ int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _CYAN_("Record") " " _YELLOW_("%zu"), counter); PrintAndLogEx(INFO, "-----------------------------------------------------"); - ndefRecordDecodeAndPrint(&ndefRecord[len], NDEFHeader.RecLen); + ndefRecordDecodeAndPrint(&ndefRecord[len], NDEFHeader.RecLen, verbose); len += NDEFHeader.RecLen; @@ -1036,21 +1043,22 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { case 0x01: { indx++; uint16_t len = ndefTLVGetLength(&ndef[indx], &indx); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Lock Control") " ---"); if (len != 3) { - PrintAndLogEx(WARNING, "NDEF Lock Control block size must be 3 instead of %d.", len); + PrintAndLogEx(WARNING, "NDEF Lock Control block size must be 3 instead of %d", len); } else { uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f; uint8_t byte_offset = ndef[indx] & 0x0f; uint8_t Size = ndef[indx + 1]; uint8_t BytesLockedPerLockBit = (ndef[indx + 2] >> 4) & 0x0f; uint8_t bytes_per_page = ndef[indx + 2] & 0x0f; - PrintAndLogEx(SUCCESS, " Pages addr (number of pages)... %d", pages_addr); - PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %d", byte_offset); - PrintAndLogEx(SUCCESS, "Size in bits of the lock area %d. bytes approx %d", Size, Size / 8); - PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page); - PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit"); - PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit is able to lock: %d", BytesLockedPerLockBit); + PrintAndLogEx(SUCCESS, " Pages addr (number of pages).... %d", pages_addr); + PrintAndLogEx(SUCCESS, " Byte offset (number of bytes)... %d", byte_offset); + PrintAndLogEx(SUCCESS, " Lock Area size in bits.......... %d ( %d bytes )", Size, Size / 8); + PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page); + PrintAndLogEx(SUCCESS, " Bytes Locked Per LockBit"); + PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit locks... %d", BytesLockedPerLockBit); } indx += len; break; @@ -1058,18 +1066,19 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { case 0x02: { indx++; uint16_t len = ndefTLVGetLength(&ndef[indx], &indx); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Memory Control") " ---"); if (len != 3) { - PrintAndLogEx(WARNING, "NDEF Memory Control block size must be 3 instead of %d.", len); + PrintAndLogEx(WARNING, "NDEF Memory Control block size must be 3 instead of %u", len); } else { uint8_t pages_addr = (ndef[indx] >> 4) & 0x0f; uint8_t byte_offset = ndef[indx] & 0x0f; uint8_t Size = ndef[indx + 1]; uint8_t bytes_per_page = ndef[indx + 2] & 0x0f; - PrintAndLogEx(SUCCESS, " Pages addr (number of pages) : %d", pages_addr); - PrintAndLogEx(SUCCESS, "Byte offset (number of bytes) : %d", byte_offset); - PrintAndLogEx(SUCCESS, "Size in bits of the reserved area : %d. bytes approx: %d", Size, Size / 8); - PrintAndLogEx(SUCCESS, " Number of bytes / page : %d", bytes_per_page); + PrintAndLogEx(SUCCESS, "Pages addr (number of pages).... %u", pages_addr); + PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %u", byte_offset); + PrintAndLogEx(SUCCESS, "Reserved area size in bits...... %u ( %u bytes )", Size, Size / 8); + PrintAndLogEx(SUCCESS, " Number of bytes / page... %u", bytes_per_page); } indx += len; break; @@ -1077,13 +1086,14 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { case 0x03: { indx++; uint16_t len = ndefTLVGetLength(&ndef[indx], &indx); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "--- " _CYAN_("NDEF Message") " ---"); if (len == 0) { PrintAndLogEx(SUCCESS, "Found NDEF message w zero length"); } else { - PrintAndLogEx(SUCCESS, "Found NDEF message (%d bytes)", len); + PrintAndLogEx(SUCCESS, "Found NDEF message ( " _YELLOW_("%u") " bytes )", len); - int res = NDEFRecordsDecodeAndPrint(&ndef[indx], len); + int res = NDEFRecordsDecodeAndPrint(&ndef[indx], len, verbose); if (res != PM3_SUCCESS) return res; } @@ -1091,15 +1101,16 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { indx += len; break; } - case 0xfd: { + case 0xFD: { indx++; uint16_t len = ndefTLVGetLength(&ndef[indx], &indx); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "--- " _CYAN_("Proprietary info") " ---"); PrintAndLogEx(SUCCESS, " Can't decode, skipping %d bytes", len); indx += len; break; } - case 0xfe: { + case 0xFE: { PrintAndLogEx(SUCCESS, "NDEF Terminator detected"); return PM3_SUCCESS; } diff --git a/client/src/nfc/ndef.h b/client/src/nfc/ndef.h index b69384443..478c28e0a 100644 --- a/client/src/nfc/ndef.h +++ b/client/src/nfc/ndef.h @@ -22,6 +22,8 @@ #include #include "common.h" +#define NDEF_MFC_AID 0xE103 + typedef enum { tnfEmptyRecord = 0x00, tnfWellKnownRecord = 0x01, @@ -72,6 +74,6 @@ typedef struct { } NDEFHeader_t; int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose); -int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen); +int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, bool verbose); #endif // _NDEF_H_ diff --git a/client/src/pm3line_vocabulory.h b/client/src/pm3line_vocabulory.h index 4e4c3948e..e6d6dc438 100644 --- a/client/src/pm3line_vocabulory.h +++ b/client/src/pm3line_vocabulory.h @@ -48,6 +48,7 @@ const static vocabulory_t vocabulory[] = { { 1, "prefs get savepaths" }, { 1, "prefs get emoji" }, { 1, "prefs get hints" }, + { 1, "prefs get output" }, { 1, "prefs get plotsliders" }, { 1, "prefs set help" }, { 1, "prefs set barmode" }, @@ -57,6 +58,7 @@ const static vocabulory_t vocabulory[] = { { 1, "prefs set emoji" }, { 1, "prefs set hints" }, { 1, "prefs set savepaths" }, + { 1, "prefs set output" }, { 1, "prefs set plotsliders" }, { 1, "analyse help" }, { 1, "analyse lcr" }, @@ -105,6 +107,7 @@ const static vocabulory_t vocabulory[] = { { 0, "data hexsamples" }, { 1, "data hex2bin" }, { 1, "data load" }, + { 1, "data num" }, { 1, "data print" }, { 0, "data samples" }, { 1, "data save" }, @@ -132,18 +135,20 @@ const static vocabulory_t vocabulory[] = { { 0, "hf sniff" }, { 1, "hf 14a help" }, { 1, "hf 14a list" }, - { 0, "hf 14a info" }, - { 0, "hf 14a reader" }, - { 0, "hf 14a ndefread" }, - { 0, "hf 14a cuids" }, - { 0, "hf 14a sim" }, - { 0, "hf 14a sniff" }, - { 0, "hf 14a apdu" }, - { 0, "hf 14a chaining" }, - { 0, "hf 14a raw" }, { 0, "hf 14a antifuzz" }, { 0, "hf 14a config" }, + { 0, "hf 14a cuids" }, + { 0, "hf 14a info" }, + { 0, "hf 14a sim" }, + { 0, "hf 14a sniff" }, + { 0, "hf 14a raw" }, + { 0, "hf 14a reader" }, + { 0, "hf 14a apdu" }, { 0, "hf 14a apdufind" }, + { 0, "hf 14a chaining" }, + { 0, "hf 14a ndefformat" }, + { 0, "hf 14a ndefread" }, + { 0, "hf 14a ndefwrite" }, { 1, "hf 14b help" }, { 0, "hf 14b apdu" }, { 0, "hf 14b dump" }, @@ -156,6 +161,7 @@ const static vocabulory_t vocabulory[] = { { 0, "hf 14b sniff" }, { 0, "hf 14b rdbl" }, { 0, "hf 14b sriwrite" }, + { 1, "hf 14b view" }, { 1, "hf 15 help" }, { 1, "hf 15 list" }, { 1, "hf 15 demod" }, @@ -168,8 +174,17 @@ const static vocabulory_t vocabulory[] = { { 0, "hf 15 reader" }, { 0, "hf 15 restore" }, { 0, "hf 15 samples" }, + { 0, "hf 15 eload" }, + { 0, "hf 15 esave" }, + { 0, "hf 15 eview" }, { 0, "hf 15 sim" }, - { 0, "hf 15 slixdisable" }, + { 0, "hf 15 slixwritepwd" }, + { 0, "hf 15 slixeasdisable" }, + { 0, "hf 15 slixeasenable" }, + { 0, "hf 15 slixprivacydisable" }, + { 0, "hf 15 slixprivacyenable" }, + { 0, "hf 15 passprotectafi" }, + { 0, "hf 15 passprotecteas" }, { 0, "hf 15 wrbl" }, { 0, "hf 15 findafi" }, { 0, "hf 15 writeafi" }, @@ -192,7 +207,8 @@ const static vocabulory_t vocabulory[] = { { 1, "hf cipurse test" }, { 1, "hf epa help" }, { 0, "hf epa cnonces" }, - { 0, "hf epa preplay" }, + { 0, "hf epa replay" }, + { 0, "hf epa sim" }, { 1, "hf emrtd help" }, { 0, "hf emrtd dump" }, { 1, "hf emrtd info" }, @@ -222,17 +238,24 @@ const static vocabulory_t vocabulory[] = { { 0, "hf fido auth" }, { 0, "hf fido make" }, { 0, "hf fido assert" }, + { 1, "hf fudan help" }, + { 0, "hf fudan reader" }, + { 0, "hf fudan dump" }, + { 0, "hf fudan rdbl" }, + { 1, "hf fudan view" }, + { 0, "hf fudan wrbl" }, { 1, "hf gallagher help" }, { 0, "hf gallagher reader" }, { 0, "hf gallagher clone" }, { 0, "hf gallagher delete" }, { 1, "hf gallagher diversifykey" }, + { 1, "hf gallagher decode" }, { 1, "hf ksx6924 help" }, - { 0, "hf ksx6924 balance" }, - { 0, "hf ksx6924 info" }, - { 0, "hf ksx6924 initialize" }, - { 0, "hf ksx6924 prec" }, { 0, "hf ksx6924 select" }, + { 0, "hf ksx6924 info" }, + { 0, "hf ksx6924 balance" }, + { 0, "hf ksx6924 init" }, + { 0, "hf ksx6924 prec" }, { 1, "hf jooki help" }, { 0, "hf jooki clone" }, { 1, "hf jooki decode" }, @@ -275,15 +298,17 @@ const static vocabulory_t vocabulory[] = { { 0, "hf legic eload" }, { 0, "hf legic esave" }, { 0, "hf legic eview" }, + { 0, "hf legic einfo" }, { 1, "hf legic crc" }, { 1, "hf legic view" }, { 1, "hf lto help" }, { 0, "hf lto dump" }, - { 0, "hf lto restore" }, { 0, "hf lto info" }, - { 0, "hf lto rdbl" }, - { 0, "hf lto wrbl" }, { 1, "hf lto list" }, + { 0, "hf lto rdbl" }, + { 0, "hf lto reader" }, + { 0, "hf lto restore" }, + { 0, "hf lto wrbl" }, { 1, "hf mf help" }, { 1, "hf mf list" }, { 0, "hf mf darkside" }, @@ -299,8 +324,7 @@ const static vocabulory_t vocabulory[] = { { 0, "hf mf auth4" }, { 1, "hf mf acl" }, { 0, "hf mf dump" }, - { 0, "hf mf mad" }, - { 0, "hf mf ndefread" }, + { 1, "hf mf mad" }, { 0, "hf mf personalize" }, { 0, "hf mf rdbl" }, { 0, "hf mf rdsc" }, @@ -331,7 +355,16 @@ const static vocabulory_t vocabulory[] = { { 0, "hf mf gen3uid" }, { 0, "hf mf gen3blk" }, { 0, "hf mf gen3freeze" }, + { 0, "hf mf ggetblk" }, + { 0, "hf mf gload" }, + { 0, "hf mf gsave" }, + { 0, "hf mf gsetblk" }, { 0, "hf mf gview" }, + { 0, "hf mf gdmconfig" }, + { 0, "hf mf gdmsetblk" }, + { 0, "hf mf ndefformat" }, + { 0, "hf mf ndefread" }, + { 0, "hf mf ndefwrite" }, { 1, "hf mfp help" }, { 0, "hf mfp info" }, { 0, "hf mfp wrp" }, @@ -356,7 +389,9 @@ const static vocabulory_t vocabulory[] = { { 0, "hf mfu restore" }, { 1, "hf mfu view" }, { 0, "hf mfu wrbl" }, + { 0, "hf mfu tamper" }, { 0, "hf mfu eload" }, + { 0, "hf mfu esave" }, { 0, "hf mfu eview" }, { 0, "hf mfu sim" }, { 0, "hf mfu setpwd" }, @@ -414,17 +449,31 @@ const static vocabulory_t vocabulory[] = { { 0, "hf st25ta protect" }, { 0, "hf st25ta pwd" }, { 0, "hf st25ta sim" }, + { 1, "hf tesla help" }, + { 0, "hf tesla info" }, + { 1, "hf tesla list" }, + { 1, "hf texkom help" }, + { 0, "hf texkom reader" }, + { 0, "hf texkom sim" }, { 1, "hf thinfilm help" }, { 0, "hf thinfilm info" }, { 1, "hf thinfilm list" }, { 0, "hf thinfilm sim" }, { 1, "hf topaz help" }, + { 0, "hf topaz dump" }, { 1, "hf topaz list" }, { 0, "hf topaz info" }, { 0, "hf topaz reader" }, { 0, "hf topaz sim" }, { 0, "hf topaz sniff" }, { 0, "hf topaz raw" }, + { 0, "hf topaz rdbl" }, + { 1, "hf topaz view" }, + { 0, "hf topaz wrbl" }, + { 1, "hf xerox help" }, + { 0, "hf xerox info" }, + { 0, "hf xerox reader" }, + { 0, "hf xerox dump" }, { 1, "hf waveshare help" }, { 0, "hf waveshare loadbmp" }, { 1, "hw help" }, @@ -510,6 +559,7 @@ const static vocabulory_t vocabulory[] = { { 0, "lf em 4x50 eview" }, { 0, "lf em 4x50 sim" }, { 1, "lf em 4x70 help" }, + { 0, "lf em 4x70 brute" }, { 0, "lf em 4x70 info" }, { 0, "lf em 4x70 write" }, { 0, "lf em 4x70 unlock" }, @@ -554,6 +604,7 @@ const static vocabulory_t vocabulory[] = { { 0, "lf idteck clone" }, { 0, "lf idteck sim" }, { 1, "lf indala help" }, + { 0, "lf indala brute" }, { 1, "lf indala demod" }, { 1, "lf indala altdemod" }, { 0, "lf indala reader" }, @@ -686,17 +737,27 @@ const static vocabulory_t vocabulory[] = { { 1, "nfc type1 help" }, { 0, "nfc type2 read" }, { 1, "nfc type2 help" }, + { 0, "nfc type4a format" }, { 0, "nfc type4a read" }, + { 0, "nfc type4a write" }, { 0, "nfc type4a st25taread" }, { 1, "nfc type4a help" }, { 0, "nfc type4b read" }, { 1, "nfc type4b help" }, + { 0, "nfc mf cformat" }, { 0, "nfc mf cread" }, + { 0, "nfc mf cwrite" }, { 0, "nfc mf pread" }, { 1, "nfc mf help" }, { 0, "nfc barcode read" }, { 0, "nfc barcode sim" }, { 1, "nfc barcode help" }, + { 1, "piv help" }, + { 0, "piv select" }, + { 0, "piv getdata" }, + { 0, "piv authsign" }, + { 0, "piv scan" }, + { 1, "piv list" }, { 1, "smart help" }, { 1, "smart list" }, { 0, "smart info" }, diff --git a/client/src/preferences.c b/client/src/preferences.c index 7195b2393..99fb20d30 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -63,6 +63,7 @@ int preferences_load(void) { g_session.overlay.w = g_session.plot.w; g_session.overlay_sliders = true; g_session.show_hints = true; + g_session.dense_output = false; g_session.bar_mode = STYLE_VALUE; setDefaultPath(spDefault, ""); @@ -151,7 +152,7 @@ int preferences_save(void) { } uint8_t dummyData = 0x00; - size_t dummyDL = 0x00; + size_t dummyDL = 0x01; if (saveFileJSON(fn, jsfCustom, &dummyData, dummyDL, &preferences_save_callback) != PM3_SUCCESS) PrintAndLogEx(ERR, "Error saving preferences to \"%s\"", fn); @@ -185,6 +186,8 @@ void preferences_save_callback(json_t *root) { JsonSaveBoolean(root, "show.hints", g_session.show_hints); + JsonSaveBoolean(root, "output.dense", g_session.dense_output); + JsonSaveBoolean(root, "os.supports.colors", g_session.supports_colors); JsonSaveStr(root, "file.default.savepath", g_session.defaultPaths[spDefault]); @@ -319,6 +322,9 @@ void preferences_load_callback(json_t *root) { if (json_unpack_ex(root, &up_error, 0, "{s:b}", "show.hints", &b1) == 0) g_session.show_hints = (bool)b1; + if (json_unpack_ex(root, &up_error, 0, "{s:b}", "output.dense", &b1) == 0) + g_session.dense_output = (bool)b1; + if (json_unpack_ex(root, &up_error, 0, "{s:b}", "os.supports.colors", &b1) == 0) g_session.supports_colors = (bool)b1; @@ -517,6 +523,11 @@ static void showBarModeState(prefShowOpt_t opt) { } } +static void showOutputState(prefShowOpt_t opt) { + PrintAndLogEx(INFO, " %s output................. %s", prefShowMsg(opt), + g_session.dense_output ? _GREEN_("dense") : _WHITE_("normal")); +} + static void showClientExeDelayState(void) { PrintAndLogEx(INFO, " Cmd execution delay.... "_GREEN_("%u"), g_session.client_exe_delay); } @@ -738,6 +749,49 @@ static int setCmdDeviceDebug (const char *Cmd) } */ +static int setCmdOutput(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs set output", + "Set dump output style to condense consecutive repeated data", + "prefs set output --normal --> sets the output style to normal\n" + "prefs set output --dense --> sets the output style to dense" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "normal", "normal output"), + arg_lit0(NULL, "dense", "dense output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool use_off = arg_get_lit(ctx, 1); + bool use_on = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if ((use_off + use_on) > 1) { + PrintAndLogEx(FAILED, "Can only set one option"); + return PM3_EINVARG; + } + + bool new_value = g_session.dense_output; + if (use_off) { + new_value = false; + } + if (use_on) { + new_value = true; + } + + if (g_session.dense_output != new_value) { + showOutputState(prefShowOLD); + g_session.dense_output = new_value; + showOutputState(prefShowNEW); + preferences_save(); + } else { + showOutputState(prefShowNone); + } + + return PM3_SUCCESS; +} static int setCmdExeDelay(const char *Cmd) { CLIParserContext *ctx; @@ -1044,6 +1098,22 @@ static int getCmdDebug(const char *Cmd) { return PM3_SUCCESS; } +static int getCmdOutput(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get output", + "Get preference of dump output style", + "prefs get output" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + showOutputState(prefShowNone); + return PM3_SUCCESS; +} + static int getCmdPlotSlider(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs get plotsliders", @@ -1119,6 +1189,7 @@ static command_t CommandTableGet[] = { // {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"}, {"emoji", getCmdEmoji, AlwaysAvailable, "Get emoji display preference"}, {"hints", getCmdHint, AlwaysAvailable, "Get hint display preference"}, + {"output", getCmdOutput, AlwaysAvailable, "Get dump output style preference"}, {"plotsliders", getCmdPlotSlider, AlwaysAvailable, "Get plot slider display preference"}, {NULL, NULL, NULL, NULL} }; @@ -1133,7 +1204,8 @@ static command_t CommandTableSet[] = { {"hints", setCmdHint, AlwaysAvailable, "Set hint display"}, {"savepaths", setCmdSavePaths, AlwaysAvailable, "... to be adjusted next ... "}, // {"devicedebug", setCmdDeviceDebug, AlwaysAvailable, "Set device debug level"}, - {"plotsliders", setCmdPlotSliders, AlwaysAvailable, "Set plot slider display"}, + {"output", setCmdOutput, AlwaysAvailable, "Set dump output style"}, + {"plotsliders", setCmdPlotSliders, AlwaysAvailable, "Set plot slider display"}, {NULL, NULL, NULL, NULL} }; @@ -1189,6 +1261,7 @@ static int CmdPrefShow(const char *Cmd) { // showDeviceDebugState(prefShowNone); showBarModeState(prefShowNone); showClientExeDelayState(); + showOutputState(prefShowNone); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; diff --git a/client/src/proxgui.cpp b/client/src/proxgui.cpp index 5019e1946..9d95a372d 100644 --- a/client/src/proxgui.cpp +++ b/client/src/proxgui.cpp @@ -67,11 +67,13 @@ extern "C" void RepaintGraphWindow(void) { // hook up picture viewer -extern "C" void ShowPictureWindow(char *fn) { +extern "C" void ShowPictureWindow(uint8_t *data, int len) { // No support for jpeg2000 in Qt Image since a while... // https://doc.qt.io/qt-5/qtimageformats-index.html - if (strlen(fn) > 4 && !strcmp(fn + strlen(fn) - 4, ".jp2")) + QImage img = QImage::fromData(data, len); + if (img.isNull()) { return; + } if (!gui) { // Show a notice if X11/XQuartz isn't available #if defined(__MACH__) && defined(__APPLE__) @@ -82,7 +84,7 @@ extern "C" void ShowPictureWindow(char *fn) { return; } - gui->ShowPictureWindow(fn); + gui->ShowPictureWindow(img); } extern "C" void ShowBase64PictureWindow(char *b64) { diff --git a/client/src/proxgui.h b/client/src/proxgui.h index d38d41f1d..56d0d9574 100644 --- a/client/src/proxgui.h +++ b/client/src/proxgui.h @@ -32,7 +32,7 @@ void HideGraphWindow(void); void RepaintGraphWindow(void); // hook up picture viewer -void ShowPictureWindow(char *fn); +void ShowPictureWindow(uint8_t *data, int len); void ShowBase64PictureWindow(char *b64); void HidePictureWindow(void); void RepaintPictureWindow(void); diff --git a/client/src/proxguiqt.cpp b/client/src/proxguiqt.cpp index 6e36af9ee..ff88bce1e 100644 --- a/client/src/proxguiqt.cpp +++ b/client/src/proxguiqt.cpp @@ -65,8 +65,8 @@ void ProxGuiQT::HideGraphWindow(void) { } // emit picture viewer signals -void ProxGuiQT::ShowPictureWindow(char *fn) { - emit ShowPictureWindowSignal(fn); +void ProxGuiQT::ShowPictureWindow(const QImage &img) { + emit ShowPictureWindowSignal(img); } void ProxGuiQT::ShowBase64PictureWindow(char *b64) { @@ -116,24 +116,14 @@ void ProxGuiQT::_HideGraphWindow(void) { } // picture viewer -void ProxGuiQT::_ShowPictureWindow(char *fn) { +void ProxGuiQT::_ShowPictureWindow(const QImage &img) { if (!plotapp) return; - if (fn == NULL) + if (img.isNull()) return; - size_t slen = strlen(fn); - if (slen == 0) - return; - - char *myfn = (char *)calloc(slen + 1, sizeof(uint8_t)); - if (myfn == NULL) - return; - - memcpy(myfn, fn, slen); - if (!pictureWidget) { #if defined(__MACH__) && defined(__APPLE__) @@ -143,12 +133,7 @@ void ProxGuiQT::_ShowPictureWindow(char *fn) { pictureWidget = new PictureWidget(); } - QPixmap pm; - if (pm.load(myfn) == false) { - qWarning("Failed to load %s", myfn); - } - free(myfn); - free(fn); + QPixmap pm = QPixmap::fromImage(img); //QPixmap newPixmap = pm.scaled(QSize(50,50), Qt::KeepAspectRatio); //pm = pm.scaled(pictureController->lbl_pm->size(), Qt::KeepAspectRatio); @@ -264,7 +249,7 @@ void ProxGuiQT::MainLoop() { connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit())); // hook up picture viewer signals - connect(this, SIGNAL(ShowPictureWindowSignal(char *)), this, SLOT(_ShowPictureWindow(char *))); + connect(this, SIGNAL(ShowPictureWindowSignal(const QImage &)), this, SLOT(_ShowPictureWindow(const QImage &))); connect(this, SIGNAL(ShowBase64PictureWindowSignal(char *)), this, SLOT(_ShowBase64PictureWindow(char *))); connect(this, SIGNAL(RepaintPictureWindowSignal()), this, SLOT(_RepaintPictureWindow())); connect(this, SIGNAL(HidePictureWindowSignal()), this, SLOT(_HidePictureWindow())); diff --git a/client/src/proxguiqt.h b/client/src/proxguiqt.h index 20277f2fd..ac19a99c9 100644 --- a/client/src/proxguiqt.h +++ b/client/src/proxguiqt.h @@ -156,7 +156,7 @@ class ProxGuiQT : public QObject { void HideGraphWindow(void); // hook up picture viewer - void ShowPictureWindow(char *fn); + void ShowPictureWindow(const QImage &img); void ShowBase64PictureWindow(char *b64); void HidePictureWindow(void); void RepaintPictureWindow(void); @@ -170,7 +170,7 @@ class ProxGuiQT : public QObject { void _HideGraphWindow(void); // hook up picture viewer - void _ShowPictureWindow(char *fn); + void _ShowPictureWindow(const QImage &img); void _ShowBase64PictureWindow(char *b64); void _HidePictureWindow(void); void _RepaintPictureWindow(void); @@ -185,7 +185,7 @@ class ProxGuiQT : public QObject { void ExitSignal(void); // hook up picture viewer signals - void ShowPictureWindowSignal(char *fn); + void ShowPictureWindowSignal(const QImage &img); void ShowBase64PictureWindowSignal(char *b64); void HidePictureWindowSignal(void); void RepaintPictureWindowSignal(void); diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index e15c9ff9f..378b9f0dc 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -41,7 +41,7 @@ static int mainret = PM3_ESOFT; #ifndef LIBPM3 #define BANNERMSG1 "" -#define BANNERMSG2 " [ :snowflake: ]" +#define BANNERMSG2 " [ :coffee: ]" #define BANNERMSG3 "" typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode; @@ -576,6 +576,7 @@ static void show_help(bool showFullHelp, char *exec_name) { PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files"); PrintAndLogEx(NORMAL, "\nOptions in flasher mode:"); PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image"); + PrintAndLogEx(NORMAL, " --reboot-bootloader reboot Proxmark3 into bootloader mode"); PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash)"); PrintAndLogEx(NORMAL, " --force Enable flashing even if firmware seems to not match client version"); PrintAndLogEx(NORMAL, " --image image to flash. Can be specified several times."); @@ -696,6 +697,22 @@ finish2: PrintAndLogEx(INFO, "\nHave a nice day!"); return ret; } + +static int reboot_bootloader_pm3(char *serial_port_name) { + if (serial_port_name == NULL) { + PrintAndLogEx(ERR, "You must specify a port.\n"); + return PM3_EINVARG; + } + + if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED) == false) { + PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name); + return PM3_ETIMEOUT; + } + + PrintAndLogEx(NORMAL, _GREEN_(" found")); + return flash_reboot_bootloader(serial_port_name); +} + #endif //LIBPM3 void pm3_init(void) { @@ -732,6 +749,7 @@ int main(int argc, char *argv[]) { strncpy(exec_name, basename(argv[0]), sizeof(exec_name) - 1); bool flash_mode = false; + bool reboot_bootloader_mode = false; bool flash_can_write_bl = false; bool flash_force = false; bool debug_mode_forced = false; @@ -944,6 +962,12 @@ int main(int argc, char *argv[]) { continue; } + // go to flash mode + if (strcmp(argv[i], "--reboot-to-bootloader") == 0) { + reboot_bootloader_mode = true; + continue; + } + // unlock bootloader area if (strcmp(argv[i], "--unlock-bootloader") == 0) { flash_can_write_bl = true; @@ -1001,6 +1025,11 @@ int main(int argc, char *argv[]) { exit(EXIT_SUCCESS); } + if (reboot_bootloader_mode) { + reboot_bootloader_pm3(port); + exit(EXIT_SUCCESS); + } + if (script_cmd) { while (script_cmd[strlen(script_cmd) - 1] == ' ') script_cmd[strlen(script_cmd) - 1] = 0x00; @@ -1043,7 +1072,7 @@ int main(int argc, char *argv[]) { PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); // ascii art only in interactive client - if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !flash_mode) + if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !flash_mode && !reboot_bootloader_mode) showBanner(); // Save settings if not loaded from settings json file. diff --git a/client/src/scripting.c b/client/src/scripting.c index a12a53fd3..c96d593df 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -886,6 +886,39 @@ static int l_detect_prng(lua_State *L) { lua_pushinteger(L, res); return 1; } +/* + * @brief l_keygen_algoB is a function to calculate pwd/pack using UID, by algo B + * @param L + * @return + */ +static int l_keygen_algoB(lua_State *L) { + //Check number of arguments + int n = lua_gettop(L); + if (n != 1) { + return returnToLuaWithError(L, "Only UID"); + } + + size_t size; + uint32_t tmp; + const char *p_uid = luaL_checklstring(L, 1, &size); + if (size != 14) + return returnToLuaWithError(L, "Wrong size of UID, got %d bytes, expected 14", (int) size); + + uint8_t uid[7] = {0, 0, 0, 0, 0, 0, 0}; + + for (int i = 0; i < 14; i += 2) { + sscanf(&p_uid[i], "%02x", &tmp); + uid[i / 2] = tmp & 0xFF; + } + + uint32_t pwd = ul_ev1_pwdgenB(uid); + uint16_t pack = ul_ev1_packgenB(uid); + + lua_pushunsigned(L, pwd); + lua_pushunsigned(L, pack); + return 2; +} + /* * @brief l_keygen_algoD is a function to calculate pwd/pack using UID, by algo D * @param L @@ -1196,6 +1229,19 @@ static int l_ndefparse(lua_State *L) { return 1; } +static int l_ul_read_uid(lua_State *L) { + uint8_t uid[7] = { 0, 0, 0, 0, 0, 0, 0 }; + int res = ul_read_uid(uid); + if (res != PM3_SUCCESS) { + return returnToLuaWithError(L, "Failed to read Ultralight/NTAG UID"); + } + char buf[15]; + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf), "%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]); + lua_pushstring(L, buf); + return 1; +} + static int l_remark(lua_State *L) { //Check number of arguments int n = lua_gettop(L); @@ -1340,7 +1386,7 @@ int set_pm3_libraries(lua_State *L) { {"hardnested", l_hardnested}, {"detect_prng", l_detect_prng}, // {"keygen.algoA", l_keygen_algoA}, -// {"keygen.algoB", l_keygen_algoB}, + {"keygen_algo_b", l_keygen_algoB}, // {"keygen.algoC", l_keygen_algoC}, {"keygen_algo_d", l_keygen_algoD}, {"t55xx_readblock", l_T55xx_readblock}, @@ -1354,6 +1400,7 @@ int set_pm3_libraries(lua_State *L) { {"rem", l_remark}, {"em4x05_read", l_em4x05_read}, {"em4x50_read", l_em4x50_read}, + {"ul_read_uid", l_ul_read_uid}, {NULL, NULL} }; diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index 1e57daeb2..9cdcc41d1 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -27,14 +27,24 @@ // The windows serial port implementation #ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN #include +#include +#include typedef struct { HANDLE hPort; // Serial port handle DCB dcb; // Device control settings COMMTIMEOUTS ct; // Serial port time-out configuration + SOCKET hSocket; // Socket handle } serial_port_windows_t; +// this is for TCP connection +struct timeval timeout = { + .tv_sec = 0, // 0 second + .tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000 +}; + uint32_t newtimeout_value = 0; bool newtimeout_pending = false; @@ -69,11 +79,111 @@ static int uart_reconfigure_timeouts_polling(serial_port sp) { serial_port uart_open(const char *pcPortName, uint32_t speed) { char acPortName[255] = {0}; serial_port_windows_t *sp = calloc(sizeof(serial_port_windows_t), sizeof(uint8_t)); + sp->hSocket = INVALID_SOCKET; // default: serial port if (sp == 0) { PrintAndLogEx(WARNING, "UART failed to allocate memory\n"); return INVALID_SERIAL_PORT; } + + char *prefix = strdup(pcPortName); + if (prefix == NULL) { + PrintAndLogEx(ERR, "error: string duplication"); + free(sp); + return INVALID_SERIAL_PORT; + } + str_lower(prefix); + + if (memcmp(prefix, "tcp:", 4) == 0) { + free(prefix); + + if (strlen(pcPortName) <= 4) { + free(sp); + return INVALID_SERIAL_PORT; + } + + struct addrinfo *addr = NULL, *rp; + + char *addrstr = strdup(pcPortName + 4); + if (addrstr == NULL) { + PrintAndLogEx(ERR, "error: string duplication"); + free(sp); + return INVALID_SERIAL_PORT; + } + + timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + + char *colon = strrchr(addrstr, ':'); + const char *portstr; + if (colon) { + portstr = colon + 1; + *colon = '\0'; + } else { + portstr = "18888"; + } + + WSADATA wsaData; + struct addrinfo info; + int iResult; + + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + PrintAndLogEx(ERR, "error: WSAStartup failed with error: %d", iResult); + free(sp); + return INVALID_SERIAL_PORT; + } + + memset(&info, 0, sizeof(info)); + info.ai_socktype = SOCK_STREAM; + info.ai_protocol = IPPROTO_TCP; + + int s = getaddrinfo(addrstr, portstr, &info, &addr); + if (s != 0) { + PrintAndLogEx(ERR, "error: getaddrinfo: %s", gai_strerror(s)); + freeaddrinfo(addr); + free(addrstr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + + SOCKET hSocket = INVALID_SOCKET; + for (rp = addr; rp != NULL; rp = rp->ai_next) { + hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (hSocket == INVALID_SOCKET) + continue; + + if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) + break; + + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + + freeaddrinfo(addr); + free(addrstr); + + if (rp == NULL) { /* No address succeeded */ + PrintAndLogEx(ERR, "error: Could not connect"); + WSACleanup(); + free(sp); + return INVALID_SERIAL_PORT; + } + + sp->hSocket = hSocket; + + int one = 1; + int res = setsockopt(sp->hSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + if (res != 0) { + closesocket(hSocket); + WSACleanup(); + free(sp); + return INVALID_SERIAL_PORT; + } + return sp; + } + // Copy the input "com?" to "\\.\COM?" format snprintf(acPortName, sizeof(acPortName), "\\\\.\\%s", pcPortName); _strupr(acPortName); @@ -120,8 +230,14 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } void uart_close(const serial_port sp) { - if (((serial_port_windows_t *)sp)->hPort != INVALID_HANDLE_VALUE) - CloseHandle(((serial_port_windows_t *)sp)->hPort); + serial_port_windows_t *spw = (serial_port_windows_t *)sp; + if (spw->hSocket != INVALID_SOCKET) { + shutdown(spw->hSocket, SD_BOTH); + closesocket(spw->hSocket); + WSACleanup(); + } + if (spw->hPort != INVALID_HANDLE_VALUE) + CloseHandle(spw->hPort); free(sp); } @@ -163,31 +279,137 @@ uint32_t uart_get_speed(const serial_port sp) { } int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uint32_t *pszRxLen) { - uart_reconfigure_timeouts_polling(sp); - int res = ReadFile(((serial_port_windows_t *)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); - if (res) + serial_port_windows_t *spw = (serial_port_windows_t *)sp; + if (spw->hSocket == INVALID_SOCKET) { // serial port + uart_reconfigure_timeouts_polling(sp); + + int res = ReadFile(((serial_port_windows_t *)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); + if (res) + return PM3_SUCCESS; + + int errorcode = GetLastError(); + + if (res == 0 && errorcode == 2) { + return PM3_EIO; + } + + return PM3_ENOTTY; + } else { // TCP + uint32_t byteCount; // FIONREAD returns size on 32b + fd_set rfds; + struct timeval tv; + + if (newtimeout_pending) { + timeout.tv_usec = newtimeout_value * 1000; + newtimeout_pending = false; + } + // Reset the output count + *pszRxLen = 0; + do { + // Reset file descriptor + FD_ZERO(&rfds); + FD_SET(spw->hSocket, &rfds); + tv = timeout; + // the first argument nfds is ignored in Windows + int res = select(0, &rfds, NULL, NULL, &tv); + + // Read error + if (res == SOCKET_ERROR) { + return PM3_EIO; + } + + // Read time-out + if (res == 0) { + if (*pszRxLen == 0) { + // We received no data + return PM3_ENODATA; + } else { + // We received some data, but nothing more is available + return PM3_SUCCESS; + } + } + + // Retrieve the count of the incoming bytes + res = ioctlsocket(spw->hSocket, FIONREAD, (u_long *)&byteCount); + // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); + if (res == SOCKET_ERROR) return PM3_ENOTTY; + + // Cap the number of bytes, so we don't overrun the buffer + if (pszMaxRxLen - (*pszRxLen) < byteCount) { + // PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); + byteCount = pszMaxRxLen - (*pszRxLen); + } + + // There is something available, read the data + res = recv(spw->hSocket, (char *)pbtRx + (*pszRxLen), byteCount, 0); + + // Stop if the OS has some troubles reading the data + if (res <= 0) { // includes 0(gracefully closed) and -1(SOCKET_ERROR) + return PM3_EIO; + } + + *pszRxLen += res; + + if (*pszRxLen == pszMaxRxLen) { + // We have all the data we wanted. + return PM3_SUCCESS; + } + } while (byteCount); + return PM3_SUCCESS; - - int errorcode = GetLastError(); - - if (res == 0 && errorcode == 2) { - return PM3_EIO; } - - return PM3_ENOTTY; } int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { - DWORD txlen = 0; - int res = WriteFile(((serial_port_windows_t *)sp)->hPort, p_tx, len, &txlen, NULL); - if (res) + serial_port_windows_t *spw = (serial_port_windows_t *)sp; + if (spw->hSocket == INVALID_SOCKET) { // serial port + DWORD txlen = 0; + int res = WriteFile(((serial_port_windows_t *)sp)->hPort, p_tx, len, &txlen, NULL); + if (res) + return PM3_SUCCESS; + + int errorcode = GetLastError(); + if (res == 0 && errorcode == 2) { + return PM3_EIO; + } + return PM3_ENOTTY; + } else { // TCP + uint32_t pos = 0; + fd_set wfds; + struct timeval tv; + + while (pos < len) { + // Reset file descriptor + FD_ZERO(&wfds); + FD_SET(spw->hSocket, &wfds); + tv = timeout; + // the first argument nfds is ignored in Windows + int res = select(0, NULL, &wfds, NULL, &tv); + + // Write error + if (res == SOCKET_ERROR) { + PrintAndLogEx(ERR, "UART:: write error (%d)", res); + return PM3_ENOTTY; + } + + // Write time-out + if (res == 0) { + PrintAndLogEx(ERR, "UART:: write time-out"); + return PM3_ETIMEOUT; + } + + // Send away the bytes + res = send(spw->hSocket, (const char *)p_tx + pos, len - pos, 0); + + // Stop if the OS has some troubles sending the data + if (res <= 0) + return PM3_EIO; + + pos += res; + } return PM3_SUCCESS; - int errorcode = GetLastError(); - if (res == 0 && errorcode == 2) { - return PM3_EIO; } - return PM3_ENOTTY; } #endif diff --git a/client/src/ui.c b/client/src/ui.c index 0ffabc341..2281497c1 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -23,7 +23,6 @@ #endif #include "ui.h" #include "commonutil.h" // ARRAYLEN - #include // for Mingw readline #include #include @@ -62,10 +61,18 @@ pthread_mutex_t g_print_lock = PTHREAD_MUTEX_INITIALIZER; static void fPrintAndLog(FILE *stream, const char *fmt, ...); +#ifdef _WIN32 +#define MKDIR_CHK _mkdir(path) +#else +#define MKDIR_CHK mkdir(path, 0700) +#endif + + // needed by flasher, so let's put it here instead of fileutils.c int searchHomeFilePath(char **foundpath, const char *subdir, const char *filename, bool create_home) { - if (foundpath == NULL) + if (foundpath == NULL) { return PM3_EINVARG; + } const char *user_path = get_my_user_directory(); if (user_path == NULL) { @@ -75,20 +82,21 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam size_t pathlen = strlen(user_path) + strlen(PM3_USER_DIRECTORY) + 1; char *path = calloc(pathlen, sizeof(char)); - if (path == NULL) + if (path == NULL) { return PM3_EMALLOC; + } strcpy(path, user_path); strcat(path, PM3_USER_DIRECTORY); - int result; + #ifdef _WIN32 struct _stat st; // Mingw _stat fails if path ends with /, so let's use a stripped path - if (path[strlen(path) - 1] == '/') { - path[strlen(path) - 1] = '\0'; + if (str_endswith(path, PATHSEP)) { + memset(path + (strlen(path) - strlen(PATHSEP)), 0x00, strlen(PATHSEP)); result = _stat(path, &st); - path[strlen(path)] = '/'; + strcat(path, PATHSEP); } else { result = _stat(path, &st); } @@ -96,19 +104,16 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam struct stat st; result = stat(path, &st); #endif + if ((result != 0) && create_home) { -#ifdef _WIN32 - if (_mkdir(path)) -#else - if (mkdir(path, 0700)) -#endif - { + if (MKDIR_CHK) { fprintf(stderr, "Could not create user directory %s\n", path); free(path); return PM3_EFILE; } } + if (subdir != NULL) { pathlen += strlen(subdir); char *tmp = realloc(path, pathlen * sizeof(char)); @@ -121,24 +126,20 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam #ifdef _WIN32 // Mingw _stat fails if path ends with /, so let's use a stripped path - if (path[strlen(path) - 1] == '/') { - path[strlen(path) - 1] = '\0'; + if (str_endswith(path, PATHSEP)) { + memset(path + (strlen(path) - strlen(PATHSEP)), 0x00, strlen(PATHSEP)); result = _stat(path, &st); - path[strlen(path)] = '/'; + strcat(path, PATHSEP); } else { result = _stat(path, &st); } #else result = stat(path, &st); #endif + if ((result != 0) && create_home) { -#ifdef _WIN32 - if (_mkdir(path)) -#else - if (mkdir(path, 0700)) -#endif - { + if (MKDIR_CHK) { fprintf(stderr, "Could not create user directory %s\n", path); free(path); return PM3_EFILE; @@ -150,15 +151,18 @@ int searchHomeFilePath(char **foundpath, const char *subdir, const char *filenam *foundpath = path; return PM3_SUCCESS; } + pathlen += strlen(filename); char *tmp = realloc(path, pathlen * sizeof(char)); if (tmp == NULL) { //free(path); return PM3_EMALLOC; } + path = tmp; strcat(path, filename); *foundpath = path; + return PM3_SUCCESS; } @@ -527,12 +531,11 @@ void memcpy_filter_emoji(void *dest, const void *src, size_t n, emojiMode_t mode uint8_t emojified_token_length = 0; char *current_token = NULL; uint8_t current_token_length = 0; - char current_char; char *rdest = (char *)dest; char *rsrc = (char *)src; uint16_t si = 0; for (size_t i = 0; i < n; i++) { - current_char = rsrc[i]; + char current_char = rsrc[i]; if (current_token_length == 0) { // starting a new token. diff --git a/client/src/ui.h b/client/src/ui.h index ec496fd80..4042550b3 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -35,7 +35,9 @@ typedef enum logLevel {NORMAL, SUCCESS, INFO, FAILED, WARNING, ERR, DEBUG, INPLA typedef enum emojiMode {EMO_ALIAS, EMO_EMOJI, EMO_ALTTEXT, EMO_NONE} emojiMode_t; typedef enum clientdebugLevel {cdbOFF, cdbSIMPLE, cdbFULL} clientdebugLevel_t; // typedef enum devicedebugLevel {ddbOFF, ddbERROR, ddbINFO, ddbDEBUG, ddbEXTENDED} devicedebugLevel_t; -typedef enum savePaths {spDefault, spDump, spTrace, spItemCount} savePaths_t; // last item spItemCount used to auto map to number of files + +// last item spItemCount used to auto map to number of files +typedef enum savePaths {spDefault, spDump, spTrace, spItemCount} savePaths_t; typedef struct {int x; int y; int h; int w;} qtWindow_t; typedef struct { @@ -47,6 +49,7 @@ typedef struct { bool pm3_present; bool help_dump_mode; bool show_hints; + bool dense_output; bool window_changed; // track if plot/overlay pos/size changed to save on exit qtWindow_t plot; qtWindow_t overlay; diff --git a/client/src/util.c b/client/src/util.c index ed17d4120..51408a524 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -33,7 +33,7 @@ #include "ui.h" // PrintAndLog -#define UTIL_BUFFER_SIZE_SPRINT 8193 +#define UTIL_BUFFER_SIZE_SPRINT 8196 // global client debug variable uint8_t g_debugMode = 0; // global client disable logging variable @@ -45,7 +45,7 @@ bool g_pendingPrompt = false; #include #endif -#define MAX_BIN_BREAK_LENGTH (3072+384+1) +#define MAX_BIN_BREAK_LENGTH (3072 + 384 + 1) #ifndef _WIN32 #include @@ -90,6 +90,28 @@ int kbd_enter_pressed(void) { } #endif +static char b2s(uint8_t v, bool uppercase) { + // clear higher bits + v &= 0xF; + + switch (v) { + case 0xA : + return (uppercase ? 'A' : 'a') ; + case 0xB : + return (uppercase ? 'B' : 'b') ; + case 0xC : + return (uppercase ? 'C' : 'c') ; + case 0xD : + return (uppercase ? 'D' : 'd') ; + case 0xE : + return (uppercase ? 'E' : 'e') ; + case 0xF : + return (uppercase ? 'F' : 'f') ; + default: + return (char)(v + 0x30); + } +} + // create filename on hex uid. // param *fn - pointer to filename char array // param *uid - pointer to uid byte array @@ -164,7 +186,7 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len size_t i = 0; for (i = 0; i < max_len; ++i, tmp++) { char c = hex_data[i]; - snprintf(tmp, hex_max_len - (tmp - tmp_base), "%c", ((c < 32) || (c == 127)) ? '.' : c); + *tmp = ((c < 32) || (c == 127)) ? '.' : c; } size_t m = (min_str_len > i) ? min_str_len : 0; @@ -172,7 +194,7 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len m = hex_max_len; for (; i < m; i++, tmp++) - snprintf(tmp, hex_max_len - (tmp - tmp_base), " "); + *tmp = ' '; // remove last space *tmp = '\0'; @@ -181,32 +203,41 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, const size_t min_str_len, const size_t spaces_between, bool uppercase) { - if (buf == NULL) return; + // sanity check + if (buf == NULL || hex_len < 1) + return; + // 1. hex string length. + // 2. byte array to be converted to string + // + + size_t max_byte_len = (hex_len > hex_max_len) ? hex_max_len : hex_len; + size_t max_str_len = (max_byte_len * (2 + spaces_between)) + 1; char *tmp_base = (char *)buf; char *tmp = tmp_base; - size_t max_len = (hex_len > hex_max_len) ? hex_max_len : hex_len; - size_t i; - for (i = 0; i < max_len; ++i, tmp += 2 + spaces_between) { - snprintf(tmp, hex_max_len - (tmp - tmp_base), (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + for (i = 0; (i < max_byte_len) && (max_str_len > strlen(tmp_base)) ; ++i) { + + *(tmp++) = b2s((hex_data[i] >> 4), uppercase); + *(tmp++) = b2s(hex_data[i], uppercase); for (size_t j = 0; j < spaces_between; j++) - snprintf(tmp + 2 + j, hex_max_len - ((tmp + 2 + j) - tmp_base), " "); + *(tmp++) = ' '; } i *= (2 + spaces_between); - size_t m = min_str_len > i ? min_str_len : 0; + size_t m = (min_str_len > i) ? min_str_len : 0; if (m > hex_max_len) m = hex_max_len; - for (; i < m; i++, tmp++) - snprintf(tmp, hex_max_len - (tmp - tmp_base), " "); + while (m--) + *(tmp++) = ' '; // remove last space *tmp = '\0'; + } // printing and converting functions @@ -235,7 +266,7 @@ void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { uint8_t mod = len % breaks; if (mod) { - char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; + char buf[UTIL_BUFFER_SIZE_SPRINT + 3] = {0}; hex_to_buffer((uint8_t *)buf, data + i, mod, (sizeof(buf) - 1), 0, 1, true); // add the spaces... @@ -245,22 +276,53 @@ void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { } } -static void print_buffer_ex(const uint8_t *data, const size_t len, int level, uint8_t breaks) { +void print_hex_noascii_break(const uint8_t *data, const size_t len, uint8_t breaks) { + if (data == NULL || len == 0 || breaks == 0) return; - if (len < 1) - return; - - char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; int i; for (i = 0; i < len; i += breaks) { if (len - i < breaks) { // incomplete block, will be treated out of the loop break; } + PrintAndLogEx(INFO, "%s", sprint_hex_inrow_spaces(data + i, breaks, 0)); + } + + // the last odd bytes + uint8_t mod = len % breaks; + + if (mod) { + char buf[UTIL_BUFFER_SIZE_SPRINT + 3] = {0}; + hex_to_buffer((uint8_t *)buf, data + i, mod, (sizeof(buf) - 1), 0, 0, true); + + // add the spaces... + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " "); + PrintAndLogEx(INFO, "%s", buf); + } +} + +static void print_buffer_ex(const uint8_t *data, const size_t len, int level, uint8_t breaks) { + + // sanity checks + if ((data == NULL) || (len < 1)) + return; + + char buf[UTIL_BUFFER_SIZE_SPRINT + 3] = {0}; + int i; + for (i = 0; i < len; i += breaks) { + + memset(buf, 0x00, sizeof(buf)); + + if (len - i < breaks) { // incomplete block, will be treated out of the loop + break; + } + // (16 * 3) + (16) + + 1 snprintf(buf, sizeof(buf), "%*s%02x: ", (level * 4), " ", i); hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, breaks, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, breaks)); + PrintAndLogEx(INFO, "%s", buf); } @@ -298,7 +360,6 @@ void print_buffer_with_offset(const uint8_t *data, const size_t len, int offset, } } - void print_blocks(uint32_t *data, size_t len) { PrintAndLogEx(SUCCESS, "Blk | Data "); PrintAndLogEx(SUCCESS, "----+------------"); @@ -312,13 +373,15 @@ void print_blocks(uint32_t *data, size_t len) { } char *sprint_hex(const uint8_t *data, const size_t len) { - static char buf[UTIL_BUFFER_SIZE_SPRINT - 3] = {0}; + static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0}; + memset(buf, 0x00, sizeof(buf)); hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, 1, true); return buf; } char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0}; + memset(buf, 0x00, sizeof(buf)); hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true); return buf; } @@ -326,8 +389,10 @@ char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t mi char *sprint_hex_inrow(const uint8_t *data, const size_t len) { return sprint_hex_inrow_ex(data, len, 0); } + char *sprint_hex_inrow_spaces(const uint8_t *data, const size_t len, size_t spaces_between) { static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0}; + memset(buf, 0x00, sizeof(buf)); hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, 0, spaces_between, true); return buf; } @@ -338,8 +403,9 @@ char *sprint_bytebits_bin_break(const uint8_t *data, const size_t len, const uin size_t rowlen = (len > MAX_BIN_BREAK_LENGTH) ? MAX_BIN_BREAK_LENGTH : len; // 3072 + end of line characters if broken at 8 bits - static char buf[MAX_BIN_BREAK_LENGTH]; - memset(buf, 0x00, sizeof(buf)); + static char buf[MAX_BIN_BREAK_LENGTH] = {0}; + memset(buf, 0, sizeof(buf)); + char *tmp = buf; // loop through the out_index to make sure we don't go too far @@ -413,39 +479,43 @@ char *sprint_bytebits_bin(const uint8_t *data, const size_t len) { char *sprint_bin(const uint8_t *data, const size_t len) { size_t binlen = (len * 8 > MAX_BIN_BREAK_LENGTH) ? MAX_BIN_BREAK_LENGTH : len * 8; - static uint8_t buf[MAX_BIN_BREAK_LENGTH]; + static uint8_t buf[MAX_BIN_BREAK_LENGTH] = {0}; bytes_to_bytebits(data, binlen / 8, buf); return sprint_bytebits_bin_break(buf, binlen, 0); } char *sprint_hex_ascii(const uint8_t *data, const size_t len) { - static char buf[UTIL_BUFFER_SIZE_SPRINT]; - memset(buf, 0x00, UTIL_BUFFER_SIZE_SPRINT); + static char buf[UTIL_BUFFER_SIZE_SPRINT + 20] = {0}; + memset(buf, 0x00, sizeof(buf)); + + char *tmp = buf; size_t max_len = (len > 1010) ? 1010 : len; - snprintf(buf, sizeof(buf), "%s| ", sprint_hex(data, max_len)); + int ret = snprintf(buf, sizeof(buf) - 1, "%s| ", sprint_hex(data, max_len)); + if (ret < 0) { + goto out; + } size_t i = 0; size_t pos = (max_len * 3) + 2; while (i < max_len) { - char c = data[i]; - if ((c < 32) || (c == 127)) - c = '.'; - - snprintf(buf + pos + i, sizeof(buf) - (pos + 1), "%c", c); + tmp[pos + i] = ((c < 32) || (c == 127)) ? '.' : c; ++i; } +out: return buf; } char *sprint_ascii_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { - static char buf[UTIL_BUFFER_SIZE_SPRINT]; + static char buf[UTIL_BUFFER_SIZE_SPRINT] = {0}; + memset(buf, 0x00, sizeof(buf)); + char *tmp = buf; - memset(buf, 0x00, UTIL_BUFFER_SIZE_SPRINT); size_t max_len = (len > 1010) ? 1010 : len; size_t i = 0; + while (i < max_len) { char c = data[i]; tmp[i] = ((c < 32) || (c == 127)) ? '.' : c; @@ -543,7 +613,7 @@ void bytes_to_bytebits(const void *src, const size_t srclen, void *dest) { // hh,gg,ff,ee,dd,cc,bb,aa, pp,oo,nn,mm,ll,kk,jj,ii // up to 64 bytes or 512 bits uint8_t *SwapEndian64(const uint8_t *src, const size_t len, const uint8_t blockSize) { - static uint8_t buf[64]; + static uint8_t buf[64] = {0}; memset(buf, 0x00, 64); uint8_t *tmp = buf; for (uint8_t block = 0; block < (uint8_t)(len / blockSize); block++) { @@ -727,13 +797,14 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) } int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen) { - int bg, en; - uint32_t temp; - char buf[5] = {0}; - if (param_getptr(line, &bg, &en, paramnum)) return 1; + int bg, en; + + if (param_getptr(line, &bg, &en, paramnum)) + return 1; *datalen = 0; + char buf[5] = {0}; int indx = bg; while (line[indx]) { @@ -756,8 +827,9 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda } if (strlen(buf) >= 2) { + uint32_t temp = 0; sscanf(buf, "%x", &temp); - data[*datalen] = (uint8_t)(temp & 0xff); + data[*datalen] = (uint8_t)(temp & 0xFF); *buf = 0; (*datalen)++; } @@ -839,27 +911,32 @@ https://github.com/ApertureLabsLtd/RFIDler/blob/master/firmware/Pic32/RFIDler.X/ // convert hex to sequence of 0/1 bit values // returns number of bits converted int hextobinarray(char *target, char *source) { - int length, i, count = 0; + return hextobinarray_n(target, source, strlen(source)); +} + +int hextobinarray_n(char *target, char *source, int sourcelen) { + int i, count = 0; char *start = source; - length = strlen(source); // process 4 bits (1 hex digit) at a time - while (length--) { + while (sourcelen--) { char x = *(source++); // capitalize - if (x >= 'a' && x <= 'f') + if (x >= 'a' && x <= 'f') { x -= 32; + } // convert to numeric value - if (x >= '0' && x <= '9') + if (x >= '0' && x <= '9') { x -= '0'; - else if (x >= 'A' && x <= 'F') + } else if (x >= 'A' && x <= 'F') { x -= 'A' - 10; - else { + } else { PrintAndLogEx(INFO, "(hextobinarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start); return 0; } // output - for (i = 0 ; i < 4 ; ++i, ++count) + for (i = 0 ; i < 4 ; ++i, ++count) { *(target++) = (x >> (3 - i)) & 1; + } } return count; @@ -867,9 +944,14 @@ int hextobinarray(char *target, char *source) { // convert hex to human readable binary string int hextobinstring(char *target, char *source) { - int length = hextobinarray(target, source); - if (length == 0) + return hextobinstring_n(target, source, strlen(source)); +} + +int hextobinstring_n(char *target, char *source, int sourcelen) { + int length = hextobinarray_n(target, source, sourcelen); + if (length == 0) { return 0; + } binarraytobinstring(target, target, length); return length; } @@ -1015,6 +1097,14 @@ void str_lower(char *s) { s[i] = tolower(s[i]); } +void str_upper(char *s) { + strn_upper(s, strlen(s)); +} + +void strn_upper(char *s, size_t n) { + for (size_t i = 0; i < n; i++) + s[i] = toupper(s[i]); +} // check for prefix in string bool str_startswith(const char *s, const char *pre) { return strncmp(pre, s, strlen(pre)) == 0; @@ -1149,7 +1239,7 @@ inline uint64_t bitcount64(uint64_t a) { inline uint32_t leadingzeros32(uint32_t a) { #if defined __GNUC__ - return __builtin_clzl(a); + return __builtin_clz(a); #else PrintAndLogEx(FAILED, "Was not compiled with fct bitcount64"); return 0; @@ -1164,3 +1254,36 @@ inline uint64_t leadingzeros64(uint64_t a) { return 0; #endif } + + +int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { + + size_t max = srclen - plen + 1; + + for (size_t i = 0; i < max; i++) { + + // compare only first byte + if (src[i] != pattern[0]) + continue; + + // try to match rest of the pattern + for (int j = plen - 1; j >= 1; j--) { + + if (src[i + j] != pattern[j]) + break; + + if (j == 1) + return i; + } + } + return -1; +} + +void sb_append_char(smartbuf *sb, unsigned char c) { + if (sb->idx >= sb->size) { + sb->size *= 2; + sb->ptr = realloc(sb->ptr, sb->size); + } + sb->ptr[sb->idx] = c; + sb->idx++; +} diff --git a/client/src/util.h b/client/src/util.h index 2401a95aa..4dab9ae06 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -67,6 +67,8 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, void print_hex(const uint8_t *data, const size_t len); void print_hex_break(const uint8_t *data, const size_t len, const uint8_t breaks); +void print_hex_noascii_break(const uint8_t *data, const size_t len, uint8_t breaks); + char *sprint_hex(const uint8_t *data, const size_t len); char *sprint_hex_inrow(const uint8_t *data, const size_t len); char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len); @@ -110,7 +112,11 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda int param_getstr(const char *line, int paramnum, char *str, size_t buffersize); int hextobinarray(char *target, char *source); +int hextobinarray_n(char *target, char *source, int sourcelen); + int hextobinstring(char *target, char *source); +int hextobinstring_n(char *target, char *source, int sourcelen); + int binarraytohex(char *target, const size_t targetlen, const char *source, size_t srclen); void binarraytobinstring(char *target, char *source, int length); int binstring2binarray(uint8_t *target, char *source, int length); @@ -127,6 +133,9 @@ uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor); int num_CPUs(void); // number of logical CPUs void str_lower(char *s); // converts string to lower case +void str_upper(char *s); // converts string to UPPER case +void strn_upper(char *s, size_t n); + bool str_startswith(const char *s, const char *pre); // check for prefix in string bool str_endswith(const char *s, const char *suffix); // check for suffix in string void clean_ascii(unsigned char *buf, size_t len); @@ -143,4 +152,12 @@ uint64_t bitcount64(uint64_t a); uint32_t leadingzeros32(uint32_t a); uint64_t leadingzeros64(uint64_t a); +int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen); + +struct smartbuf { + char *ptr; + size_t size; + size_t idx; +} typedef smartbuf; +void sb_append_char(smartbuf *sb, unsigned char c); #endif diff --git a/client/src/wiegand_formatutils.c b/client/src/wiegand_formatutils.c index 5f7dae901..e55f999bd 100644 --- a/client/src/wiegand_formatutils.c +++ b/client/src/wiegand_formatutils.c @@ -131,25 +131,35 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit static uint8_t get_length_from_header(wiegand_message_t *data) { /** * detect if message has "preamble" / "sentinel bit" - * + * Right now we just calculate the highest bit set + * 37 bit formats is hard to detect since it doesnt have a sentinel bit */ - - uint8_t len = 0; uint32_t hfmt = 0; // for calculating card length if ((data->Top & 0x000FFFFF) > 0) { // > 64 bits hfmt = data->Top & 0x000FFFFF; len = 64; - } else if ((data->Mid & 0xFFFFFFC0) > 0) { // < 63-38 bits - hfmt = data->Mid & 0xFFFFFFC0; - len = 32; - } else if (data->Mid && (data->Mid & 0x00000020) == 0) { // 37 bits; - hfmt = 0; - len = 37; - } else if ((data->Mid & 0x0000001F) > 0) { // 36-32 bits - hfmt = data->Mid & 0x0000001F; - len = 32; + } else if (data->Mid > 0) { // < 63-32 bits + + // detect HID format b38 set + if (data->Mid & 0xFFFFFFC0) { + hfmt = data->Mid; + len = 32; + } else { + + printf("hid preamble detected\n"); + len = 32; + + if ((data->Mid ^ 0x20) == 0) { hfmt = data->Bot; len = 0; } + else if ((data->Mid & 0x10) == 0) { hfmt = data->Mid & 0x1F; } + else if ((data->Mid & 0x08) == 0) { hfmt = data->Mid & 0x0F; } + else if ((data->Mid & 0x04) == 0) { hfmt = data->Mid & 0x07; } + else if ((data->Mid & 0x02) == 0) { hfmt = data->Mid & 0x03; } + else if ((data->Mid & 0x01) == 0) { hfmt = data->Mid & 0x01; } + else { hfmt = data->Mid & 0x3F;} + } + } else { hfmt = data->Bot; len = 0; @@ -159,8 +169,11 @@ static uint8_t get_length_from_header(wiegand_message_t *data) { hfmt >>= 1; len++; } + + // everything less than 26 bits found, assume 26 bits if (len < 26) len = 26; + return len; } diff --git a/common/commonutil.c b/common/commonutil.c index 2101414ab..4af16b3c0 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -262,10 +262,34 @@ uint32_t rotr(uint32_t a, uint8_t n) { return (a >> n) | (a << (32 - n)); } -uint16_t get_sw(const uint8_t *d, uint8_t n) { +uint16_t get_sw(const uint8_t *d, uint16_t n) { if (n < 2) return 0; n -= 2; - return d[n] * 0x0100 + d[n + 1]; + return (d[n] << 8 | d[n + 1]); +} + +// reverse same array +void reverse_array(uint8_t *d, size_t n) { + if (d == NULL || n < 2) { + return; + } + + for (int i = 0, j = n - 1; i < j; ++i, --j) { + d[i] ^= d[j]; + d[j] ^= d[i]; + d[i] ^= d[j]; + } +} + +// reverse src array into dest array +void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest) { + if (src == NULL || src_len == 0 || dest == NULL) { + return; + } + + for (int i = 0; i < src_len; i++) { + dest[i] = src[(src_len - 1) - i]; + } } diff --git a/common/commonutil.h b/common/commonutil.h index 0d8932a51..7beb95355 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -85,5 +85,9 @@ void htole24(uint32_t val, uint8_t data[3]); uint32_t rotl(uint32_t a, uint8_t n); uint32_t rotr(uint32_t a, uint8_t n); -uint16_t get_sw(const uint8_t *d, uint8_t n); +uint16_t get_sw(const uint8_t *d, uint16_t n); + +void reverse_array(uint8_t *d, size_t n); +void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest); + #endif diff --git a/common/cryptorf/cryptolib.c b/common/cryptorf/cryptolib.c index eded5315c..b4abbdd54 100644 --- a/common/cryptorf/cryptolib.c +++ b/common/cryptorf/cryptolib.c @@ -280,7 +280,6 @@ void cm_auth(const uint8_t *Gc, const uint8_t *Ci, const uint8_t *Q, uint8_t *Ch static void cm_crypt(const CryptoAction ca, const uint8_t offset, const uint8_t len, const uint8_t *in, uint8_t *out, crypto_state s) { size_t pos; - uint8_t bt; next_n(true, 5, 0, s); next(true, offset, s); @@ -288,7 +287,7 @@ static void cm_crypt(const CryptoAction ca, const uint8_t offset, const uint8_t next(true, len, s); for (pos = 0; pos < len; pos++) { // Perform the crypto operation - bt = in[pos] ^ cm_byte(s); + uint8_t bt = in[pos] ^ cm_byte(s); // Generate output if (out) out[pos] = bt; diff --git a/common/generator.c b/common/generator.c index 4856327e2..852ea2b61 100644 --- a/common/generator.c +++ b/common/generator.c @@ -27,10 +27,20 @@ #include "common.h" //BSWAP_32/64 #include "util.h" #include "pm3_cmd.h" -#include "ui.h" +#include "crc16.h" // crc16 ccitt #include "mbedtls/sha1.h" #include "mbedtls/md5.h" -#include "crc16.h" // crc16 ccitt +#include "mbedtls/cmac.h" +#include "mbedtls/cipher.h" +#include "mbedtls/md.h" + +#ifndef ON_DEVICE +#include "ui.h" +# define prnt(args...) PrintAndLogEx(DEBUG, ## args ); +#else +# include "dbprint.h" +# define prnt Dbprintf +#endif // Implementation tips: // For each implementation of the algos, I recommend adding a self test for easy "simple unit" tests when Travis CI / Appveyor runs. @@ -230,6 +240,12 @@ uint16_t ul_ev1_packgen_def(const uint8_t *uid) { return 0x0000; } +// MIFARE ULTRALIGHT OTP generators +uint32_t ul_c_otpgenA(const uint8_t *uid) { + return 0x534C544F; +} + + //------------------------------------ // MFC key generation stuff // Each algo implementation should offer two key generation functions. @@ -475,15 +491,14 @@ uint32_t lf_t55xx_white_pwdgen(uint32_t id) { // Gallagher Desfire Key Diversification Input for Cardax Card Data Application int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint32_t aid, uint8_t *kdfInputOut, uint8_t *kdfInputLen) { if (uid == NULL || (uidLen != 4 && uidLen != 7) || keyNo > 2 || kdfInputOut == NULL || kdfInputLen == NULL) { - if (g_debugMode) { - PrintAndLogEx(WARNING, "Invalid arguments"); - } + prnt("Invalid arguments"); return PM3_EINVARG; } int len = 0; - // If the keyNo == 1, then omit the UID. - if (keyNo != 1) { + // If the keyNo == 1 or the aid is 000000, then omit the UID. + // On the other hand, if the aid is 1f81f4 (config card) always include the UID. + if ((keyNo != 1 && aid != 0x000000) || (aid == 0x1f81f4)) { if (*kdfInputLen < (4 + uidLen)) { return PM3_EINVARG; } @@ -538,8 +553,9 @@ int mfc_algo_touch_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t * //------------------------------------ // Self tests //------------------------------------ -int generator_selftest(void) { +int generator_selftest(void) { +#ifndef ON_DEVICE #define NUM_OF_TEST 8 PrintAndLogEx(INFO, "PWD / KEY generator selftest"); @@ -611,6 +627,8 @@ int generator_selftest(void) { PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? "OK" : "->00018383<--"); PrintAndLogEx(SUCCESS, "------------------- Selftest %s", (testresult == NUM_OF_TEST) ? "OK" : "fail"); + +#endif return PM3_SUCCESS; } diff --git a/common/generator.h b/common/generator.h index 5fc81ba25..1008d01fa 100644 --- a/common/generator.h +++ b/common/generator.h @@ -36,6 +36,8 @@ uint16_t ul_ev1_packgenC(const uint8_t *uid); uint16_t ul_ev1_packgenD(const uint8_t *uid); uint16_t ul_ev1_packgenE(const uint8_t *uid); +uint32_t ul_c_otpgenA(const uint8_t *uid); + int mfc_algo_ving_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key); int mfc_algo_ving_all(uint8_t *uid, uint8_t *keys); diff --git a/common/mbedtls/bignum.c b/common/mbedtls/bignum.c index 7c0daebd3..200ad7ca0 100644 --- a/common/mbedtls/bignum.c +++ b/common/mbedtls/bignum.c @@ -33,6 +33,8 @@ * */ +#pragma GCC diagnostic ignored "-Wunused-variable" + #include "common.h" #if defined(MBEDTLS_BIGNUM_C) @@ -1440,6 +1442,7 @@ __attribute__((noinline)) #endif void mpi_mul_hlp(size_t i, mbedtls_mpi_uint *s, mbedtls_mpi_uint *d, mbedtls_mpi_uint b) { mbedtls_mpi_uint c = 0, t = 0; + (void)t; #if defined(MULADDC_HUIT) for (; i >= 8; i -= 8) { diff --git a/common_arm/Makefile.hal b/common_arm/Makefile.hal index 62b99020d..a65514784 100644 --- a/common_arm/Makefile.hal +++ b/common_arm/Makefile.hal @@ -95,6 +95,7 @@ ifeq ($(PLATFORM),PM3RDV4) PLATFORM_DEFS = -DWITH_SMARTCARD -DWITH_FLASH -DRDV4 PLTNAME = Proxmark3 RDV4 PLATFORM_FPGA = xc2s30 + RDV4 = yes else ifeq ($(PLATFORM),PM3OTHER) $(warning PLATFORM=PM3OTHER is deprecated, please use PLATFORM=PM3GENERIC) PLTNAME = Proxmark3 generic target @@ -113,6 +114,10 @@ endif # parsing additional PLATFORM_EXTRAS tokens PLATFORM_EXTRAS_TMP:=$(PLATFORM_EXTRAS) +ifneq (,$(findstring FLASH,$(PLATFORM_EXTRAS_TMP))) + PLATFORM_DEFS += -DWITH_FLASH + PLATFORM_EXTRAS_TMP := $(strip $(filter-out FLASH,$(PLATFORM_EXTRAS_TMP))) +endif ifneq (,$(findstring BTADDON,$(PLATFORM_EXTRAS_TMP))) PLATFORM_DEFS += -DWITH_FPC_USART_HOST PLATFORM_EXTRAS_TMP := $(strip $(filter-out BTADDON,$(PLATFORM_EXTRAS_TMP))) @@ -143,6 +148,9 @@ ifneq ($(SKIP_ZX8211),1) endif # common HF support +ifneq ($(SKIP_HF),1) + PLATFORM_DEFS += -DWITH_GENERAL_HF +endif ifneq ($(SKIP_ISO15693),1) PLATFORM_DEFS += -DWITH_ISO15693 endif diff --git a/armsrc/flashmem.c b/common_arm/flashmem.c similarity index 88% rename from armsrc/flashmem.c rename to common_arm/flashmem.c index fb1f74140..94dcf1fd3 100644 --- a/armsrc/flashmem.c +++ b/common_arm/flashmem.c @@ -20,8 +20,13 @@ #include "proxmark3_arm.h" #include "ticks.h" + +#ifndef AS_BOOTROM #include "dbprint.h" +#endif // AS_BOOTROM + #include "string.h" +#include "usb_cdc.h" /* here: use NCPS2 @ PA10: */ #define SPI_CSR_NUM 2 @@ -36,11 +41,392 @@ static uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD; #define FASTFLASH (FLASHMEM_SPIBAUDRATE > FLASH_MINFAST) +#ifndef AS_BOOTROM + void FlashmemSetSpiBaudrate(uint32_t baudrate) { FLASHMEM_SPIBAUDRATE = baudrate; Dbprintf("Spi Baudrate : %dMHz", FLASHMEM_SPIBAUDRATE / 1000000); } +// read ID out +bool Flash_ReadID_90(flash_device_type_90_t *result) { + + if (Flash_CheckBusy(BUSY_TIMEOUT)) return false; + + // Manufacture ID / device ID + FlashSendByte(ID); + FlashSendByte(0x00); + FlashSendByte(0x00); + FlashSendByte(0x00); + + result->manufacturer_id = FlashSendByte(0xFF); + result->device_id = FlashSendLastByte(0xFF); + + return true; +} + +uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { + + if (!FlashInit()) return 0; + + // length should never be zero + if (!len || Flash_CheckBusy(BUSY_TIMEOUT)) return 0; + + uint8_t cmd = (FASTFLASH) ? FASTREAD : READDATA; + + FlashSendByte(cmd); + Flash_TransferAdresse(address); + + if (FASTFLASH) { + FlashSendByte(DUMMYBYTE); + } + + uint16_t i = 0; + for (; i < (len - 1); i++) + out[i] = FlashSendByte(0xFF); + + out[i] = FlashSendLastByte(0xFF); + FlashStop(); + return len; +} + +void Flash_TransferAdresse(uint32_t address) { + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendByte((address >> 0) & 0xFF); +} + +/* This ensures we can ReadData without having to cycle through initialization every time */ +uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { + + // length should never be zero + if (!len) return 0; + + uint8_t cmd = (FASTFLASH) ? FASTREAD : READDATA; + + FlashSendByte(cmd); + Flash_TransferAdresse(address); + + if (FASTFLASH) { + FlashSendByte(DUMMYBYTE); + } + + uint16_t i = 0; + for (; i < (len - 1); i++) + out[i] = FlashSendByte(0xFF); + + out[i] = FlashSendLastByte(0xFF); + return len; +} + +//////////////////////////////////////// +// Write data can only program one page. A page has 256 bytes. +// if len > 256, it might wrap around and overwrite pos 0. +uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { + + // length should never be zero + if (!len) + return 0; + + // Max 256 bytes write + if (((address & 0xFF) + len) > 256) { + Dbprintf("Flash_WriteData 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF) + len, len); + return 0; + } + + // out-of-range + if (((address >> 16) & 0xFF) > MAX_BLOCKS) { + Dbprintf("Flash_WriteData, block out-of-range"); + return 0; + } + + if (!FlashInit()) { + if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); + return 0; + } + + Flash_CheckBusy(BUSY_TIMEOUT); + + Flash_WriteEnable(); + + FlashSendByte(PAGEPROG); + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendByte((address >> 0) & 0xFF); + + uint16_t i = 0; + for (; i < (len - 1); i++) + FlashSendByte(in[i]); + + FlashSendLastByte(in[i]); + + FlashStop(); + return len; +} + +// length should never be zero +// Max 256 bytes write +// out-of-range +uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len) { + + if (!len) + return 0; + + if (((address & 0xFF) + len) > 256) { + Dbprintf("Flash_WriteDataCont 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF) + len, len); + return 0; + } + + if (((address >> 16) & 0xFF) > MAX_BLOCKS) { + Dbprintf("Flash_WriteDataCont, block out-of-range"); + return 0; + } + + FlashSendByte(PAGEPROG); + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendByte((address >> 0) & 0xFF); + + uint16_t i = 0; + for (; i < (len - 1); i++) + FlashSendByte(in[i]); + + FlashSendLastByte(in[i]); + return len; +} + +// assumes valid start 256 based 00 address +// +uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len) { + + bool isok; + uint16_t res, bytes_sent = 0, bytes_remaining = len; + uint8_t buf[FLASH_MEM_BLOCK_SIZE]; + while (bytes_remaining > 0) { + + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + memcpy(buf, in + bytes_sent, bytes_in_packet); + + res = Flash_WriteDataCont(address + bytes_sent, buf, bytes_in_packet); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + isok = (res == bytes_in_packet); + + if (!isok) + goto out; + } + +out: + FlashStop(); + return len; +} + +// WARNING -- if callers are using a file system (such as SPIFFS), +// they should inform the file system of this change +// e.g., rdv40_spiffs_check() +bool Flash_WipeMemoryPage(uint8_t page) { + if (!FlashInit()) { + if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); + return false; + } + Flash_ReadStat1(); + + // Each block is 64Kb. One block erase takes 1s ( 1000ms ) + Flash_WriteEnable(); + Flash_Erase64k(page); + Flash_CheckBusy(BUSY_TIMEOUT); + + FlashStop(); + + return true; +} +// Wipes flash memory completely, fills with 0xFF +bool Flash_WipeMemory(void) { + if (!FlashInit()) { + if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); + return false; + } + Flash_ReadStat1(); + + // Each block is 64Kb. Four blocks + // one block erase takes 1s ( 1000ms ) + Flash_WriteEnable(); + Flash_Erase64k(0); + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + Flash_Erase64k(1); + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + Flash_Erase64k(2); + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + Flash_Erase64k(3); + Flash_CheckBusy(BUSY_TIMEOUT); + + FlashStop(); + return true; +} + +// enable the flash write +void Flash_WriteEnable(void) { + FlashSendLastByte(WRITEENABLE); + if (g_dbglevel > 3) Dbprintf("Flash Write enabled"); +} + +// erase 4K at one time +// execution time: 0.8ms / 800us +bool Flash_Erase4k(uint8_t block, uint8_t sector) { + + if (block > MAX_BLOCKS || sector > MAX_SECTORS) return false; + + FlashSendByte(SECTORERASE); + FlashSendByte(block); + FlashSendByte(sector << 4); + FlashSendLastByte(00); + return true; +} + +/* +// erase 32K at one time +// execution time: 0,3s / 300ms +bool Flash_Erase32k(uint32_t address) { + if (address & (32*1024 - 1)) { + if ( g_dbglevel > 1 ) Dbprintf("Flash_Erase32k : Address is not align at 4096"); + return false; + } + FlashSendByte(BLOCK32ERASE); + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendLastByte((address >> 0) & 0xFF); + return true; +} +*/ + +// erase 64k at one time +// since a block is 64kb, and there is four blocks. +// we only need block number, as MSB +// execution time: 1s / 1000ms +// 0x00 00 00 -- 0x 00 FF FF == block 0 +// 0x01 00 00 -- 0x 01 FF FF == block 1 +// 0x02 00 00 -- 0x 02 FF FF == block 2 +// 0x03 00 00 -- 0x 03 FF FF == block 3 +bool Flash_Erase64k(uint8_t block) { + + if (block > MAX_BLOCKS) return false; + + FlashSendByte(BLOCK64ERASE); + FlashSendByte(block); + FlashSendByte(0x00); + FlashSendLastByte(0x00); + return true; +} + +/* +// Erase chip +void Flash_EraseChip(void) { + FlashSendLastByte(CHIPERASE); +} +*/ + +void Flashmem_print_status(void) { + DbpString(_CYAN_("Flash memory")); + Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), FLASHMEM_SPIBAUDRATE / 1000000); + + if (!FlashInit()) { + DbpString(" Init.................... " _RED_("FAILED")); + return; + } + DbpString(" Init.................... " _GREEN_("OK")); + + // NOTE: It would likely be more useful to use JDEC ID command 9F, + // as it provides a third byte indicative of capacity. + flash_device_type_90_t device_type = {0}; + if (!Flash_ReadID_90(&device_type)) { + DbpString(" Device ID............... " _RED_(" --> Not Found <--")); + } else { + if (device_type.manufacturer_id == WINBOND_MANID) { + switch (device_type.device_id) { + case WINBOND_2MB_DEVID: + DbpString(" Memory size............. " _YELLOW_("2 mbits / 256 kb")); + break; + case WINBOND_1MB_DEVID: + DbpString(" Memory size..... ....... " _YELLOW_("1 mbits / 128 kb")); + break; + case WINBOND_512KB_DEVID: + DbpString(" Memory size............. " _YELLOW_("512 kbits / 64 kb")); + break; + default: + break; + } + } else { + Dbprintf(" Device ID............... " _YELLOW_("%02X / %02X (unknown)"), + device_type.manufacturer_id, + device_type.device_id + ); + } + } + + uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + Flash_UniqueID(uid); + Dbprintf(" Unique ID (be).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), + uid[0], uid[1], uid[2], uid[3], + uid[4], uid[5], uid[6], uid[7] + ); + if (g_dbglevel > 3) { + Dbprintf(" Unique ID (le).......... " _YELLOW_("0x%02X%02X%02X%02X%02X%02X%02X%02X"), + uid[7], uid[6], uid[5], uid[4], + uid[3], uid[2], uid[1], uid[0] + ); + } + FlashStop(); +} + +void Flashmem_print_info(void) { + + if (!FlashInit()) return; + + DbpString(_CYAN_("Flash memory dictionary loaded")); + + // load dictionary offsets. + uint8_t keysum[2]; + uint16_t num; + + Flash_CheckBusy(BUSY_TIMEOUT); + uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET, keysum, 2); + if (isok == 2) { + num = ((keysum[1] << 8) | keysum[0]); + if (num != 0xFFFF && num != 0x0) + Dbprintf(" Mifare.................. "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_MF_KEYS_MAX); + } + + Flash_CheckBusy(BUSY_TIMEOUT); + isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET, keysum, 2); + if (isok == 2) { + num = ((keysum[1] << 8) | keysum[0]); + if (num != 0xFFFF && num != 0x0) + Dbprintf(" T55x7................... "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_T55XX_KEYS_MAX); + } + + Flash_CheckBusy(BUSY_TIMEOUT); + isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET, keysum, 2); + if (isok == 2) { + num = ((keysum[1] << 8) | keysum[0]); + if (num != 0xFFFF && num != 0x0) + Dbprintf(" iClass.................. "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_ICLASS_KEYS_MAX); + } + + FlashStop(); +} + +#endif // #ifndef AS_BOOTROM + + // initialize bool FlashInit(void) { FlashSetup(FLASHMEM_SPIBAUDRATE); @@ -55,6 +441,52 @@ bool FlashInit(void) { return true; } +// read unique id for chip. +void Flash_UniqueID(uint8_t *uid) { + + if (Flash_CheckBusy(BUSY_TIMEOUT)) return; + + // reading unique serial number + FlashSendByte(UNIQUE_ID); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + + uid[7] = FlashSendByte(0xFF); + uid[6] = FlashSendByte(0xFF); + uid[5] = FlashSendByte(0xFF); + uid[4] = FlashSendByte(0xFF); + uid[3] = FlashSendByte(0xFF); + uid[2] = FlashSendByte(0xFF); + uid[1] = FlashSendByte(0xFF); + uid[0] = FlashSendLastByte(0xFF); +} + +void FlashStop(void) { + //Bof + //* Reset all the Chip Select register + AT91C_BASE_SPI->SPI_CSR[0] = 0; + AT91C_BASE_SPI->SPI_CSR[1] = 0; + AT91C_BASE_SPI->SPI_CSR[2] = 0; + AT91C_BASE_SPI->SPI_CSR[3] = 0; + + // Reset the SPI mode + AT91C_BASE_SPI->SPI_MR = 0; + + // Disable all interrupts + AT91C_BASE_SPI->SPI_IDR = 0xFFFFFFFF; + + // SPI disable + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; + +#ifndef AS_BOOTROM + if (g_dbglevel > 3) Dbprintf("FlashStop"); +#endif // AS_BOOTROM + + StopTicks(); +} + void FlashSetup(uint32_t baudrate) { //WDT_DISABLE AT91C_BASE_WDTC->WDTC_WDMR = AT91C_WDTC_WDDIS; @@ -155,26 +587,28 @@ void FlashSetup(uint32_t baudrate) { if (AT91C_BASE_SPI->SPI_RDR == 0) {}; } -void FlashStop(void) { - //Bof - //* Reset all the Chip Select register - AT91C_BASE_SPI->SPI_CSR[0] = 0; - AT91C_BASE_SPI->SPI_CSR[1] = 0; - AT91C_BASE_SPI->SPI_CSR[2] = 0; - AT91C_BASE_SPI->SPI_CSR[3] = 0; +bool Flash_CheckBusy(uint32_t timeout) { + WaitUS(WINBOND_WRITE_DELAY); + StartCountUS(); + uint32_t _time = GetCountUS(); - // Reset the SPI mode - AT91C_BASE_SPI->SPI_MR = 0; + do { + if (!(Flash_ReadStat1() & BUSY)) { + return false; + } + } while ((GetCountUS() - _time) < timeout); - // Disable all interrupts - AT91C_BASE_SPI->SPI_IDR = 0xFFFFFFFF; + if (timeout <= (GetCountUS() - _time)) { + return true; + } - // SPI disable - AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; + return false; +} - if (g_dbglevel > 3) Dbprintf("FlashStop"); - - StopTicks(); +// read state register 1 +uint8_t Flash_ReadStat1(void) { + FlashSendByte(READSTAT1); + return FlashSendLastByte(0xFF); } // send one byte over SPI @@ -200,416 +634,3 @@ uint16_t FlashSendByte(uint32_t data) { uint16_t FlashSendLastByte(uint32_t data) { return FlashSendByte(data | AT91C_SPI_LASTXFER); } - -// read state register 1 -uint8_t Flash_ReadStat1(void) { - FlashSendByte(READSTAT1); - return FlashSendLastByte(0xFF); -} - -bool Flash_CheckBusy(uint32_t timeout) { - WaitUS(WINBOND_WRITE_DELAY); - StartCountUS(); - uint32_t _time = GetCountUS(); - - if (g_dbglevel > 3) Dbprintf("Checkbusy in..."); - - do { - if (!(Flash_ReadStat1() & BUSY)) { - return false; - } - } while ((GetCountUS() - _time) < timeout); - - if (timeout <= (GetCountUS() - _time)) { - return true; - } - - return false; -} - -// read ID out -uint8_t Flash_ReadID(void) { - - if (Flash_CheckBusy(BUSY_TIMEOUT)) return 0; - - // Manufacture ID / device ID - FlashSendByte(ID); - FlashSendByte(0x00); - FlashSendByte(0x00); - FlashSendByte(0x00); - - uint8_t man_id = FlashSendByte(0xFF); - uint8_t dev_id = FlashSendLastByte(0xFF); - - if (g_dbglevel > 3) Dbprintf("Flash ReadID | Man ID %02x | Device ID %02x", man_id, dev_id); - - if ((man_id == WINBOND_MANID) && (dev_id == WINBOND_DEVID)) - return dev_id; - - return 0; -} - -// read unique id for chip. -void Flash_UniqueID(uint8_t *uid) { - - if (Flash_CheckBusy(BUSY_TIMEOUT)) return; - - // reading unique serial number - FlashSendByte(UNIQUE_ID); - FlashSendByte(0xFF); - FlashSendByte(0xFF); - FlashSendByte(0xFF); - FlashSendByte(0xFF); - - uid[7] = FlashSendByte(0xFF); - uid[6] = FlashSendByte(0xFF); - uid[5] = FlashSendByte(0xFF); - uid[4] = FlashSendByte(0xFF); - uid[3] = FlashSendByte(0xFF); - uid[2] = FlashSendByte(0xFF); - uid[1] = FlashSendByte(0xFF); - uid[0] = FlashSendLastByte(0xFF); -} - -uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { - - if (!FlashInit()) return 0; - - // length should never be zero - if (!len || Flash_CheckBusy(BUSY_TIMEOUT)) return 0; - - uint8_t cmd = (FASTFLASH) ? FASTREAD : READDATA; - - FlashSendByte(cmd); - Flash_TransferAdresse(address); - - if (FASTFLASH) { - FlashSendByte(DUMMYBYTE); - } - - uint16_t i = 0; - for (; i < (len - 1); i++) - out[i] = FlashSendByte(0xFF); - - out[i] = FlashSendLastByte(0xFF); - FlashStop(); - return len; -} - -void Flash_TransferAdresse(uint32_t address) { - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendByte((address >> 0) & 0xFF); -} - -/* This ensures we can ReadData without having to cycle through initialization every time */ -uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len) { - - // length should never be zero - if (!len) return 0; - - uint8_t cmd = (FASTFLASH) ? FASTREAD : READDATA; - - FlashSendByte(cmd); - Flash_TransferAdresse(address); - - if (FASTFLASH) { - FlashSendByte(DUMMYBYTE); - } - - uint16_t i = 0; - for (; i < (len - 1); i++) - out[i] = FlashSendByte(0xFF); - - out[i] = FlashSendLastByte(0xFF); - return len; -} - - -//////////////////////////////////////// -// Write data can only program one page. A page has 256 bytes. -// if len > 256, it might wrap around and overwrite pos 0. -uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { - - // length should never be zero - if (!len) - return 0; - - // Max 256 bytes write - if (((address & 0xFF) + len) > 256) { - Dbprintf("Flash_WriteData 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF) + len, len); - return 0; - } - - // out-of-range - if (((address >> 16) & 0xFF) > MAX_BLOCKS) { - Dbprintf("Flash_WriteData, block out-of-range"); - return 0; - } - - if (!FlashInit()) { - if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); - return 0; - } - - Flash_CheckBusy(BUSY_TIMEOUT); - - Flash_WriteEnable(); - - FlashSendByte(PAGEPROG); - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendByte((address >> 0) & 0xFF); - - uint16_t i = 0; - for (; i < (len - 1); i++) - FlashSendByte(in[i]); - - FlashSendLastByte(in[i]); - - FlashStop(); - return len; -} - - -// length should never be zero -// Max 256 bytes write -// out-of-range -uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len) { - - if (!len) - return 0; - - if (((address & 0xFF) + len) > 256) { - Dbprintf("Flash_WriteDataCont 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF) + len, len); - return 0; - } - - if (((address >> 16) & 0xFF) > MAX_BLOCKS) { - Dbprintf("Flash_WriteDataCont, block out-of-range"); - return 0; - } - - FlashSendByte(PAGEPROG); - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendByte((address >> 0) & 0xFF); - - uint16_t i = 0; - for (; i < (len - 1); i++) - FlashSendByte(in[i]); - - FlashSendLastByte(in[i]); - return len; -} - -// assumes valid start 256 based 00 address -// -uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len) { - - bool isok; - uint16_t res, bytes_sent = 0, bytes_remaining = len; - uint8_t buf[FLASH_MEM_BLOCK_SIZE]; - while (bytes_remaining > 0) { - - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - - uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - memcpy(buf, in + bytes_sent, bytes_in_packet); - - res = Flash_WriteDataCont(address + bytes_sent, buf, bytes_in_packet); - - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - - isok = (res == bytes_in_packet); - - if (!isok) - goto out; - } - -out: - FlashStop(); - return len; -} - - -bool Flash_WipeMemoryPage(uint8_t page) { - if (!FlashInit()) { - if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); - return false; - } - Flash_ReadStat1(); - - // Each block is 64Kb. One block erase takes 1s ( 1000ms ) - Flash_WriteEnable(); - Flash_Erase64k(page); - Flash_CheckBusy(BUSY_TIMEOUT); - - FlashStop(); - return true; -} -// Wipes flash memory completely, fills with 0xFF -bool Flash_WipeMemory(void) { - if (!FlashInit()) { - if (g_dbglevel > 3) Dbprintf("Flash_WriteData init fail"); - return false; - } - Flash_ReadStat1(); - - // Each block is 64Kb. Four blocks - // one block erase takes 1s ( 1000ms ) - Flash_WriteEnable(); - Flash_Erase64k(0); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(1); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(2); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase64k(3); - Flash_CheckBusy(BUSY_TIMEOUT); - - FlashStop(); - return true; -} - -// enable the flash write -void Flash_WriteEnable(void) { - FlashSendLastByte(WRITEENABLE); - if (g_dbglevel > 3) Dbprintf("Flash Write enabled"); -} - -// erase 4K at one time -// execution time: 0.8ms / 800us -bool Flash_Erase4k(uint8_t block, uint8_t sector) { - - if (block > MAX_BLOCKS || sector > MAX_SECTORS) return false; - - FlashSendByte(SECTORERASE); - FlashSendByte(block); - FlashSendByte(sector << 4); - FlashSendLastByte(00); - return true; -} - -/* -// erase 32K at one time -// execution time: 0,3s / 300ms -bool Flash_Erase32k(uint32_t address) { - if (address & (32*1024 - 1)) { - if ( g_dbglevel > 1 ) Dbprintf("Flash_Erase32k : Address is not align at 4096"); - return false; - } - FlashSendByte(BLOCK32ERASE); - FlashSendByte((address >> 16) & 0xFF); - FlashSendByte((address >> 8) & 0xFF); - FlashSendLastByte((address >> 0) & 0xFF); - return true; -} -*/ - -// erase 64k at one time -// since a block is 64kb, and there is four blocks. -// we only need block number, as MSB -// execution time: 1s / 1000ms -// 0x00 00 00 -- 0x 00 FF FF == block 0 -// 0x01 00 00 -- 0x 01 FF FF == block 1 -// 0x02 00 00 -- 0x 02 FF FF == block 2 -// 0x03 00 00 -- 0x 03 FF FF == block 3 -bool Flash_Erase64k(uint8_t block) { - - if (block > MAX_BLOCKS) return false; - - FlashSendByte(BLOCK64ERASE); - FlashSendByte(block); - FlashSendByte(0x00); - FlashSendLastByte(0x00); - return true; -} - -/* -// Erase chip -void Flash_EraseChip(void) { - FlashSendLastByte(CHIPERASE); -} -*/ - -void Flashmem_print_status(void) { - DbpString(_CYAN_("Flash memory")); - Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), FLASHMEM_SPIBAUDRATE / 1000000); - - if (!FlashInit()) { - DbpString(" Init.................... " _RED_("FAILED")); - return; - } - DbpString(" Init.................... " _GREEN_("OK")); - - uint8_t dev_id = Flash_ReadID(); - switch (dev_id) { - case 0x11 : - DbpString(" Memory size............. " _YELLOW_("2 mbits / 256 kb")); - break; - case 0x10 : - DbpString(" Memory size..... ....... " _YELLOW_("1 mbits / 128 kb")); - break; - case 0x05 : - DbpString(" Memory size............. " _YELLOW_("512 kbits / 64 kb")); - break; - default : - DbpString(" Device ID............... " _YELLOW_(" --> Unknown <--")); - break; - } - - uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - Flash_UniqueID(uid); - Dbprintf(" Unique ID............... 0x%02X%02X%02X%02X%02X%02X%02X%02X", - uid[7], uid[6], uid[5], uid[4], - uid[3], uid[2], uid[1], uid[0] - ); - - FlashStop(); -} - -void Flashmem_print_info(void) { - - if (!FlashInit()) return; - - DbpString(_CYAN_("Flash memory dictionary loaded")); - - // load dictionary offsets. - uint8_t keysum[2]; - uint16_t num; - - Flash_CheckBusy(BUSY_TIMEOUT); - uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET, keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" Mifare.................. "_YELLOW_("%d")" keys", num); - } - - Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET, keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" T55x7................... "_YELLOW_("%d")" keys", num); - } - - Flash_CheckBusy(BUSY_TIMEOUT); - isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET, keysum, 2); - if (isok == 2) { - num = ((keysum[1] << 8) | keysum[0]); - if (num != 0xFFFF && num != 0x0) - Dbprintf(" iClass.................. "_YELLOW_("%d")" keys", num); - } - - FlashStop(); -} - - diff --git a/armsrc/flashmem.h b/common_arm/flashmem.h similarity index 91% rename from armsrc/flashmem.h rename to common_arm/flashmem.h index 2b829c378..f23a2786d 100644 --- a/armsrc/flashmem.h +++ b/common_arm/flashmem.h @@ -54,8 +54,11 @@ // Flash busy timeout: 20ms is the strict minimum when writing 256kb #define BUSY_TIMEOUT 200000L -#define WINBOND_MANID 0xEF -#define WINBOND_DEVID 0x11 +#define WINBOND_MANID 0xEF +#define WINBOND_2MB_DEVID 0x11 +#define WINBOND_1MB_DEVID 0x10 +#define WINBOND_512KB_DEVID 0x05 + #define PAGESIZE 0x100 #define WINBOND_WRITE_DELAY 0x02 @@ -100,17 +103,21 @@ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -void FlashmemSetSpiBaudrate(uint32_t baudrate); bool FlashInit(void); -void FlashSetup(uint32_t baudrate); +void Flash_UniqueID(uint8_t *uid); void FlashStop(void); -bool Flash_WaitIdle(void); -uint8_t Flash_ReadStat1(void); -uint8_t Flash_ReadStat2(void); -uint16_t FlashSendByte(uint32_t data); -void Flash_TransferAdresse(uint32_t address); +void FlashSetup(uint32_t baudrate); bool Flash_CheckBusy(uint32_t timeout); +uint8_t Flash_ReadStat1(void); +uint16_t FlashSendByte(uint32_t data); +uint16_t FlashSendLastByte(uint32_t data); + + +#ifndef AS_BOOTROM +void FlashmemSetSpiBaudrate(uint32_t baudrate); +bool Flash_WaitIdle(void); +void Flash_TransferAdresse(uint32_t address); void Flash_WriteEnable(void); bool Flash_WipeMemoryPage(uint8_t page); @@ -119,8 +126,12 @@ bool Flash_Erase4k(uint8_t block, uint8_t sector); //bool Flash_Erase32k(uint32_t address); bool Flash_Erase64k(uint8_t block); -void Flash_UniqueID(uint8_t *uid); -uint8_t Flash_ReadID(void); +typedef struct { + uint8_t manufacturer_id; + uint8_t device_id; +} flash_device_type_90_t; // to differentiate from JDEC ID via cmd 9F +bool Flash_ReadID_90(flash_device_type_90_t *result); + uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len); uint16_t Flash_ReadDataCont(uint32_t address, uint8_t *out, uint16_t len); uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len); @@ -128,6 +139,8 @@ uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); void Flashmem_print_info(void); -uint16_t FlashSendLastByte(uint32_t data); + +#endif // #ifndef AS_BOOTROM + #endif diff --git a/armsrc/ticks.c b/common_arm/ticks.c similarity index 96% rename from armsrc/ticks.c rename to common_arm/ticks.c index 61089595c..342aa3cab 100644 --- a/armsrc/ticks.c +++ b/common_arm/ticks.c @@ -19,9 +19,13 @@ #include "ticks.h" #include "proxmark3_arm.h" +#ifndef AS_BOOTROM #include "dbprint.h" +#endif +#ifndef AS_BOOTROM + // timer counts in 666ns increments (32/48MHz), rounding applies // WARNING: timer can't measure more than 43ms (666ns * 0xFFFF) void SpinDelayUsPrecision(int us) { @@ -35,11 +39,14 @@ void SpinDelayUsPrecision(int us) { AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xFFFF; // Channel Period Register - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks; + if (end == 0) // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0 + end++; // so we have to end++ to avoid inivity loop for (;;) { uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - if (now == (uint16_t)(start + ticks)) + + if (now == end) return; WDT_HIT(); @@ -59,13 +66,15 @@ void SpinDelayUs(int us) { AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + uint16_t end = AT91C_BASE_PWMC_CH0->PWMC_CCNTR + ticks; + if (end == 0) // AT91C_BASE_PWMC_CH0->PWMC_CCNTR is never == 0 + end++; // so we have to end++ to avoid inivity loop for (;;) { uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - if (now == (uint16_t)(start + ticks)) - return; + if (now == end) + return; WDT_HIT(); } } @@ -112,40 +121,6 @@ uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { return (UINT32_MAX - start_ticks) + stop_ticks; } -// ------------------------------------------------------------------------- -// microseconds timer -// ------------------------------------------------------------------------- -void StartCountUS(void) { - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; - - // fast clock - // tick=1.5mks - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 - AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | - AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; - AT91C_BASE_TC0->TC_RA = 1; - AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 - - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from timer 0 - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // Assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; - - while (AT91C_BASE_TC1->TC_CV > 0); -} - -uint32_t RAMFUNC GetCountUS(void) { - //return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV / 15) * 10); - // By suggestion from PwPiwi, http://www.proxmark.org/forum/viewtopic.php?pid=17548#p17548 - return ((uint32_t)AT91C_BASE_TC1->TC_CV) * 0x8000 + (((uint32_t)AT91C_BASE_TC0->TC_CV) * 2) / 3; -} - // ------------------------------------------------------------------------- // Timer for iso14443 commands. Uses ssp_clk from FPGA // ------------------------------------------------------------------------- @@ -241,6 +216,47 @@ uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start) { return (UINT32_MAX - start) + stop; } +void WaitMS(uint32_t ms) { + WaitTicks((ms & 0x1FFFFF) * 1500); +} + +#endif // #ifndef AS_BOOTROM + +// ------------------------------------------------------------------------- +// microseconds timer +// ------------------------------------------------------------------------- +void StartCountUS(void) { + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; + + // fast clock + // tick=1.5mks + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | + AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; + AT91C_BASE_TC0->TC_RA = 1; + AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 + + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from timer 0 + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; + + while (AT91C_BASE_TC1->TC_CV > 0); +} + +uint32_t RAMFUNC GetCountUS(void) { + //return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV / 15) * 10); + // By suggestion from PwPiwi, http://www.proxmark.org/forum/viewtopic.php?pid=17548#p17548 + return ((uint32_t)AT91C_BASE_TC1->TC_CV) * 0x8000 + (((uint32_t)AT91C_BASE_TC0->TC_CV) * 2) / 3; +} + + // ------------------------------------------------------------------------- // Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks @@ -277,7 +293,6 @@ void StartTicks(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; while (AT91C_BASE_TC0->TC_CV > 0); } - uint32_t GetTicks(void) { uint32_t hi, lo; @@ -302,9 +317,6 @@ void WaitTicks(uint32_t ticks) { void WaitUS(uint32_t us) { WaitTicks((us & 0x3FFFFFFF) * 3 / 2); } -void WaitMS(uint32_t ms) { - WaitTicks((ms & 0x1FFFFF) * 1500); -} // stop clock void StopTicks(void) { diff --git a/armsrc/ticks.h b/common_arm/ticks.h similarity index 89% rename from armsrc/ticks.h rename to common_arm/ticks.h index 7ad4486fa..4a2276268 100644 --- a/armsrc/ticks.h +++ b/common_arm/ticks.h @@ -26,6 +26,19 @@ #define GET_TICKS GetTicks() #endif +void StartTicks(void); +uint32_t GetTicks(void); +void WaitUS(uint32_t us); +void WaitTicks(uint32_t ticks); +void StartCountUS(void); +uint32_t RAMFUNC GetCountUS(void); +void StopTicks(void); + + +#ifndef AS_BOOTROM ////////////////////////////////////////////////////////////// +// Bootrom does not require these functions. +// Wrap in #ifndef to avoid accidental bloat of bootrom + void SpinDelay(int ms); void SpinDelayUs(int us); void SpinDelayUsPrecision(int us); // precision 0.6us , running for 43ms before @@ -34,8 +47,6 @@ void StartTickCount(void); uint32_t RAMFUNC GetTickCount(void); uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks); -void StartCountUS(void); -uint32_t RAMFUNC GetCountUS(void); void ResetUSClock(void); void SpinDelayCountUs(uint32_t us); @@ -44,12 +55,10 @@ void ResetSspClk(void); uint32_t RAMFUNC GetCountSspClk(void); uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start); -void StartTicks(void); -uint32_t GetTicks(void); -void WaitTicks(uint32_t ticks); -void WaitUS(uint32_t us); void WaitMS(uint32_t ms); -void StopTicks(void); +#endif // #ifndef AS_BOOTROM + + #endif diff --git a/common_arm/usb_cdc.c b/common_arm/usb_cdc.c index 0ccf25aef..cb2d0c64a 100644 --- a/common_arm/usb_cdc.c +++ b/common_arm/usb_cdc.c @@ -366,11 +366,60 @@ static const char StrProduct[] = { 'p', 0, 'r', 0, 'o', 0, 'x', 0, 'm', 0, 'a', 0, 'r', 0, 'k', 0, '3', 0 }; +#ifndef WITH_FLASH static const char StrSerialNumber[] = { 14, // Length 0x03, // Type is string 'i', 0, 'c', 0, 'e', 0, 'm', 0, 'a', 0, 'n', 0 }; +#else // WITH_FLASH is defined + +// Manually calculated size of descriptor with unique ID: +// offset 0, lengt h 1: total length field +// offset 1, length 1: descriptor type field +// offset 2, length 12: 6x unicode chars (original string) +// offset 14, length 4: 2x unicode chars (underscores) [[ to avoid descriptor being (size % 8) == 0, OS bug workaround ]] +// offset 18, length 32: 16x unicode chars (8-byte serial as hex characters) +// ============================ +// total: 50 bytes +#define USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH 50 +char StrSerialNumber[] = { + 14, // Length is initially identical to non-unique version ... The length updated at boot, if unique serial is available + 0x03, // Type is string + 'i', 0, 'c', 0, 'e', 0, 'm', 0, 'a', 0, 'n', 0, + '_', 0, '_', 0, + 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, + 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, 'x', 0, +}; +void usb_update_serial(uint64_t newSerialNumber) { + static bool configured = false; // TODO: enable by setting to false here... + if (configured) { + return; + } + // run this only once per boot... even if it fails to find serial number + configured = true; + // reject serial number if all-zero or all-ones + if ((newSerialNumber == 0x0000000000000000) || (newSerialNumber == 0xFFFFFFFFFFFFFFFF)) { + return; + } + // Descriptor is, effectively, initially identical to non-unique serial + // number because it reports the shorter length in the first byte. + // Convert uniqueID's eight bytes to 16 unicode characters in the + // descriptor and, finally, update the descriptor's length, which + // causes the serial number to become visible. + for (uint8_t i = 0; i < 8; i++) { + // order of nibbles chosen to match display order from `hw status` + uint8_t nibble1 = (newSerialNumber >> ((8 * i) + 4)) & 0xFu; // bitmasks [0xF0, 0xF000, 0xF00000, ... 0xF000000000000000] + uint8_t nibble2 = (newSerialNumber >> ((8 * i) + 0)) & 0xFu; // bitmasks [0x0F, 0x0F00, 0x0F0000, ... 0x0F00000000000000] + char c1 = nibble1 < 10 ? '0' + nibble1 : 'A' + (nibble1 - 10); + char c2 = nibble2 < 10 ? '0' + nibble2 : 'A' + (nibble2 - 10); + StrSerialNumber[18 + (4 * i) + 0] = c1; // [ 18, 22, .., 42, 46 ] + StrSerialNumber[18 + (4 * i) + 2] = c2; // [ 20, 24, .., 44, 48 ] + } + StrSerialNumber[0] = USB_STRING_DESCRIPTOR_SERIAL_NUMBER_LENGTH; +} +#endif + // size includes their own field. static const char StrMS_OSDescriptor[] = { diff --git a/common_arm/usb_cdc.h b/common_arm/usb_cdc.h index a09d3fc33..e7f6f3d09 100644 --- a/common_arm/usb_cdc.h +++ b/common_arm/usb_cdc.h @@ -31,6 +31,7 @@ bool usb_poll_validate_length(void); uint32_t usb_read(uint8_t *data, size_t len); int usb_write(const uint8_t *data, const size_t len); uint32_t usb_read_ng(uint8_t *data, size_t len); +void usb_update_serial(uint64_t newSerialNumber); void SetUSBreconnect(int value); int GetUSBreconnect(void); diff --git a/covbuild.sh b/covbuild.sh index b96e7059f..f3fbee6ba 100755 --- a/covbuild.sh +++ b/covbuild.sh @@ -27,6 +27,7 @@ cov-build --dir "$COVDIR" --no-generate-build-id --force make recovery cov-build --dir "$COVDIR" --c-coverage=gcov --no-network-coverage --no-generate-build-id --force make CC=$HOSTCC CXX=$HOSTCXX LD=$HOSTLD mfkey cov-build --dir "$COVDIR" --c-coverage=gcov --no-network-coverage --no-generate-build-id --force make CC=$HOSTCC CXX=$HOSTCXX LD=$HOSTLD nonce2key cov-build --dir "$COVDIR" --c-coverage=gcov --no-network-coverage --no-generate-build-id --force make CC=$HOSTCC CXX=$HOSTCXX LD=$HOSTLD mf_nonce_brute +cov-build --dir "$COVDIR" --c-coverage=gcov --no-network-coverage --no-generate-build-id --force make CC=$HOSTCC CXX=$HOSTCXX LD=$HOSTLD mfd_aes_brute cov-build --dir "$COVDIR" --c-coverage=gcov --no-network-coverage --no-generate-build-id --force make CC=$HOSTCC CXX=$HOSTCXX LD=$HOSTLD client ######################################### diff --git a/doc/T5577_Guide.md b/doc/T5577_Guide.md index f1abe2bb9..6c4eceaf5 100644 --- a/doc/T5577_Guide.md +++ b/doc/T5577_Guide.md @@ -22,6 +22,8 @@ | [The configuration Block – Block 0 Page 0](#the-configuration-block-block-0-page-0) | | [Exercise 2](#exercise-2) | | [The configuration Block – Block 3 Page 1](#the-configuration-block-block-3-page-1) | +| [Sniffing commands](#sniffing-commands) | +| [T5577 and Keysy](#t5577-and-keysy) | # Part 1 ^[Top](#top) @@ -391,8 +393,8 @@ required, please do not proceed. | Hex Data | Binary Data | |:--------:|:---------------------------------------| - | 00088040 | 000000000000100010000000111***0***0000 | - | 00088050 | 000000000000100010000000111***1***0000 | + | 000880E0 | 000000000000100010000000111***0***0000 | + | 000880F0 | 000000000000100010000000111***1***0000 | See how in the above we changed the bit in location 28 from a 0 to 1 0 = No Password, 1 = Use Password @@ -462,7 +464,9 @@ required, please do not proceed. ***Reading a T5577 block with a password when a password is not enabled can result in locking the card. Please only use read with a - password when it is known that a password is in use.*** + password when it is known that a password is in use. + + At least don't use block 0 for this and password with `1` in the most significant bit*** The proxmark3 has a safety check\! ``` @@ -529,7 +533,7 @@ required, please do not proceed. [=] Downlink mode..... default/fixed bit length [=] Password set...... No ``` - Yes we can! We can see Block 0 is the correct config 00088040 + Yes we can! We can see Block 0 is the correct config 000880E0 # Part 2 – Configuration Blocks ^[Top](#top) @@ -718,8 +722,28 @@ it, we can follow the password section and update the config from _to be written_ +## Sniffing commands +^[Top](#top) + +Some readers work with cards via T55xx commands (read/write/etc) and think that they are safe) +The password in this case is sent in clear text. +So) There is a sniff command to get this command from the buffer or the field: + + [usb] pm3 --> lf t55xx sniff + + result: + + [=] T55xx command detection + [+] Downlink mode | password | Data | blk | page | 0 | 1 | raw + [+] ------------------------+------------+----------+-----+------+-----+-+--------------------------------------------- + [+] Default write/pwd read | [FFxxxxxx] | FFxxxxxx | 6 | 0 | 16 | 45 | 1011111111101xxxxxxxxxxxxxxxx100000110 + [+] Default write/pwd read | [FFxxxxxx] | FFxxxxxx | 6 | 0 | 17 | 46 | 1011111111101xxxxxxxxxxxxxxxx100000110 + [+] ------------------------------------------------------------------------------------------------------------------- + + ## T5577 and Keysy +^[Top](#top) The Keysy tag cloning tool (https://tinylabs.io/keysy/) uses T5577 tags that have a special "password" value (NOT the password described above) written in Block 6 that is tied to the traceability data. The Keysy checks and computes the proper value for Block 6 and will not write to a tag that does not contain the proper value. This DRM technology relies on the face that genuine T5577 chips cannot have their traceability data (Blocks 1 and 2 of Page 1) re-written and that the method to computer the proper value for Block 6 is proprietary, therefore compelling you to buy their branded tags. diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index a1f04faf1..22e47b3f1 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -713,7 +713,7 @@ pm3 --> mem load -f iclass_default_keys --iclass Upgrade Sim Module firmware ``` -pm3 --> smart upgrade -f sim011.bin +pm3 --> smart upgrade -f sim013.bin ``` ## Smart Card diff --git a/doc/commands.json b/doc/commands.json index 4f6703656..703ff688c 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -175,13 +175,15 @@ "command": "clear", "description": "Clear the Proxmark3 client terminal screen", "notes": [ - "clear" + "clear -> clear the terminal screen", + "clear -b -> clear the terminal screen and the scrollback buffer" ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "-b, --back also clear the scrollback buffer" ], - "usage": "clear [-h]" + "usage": "clear [-hb]" }, "data askedgedetect": { "command": "data askedgedetect", @@ -404,7 +406,7 @@ }, "data help": { "command": "data help", - "description": "help This help ----------- ------------------------- Modulation------------------------- biphaserawdecode Biphase decode bin stream in DemodBuffer detectclock Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer fsktonrz Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk) manrawdecode Manchester decode binary stream in DemodBuffer modulation Identify LF signal for clock and modulation rawdemod Demodulate the data in the GraphBuffer and output binary ----------- ------------------------- Graph------------------------- askedgedetect Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave autocorr Autocorrelation over window dirthreshold Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev. decimate Decimate samples undecimate Un-decimate samples hide Hide graph window hpf Remove DC offset from trace iir Apply IIR buttersworth filter on plot data grid overlay grid on graph window ltrim Trim samples from left of trace mtrim Trim out samples from the specified start to the specified stop norm Normalize max/min to +/-128 plot Show graph window rtrim Trim samples from right of trace setgraphmarkers Set blue and orange marker in graph window shiftgraphzero Shift 0 for Graphed wave + or - shift value timescale Set a timescale to get a differential reading between the yellow and purple markers as time duration zerocrossings Count time between zero-crossings convertbitstream Convert GraphBuffer's 0/1 values to 127 / -127 getbitstream Convert GraphBuffer's >=1 values to 1 and <1 to 0 ----------- ------------------------- General------------------------- asn1 asn1 decoder bin2hex Converts binary to hexadecimal clear Clears bigbuf on deviceside and graph window diff diff of input files hex2bin Converts hexadecimal to binary load Load contents of file into graph window print Print the data in the DemodBuffer save Save signal trace data (from graph window) setdebugmode Set Debugging Level on client side", + "description": "help This help ----------- ------------------------- Modulation------------------------- biphaserawdecode Biphase decode bin stream in DemodBuffer detectclock Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer fsktonrz Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk) manrawdecode Manchester decode binary stream in DemodBuffer modulation Identify LF signal for clock and modulation rawdemod Demodulate the data in the GraphBuffer and output binary ----------- ------------------------- Graph------------------------- askedgedetect Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave autocorr Autocorrelation over window dirthreshold Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev. decimate Decimate samples undecimate Un-decimate samples hide Hide graph window hpf Remove DC offset from trace iir Apply IIR buttersworth filter on plot data grid overlay grid on graph window ltrim Trim samples from left of trace mtrim Trim out samples from the specified start to the specified stop norm Normalize max/min to +/-128 plot Show graph window rtrim Trim samples from right of trace setgraphmarkers Set blue and orange marker in graph window shiftgraphzero Shift 0 for Graphed wave + or - shift value timescale Set a timescale to get a differential reading between the yellow and purple markers as time duration zerocrossings Count time between zero-crossings convertbitstream Convert GraphBuffer's 0/1 values to 127 / -127 getbitstream Convert GraphBuffer's >=1 values to 1 and <1 to 0 ----------- ------------------------- General------------------------- asn1 asn1 decoder bin2hex Converts binary to hexadecimal clear Clears bigbuf on deviceside and graph window diff diff of input files hex2bin Converts hexadecimal to binary load Load contents of file into graph window num Converts dec/hex/bin print Print the data in the DemodBuffer save Save signal trace data (from graph window) setdebugmode Set Debugging Level on client side", "notes": [], "offline": true, "options": [], @@ -484,9 +486,11 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file file to load" + "-f, --file file to load", + "-b, --bin binary file", + "-n, --no-fix Load data from file without any transformations" ], - "usage": "data load [-h] -f " + "usage": "data load [-hbn] -f " }, "data ltrim": { "command": "data ltrim", @@ -553,6 +557,23 @@ ], "usage": "data norm [-h]" }, + "data num": { + "command": "data num", + "description": "Function takes a decimal or hexdecimal number and print it in decimal/hex/binary Will print message if number is a prime number", + "notes": [ + "data num --dec 2023", + "data num --hex 0x1000" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--dec decimal value", + "--hex hexadecimal value", + "--bin binary value", + "-i print inverted value" + ], + "usage": "data num [-hi] [--dec ] [--hex ] [--bin ]" + }, "data plot": { "command": "data plot", "description": "Show graph window hit 'h' in window for detail keystroke help available", @@ -631,7 +652,7 @@ "options": [ "-h, --help This help", "-n num of samples (512 - 40000)", - "-v, --verbose verbose" + "-v, --verbose verbose output" ], "usage": "data samples [-hv] [-n ]" }, @@ -759,7 +780,7 @@ "options": [ "-h, --help This help", "-k, -K, --keep Keep field ON for next command", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], "usage": "emv challenge [-hkaw]" @@ -775,7 +796,7 @@ "options": [ "-h, --help This help", "-s, -S, --select Activate field and select card", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-t, -T, --tlv TLV decode results", "-j, -J, --jload Load transaction parameters from `emv_defparams.json` file", "-f, -F, --forceaid Force search AID. Search AID instead of execute PPSE", @@ -805,7 +826,7 @@ "-d, -D, --decision Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested", "-p, -P, --params Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters", "-m, -M, --make Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-t, -T, --tlv TLV decode results of selected applets", "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " CDOLdata/CDOL" @@ -826,7 +847,7 @@ "-k, -K, --keep Keep field ON for next command", "-p, -P, --params Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters", "-m, -M, --make Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-t, -T, --tlv TLV decode results of selected applets", "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " PDOLdata/PDOL" @@ -855,7 +876,7 @@ "-k, -K, --keep Keep field ON for next command", "-p, -P, --params Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters", "-m, -M, --make Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-t, -T, --tlv TLV decode results of selected applets", "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " DDOLdata/DDOL" @@ -866,22 +887,22 @@ "command": "emv list", "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "emv list -f -> show frame delay times", + "emv list --frame -> show frame delay times", "emv list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "emv list [-h1fcrux] [--dict ]" + "usage": "emv list [-h1crux] [--frame] [-f ]" }, "emv pse": { "command": "emv pse", @@ -897,7 +918,7 @@ "-k, -K, --keep Keep field ON for next command", "-1, --pse PSE (1PAY.SYS.DDF01) mode", "-2, --ppse PPSE (2PAY.SYS.DDF01) mode (def)", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-t, -T, --tlv TLV decode results of selected applets", "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], @@ -914,7 +935,7 @@ "options": [ "-h, --help This help", "-k, -K, --keep Keep field ON for next command", - "-a, -A, --apdu Show APDU reqests and responses", + "-a, -A, --apdu Show APDU requests and responses", "-t, -T, --tlv TLV decode results of selected applets", "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } nfc { NFC commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program", + "description": "help Use ` help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } nfc { NFC commands... } piv { PIV commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program", "notes": [], "offline": true, "options": [], @@ -1072,43 +1093,55 @@ ], "usage": "hf 14a apdu [-hsktde] [-m ] [-l ] []..." }, + "hf 14a apdufind": { + "command": "hf 14a apdufind", + "description": "Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1/P2 commands. It loops all 256 possible values for each byte. The loop oder is INS -> P1/P2 (alternating) -> CLA. Tag must be on antenna before running.", + "notes": [ + "hf 14a apdufind", + "hf 14a apdufind --cla 80", + "hf 14a apdufind --cla 80 --error-limit 20 --skip-ins a4 --skip-ins b0 --with-le" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-c, --cla Start value of CLASS (1 hex byte)", + "-i, --ins Start value of INSTRUCTION (1 hex byte)", + "--p1 Start value of P1 (1 hex byte)", + "--p2 Start value of P2 (1 hex byte)", + "-r, --reset Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes", + "-e, --error-limit Maximum times an status word other than 0x9000 or 0x6D00 is shown. Default is 512.", + "-s, --skip-ins Do not test an instruction (can be specified multiple times)", + "-l, --with-le Search for APDUs with Le=0 (case 2S) as well", + "-v, --verbose Verbose output" + ], + "usage": "hf 14a apdufind [-hlv] [-c ] [-i ] [--p1 ] [--p2 ] [-r ] [-e ] [-s ]..." + }, "hf 14a chaining": { "command": "hf 14a chaining", "description": "Enable/Disable ISO14443a input chaining. Maximum input length goes from ATS.", "notes": [ - "hf 14a chaining disable -> disable chaining", - "hf 14a chaining -> show chaining enable/disable state" + "hf 14a chaining -> show chaining enable/disable state", + "hf 14a chaining --off -> disable chaining" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-1, --on enabled chaining", + "-0, --off disable chaining" ], - "usage": "hf 14a chaining [-h] []" + "usage": "hf 14a chaining [-h10]" }, "hf 14a config": { "command": "hf 14a config", - "description": "--------------------------------------------------------------------------------------- hf 14a apdufind available offline: no", + "description": "--------------------------------------------------------------------------------------- hf 14a cuids available offline: no", "notes": [], "offline": false, "options": [], "usage": "" }, - "hf 14a cuids": { - "command": "hf 14a cuids", - "description": "Collect n>0 ISO14443-a UIDs in one go", - "notes": [ - "hf 14a cuids -n 5 -> Collect 5 UIDs" - ], - "offline": false, - "options": [ - "-h, --help This help", - "-n, --num Number of UIDs to collect" - ], - "usage": "hf 14a cuids [-h] [-n ]" - }, "hf 14a help": { "command": "hf 14a help", - "description": "help This help list List ISO 14443-a history", + "description": "----------- ----------------------- General ----------------------- help This help list List ISO 14443-a history", "notes": [], "offline": true, "options": [], @@ -1133,22 +1166,35 @@ "command": "hf 14a list", "description": "Alias of `trace list -t 14a` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf 14a list -f -> show frame delay times", + "hf 14a list --frame -> show frame delay times", "hf 14a list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf 14a list [-h1fcrux] [--dict ]" + "usage": "hf 14a list [-h1crux] [--frame] [-f ]" + }, + "hf 14a ndefformat": { + "command": "hf 14a ndefformat", + "description": "Format ISO14443-a Tag as a NFC tag with Data Exchange Format (NDEF)", + "notes": [ + "hf 14a ndefformat" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose show technical data" + ], + "usage": "hf 14a ndefformat [-hv]" }, "hf 14a ndefread": { "command": "hf 14a ndefread", @@ -1160,9 +1206,28 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file save raw NDEF to file" + "-f, --file save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf 14a ndefread [-h] [-f ]" + "usage": "hf 14a ndefread [-hv] [-f ]" + }, + "hf 14a ndefwrite": { + "command": "hf 14a ndefwrite", + "description": "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.", + "notes": [ + "hf 14a ndefwrite -d 0300FE -> write empty record to tag", + "hf 14a ndefwrite -f myfilename", + "hf 14a ndefwrite -d 003fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-d raw NDEF hex bytes", + "-f, --file write raw NDEF file to tag", + "-p fix NDEF record headers / terminator block if missing", + "-v, --verbose verbose output" + ], + "usage": "hf 14a ndefwrite [-hpv] [-d ] [-f ]" }, "hf 14a raw": { "command": "hf 14a raw", @@ -1215,7 +1280,7 @@ }, "hf 14a sim": { "command": "hf 14a sim", - "description": "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID", + "description": "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID Use type 7 for Mifare Ultralight EV1, Amiibo (NTAG215 pack 0x8080)", "notes": [ "hf 14a sim -t 1 --uid 11223344 -> MIFARE Classic 1k", "hf 14a sim -t 2 -> MIFARE Ultralight", @@ -1223,7 +1288,7 @@ "hf 14a sim -t 4 -> ISO/IEC 14443-4", "hf 14a sim -t 5 -> MIFARE Tnp3xxx", "hf 14a sim -t 6 -> MIFARE Mini", - "hf 14a sim -t 7 -> AMIIBO (NTAG 215), pack 0x8080", + "hf 14a sim -t 7 -> MFU EV1 / NTAG 215 Amiibo", "hf 14a sim -t 8 -> MIFARE Classic 4k", "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro", "hf 14a sim -t 10 -> ST25TA IKEA Rothult" @@ -1232,7 +1297,7 @@ "options": [ "-h, --help This help", "-t, --type <1-10> Simulation type to use", - "-u, --uid 4, 7 or 10 byte UID", + "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks have been read by reader. 0 = infinite", "-x Performs the 'reader attack', nr/ar attack against a reader", "--sk Fill simulator keys from found keys", @@ -1295,7 +1360,7 @@ }, "hf 14b help": { "command": "hf 14b help", - "description": "help This help list List ISO-14443-B history", + "description": "help This help list List ISO-14443-B history view Display content from tag dump file", "notes": [], "offline": true, "options": [], @@ -1311,7 +1376,7 @@ "options": [ "-h, --help This help", "-s, --aidsearch checks if AIDs from aidlist.json is present on the card and prints information about found AIDs", - "-v, --verbose verbose" + "-v, --verbose verbose output" ], "usage": "hf 14b info [-hsv]" }, @@ -1319,22 +1384,22 @@ "command": "hf 14b list", "description": "Alias of `trace list -t 14b` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf 14b list -f -> show frame delay times", + "hf 14b list --frame -> show frame delay times", "hf 14b list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf 14b list [-h1fcrux] [--dict ]" + "usage": "hf 14b list [-h1crux] [--frame] [-f ]" }, "hf 14b ndefread": { "command": "hf 14b ndefread", @@ -1346,9 +1411,10 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file save raw NDEF to file" + "-f, --file save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf 14b ndefread [-h] [-f ]" + "usage": "hf 14b ndefread [-hv] [-f ]" }, "hf 14b raw": { "command": "hf 14b raw", @@ -1365,13 +1431,14 @@ "-s, --std activate field, use ISO14B select", "--sr activate field, use SRx ST select", "--cts activate field, use ASK C-ticket select", + "--xrx activate field, use Fuji/Xerox select", "-c, --crc calculate and append CRC", "-r do not read response from card", "-t, --timeout timeout in ms", - "-v, --verbose verbose", + "-v, --verbose verbose output", "-d, --data data, bytes to send" ], - "usage": "hf 14b raw [-hkscrv] [--sr] [--cts] [-t ] [-d ]" + "usage": "hf 14b raw [-hkscrv] [--sr] [--cts] [--xrx] [-t ] [-d ]" }, "hf 14b rdbl": { "command": "hf 14b rdbl", @@ -1390,15 +1457,16 @@ "command": "hf 14b reader", "description": "Act as a 14443B reader to identify a tag", "notes": [ - "hf 14b reader" + "hf 14b reader", + "hf 14b reader -@ -> continuous reader mode" ], "offline": false, "options": [ "-h, --help This help", - "-s, --silent silent (no messages)", + "-v, --verbose verbose output", "-@ optional - continuous reader mode" ], - "usage": "hf 14b reader [-hs@]" + "usage": "hf 14b reader [-hv@]" }, "hf 14b sim": { "command": "hf 14b sim", @@ -1445,6 +1513,20 @@ ], "usage": "hf 14b sriwrite [-h] [-b ] -d [--512] [--4k] [--sb]" }, + "hf 14b view": { + "command": "hf 14b view", + "description": "Print a ISO14443-B dump file (bin/eml/json)", + "notes": [ + "hf 14b view -f hf-14b-01020304-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-f, --file filename of dump", + "-v, --verbose verbose output" + ], + "usage": "hf 14b view [-hv] -f " + }, "hf 15 csetuid": { "command": "hf 15 csetuid", "description": "Set UID for magic Chinese card (only works with such cards)", @@ -1490,6 +1572,49 @@ ], "usage": "hf 15 dump [-h*2o] [-u ] [--ua] [-f ]" }, + "hf 15 eload": { + "command": "hf 15 eload", + "description": "Load memory image from file to be used with 'hf 15 sim'", + "notes": [ + "hf 15 eload -f hf-15-01020304.bin" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file filename of image" + ], + "usage": "hf 15 eload [-h] -f " + }, + "hf 15 esave": { + "command": "hf 15 esave", + "description": "Save emulator memory into three files (BIN/EML/JSON)", + "notes": [ + "hf 15 esave -f hf-15-01020304hf 15 esave -b 8 -c 42 -f hf-15-01020304" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file filename of dump", + "-b, --blocksize block size, defaults to 4", + "-c, --count number of blocks to export, defaults to all" + ], + "usage": "hf 15 esave [-h] -f [-b ] [-c ]" + }, + "hf 15 eview": { + "command": "hf 15 eview", + "description": "It displays emulator memory", + "notes": [ + "hf 15 eview", + "hf 15 eview -b 8 -c 60" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-b, --blocksize block size, defaults to 4", + "-c, --count number of blocks to display, defaults to all" + ], + "usage": "hf 15 eview [-h] [-b ] [-c ]" + }, "hf 15 findafi": { "command": "hf 15 findafi", "description": "This command attempts to brute force AFI of an ISO-15693 tag Estimated execution time is around 2 minutes", @@ -1533,22 +1658,50 @@ "command": "hf 15 list", "description": "Alias of `trace list -t 15` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf 15 list -f -> show frame delay times", + "hf 15 list --frame -> show frame delay times", "hf 15 list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf 15 list [-h1fcrux] [--dict ]" + "usage": "hf 15 list [-h1crux] [--frame] [-f ]" + }, + "hf 15 passprotectafi": { + "command": "hf 15 passprotectafi", + "description": "This command enables the password protect of AFI. *** OBS! This action can not be undone! ***", + "notes": [ + "hf 15 passprotectafi -p 00000000 --force" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd EAS/AFI password, 8 hex bytes", + "--force Force execution of command (irreversible)" + ], + "usage": "hf 15 passprotectafi [-h] -p [--force]" + }, + "hf 15 passprotecteas": { + "command": "hf 15 passprotecteas", + "description": "This command enables the password protect of EAS. *** OBS! This action can not be undone! ***", + "notes": [ + "hf 15 passprotecteas -p 00000000 --force" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd EAS/AFI password, 8 hex bytes", + "--force Force execution of command (irreversible)" + ], + "usage": "hf 15 passprotecteas [-h] -p [--force]" }, "hf 15 raw": { "command": "hf 15 raw", @@ -1665,12 +1818,39 @@ "offline": false, "options": [ "-h, --help This help", - "-u, --uid <8b hex> UID eg E011223344556677" + "-u, --uid <8b hex> UID eg E011223344556677", + "-b, --blocksize block size, defaults to 4" ], - "usage": "hf 15 sim [-h] -u <8b hex>" + "usage": "hf 15 sim [-h] -u <8b hex> [-b ]" }, - "hf 15 slixdisable": { - "command": "hf 15 slixdisable", + "hf 15 slixeasdisable": { + "command": "hf 15 slixeasdisable", + "description": "Disable EAS mode on SLIX ISO-15693 tag", + "notes": [ + "hf 15 slixeasdisable -p 0F0F0F0F" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd optional password, 8 hex bytes" + ], + "usage": "hf 15 slixeasdisable [-h] [-p ]" + }, + "hf 15 slixeasenable": { + "command": "hf 15 slixeasenable", + "description": "Enable EAS mode on SLIX ISO-15693 tag", + "notes": [ + "hf 15 slixeasenable -p 0F0F0F0F" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd optional password, 8 hex bytes" + ], + "usage": "hf 15 slixeasenable [-h] [-p ]" + }, + "hf 15 slixprivacydisable": { + "command": "hf 15 slixprivacydisable", "description": "Disable privacy mode on SLIX ISO-15693 tag", "notes": [ "hf 15 slixdisable -p 0F0F0F0F" @@ -1680,7 +1860,35 @@ "-h, --help This help", "-p, --pwd password, 8 hex bytes" ], - "usage": "hf 15 slixdisable [-h] -p " + "usage": "hf 15 slixprivacydisable [-h] -p " + }, + "hf 15 slixprivacyenable": { + "command": "hf 15 slixprivacyenable", + "description": "Enable privacy mode on SLIX ISO-15693 tag", + "notes": [ + "hf 15 slixenable -p 0F0F0F0F" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd password, 8 hex bytes" + ], + "usage": "hf 15 slixprivacyenable [-h] -p " + }, + "hf 15 slixwritepwd": { + "command": "hf 15 slixwritepwd", + "description": "Write a password on a SLIX family ISO-15693 tag.nSome tags do not support all different password types.", + "notes": [ + "hf 15 slixwritepwd -t READ -o 00000000 -n 12131415" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-t, --type which password field to write to", + "-o, --old old password (if present), 8 hex bytes", + "-n, --new new password, 8 hex bytes" + ], + "usage": "hf 15 slixwritepwd [-h] -t [-o ] -n " }, "hf 15 sniff": { "command": "hf 15 sniff", @@ -1720,19 +1928,16 @@ "description": "Write AFI on card", "notes": [ "hf 15 writeafi -* --afi 12", - "hf 15 writeafi -u E011223344556677 --afi 12" + "hf 15 writeafi -u E011223344556677 --afi 12 -p 0F0F0F0F" ], "offline": false, "options": [ "-h, --help This help", "-u, --uid full UID, 8 bytes", - "--ua unaddressed mode", - "-* scan for tag", - "-2 use slower '1 out of 256' mode", - "-o, --opt set OPTION Flag (needed for TI)", - "--afi AFI number (0-255)" + "--afi AFI number (0-255)", + "-p, --pwd optional AFI/EAS password" ], - "usage": "hf 15 writeafi [-h*2o] [-u ] [--ua] --afi " + "usage": "hf 15 writeafi [-h] [-u ] --afi [-p ]" }, "hf 15 writedsfid": { "command": "hf 15 writedsfid", @@ -2084,7 +2289,9 @@ "command": "hf emrtd dump", "description": "Dump all files on an eMRTD", "notes": [ - "hf emrtd dump" + "hf emrtd dump", + "hf emrtd dump --dir ../dump", + "hf emrtd dump -n 123456789 -d 19890101 -e 20250401" ], "offline": false, "options": [ @@ -2093,9 +2300,9 @@ "-d, --dateofbirth date of birth in YYMMDD format", "-e, --expiry expiry in YYMMDD format", "-m, --mrz <[0-9A-Z<]> 2nd line of MRZ, 44 chars", - "--path save dump to the given dirpath" + "--dir save dump to the given dirpath" ], - "usage": "hf emrtd dump [-h] [-n ] [-d ] [-e ] [-m <[0-9A-Z<]>] [--path ]" + "usage": "hf emrtd dump [-h] [-n ] [-d ] [-e ] [-m <[0-9A-Z<]>] [--dir ]" }, "hf emrtd help": { "command": "hf emrtd help", @@ -2109,7 +2316,10 @@ "command": "hf emrtd info", "description": "Display info about an eMRTD", "notes": [ - "hf emrtd info" + "hf emrtd info", + "hf emrtd info --dir ../dumps", + "hf emrtd info -n 123456789 -d 19890101 -e 20250401", + "hf emrtd info -n 123456789 -d 19890101 -e 20250401 -i" ], "offline": true, "options": [ @@ -2118,30 +2328,31 @@ "-d, --dateofbirth date of birth in YYMMDD format", "-e, --expiry expiry in YYMMDD format", "-m, --mrz <[0-9A-Z<]> 2nd line of MRZ, 44 chars (passports only)", - "--path display info from offline dump stored in dirpath" + "--dir display info from offline dump stored in dirpath", + "-i, --images show images" ], - "usage": "hf emrtd info [-h] [-n ] [-d ] [-e ] [-m <[0-9A-Z<]>] [--path ]" + "usage": "hf emrtd info [-hi] [-n ] [-d ] [-e ] [-m <[0-9A-Z<]>] [--dir ]" }, "hf emrtd list": { "command": "hf emrtd list", "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf emrtd list -f -> show frame delay times", + "hf emrtd list --frame -> show frame delay times", "hf emrtd list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf emrtd list [-h1fcrux] [--dict ]" + "usage": "hf emrtd list [-h1crux] [--frame] [-f ]" }, "hf epa cnonces": { "command": "hf epa cnonces", @@ -2166,11 +2377,11 @@ "options": [], "usage": "" }, - "hf epa preplay": { - "command": "hf epa preplay", + "hf epa replay": { + "command": "hf epa replay", "description": "Perform PACE protocol by replaying given APDUs", "notes": [ - "hf epa preplay --mse 0022C1A4 --get 1068000000 --map 1086000002 --pka 1234ABCDEF --ma 1A2B3C4D" + "hf epa replay --mse 0022C1A4 --get 1068000000 --map 1086000002 --pka 1234ABCDEF --ma 1A2B3C4D" ], "offline": false, "options": [ @@ -2181,7 +2392,23 @@ "--pka pka APDU", "--ma ma APDU" ], - "usage": "hf epa preplay [-h] --mse --get --map --pka --ma " + "usage": "hf epa replay [-h] --mse --get --map --pka --ma " + }, + "hf epa sim": { + "command": "hf epa sim", + "description": "Simulate PACE protocol with given password pwd of type pty. The crypto is performed on pc or proxmark", + "notes": [ + "hf epa sim --pwd 112233445566", + "hf epa sim --pc --pty 1 --pwd 112233445566" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--pc perform crypto on PC", + "--pty type of password", + "-p, --pwd password" + ], + "usage": "hf epa sim [-h] --pc --pty -p " }, "hf felica auth1": { "command": "hf felica auth1", @@ -2200,7 +2427,7 @@ "--sn number of service, 1 byte", "--scl service code list, 2 bytes", "-k, --key 3des key, 16 bytes", - "-v, --verbose verbose helptext" + "-v, --verbose verbose output" ], "usage": "hf felica auth1 [-hv] [--an ] [--acl ] [-i ] [--sn ] [--scl ] [-k ]" }, @@ -2217,7 +2444,7 @@ "-i set custom IDm", "-c, --cc M3c card challenge, 8 bytes", "-k, --key 3des M3c decryption key, 16 bytes", - "-v, --verbose verbose helptext" + "-v, --verbose verbose output" ], "usage": "hf felica auth2 [-hv] [-i ] [-c ] [-k ]" }, @@ -2245,22 +2472,22 @@ "command": "hf felica list", "description": "Alias of `trace list -t felica` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf felica list -f -> show frame delay times", + "hf felica list --frame -> show frame delay times", "hf felica list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf felica list [-h1fcrux] [--dict ]" + "usage": "hf felica list [-h1crux] [--frame] [-f ]" }, "hf felica litedump": { "command": "hf felica litedump", @@ -2342,7 +2569,7 @@ "-h, --help This help", "-i set custom IDm", "-r set custom reserve", - "-v, --verbose verbose helptext" + "-v, --verbose verbose output" ], "usage": "hf felica resetmode [-hv] [-i ] [-r ]" }, @@ -2390,7 +2617,7 @@ "-h, --help This help", "-i set custom IDm", "-r set custom reserve", - "-v, --verbose verbose helptext" + "-v, --verbose verbose output" ], "usage": "hf felica rqspecver [-hv] [-i ] [-r ]" }, @@ -2453,7 +2680,7 @@ "offline": false, "options": [ "-h, --help This help", - "-a, --apdu Show APDU reqests and responses", + "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose mode. vv - show full certificates data", "-c, --cbor Show CBOR decoded data", "-l, --list Add CredentialId from json to allowList", @@ -2474,7 +2701,7 @@ "offline": false, "options": [ "-h, --help This help", - "-a, --apdu Show APDU reqests and responses", + "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose mode", "default mode: dont-enforce-user-presence-and-sign", "-u, --user mode: enforce-user-presence-and-sign", @@ -2513,22 +2740,22 @@ "command": "hf fido list", "description": "Alias of `trace list -t 14a` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf fido list -f -> show frame delay times", + "hf fido list --frame -> show frame delay times", "hf fido list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf fido list [-h1fcrux] [--dict ]" + "usage": "hf fido list [-h1crux] [--frame] [-f ]" }, "hf fido make": { "command": "hf fido make", @@ -2540,7 +2767,7 @@ "offline": false, "options": [ "-h, --help This help", - "-a, --apdu Show APDU reqests and responses", + "-a, --apdu Show APDU requests and responses", "-v, --verbose Verbose mode. vv - show full certificates data", "-t, --tlv Show DER certificate contents in TLV representation", "-c, --cbor Show CBOR decoded data", @@ -2571,6 +2798,86 @@ ], "usage": "hf fido reg [-havt] [-f ] [--cp ] [--ap ] [--cpx ] [--apx ]" }, + "hf fudan dump": { + "command": "hf fudan dump", + "description": "Dump FUDAN tag to binary file If no given, UID will be used as filename", + "notes": [ + "hf fudan dump -f mydump -> dump using filename" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file filename of dump" + ], + "usage": "hf fudan dump [-h] [-f ]" + }, + "hf fudan help": { + "command": "hf fudan help", + "description": "help This help view Display content from tag dump file", + "notes": [], + "offline": true, + "options": [], + "usage": "" + }, + "hf fudan rdbl": { + "command": "hf fudan rdbl", + "description": "Read fudan block", + "notes": [ + "hf fudan rdbl --blk 0 -k FFFFFFFFFFFF", + "hf fudan rdbl --blk 3 -v" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--blk block number", + "-k, --key key, 6 hex bytes", + "-v, --verbose verbose output" + ], + "usage": "hf fudan rdbl [-hv] --blk [-k ]" + }, + "hf fudan reader": { + "command": "hf fudan reader", + "description": "Read a fudan tag", + "notes": [ + "hf fudan reader", + "hf fudan reader -@ -> continuous reader mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "-@ optional - continuous reader mode" + ], + "usage": "hf fudan reader [-hv@]" + }, + "hf fudan view": { + "command": "hf fudan view", + "description": "Print a FUDAN dump file (bin/eml/json)", + "notes": [ + "hf fudan view -f hf-fudan-01020304-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-f, --file filename of dump" + ], + "usage": "hf fudan view [-h] -f " + }, + "hf fudan wrbl": { + "command": "hf fudan wrbl", + "description": "Write fudan block with 4 hex bytes of data", + "notes": [ + "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--blk block number", + "-k, --key key, 6 hex bytes", + "-d, --data bytes to write, 4 hex bytes" + ], + "usage": "hf fudan wrbl [-h] --blk [-k ] [-d ]" + }, "hf gallagher clone": { "command": "hf gallagher clone", "description": "Clone Gallagher credentials to a writable DESFire card Specify site key is required if using non-default key Key, lengths for the different crypto: DES 8 bytes 2TDEA or AES 16 bytes 3TDEA 24 bytes AID, default finds lowest available in range 0x??81F4, where ?? >= 0x20.", @@ -2597,6 +2904,19 @@ ], "usage": "hf gallagher clone [-hv] [-n ] [-t ] [-k ] --rc --fc --cn --il [--aid ] [--sitekey ] [--cadkey ] [--nocadupdate] [--noappcreate] [--apdu]" }, + "hf gallagher decode": { + "command": "hf gallagher decode", + "description": "Decode Gallagher credential block Credential block can be specified with or without the bitwise inverse.", + "notes": [ + "hf gallagher decode --data A3B4B0C151B0A31B" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--data Credential block (8 or 16 bytes)" + ], + "usage": "hf gallagher decode [-h] --data " + }, "hf gallagher delete": { "command": "hf gallagher delete", "description": "Delete Gallagher application from a DESFire card Specify site key is required if using non-default key", @@ -2635,7 +2955,7 @@ }, "hf gallagher help": { "command": "hf gallagher help", - "description": "help This help diversifykey Diversify Gallagher key", + "description": "help This help diversifykey Diversify Gallagher key decode Decode Gallagher credential block", "notes": [], "offline": true, "options": [], @@ -2661,7 +2981,7 @@ }, "hf help": { "command": "hf help", - "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } gallagher { Gallagher DESFire RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } jooki { Jooki RFIDs... } iclass { ICLASS RFIDs... } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } waveshare { Waveshare NFC ePaper... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags", + "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } fudan { Fudan RFIDs... } gallagher { Gallagher DESFire RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } jooki { Jooki RFIDs... } iclass { ICLASS RFIDs... } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } tesla { TESLA Cards... } texkom { Texkom RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } xerox { Fuji/Xerox cartridge RFIDs... } waveshare { Waveshare NFC ePaper... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags", "notes": [], "offline": true, "options": [], @@ -2701,9 +3021,10 @@ "-f, --file Dictionary file with default iclass keys", "--credit key is assumed to be the credit key", "--elite elite computations applied to key", - "--raw no computations applied to key (raw)" + "--raw no computations applied to key (raw)", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass chk [-h] -f [--credit] [--elite] [--raw]" + "usage": "hf iclass chk [-h] -f [--credit] [--elite] [--raw] [--shallow]" }, "hf iclass configcard": { "command": "hf iclass configcard", @@ -2753,27 +3074,31 @@ "--ci credit key index to select key from memory 'hf iclass managekeys'", "--elite elite computations applied to key", "--raw raw, the key is interpreted as raw block 3/4", - "--nr replay of NR/MAC" + "--nr replay of NR/MAC", + "-z, --dense dense dump output style", + "--force force unsecure card read", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass dump [-h] [-f ] [-k ] [--ki ] [--credit ] [--ci ] [--elite] [--raw] [--nr]" + "usage": "hf iclass dump [-hz] [-f ] [-k ] [--ki ] [--credit ] [--ci ] [--elite] [--raw] [--nr] [--force] [--shallow]" }, "hf iclass eload": { "command": "hf iclass eload", "description": "Load emulator memory with data from (bin/eml/json) iCLASS dump file", "notes": [ - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin", - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml" + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml", + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -m" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)" + "-f, --file filename of dump (bin/eml/json)", + "-m, --mem use RDV4 spiffs" ], - "usage": "hf iclass eload [-h] -f " + "usage": "hf iclass eload [-hm] -f " }, "hf iclass encode": { "command": "hf iclass encode", - "description": "Encode binary wiegand to block 7 Use either --bin or --wiegand/--fc/--cn", + "description": "Encode binary wiegand to block 7,8,9 Use either --bin or --wiegand/--fc/--cn", "notes": [ "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337", "hf iclass encode --fc 31 --cn 337 --ki 0 -> FC 31 CN 337", @@ -2790,9 +3115,11 @@ "--enckey 3DES transport key, 16 hex bytes", "--fc facility code", "--cn card number", - "-w, --wiegand see `wiegand list` for available formats" + "-w, --wiegand see `wiegand list` for available formats", + "--shallow use shallow (ASK) reader modulation instead of OOK", + "-v verbose (print encoded blocks)" ], - "usage": "hf iclass encode [-h] [--bin ] --ki [--credit] [--elite] [--raw] [--enckey ] [--fc ] [--cn ] [-w ]" + "usage": "hf iclass encode [-hv] [--bin ] --ki [--credit] [--elite] [--raw] [--enckey ] [--fc ] [--cn ] [-w ] [--shallow]" }, "hf iclass encrypt": { "command": "hf iclass encrypt", @@ -2838,9 +3165,10 @@ "options": [ "-h, --help This help", "-s, --size <256|2048> number of bytes to save (default 256)", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "-z, --dense dense dump output style" ], - "usage": "hf iclass eview [-hv] [-s <256|2048>]" + "usage": "hf iclass eview [-hvz] [-s <256|2048>]" }, "hf iclass help": { "command": "hf iclass help", @@ -2858,30 +3186,31 @@ ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass info [-h]" + "usage": "hf iclass info [-h] [--shallow]" }, "hf iclass list": { "command": "hf iclass list", "description": "Alias of `trace list -t iclass` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf iclass list -f -> show frame delay times", + "hf iclass list --frame -> show frame delay times", "hf iclass list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf iclass list [-h1fcrux] [--dict ]" + "usage": "hf iclass list [-h1crux] [--frame] [-f ]" }, "hf iclass loclass": { "command": "hf iclass loclass", @@ -2972,9 +3301,10 @@ "--elite elite computations applied to key", "--raw no computations applied to key", "--nr replay of NR/MAC", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass rdbl [-hv] [-k ] [--ki ] -b [--credit] [--elite] [--raw] [--nr]" + "usage": "hf iclass rdbl [-hv] [-k ] [--ki ] -b [--credit] [--elite] [--raw] [--nr] [--shallow]" }, "hf iclass reader": { "command": "hf iclass reader", @@ -2985,9 +3315,10 @@ "offline": false, "options": [ "-h, --help This help", - "-@ optional - continuous reader mode" + "-@ optional - continuous reader mode", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass reader [-h@]" + "usage": "hf iclass reader [-h@] [--shallow]" }, "hf iclass restore": { "command": "hf iclass restore", @@ -3008,9 +3339,10 @@ "--credit key is assumed to be the credit key", "--elite elite computations applied to key", "--raw no computations applied to key", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw]" + "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow]" }, "hf iclass sim": { "command": "hf iclass sim", @@ -3057,9 +3389,10 @@ "-f, --file filename of dump (bin/eml/json)", "--first Begin printing from this block (default block 6)", "--last End printing at this block (default 0, ALL)", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "-z, --dense dense dump output style" ], - "usage": "hf iclass view [-hv] -f [--first ] [--last ]" + "usage": "hf iclass view [-hvz] -f [--first ] [--last ]" }, "hf iclass wrbl": { "command": "hf iclass wrbl", @@ -3076,13 +3409,15 @@ "--ki Key index to select key from memory 'hf iclass managekeys'", "-b, --block The block number to read", "-d, --data data to write as 8 hex bytes", + "-m, --mac replay mac data (4 hex bytes)", "--credit key is assumed to be the credit key", "--elite elite computations applied to key", "--raw no computations applied to key", "--nr replay of NR/MAC", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass wrbl [-hv] [-k ] [--ki ] -b -d [--credit] [--elite] [--raw] [--nr]" + "usage": "hf iclass wrbl [-hv] [-k ] [--ki ] -b -d [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow]" }, "hf jooki clone": { "command": "hf jooki clone", @@ -3180,7 +3515,7 @@ "options": [ "-h, --help This help", "-k, --keep keep field ON for next command", - "-a, --apdu show APDU reqests and responses" + "-a, --apdu Show APDU requests and responses" ], "usage": "hf ksx6924 balance [-hka]" }, @@ -3202,23 +3537,23 @@ "options": [ "-h, --help This help", "-k, --keep keep field ON for next command", - "-a, --apdu show APDU reqests and responses" + "-a, --apdu Show APDU requests and responses" ], "usage": "hf ksx6924 info [-hka]" }, - "hf ksx6924 initialize": { - "command": "hf ksx6924 initialize", - "description": "Perform transaction initialization (mpda)", + "hf ksx6924 init": { + "command": "hf ksx6924 init", + "description": "Perform transaction initialization with Mpda (Money of Purchase Transaction)", "notes": [ - "hf ksx6924 initialize 000003e8 -> mpda" + "hf ksx6924 init 000003e8 -> Mpda" ], "offline": false, "options": [ "-h, --help This help", "-k, --keep keep field ON for next command", - "-a, --apdu show APDU reqests and responses" + "-a, --apdu Show APDU requests and responses" ], - "usage": "hf ksx6924 initialize [-hka] " + "usage": "hf ksx6924 init [-hka] " }, "hf ksx6924 prec": { "command": "hf ksx6924 prec", @@ -3230,7 +3565,7 @@ "options": [ "-h, --help This help", "-k, --keep keep field ON for next command", - "-a, --apdu show APDU reqests and responses" + "-a, --apdu Show APDU requests and responses" ], "usage": "hf ksx6924 prec [-hka] " }, @@ -3243,7 +3578,7 @@ "offline": false, "options": [ "-h, --help This help", - "-a, --apdu show APDU reqests and responses" + "-a, --apdu Show APDU requests and responses" ], "usage": "hf ksx6924 select [-ha]" }, @@ -3279,6 +3614,18 @@ ], "usage": "hf legic dump [-h] [-f ] [--de]" }, + "hf legic einfo": { + "command": "hf legic einfo", + "description": "It decodes and displays emulator memory", + "notes": [ + "hf legic einfo" + ], + "offline": false, + "options": [ + "-h, --help This help" + ], + "usage": "hf legic einfo [-h]" + }, "hf legic eload": { "command": "hf legic eload", "description": "Loads a LEGIC Prime dump file into emulator memory", @@ -3331,7 +3678,7 @@ }, "hf legic help": { "command": "hf legic help", - "description": "----------- --------------------- operations --------------------- help This help list List LEGIC history ----------- --------------------- simulation --------------------- ----------- --------------------- utils --------------------- crc Calculate Legic CRC over given bytes view Display content from tag dump file", + "description": "----------- --------------------- operations --------------------- help This help list List LEGIC history ----------- --------------------- simulation --------------------- ----------- --------------------- utils --------------------- crc Calculate Legic CRC over given bytes view Display deobfuscated and decoded content from tag dump file", "notes": [], "offline": true, "options": [], @@ -3353,22 +3700,22 @@ "command": "hf legic list", "description": "Alias of `trace list -t legic` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf legic list -f -> show frame delay times", + "hf legic list --frame -> show frame delay times", "hf legic list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf legic list [-h1fcrux] [--dict ]" + "usage": "hf legic list [-h1crux] [--frame] [-f ]" }, "hf legic rdbl": { "command": "hf legic rdbl", @@ -3475,22 +3822,22 @@ "command": "hf list", "description": "Alias of `trace list -t raw` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf list -f -> show frame delay times", + "hf list --frame -> show frame delay times", "hf list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf list [-h1fcrux] [--dict ]" + "usage": "hf list [-h1crux] [--frame] [-f ]" }, "hf lto dump": { "command": "hf lto dump", @@ -3529,22 +3876,22 @@ "command": "hf lto list", "description": "Alias of `trace list -t lto` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf lto list -f -> show frame delay times", + "hf lto list --frame -> show frame delay times", "hf lto list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf lto list [-h1fcrux] [--dict ]" + "usage": "hf lto list [-h1crux] [--frame] [-f ]" }, "hf lto rdbl": { "command": "hf lto rdbl", @@ -3560,6 +3907,19 @@ ], "usage": "hf lto rdbl [-h] [--first ] [--last ]" }, + "hf lto reader": { + "command": "hf lto reader", + "description": "Act as a LTO-CM reader. Look for LTO-CM tags until Enter or the pm3 button is pressed", + "notes": [ + "hf lto reader -@ -> continuous reader mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-@ optional - continuous reader mode" + ], + "usage": "hf lto reader [-h@]" + }, "hf lto restore": { "command": "hf lto restore", "description": "Restore data from dumpfile to LTO tag", @@ -3631,7 +3991,7 @@ "-a Input key A (def)", "-b Input key B", "-f, --file filename of dictionary", - "-s, --slow Slower acquisition (required by some non standard cards)", + "--slow Slower acquisition (required by some non standard cards)", "-l, --legacy legacy mode (use the slow `hf mf chk`)", "-v, --verbose verbose output (statistics)", "--mini MIFARE Classic Mini / S20", @@ -3645,7 +4005,7 @@ "--i2 AVX2", "--i5 AVX512" ], - "usage": "hf mf autopwn [-habslv] [-k ] [-s ] [-f ] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" + "usage": "hf mf autopwn [-hablv] [-k ] [-s ] [-f ] [--slow] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" }, "hf mf cgetblk": { "command": "hf mf cgetblk", @@ -3736,7 +4096,7 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "--emu from emulator memory" + "--emu to emulator memory" ], "usage": "hf mf csave [-h] [-f ] [--mini] [--1k] [--2k] [--4k] [--emu]" }, @@ -3864,7 +4224,7 @@ }, "hf mf ecfill": { "command": "hf mf ecfill", - "description": "Dump card and transfer the data to emulator memory. Keys must be laid in the emulator memory", + "description": "Dump card and transfer the data to emulator memory. Keys must be in the emulator memory", "notes": [ "hf mf ecfill -> use key type A", "hf mf ecfill --4k -b -> target 4K card with key type B" @@ -3956,9 +4316,10 @@ "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", "--ul MIFARE Ultralight family", + "-m, --mem use RDV4 spiffs", "-q, --qty manually set number of blocks (overrides)" ], - "usage": "hf mf eload [-h] -f [--mini] [--1k] [--2k] [--4k] [--ul] [-q ]" + "usage": "hf mf eload [-hm] -f [--mini] [--1k] [--2k] [--4k] [--ul] [-q ]" }, "hf mf esave": { "command": "hf mf esave", @@ -4039,6 +4400,37 @@ ], "usage": "hf mf fchk [-h] [-k ]... [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [--mem] [-f ]" }, + "hf mf gdmconfig": { + "command": "hf mf gdmconfig", + "description": "Get configuration data from magic gen4 GDM card.", + "notes": [ + "hf mf gdmconfig" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-k, --key key 6 bytes" + ], + "usage": "hf mf gdmconfig [-h] [-k ]" + }, + "hf mf gdmsetblk": { + "command": "hf mf gdmsetblk", + "description": "Set block data on a magic gen4 GDM card `--force` param is used to override warnings like bad ACL writes. if not specified, it will exit if detected", + "notes": [ + "hf mf gdmsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--blk block number", + "-a input key type is key A (def)", + "-b input key type is key B", + "-d, --data bytes to write, 16 hex bytes", + "-k, --key key, 6 hex bytes", + "--force override warnings" + ], + "usage": "hf mf gdmsetblk [-hab] --blk [-d ] [-k ] [--force]" + }, "hf mf gen3blk": { "command": "hf mf gen3blk", "description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only", @@ -4069,6 +4461,85 @@ ], "usage": "hf mf gen3uid [-h] [-u ]" }, + "hf mf ggetblk": { + "command": "hf mf ggetblk", + "description": "Get block data from magic gen4 GTU card.", + "notes": [ + "hf mf ggetblk --blk 0 -> get block 0 (manufacturer)", + "hf mf ggetblk --blk 3 -v -> get block 3, decode sector trailer" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-b, --blk block number", + "-v, --verbose verbose output", + "-p, --pwd password 4bytes" + ], + "usage": "hf mf ggetblk [-hv] -b [-p ]" + }, + "hf mf gload": { + "command": "hf mf gload", + "description": "Load magic gen4 gtu card with data from (bin/eml/json) dump file or from emulator memory.", + "notes": [ + "hf mf gload --emu", + "hf mf gload -f hf-mf-01020304.eml", + "hf mf gload -p AABBCCDD --4k -v -f hf-mf-01020304-dump.bin", + "", + "Card must be configured beforehand with `script run hf_mf_ultimatecard`.", + "Blocks are 16 bytes long." + ], + "offline": false, + "options": [ + "-h, --help This help", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70", + "-p, --pwd password 4bytes", + "-v, --verbose verbose output", + "-f, --file filename of dump", + "--emu from emulator memory", + "--start index of block to start writing (default 0)", + "--end index of block to end writing (default last block)" + ], + "usage": "hf mf gload [-hv] [--mini] [--1k] [--2k] [--4k] [-p ] [-f ] [--emu] [--start ] [--end ]" + }, + "hf mf gsave": { + "command": "hf mf gsave", + "description": "Save `magic gen4 gtu` card memory into three files (BIN/EML/JSON)or into emulator memory", + "notes": [ + "hf mf gsave", + "hf mf gsave --4k", + "hf mf gsave -p DEADBEEF -f hf-mf-01020304.json" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70", + "-p, --pwd password 4bytes", + "-f, --file filename of dump", + "--emu to emulator memory" + ], + "usage": "hf mf gsave [-h] [--mini] [--1k] [--2k] [--4k] [-p ] [-f ] [--emu]" + }, + "hf mf gsetblk": { + "command": "hf mf gsetblk", + "description": "Set block data on a magic gen4 GTU card", + "notes": [ + "hf mf gsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-b, --blk block number", + "-d, --data bytes to write, 16 hex bytes", + "-p, --pwd password 4bytes" + ], + "usage": "hf mf gsetblk [-h] -b [-d ] [-p ]" + }, "hf mf gview": { "command": "hf mf gview", "description": "View `magic gen4 gtu` card memory", @@ -4090,8 +4561,9 @@ }, "hf mf hardnested": { "command": "hf mf hardnested", - "description": "Nested attack for hardened MIFARE Classic cards. `--i` set type of SIMD instructions. Without this flag programs autodetect it. or hf mf hardnested -r --tk [known target key] Add the known target key to check if it is present in the remaining key space hf mf hardnested --blk 0 -a -k A0A1A2A3A4A5 --tblk 4 --ta --tk FFFFFFFFFFFF", + "description": "Nested attack for hardened MIFARE Classic cards. if card is EV1, command can detect and use known key see example below `--i` set type of SIMD instructions. Without this flag programs autodetect it. or hf mf hardnested -r --tk [known target key] Add the known target key to check if it is present in the remaining key space hf mf hardnested --blk 0 -a -k A0A1A2A3A4A5 --tblk 4 --ta --tk FFFFFFFFFFFF", "notes": [ + "hf mf hardnested --tblk 4 --ta -> works for MFC EV1", "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta", "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -w", "hf mf hardnested --blk 0 -a -k FFFFFFFFFFFF --tblk 4 --ta -f nonces.bin -w -s", @@ -4128,7 +4600,7 @@ }, "hf mf help": { "command": "hf mf help", - "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt [nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace acl Decode and print MIFARE Classic access rights bytes value Value blocks view Display content from tag dump file", + "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt [nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file", "notes": [], "offline": true, "options": [], @@ -4138,22 +4610,22 @@ "command": "hf mf list", "description": "Alias of `trace list -t mf` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf mf list -f -> show frame delay times", + "hf mf list --frame -> show frame delay times", "hf mf list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf mf list [-h1fcrux] [--dict ]" + "usage": "hf mf list [-h1crux] [--frame] [-f ]" }, "hf mf mad": { "command": "hf mf mad", @@ -4163,7 +4635,7 @@ "hf mf mad --aid e103 -k ffffffffffff -b -> shows NDEF data if exists. read card with custom key and key B", "hf mf mad --dch -k ffffffffffff -> decode CardHolder information" ], - "offline": false, + "offline": true, "options": [ "-h, --help This help", "-v, --verbose show technical data", @@ -4189,6 +4661,14 @@ ], "usage": "hf mf nack [-hv]" }, + "hf mf ndefformat": { + "command": "hf mf ndefformat", + "description": "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write.", + "notes": [], + "offline": false, + "options": [], + "usage": "" + }, "hf mf ndefread": { "command": "hf mf ndefread", "description": "Prints NFC Data Exchange Format (NDEF)", @@ -4209,6 +4689,28 @@ ], "usage": "hf mf ndefread [-hvb] [--aid ] [-k ] [-f ]" }, + "hf mf ndefwrite": { + "command": "hf mf ndefwrite", + "description": "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.", + "notes": [ + "hf mf ndefwrite -d 0300FE -> write empty record to tag", + "hf mf ndefwrite -f myfilename", + "hf mf ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-d raw NDEF hex bytes", + "-f, --file write raw NDEF file to tag", + "-p fix NDEF record headers / terminator block if missing", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70", + "-v, --verbose verbose output" + ], + "usage": "hf mf ndefwrite [-hpv] [-d ] [-f ] [--mini] [--1k] [--2k] [--4k]" + }, "hf mf nested": { "command": "hf mf nested", "description": "Execute Nested attack against MIFARE Classic card for key recovery", @@ -4322,7 +4824,7 @@ }, "hf mf sim": { "command": "hf mf sim", - "description": "Simulate MIFARE Classic card", + "description": "Simulate MIFARE Classic family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mf eload` first. The UID from emulator memory will be used if not specified.", "notes": [ "hf mf sim --mini -> MIFARE Mini", "hf mf sim --1k -> MIFARE Classic 1k (default)", @@ -4335,7 +4837,7 @@ "offline": false, "options": [ "-h, --help This help", - "-u, --uid UID 4,7 or 10bytes. If not specified, the UID 4b/7b from emulator memory will be used", + "-u, --uid <4|7|10> hex bytes UID", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50", "--2k MIFARE Classic/Plus 2k", @@ -4380,14 +4882,17 @@ "command": "hf mf supercard", "description": "Extract info from a `super card`", "notes": [ - "hf mf supercard" + "hf mf supercard -> recover key", + "hf mf supercard -r -> reset card", + "hf mf supercard -u 11223344 -> change UID" ], "offline": false, "options": [ "-h, --help This help", - "-r, --reset reset card" + "-r, --reset Reset card", + "-u, --uid New UID (4 hex bytes)" ], - "usage": "hf mf supercard [-hr]" + "usage": "hf mf supercard [-hr] [-u ]" }, "hf mf value": { "command": "hf mf value", @@ -4446,7 +4951,7 @@ }, "hf mf wrbl": { "command": "hf mf wrbl", - "description": "Write MIFARE Classic block with 16 hex bytes of data Sector 0 / Block 0 - Manufacturer block When writing to block 0 you must use a VALID block 0 data (UID, BCC, SAK, ATQA) Writing an invalid block 0 means rendering your Magic GEN2 card undetectable. Look in the magic_cards_notes.md file for help to resolve it.", + "description": "Write MIFARE Classic block with 16 hex bytes of data Sector 0 / Block 0 - Manufacturer block When writing to block 0 you must use a VALID block 0 data (UID, BCC, SAK, ATQA) Writing an invalid block 0 means rendering your Magic GEN2 card undetectable. Look in the magic_cards_notes.md file for help to resolve it. `--force` param is used to override warnings like bad ACL and BLOCK 0 writes. if not specified, it will exit if detected", "notes": [ "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 000102030405060708090a0b0c0d0e0f" ], @@ -4456,7 +4961,7 @@ "--blk block number", "-a input key type is key A (def)", "-b input key type is key B", - "--force enforce block0 writes", + "--force override warnings", "-k, --key key, 6 hex bytes", "-d, --data bytes to write, 16 hex bytes" ], @@ -4515,7 +5020,7 @@ "but for APP keys crypto algorithm is set by createapp command and can't be changed wo application delete", "", "hf mfdes changekey --aid 123456 -> execute with default factory setup. change des key 0 in the app 123456 from 00..00 to 00..00", - "hf mfdes changekey --isoid df01 -t aes -s lrp --newkeyno 01 -> change key 01 via lrp channelhf mfdes changekey -t des --newalgo aes --newkey 11223344556677889900112233445566 --newver a5 -> change card master key to AES one", + "hf mfdes changekey --isoid df01 -t aes --schann lrp --newkeyno 01 -> change key 01 via lrp channelhf mfdes changekey -t des --newalgo aes --newkey 11223344556677889900112233445566 --newver a5 -> change card master key to AES one", "hf mfdes changekey --aid 123456 -t aes --key 00000000000000000000000000000000 --newkey 11223344556677889900112233445566 -> change app master key", "hf mfdes changekey --aid 123456 -t des -n 0 --newkeyno 1 --oldkey 5555555555555555 --newkey 1122334455667788 -> change key 1 with auth from key 0", "hf mfdes changekey --aid 123456 -t 3tdea --newkey 112233445566778899001122334455667788990011223344 -> change 3tdea key 0 from default 00..00 to provided" @@ -4551,7 +5056,7 @@ "hf mfdes chfilesettings --aid 123456 --fid 01 --amode plain --rrights free --wrights free --rwrights free --chrights key0 -> change file settings app=123456, file=01 with defaults from `default` command", "hf mfdes chfilesettings -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 --rawdata 00EEEE -> execute with default factory setup", "hf mfdes chfilesettings --aid 123456 --fid 01 --rawdata 810000021f112f22 -> change file settings with additional rights for keys 1 and 2", - "hf mfdes chfilesettings --isoid df01 --fid 00 --amode plain --rawrights eee0 -s lrp -t aes -> change file settings via lrp channel" + "hf mfdes chfilesettings --isoid df01 --fid 00 --amode plain --rawrights eee0 --schann lrp -t aes -> change file settings via lrp channel" ], "offline": false, "options": [ @@ -4637,7 +5142,7 @@ "description": "Clear record file. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ "hf mfdes clearrecfile --aid 123456 --fid 01 -> clear record file for: app=123456, file=01 with defaults from `default` command", - "hf mfdes clearrecfile --isoid df01 --fid 01 -s lrp -t aes -n 3 -> clear record file for lrp channel with key number 3" + "hf mfdes clearrecfile --isoid df01 --fid 01 --schann lrp -t aes -n 3 -> clear record file for lrp channel with key number 3" ], "offline": false, "options": [ @@ -4775,8 +5280,8 @@ "hf mfdes createmacfile --aid 123456 --fid 01 --rawrights 0FF0 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file with parameters. Rights from default. Authentication with defaults from `default` command", "hf mfdes createmacfile --aid 123456 --fid 01 --amode plain --rrights free --wrights deny --rwrights free --chrights key0 --mackey 00112233445566778899aabbccddeeff -> create file app=123456, file=01, with key, and mentioned rights with defaults from `default` command", "hf mfdes createmacfile -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> execute with default factory setup. key and keyver == 0x00..00", - "hf mfdes createmacfile --isoid df01 --fid 0f -s lrp -t aes --rawrights 0FF0 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel", - "hf mfdes createmacfile --isoid df01 --fid 0f -s lrp -t aes --rawrights 0F10 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel with CommitReaderID command enable" + "hf mfdes createmacfile --isoid df01 --fid 0f --schann lrp -t aes --rawrights 0FF0 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel", + "hf mfdes createmacfile --isoid df01 --fid 0f --schann lrp -t aes --rawrights 0F10 --mackey 00112233445566778899aabbccddeeff --mackeyver 01 -> create transaction mac file via lrp channel with CommitReaderID command enable" ], "offline": false, "options": [ @@ -4932,7 +5437,7 @@ "description": "Delete file from application. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ "hf mfdes deletefile --aid 123456 --fid 01 -> delete file for: app=123456, file=01 with defaults from `default` command", - "hf mfdes deletefile --isoid df01 --fid 0f -s lrp -t aes -> delete file for lrp channel" + "hf mfdes deletefile --isoid df01 --fid 0f --schann lrp -t aes -> delete file for lrp channel" ], "offline": false, "options": [ @@ -4988,7 +5493,7 @@ "command": "hf mfdes dump", "description": "For each application show fil list and then file content. Key needs to be provided for authentication or flag --no-auth set (depend on cards settings).", "notes": [ - "hf mfdes dump --aid 123456 -> show file dump for: app=123456 with channel defaults from `default` command/nhf mfdes dump --isoid df01 -s lrp -t aes --length 000090 -> lrp default settings with length limit" + "hf mfdes dump --aid 123456 -> show file dump for: app=123456 with channel defaults from `default` command/nhf mfdes dump --isoid df01 --schann lrp -t aes --length 000090 -> lrp default settings with length limit" ], "offline": false, "options": [ @@ -5135,7 +5640,7 @@ "hf mfdes getfileisoids --aid 123456 -> execute with defaults from `default` command", "hf mfdes getfileisoids -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 -> execute with default factory setup", "hf mfdes getfileisoids --isoid df01 -> get iso file ids from Desfire Light with factory card settings", - "hf mfdes getfileisoids --isoid df01 -s lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication" + "hf mfdes getfileisoids --isoid df01 --schann lrp -t aes -> get iso file ids from Desfire Light via lrp channel with default key authentication" ], "offline": false, "options": [ @@ -5243,7 +5748,7 @@ "description": "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Any card's key needs to be provided.", "notes": [ "hf mfdes getuid -> execute with default factory setup", - "hf mfdes getuid --isoid df01 -t aes -s lrp -> for desfire lights default settings" + "hf mfdes getuid --isoid df01 -t aes --schan lrp -> for desfire lights default settings" ], "offline": false, "options": [ @@ -5287,22 +5792,22 @@ "command": "hf mfdes list", "description": "Alias of `trace list -t des` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf mfdes list -f -> show frame delay times", + "hf mfdes list --frame -> show frame delay times", "hf mfdes list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf mfdes list [-h1fcrux] [--dict ]" + "usage": "hf mfdes list [-h1crux] [--frame] [-f ]" }, "hf mfdes lsapp": { "command": "hf mfdes lsapp", @@ -5332,9 +5837,10 @@ }, "hf mfdes lsfiles": { "command": "hf mfdes lsfiles", - "description": "Show file list. Master key needs to be provided or flag --no-auth set (depend on cards settings).", + "description": "This commands List files inside application AID / ISOID. Master key needs to be provided or flag --no-auth set (depend on cards settings).", "notes": [ - "hf mfdes lsfiles --aid 123456 -> show file list for: app=123456 with defaults from `default` commandhf mfdes lsfiles --isoid df01 --no-auth -> show files from desfire light" + "hf mfdes lsfiles --aid 123456 -> AID 123456, list files using `default` command creds", + "hf mfdes lsfiles --isoid df01 --no-auth -> list files for DESFire light" ], "offline": false, "options": [ @@ -5399,8 +5905,8 @@ "hf mfdes read --isoid 0102 --fileisoid 1000 --type data -c iso -> read file via ISO channel: app iso id=0102, iso id=1000, offset=0. Select via ISO commands", "hf mfdes read --isoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000001 -> get one record (number 5) from file 1100 via iso commands", "hf mfdes read --isoid 0102 --fileisoid 1100 --type record -c iso --offset 000005 --length 000000 -> get all record (from 5 to 1) from file 1100 via iso commands", - "hf mfdes read --isoid df01 --fid 00 -s lrp -t aes --length 000010 -> read via lrp channel", - "hf mfdes read --isoid df01 --fid 00 -s ev2 -t aes --length 000010 --isochain -> read Desfire Light via ev2 channel" + "hf mfdes read --isoid df01 --fid 00 --schann lrp -t aes --length 000010 -> read via lrp channel", + "hf mfdes read --isoid df01 --fid 00 --schann ev2 -t aes --length 000010 --isochain -> read Desfire Light via ev2 channel" ], "offline": false, "options": [ @@ -5478,9 +5984,9 @@ "", "hf mfdes setconfig --param 03 --data 0428 -> set SAK", "hf mfdes setconfig --param 02 --data 0875778102637264 -> set ATS (first byte - length)", - "hf mfdes setconfig --isoid df01 -t aes -s ev2 --param 05 --data 00000000020000000000 -> set LRP mode enable for Desfire Light", - "hf mfdes setconfig --isoid df01 -t aes -s ev2 --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light", - "hf mfdes setconfig --isoid df01 -t aes -s lrp --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light via lrp" + "hf mfdes setconfig --isoid df01 -t aes --schann ev2 --param 05 --data 00000000020000000000 -> set LRP mode enable for Desfire Light", + "hf mfdes setconfig --isoid df01 -t aes --schann ev2 --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light", + "hf mfdes setconfig --isoid df01 -t aes --schann lrp --param 0a --data 00ffffffff -> Disable failed auth counters for Desfire Light via lrp" ], "offline": false, "options": [ @@ -5521,8 +6027,8 @@ "hf mfdes value --aid 123456 --fid 01 -> get value app=123456, file=01 with defaults from `default` command", "hf mfdes value --aid 123456 --fid 01 --op credit -d 00000001 -> credit value app=123456, file=01 with defaults from `default` command", "hf mfdes value -n 0 -t des -k 0000000000000000 --kdf none --aid 123456 --fid 01 -> get value with default factory setup", - "hf mfdes val --isoid df01 --fid 03 -s lrp -t aes -n 1 --op credit --d 00000001 -m encrypt -> credit value in the lrp encrypted mode", - "hf mfdes val --isoid df01 --fid 03 -s lrp -t aes -n 1 --op get -m plain -> get value in plain (nevertheless of mode) works for desfire light (look SetConfiguration option 0x09)" + "hf mfdes val --isoid df01 --fid 03 --schann lrp -t aes -n 1 --op credit --d 00000001 -m encrypt -> credit value in the lrp encrypted mode", + "hf mfdes val --isoid df01 --fid 03 --schann lrp -t aes -n 1 --op get -m plain -> get value in plain (nevertheless of mode) works for desfire light (look SetConfiguration option 0x09)" ], "offline": false, "options": [ @@ -5552,7 +6058,7 @@ "notes": [ "In the mode with CommitReaderID to decode previous reader id command needs to read transaction counter via dump/read command and specify --trkey", "", - "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> write file: app=123456, file=01, offset=0, get file type from card. use default channel settings from `default` command", + "hf mfdes write --aid 123456 --fid 01 -d 01020304 -> AID 123456, file=01, offset=0, get file type from card. use default channel settings from `default` command", "hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --0ffset 000100 -> write data to std file with offset 0x100", "hf mfdes write --aid 123456 --fid 01 --type data -d 01020304 --commit -> write data to backup file with commit", "hf mfdes write --aid 123456 --fid 01 --type value -d 00000001 -> increment value file", @@ -5564,7 +6070,7 @@ "hf mfdes write --isoid 1234 --fileisoid 1000 --type data -c iso -d 01020304 -> write data to std/backup file via iso commandset", "hf mfdes write --isoid 1234 --fileisoid 2000 --type record -c iso -d 01020304 -> send record to record file via iso commandset", "hf mfdes write --aid 123456 --fid 01 -d 01020304 --readerid 010203 -> write data to file with CommitReaderID command before write and CommitTransaction after write", - "hf mfdes write --isoid df01 --fid 04 -d 01020304 --trkey 00112233445566778899aabbccddeeff --readerid 5532 -t aes -s lrp -> advanced CommitReaderID via lrp channel sample" + "hf mfdes write --isoid df01 --fid 04 -d 01020304 --trkey 00112233445566778899aabbccddeeff --readerid 5532 -t aes --schann lrp -> advanced CommitReaderID via lrp channel sample" ], "offline": false, "options": [ @@ -5790,7 +6296,7 @@ "offline": false, "options": [ "-h, --help This help", - "-v, --verbose Verbose mode", + "-v, --verbose Verbose output", "--ki Key number, 2 hex bytes", "--key Key, 16 hex bytes" ], @@ -5848,17 +6354,35 @@ ], "usage": "hf mfu eload [-h] -f [-q ]" }, - "hf mfu eview": { - "command": "hf mfu eview", - "description": "It displays emulator memory", + "hf mfu esave": { + "command": "hf mfu esave", + "description": "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/eml/json) By default number of pages saved depends on defined tag type. You can override this with option --end.", "notes": [ - "hf mfu eview" + "hf mfu esave", + "hf mfu esave --end 255 -> saves whole memory", + "hf mfu esave -f hf-mfu-04010203040506-dump.json" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-e, --end index of last block", + "-f, --file filename of dump" ], - "usage": "hf mfu eview [-h]" + "usage": "hf mfu esave [-h] [-e ] [-f ]" + }, + "hf mfu eview": { + "command": "hf mfu eview", + "description": "Displays emulator memory By default number of pages shown depends on defined tag type. You can override this with option --end.", + "notes": [ + "hf mfu eview", + "hf mfu eview --end 255 -> dumps whole memory" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-e, --end index of last block" + ], + "usage": "hf mfu eview [-h] [-e ]" }, "hf mfu help": { "command": "hf mfu help", @@ -5911,9 +6435,10 @@ "options": [ "-h, --help This help", "-l Swap entered key's endianness", - "-f, --file Save raw NDEF to file" + "-f, --file Save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf mfu ndefread [-hl] [-k Replace default key for NDEF] [-f ]" + "usage": "hf mfu ndefread [-hlv] [-k Replace default key for NDEF] [-f ]" }, "hf mfu otptear": { "command": "hf mfu otptear", @@ -6022,10 +6547,11 @@ }, "hf mfu sim": { "command": "hf mfu sim", - "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", "notes": [ "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight", - "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> AMIIBO (NTAG 215), pack 0x8080" + "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo", + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" ], "offline": false, "options": [ @@ -6037,6 +6563,25 @@ ], "usage": "hf mfu sim [-hv] -t <1..10> [-u ] [-n ]" }, + "hf mfu tamper": { + "command": "hf mfu tamper", + "description": "Set the congiguration of the NTAG 213TT tamper feature Supports: NTAG 213TT", + "notes": [ + "hf mfu tamper -e -> enable tamper feature", + "hf mfu tamper -d -> disable tamper feature", + "hf mfu tamper -m 0A0A0A0A -> set the tamper message to 0A0A0A0A", + "hf mfu tamper --lockmessage -> permanently lock the tamper message and mask it from memory" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-e, --enable Enable the tamper feature", + "-d, --disable Disable the tamper feature", + "-m, --message Set the tamper message (4 bytes)", + "--lockmessage Permanently lock the tamper message and mask it from memory (does not lock tamper feature itself)" + ], + "usage": "hf mfu tamper [-hed] [-m ] [--lockmessage]" + }, "hf mfu view": { "command": "hf mfu view", "description": "Print a MIFARE Ultralight/NTAG dump file (bin/eml/json)", @@ -6166,22 +6711,22 @@ "command": "hf seos list", "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf seos list -f -> show frame delay times", + "hf seos list --frame -> show frame delay times", "hf seos list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf seos list [-h1fcrux] [--dict ]" + "usage": "hf seos list [-h1crux] [--frame] [-f ]" }, "hf sniff": { "command": "hf sniff", @@ -6194,9 +6739,11 @@ "options": [ "-h, --help This help", "--sp skip sample pairs", - "--st skip number of triggers" + "--st skip number of triggers", + "--smode [none|drop|min|max|avg] Skip mode. It switches on the function that applies to several samples before they saved to memory", + "--sratio Skip ratio. It applied the function above to (ratio * 2) samples. For ratio = 1 it 2 samples." ], - "usage": "hf sniff [-h] [--sp ] [--st ]" + "usage": "hf sniff [-h] [--sp ] [--st ] [--smode [none|drop|min|max|avg]] [--sratio ]" }, "hf st25ta help": { "command": "hf st25ta help", @@ -6222,22 +6769,22 @@ "command": "hf st25ta list", "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf st25ta list -f -> show frame delay times", + "hf st25ta list --frame -> show frame delay times", "hf st25ta list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf st25ta list [-h1fcrux] [--dict ]" + "usage": "hf st25ta list [-h1crux] [--frame] [-f ]" }, "hf st25ta ndefread": { "command": "hf st25ta ndefread", @@ -6250,9 +6797,10 @@ "options": [ "-h, --help This help", "-p, --pwd 16 byte read password", - "-f, --file save raw NDEF to file" + "-f, --file save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf st25ta ndefread [-h] [-p ] [-f ]" + "usage": "hf st25ta ndefread [-hv] [-p ] [-f ]" }, "hf st25ta protect": { "command": "hf st25ta protect", @@ -6302,6 +6850,92 @@ ], "usage": "hf st25ta sim [-h] -u " }, + "hf tesla help": { + "command": "hf tesla help", + "description": "help This help list List ISO 14443A/7816 history", + "notes": [], + "offline": true, + "options": [], + "usage": "" + }, + "hf tesla info": { + "command": "hf tesla info", + "description": "Get info about TESLA Key tag", + "notes": [ + "hf tesla info" + ], + "offline": false, + "options": [ + "-h, --help This help" + ], + "usage": "hf telsa info [-h]" + }, + "hf tesla list": { + "command": "hf tesla list", + "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "notes": [ + "hf tesla list --frame -> show frame delay times", + "hf tesla list -1 -> use trace buffer" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-1, --buffer use data from trace buffer", + "--frame show frame delay times", + "-c mark CRC bytes", + "-r show relative times (gap and duration)", + "-u display times in microseconds instead of clock cycles", + "-x show hexdump to convert to pcap(ng)", + "or to import into Wireshark using encapsulation type \"ISO 14443\"", + "-f, --file filename of dictionary" + ], + "usage": "hf tesla list [-h1crux] [--frame] [-f ]" + }, + "hf texkom help": { + "command": "hf texkom help", + "description": "help This help", + "notes": [], + "offline": true, + "options": [], + "usage": "" + }, + "hf texkom reader": { + "command": "hf texkom reader", + "description": "Read a texkom tag", + "notes": [ + "hf texkom reader", + "hf texkom reader -@ -> continuous reader mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-1 Use data from Graphbuffer", + "-v, --verbose Verbose scan and output", + "-@ optional - continuous reader mode" + ], + "usage": "hf texkom reader [-h1v@]" + }, + "hf texkom sim": { + "command": "hf texkom sim", + "description": "Simulate a texkom tag", + "notes": [ + "hf texkom sim", + "hf texkom sim --raw FFFF638C7DC45553 -> simulate TK13 tag with id 8C7DC455", + "hf texkom sim --tk17 --raw FFFFCA17F31EC512 -> simulate TK17 tag with id 17F31EC5", + "hf texkom sim --id 8C7DC455 -> simulate TK13 tag with id 8C7DC455", + "hf texkom sim --id 8C7DC455 --tk17 -> simulate TK17 tag with id 17F31EC5" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose Verbose work", + "-t, --tk17 Use TK-17 modulation (TK-13 by default)", + "--raw Raw data for texkom card, 8 bytes. Manual modulation select.", + "--id Raw data for texkom card, 8 bytes. Manual modulation select.", + "--timeout Simulation timeout in the ms. If not specified or 0 - infinite. Command can be skipped by pressing the button" + ], + "usage": "hf texkom sim [-hvt] [--raw ] [--id ] [--timeout ]" + }, "hf thinfilm help": { "command": "hf thinfilm help", "description": "help This help list List NFC Barcode / Thinfilm history - not correct", @@ -6326,22 +6960,22 @@ "command": "hf thinfilm list", "description": "Alias of `trace list -t thinfilm` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf thinfilm list -f -> show frame delay times", + "hf thinfilm list --frame -> show frame delay times", "hf thinfilm list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf thinfilm list [-h1fcrux] [--dict ]" + "usage": "hf thinfilm list [-h1crux] [--frame] [-f ]" }, "hf thinfilm sim": { "command": "hf thinfilm sim", @@ -6357,9 +6991,22 @@ ], "usage": "hf thinfilm sim [-h] -d [--raw]" }, + "hf topaz dump": { + "command": "hf topaz dump", + "description": "Dump TOPAZ tag to binary file If no given, UID will be used as filename", + "notes": [ + "hf topaz dump" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file filename of dump" + ], + "usage": "hf topaz dump [-h] [-f ]" + }, "hf topaz help": { "command": "hf topaz help", - "description": "help This help list List Topaz history", + "description": "help This help list List Topaz history view Display content from tag dump file", "notes": [], "offline": true, "options": [], @@ -6384,28 +7031,28 @@ "command": "hf topaz list", "description": "Alias of `trace list -t topaz` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "hf topaz list -f -> show frame delay times", + "hf topaz list --frame -> show frame delay times", "hf topaz list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "hf topaz list [-h1fcrux] [--dict ]" + "usage": "hf topaz list [-h1crux] [--frame] [-f ]" }, "hf topaz raw": { "command": "hf topaz raw", "description": "Send raw hex data to Topaz tags", "notes": [ - "hf topaz raw -> Not yet implemented" + "hf topaz raw" ], "offline": false, "options": [ @@ -6413,18 +7060,33 @@ ], "usage": "hf topaz raw [-h]" }, - "hf topaz reader": { - "command": "hf topaz reader", - "description": "Read UID from Topaz tags", + "hf topaz rdbl": { + "command": "hf topaz rdbl", + "description": "Read a block", "notes": [ - "hf topaz reader" + "hf topaz rdbl -b 7" ], "offline": false, "options": [ "-h, --help This help", - "-v, --verbose verbose output" + "-b, --block Block number to write" ], - "usage": "hf topaz reader [-hv]" + "usage": "hf topaz rdbl [-h] -b " + }, + "hf topaz reader": { + "command": "hf topaz reader", + "description": "Read UID from Topaz tags", + "notes": [ + "hf topaz reader", + "hf topaz reader -@ -> Continuous mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "-@ optional - continuous reader mode" + ], + "usage": "hf topaz reader [-hv@]" }, "hf topaz sim": { "command": "hf topaz sim", @@ -6450,6 +7112,33 @@ ], "usage": "hf topaz sniff [-h]" }, + "hf topaz view": { + "command": "hf topaz view", + "description": "Print a Topaz tag dump file (bin/eml/json)", + "notes": [ + "hf topaz view -f hf-topaz-04010203-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-f, --file filename of dump (bin/eml/json)" + ], + "usage": "hf topaz view [-h] -f " + }, + "hf topaz wrbl": { + "command": "hf topaz wrbl", + "description": "Write a block", + "notes": [ + "hf topaz wrbl -b 7 -d 1122334455667788" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-b, --block Block number to write", + "-d, --data Block data (8 hex bytes)" + ], + "usage": "hf topaz wrbl [-h] -b -d " + }, "hf tune": { "command": "hf tune", "description": "Continuously measure HF antenna tuning. Press button or to interrupt.", @@ -6497,6 +7186,56 @@ ], "usage": "hf waveshare loadbmp [-hs] -m -f " }, + "hf xerox dump": { + "command": "hf xerox dump", + "description": "Dump all memory from a Fuji/Xerox tag", + "notes": [ + "hf xerox dump" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file filename to save dump to", + "-d, --decrypt decrypt secret blocks" + ], + "usage": "hf xerox dump [-hd] [-f ]" + }, + "hf xerox help": { + "command": "hf xerox help", + "description": "help This help", + "notes": [], + "offline": true, + "options": [], + "usage": "" + }, + "hf xerox info": { + "command": "hf xerox info", + "description": "Tag information for ISO/IEC 14443 type B / XEROX based tags", + "notes": [ + "hf xerox info" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output" + ], + "usage": "hf xerox info [-hv]" + }, + "hf xerox reader": { + "command": "hf xerox reader", + "description": "Act as a 14443B reader to identify a tag", + "notes": [ + "hf xerox reader", + "hf xerox reader -@" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "-@ optional - continuous reader mode" + ], + "usage": "hf xerox reader [-hv@]" + }, "hints": { "command": "hints", "description": "Turn on/off hints", @@ -6780,7 +7519,7 @@ "--fc 8|16bit value facility code", "--cn optional - card number to start with, max 65535", "--delay optional - delay betweens attempts in ms. Default 1000ms", - "-v, --verbose verbose logging, show all tries" + "-v, --verbose verbose output" ], "usage": "lf awid brute [-hv] --fmt --fc [--cn ] [--delay ]" }, @@ -7400,7 +8139,7 @@ "options": [ "-h, --help This help", "-p, --pwd password, 4 hex bytes, lsb", - "-v, --verbose additional output of data section" + "-v, --verbose verbose output" ], "usage": "lf em 4x50 info [-hv] [-p ]" }, @@ -7536,6 +8275,23 @@ ], "usage": "lf em 4x70 auth [-h] [--par] --rnd --frn " }, + "lf em 4x70 brute": { + "command": "lf em 4x70 brute", + "description": "Optimized partial key-update attack of 16-bit key block 7, 8 or 9 of an EM4x70 This attack does NOT write anything to the tag. Before starting this attack, 0000 must be written to the 16-bit key block: 'lf em 4x70 write -b 9 -d 0000'. After success, the 16-bit key block have to be restored with the key found: 'lf em 4x70 write -b 9 -d c0de'", + "notes": [ + "lf em 4x70 brute -b 9 --rnd 45F54ADA252AAC --frn 4866BB70 -> bruteforcing key bits k95...k80" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--par Add parity bit when sending commands", + "-b, --block block/word address, dec", + "--rnd Random 56-bit", + "--frn F(RN) 28-bit as 4 hex bytes", + "-s, --start Start bruteforce enumeration from this key value" + ], + "usage": "lf em 4x70 brute [-h] [--par] -b --rnd --frn [-s ]" + }, "lf em 4x70 help": { "command": "lf em 4x70 help", "description": "help This help", @@ -7772,22 +8528,23 @@ }, "lf gproxii clone": { "command": "lf gproxii clone", - "description": "clone a Guardall tag to a T55x7, Q5/T5555 or EM4305/4469 tag. The facility-code is 8-bit and the card number is 20-bit. Larger values are truncated. Currently work only on 26 | 36 bit format", + "description": "Clone a Guardall tag to a T55x7, Q5/T5555 or EM4305/4469 tag. The facility-code is 8-bit and the card number is 20-bit. Larger values are truncated. Currently work only on 26 | 36 bit format", "notes": [ - "lf gproxii clone --fmt 26 --fc 123 --cn 1337 -> encode for T55x7 tag", - "lf gproxii clone --fmt 26 --fc 123 --cn 1337 --q5 -> encode for Q5/T5555 tag", - "lf gproxii clone --fmt 26 --fc 123 --cn 1337 --em -> encode for EM4305/4469" + "lf gproxii clone --xor 141 --fmt 26 --fc 123 --cn 1337 -> encode for T55x7 tag", + "lf gproxii clone --xor 141 --fmt 26 --fc 123 --cn 1337 --q5 -> encode for Q5/T5555 tag", + "lf gproxii clone --xor 141 --fmt 26 --fc 123 --cn 1337 --em -> encode for EM4305/4469" ], "offline": false, "options": [ "-h, --help This help", + "--xor 8-bit xor value (installation dependant)", "--fmt format length 26|32|36|40", "--fc 8-bit value facility code", "--cn 16-bit value card number", "--q5 optional - specify writing to Q5/T5555 tag", "--em optional - specify writing to EM4305/4469 tag" ], - "usage": "lf gproxii clone [-h] --fmt --fc --cn [--q5] [--em]" + "usage": "lf gproxii clone [-h] --xor --fmt --fc --cn [--q5] [--em]" }, "lf gproxii demod": { "command": "lf gproxii demod", @@ -7828,16 +8585,17 @@ "command": "lf gproxii sim", "description": "Enables simulation of Guardall card with specified card number. Simulation runs until the button is pressed or another USB command is issued. The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated. Currently work only on 26 | 36 bit format", "notes": [ - "lf gproxii sim --fmt 26 --fc 123 --cn 1337" + "lf gproxii sim --xor 141 --fmt 26 --fc 123 --cn 1337" ], "offline": false, "options": [ "-h, --help This help", + "--xor 8-bit xor value (installation dependant)", "--fmt format length 26|32|36|40", "--fc 8-bit value facility code", "--cn 16-bit value card number" ], - "usage": "lf gproxii sim [-h] --fmt --fc --cn " + "usage": "lf gproxii sim [-h] --xor --fmt --fc --cn " }, "lf help": { "command": "lf help", @@ -7849,27 +8607,28 @@ }, "lf hid brute": { "command": "lf hid brute", - "description": "Enables bruteforce of HID readers with specified facility code. This is a attack against reader. if cardnumber is given, it starts with it and goes up / down one step if cardnumber is not given, it starts with 1 and goes up to 65535", + "description": "Enables bruteforce of HID readers with specified facility code or card number. This is an attack against the reader. If the field being bruteforced is provided, it starts with it and goes up / down one step while maintaining other supplied values. If the field being bruteforced is not provided, it will iterate through the full range while maintaining other supplied values.", "notes": [ - "lf hid brute -w H10301 --fc 224", - "lf hid brute -w H10301 --fc 21 -d 2000", - "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000", - "lf hid brute -v -w H10301 --fc 21 --cn 200 -d 2000 --up" + "lf hid brute -w H10301 --field fc --fc 224 --cn 6278", + "lf hid brute -w H10301 --field cn --fc 21 -d 2000", + "lf hid brute -v -w H10301 --field cn --fc 21 --cn 200 -d 2000", + "lf hid brute -v -w H10301 --field fc --fc 21 --cn 200 -d 2000 --up" ], "offline": false, "options": [ "-h, --help This help", - "-v, --verbose verbose logging, show all tries", + "-v, --verbose verbose output", "-w, --wiegand see `wiegand list` for available formats", + "--field field to bruteforce", "--fc facility code", - "--cn card number to start with", + "--cn card number", "-i, --issue issue level", "-o, --oem OEM code", - "-d, --delay delay betweens attempts in ms. Default 1000ms", - "--up direction to increment card number. (default is both directions)", - "--down direction to decrement card number. (default is both directions)" + "-d, --delay delay betweens attempts in ms. (def is 1000)", + "--up direction to increment field value. (def is both directions)", + "--down direction to decrement field value. (def is both directions)" ], - "usage": "lf hid brute [-hv] -w [--fc ] [--cn ] [-i ] [-o ] [-d ] [--up] [--down]" + "usage": "lf hid brute [-hv] -w --field [--fc ] [--cn ] [-i ] [-o ] [-d ] [--up] [--down]" }, "lf hid clone": { "command": "lf hid clone", @@ -8035,22 +8794,22 @@ "command": "lf hitag list", "description": "Alias of `trace list -t hitag2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "lf hitag list -f -> show frame delay times", + "lf hitag list --frame -> show frame delay times", "lf hitag list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "lf hitag list [-h1fcrux] [--dict ]" + "usage": "lf hitag list [-h1crux] [--frame] [-f ]" }, "lf hitag reader": { "command": "lf hitag reader", @@ -8207,6 +8966,28 @@ ], "usage": "lf indala altdemod [-hl]" }, + "lf indala brute": { + "command": "lf indala brute", + "description": "Enables bruteforce of INDALA readers with specified facility code. This is a attack against reader. if cardnumber is given, it starts with it and goes up / down one step if cardnumber is not given, it starts with 1 and goes up to 65535", + "notes": [ + "lf indala brute --fc 224", + "lf indala brute --fc 21 -d 2000", + "lf indala brute -v --fc 21 --cn 200 -d 2000", + "lf indala brute -v --fc 21 --cn 200 -d 2000 --up" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "--fc facility code", + "--cn card number to start with", + "-d, --delay delay betweens attempts in ms. Default 1000ms", + "--up direction to increment card number. (default is both directions)", + "--down direction to decrement card number. (default is both directions)", + "--4041x specify Indala 4041X format" + ], + "usage": "lf indala brute [-hv] [--fc ] [--cn ] [-d ] [--up] [--down] [--4041x]" + }, "lf indala clone": { "command": "lf indala clone", "description": "clone Indala UID to T55x7 or Q5/T5555 tag using different known formats", @@ -8262,6 +9043,8 @@ "description": "Enables simulation of Indala card with specified facility code and card number. Simulation runs until the button is pressed or another USB command is issued.", "notes": [ "lf indala sim --heden 888", + "lf indala sim --fc 123 --cn 1337", + "lf indala sim --fc 123 --cn 1337 --4041x", "lf indala sim --raw a0000000a0002021", "lf indala sim --raw 80000001b23523a6c2e31eba3cbee4afb3c6ad1fcf649393928c14e5" ], @@ -8269,9 +9052,12 @@ "options": [ "-h, --help This help", "-r, --raw raw bytes", - "--heden Cardnumber for Heden 2L format" + "--heden Cardnumber for Heden 2L format", + "--fc Facility code (26 bit H10301 format)", + "--cn Card number (26 bit H10301 format)", + "--4041x Optional - specify Indala 4041X format, must use with fc and cn" ], - "usage": "lf indala sim [-h] [-r ] [--heden ]" + "usage": "lf indala sim [-h] [-r ] [--heden ] [--fc ] [--cn ] [--4041x]" }, "lf io clone": { "command": "lf io clone", @@ -8824,6 +9610,7 @@ "command": "lf paradox clone", "description": "clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", "notes": [ + "lf paradox clone --fc 96 --cn 40426 -> encode for T55x7 tag with fc and cn", "lf paradox clone --raw 0f55555695596a6a9999a59a -> encode for T55x7 tag", "lf paradox clone --raw 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag", "lf paradox clone --raw 0f55555695596a6a9999a59a --em -> encode for EM4305/4469" @@ -8832,10 +9619,12 @@ "options": [ "-h, --help This help", "-r, --raw raw hex data. 12 bytes max", + "--fc facility code", + "--cn card number", "--q5 optional - specify writing to Q5/T5555 tag", "--em optional - specify writing to EM4305/4469 tag" ], - "usage": "lf paradox clone [-h] [-r ] [--q5] [--em]" + "usage": "lf paradox clone [-h] [-r ] [--fc ] [--cn ] [--q5] [--em]" }, "lf paradox demod": { "command": "lf paradox demod", @@ -10111,7 +10900,7 @@ "command": "msleep", "description": "Sleep for given amount of milliseconds", "notes": [ - "msleep 100" + "msleep -t 100" ], "offline": true, "options": [ @@ -10179,6 +10968,14 @@ "options": [], "usage": "" }, + "nfc mf cformat": { + "command": "nfc mf cformat", + "description": "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write.", + "notes": [], + "offline": false, + "options": [], + "usage": "" + }, "nfc mf cread": { "command": "nfc mf cread", "description": "Prints NFC Data Exchange Format (NDEF)", @@ -10199,6 +10996,28 @@ ], "usage": "hf mf ndefread [-hvb] [--aid ] [-k ] [-f ]" }, + "nfc mf cwrite": { + "command": "nfc mf cwrite", + "description": "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.", + "notes": [ + "hf mf ndefwrite -d 0300FE -> write empty record to tag", + "hf mf ndefwrite -f myfilename", + "hf mf ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-d raw NDEF hex bytes", + "-f, --file write raw NDEF file to tag", + "-p fix NDEF record headers / terminator block if missing", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70", + "-v, --verbose verbose output" + ], + "usage": "hf mf ndefwrite [-hpv] [-d ] [-f ] [--mini] [--1k] [--2k] [--4k]" + }, "nfc mf help": { "command": "nfc mf help", "description": "-------- --------- NFC Type MIFARE Classic/Plus Tag -------- -------- --------------------- General --------------------- help This help", @@ -10270,9 +11089,23 @@ "options": [ "-h, --help This help", "-l Swap entered key's endianness", - "-f, --file Save raw NDEF to file" + "-f, --file Save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf mfu ndefread [-hl] [-k Replace default key for NDEF] [-f ]" + "usage": "hf mfu ndefread [-hlv] [-k Replace default key for NDEF] [-f ]" + }, + "nfc type4a format": { + "command": "nfc type4a format", + "description": "Format ISO14443-a Tag as a NFC tag with Data Exchange Format (NDEF)", + "notes": [ + "hf 14a ndefformat" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose show technical data" + ], + "usage": "hf 14a ndefformat [-hv]" }, "nfc type4a help": { "command": "nfc type4a help", @@ -10292,9 +11125,10 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file save raw NDEF to file" + "-f, --file save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf 14a ndefread [-h] [-f ]" + "usage": "hf 14a ndefread [-hv] [-f ]" }, "nfc type4a st25taread": { "command": "nfc type4a st25taread", @@ -10307,9 +11141,28 @@ "options": [ "-h, --help This help", "-p, --pwd 16 byte read password", - "-f, --file save raw NDEF to file" + "-f, --file save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf st25ta ndefread [-h] [-p ] [-f ]" + "usage": "hf st25ta ndefread [-hv] [-p ] [-f ]" + }, + "nfc type4a write": { + "command": "nfc type4a write", + "description": "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.", + "notes": [ + "hf 14a ndefwrite -d 0300FE -> write empty record to tag", + "hf 14a ndefwrite -f myfilename", + "hf 14a ndefwrite -d 003fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-d raw NDEF hex bytes", + "-f, --file write raw NDEF file to tag", + "-p fix NDEF record headers / terminator block if missing", + "-v, --verbose verbose output" + ], + "usage": "hf 14a ndefwrite [-hpv] [-d ] [-f ]" }, "nfc type4b help": { "command": "nfc type4b help", @@ -10329,9 +11182,118 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file save raw NDEF to file" + "-f, --file save raw NDEF to file", + "-v, --verbose show technical data" ], - "usage": "hf 14b ndefread [-h] [-f ]" + "usage": "hf 14b ndefread [-hv] [-f ]" + }, + "piv authsign": { + "command": "piv authsign", + "description": "Send a nonce and ask the PIV card to sign it", + "notes": [ + "piv sign -sk -> select card, select applet, sign a NULL nonce" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-s, -S, --select Activate field and select applet", + "-k, -K, --keep Keep field for next command", + "-a, -A, --apdu Show APDU requests and responses", + "-t, -T, --tlv TLV decode results", + "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "--aid Applet ID to select. By default A0000003080000100 will be used", + "--nonce Nonce to sign.", + "--slot Slot number. Default will be 0x9E (card auth cert).", + "--alg Algorithm to use to sign. Example values: 06=RSA-1024, 07=RSA-2048, 11=ECC-P256 (default), 14=ECC-P384" + ], + "usage": "piv sign [-hskatw] [--aid ] --nonce [--slot ] [--alg ]" + }, + "piv getdata": { + "command": "piv getdata", + "description": "Get a data container of a given tag", + "notes": [ + "piv getdata -s 5fc102 -> select card, select applet, get card holder unique identifer", + "piv getdata -st 5fc102 -> select card, select applet, get card holder unique identifer, show result in TLV" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-s, -S, --select Activate field and select applet", + "-k, -K, --keep Keep field for next command", + "-a, -A, --apdu Show APDU requests and responses", + "-t, -T, --tlv TLV decode results", + "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "--aid Applet ID to select. By default A0000003080000100 will be used", + " Tag ID to read, between 1 and 3 bytes." + ], + "usage": "piv getdata [-hskatw] [--aid ] " + }, + "piv help": { + "command": "piv help", + "description": "help This help list List ISO7816 history", + "notes": [], + "offline": true, + "options": [], + "usage": "" + }, + "piv list": { + "command": "piv list", + "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "notes": [ + "piv list --frame -> show frame delay times", + "piv list -1 -> use trace buffer" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-1, --buffer use data from trace buffer", + "--frame show frame delay times", + "-c mark CRC bytes", + "-r show relative times (gap and duration)", + "-u display times in microseconds instead of clock cycles", + "-x show hexdump to convert to pcap(ng)", + "or to import into Wireshark using encapsulation type \"ISO 14443\"", + "-f, --file filename of dictionary" + ], + "usage": "piv list [-h1crux] [--frame] [-f ]" + }, + "piv scan": { + "command": "piv scan", + "description": "Scan a PIV card for known containers", + "notes": [ + "piv scan -s -> select card, select applet and run scan", + "piv scan -st --aid a00000030800001000 -> select card, select applet a00000030800001000, show result of the scan in TLV" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-s, -S, --select Activate field and select applet", + "-k, -K, --keep Keep field for next command", + "-a, -A, --apdu Show APDU requests and responses", + "-t, -T, --tlv TLV decode results", + "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "--aid Applet ID to select. By default A0000003080000100 will be used" + ], + "usage": "piv scan [-hskatw] [--aid ]" + }, + "piv select": { + "command": "piv select", + "description": "Executes select applet command", + "notes": [ + "piv select -s -> select card, select applet", + "piv select -st --aid a00000030800001000 -> select card, select applet a00000030800001000, show result in TLV" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-s, -S, --select Activate field and select applet", + "-k, -K, --keep Keep field for next command", + "-a, -A, --apdu Show APDU requests and responses", + "-t, -T, --tlv TLV decode results", + "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "--aid Applet ID to select. By default A0000003080000100 will be used" + ], + "usage": "piv select [-hskatw] [--aid ]" }, "prefs get barmode": { "command": "prefs get barmode", @@ -10405,6 +11367,18 @@ ], "usage": "prefs get hints [-h]" }, + "prefs get output": { + "command": "prefs get output", + "description": "Get preference of dump output style", + "notes": [ + "prefs get output" + ], + "offline": true, + "options": [ + "-h, --help This help" + ], + "usage": "prefs get output [-h]" + }, "prefs get plotsliders": { "command": "prefs get plotsliders", "description": "Get preference of showing the plotslider control in the client", @@ -10513,7 +11487,7 @@ }, "prefs set help": { "command": "prefs set help", - "description": "help This help barmode Set bar mode clientdebug Set client debug level clientdelay Set client execution delay color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... plotsliders Set plot slider display", + "description": "help This help barmode Set bar mode clientdebug Set client debug level clientdelay Set client execution delay color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... output Set dump output style plotsliders Set plot slider display", "notes": [], "offline": true, "options": [], @@ -10533,6 +11507,21 @@ ], "usage": "prefs set hints [-h] [--off] [--on]" }, + "prefs set output": { + "command": "prefs set output", + "description": "Set dump output style to condense consecutive repeated data", + "notes": [ + "prefs set output --normal -> sets the output style to normal", + "prefs set output --dense -> sets the output style to dense" + ], + "offline": true, + "options": [ + "-h, --help This help", + "--normal normal output", + "--dense dense output" + ], + "usage": "prefs set output [-h] [--normal] [--dense]" + }, "prefs set plotsliders": { "command": "prefs set plotsliders", "description": "Set persistent preference of showing the plotslider control in the client", @@ -10653,7 +11642,7 @@ "offline": false, "options": [ "-h, --help This help", - "-v, --verbose verbose" + "-v, --verbose verbose output" ], "usage": "smart info [-hv]" }, @@ -10661,22 +11650,22 @@ "command": "smart list", "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ - "smart list -f -> show frame delay times", + "smart list --frame -> show frame delay times", "smart list -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "smart list [-h1fcrux] [--dict ]" + "usage": "smart list [-h1crux] [--frame] [-f ]" }, "smart raw": { "command": "smart raw", @@ -10708,7 +11697,7 @@ "offline": false, "options": [ "-h, --help This help", - "-v, --verbose verbose" + "-v, --verbose verbose output" ], "usage": "smart reader [-hv]" }, @@ -10783,24 +11772,24 @@ "trace list -t thinfilm -> interpret as Thinfilm", "trace list -t topaz -> interpret as Topaz", "", - "trace list -t mf --dict -> use dictionary keys file", - "trace list -t 14a -f -> show frame delay times", + "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file", + "trace list -t 14a --frame -> show frame delay times", "trace list -t 14a -1 -> use trace buffer" ], "offline": true, "options": [ "-h, --help This help", "-1, --buffer use data from trace buffer", - "-f show frame delay times", + "--frame show frame delay times", "-c mark CRC bytes", "-r show relative times (gap and duration)", "-u display times in microseconds instead of clock cycles", "-x show hexdump to convert to pcap(ng)", "or to import into Wireshark using encapsulation type \"ISO 14443\"", "-t, --type protocol to annotate the trace", - "--dict use dictionary keys file" + "-f, --file filename of dictionary" ], - "usage": "trace list [-h1fcrux] [-t ] [--dict ]" + "usage": "trace list [-h1crux] [--frame] [-t ] [-f ]" }, "trace load": { "command": "trace load", @@ -10998,8 +11987,8 @@ } }, "metadata": { - "commands_extracted": 693, + "commands_extracted": 754, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2022-06-12T09:54:12" + "extracted_on": "2023-03-26T15:04:49" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 6421ba761..487cea0c9 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -43,6 +43,7 @@ Check column "offline" for their availability. |`prefs get savepaths `|Y |`Get file folder ` |`prefs get emoji `|Y |`Get emoji display preference` |`prefs get hints `|Y |`Get hint display preference` +|`prefs get output `|Y |`Get dump output style preference` |`prefs get plotsliders `|Y |`Get plot slider display preference` @@ -60,6 +61,7 @@ Check column "offline" for their availability. |`prefs set emoji `|Y |`Set emoji display` |`prefs set hints `|Y |`Set hint display` |`prefs set savepaths `|Y |`... to be adjusted next ... ` +|`prefs set output `|Y |`Set dump output style` |`prefs set plotsliders `|Y |`Set plot slider display` @@ -124,6 +126,7 @@ Check column "offline" for their availability. |`data hexsamples `|N |`Dump big buffer as hex bytes` |`data hex2bin `|Y |`Converts hexadecimal to binary` |`data load `|Y |`Load contents of file into graph window` +|`data num `|Y |`Converts dec/hex/bin` |`data print `|Y |`Print the data in the DemodBuffer` |`data samples `|N |`Get raw samples for graph window (GraphBuffer)` |`data save `|Y |`Save signal trace data (from graph window)` @@ -175,18 +178,20 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf 14a help `|Y |`This help` |`hf 14a list `|Y |`List ISO 14443-a history` -|`hf 14a info `|N |`Tag information` -|`hf 14a reader `|N |`Act like an ISO14443-a reader` -|`hf 14a ndefread `|N |`Read an NDEF file from ISO 14443-A Type 4 tag` -|`hf 14a cuids `|N |`Collect n>0 ISO14443-a UIDs in one go` -|`hf 14a sim `|N |`Simulate ISO 14443-a tag` -|`hf 14a sniff `|N |`sniff ISO 14443-a traffic` -|`hf 14a apdu `|N |`Send ISO 14443-4 APDU to tag` -|`hf 14a chaining `|N |`Control ISO 14443-4 input chaining` -|`hf 14a raw `|N |`Send raw hex data to tag` |`hf 14a antifuzz `|N |`Fuzzing the anticollision phase. Warning! Readers may react strange` |`hf 14a config `|N |`Configure 14a settings (use with caution)` +|`hf 14a cuids `|N |`Collect n>0 ISO14443-a UIDs in one go` +|`hf 14a info `|N |`Tag information` +|`hf 14a sim `|N |`Simulate ISO 14443-a tag` +|`hf 14a sniff `|N |`sniff ISO 14443-a traffic` +|`hf 14a raw `|N |`Send raw hex data to tag` +|`hf 14a reader `|N |`Act like an ISO14443-a reader` +|`hf 14a apdu `|N |`Send ISO 14443-4 APDU to tag` |`hf 14a apdufind `|N |`Enumerate APDUs - CLA/INS/P1P2` +|`hf 14a chaining `|N |`Control ISO 14443-4 input chaining` +|`hf 14a ndefformat `|N |`Format ISO 14443-A as NFC Type 4 tag` +|`hf 14a ndefread `|N |`Read an NDEF file from ISO 14443-A Type 4 tag` +|`hf 14a ndefwrite `|N |`Write NDEF records to ISO 14443-A tag` ### hf 14b @@ -207,6 +212,7 @@ Check column "offline" for their availability. |`hf 14b sniff `|N |`Eavesdrop ISO-14443-B` |`hf 14b rdbl `|N |`Read SRI512/SRIX4x block` |`hf 14b sriwrite `|N |`Write data to a SRI512 or SRIX4K tag` +|`hf 14b view `|Y |`Display content from tag dump file` ### hf 15 @@ -227,8 +233,17 @@ Check column "offline" for their availability. |`hf 15 reader `|N |`Act like an ISO-15693 reader` |`hf 15 restore `|N |`Restore from file to all memory pages of an ISO-15693 tag` |`hf 15 samples `|N |`Acquire samples as reader (enables carrier, sends inquiry)` +|`hf 15 eload `|N |`Load image file into emulator to be used by 'sim' command` +|`hf 15 esave `|N |`Save emulator memory into image file` +|`hf 15 eview `|N |`View emulator memory` |`hf 15 sim `|N |`Fake an ISO-15693 tag` -|`hf 15 slixdisable `|N |`Disable privacy mode on SLIX ISO-15693 tag` +|`hf 15 slixwritepwd `|N |`Writes a password on a SLIX ISO-15693 tag` +|`hf 15 slixeasdisable `|N |`Disable EAS mode on SLIX ISO-15693 tag` +|`hf 15 slixeasenable `|N |`Enable EAS mode on SLIX ISO-15693 tag` +|`hf 15 slixprivacydisable`|N |`Disable privacy mode on SLIX ISO-15693 tag` +|`hf 15 slixprivacyenable`|N |`Enable privacy mode on SLIX ISO-15693 tag` +|`hf 15 passprotectafi `|N |`Password protect AFI - Cannot be undone` +|`hf 15 passprotecteas `|N |`Password protect EAS - Cannot be undone` |`hf 15 wrbl `|N |`Write a block` |`hf 15 findafi `|N |`Brute force AFI of an ISO-15693 tag` |`hf 15 writeafi `|N |`Writes the AFI on an ISO-15693 tag` @@ -267,7 +282,8 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf epa help `|Y |`This help` |`hf epa cnonces `|N |`Acquire encrypted PACE nonces of specific size` -|`hf epa preplay `|N |`Perform PACE protocol by replaying given APDUs` +|`hf epa replay `|N |`Perform PACE protocol by replaying given APDUs` +|`hf epa sim `|N |`Simulate PACE protocol` ### hf emrtd @@ -323,6 +339,20 @@ Check column "offline" for their availability. |`hf fido assert `|N |`FIDO2 GetAssertion command.` +### hf fudan + + { Fudan RFIDs... } + +|command |offline |description +|------- |------- |----------- +|`hf fudan help `|Y |`This help` +|`hf fudan reader `|N |`Act like a fudan reader` +|`hf fudan dump `|N |`Dump FUDAN tag to binary file` +|`hf fudan rdbl `|N |`Read a fudan tag` +|`hf fudan view `|Y |`Display content from tag dump file` +|`hf fudan wrbl `|N |`Write a fudan tag` + + ### hf gallagher { Gallagher DESFire RFIDs... } @@ -334,6 +364,7 @@ Check column "offline" for their availability. |`hf gallagher clone `|N |`Add Gallagher credentials to a DESFire card` |`hf gallagher delete `|N |`Delete Gallagher credentials from a DESFire card` |`hf gallagher diversifykey`|Y |`Diversify Gallagher key` +|`hf gallagher decode `|Y |`Decode Gallagher credential block` ### hf ksx6924 @@ -343,11 +374,11 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf ksx6924 help `|Y |`This help` -|`hf ksx6924 balance `|N |`Get current purse balance` -|`hf ksx6924 info `|N |`Get info about a KS X 6924 (T-Money, Snapper+) transit card` -|`hf ksx6924 initialize `|N |`Perform transaction initialization (Mpda)` -|`hf ksx6924 prec `|N |`Send proprietary get record command (CLA=90, INS=4C)` |`hf ksx6924 select `|N |`Select application, and leave field up` +|`hf ksx6924 info `|N |`Get info about a KS X 6924 (T-Money, Snapper+) transit card` +|`hf ksx6924 balance `|N |`Get current purse balance` +|`hf ksx6924 init `|N |`Perform transaction initialization with Mpda` +|`hf ksx6924 prec `|N |`Send proprietary get record command (CLA=90, INS=4C)` ### hf jooki @@ -414,8 +445,9 @@ Check column "offline" for their availability. |`hf legic eload `|N |`Load binary dump to emulator memory` |`hf legic esave `|N |`Save emulator memory to binary file` |`hf legic eview `|N |`View emulator memory` +|`hf legic einfo `|N |`Display deobfuscated and decoded emulator memory` |`hf legic crc `|Y |`Calculate Legic CRC over given bytes` -|`hf legic view `|Y |`Display content from tag dump file` +|`hf legic view `|Y |`Display deobfuscated and decoded content from tag dump file` ### hf lto @@ -426,11 +458,12 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf lto help `|Y |`This help` |`hf lto dump `|N |`Dump LTO-CM tag to file` -|`hf lto restore `|N |`Restore dump file to LTO-CM tag` |`hf lto info `|N |`Tag information` -|`hf lto rdbl `|N |`Read block` -|`hf lto wrbl `|N |`Write block` |`hf lto list `|Y |`List LTO-CM history` +|`hf lto rdbl `|N |`Read block` +|`hf lto reader `|N |`Act like a LTO-CM reader` +|`hf lto restore `|N |`Restore dump file to LTO-CM tag` +|`hf lto wrbl `|N |`Write block` ### hf mf @@ -454,12 +487,11 @@ Check column "offline" for their availability. |`hf mf auth4 `|N |`ISO14443-4 AES authentication` |`hf mf acl `|Y |`Decode and print MIFARE Classic access rights bytes` |`hf mf dump `|N |`Dump MIFARE Classic tag to binary file` -|`hf mf mad `|N |`Checks and prints MAD` -|`hf mf ndefread `|N |`Prints NDEF records from card` +|`hf mf mad `|Y |`Checks and prints MAD` |`hf mf personalize `|N |`Personalize UID (MIFARE Classic EV1 only)` |`hf mf rdbl `|N |`Read MIFARE Classic block` |`hf mf rdsc `|N |`Read MIFARE Classic sector` -|`hf mf restore `|N |`Restore MIFARE Classic binary file to BLANK tag` +|`hf mf restore `|N |`Restore MIFARE Classic binary file to tag` |`hf mf setmod `|N |`Set MIFARE Classic EV1 load modulation strength` |`hf mf value `|Y |`Value blocks` |`hf mf view `|Y |`Display content from tag dump file` @@ -486,7 +518,16 @@ Check column "offline" for their availability. |`hf mf gen3uid `|N |`Set UID without changing manufacturer block` |`hf mf gen3blk `|N |`Overwrite manufacturer block` |`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible` +|`hf mf ggetblk `|N |`Read block from card` +|`hf mf gload `|N |`Load dump to card` +|`hf mf gsave `|N |`Save dump from card into file or emulator` +|`hf mf gsetblk `|N |`Write block to card` |`hf mf gview `|N |`View card` +|`hf mf gdmconfig `|N |`Read config block from card` +|`hf mf gdmsetblk `|N |`Write block to card` +|`hf mf ndefformat `|N |`Format MIFARE Classic Tag as NFC Tag` +|`hf mf ndefread `|N |`Read and print NDEF records from card` +|`hf mf ndefwrite `|N |`Write NDEF records to card` ### hf mfp @@ -527,7 +568,9 @@ Check column "offline" for their availability. |`hf mfu restore `|N |`Restore a dump onto a MFU MAGIC tag` |`hf mfu view `|Y |`Display content from tag dump file` |`hf mfu wrbl `|N |`Write block` -|`hf mfu eload `|N |`Load Ultralight .eml dump file into emulator memory` +|`hf mfu tamper `|N |`Cofigure the tamper feature on an NTAG 213TT` +|`hf mfu eload `|N |`Load Ultralight dump file into emulator memory` +|`hf mfu esave `|N |`Save Ultralight dump file from emulator memory` |`hf mfu eview `|N |`View emulator memory` |`hf mfu sim `|N |`Simulate MIFARE Ultralight from emulator memory` |`hf mfu setpwd `|N |`Set 3DES key - Ultralight-C` @@ -619,6 +662,28 @@ Check column "offline" for their availability. |`hf st25ta sim `|N |`Fake ISO 14443A/ST tag` +### hf tesla + + { TESLA Cards... } + +|command |offline |description +|------- |------- |----------- +|`hf tesla help `|Y |`This help` +|`hf tesla info `|N |`Tag information` +|`hf tesla list `|Y |`List ISO 14443A/7816 history` + + +### hf texkom + + { Texkom RFIDs... } + +|command |offline |description +|------- |------- |----------- +|`hf texkom help `|Y |`This help` +|`hf texkom reader `|N |`Act like a Texkom reader` +|`hf texkom sim `|N |`Simulate a Texkom tag` + + ### hf thinfilm { Thinfilm RFIDs... } @@ -638,12 +703,28 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf topaz help `|Y |`This help` +|`hf topaz dump `|N |`Dump TOPAZ family tag to file` |`hf topaz list `|Y |`List Topaz history` |`hf topaz info `|N |`Tag information` |`hf topaz reader `|N |`Act like a Topaz reader` -|`hf topaz sim `|N |` -- Simulate Topaz tag` +|`hf topaz sim `|N |`Simulate Topaz tag` |`hf topaz sniff `|N |`Sniff Topaz reader-tag communication` |`hf topaz raw `|N |`Send raw hex data to tag` +|`hf topaz rdbl `|N |`Read block` +|`hf topaz view `|Y |`Display content from tag dump file` +|`hf topaz wrbl `|N |`Write block` + + +### hf xerox + + { Fuji/Xerox cartridge RFIDs... } + +|command |offline |description +|------- |------- |----------- +|`hf xerox help `|Y |`This help` +|`hf xerox info `|N |`Short info on Fuji/Xerox tag` +|`hf xerox reader `|N |`Act like a Fuji/Xerox reader` +|`hf xerox dump `|N |`Read all memory pages of an Fuji/Xerox tag, save to file` ### hf waveshare @@ -817,6 +898,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em 4x70 help `|Y |`This help` +|`lf em 4x70 brute `|N |`Bruteforce EM4X70 to find partial Crypt Key` |`lf em 4x70 info `|N |`Tag information EM4x70` |`lf em 4x70 write `|N |`Write EM4x70` |`lf em 4x70 unlock `|N |`Unlock EM4x70 for writing` @@ -875,7 +957,7 @@ Check column "offline" for their availability. |`lf hid reader `|N |`attempt to read and extract tag data` |`lf hid clone `|N |`clone HID tag to T55x7` |`lf hid sim `|N |`simulate HID tag` -|`lf hid brute `|N |`bruteforce card number against reader` +|`lf hid brute `|N |`bruteforce facility code or card number against reader` |`lf hid watch `|N |`continuously watch for cards. Reader mode` @@ -917,6 +999,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf indala help `|Y |`This help` +|`lf indala brute `|N |`Demodulate an Indala tag (PSK1) from the GraphBuffer` |`lf indala demod `|Y |`Demodulate an Indala tag (PSK1) from the GraphBuffer` |`lf indala altdemod `|Y |`Alternative method to demodulate samples for Indala 64 bit UID (option '224' for 224 bit)` |`lf indala reader `|N |`Read an Indala tag from the antenna` @@ -1233,7 +1316,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- +|`nfc type4a format `|N |`format ISO-14443-a tag as NFC Tag` |`nfc type4a read `|N |`read NFC Forum Tag Type 4 A` +|`nfc type4a write `|N |`write NFC Forum Tag Type 4 A` |`nfc type4a st25taread `|N |`read ST25TA as NFC Forum Tag Type 4` |`nfc type4a help `|Y |`This help` @@ -1254,7 +1339,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- +|`nfc mf cformat `|N |`format MIFARE Classic Tag as NFC Tag` |`nfc mf cread `|N |`read NFC Type MIFARE Classic Tag` +|`nfc mf cwrite `|N |`write NFC Type MIFARE Classic Tag` |`nfc mf pread `|N |`read NFC Type MIFARE Plus Tag` |`nfc mf help `|Y |`This help` @@ -1270,6 +1357,20 @@ Check column "offline" for their availability. |`nfc barcode help `|Y |`This help` +### piv + + { PIV commands... } + +|command |offline |description +|------- |------- |----------- +|`piv help `|Y |`This help` +|`piv select `|N |`Select the PIV applet` +|`piv getdata `|N |`Gets a container on a PIV card` +|`piv authsign `|N |`Authenticate with the card` +|`piv scan `|N |`Scan PIV card for known containers` +|`piv list `|Y |`List ISO7816 history` + + ### reveng { CRC calculations from RevEng software... } diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 4b2452d2f..d38b56de6 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -19,9 +19,9 @@ External 256kbytes flash is a unique feature of the RDV4 edition. Flash memory is -* 256kb (0x40000= 262144) -* divided into 4 pages of 64kb (0x10000 = 65536) -* 4 pages divided into 16 sectors of 4kb (0x1000 = 4096), so last sector is at 0x3F000 +* 256KB (0x40000= 262144) +* divided into 4 pages of 64KB (0x10000 = 65536) +* 4 pages divided into 16 sectors of 4KB (0x1000 = 4096), so last sector is at 0x3F000 Therefore a flash address can be interpreted as such: ``` diff --git a/doc/jtag_notes.md b/doc/jtag_notes.md index eaea64b6e..e5fc2dd99 100644 --- a/doc/jtag_notes.md +++ b/doc/jtag_notes.md @@ -57,34 +57,43 @@ You can also make yours with some 1.27mm headers (look for `1.27mm header` on Al J-Link [pinout](https://www.segger.com/interface-description.html): ``` - --------- --------- - |1917151311 9 7 5 3 1| - |201816141210 8 6 4 2| - -------------------- +Pin cut-out on a JLink 20 pin connector + + ^^ + -------------- --------- + |19 17 15 13 11 9 7 5 3 1| + |20 18 16 14 12 10 8 6 4 2| + ------------------------- ``` +``` +Map of pins between PM3 / JLink + PM3 | JLink --- | ----- -TMS | 7 -TDI | 5 -TDO |13 -TCK | 9 -GND | 6 -3.3 | 2 +TMS | 7 +TDI | 5 +TDO | 13 +TCK | 9 +GND | 6 +3.3 | 2 +``` ## Raspberry Pi pinout ^[Top](#top) RPi [pinout](https://pinout.xyz/): +``` PM3 | RPi ---- | ----- +--- | --- TMS | 22 TDI | 19 TDO | 21 TCK | 23 -GND | 6 -3.3 | 1 +GND | 6 +3.3 | 1 +``` # Where to find more information? ^[Top](#top) @@ -125,4 +134,4 @@ https://stackoverflow.com/questions/48794076/error-halt-timed-out-wake-up-gdb/64 ^[Top](#top) Describes the SEGGER JLINK, JTAG process but be warned, this document is old. -https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/original_proxmark3/Compiling%20Proxmark%20source%20and%20firmware%20upgrading%20v1.pdf \ No newline at end of file +https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/original_proxmark3/Compiling%20Proxmark%20source%20and%20firmware%20upgrading%20v1.pdf diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index 9291fdec5..9122f064d 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -20,6 +20,7 @@ Useful docs: * [MIFARE Classic DirectWrite, UFUID version](#mifare-classic-directwrite-ufuid-version) * [MIFARE Classic, other versions](#mifare-classic-other-versions) * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) + * [MIFARE Classic Gen4 aka GDM](#mifare-classic-gen4-aka-gdm) * [MIFARE Classic Super](#mifare-classic-super) - [MIFARE Ultralight](#mifare-ultralight) * [MIFARE Ultralight blocks 0..2](#mifare-ultralight-blocks-02) @@ -68,6 +69,7 @@ To restore anticollision config of the Proxmark3: ``` hf 14a config --std ``` + # MIFARE Classic ^[Top](#top) @@ -89,7 +91,7 @@ UID 4b: (actually NUID as there are no more "unique" IDs on 4b) ``` -Computing BCC on UID 11223344: `hf analyse lcr -d 11223344` = `44` +Computing BCC on UID 11223344: `analyse lcr -d 11223344` = `44` UID 7b: @@ -262,6 +264,8 @@ hf 14a info * Read: `40(7)`, `30xx` * Write: `40(7)`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc + + ## MIFARE Classic DirectWrite aka Gen2 aka CUID ^[Top](#top) @@ -366,7 +370,7 @@ Android compatible ^[Top](#top) ``` -hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344440804006263646566676869 +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344440804006263646566676869 --force hf mf wipe --gen2 ``` @@ -381,8 +385,13 @@ e.g. for 4b UID: ``` hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip -hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344440804006263646566676869 # for 1k -hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344441802006263646566676869 # for 4k + +# for 1k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344440804006263646566676869 --force + +# for 4k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344441802006263646566676869 --force + hf 14a config --std hf 14a reader ``` @@ -391,11 +400,17 @@ e.g. for 7b UID: ``` hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip -hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566084400626364656667 # for 1k -hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566184200626364656667 # for 4k + +# for 1k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566084400626364656667 --force + +# for 4k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566184200626364656667 --force + hf 14a config --std hf 14a reader ``` + ## MIFARE Classic DirectWrite, FUID version aka 1-write ^[Top](#top) @@ -436,14 +451,6 @@ hf 14a raw -k -c e100 hf 14a raw -c 85000000000000000000000000000008 ``` -## MIFARE Classic, other versions -^[Top](#top) - -**TODO** - -* ZXUID, EUID, ICUID ? -* Some cards exhibit a specific SAK=28 ?? - ## MIFARE Classic Gen3 aka APDU ^[Top](#top) @@ -517,27 +524,174 @@ hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000 hf 14a raw -s -c 90FD111100 ``` -## MIFARE Classic Super +## MIFARE Classic Gen4 aka GDM ^[Top](#top) -It behaves like DirectWrite but records reader auth attempts. +Tag has shadow mode enabled from start. +Meaning every write or changes to normal MFC memory is restored back to a copy from persistent memory after about 3 seconds +off rfid field. +Tag also seems to support Gen2 style, direct write, to block 0 to the normal MFC memory. -To change UID: same commands as for MFC DirectWrite +The persistent memory is also writable. For that tag uses its own backdoor commands. +for example to write, you must use a customer authentication byte, 0x80, to authenticate with an all zeros key, 0x0000000000. +Then send the data to be written. -To do reader-only attack: at least two versions exist. +This tag has simular commands to the [UFUID](#mifare-classic-directwrite-ufuid-version) +This indicates that both tagtypes are developed by the same person. -* type 1: https://github.com/nfc-tools/nfc-supercard for card with ATS: 0978009102DABC1910F005 -* type 2: https://github.com/netscylla/super-card/blob/master/libnfc-1.7.1/utils/nfc-super.c for ?? +**OBS** + +When writing to persistent memory it is possible to write _bad_ ACL and perm-brick the tag. + +**OBS** + +It is possible to write a configuration that perma locks the tag, ie no more magic ### Identify ^[Top](#top) -Only type 1 at the moment: +``` +hf 14a info +... +[+] Magic capabilities : Gen 4 GDM +``` +### Magic commands +^[Top](#top) + +* Auth: `80xx`+crc +* Write: `A8xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc +* Read config: `E000`+crc +* Write config: `E100`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc + +### Characteristics +^[Top](#top) + +* Have no knowledge in ATQA/SAK/BCC quirks or if there is a wipe, softbrick recover +* Its magic part seem to be three identified custom command. +* Auth command 0x80, with the key 0x0000000000, Write 0xA8 allows writing to persistent memory, Read 0xE0 which seems to return a configuration. This is unknown today what these bytes are. + +Read config: +1. sending custom auth with all zeros key +2. send 0xE000, will return the configuration bytes. +`results: 850000000000000000005A5A00000008` + + +Mapping of configuration bytes so far: +``` +850000000000000000005A5A00000008 + ^^ --> SAK +``` + +Write config: +1. sending custom auth with all zeros key +2. send 0xE100 +3. send 16 bytes + +**Warning** + +Example of configuration to Perma lock tag: +`85000000000000000000000000000008` + + +It is unknown what kind of block 0 changes the tag supports +* UID: 4b +* ATQA/SAK: unknown +* BCC: unknown +* ATS: none + +### Proxmark3 commands +^[Top](#top) +``` +# Write to persistent memory +hf mf gdmsetblk + +# Read configuration (0xE0): +hf mf gdmcfg + +# Write configuration (0xE1): +hf mf gdmsetcfg +``` + +### libnfc commands +^[Top](#top) +No implemented commands today + +## MIFARE Classic, other versions +^[Top](#top) + +**TODO** + +* ZXUID, EUID, ICUID, KUID, HUID, RFUID ? +* Some cards exhibit a specific SAK=28 ?? + +## MIFARE Classic Super +^[Top](#top) + +It behaves like regular Mifare Classic but records reader auth attempts. + +#### MIFARE Classic Super Gen1 +^[Top](#top) + +Old type of cards, hard to obtain. They are DirectWrite, UID can be changed via 0 block or backdoor commands. + +* UID: 4b version +* ATQA/SAK: fixed +* BCC: auto +* ATS: fixed, 0978009102DABC1910F005 + +ATQA/SAK matches 1k card, but works as 4k card. + +Backdoor commands provided over APDU. Format: + +``` +00 A6 A0 00 05 FF FF FF FF 00 +^^ ^^ Backdoor command header + ^^ Backdoor command (A0 - set UID/B0 - get trace/C0 - reset card) + ^^ Type of answer (used in key recovery to select trace number) + ^^ Length of user provided data + ^^ ^^ ^^ ^^ ^^ User data +``` + +👉 You can't change UID with backdoor command if incorrect data is written to the 0 sector trailer! + +#### MIFARE Classic Super Gen1B + +DirectWrite card, ATS unknown. Probably same as Gen1, except backdoor commands. +Implementation: https://github.com/netscylla/super-card/blob/master/libnfc-1.7.1/utils/nfc-super.c + +#### MIFARE Classic Super Gen2 +^[Top](#top) + +New generation of cards, based on limited Gen4 chip. Emulates Gen1 backdoor protocol, but can store up to 7 different traces. + +Card always answer `ff ff ff ff` to auth, so writing/reading it via Mifare protocol is impossible. + +UID is changeable via Gen4 backdoor write to 0 block. + +* UID: 4b and 7b versions +* ATQA/SAK: fixed +* BCC: auto +* ATS: changeable, default as Gen1 + +Gen4 commands available: + +``` +CF 34 <1b length><0-16b ATS> // Configure ATS +CF CC // Factory test, returns 00 00 00 02 AA +CF CD <1b block number><16b block data> // Backdoor write 16b block +CF CE <1b block number> // Backdoor read 16b block +CF FE <4b new_password> // Change password +``` + +### Identify +^[Top](#top) + +Only Gen1/Gen2 at this moment (Gen1B is unsupported): ``` hf 14a info ... -[+] Magic capabilities : super card +[+] Magic capabilities : Super card (Gen ?) ``` # MIFARE Ultralight @@ -972,7 +1126,6 @@ script run hf_15_magic -u E004013344556677 A.k.a ultimate magic card, most promenent feature is shadow mode (GTU) and optional password protected backdoor commands. - Can emulate MIFARE Classic, Ultralight/NTAG families, 14b UID & App Data - [Identify](#identify) @@ -988,6 +1141,7 @@ Can emulate MIFARE Classic, Ultralight/NTAG families, 14b UID & App Data - [Select Ultralight mode](#select-ultralight-mode) - [Set shadow mode (GTU)](#set-shadow-mode-gtu) - [Direct block read and write](#direct-block-read-and-write) +- [(De)Activate direct write to block 0](#deactivate-direct-write-to-block-0) - [Change backdoor password](#change-backdoor-password) - [Dump configuration](#dump-configuration) - [Fast configuration](#fast-configuration) @@ -998,13 +1152,19 @@ Can emulate MIFARE Classic, Ultralight/NTAG families, 14b UID & App Data ### Identify ^[Top](#top) ^^[Gen4](#g4top) -👉 **TODO** Tag doesn't get identified correctly by latest Proxmark3 client (it might get mislabeled as MFC Gen2/CUID, Gen3/APDU or NTAG21x Modifiable, depending on configured UID/ATQA/SAK/ATS) +👉 **TODO** If the password is not default, Tag doesn't get identified correctly by latest Proxmark3 client (it might get mislabeled as MFC Gen2/CUID, Gen3/APDU or NTAG21x Modifiable, depending on configured UID/ATQA/SAK/ATS) -One can identify manually such card if the password is still the default one, with the command to get the current configuration: +``` +hf 14a info +[+] Magic capabilities : Gen 4 GTU +``` + +The card will be identified only if the password is the default one. One can identify manually such card if the password is still the default one, with the command to get the current configuration: ``` hf 14a raw -s -c -t 1000 CF00000000C6 ``` -If the card is an Ultimate Magic Card, it returns 30 bytes. +If the card is an Ultimate Magic Card, it returns 30 or 32 bytes. + ### Magic commands ^[Top](#top) ^^[Gen4](#g4top) @@ -1081,10 +1241,12 @@ CF 35 <2b ATQA><1b SAK> // Configure ATQA/SAK (swap ATQ CF 68 <00-02> // Configure UID length CF 69 <00-01> // (De)Activate Ultralight mode CF 6A <00-03> // Select Ultralight mode +CF 6B <1b> // Set Ultralight and M1 maximum read/write sectors CF C6 // Dump configuration CF CC // Factory test, returns 6666 CF CD <1b block number><16b block data> // Backdoor write 16b block CF CE <1b block number> // Backdoor read 16b block +CF CF <1b param> // (De)Activate direct write to block 0 CF F0 <30b configuration data> // Configure all params in one cmd CF F1 <30b configuration data> // Configure all params in one cmd and fuse the configuration permanently CF FE <4b new_password> // change password @@ -1108,6 +1270,14 @@ Default ``: `00000000` ``` # view contents of tag memory: hf mf gview +# Read a specific block via backdoor command: +hf mf ggetblk +# Write a specific block via backdoor command: +hf mf gsetblk +# Load dump to tag: +hf mf gload +# Save dump from tag: +hf mf gsave ``` 👉 **TODO** `hf mf gview` is currently missing Ultralight memory maps @@ -1120,6 +1290,8 @@ hf 14a raw -s -c -t 1000 CF00000000CE02 ... ``` +👉 **TODO** In Mifare Ultralight / NTAG mode, the special writes (`hf mfu restore` option `-s`, `-e`, `-r`) do not apply. Use `script run hf_mf_ultimatecard` for UID and signature, and `hf mfu wrbl` for PWD and PACK. + ### Change ATQA / SAK ^[Top](#top) ^^[Gen4](#g4top) @@ -1139,6 +1311,7 @@ OR (Note the script will correct the ATQA correctly) ``` script run hf_mf_ultimatecard -q 004428 ``` + ### Change ATS ^[Top](#top) ^^[Gen4](#g4top) @@ -1176,6 +1349,7 @@ Example: set UID length to 7 bytes, default pwd ``` hf 14a raw -s -c -t 1000 CF000000006801 ``` + ### Set 14443A UID ^[Top](#top) ^^[Gen4](#g4top) @@ -1289,6 +1463,21 @@ script run hf_mf_ultimatecard -m 02 ``` Now the card supports the 3DES UL-C authentication. + +### Set Ultralight and M1 maximum read/write sectors +^[Top](#top) ^^[Gen4](#g4top) + +``` +hf 14a raw -s -c -t 1000 CF6B<1b blocks> +``` +Hexadecimal, maximum sector data, default 0xFF, range 0x00-0xFF + +Example: set maximum 63 blocks read/write for Mifare Classic 1K + +``` +hf 14a raw -s -c -t 1000 CF000000006B3F +``` + ### Set shadow mode (GTU) ^[Top](#top) ^^[Gen4](#g4top) @@ -1350,6 +1539,28 @@ Example: write block0 with factory data, default pwd hf 14a raw -s -c -t 1000 CF00000000CD00112233441C000011778185BA18000000 ``` +### (De)Activate direct write to block 0 +^[Top](#top) ^^[Gen4](#g4top) + +This command enables/disables direct writes to block 0. + +``` +hf 14a raw -s -c -t 1000 CFCF<1b param> +``` + * `` + * `00`: Activate direct write to block 0 (Same behaviour of Gen2 cards. Some readers may identify the card as magic) + * `01`: Deactivate direct write to block 0 (Same behaviour of vanilla cards) + * `02`: Default value. (Same behaviour as `00` (?)) + +Example: enable direct writes to block 0, default pwd +``` +hf 14a raw -s -c -t 1000 CF00000000CF00 +``` +Example: disable direct writes to block 0, default pwd +``` +hf 14a raw -s -c -t 1000 CF00000000CF01 +``` + ### Change backdoor password ^[Top](#top) ^^[Gen4](#g4top) @@ -1376,8 +1587,10 @@ hf 14a raw -s -c -t 1000 CFC6 ``` Default configuration: ``` -00000000000002000978009102DABC191010111213141516040008004F6B - ^^^^ ?? +00000000000002000978009102DABC191010111213141516040008006B024F6B + ^^^^ ?? + ^^ cf cmd cf: block0 direct write setting, factory value 0x02 + ^^ cf cmd 6b: maximum read/write sectors, factory value 0x6b ^^ cf cmd 6a: UL mode ^^^^^^ cf cmd 35: ATQA/SAK ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cf cmd 34: ATS length & content @@ -1386,6 +1599,7 @@ Default configuration: ^^ cf cmd 68: UID length ^^ cf cmd 69: Ultralight protocol ``` + ### Fast configuration ^[Top](#top) ^^[Gen4](#g4top) @@ -1496,4 +1710,3 @@ hf mfu wrbl -b 250 -d 00040402 --force hf mfu wrbl -b 251 -d 01001303 --force hf mfu info ``` - diff --git a/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md b/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md index f905084f0..3b2e31530 100644 --- a/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md @@ -7,6 +7,7 @@ # Table of Contents - [Mac OS X - Homebrew automatic installation](#mac-os-x---homebrew-automatic-installation) - [Table of Contents](#table-of-contents) + - [macOS Ventura Beta users](#macos-ventura-beta-users) - [Apple Silicon (M1) Notes](#apple-silicon-m1-notes) - [Install Proxmark3 tools](#install-proxmark3-tools) - [Upgrade HomeBrew tap formula](#upgrade-homebrew-tap-formula) @@ -21,6 +22,22 @@ +## macOS Ventura Beta users +^[Top](#top) + +Early users of macOS Ventura and Xcode 14.0 might run into an error saying that Xcode 14.0 is out-of-date (even though you have the latest Xcode Beta installed). + +If (and only if) you run into that error, here is the fix: +- RE-download the *latest* Command Line Tools of Xcode Beta 14 and install them (again). (https://developer.apple.com/download/all/) +- Run `sudo xcode-select -s /Applications/Xcode-beta.app` in Terminal. +- Proceed with Brew installation +That should normally fix the issue. + +Alternatively, and only if the issue still persists after following the steps above, you can use this *temporary and ugly* fix: +- Try renaming `Xcode-beta.app` to `Xcode.app` (Note: If you still need Xcode 13.0 for signing and uploading apps to App Store rename `Xcode.app` to `Xcode-2.app`) +- Proceed with Brew installation +- IMPORTANT: Reverse renaming done in first step. + ## Apple Silicon (M1) Notes ^[Top](#top) @@ -37,6 +54,8 @@ Then you are missing Rosetta 2 and need to install it: `/usr/sbin/softwareupdate Homebrew has changed their prefix to differentiate between native Apple Silicon and Intel compiled binaries. The Makefile attempts to account for this but please note that whichever terminal or application you're using must be running under Architecture "Apple" as seen by Activity Monitor as all child processes inherit the Rosetta 2 environment of their parent. You can check which architecture you're currently running under with a `uname -m` in your terminal. +The fastest option is to run the brew command with the `arch -arm64` prefix i.e. `arch -arm64 brew install --HEAD --with-blueshark proxmark3`. This doesn't require running the whole terminal in Rosetta 2. + Visual Studio Code still runs under Rosetta 2 and if you're developing for proxmark3 on an Apple Silicon Mac you might want to consider running the Insiders build which has support for running natively on Apple Silicon. ## Install Proxmark3 tools @@ -185,4 +204,4 @@ pm3 If you want to manually select serial port, remember that the Proxmark3 port is `/dev/tty.usbmodemiceman1`, so commands become: ```sh proxmark3 /dev/ttyACM0 => proxmark3 /dev/tty.usbmodemiceman1 -``` \ No newline at end of file +``` diff --git a/doc/md/Installation_Instructions/Troubleshooting.md b/doc/md/Installation_Instructions/Troubleshooting.md index a69b0b9f9..d3fbab571 100644 --- a/doc/md/Installation_Instructions/Troubleshooting.md +++ b/doc/md/Installation_Instructions/Troubleshooting.md @@ -6,24 +6,26 @@ Always use the latest repository commits from *master* branch. There are always ## Table of Contents - * [pm3 or pm3-flash* doesn't see my Proxmark](#pm3-or-pm3-flash-doesnt-see-my-proxmark) - * [pm3-flash* stops and warns about up-to-date firmware images](#pm3-flash-stops-and-warns-about-up-to-date-firmware-images) - * [My Proxmark3 seems bricked](#my-proxmark3-seems-bricked) - * [Maybe just a false alarm?](#maybe-just-a-false-alarm) - * [Find out why it would be bricked](#find-out-why-it-would-be-bricked) - * [Determine if the bootloader was damaged or only the main OS image](#determine-if-the-bootloader-was-damaged-or-only-the-main-os-image) - * [Ok, my bootloader is definitively dead, now what?](#ok-my-bootloader-is-definitively-dead-now-what) - * [Slow to boot or difficulties to enumerate the device over USB](#slow-to-boot-or-difficulties-to-enumerate-the-device-over-usb) - * [Troubles with SIM card reader](#troubles-with-sim-card-reader) - * [Troubles with t5577 commands or MFC/iClass/T55x7 dictionaries](#troubles-with-t5577-commands-or-mfciclasst55x7-dictionaries) - * [File not found](#file-not-found) - * [Pixmap / pixbuf warnings](#pixmap--pixbuf-warnings) - * [Usb cable](#usb-cable) - * [WSL explorer.exe . doesn't work](#WSL) - * [Troubles with running the Proxmark3 client](#troubles-with-running-the-proxmark3-client) - * [libQt5Core.so.5 not found](#libQt5Coreso5-not-found) - * [Target attribute is not supported on this machine](#target-attribute-is-not-supported-on-this-machine) - * [Qt: Session management error:](#qt-session-management-error) +- [Troubleshooting guide](#troubleshooting-guide) + - [Table of Contents](#table-of-contents) + - [`pm3` or `pm3-flash*` doesn't see my Proxmark](#pm3-or-pm3-flash-doesnt-see-my-proxmark) + - [`pm3-flash*` stops and warns about up-to-date firmware images](#pm3-flash-stops-and-warns-about-up-to-date-firmware-images) + - [My Proxmark3 seems bricked](#my-proxmark3-seems-bricked) + - [Maybe just a false alarm?](#maybe-just-a-false-alarm) + - [Find out why it would be bricked](#find-out-why-it-would-be-bricked) + - [Determine if the bootloader was damaged or only the main OS image](#determine-if-the-bootloader-was-damaged-or-only-the-main-os-image) + - [Ok, my bootloader is definitively dead, now what?](#ok-my-bootloader-is-definitively-dead-now-what) + - [Slow to boot or difficulties to enumerate the device over USB](#slow-to-boot-or-difficulties-to-enumerate-the-device-over-usb) + - [Troubles with SIM card reader](#troubles-with-sim-card-reader) + - [Troubles with t5577 commands or MFC/iClass/T55x7 dictionaries](#troubles-with-t5577-commands-or-mfciclasst55x7-dictionaries) + - [File not found](#file-not-found) + - [Pixmap / pixbuf warnings](#pixmap--pixbuf-warnings) + - [Usb cable](#usb-cable) + - [WSL](#wsl) + - [Troubles with running the Proxmark3 client](#troubles-with-running-the-proxmark3-client) + - [libQt5Core.so.5 not found](#libqt5coreso5-not-found) + - [target attribute is not supported on this machine](#target-attribute-is-not-supported-on-this-machine) + - [Qt Session management error](#qt-session-management-error) ## `pm3` or `pm3-flash*` doesn't see my Proxmark @@ -133,6 +135,8 @@ See [details here](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) (RDV4 only) Make sure you've the latest SIM firmware according to the [configuration documentation](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md#verify-sim-module-firmware-version). +Check that the SIM daughterboard is properly seated onto the mainboard. Pressure on the edges of the daughterboard could cause the connector to partially disconnect (especially good to check if problems began after installation of the bluetooth module, for example). + ## Troubles with t5577 commands or MFC/iClass/T55x7 dictionaries ^[Top](#top) @@ -159,12 +163,12 @@ proxmark3 --flash --image /usr/local/share/proxmark3/firmware/f proxmark3 --flash --image /usr/share/proxmark3/firmware/fullimage.elf using the script: -pm3 --> smart upgrade -f /usr/local/share/proxmark3/firmware/sim011.bin +pm3 --> smart upgrade -f /usr/local/share/proxmark3/firmware/sim013.bin <> -pm3 --> smart upgrade -f /usr/share/proxmark3/firmware/sim011.bin +pm3 --> smart upgrade -f /usr/share/proxmark3/firmware/sim013.bin ``` -If you didn't install the PRoxmark but you're working from the sources directory and depending how you launch the client, your working directory might be the root of the repository: +If you didn't install the Proxmark but you're working from the sources directory and depending how you launch the client, your working directory might be the root of the repository: ``` ./pm3 ... @@ -184,7 +188,7 @@ client/proxmark3 --flash --image armsrc/obj/fullimage.elf <> ./proxmark3 --flash --image ../armsrc/obj/fullimage.elf -pm3 --> smart upgrade -f sim011.bin +pm3 --> smart upgrade -f sim013.bin ``` etc. @@ -220,7 +224,7 @@ Try running it with ## libQt5Core.so.5 not found ^[Top](#top) -On WSL1 / updated to Ubuntu 20.04, there is a slight chance you experience problems when compiling the repo with QT5. +On WSL1 / updated to Ubuntu 20.04 and 22.04, there is a slight chance you experience problems when compiling the repo with QT5. The following steps is needed to make the development environment happy again. ``` sudo apt reinstall qtbase5-dev diff --git a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md index a16c3c4d4..6ac9e53b3 100644 --- a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md @@ -90,7 +90,7 @@ git clone https://github.com/RfidResearchGroup/proxmark3.git cd proxmark3 ``` -If you're a contributing developer, you probably want to be able to use `make style`. If so, you've to install astyle: +If you're a contributing developer, you probably want to be able to use `make style`. If so, you've got to install astyle: ```sh pacman -S mingw-w64-x86_64-astyle diff --git a/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md new file mode 100644 index 000000000..9519b4849 --- /dev/null +++ b/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md @@ -0,0 +1,224 @@ + + +# WSL2 Installation instructions + +## Table of Contents +- [WSL2 Installation instructions](#wsl2-installation-instructions) + - [Table of Contents](#table-of-contents) + - [Requirements](#requirements) + - [Install Kali Linux distribution](#install-kali-linux-distribution) + - [Driver installation (Windows 11)](#driver-installation-windows-11) + - [USBIPD hints](#usbipd-hints) + - [WSL2 / Kali Linux Installation](#wsl2--kali-linux-installation) + - [X Server Installation](#x-server-installation) + - [Clone the Iceman repository](#clone-the-iceman-repository) + - [Compile the project](#compile-the-project) + - [Install the udev rules](#install-the-udev-rules) + - [Inform udev that it really, really should work](#inform-udev-that-it-really-really-should-work) + - [Verify Device Exists](#verify-device-exists) + - [Using the client...](#using-the-client) + - [Done!](#done) + +This provides instructions on how to install, build, and use Proxmark3 +on Windows 11, using WSL2 (and Kali Linux). + +## Requirements +^[Top](#top) + +This WSL 2 method requires Windows 11 (Build 22000 or later), +WSL installed and [set to WSL2](https://learn.microsoft.com/en-us/windows/wsl/basic-commands#set-wsl-version-to-1-or-2), + +While WSL 2 does not itself support passing through USB or +serial devices, it can work by using the USB/IP open-source +project, [`usbipd-win`](https://github.com/dorssel/usbipd-win). + + +## Install Kali Linux distribution +^[Top](#top) + +Open the Windows App Store, and install Kali Linux. + +For WSL configuration, see [Manage and configure Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/wsl-config). + +Start the Kali Linux distribution at least once, to ensure it's fully installed. + +## Driver installation (Windows 11) +^[Top](#top) + +On the Windows (host) machine, install the +[latest release](https://github.com/dorssel/usbipd-win/releases) +of `usbpid-win` (typically an `.MSI` file). + +## USBIPD hints +^[Top](#top) + +This is *NOT* intended to be a full description of how to use USBIPD. +Rather, this is intended only to give a starting point, as ***the values +shown here are extremely likely to differ per machine***. + +It's presumed that you've already installed USBIPD. Plug the Proxmark +device into a USB port. Then, from a `cmd.exe` or `wt.exe` ***launched +with administrative permissions***: + +Get a list of attached devices. Example (NOTE: VID/PID for non-proxmark devices redacted) + +```cmd +C:\qwert> usbipd list + +Connected: +BUSID VID:PID DEVICE STATE +1-2 xxxx:xxxx USB Input Device Not shared +2-3 xxxx:xxxx USB Mass Storage Device Not shared +5-3 9ac4:4b8f USB Serial Device (COM31) Not shared + +Persisted: +GUID DEVICE +``` + +Take note of the `BUSID` for the proxmark device, which should show as a USB Serial Device. + +Setup that bus ID to always be redirected to the WSL distribution named `kali-linux`: + +```cmd +C:\qwert> usbipd wsl attach --busid 5-3 --distribution kali-linux --auto-attach +usbipd: info: Starting endless attach loop; press Ctrl+C to quit. +Attached +``` + +NOTE: You must leave that running in the background, to allow the device to automatically +re-attach to the WSL2 instance. + + + +## WSL2 / Kali Linux Installation +^[Top](#top) + +Start the Kali Linux distribution you installed. First, make sure +the distribution is up-to-date: + +```sh +sudo apt-get update +sudo apt-get upgrade -y +sudo apt-get auto-remove -y +``` + +then, install proxmark dependencies: + +```sh +sudo apt-get install --no-install-recommends \ + git ca-certificates build-essential pkg-config \ + libreadline-dev gcc-arm-none-eabi libnewlib-dev \ + libbz2-dev libpython3-dev qtbase5-dev libssl-dev +``` + +_note_ +If you don't need the graphical components of the Proxmark3 client, you can skip the installation of `qtbase5-dev`. +If you don't need support for Python3 scripts in the Proxmark3 client, you can skip the installation of `libpython3-dev`. + +## X Server Installation +^[Top](#top) + +TBD -- Installing [`Win-KeX`](https://www.kali.org/docs/wsl/win-kex/) has worked +to provide a fully integrated experience, with three distinct modes..... +However, WSL2 may have some functionality already built-in? + +## Clone the Iceman repository +^[Top](#top) + +```sh +cd ~/ +git clone https://github.com/RfidResearchGroup/proxmark3.git +``` + +## Compile the project +^[Top](#top) + +```sh +cd ~/proxmark3 +make clean && make -j +``` + +## Install the udev rules + +```sh +sudo make accessrights +sudo make udev +``` + +On Kali, the above does two things: +1. Ensures the user is a member of the `dialout` group +2. Copies the `./driver/77-pm3-usb-device-blacklist.rules` file to the `/etc/udev/rules.d/` directory + +This presumes that the file includes `MODE="660" GROUP="dialout"` at the end of the three match lines. +The goal is that Kali Linux will automatically apply the proper permissions when the device is attached. + +However, it may be necessary to give the `udev` service a kind reminder: + +## Inform udev that it really, really should work + +The following workaround appears to work to get udev to apply the permissions +appropriately. Note that this may need to be run again, such as when the WSL2 +distributions have been restarted. I don't know why ... but it's a small hiccup. + +```sh +sudo udevadm trigger --action=change +``` + +General instructions suggested to use `sudo udevadm control --reload-rules`. However, +this may simply result in the following cryptic error message: + +```sh +$ sudo udevadm control --reload-rules +[sudo] password for root: +Failed to send reload request: No such file or directory +``` + +_Note that the following should **NOT** be required:_ + +```sh +sudo service udev restart +``` + +## Verify Device Exists + +Verify the device exists, and has a symbolic link created: + +```sh +ls -lFA /dev/ttyACM* +ls -lFA /dev/pm3* +``` + + +The first should show the `rw` permissions for both owner +and group, and show the group as `dialout`: + +```sh +┌──(qwert㉿host)-[~] +└─$ ls -lFA /dev/ttyACM* +crw-rw---- 1 root dialout 166, 0 Jan 22 11:28 /dev/ttyACM0 +``` + +The second command should show that a symbolic link exists +from the friendly name `/dev/pm3-0` to the TTY device: + +```sh +┌──(qwert㉿host)-[~] +└─$ ls -lFA /dev/pm3* +lrwxrwxrwx 1 root root 7 Jan 17 19:46 /dev/pm3-0 -> ttyACM0 +``` + +## Using the client... + +```sh +┌──(qwert㉿host)-[~] +└─$ pushd ~/proxmark3 + +┌──(qwert㉿host)-[~] +└─$ ./pm3 +``` + +## Done! +^[Top](#top) + +Full [compilation instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md) may be helpful. + diff --git a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md index a53b209e7..d8dd7d5e0 100644 --- a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md +++ b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md @@ -13,6 +13,7 @@ - [if you got an error](#if-you-got-an-error) - [Install](#install) - [Flash the BOOTROM & FULLIMAGE](#flash-the-bootrom--fullimage) + - [The button trick](#the-button-trick) - [flasher stops and warns you about firmware image](#flasher-stops-and-warns-you-about-firmware-image) - [Run the client](#run-the-client) - [Next steps](#next-steps) @@ -106,8 +107,16 @@ or proxmark3 /dev/ttyACM0 --flash --unlock-bootloader --image /tmp/my-bootrom.elf --image /tmp/my-fullimage.elf ``` -If the flasher can't detect your Proxmark3 (especially the very first time you flash a new device), force it to enter the bootloader mode as following: -With your Proxmark3 unplugged from your machine, press and hold the button on your Proxmark3 as you plug it into a USB port. You can release the button, two of the four LEDs should stay on. You're in bootloader mode, ready for the next step. In case the two LEDs don't stay on when you're releasing the button, you've a very old bootloader, start over and keep the button pressed during the whole flashing procedure. +### The button trick +^[Top](#top) + +If the flasher can't detect your Proxmark3 (especially the very first time you flash a new device), force it to enter the bootloader mode as following: + +With your Proxmark3 unplugged from your machine, press and hold the button on your Proxmark3 as you plug it into a USB port. +You can release the button, two of the four LEDs should stay on. +You're in bootloader mode, ready for the next step. + +In case the two LEDs don't stay on when you're releasing the button, you've a very old bootloader, start over and keep the button pressed during the whole flashing procedure. ## flasher stops and warns you about firmware image diff --git a/doc/md/Use_of_Proxmark/1_Validation.md b/doc/md/Use_of_Proxmark/1_Validation.md index 51a701388..a283b0d47 100644 --- a/doc/md/Use_of_Proxmark/1_Validation.md +++ b/doc/md/Use_of_Proxmark/1_Validation.md @@ -15,7 +15,7 @@ If all went well you should get some information about the firmware and memory usage as well as the prompt, something like this. ``` -[=] Session log /home/iceman/.proxmark3/logs/log_20220213.txt +[=] Session log /home/iceman/.proxmark3/logs/log_20230208.txt [+] loaded from JSON file /home/iceman/.proxmark3/preferences.json [=] Using UART port /dev/ttyS3 [=] Communicating with PM3 over USB-CDC @@ -28,48 +28,19 @@ If all went well you should get some information about the firmware and memory u 8888888P" 888 Y888P 888 "Y8b. 888 888 Y8P 888 888 888 888 888 " 888 Y88b d88P - 888 888 888 "Y8888P" [ Iceman ❄️ ] - + 888 888 888 "Y8888P" [ ☕ ] [ Proxmark3 RFID instrument ] - [ CLIENT ] - RRG/Iceman/master/v4.14831-269 2022-02-13 05:03:08 - compiled with............. GCC 10.3.0 - platform.................. Linux / x86_64 - Readline support.......... present - QT GUI support............ present - native BT support......... absent - Python script support..... present - Lua SWIG support.......... present - Python SWIG support....... present - - [ PROXMARK3 ] - device.................... RDV4 - firmware.................. RDV4 - external flash............ present - smartcard reader.......... present - FPC USART for BT add-on... absent - - [ ARM ] - bootrom: RRG/Iceman/master/v4.14831-269 2022-02-13 05:03:55 - os: RRG/Iceman/master/v4.14831-269 2022-02-13 05:03:49 - compiled with GCC 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] - - [ FPGA ] - LF image 2s30vq100 2022-03-20 09:28:32 - HF image 2s30vq100 2022-03-20 09:02:07 - HF FeliCa image 2s30vq100 2022-03-20 09:28:28 - HF 15 image 2s30vq100 2022-03-20 09:01:59 - - [ Hardware ] - --= uC: AT91SAM7S512 Rev A - --= Embedded Processor: ARM7TDMI - --= Internal SRAM size: 64K bytes - --= Architecture identifier: AT91SAM7Sxx Series - --= Embedded flash memory 512K bytes ( 59% used ) + MCU....... AT91SAM7S512 Rev A + Memory.... 512 Kb ( 66% used ) + Client.... Iceman/master/v4.16191 2023-02-08 22:54:30 + Bootrom... Iceman/master/v4.16191 2023-02-08 22:54:26 + OS........ Iceman/master/v4.16191 2023-02-08 22:54:27 + Target.... RDV4 + [usb] pm3 --> ``` diff --git a/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md b/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md index 175561062..791605f17 100644 --- a/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md +++ b/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md @@ -40,7 +40,7 @@ Set all t55xx settings to defaults (will set all 4 at once) To make sure you got the latest sim module firmware. -_Latest version is v3.11_ +_Latest version is v4.12_ ``` [usb] pm3 --> hw status @@ -51,27 +51,33 @@ Find version in the long output, look for these two lines ``` #db# Smart card module (ISO 7816) #db# version.................v2.06 + +or + +#db# Smart card module (ISO 7816) +#db# version.................v3.11 + ``` -This version is obsolete. +These versions is obsolete. -If you didn't download sim011.bin from the RRG Repo be aware that it might be corrupted or faulty. +If you didn't download sim013.bin from the RRG Repo be aware that it might be corrupted or faulty. You find a hash text file in this folder. It was generated with the following linux command. ``` -sha512sum -b sim011.bin > sim011.sha512.txt +sha512sum -b sim013.bin > sim013.sha512.txt ``` -You should validate the sim011.bin file against this hash file in order to be sure the file is not corrupted or faulty. +You should validate the sim013.bin file against this hash file in order to be sure the file is not corrupted or faulty. The following command upgrades your device sim module firmware. Don't not turn off your device during the execution of this command!! Even its a quite fast command you should be warned. You may brick it if you interrupt it. ``` -[usb] pm3 --> smart upgrade -f /usr/local/share/proxmark3/firmware/sim011.bin +[usb] pm3 --> smart upgrade -f /usr/local/share/proxmark3/firmware/sim013.bin # or if from local repo -[usb] pm3 --> smart upgrade -f sim011.bin +[usb] pm3 --> smart upgrade -f sim013.bin ``` You get the following output if the execution was successful: @@ -82,13 +88,13 @@ You get the following output if the execution was successful: [!] ⚠️ A dangerous command, do wrong and you could brick the sim module [=] ------------------------------------------------------------------- -[=] firmware file sim011.bin -[=] Checking integrity sim011.sha512.txt -[+] loaded 733 bytes from binary file sim011.bin -[+] loaded 141 bytes from binary file sim011.sha512.txt +[=] firmware file sim013.bin +[=] Checking integrity sim013.sha512.txt +[+] loaded 866 bytes from binary file sim013.bin +[+] loaded 141 bytes from binary file sim013.sha512.txt [=] Don't turn off your PM3! [+] Sim module firmware uploading to PM3... - 🕑 733 bytes sent + 🕑 864 bytes sent [+] Sim module firmware updating... [#] FW 0000 [#] FW 0080 @@ -96,6 +102,7 @@ You get the following output if the execution was successful: [#] FW 0180 [#] FW 0200 [#] FW 0280 +[#] FW 0300 [+] Sim module firmware upgrade successful ``` diff --git a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md index ba53d3aaf..5ead60b1a 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -10,7 +10,7 @@ - [PLATFORM](#platform) - [PLATFORM_EXTRAS](#platform_extras) - [STANDALONE](#standalone) - - [256kb versions](#256kb-versions) + - [256KB versions](#256KB-versions) - [Next step](#next-step) @@ -48,10 +48,10 @@ make SKIPBT=1 ## Firmware ^[Top](#top) -By default, the firmware is of course tuned for the Proxmark3 RDV4 device, which has built-in support for 256kb onboard flash SPI memory, Sim module (smart card support), FPC connector. +By default, the firmware is of course tuned for the Proxmark3 RDV4 device, which has built-in support for 256KB onboard flash SPI memory, Sim module (smart card support), FPC connector. These features make it very different from all other Proxmark3 devices, there is non other like this one. -**Recommendation**: if you don't have a RDV4, we strongly recommend your device to have at least a 512kb arm chip, since this repo is crossing 256kb limit. There is still a way to skip parts to make it fit on a 256kb device, see below. +**Recommendation**: if you don't have a RDV4, we strongly recommend your device to have at least a 512KB arm chip, since this repo is crossing 256KB limit. There is still a way to skip parts to make it fit on a 256KB device, see below. If you need to tune things and save the configuration, create a file `Makefile.platform` in the root directory of the repository, see `Makefile.platform.sample`. For an up-to-date exhaustive list of options, you can run `make PLATFORM=`. @@ -73,7 +73,7 @@ The MCU version (256 or 512) will be detected automatically during flashing. Known issues: -* 256kb Arm chip devices: The compiled firmware image from this repo may/will be too large for your device. +* 256KB Arm chip devices: The compiled firmware image from this repo may/will be too large for your device. * PM3 Evo: it has a different led/button pin assignment. It tends to be messed up. * Proxmark Pro: it has different fpga and unknown pin assignments. Unsupported. @@ -108,8 +108,10 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | LF_HIDBRUTE | HID corporate 1000 bruteforce - Federico dotta & Maurizio Agazzini | LF_HIDFCBRUTE | LF HID facility code bruteforce - ss23 | LF_ICEHID | LF HID collector to flashmem - Iceman1001 +| LF_NEDAP_SIM | LF Nedap ID simulator | LF_NEXID | Nexwatch credentials detection mode - jrjgjk & Zolorah | LF_PROXBRUTE | HID ProxII bruteforce - Brad Antoniewicz +| LF_PROX2BRUTE | HID ProxII bruteforce v2 - Yann Gascuel | LF_SAMYRUN (def)| HID26 read/clone/sim - Samy Kamkar | LF_SKELETON | standalone mode skeleton - Iceman1001 | LF_THAREXDE | LF EM4x50 simulator/read standalone mode - tharexde @@ -118,8 +120,8 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | HF_15SNIFF | 15693 sniff storing to flashmem - Glaser | HF_AVEFUL | MIFARE Ultralight read/simulation - Ave Ozkal | HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth storing in flashmem - Bogito -| HF_CRAFTBYTE | UID stealer - Emulates scanned 14a UID - Anze Jensterle | HF_COLIN | Mifare ultra fast sniff/sim/clone - Colin Brigato +| HF_CRAFTBYTE | UID stealer - Emulates scanned 14a UID - Anze Jensterle | HF_ICECLASS | iCLASS 4-1 mode sim/read & dump/loclass/glitch & config to flashmem - Iceman1001 | HF_LEGIC | HF Legic Prime Read/Store/Sim standalone - uhei | HF_LEGICSIM | HF Legic Prime Simulate standalone - uhei @@ -134,10 +136,10 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo By default `STANDALONE=LF_SAMYRUN`. -## 256kb versions +## 256KB versions ^[Top](#top) -If you own a Proxmark3 Easy with only 256kb, you can use a few definitions to help you getting a smaller firmware. +If you own a Proxmark3 Easy with only 256KB, you can use a few definitions to help you getting a smaller firmware. First thing is of course to use the `PLATFORM=PM3GENERIC`. Adding `PLATFORM_SIZE=256` will provoke an error during compilation of the recovery image if your image is too big, so you can detect the problem before trying to flash the Proxmark3, e.g. @@ -153,22 +155,22 @@ a series of `SKIP_*` allow to skip some of the functionalities and to get a smal | Definitions | Rough estimation of the saved space | |---------------------|-------------------------------------| -|STANDALONE= | 3.6kb -|SKIP_LF=1 | 25.8kb -|SKIP_HITAG=1 | 24.2kb -|SKIP_EM4x50=1 | 2.9kb -|SKIP_ISO15693=1 | 3.2kb -|SKIP_LEGICRF=1 | 3.9kb -|SKIP_ISO14443b=1 | 3.7kb -|SKIP_ISO14443a=1 | 63.0kb -|SKIP_ICLASS=1 | 10.5kb -|SKIP_FELICA=1 | 4.0kb -|SKIP_NFCBARCODE=1 | 1.4kb -|SKIP_HFSNIFF=1 | 0.5kb -|SKIP_HFPLOT=1 | 0.3kb -|SKIP_ZX8211=1 | unknown yet +|STANDALONE= | 3.6KB +|SKIP_LF=1 | 25.8KB +|SKIP_HITAG=1 | 24.2KB +|SKIP_EM4x50=1 | 2.9KB +|SKIP_ISO15693=1 | 3.2KB +|SKIP_LEGICRF=1 | 3.9KB +|SKIP_ISO14443b=1 | 3.7KB +|SKIP_ISO14443a=1 | 63.0KB +|SKIP_ICLASS=1 | 10.5KB +|SKIP_FELICA=1 | 4.0KB +|SKIP_NFCBARCODE=1 | 1.4KB +|SKIP_HFSNIFF=1 | 0.5KB +|SKIP_HFPLOT=1 | 0.3KB +|SKIP_ZX8211=1 | 0.3KB -So for example, at the time of writing, this is a valid `Makefile.platform` compiling an image for 256k: +So for example, at the time of writing, this is a valid `Makefile.platform` compiling an image for 256KB: ``` PLATFORM=PM3GENERIC PLATFORM_SIZE=256 diff --git a/doc/path_notes.md b/doc/path_notes.md index 8f0d20b88..a111e8cd1 100644 --- a/doc/path_notes.md +++ b/doc/path_notes.md @@ -22,7 +22,7 @@ - [Gory details](#gory-details) - [Scripts](#scripts) - [Proxmark command script (.cmd)](#proxmark-command-script-cmd) - - [Shebangs (on *nix)](#shebangs-on-nix) + - [Shebangs (on \*nix)](#shebangs-on-nix) With the recent (2019-09-01) changes and creation of `make install` command it is easy to get lost. @@ -64,7 +64,7 @@ The recovery / firmware files will be copied to ``` * Proxmark3 firmware: `bootrom.elf`, `fullimage.elf`, `proxmark3_recovery.bin` (used for JTAG) -* SIM firmware: `sim011.bin`, `sim011.sha512.txt` +* SIM firmware: `sim013.bin`, `sim013.sha512.txt` ## Traces diff --git a/doc/termux_notes.md b/doc/termux_notes.md index 98cbdcb25..852c3cdc1 100644 --- a/doc/termux_notes.md +++ b/doc/termux_notes.md @@ -30,10 +30,11 @@ ^[Top](#top) - Android phone -- [Termux](https://play.google.com/store/apps/details?id=com.termux) -- Proxmark3 RDV4 (https://www.proxmark.com/proxmark-3-hardware/proxmark-3-rdv4) -- Blueshark Standalone Module (Bluetooth ONLY) (https://www.proxmark.com/proxmark-news/proxmark3-blueshark-bluetooth-released) -- Proxmark with BTADDON compiled Firmware (Bluetooth ONLY) (https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform_extras) +- [F-Droid](https://f-droid.org/) +- [Termux](https://f-droid.org/en/packages/com.termux/) +- [Proxmark3 RDV4](https://www.proxmark.com/proxmark-3-hardware/proxmark-3-rdv4) +- [Blueshark Standalone Module](https://www.proxmark.com/proxmark-news/proxmark3-blueshark-bluetooth-released) **(ONLY if using Bluetooth)** +- [Proxmark with BTADDON compiled Firmware](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform_extras) **(ONLY if using Bluetooth)** ## Notes @@ -48,8 +49,9 @@ ref : https://github.com/Proxmark/proxmark3/wiki/android ### Setting up Termux ^[Top](#top) -Install [Termux](https://f-droid.org/en/packages/com.termux/) and start it +Use [F-Droid](https://f-droid.org/) to install [Termux](https://f-droid.org/en/packages/com.termux/) and start it. +It is recommended to use the F-Droid version of Termux as it will be the latest. The [Play Store version](https://play.google.com/store/apps/details?id=com.termux) is not maintained (as stated in the description: "Updates over Google Play [are] currently halted due to technical reasons"). ### Install Proxmark3 package which follows tagged releases ^[Top](#top) diff --git a/docker/archlinux/Dockerfile b/docker/archlinux/Dockerfile index f822b33cc..93584c098 100644 --- a/docker/archlinux/Dockerfile +++ b/docker/archlinux/Dockerfile @@ -7,6 +7,12 @@ RUN pacman-db-upgrade # bluez skipped, can't be installed in docker RUN pacman -S --noconfirm sudo git base-devel cmake libusb readline bzip2 arm-none-eabi-gcc arm-none-eabi-newlib python --needed +RUN pacman -S --noconfirm python-pip +RUN python3 -m pip install ansicolors sslcrypto + +# OpenCL for hitag2crack +RUN pacman -S --noconfirm ocl-icd + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/archlinux/README.md b/docker/archlinux/README.md index 72756a91e..872d46cb4 100644 --- a/docker/archlinux/README.md +++ b/docker/archlinux/README.md @@ -27,12 +27,25 @@ sudo pacman -S testing/gcc sudo pacman -S gcc ``` -# Notes to run tests +# Notes on run_tests.sh script +This script does both setup the mirrors and pip install and then run a +bunch of different builds with make and cmake together with the different combos +of RDV4, GENERIC, BTADDON combos. -Add first the mirrors, see above +If all tests OK, the script will finish with PASS. + + +# Notes to run tests +Add first the mirrors, see above, if needed. + +The release test build script is to be run in proxmark root folder inside the docker env. +``` +docker/archlinux/run_tests.sh; +``` + +Or if you want to run single test, ``` -sudo pacman -S python-pip -python3 -m pip install ansicolors sslcrypto +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/archlinux/docker_run.sh b/docker/archlinux/docker_run.sh index eac7e1378..f379c5307 100755 --- a/docker/archlinux/docker_run.sh +++ b/docker/archlinux/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-arch:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-arch:1.0 diff --git a/docker/archlinux/run_tests.sh b/docker/archlinux/run_tests.sh new file mode 100755 index 000000000..53eeda9a3 --- /dev/null +++ b/docker/archlinux/run_tests.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/archlinux/run_tests.sh; +# +# Script contains two phases. +# +# -- Init / setup phase +# Script to be run inside docker env. First install some dependencies for docker image. +# +# -- Build phase begins +# make builds +# cmake client builds +# of the different possible PLATFORM (PM3RDV4 / PM3GENERIC) and BTADDON combos + +cat << EOF |sudo tee -a /etc/pacman.conf + +[testing] +Include = /etc/pacman.d/mirrorlist + +[community-testing] +Include = /etc/pacman.d/mirrorlist + +[staging] +Include = /etc/pacman.d/mirrorlist +EOF + +sudo pacman -Syu + +# search available versions +pacman -Ss '^arm-none-eabi-gcc$' +pacman -Ss '^gcc$' + +# depending on where the latest bleeding edge is: +# sudo pacman -S community-testing/arm-none-eabi-gcc +# sudo pacman -S arm-none-eabi-gcc +# sudo pacman -S staging/gcc +# sudo pacman -S testing/gcc +# sudo pacman -S gcc + +tools/release_tests.sh diff --git a/docker/build-all.sh b/docker/build-all.sh index 3116d96dc..aba45382f 100755 --- a/docker/build-all.sh +++ b/docker/build-all.sh @@ -1,5 +1,5 @@ #!/bin/bash -for os in archlinux debian-buster fedora-34 fedora-35 homebrew kali opensuse-leap opensuse-tumbleweed parrot-core-latest ubuntu-18.04 ubuntu-20.04 ubuntu-21.04; do +for os in archlinux debian-buster fedora-36 fedora-37 homebrew kali opensuse-leap opensuse-tumbleweed parrot-core-latest ubuntu-18.04 ubuntu-20.04 ubuntu-22.04; do ( cd $os && ./docker_build.sh ) done diff --git a/docker/debian-bullseye/Dockerfile b/docker/debian-bullseye/Dockerfile index a23d32728..ff456b558 100644 --- a/docker/debian-bullseye/Dockerfile +++ b/docker/debian-bullseye/Dockerfile @@ -4,10 +4,19 @@ ENV LANG C ENV DEBIAN_FRONTEND noninteractive # qtbase5-dev skipped RUN apt-get update && \ + apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev libbluetooth-dev libpython3-dev libssl-dev sudo && \ apt-get clean +RUN apt-get install -y python3-minimal && \ + apt-get install -y python3-pip && \ + apt-get clean && \ + python3 -m pip install ansicolors sslcrypto + +RUN apt-get install -y opencl-dev && \ + apt-get clean + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/debian-bullseye/README.md b/docker/debian-bullseye/README.md index c6fce658c..e86881019 100644 --- a/docker/debian-bullseye/README.md +++ b/docker/debian-bullseye/README.md @@ -1,9 +1,21 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + # Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/debian-bullseye/run_tests.sh; +``` + +Or if you want to run single test, ``` sudo apt update -sudo apt install python3-minimal -sudo apt install python3-pip -python3 -m pip install ansicolors sslcrypto +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/debian-bullseye/docker_run.sh b/docker/debian-bullseye/docker_run.sh index abe9dce77..1216f54cd 100755 --- a/docker/debian-bullseye/docker_run.sh +++ b/docker/debian-bullseye/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-debian-bullseye:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-debian-bullseye:1.0 diff --git a/docker/debian-bullseye/run_tests.sh b/docker/debian-bullseye/run_tests.sh new file mode 100755 index 000000000..25e6d2a21 --- /dev/null +++ b/docker/debian-bullseye/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/debian-bullseye/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +tools/release_tests.sh diff --git a/docker/fedora-34/README.md b/docker/fedora-34/README.md deleted file mode 100644 index 5fccbba41..000000000 --- a/docker/fedora-34/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Notes to run tests - -``` -sudo yum -y update -sudo yum -y install cmake python-pip -python3 -m pip install ansicolors sslcrypto -tools/pm3_tests.sh --long -``` - -Warning, `recover_pk selftests` will fail on Fedora because they stripped down the available ECC curves in their OpenSSL. - -So just comment the "recover_pk test" diff --git a/docker/fedora-34/docker_build.sh b/docker/fedora-34/docker_build.sh deleted file mode 100755 index 753d223a8..000000000 --- a/docker/fedora-34/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-fedora-34:1.0" . diff --git a/docker/fedora-34/docker_rm.sh b/docker/fedora-34/docker_rm.sh deleted file mode 100644 index eec1ba46c..000000000 --- a/docker/fedora-34/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker image rm pm3-fedora-34:1.0 -docker image rm fedora:34 diff --git a/docker/fedora-34/docker_run.sh b/docker/fedora-34/docker_run.sh deleted file mode 100755 index 6fbb2e6a9..000000000 --- a/docker/fedora-34/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-fedora-34:1.0 diff --git a/docker/fedora-35/README.md b/docker/fedora-35/README.md deleted file mode 100644 index 5fccbba41..000000000 --- a/docker/fedora-35/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Notes to run tests - -``` -sudo yum -y update -sudo yum -y install cmake python-pip -python3 -m pip install ansicolors sslcrypto -tools/pm3_tests.sh --long -``` - -Warning, `recover_pk selftests` will fail on Fedora because they stripped down the available ECC curves in their OpenSSL. - -So just comment the "recover_pk test" diff --git a/docker/fedora-35/docker_build.sh b/docker/fedora-35/docker_build.sh deleted file mode 100755 index 57eca95c0..000000000 --- a/docker/fedora-35/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-fedora-35:1.0" . diff --git a/docker/fedora-35/docker_rm.sh b/docker/fedora-35/docker_rm.sh deleted file mode 100644 index 100bd2690..000000000 --- a/docker/fedora-35/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker image rm pm3-fedora-35:1.0 -docker image rm fedora:35 diff --git a/docker/fedora-35/docker_run.sh b/docker/fedora-35/docker_run.sh deleted file mode 100755 index aabf0b67a..000000000 --- a/docker/fedora-35/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-fedora-35:1.0 diff --git a/docker/fedora-35/Dockerfile b/docker/fedora-36/Dockerfile similarity index 60% rename from docker/fedora-35/Dockerfile rename to docker/fedora-36/Dockerfile index 6397d60c4..007af68c3 100644 --- a/docker/fedora-35/Dockerfile +++ b/docker/fedora-36/Dockerfile @@ -1,8 +1,14 @@ -FROM fedora:35 +FROM fedora:36 ENV LANG C # qt5-qtbase-devel skipped -RUN dnf install -y passwd sudo git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib readline-devel bzip2-devel bluez-libs-devel python3-devel openssl-devel libatomic +RUN dnf install -y passwd sudo git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib readline-devel bzip2-devel bluez-libs-devel python3-devel openssl-devel libatomic findutils + +RUN yum -y update +RUN yum -y install cmake python-pip +RUN python3 -m pip install ansicolors sslcrypto + +RUN yum -y install mesa-libOpenCL ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg diff --git a/docker/fedora-36/README.md b/docker/fedora-36/README.md new file mode 100644 index 000000000..6648fb28d --- /dev/null +++ b/docker/fedora-36/README.md @@ -0,0 +1,21 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/fedora-36/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo yum -y update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-36/docker_build.sh b/docker/fedora-36/docker_build.sh new file mode 100755 index 000000000..1a2e2d392 --- /dev/null +++ b/docker/fedora-36/docker_build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t "pm3-fedora-36:1.0" . diff --git a/docker/fedora-36/docker_rm.sh b/docker/fedora-36/docker_rm.sh new file mode 100644 index 000000000..ea2b28809 --- /dev/null +++ b/docker/fedora-36/docker_rm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +docker image rm pm3-fedora-36:1.0 +docker image rm fedora:36 diff --git a/docker/fedora-36/docker_run.sh b/docker/fedora-36/docker_run.sh new file mode 100755 index 000000000..0e6e69925 --- /dev/null +++ b/docker/fedora-36/docker_run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-fedora-36:1.0 diff --git a/docker/fedora-36/run_tests.sh b/docker/fedora-36/run_tests.sh new file mode 100755 index 000000000..05cdb7da8 --- /dev/null +++ b/docker/fedora-36/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/fedora-36/run_tests.sh; + +sudo yum -y update +tools/release_tests.sh diff --git a/docker/fedora-34/Dockerfile b/docker/fedora-37/Dockerfile similarity index 60% rename from docker/fedora-34/Dockerfile rename to docker/fedora-37/Dockerfile index 444b5b195..930767853 100644 --- a/docker/fedora-34/Dockerfile +++ b/docker/fedora-37/Dockerfile @@ -1,8 +1,14 @@ -FROM fedora:34 +FROM fedora:37 ENV LANG C # qt5-qtbase-devel skipped -RUN dnf install -y passwd sudo git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib readline-devel bzip2-devel bluez-libs-devel python3-devel openssl-devel libatomic +RUN dnf install -y passwd sudo git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib readline-devel bzip2-devel bluez-libs-devel python3-devel openssl-devel libatomic findutils + +RUN yum -y update +RUN yum -y install cmake python-pip +RUN python3 -m pip install ansicolors sslcrypto + +RUN yum -y install mesa-libOpenCL ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg diff --git a/docker/fedora-37/README.md b/docker/fedora-37/README.md new file mode 100644 index 000000000..79c88e040 --- /dev/null +++ b/docker/fedora-37/README.md @@ -0,0 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/fedora-37/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo yum -y update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-37/docker_build.sh b/docker/fedora-37/docker_build.sh new file mode 100755 index 000000000..5e3049b68 --- /dev/null +++ b/docker/fedora-37/docker_build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t "pm3-fedora-37:1.0" . diff --git a/docker/fedora-37/docker_rm.sh b/docker/fedora-37/docker_rm.sh new file mode 100644 index 000000000..a9359d0fd --- /dev/null +++ b/docker/fedora-37/docker_rm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +docker image rm pm3-fedora-37:1.0 +docker image rm fedora:37 diff --git a/docker/fedora-37/docker_run.sh b/docker/fedora-37/docker_run.sh new file mode 100755 index 000000000..eb51525b7 --- /dev/null +++ b/docker/fedora-37/docker_run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-fedora-37:1.0 diff --git a/docker/fedora-37/run_tests.sh b/docker/fedora-37/run_tests.sh new file mode 100755 index 000000000..05cdb7da8 --- /dev/null +++ b/docker/fedora-37/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/fedora-36/run_tests.sh; + +sudo yum -y update +tools/release_tests.sh diff --git a/docker/homebrew/docker_run.sh b/docker/homebrew/docker_run.sh index 349df8a26..16c744594 100755 --- a/docker/homebrew/docker_run.sh +++ b/docker/homebrew/docker_run.sh @@ -1,4 +1,4 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/linuxbrew/proxmark3 -it pm3-brew:1.0 +docker run --volume=$(pwd)/../..:/home/linuxbrew/proxmark3 -w /home/rrg/proxmark3 -it pm3-brew:1.0 # if needed, run brew as user linuxbrew diff --git a/docker/kali/Dockerfile b/docker/kali/Dockerfile index 72916c7b5..79d4bb9a6 100644 --- a/docker/kali/Dockerfile +++ b/docker/kali/Dockerfile @@ -4,10 +4,19 @@ ENV LANG C ENV DEBIAN_FRONTEND noninteractive # qtbase5-dev skipped RUN apt-get update && \ + apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev libbluetooth-dev libpython3-dev libssl-dev sudo && \ apt-get clean +RUN apt-get install -y python3-minimal && \ + apt-get install -y python3-pip && \ + apt-get clean && \ + python3 -m pip install ansicolors sslcrypto + +RUN apt-get install -y opencl-dev && \ + apt-get clean + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/kali/README.md b/docker/kali/README.md index f586fe064..dbf1f7f50 100644 --- a/docker/kali/README.md +++ b/docker/kali/README.md @@ -1,9 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + # Notes to run tests +The script is to be run in proxmark root folder inside the docker env. +``` +docker/kali/run_tests.sh; +``` + +Or if you want to run single test, ``` sudo apt update -sudo apt install -y python3-minimal -sudo apt install -y python3-pip -python3 -m pip install ansicolors sslcrypto +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/kali/docker_run.sh b/docker/kali/docker_run.sh index 5d9dcf13d..7124fe5b5 100755 --- a/docker/kali/docker_run.sh +++ b/docker/kali/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-kali:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-kali:1.0 diff --git a/docker/kali/run_tests.sh b/docker/kali/run_tests.sh new file mode 100755 index 000000000..dec20763b --- /dev/null +++ b/docker/kali/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/kali/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +tools/release_tests.sh diff --git a/docker/opensuse-leap/Dockerfile b/docker/opensuse-leap/Dockerfile index 97eff3030..1af777d23 100644 --- a/docker/opensuse-leap/Dockerfile +++ b/docker/opensuse-leap/Dockerfile @@ -4,6 +4,15 @@ ENV LANG C # libqt5-qtbase-devel skipped RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel bluez-devel python3-devel libopenssl-devel +RUN zypper addrepo https://download.opensuse.org/repositories/home:wkazubski/15.4/home:wkazubski.repo && \ + zypper --gpg-auto-import-keys refresh && \ + zypper --non-interactive install cross-arm-none-eabi-gcc12 cross-arm-none-eabi-newlib + +RUN zypper --non-interactive install cmake python3 python3-pip && \ + python3 -m pip install ansicolors sslcrypto + +RUN zypper --non-interactive install ocl-icd-devel + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/opensuse-leap/README.md b/docker/opensuse-leap/README.md index 87ca26f4d..ecda34706 100644 --- a/docker/opensuse-leap/README.md +++ b/docker/opensuse-leap/README.md @@ -1,11 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + # Notes to run tests - -No ARM compiler available ? +The script is to be run in proxmark root folder inside the docker env. ``` -sudo zypper --non-interactive install cmake -sudo zypper --non-interactive install python3 -sudo zypper --non-interactive install python3-pip -python3 -m pip install ansicolors sslcrypto -tools/pm3_tests.sh --long mfkey nonce2key mf_nonce_brute fpga_compress common client +docker/opensuse-leap/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo zypper refresh && sudo zypper --non-interactive update +make clean; make -j +tools/pm3_tests.sh --long ``` diff --git a/docker/opensuse-leap/docker_run.sh b/docker/opensuse-leap/docker_run.sh index 66e7b7ce2..f3c830626 100755 --- a/docker/opensuse-leap/docker_run.sh +++ b/docker/opensuse-leap/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-suse-leap:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-suse-leap:1.0 diff --git a/docker/opensuse-leap/run_tests.sh b/docker/opensuse-leap/run_tests.sh new file mode 100755 index 000000000..d2d49211b --- /dev/null +++ b/docker/opensuse-leap/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/opensuse-leap/run_tests.sh; + +sudo zypper refresh && sudo zypper --non-interactive update +tools/release_tests.sh diff --git a/docker/opensuse-tumbleweed/Dockerfile b/docker/opensuse-tumbleweed/Dockerfile index 007f98455..169d087c3 100644 --- a/docker/opensuse-tumbleweed/Dockerfile +++ b/docker/opensuse-tumbleweed/Dockerfile @@ -2,7 +2,16 @@ FROM opensuse/tumbleweed ENV LANG C # libqt5-qtbase-devel skipped -RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel bluez-devel python3-devel libopenssl-devel cross-arm-none-gcc11 cross-arm-none-newlib-devel +RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel bluez-devel python3-devel libopenssl-devel cross-arm-none-gcc12 cross-arm-none-newlib-devel + +#RUN zypper addrepo https://download.opensuse.org/repositories/home:wkazubski/openSUSE_Tumbleweed/home:wkazubski.repo && \ +# zypper --gpg-auto-import-keys refresh && \ +# zypper --non-interactive install cross-arm-none-eabi-gcc12 cross-arm-none-eabi-newlib + +RUN zypper --non-interactive install cmake python3 python3-pip && \ + python3 -m pip install ansicolors sslcrypto + +RUN zypper --non-interactive install ocl-icd-devel # Create rrg user RUN useradd -ms /bin/bash rrg diff --git a/docker/opensuse-tumbleweed/README.md b/docker/opensuse-tumbleweed/README.md index b1456b1bb..bd55e0c9d 100644 --- a/docker/opensuse-tumbleweed/README.md +++ b/docker/opensuse-tumbleweed/README.md @@ -1,9 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + # Notes to run tests +The script is to be run in proxmark root folder inside the docker env. ``` -sudo zypper --non-interactive install cmake -sudo zypper --non-interactive install python3 -sudo zypper --non-interactive install python3-pip -python3 -m pip install ansicolors sslcrypto +docker/opensuse-tumbleweed/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo zypper refresh && sudo zypper --non-interactive update +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/opensuse-tumbleweed/docker_run.sh b/docker/opensuse-tumbleweed/docker_run.sh index 047afd9b7..79ef74d5c 100755 --- a/docker/opensuse-tumbleweed/docker_run.sh +++ b/docker/opensuse-tumbleweed/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-suse-tumbleweed:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-suse-tumbleweed:1.0 diff --git a/docker/opensuse-tumbleweed/run_tests.sh b/docker/opensuse-tumbleweed/run_tests.sh new file mode 100755 index 000000000..9002131e9 --- /dev/null +++ b/docker/opensuse-tumbleweed/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/opensuse-tumbleweed/run_tests.sh; + +sudo zypper refresh && sudo zypper --non-interactive update +tools/release_tests.sh diff --git a/docker/parrot-core-latest/Dockerfile b/docker/parrot-core-latest/Dockerfile index aef00ff54..a33af1d4c 100644 --- a/docker/parrot-core-latest/Dockerfile +++ b/docker/parrot-core-latest/Dockerfile @@ -4,10 +4,19 @@ ENV LANG C ENV DEBIAN_FRONTEND noninteractive # qtbase5-dev skipped RUN apt-get update && \ + apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev libbluetooth-dev libpython3-dev libssl-dev sudo && \ apt-get clean +RUN apt-get install -y python3-minimal && \ + apt-get install -y python3-pip && \ + apt-get clean && \ + python3 -m pip install ansicolors sslcrypto + +RUN apt-get install -y opencl-dev && \ + apt-get clean + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/parrot-core-latest/README.md b/docker/parrot-core-latest/README.md index f586fe064..679c3a1eb 100644 --- a/docker/parrot-core-latest/README.md +++ b/docker/parrot-core-latest/README.md @@ -1,9 +1,21 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + # Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/parrot-core-latest/run_tests.sh; +``` + +Or if you want to run single test, ``` sudo apt update -sudo apt install -y python3-minimal -sudo apt install -y python3-pip -python3 -m pip install ansicolors sslcrypto +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/parrot-core-latest/docker_run.sh b/docker/parrot-core-latest/docker_run.sh index 4f33acfe9..509df4461 100755 --- a/docker/parrot-core-latest/docker_run.sh +++ b/docker/parrot-core-latest/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-parrotsec-core-latest:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-parrotsec-core-latest:1.0 diff --git a/docker/parrot-core-latest/run_tests.sh b/docker/parrot-core-latest/run_tests.sh new file mode 100755 index 000000000..65b0737c4 --- /dev/null +++ b/docker/parrot-core-latest/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/parrot-core-latest/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +tools/release_tests.sh diff --git a/docker/ubuntu-18.04/Dockerfile b/docker/ubuntu-18.04/Dockerfile index 107a851a7..29c958a4d 100644 --- a/docker/ubuntu-18.04/Dockerfile +++ b/docker/ubuntu-18.04/Dockerfile @@ -5,10 +5,19 @@ ENV DEBIAN_FRONTEND noninteractive # qtbase5-dev skipped # python3 skipped, not yet searchable with pkg-config python3 RUN apt-get update && \ + apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev libbluetooth-dev libssl-dev sudo && \ apt-get clean +RUN apt-get install -y python3-minimal && \ + apt-get install -y python3-pip && \ + apt-get clean && \ + python3 -m pip install ansicolors sslcrypto + +RUN apt-get install -y opencl-dev && \ + apt-get clean + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/ubuntu-18.04/README.md b/docker/ubuntu-18.04/README.md index f586fe064..6cc3b9ef2 100644 --- a/docker/ubuntu-18.04/README.md +++ b/docker/ubuntu-18.04/README.md @@ -1,9 +1,21 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + # Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/ubuntu-18.04/run_tests.sh; +``` + +Or if you want to run single test, ``` sudo apt update -sudo apt install -y python3-minimal -sudo apt install -y python3-pip -python3 -m pip install ansicolors sslcrypto +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/ubuntu-18.04/docker_run.sh b/docker/ubuntu-18.04/docker_run.sh index 6827b69ac..01d133712 100755 --- a/docker/ubuntu-18.04/docker_run.sh +++ b/docker/ubuntu-18.04/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-ubuntu-18.04:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-ubuntu-18.04:1.0 diff --git a/docker/ubuntu-18.04/run_tests.sh b/docker/ubuntu-18.04/run_tests.sh new file mode 100755 index 000000000..1efdbc060 --- /dev/null +++ b/docker/ubuntu-18.04/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-18.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +tools/release_tests.sh diff --git a/docker/ubuntu-20.04/Dockerfile b/docker/ubuntu-20.04/Dockerfile index 5c3fab542..72f6ec6cf 100644 --- a/docker/ubuntu-20.04/Dockerfile +++ b/docker/ubuntu-20.04/Dockerfile @@ -4,10 +4,19 @@ ENV LANG C ENV DEBIAN_FRONTEND noninteractive # qtbase5-dev skipped RUN apt-get update && \ + apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev libbluetooth-dev libpython3-dev libssl-dev sudo && \ apt-get clean +RUN apt-get install -y python3-minimal && \ + apt-get install -y python3-pip && \ + apt-get clean && \ + python3 -m pip install ansicolors sslcrypto + +RUN apt-get install -y opencl-dev && \ + apt-get clean + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/ubuntu-20.04/README.md b/docker/ubuntu-20.04/README.md index f586fe064..4c432eff1 100644 --- a/docker/ubuntu-20.04/README.md +++ b/docker/ubuntu-20.04/README.md @@ -1,9 +1,21 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + # Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/ubuntu-20.04/run_tests.sh; +``` + +Or if you want to run single test, ``` sudo apt update -sudo apt install -y python3-minimal -sudo apt install -y python3-pip -python3 -m pip install ansicolors sslcrypto +make clean; make -j tools/pm3_tests.sh --long ``` diff --git a/docker/ubuntu-20.04/docker_run.sh b/docker/ubuntu-20.04/docker_run.sh index bb68ba6f8..dd05c9f79 100755 --- a/docker/ubuntu-20.04/docker_run.sh +++ b/docker/ubuntu-20.04/docker_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-ubuntu-20.04:1.0 +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-ubuntu-20.04:1.0 diff --git a/docker/ubuntu-20.04/run_tests.sh b/docker/ubuntu-20.04/run_tests.sh new file mode 100755 index 000000000..aa98bc327 --- /dev/null +++ b/docker/ubuntu-20.04/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-20.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +tools/release_tests.sh diff --git a/docker/ubuntu-21.04/README.md b/docker/ubuntu-21.04/README.md deleted file mode 100644 index f586fe064..000000000 --- a/docker/ubuntu-21.04/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Notes to run tests - -``` -sudo apt update -sudo apt install -y python3-minimal -sudo apt install -y python3-pip -python3 -m pip install ansicolors sslcrypto -tools/pm3_tests.sh --long -``` diff --git a/docker/ubuntu-21.04/docker_build.sh b/docker/ubuntu-21.04/docker_build.sh deleted file mode 100755 index 70e0349f8..000000000 --- a/docker/ubuntu-21.04/docker_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker build -t "pm3-ubuntu-21.04:1.0" . diff --git a/docker/ubuntu-21.04/docker_rm.sh b/docker/ubuntu-21.04/docker_rm.sh deleted file mode 100644 index bb848bfe0..000000000 --- a/docker/ubuntu-21.04/docker_rm.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -docker image rm pm3-ubuntu-21.04:1.0 -docker image rm ubuntu:21.04 diff --git a/docker/ubuntu-21.04/docker_run.sh b/docker/ubuntu-21.04/docker_run.sh deleted file mode 100755 index ddd958144..000000000 --- a/docker/ubuntu-21.04/docker_run.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -it pm3-ubuntu-21.04:1.0 diff --git a/docker/ubuntu-21.04/Dockerfile b/docker/ubuntu-22.04/Dockerfile similarity index 66% rename from docker/ubuntu-21.04/Dockerfile rename to docker/ubuntu-22.04/Dockerfile index 102f0c556..9d867e785 100644 --- a/docker/ubuntu-21.04/Dockerfile +++ b/docker/ubuntu-22.04/Dockerfile @@ -1,13 +1,22 @@ -FROM ubuntu:21.04 +FROM ubuntu:22.04 ENV LANG C ENV DEBIAN_FRONTEND noninteractive # qtbase5-dev skipped RUN apt-get update && \ + apt-get upgrade -y && \ apt-get dist-upgrade -y && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev libbluetooth-dev libpython3-dev libssl-dev sudo && \ apt-get clean +RUN apt-get install -y python3-minimal && \ + apt-get install -y python3-pip && \ + apt-get clean && \ + python3 -m pip install ansicolors sslcrypto + +RUN apt-get install -y opencl-dev && \ + apt-get clean + # Create rrg user RUN useradd -ms /bin/bash rrg RUN passwd -d rrg diff --git a/docker/ubuntu-22.04/README.md b/docker/ubuntu-22.04/README.md new file mode 100644 index 000000000..93d4462ad --- /dev/null +++ b/docker/ubuntu-22.04/README.md @@ -0,0 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/ubuntu-22.04/run_tests.sh; +``` + +Or if you want to run single test, +``` +sudo apt update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/ubuntu-22.04/docker_build.sh b/docker/ubuntu-22.04/docker_build.sh new file mode 100755 index 000000000..1cfd6c10a --- /dev/null +++ b/docker/ubuntu-22.04/docker_build.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker build -t "pm3-ubuntu-22.04:1.0" . diff --git a/docker/ubuntu-22.04/docker_rm.sh b/docker/ubuntu-22.04/docker_rm.sh new file mode 100644 index 000000000..e6a5f0302 --- /dev/null +++ b/docker/ubuntu-22.04/docker_rm.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +docker image rm pm3-ubuntu-22.04:1.0 +docker image rm ubuntu:22.04 diff --git a/docker/ubuntu-22.04/docker_run.sh b/docker/ubuntu-22.04/docker_run.sh new file mode 100755 index 000000000..04f8d99a0 --- /dev/null +++ b/docker/ubuntu-22.04/docker_run.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +docker run --volume=$(pwd)/../..:/home/rrg/proxmark3 -w /home/rrg/proxmark3 -it pm3-ubuntu-22.04:1.0 diff --git a/docker/ubuntu-22.04/run_tests.sh b/docker/ubuntu-22.04/run_tests.sh new file mode 100755 index 000000000..4f8ce55ea --- /dev/null +++ b/docker/ubuntu-22.04/run_tests.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-22.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +tools/release_tests.sh diff --git a/driver/77-pm3-usb-device-blacklist-dialout.rules b/driver/77-pm3-usb-device-blacklist-dialout.rules new file mode 100644 index 000000000..8d699faca --- /dev/null +++ b/driver/77-pm3-usb-device-blacklist-dialout.rules @@ -0,0 +1,22 @@ +# Proxmark3 linux modem-manager de-confliction file +# +# copy this file to /etc/udev/rules.d (or add the entry to the end of an existing file) +# and restart udev hotplug: +# +# 'sudo udevadm control --reload-rules' +# + +# proxmark3 +ACTION!="add|change", GOTO="pm3_usb_device_blacklist_end" +SUBSYSTEM!="tty", GOTO="pm3_ignore" + +ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" MODE="660" GROUP="dialout" +ATTRS{idVendor}=="9ac4" ATTRS{idProduct}=="4b8f", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" MODE="660" GROUP="dialout" +ATTRS{idVendor}=="502d" ATTRS{idProduct}=="502d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" MODE="660" GROUP="dialout" + +LABEL="pm3_ignore" +ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" +ATTRS{idVendor}=="9ac4" ATTRS{idProduct}=="4b8f", ENV{ID_MM_DEVICE_IGNORE}="1" +ATTRS{idVendor}=="502d" ATTRS{idProduct}=="502d", ENV{ID_MM_DEVICE_IGNORE}="1" + +LABEL="pm3_usb_device_blacklist_end" diff --git a/driver/77-pm3-usb-device-blacklist.rules b/driver/77-pm3-usb-device-blacklist-uucp.rules similarity index 82% rename from driver/77-pm3-usb-device-blacklist.rules rename to driver/77-pm3-usb-device-blacklist-uucp.rules index d939468f2..101381d95 100644 --- a/driver/77-pm3-usb-device-blacklist.rules +++ b/driver/77-pm3-usb-device-blacklist-uucp.rules @@ -10,9 +10,9 @@ ACTION!="add|change", GOTO="pm3_usb_device_blacklist_end" SUBSYSTEM!="tty", GOTO="pm3_ignore" -ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" -ATTRS{idVendor}=="9ac4" ATTRS{idProduct}=="4b8f", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" -ATTRS{idVendor}=="502d" ATTRS{idProduct}=="502d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" +ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" MODE="660" GROUP="uucp" +ATTRS{idVendor}=="9ac4" ATTRS{idProduct}=="4b8f", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" MODE="660" GROUP="uucp" +ATTRS{idVendor}=="502d" ATTRS{idProduct}=="502d", ENV{ID_MM_DEVICE_IGNORE}="1" SYMLINK+="pm3-%n" MODE="660" GROUP="uucp" LABEL="pm3_ignore" ATTRS{idVendor}=="2d2d" ATTRS{idProduct}=="504d", ENV{ID_MM_DEVICE_IGNORE}="1" diff --git a/include/ansi.h b/include/ansi.h index c06c06e6b..20815bc03 100644 --- a/include/ansi.h +++ b/include/ansi.h @@ -21,18 +21,46 @@ #define AEND "\x1b[0m" -#define _BLUE_(s) "\x1b[34m" s AEND -#define _RED_(s) "\x1b[31m" s AEND -#define _GREEN_(s) "\x1b[32m" s AEND -#define _YELLOW_(s) "\x1b[33m" s AEND -#define _MAGENTA_(s) "\x1b[35m" s AEND -#define _CYAN_(s) "\x1b[36m" s AEND -#define _WHITE_(s) "\x1b[37m" s AEND +#define _BLACK_(s) "\x1b[30m" s AEND +#define _RED_(s) "\x1b[31m" s AEND +#define _GREEN_(s) "\x1b[32m" s AEND +#define _YELLOW_(s) "\x1b[33m" s AEND +#define _BLUE_(s) "\x1b[34m" s AEND +#define _MAGENTA_(s) "\x1b[35m" s AEND +#define _CYAN_(s) "\x1b[36m" s AEND +#define _WHITE_(s) "\x1b[37m" s AEND + +#define _BRIGHT_BLACK_(s) "\x1b[30;1m" s AEND +#define _BRIGHT_RED_(s) "\x1b[31;1m" s AEND +#define _BRIGHT_GREEN_(s) "\x1b[32;1m" s AEND +#define _BRIGHT_YELLOW_(s) "\x1b[33;1m" s AEND +#define _BRIGHT_BLUE_(s) "\x1b[34;1m" s AEND +#define _BRIGHT_MAGENTA_(s) "\x1b[35;1m" s AEND +#define _BRIGHT_CYAN_(s) "\x1b[36;1m" s AEND +#define _BRIGHT_WHITE_(s) "\x1b[37;1m" s AEND + +#define _BACK_BLACK_(s) "\x1b[40m" s AEND +#define _BACK_RED_(s) "\x1b[41m" s AEND +#define _BACK_GREEN_(s) "\x1b[42m" s AEND +#define _BACK_YELLOW_(s) "\x1b[43m" s AEND +#define _BACK_BLUE_(s) "\x1b[44m" s AEND +#define _BACK_MAGENTA_(s) "\x1b[45m" s AEND +#define _BACK_CYAN_(s) "\x1b[46m" s AEND +#define _BACK_WHITE_(s) "\x1b[47m" s AEND + +#define _BACK_BRIGHT_BLACK_(s) "\x1b[40;1m" s AEND +#define _BACK_BRIGHT_RED_(s) "\x1b[41;1m" s AEND +#define _BACK_BRIGHT_GREEN_(s) "\x1b[42;1m" s AEND +#define _BACK_BRIGHT_YELLOW_(s) "\x1b[43;1m" s AEND +#define _BACK_BRIGHT_BLUE_(s) "\x1b[44;1m" s AEND +#define _BACK_BRIGHT_MAGENTA_(s) "\x1b[45;1m" s AEND +#define _BACK_BRIGHT_CYAN_(s) "\x1b[46;1m" s AEND +#define _BACK_BRIGHT_WHITE_(s) "\x1b[47;1m" s AEND #define _CLEAR_ "\x1b[2J" +#define _CLEAR_SCROLLBACK_ "\x1b[3J" #define _TOP_ "\x1b[1;1f" - #if defined(HAVE_READLINE) // https://wiki.hackzine.org/development/misc/readline-color-prompt.html // Applications may indicate that the prompt contains diff --git a/include/common.h b/include/common.h index 22c7a90e7..a97545996 100644 --- a/include/common.h +++ b/include/common.h @@ -23,7 +23,14 @@ #include #include +#ifdef _WIN32 +#define ABOVE "../" #define PATHSEP "/" +#else +#define ABOVE "../" +#define PATHSEP "/" +#endif + // PM3 share path relative to executable when installed #define PM3_SHARE_RELPATH ".." PATHSEP "share" PATHSEP "proxmark3" PATHSEP @@ -53,7 +60,7 @@ struct version_information_t { char clean; /* 1: Tree was clean, no local changes. 0: Tree was unclean. 2: Couldn't be determined */ char gitversion[50]; /* String with the git revision */ char buildtime[30]; /* string with the build time */ - char armsrc[10]; /* sha256sum of sha256sum of armsrc files */ + char armsrc[10]; /* sha256sum of sha256sum of armsrc && common_arm files */ } PACKED; // debug diff --git a/include/em4x70.h b/include/em4x70.h index bce860bb1..734e83f48 100644 --- a/include/em4x70.h +++ b/include/em4x70.h @@ -42,6 +42,9 @@ typedef struct { // Used to write new key uint8_t crypt_key[12]; + // used for bruteforce the partial key + uint16_t start_key; + } em4x70_data_t; #endif /* EM4X70_H__ */ diff --git a/include/iclass_cmd.h b/include/iclass_cmd.h index e78531868..bc7c1e6ca 100644 --- a/include/iclass_cmd.h +++ b/include/iclass_cmd.h @@ -32,6 +32,7 @@ //#define FLAG_ICLASS_READER_ONLY_ONCE 0x04 #define FLAG_ICLASS_READER_CREDITKEY 0x08 #define FLAG_ICLASS_READER_AIA 0x10 +#define FLAG_ICLASS_READER_SHALLOW_MOD 0x20 // iCLASS reader status flags #define FLAG_ICLASS_NULL 0x00 @@ -60,6 +61,7 @@ typedef struct { bool use_replay; bool send_reply; bool do_auth; + bool shallow_mod; uint8_t blockno; } PACKED iclass_auth_req_t; @@ -82,6 +84,7 @@ typedef struct { typedef struct { iclass_auth_req_t req; uint8_t data[8]; + uint8_t mac[4]; } PACKED iclass_writeblock_req_t; // iCLASS dump data structure @@ -102,6 +105,7 @@ typedef struct iclass_premac { typedef struct { bool use_credit_key; + bool shallow_mod; uint8_t count; iclass_premac_t items[]; } PACKED iclass_chk_t; diff --git a/include/iso14b.h b/include/iso14b.h index 432baac0c..88ba0c2be 100644 --- a/include/iso14b.h +++ b/include/iso14b.h @@ -47,8 +47,16 @@ typedef enum ISO14B_COMMAND { ISO14B_SEND_CHAINING = (1 << 9), ISO14B_SELECT_CTS = (1 << 10), ISO14B_CLEARTRACE = (1 << 11), + ISO14B_SELECT_XRX = (1 << 12), } iso14b_command_t; +typedef enum ISO14B_TYPE { + ISO14B_NONE = 0, + ISO14B_STANDARD = 1, + ISO14B_SR = 2, + ISO14B_CT = 4, +} iso14b_type_t; + typedef struct { uint16_t flags; // the ISO14B_COMMAND enum uint32_t timeout; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index c900eb952..7a19e6dbe 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -205,6 +205,7 @@ typedef struct { // rdv4 bool hw_available_flash : 1; bool hw_available_smartcard : 1; + bool is_rdv4 : 1; } PACKED capabilities_t; #define CAPABILITIES_VERSION 6 extern capabilities_t g_pm3_capabilities; @@ -416,16 +417,15 @@ typedef struct { #define CMD_SPIFFS_WIPE 0x013A // This take a +0x2000 as they are high level helper and special functions -// As the others, they may have safety level argument if it makkes sense +// As the others, they may have safety level argument if it makes sense #define CMD_SPIFFS_PRINT_TREE 0x2130 #define CMD_SPIFFS_GET_TREE 0x2131 #define CMD_SPIFFS_TEST 0x2132 #define CMD_SPIFFS_PRINT_FSINFO 0x2133 #define CMD_SPIFFS_DOWNLOAD 0x2134 #define CMD_SPIFFS_DOWNLOADED 0x2135 +#define CMD_SPIFFS_ELOAD 0x2136 #define CMD_SPIFFS_CHECK 0x3000 -// more ? - // RDV40, Smart card operations #define CMD_SMART_RAW 0x0140 @@ -485,6 +485,7 @@ typedef struct { #define CMD_LF_EM4X70_AUTH 0x0263 #define CMD_LF_EM4X70_WRITEPIN 0x0264 #define CMD_LF_EM4X70_WRITEKEY 0x0265 +#define CMD_LF_EM4X70_BRUTE 0x0266 // Sampling configuration for LF reader/sniffer #define CMD_LF_SAMPLING_SET_CONFIG 0x021D #define CMD_LF_FSK_SIMULATE 0x021E @@ -511,6 +512,7 @@ typedef struct { // For the 13.56 MHz tags #define CMD_HF_ISO15693_ACQ_RAW_ADC 0x0300 +#define CMD_HF_ACQ_RAW_ADC 0x0301 #define CMD_HF_SRI_READ 0x0303 #define CMD_HF_ISO14443B_COMMAND 0x0305 #define CMD_HF_ISO15693_READER 0x0310 @@ -519,8 +521,17 @@ typedef struct { #define CMD_HF_ISO15693_COMMAND 0x0313 #define CMD_HF_ISO15693_FINDAFI 0x0315 #define CMD_HF_ISO15693_CSETUID 0x0316 -#define CMD_HF_ISO15693_SLIX_L_DISABLE_PRIVACY 0x0317 -#define CMD_HF_ISO15693_SLIX_L_DISABLE_AESAFI 0x0318 +#define CMD_HF_ISO15693_SLIX_ENABLE_PRIVACY 0x0867 +#define CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY 0x0317 +#define CMD_HF_ISO15693_SLIX_DISABLE_EAS 0x0318 +#define CMD_HF_ISO15693_SLIX_ENABLE_EAS 0x0862 +#define CMD_HF_ISO15693_SLIX_PASS_PROTECT_AFI 0x0863 +#define CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS 0x0864 +#define CMD_HF_ISO15693_SLIX_WRITE_PWD 0x0865 +#define CMD_HF_ISO15693_WRITE_AFI 0x0866 +#define CMD_HF_TEXKOM_SIMULATE 0x0320 +#define CMD_HF_ISO15693_EML_CLEAR 0x0330 +#define CMD_HF_ISO15693_EML_SETMEM 0x0331 #define CMD_LF_SNIFF_RAW_ADC 0x0360 @@ -552,6 +563,7 @@ typedef struct { #define CMD_HF_EPA_COLLECT_NONCE 0x038A #define CMD_HF_EPA_REPLAY 0x038B +#define CMD_HF_EPA_PACE_SIMULATE 0x039C #define CMD_HF_LEGIC_INFO 0x03BC #define CMD_HF_LEGIC_ESET 0x03BD @@ -672,6 +684,13 @@ typedef struct { // Gen 4 GTU magic cards #define CMD_HF_MIFARE_G4_RDBL 0x0860 +#define CMD_HF_MIFARE_G4_WRBL 0x0861 + +// Gen 4 GDM magic cards +#define CMD_HF_MIFARE_G4_GDM_RDBL 0x0870 +#define CMD_HF_MIFARE_G4_GDM_WRBL 0x0871 +#define CMD_HF_MIFARE_G4_GDM_CONFIG 0x0872 +#define CMD_HF_MIFARE_G4_GDM_WRCFG 0x0873 #define CMD_UNKNOWN 0xFFFF @@ -768,11 +787,15 @@ typedef struct { // Got bad CRC client/pm3: error in transfer of data, crc mismatch. #define PM3_ECRC -24 +// STATIC Nonce detect pm3: when collecting nonces for hardnested +#define PM3_ESTATIC_NONCE -25 + // No data pm3: no data available, no host frame available (not really an error) #define PM3_ENODATA -98 // Quit program client: reserved, order to quit the program #define PM3_EFATAL -99 + // LF #define LF_FREQ2DIV(f) ((int)(((12000.0 + (f)/2.0)/(f))-1)) #define LF_DIVISOR_125 LF_FREQ2DIV(125) diff --git a/include/pmflash.h b/include/pmflash.h index 58bf2eb46..f0deac507 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -28,7 +28,7 @@ // 0x3E000 - 1 4kb sector = settings // 0x3D000 - 1 4kb sector = default T55XX keys dictionary // 0x3B000 - 1 4kb sector = default ICLASS keys dictionary -// 0x39000 - 2 4kb sectors = default MFC keys dictionary +// 0x38000 - 3 4kb sectors = default MFC keys dictionary // #ifndef FLASH_MEM_BLOCK_SIZE # define FLASH_MEM_BLOCK_SIZE 256 @@ -65,17 +65,23 @@ // Reserved space for T55XX PWD = 4 kb #ifndef DEFAULT_T55XX_KEYS_OFFSET -# define DEFAULT_T55XX_KEYS_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x3000) +# define DEFAULT_T55XX_KEYS_LEN (0x1000) +# define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN) +# define DEFAULT_T55XX_KEYS_MAX ((DEFAULT_T55XX_KEYS_LEN - 2) / 4) #endif // Reserved space for iClass keys = 4 kb #ifndef DEFAULT_ICLASS_KEYS_OFFSET -# define DEFAULT_ICLASS_KEYS_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x4000) +# define DEFAULT_ICLASS_KEYS_LEN (0x1000) +# define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN) +# define DEFAULT_ICLASS_KEYS_MAX ((DEFAULT_ICLASS_KEYS_LEN - 2) / 8) #endif -// Reserved space for MIFARE Keys = 8 kb +// Reserved space for MIFARE Keys = 12 kb #ifndef DEFAULT_MF_KEYS_OFFSET -# define DEFAULT_MF_KEYS_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x6000) +# define DEFAULT_MF_KEYS_LEN (0x3000) +# define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN) +# define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6) #endif // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 diff --git a/include/protocol_vigik.h b/include/protocol_vigik.h new file mode 100644 index 000000000..7512c1c2d --- /dev/null +++ b/include/protocol_vigik.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef PROTOCOL_VIGIK_H +#define PROTOCOL_VIGIK_H +#include "common.h" + +typedef struct mfc_vigik_s { + uint8_t b0[16]; + uint8_t mad[32]; + uint8_t counters; + uint8_t rtf[15]; + uint32_t service_code; + uint8_t info_flag; + uint8_t key_version; + uint16_t ptr_counter; + uint8_t counter_num; + uint8_t slot_access_date[5]; + uint16_t slot_dst_duration; + uint8_t other_slots[8]; + uint8_t services_counter; + uint8_t loading_date[5]; + uint16_t reserved_null; + uint8_t rsa_signature[128]; +} mfc_vigik_t; + +typedef struct vigik_pk_s { + const char *desc; + uint16_t code; + const char *n; +} vigik_pk_t; + +#endif diff --git a/include/protocols.h b/include/protocols.h index 41ba5e1c5..f67ae1798 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -190,6 +190,11 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MIFARE_CMD_RESTORE 0xC2 #define MIFARE_CMD_TRANSFER 0xB0 +#define MIFARE_MAGIC_GDM_AUTH_KEY 0x80 +#define MIFARE_MAGIC_GDM_WRITEBLOCK 0xA8 +#define MIFARE_MAGIC_GDM_READ_CFG 0xE0 +#define MIFARE_MAGIC_GDM_WRITE_CFG 0xE1 + #define MIFARE_EV1_PERSONAL_UID 0x40 #define MIFARE_EV1_SETMODE 0x43 #define MIFARE_EV1_UIDF0 0x00 @@ -220,6 +225,9 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define NTAG_I2C_SELECT_SECTOR 0xC2 #define NTAG_I2C_FASTWRITE 0xA6 +//NTAG 213TT (tamper) command +#define NTAGTT_CMD_READ_TT 0xA4 + // mifare 4bit card answers #define CARD_ACK 0x0A // 1010 - ACK #define CARD_NACK_IV 0x00 // 0000 - NACK, invalid argument (invalid page address) @@ -249,9 +257,30 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MAGIC_GEN_1B 2 #define MAGIC_GEN_2 4 #define MAGIC_GEN_UNFUSED 5 -#define MAGIC_SUPER 6 -#define MAGIC_NTAG21X 7 -#define MAGIC_GEN_3 8 +#define MAGIC_SUPER_GEN1 6 +#define MAGIC_SUPER_GEN2 7 +#define MAGIC_NTAG21X 8 +#define MAGIC_GEN_3 9 +#define MAGIC_GEN_4GTU 10 +#define MAGIC_GEN_4GDM 11 + +// Commands for configuration of Gen4 GTU cards. +// see https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md +#define GEN_4GTU_CMD 0xCF // Prefix for all commands, followed by pasword (4b) +#define GEN_4GTU_SHADOW 0x32 // Configure GTU shadow mode +#define GEN_4GTU_ATS 0x34 // Configure ATS +#define GEN_4GTU_ATQA 0x35 // Configure ATQA/SAK (swap ATQA bytes) +#define GEN_4GTU_UIDLEN 0x68 // Configure UID length +#define GEN_4GTU_ULEN 0x69 // (De)Activate Ultralight mode +#define GEN_4GTU_ULMODE 0x6A // Select Ultralight mode +#define GEN_4GTU_GETCNF 0xC6 // Dump configuration +#define GEN_4GTU_TEST 0xCC // Factory test, returns 6666 +#define GEN_4GTU_WRITE 0xCD // Backdoor write 16b block +#define GEN_4GTU_READ 0xCE // Backdoor read 16b block +#define GEN_4GTU_SETCNF 0xF0 // Configure all params in one cmd +#define GEN_4GTU_FUSCNF 0xF1 // Configure all params in one cmd and fuse the configuration permanently +#define GEN_4GTU_CHPWD 0xFE // change password + /** 06 00 = INITIATE 0E xx = SELECT ID (xx = Chip-ID) @@ -431,9 +460,71 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define ISO7816_MANAGE_CHANNEL 0x70 #define ISO7816_GET_RESPONSE 0xC0 + // ISO7816-4 For response APDU's -#define ISO7816_OK 0x9000 -// 6x xx = ERROR +#define ISO7816_OK 0x9000 + +// 6x xx = APDU ERROR CODES + +// 61 xx +#define ISO7816_BYTES_REMAINING_00 0x6100 // Response bytes remaining + +// 62 xx +#define ISO7816_WARNING_STATE_UNCHANGED 0x6200 // Warning, card state unchanged +#define ISO7816_DATA_CORRUPT 0x6281 // Returned data may be corrupted +#define ISO7816_FILE_EOF 0x6282 // The end of the file has been reached before the end of reading +#define ISO7816_INVALID_DF 0x6283 // Invalid DF +#define ISO7816_INVALID_FILE 0x6284 // Selected file is not valid +#define ISO7816_FILE_TERMINATED 0x6285 // File is terminated + +// 63 xx +#define ISO7816_AUTH_FAILED 0x6300 // Authentification failed +#define ISO7816_FILE_FILLED 0x6381 // File filled up by the last write + +// 65 xx +#define ISO7816_MEMORY_FULL 0x6501 // Memory failure +#define ISO7816_WRITE_MEMORY_ERR 0x6581 // Write problem / Memory failure / Unknown mode + +// 67 xx +#define ISO7816_WRONG_LENGTH 0x6700 // Wrong length + +// 68 xx +#define ISO7816_LOGICAL_CHANNEL_NOT_SUPPORTED 0x6881 // Card does not support the operation on the specified logical channel +#define ISO7816_SECURE_MESSAGING_NOT_SUPPORTED 0x6882 // Card does not support secure messaging +#define ISO7816_LAST_COMMAND_EXPECTED 0x6883 // Last command in chain expected +#define ISO7816_COMMAND_CHAINING_NOT_SUPPORTED 0x6884 // Command chaining not supported + +// 69 xx +#define ISO7816_TRANSACTION_FAIL 0x6900 // No successful transaction executed during session +#define ISO7816_SELECT_FILE_ERR 0x6981 // Cannot select indicated file, command not compatible with file organization +#define ISO7816_SECURITY_STATUS_NOT_SATISFIED 0x6982 // Security condition not satisfied +#define ISO7816_FILE_INVALID 0x6983 // File invalid +#define ISO7816_DATA_INVALID 0x6984 // Data invalid +#define ISO7816_CONDITIONS_NOT_SATISFIED 0x6985 // Conditions of use not satisfied +#define ISO7816_COMMAND_NOT_ALLOWED 0x6986 // Command not allowed (no current EF) +#define ISO7816_SM_DATA_MISSING 0x6987 // Expected SM data objects missing +#define ISO7816_SM_DATA_INCORRECT 0x6988 // SM data objects incorrect +#define ISO7816_APPLET_SELECT_FAILED 0x6999 // Applet selection failed + +// 6A xx +#define ISO7816_INVALID_P1P2 0x6A00 // Bytes P1 and/or P2 are invalid +#define ISO7816_WRONG_DATA 0x6A80 // Wrong data +#define ISO7816_FUNC_NOT_SUPPORTED 0x6A81 // Function not supported +#define ISO7816_FILE_NOT_FOUND 0x6A82 // File not found +#define ISO7816_RECORD_NOT_FOUND 0x6A83 // Record not found +#define ISO7816_FILE_FULL 0x6A84 // Not enough memory space in the file +#define ISO7816_LC_TLV_CONFLICT 0x6A85 // LC / TLV conlict +#define ISO7816_INCORRECT_P1P2 0x6A86 // Incorrect parameters (P1,P2) +#define ISO7816_FILE_EXISTS 0x6A89 // File exists +#define ISO7816_NOT_IMPLEMENTED 0x6AFF // + +// 6x 00 +#define ISO7816_WRONG_P1P2 0x6B00 // Incorrect parameters (P1,P2) +#define ISO7816_CORRECT_LENGTH_00 0x6C00 // Correct Expected Length (Le) +#define ISO7816_INS_NOT_SUPPORTED 0x6D00 // INS value not supported +#define ISO7816_CLA_NOT_SUPPORTED 0x6E00 // CLA value not supported +#define ISO7816_UNKNOWN 0x6F00 // No precise diagnosis + // MIFARE DESFire command set: #define MFDES_AUTHENTICATE 0x0A // AUTHENTICATE_NATIVE diff --git a/pm3 b/pm3 index e4469cd29..7c1d0e289 100755 --- a/pm3 +++ b/pm3 @@ -59,14 +59,17 @@ function get_pm3_list_Linux { fi for DEV in $(find /dev/ttyACM* 2>/dev/null); do if command -v udevadm >/dev/null; then + # WSL1 detection if udevadm info -q property -n "$DEV" | grep -q "ID_VENDOR=proxmark.org"; then PM3LIST+=("$DEV") if [ ${#PM3LIST[*]} -ge "$N" ]; then return fi fi - else - if grep -q "proxmark.org" "/sys/class/tty/${DEV#/dev/}/../../../manufacturer" 2>/dev/null; then + fi + # WSL2 with usbipd detection - doesn't report same things as WSL1 + if grep -q "proxmark.org" "/sys/class/tty/${DEV#/dev/}/../../../manufacturer" 2>/dev/null; then + if echo "${PM3LIST[*]}" | grep -qv "${DEV}"; then PM3LIST+=("$DEV") if [ ${#PM3LIST[*]} -ge "$N" ]; then return @@ -180,7 +183,8 @@ function get_pm3_list_WSL { PM3LIST=() # Normal SERIAL PORTS (COM) - for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.PNPDeviceID -like '*VID_9AC4&PID_4B8F*' -or \$_.PNPDeviceID -like '*VID_2D2D&PID_504D*'} | Select -expandproperty DeviceID" 2>/dev/null | tr -dc '[:print:]'); do + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.PNPDeviceID -like '*VID_9AC4&PID_4B8F*' -or \$_.PNPDeviceID -like '*VID_2D2D&PID_504D*'} | Select -expandproperty DeviceID" 2>/dev/null); do + DEV=$(echo $DEV | tr -dc '[:print:]') _comport=$DEV DEV=$(echo $DEV | sed -nr 's#^COM([0-9]+)\b#/dev/ttyS\1#p') # ttyS counterpart takes some more time to appear @@ -252,7 +256,7 @@ Quick helper script for proxmark3 client when working with a Proxmark3 device Description: The usage is the same as for the proxmark3 client, with the following differences: * the correct port name will be automatically guessed; - * the script will wait for a Proxmark to be connected (same as option -w of the client). + * the script will wait for a Proxmark3 to be connected (same as option -w of the client). You can also specify a first option -n N to access the Nth Proxmark3 connected. To see a list of available ports, use --list. @@ -299,7 +303,7 @@ elif [ "$SCRIPT" = "pm3-flash" ]; then } HELP() { cat << EOF -Quick helper script for flashing a Proxmark device via USB +Quick helper script for flashing a Proxmark3 device via USB Description: The usage is similar to the old proxmark3-flasher binary, except that the correct port name will be automatically guessed. @@ -337,7 +341,7 @@ elif [ "$SCRIPT" = "pm3-flash-all" ]; then } HELP() { cat << EOF -Quick helper script for flashing a Proxmark device via USB +Quick helper script for flashing a Proxmark3 device via USB Description: The correct port name will be automatically guessed and the stock bootloader and firmware image will be flashed. @@ -367,7 +371,7 @@ elif [ "$SCRIPT" = "pm3-flash-fullimage" ]; then } HELP() { cat << EOF -Quick helper script for flashing a Proxmark device via USB +Quick helper script for flashing a Proxmark3 device via USB Description: The correct port name will be automatically guessed and the stock firmware image will be flashed. @@ -397,7 +401,7 @@ elif [ "$SCRIPT" = "pm3-flash-bootrom" ]; then } HELP() { cat << EOF -Quick helper script for flashing a Proxmark device via USB +Quick helper script for flashing a Proxmark3 device via USB Description: The correct port name will be automatically guessed and the stock bootloader will be flashed. @@ -439,10 +443,12 @@ done # if a port is already provided, let's just run the command as such for ARG; do + shift if [ "$ARG" == "-p" ]; then CMD "$@" exit $? fi + set -- "$@" "$ARG" done if [ "$1" == "--list" ]; then @@ -454,7 +460,7 @@ if [ "$1" == "--list" ]; then SHOWLIST=true fi -# Number of the proxmark3 we're interested in +# Number of the Proxmark3 we're interested in N=1 if [ "$1" == "-n" ]; then shift @@ -469,7 +475,8 @@ fi HOSTOS=$(uname | awk '{print toupper($0)}') if [ "$HOSTOS" = "LINUX" ]; then - if uname -a|grep -q Microsoft; then + # Detect when running under WSL1 (but exclude WSL2) + if uname -a | grep -qi Microsoft && uname -a | grep -qvi WSL2; then # First try finding it using the PATH environment variable PSHEXE=$(command -v powershell.exe 2>/dev/null) @@ -513,7 +520,7 @@ if $SHOWLIST; then exit 0 fi -# Wait till we get at least N proxmark3 devices +# Wait till we get at least N Proxmark3 devices $GETPM3LIST "$N" if [ ${#PM3LIST} -lt "$N" ]; then echo >&2 "[=] Waiting for Proxmark3 to appear..." diff --git a/tools/build_all_firmwares.sh b/tools/build_all_firmwares.sh index 482033189..644438032 100755 --- a/tools/build_all_firmwares.sh +++ b/tools/build_all_firmwares.sh @@ -22,7 +22,8 @@ echo "Destination: ${DEST:=firmware}" echo "Produce stats?: ${STATS:=false}" # Which parts to skip for the 256kb version? -SKIPS256="SKIP_HITAG=1" +SKIPS256="SKIP_HITAG=1 SKIP_LEGICRF=1 SKIP_FELICA=1 SKIP_EM4x50=1 SKIP_ISO14443b=1 SKIP_NFCBARCODE=1 SKIP_ZX8211=1" + make $MKFLAGS bootrom || exit 1 chmod 644 bootrom/obj/bootrom.elf @@ -30,11 +31,13 @@ mkdir -p "$DEST" mv bootrom/obj/bootrom.elf "$DEST/PM3BOOTROM.elf" # cf armsrc/Standalone/Makefile.hal -STANDALONE_MODES=(LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE LF_NEXID) -STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG HF_REBLAY DANKARMULTI) +STANDALONE_MODES=(LF_SKELETON) +STANDALONE_MODES+=(LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE) +STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_YOUNG) +STANDALONE_MODES+=(DANKARMULTI) STANDALONE_MODES_REQ_BT=(HF_REBLAY) STANDALONE_MODES_REQ_SMARTCARD=() -STANDALONE_MODES_REQ_FLASH=(LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_14ASNIFF HF_15SNIFF HF_BOG HF_COLIN HF_ICECLASS HF_MFCSIM) +STANDALONE_MODES_REQ_FLASH=(LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM) # PM3GENERIC 256kb, no flash, need to skip some parts to reduce size diff --git a/tools/cryptorf/sma.cpp b/tools/cryptorf/sma.cpp index eb9cf3c9d..d15b0e6a2 100644 --- a/tools/cryptorf/sma.cpp +++ b/tools/cryptorf/sma.cpp @@ -331,11 +331,10 @@ static inline uint8_t next_right_fast(uint8_t in, uint64_t *right) { static inline void sm_left_mask(const uint8_t *ks, uint8_t *mask, uint64_t rstate) { size_t pos; - uint8_t bt; for (pos = 0; pos < 16; pos++) { next_right_fast(0, &rstate); - bt = next_right_fast(0, &rstate) << 4; + uint8_t bt = next_right_fast(0, &rstate) << 4; next_right_fast(0, &rstate); bt |= next_right_fast(0, &rstate); @@ -349,7 +348,7 @@ static inline void sm_left_mask(const uint8_t *ks, uint8_t *mask, uint64_t rstat static inline uint32_t sm_right(const uint8_t *ks, uint8_t *mask, vector *pcrstates) { uint8_t tmp_mask[16]; - size_t pos, bits, bit, topbits; + size_t pos, bit, topbits; uint64_t rstate, counter; map bincstates; map::iterator it; @@ -358,7 +357,7 @@ static inline uint32_t sm_right(const uint8_t *ks, uint8_t *mask, vector *pcstates) { map bincstates; map::iterator it; - uint64_t counter, lstate; + uint64_t counter; size_t pos, bits, bit; uint8_t correct_bits[16]; uint8_t bt; @@ -526,7 +525,7 @@ static inline void sm_left(const uint8_t *ks, const uint8_t *mask, vector state.invalid = false; for (counter = 0; counter < 0x800000000ull; counter++) { - lstate = counter; + uint64_t lstate = counter; for (pos = 0; pos < 16; pos++) { lstate = (((lstate) >> 5) | ((uint64_t)left_addition[((lstate) & 0xf801f)] << 30)); diff --git a/tools/cryptorf/sma_multi.cpp b/tools/cryptorf/sma_multi.cpp index 577811f64..50b962248 100644 --- a/tools/cryptorf/sma_multi.cpp +++ b/tools/cryptorf/sma_multi.cpp @@ -165,6 +165,9 @@ void print_cs(const char *text, pcs s) { } static inline uint8_t mod(uint8_t a, uint8_t m) { + if (m == 0) { + return 0; // Actually, divide by zero error + } // Just return the input when this is less or equal than the modular value if (a < m) return a; @@ -199,18 +202,15 @@ static lookup_entry lookup_right[0x8000]; static uint8_t left_addition[0x100000]; static inline void init_lookup_left() { - uint8_t b3, b6, temp; - int i, index; - - for (i = 0; i < 0x400; i++) { - b6 = i & 0x1f; - b3 = (i >> 5) & 0x1f; - index = (b3 << 15) | b6; + for (int i = 0; i < 0x400; i++) { + uint8_t b6 = i & 0x1f; + uint8_t b3 = (i >> 5) & 0x1f; + int index = (b3 << 15) | b6; // b6 = bit_rotate_l(b6, 5); b6 = BIT_ROL(b6); - temp = mod(b3 + b6, 0x1f); + uint8_t temp = mod(b3 + b6, 0x1f); left_addition[index] = temp; lookup_left[index].addition = temp; lookup_left[index].out = ((temp ^ b3) & 0x0f); @@ -218,15 +218,12 @@ static inline void init_lookup_left() { } static inline void init_lookup_right() { - uint8_t b16, b18, temp; - int i, index; + for (int i = 0; i < 0x400; i++) { + uint8_t b18 = i & 0x1f; + uint8_t b16 = (i >> 5) & 0x1f; + int index = (b16 << 10) | b18; - for (i = 0; i < 0x400; i++) { - b18 = i & 0x1f; - b16 = (i >> 5) & 0x1f; - index = (b16 << 10) | b18; - - temp = mod(b18 + b16, 0x1f); + uint8_t temp = mod(b18 + b16, 0x1f); lookup_right[index].addition = temp; lookup_right[index].out = ((temp ^ b16) & 0x0f); } @@ -589,23 +586,21 @@ static void ice_sm_left(const uint8_t *ks, uint8_t *mask, vector *pcstates static inline uint32_t sm_right(const uint8_t *ks, uint8_t *mask, vector *pcrstates) { uint8_t tmp_mask[16]; - size_t pos, bits, bit, topbits; + size_t topbits = 0; map bincstates; map::iterator it; - uint8_t bt; - topbits = 0; for (uint64_t counter = 0; counter < 0x2000000; counter++) { // Reset the current bitcount of correct bits - bits = 0; + size_t bits = 0; // Copy the state we are going to test uint64_t rstate = counter; - for (pos = 0; pos < 16; pos++) { + for (size_t pos = 0; pos < 16; pos++) { next_right_fast(0, &rstate); - bt = next_right_fast(0, &rstate) << 4; + uint8_t bt = next_right_fast(0, &rstate) << 4; next_right_fast(0, &rstate); bt |= next_right_fast(0, &rstate); @@ -615,7 +610,7 @@ static inline uint32_t sm_right(const uint8_t *ks, uint8_t *mask, vector>= 1; @@ -744,7 +739,7 @@ static inline void search_gc_candidates_right(const uint64_t rstate_before_gc, c static inline void sm_left(const uint8_t *ks, const uint8_t *mask, vector *pcstates) { map bincstates; map::iterator it; - uint64_t counter, lstate; + uint64_t counter; size_t pos, bits; uint8_t correct_bits[16]; uint8_t bt; @@ -756,7 +751,7 @@ static inline void sm_left(const uint8_t *ks, const uint8_t *mask, vector state.invalid = false; for (counter = 0; counter < 0x800000000ull; counter++) { - lstate = counter; + uint64_t lstate = counter; for (pos = 0; pos < 16; pos++) { diff --git a/tools/hitag2crack/common/ht2crackutils.c b/tools/hitag2crack/common/ht2crackutils.c index cfac2bfd8..492676724 100644 --- a/tools/hitag2crack/common/ht2crackutils.c +++ b/tools/hitag2crack/common/ht2crackutils.c @@ -141,6 +141,8 @@ int fnf(uint64_t s) { // builds the lfsr for the prng (quick calcs for hitag2_nstep()) void buildlfsr(Hitag_State *hstate) { + if (hstate == NULL) + return; uint64_t state = hstate->shiftreg; uint64_t temp = state ^ (state >> 1); hstate->lfsr = state ^ (state >> 6) ^ (state >> 16) diff --git a/tools/mf_nonce_brute/iso14443crc.c b/tools/mf_nonce_brute/iso14443crc.c index 60631f4fb..c5acbb9be 100644 --- a/tools/mf_nonce_brute/iso14443crc.c +++ b/tools/mf_nonce_brute/iso14443crc.c @@ -20,11 +20,10 @@ void ComputeCrc14443(int CrcType, const unsigned char *Data, int Length, unsigned char *TransmitFirst, unsigned char *TransmitSecond) { - unsigned char chBlock; unsigned short wCrc = CrcType; do { - chBlock = *Data++; + unsigned char chBlock = *Data++; UpdateCrc14443(chBlock, &wCrc); } while (--Length); diff --git a/tools/mf_nonce_brute/mf_nonce_brute.c b/tools/mf_nonce_brute/mf_nonce_brute.c index 8d98e04cf..161f1c749 100644 --- a/tools/mf_nonce_brute/mf_nonce_brute.c +++ b/tools/mf_nonce_brute/mf_nonce_brute.c @@ -331,6 +331,8 @@ static bool checkValidCmd(uint32_t decrypted) { static bool checkValidCmdByte(uint8_t *cmd, uint16_t n) { bool ok = false; + if (cmd == NULL) + return false; for (int i = 0; i < 8; ++i) { if (cmd[0] == cmds[i][0]) { diff --git a/tools/mf_nonce_brute/mf_trace_brute.c b/tools/mf_nonce_brute/mf_trace_brute.c index 8bc255517..1d8a7c6fb 100644 --- a/tools/mf_nonce_brute/mf_trace_brute.c +++ b/tools/mf_nonce_brute/mf_trace_brute.c @@ -174,6 +174,8 @@ static char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const si static bool checkValidCmdByte(uint8_t *cmd, uint16_t n) { bool ok = false; + if (cmd == NULL) + return false; for (int i = 0; i < 8; ++i) { if (cmd[0] == cmds[i][0]) { diff --git a/tools/mfd_aes_brute/brute_key.c b/tools/mfd_aes_brute/brute_key.c index 41c91f3c7..e02a597e2 100644 --- a/tools/mfd_aes_brute/brute_key.c +++ b/tools/mfd_aes_brute/brute_key.c @@ -111,7 +111,8 @@ int main(int argc, char *argv[]) { uint8_t tag_challenge[16] = {0x00}; uint8_t lock_challenge[32] = {0x00}; - uint64_t timestamp = atoi(argv[1]); + uint64_t timestamp = 0; + sscanf(argv[1], "%"PRIu64, ×tamp); if (argc != 4) { printf("\nusage: %s <16 byte tag challenge> <32 byte lock challenge>\n\n", argv[0]); @@ -124,8 +125,10 @@ int main(int argc, char *argv[]) { if (hexstr_to_byte_array(argv[3], lock_challenge, sizeof(lock_challenge))) return 3; + // current time uint64_t start_time = time(NULL); + // from a time before up until current time. for (; timestamp < start_time; timestamp++) { make_key(timestamp, key); diff --git a/tools/mfd_aes_brute/mfd_aes_brute.c b/tools/mfd_aes_brute/mfd_aes_brute.c index d4bca2bb8..19bb54ce5 100644 --- a/tools/mfd_aes_brute/mfd_aes_brute.c +++ b/tools/mfd_aes_brute/mfd_aes_brute.c @@ -139,10 +139,13 @@ static void print_time(uint64_t at) { (void)localtime_r(&t, <); #endif - char res[32]; - strftime(res, sizeof(res), "%Y-%m-%d %H:%M:%S", <); - - printf("%u ( '%s' )\n", (unsigned)t, res); + char res[70]; +#if defined(__MINGW32__) || defined(__MINGW64__) + strftime(res, sizeof(res), "('%Y-%m-%d %H:%M:%S')", <); +#else + strftime(res, sizeof(res), "%s ('%Y-%m-%d %H:%M:%S')", <); +#endif + printf("%s\n", res); } static void *brute_thread(void *arguments) { @@ -233,7 +236,8 @@ int main(int argc, char *argv[]) { if (argc != 4) return usage(argv[0]); - uint64_t start_time = atoi(argv[1]); + uint64_t start_time = 0; + sscanf(argv[1], "%"PRIu64, &start_time); uint8_t tag_challenge[16] = {0x00}; if (hexstr_to_byte_array(argv[2], tag_challenge, sizeof(tag_challenge))) diff --git a/tools/mfd_aes_brute/mfd_multi_brute.c b/tools/mfd_aes_brute/mfd_multi_brute.c index 704796255..226fd9f7f 100644 --- a/tools/mfd_aes_brute/mfd_multi_brute.c +++ b/tools/mfd_aes_brute/mfd_multi_brute.c @@ -169,10 +169,13 @@ static void print_time(uint64_t at) { (void)localtime_r(&t, <); #endif - char res[32]; - strftime(res, sizeof(res), "%Y-%m-%d %H:%M:%S", <); - - printf("%u ( '%s' )\n", (unsigned)t, res); + char res[70]; +#if defined(__MINGW32__) || defined(__MINGW64__) + strftime(res, sizeof(res), "('%Y-%m-%d %H:%M:%S')", <); +#else + strftime(res, sizeof(res), "%s ('%Y-%m-%d %H:%M:%S')", <); +#endif + printf("%s\n", res); } static void *brute_thread(void *arguments) { @@ -378,7 +381,8 @@ int main(int argc, char *argv[]) { return 1; } - uint64_t start_time = atoi(argv[3]); + uint64_t start_time = 0; + sscanf(argv[3], "%"PRIu64, &start_time); printf("Crypto algo............ " _GREEN_("%s") "\n", algostr); printf("LCR Random generator... " _GREEN_("%s") "\n", generators[g_idx].Name); diff --git a/tools/mfd_aes_brute/randoms.c b/tools/mfd_aes_brute/randoms.c index c957350ec..523366d9d 100644 --- a/tools/mfd_aes_brute/randoms.c +++ b/tools/mfd_aes_brute/randoms.c @@ -68,13 +68,12 @@ void make_key_turbopascal_n(uint32_t seed, uint8_t key[], const size_t keylen) { void make_key_posix_rand_r_n(uint32_t seed, uint8_t key[], const size_t keylen) { uint32_t lseed = seed; - int result; for (int i = 0; i < keylen; i++) { lseed *= 1103515245; lseed += 12345; - result = (uint16_t)(lseed / 0x10000) % 2048; + int result = (uint16_t)(lseed / 0x10000) % 2048; lseed *= 1103515245; lseed += 12345; diff --git a/tools/mkversion.sh b/tools/mkversion.sh index c1819f34e..f741f2980 100755 --- a/tools/mkversion.sh +++ b/tools/mkversion.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env sh # Output a version_pm3.c file that includes information about the current build # From mkversion.pl diff --git a/tools/pm3_online_check.py b/tools/pm3_online_check.py index 3a5f9d4cd..a7961d319 100755 --- a/tools/pm3_online_check.py +++ b/tools/pm3_online_check.py @@ -12,7 +12,7 @@ # # This code is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or +# the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This code is distributed in the hope that it will be useful, @@ -122,7 +122,7 @@ def pm3_lf_t55xx(child): def pm3_flash_sm(child): try: print("[+] Updating smart card fw") - child.sendline('smart upgrade -f sim011.bin') + child.sendline('smart upgrade -f sim013.bin') i = child.expect('pm3 --> ') msg = escape_ansi(str(child.before)) print("================") @@ -204,7 +204,7 @@ def main(): flash_mem = "baudrate................24 mhz".lower() # check smartcard fw version - sm_version = "version.................v3.11".lower() + sm_version = "version.................v4.12".lower() # check LF lf_search = "valid hid prox id found!".lower() diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index df0d4aa5d..f116de2b1 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -193,7 +193,7 @@ function CheckExecute() { for I in $RETRY do RES=$(eval "$2") - if echo "$RES" | egrep -q "$3"; then + if echo "$RES" | grep -E -q "$3"; then echo -e "[ ${C_GREEN}OK${C_NC} ] ${C_OK}" return $RESULT fi @@ -236,7 +236,7 @@ while true; do if $TESTALL || $TESTCOMMON; then echo -e "\n${C_BLUE}Testing common:${C_NC}" if ! CheckFileExist "hardnested tables exists" "$RESOURCEPATH/hardnested_tables/bitflip_0_001_states.bin.bz2"; then break; fi - if ! CheckFileExist "simmodule fw file exists" "$RESOURCEPATH/sim011.bin"; then break; fi + if ! CheckFileExist "simmodule fw file exists" "$RESOURCEPATH/sim013.bin"; then break; fi if ! CheckFileExist "iCLASS dictionary exists" "$DICPATH/iclass_default_keys.dic"; then break; fi if ! CheckFileExist "MFC dictionary exists" "$DICPATH/mfc_default_keys.dic"; then break; fi if ! CheckFileExist "MFDES dictionary exists" "$DICPATH/mfdes_default_keys.dic"; then break; fi @@ -293,7 +293,7 @@ while true; do echo -e "\n${C_BLUE}Testing mfd_aes_brute:${C_NC} ${MFDASEBRUTEBIN:=./tools/mfd_aes_brute/mfd_aes_brute}" if ! CheckFileExist "mfd_aes_brute exists" "$MFDASEBRUTEBIN"; then break; fi if ! CheckExecute "mfd_aes_brute test 1/2" "$MFDASEBRUTEBIN 1605394800 bb6aea729414a5b1eff7b16328ce37fd 82f5f498dbc29f7570102397a2e5ef2b6dc14a864f665b3c54d11765af81e95c" "key.................... .*261C07A23F2BC8262F69F10A5BDF3764"; then break; fi - if ! CheckExecute slow "mfd_aes_brute test 2/2" "$MFDASEBRUTEBIN 1136073600 3fda933e2953ca5e6cfbbf95d1b51ddf 97fe4b5de24188458d102959b888938c988e96fb98469ce7426f50f108eaa583" "key.................... .*E757178E13516A4F3171BC6EA85E165A"; then break; fi + if ! CheckExecute slow "mfd_aes_brute test 2/2" "$MFDASEBRUTEBIN 1546300800 3fda933e2953ca5e6cfbbf95d1b51ddf 97fe4b5de24188458d102959b888938c988e96fb98469ce7426f50f108eaa583" "key.................... .*E757178E13516A4F3171BC6EA85E165A"; then break; fi fi # hitag2crack not yet part of "all" # if $TESTALL || $TESTHITAG2CRACK; then @@ -315,7 +315,7 @@ while true; do HT2CRACK3NRAR=hitag2_${HT2CRACK3UID}_nrar_${HT2CRACK3N}emul.txt if ! CheckExecute "ht2crack3 gen testfile" "cd $HT2CRACK3PATH; python3 ../hitag2_gen_nRaR.py $HT2CRACK3KEY $HT2CRACK3UID $HT2CRACK3N > $HT2CRACK3NRAR && echo SUCCESS" "SUCCESS"; then break; fi if ! CheckExecute "ht2crack3test test" "cd $HT2CRACK3PATH; ./ht2crack3test $HT2CRACK3NRAR $HT2CRACK3KEY $HT2CRACK3UID|grep -v SUCCESS||echo SUCCESS" "SUCCESS"; then break; fi - if ! CheckExecute "ht2crack3 test" "cd $HT2CRACK3PATH; ./ht2crack3 $HT2CRACK3UID $HT2CRACK3NRAR |egrep -v '(trying|partial)'" "key = $HT2CRACK3KEY"; then break; fi + if ! CheckExecute "ht2crack3 test" "cd $HT2CRACK3PATH; ./ht2crack3 $HT2CRACK3UID $HT2CRACK3NRAR |grep -E -v '(trying|partial)'" "key = $HT2CRACK3KEY"; then break; fi if ! CheckExecute "ht2crack3 rm testfile" "cd $HT2CRACK3PATH; rm $HT2CRACK3NRAR && echo SUCCESS" "SUCCESS"; then break; fi echo -e "\n${C_BLUE}Testing ht2crack4:${C_NC} ${HT2CRACK4PATH:=./tools/hitag2crack/crack4/}" @@ -428,7 +428,7 @@ while true; do "GALLAGHER - Region: 1 Facility: 16640 Card No.: 201 Issue Level: 1"; then break; fi if ! CheckExecute slow "lf T55 gproxii test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_gproxii.pm3; lf search -1'" "Guardall G-Prox II ID found"; then break; fi if ! CheckExecute slow "lf T55 gproxii test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_gproxii.pm3; lf gproxii demod'" \ - "G-Prox-II - len: 26 FC: 123 Card: 11223, Raw: f98c7038c63356c7ac26398c"; then break; fi + "G-Prox-II - Len: 26 FC: 123 Card: 11223 xor: 102, Raw: f98c7038c63356c7ac26398c"; then break; fi if ! CheckExecute slow "lf T55 hid test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_hid.pm3; lf search -1'" "HID Prox ID found"; then break; fi if ! CheckExecute slow "lf T55 hid test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_hid.pm3; lf hid demod'" \ "FC: 118 CN: 1603"; then break; fi @@ -502,11 +502,11 @@ while true; do echo -e "\n${C_BLUE}Testing HF:${C_NC}" if ! CheckExecute "hf mf offline text" "$CLIENTBIN -c 'hf mf'" "at_enc"; then break; fi if ! CheckExecute slow retry ignore "hf mf hardnested long test" "$CLIENTBIN -c 'hf mf hardnested -t --tk 000000000000'" "found:"; then break; fi - if ! CheckExecute slow "hf iclass loclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified \(ok\)"; then break; fi + if ! CheckExecute slow "hf iclass loclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified \( ok \)"; then break; fi if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Test\(s\) \[ ok"; then break; fi if ! CheckExecute "hf iclass lookup test" "$CLIENTBIN -c 'hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f $DICPATH/iclass_default_keys.dic'" \ "valid key AE A6 84 A6 DA B2 32 78"; then break; fi - if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification \(ok\)"; then break; fi + if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification \( ok \)"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Test\(s\) \[ ok"; then break; fi if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \[ ok"; then break; fi if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \[ ok"; then break; fi diff --git a/tools/recover_pk.py b/tools/recover_pk.py index b9f8dde4b..7ef149ceb 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -158,6 +158,11 @@ def selftests(): # # uses secp256r1?, SHA-256, # 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"], # 'pk': ""}, + {'name': "MIFARE Plus Trojka", + # uses secp224r1, None, + 'samples': ["04B59F6A226F82", "6F577EB7F570D74DB6250477427F68A0088762BD318767537122919A7916597149F9D16D8B135E9BF826FB28AE293F3168661CD4A049FAED", + "04B44A82D80F92", "A0868ECF26733D3C3C838D055968B4559F77693CC3E346E3A4741BC826801F8360FD88857BEC440AAD3A21153D64302DEB6F5ED40B15C3F7"], + 'pk': "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"}, ] succeeded = True for t in tests: diff --git a/tools/release_tests.sh b/tools/release_tests.sh new file mode 100755 index 000000000..728e546f3 --- /dev/null +++ b/tools/release_tests.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# To be run from proxmark3 root directory +set -x +make clean && make -j PLATFORM=PM3GENERIC PLATFORM_EXTRAS= && tools/pm3_tests.sh --long || exit 1 +make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS= || exit 1 +make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON || exit 1 +make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && INSTALLSUDO=sudo make install PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && ( cd /tmp; proxmark3 -c 'data load -f lf_EM4x05.pm3;lf search -1'|grep 'Valid FDX-B ID found' ) && INSTALLSUDO=sudo make uninstall || exit 1 +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3GENERIC PLATFORM_EXTRAS= && cp -a ../*scripts ../*libs . && ../../tools/pm3_tests.sh --clientbin $(pwd)/proxmark3 client ) || exit 1 +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS= ) || exit 1 +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON ) || exit 1 + +# Hitag2crack, optionally with --long and --opencl... +make hitag2crack/clean && make hitag2crack && tools/pm3_tests.sh hitag2crack || exit 1 +echo PASS diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 000000000..b6be80271 --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,3 @@ +ansicolors==1.1.8 +sslcrypto==5.3 +pexpect==4.8.0 \ No newline at end of file diff --git a/tools/simmodule/sim013.asm b/tools/simmodule/sim013.asm new file mode 100644 index 000000000..b26aa1906 --- /dev/null +++ b/tools/simmodule/sim013.asm @@ -0,0 +1,819 @@ +; --------------------------------------------------------------------------- +; Proxmark3 RDV4 SIM module firmware +; +; Copyright (C) 2109, 2022 Sentinel +; +; This program is free software: you can redistribute it and/or modify it +; under the terms of the GNU Lesser General Public License as published by the +; Free Software Foundation, either version 3 of the License, or (at your +; option) any later version. +; +; This program is distributed in the hope that it will be useful, but WITHOUT +; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +; more details. +; +; You should have received a copy of the GNU Lesser General Public License +; along with this program. If not, see +; --------------------------------------------------------------------------- + VERS_HI equ 4 + VERS_LO equ 13 +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + SCON_0 equ 098h + FE_0 equ 098h.7 + + SCON_1 equ 0F8h + RI_1 equ 0F8h.0 + TI_1 equ 0F8h.1 + FE_1 equ 0F8h.7 + SBUF_1 equ 09Ah + T3CON equ 0C4h + RL3 equ 0C5h + RH3 equ 0C6h + + P0M1 equ 0B1h + P0M2 equ 0B2h + P1M1 equ 0B3h + P1M2 equ 0B4h + P3M1 equ 0ACh; + P3M2 equ 0ADh; + + EIE equ 09Bh + EIE1 equ 09Ch + + TA equ 0C7h + + RCTRIM0 equ 084h +; --------------------------------------------------------------------------- + CKCON equ 08Eh + CKDIV equ 095h +; --------------------------------------------------------------------------- + P1S equ 0B3h ;Page1 + SFRS equ 091h ;TA Protection +; --------------------------------------------------------------------------- + ;AUXR1 equ 0A2h +; --------------------------------------------------------------------------- + I2DAT equ 0BCh; + I2STAT equ 0BDh; + I2CLK equ 0BEh; + I2TOC equ 0BFh; + I2CON equ 0C0h; + ; equ I2CON.7;8 + I2CEN equ I2CON.6;4 + STA equ I2CON.5;2 + STO equ I2CON.4;1 + SI equ I2CON.3;8 + AA equ I2CON.2;4 + ; equ I2CON.1;2 + I2CPX equ I2CON.0;1 + + + I2ADDR equ 0C1h; + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + pin_TX1 equ P1.6 + + pin_TX0 equ P0.6 + pin_RX0 equ P0.7 + + pin_SCL equ P1.3 + pin_SDA equ P1.4 + + pin_RST equ P1.0 + pin_CLC equ P1.1 + pin_led equ P1.2 + +; --------------------------------------------------------------------------- +; =========================================================================== + + + CMD_GENERATE_ATR equ 01h + CMD_WRITE_DATA_SIM equ 02h + CMD_READ_DATA_SIM equ 03h + + CMD_SET_BAUD_RATE equ 04h + CMD_SET_SIM_CLC equ 05h + CMD_GET_VERS equ 06h + CMD_WRITE_CONFIRM equ 07h + + + +; --------------------------------------------------------------------------- +; =========================================================================== + + bit_RX0 equ 32.0 + bit_command_receive equ 32.1 + bit_generate_ATR equ 32.2 + i2c_write_mode equ 32.3 + i2c_write_done equ 32.4 + bit_data_sim_wr equ 32.5 + ; equ 32.6 + bit_TX0 equ 32.7 + + bit_command_buff equ 33.0 + i2c_write_command equ 33.1 + i2c_command_done equ 33.2 + bit_wait_confirm equ 33.3 + bit_first_ATR equ 33.4 ;11/03/2019 + bit_length_answerH equ 33.5 + bit_length_answerL equ 33.6 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + bit_32 equ 32 + bit_33 equ 33 + + time_data_read equ 34 + time_confirm equ 35 + + pointer_RX1H equ 36 ;save SBUF(SIM) to XRAM + pointer_RX1L equ 37 ;save SBUF(SIM) to XRAM + + pointer_RX2H equ 38 ;read XRAM to I2C + pointer_RX2L equ 39 ;read XRAM to I2C + + pointer_TXH equ 40 + pointer_TXL equ 41 + + length_send_to_simH equ 42 + length_send_to_simL equ 43 + + length_answer_simH equ 44 + length_answer_simL equ 45 + + length_command equ 46 + + buff_command equ 47 + cmd_command equ 48 + data_command equ 49 + + STACKKKKK equ 200 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + XRAM_TX_BUFF equ 0 + XRAM_RX_BUFF equ 384 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +; Beginning of the main program + cseg at 00 + Ljmp main_start + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + cseg at 11 ;1302Hz = 4MHZ(Fsys)/12/256 +; --------------------------------------------------------------------------- + jb time_confirm.7, $+3+2 ;3 + dec time_confirm ;2 +; --------------------------------------------------------------------------- + jb time_data_read.7,reti_timer0 + djnz time_data_read, reti_timer0 + setb pin_scl +reti_timer0: + reti + + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + cseg at 35 ;UART0 + ajmp jmp_UART0_interrupt + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + cseg at 51 ;I2C + ajmp jmp_i2c_interrupt + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + cseg at 123 ;UART1 + clr RI_1 + clr TI_1 + reti + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +jmp_UART0_interrupt: + jbc RI,jmp_byte_RI + jbc TI,jmp_byte_TI + reti +; --------------------------------------------------------------------------- +jmp_byte_RI: + jnb bit_first_ATR, jmp_not_collect ;11/03/2019 + + setb bit_RX0 + jb i2c_write_done,jmp_not_collect + PUSH ACC + PUSH DPH + PUSH DPL + mov DPL,pointer_RX1L + mov DPH,pointer_RX1H + mov a,SBUF + movx @DPTR,a + inc DPTR + mov pointer_RX1L,DPL + mov pointer_RX1H,DPH + POP DPL + POP DPH + POP ACC + ;09/08/2018 + clr pin_scl + mov time_data_read,#52 ;52/1302Hz = 40mS + + inc length_answer_simL + mov a,length_answer_simL + jnz $+2+2 ;2 + inc length_answer_simH ;2 + +jmp_not_collect: + reti +; --------------------------------------------------------------------------- +jmp_byte_TI: + setb bit_TX0 + reti + + +; =========================================================================== +; --------------------------------------------------------------------------- +jmp_i2c_interrupt: + PUSH ACC + PUSH PSW + mov PSW,#24 + mov R7,I2STAT +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne R7,#000h,nextttt00000 + setb STO + clr SI + jb STO,$ + ajmp pop_i2c_psw +nextttt00000: +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne R7,#060h,nextttt00001 ;START+MY ADRESS + clr pin_led ;LED ON + + clr bit_command_receive + clr i2c_write_mode + clr bit_data_sim_wr + clr bit_length_answerH + clr bit_length_answerL + clr bit_command_buff + clr i2c_write_command + + ajmp end_i2c_interrupt +nextttt00001: +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne R7,#080h,nextttt00002 ;RAM ADRESS + + jb bit_command_receive,jmp_data_receive + setb bit_command_receive + + mov a,I2DAT +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne a,#CMD_WRITE_CONFIRM,next_comm001a + setb bit_wait_confirm + ajmp WRITEDATASIM +next_comm001a: +; --------------------------------------------------------------------------- + cjne a,#CMD_WRITE_DATA_SIM,next_comm001b + clr bit_wait_confirm + ajmp WRITEDATASIM +next_comm001b: +; --------------------------------------------------------------------------- + cjne a,#CMD_GENERATE_ATR,next_comm002 + ajmp ATR_GENERATE +next_comm002: +; --------------------------------------------------------------------------- + cjne a,#CMD_GET_VERS,next_comm003 + ajmp ANSWER_VERS +next_comm003: +; --------------------------------------------------------------------------- + cjne a,#CMD_SET_BAUD_RATE,next_comm004 + ajmp BAUD_RATE_SET +next_comm004: +; --------------------------------------------------------------------------- + cjne a,#CMD_SET_SIM_CLC,next_comm005 + ajmp SIM_CLC_SET +next_comm005: +; --------------------------------------------------------------------------- + ajmp end_i2c_interrupt +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +jmp_data_receive: + ;What receive ? Data to SIM/Command to bridge + jb bit_data_sim_wr, jmp_data_sim_receive + jb i2c_write_command,jmp_comm_bridge_receive + ajmp end_i2c_interrupt +; --------------------------------------------------------------------------- +jmp_comm_bridge_receive: + mov @R0,I2DAT + inc R0 + inc length_command + ajmp end_i2c_interrupt +; --------------------------------------------------------------------------- +jmp_data_sim_receive: + + setb i2c_write_mode + + PUSH DPH + PUSH DPL + mov DPL,pointer_TXL + mov DPH,pointer_TXH + mov a,I2DAT + movx @DPTR,a + inc DPTR + mov pointer_TXL,DPL + mov pointer_TXH,DPH + POP DPL + POP DPH + + inc length_send_to_simL + mov a,length_send_to_simL + jnz $+2+2 ;2 + inc length_send_to_simH ;2 + + ajmp end_i2c_interrupt +nextttt00002: +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne R7,#0A0h,nextttt00003 ;STOP + setb pin_led ;LED OFF + + ;Command finish ? + jnb i2c_write_command,jmp_not_command + clr i2c_write_command + setb i2c_command_done +jmp_not_command: + + ;data to SIM finish ? + jnb i2c_write_mode,end_i2c_interrupt + clr i2c_write_mode + + setb i2c_write_done + ;Prepare to answer + mov length_answer_simH,#0 + mov length_answer_simL,#0 + mov pointer_RX1H,#HIGH(XRAM_RX_BUFF) + mov pointer_RX1L,#LOW (XRAM_RX_BUFF) + mov pointer_RX2H,#HIGH(XRAM_RX_BUFF) + mov pointer_RX2L,#LOW (XRAM_RX_BUFF) + + ajmp end_i2c_interrupt +nextttt00003: +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne R7,#0A8h,nextttt00004 + sjmp read_byte_I2C +nextttt00004: +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + cjne R7,#0B8h,nextttt00005 +read_byte_I2C: + jnb bit_command_buff,jmp_not_comm_buff2 + mov I2DAT,@R0 + inc R0 + ajmp end_i2c_interrupt + +jmp_not_comm_buff2: + jb bit_length_answerH,jmp_not_comm_buff3 + setb bit_length_answerH + mov I2DAT,length_answer_simH + ajmp end_i2c_interrupt + +jmp_not_comm_buff3: + jb bit_length_answerL,read_byte_APROM + setb bit_length_answerL + mov I2DAT,length_answer_simL + ajmp end_i2c_interrupt + +read_byte_APROM: + PUSH DPH + PUSH DPL + mov DPL,pointer_RX2L + mov DPH,pointer_RX2H + movx a,@DPTR + mov I2DAT,a + inc DPTR + mov pointer_RX2L,DPL + mov pointer_RX2H,DPH + POP DPL + POP DPH + +nextttt00005: +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +end_i2c_interrupt: + clr STA + clr STO + setb AA +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +pop_i2c_psw: + POP PSW + POP ACC + clr SI + reti + + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +ANSWER_VERS: + mov R0,#data_command + mov cmd_command,#CMD_GET_VERS + mov (data_command+0),#2 + mov (data_command+1),#VERS_HI + mov (data_command+2),#VERS_LO + setb bit_command_buff + ajmp end_i2c_interrupt + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +ATR_GENERATE: + setb bit_generate_ATR + ;Prepare to answer + mov length_answer_simH,#0 + mov length_answer_simL,#0 + mov pointer_RX1H,#HIGH(XRAM_RX_BUFF) + mov pointer_RX1L,#LOW (XRAM_RX_BUFF) + mov pointer_RX2H,#HIGH(XRAM_RX_BUFF) + mov pointer_RX2L,#LOW (XRAM_RX_BUFF) + ajmp end_i2c_interrupt + + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +BAUD_RATE_SET: + mov R0,#data_command + mov length_command,#0 + mov cmd_command,#CMD_SET_BAUD_RATE + setb i2c_write_command + ajmp end_i2c_interrupt + + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +SIM_CLC_SET: + mov R0,#data_command + mov length_command,#0 + mov cmd_command,#CMD_SET_SIM_CLC + setb i2c_write_command + ajmp end_i2c_interrupt + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +WRITEDATASIM: + mov length_send_to_simH,#0 + mov length_send_to_simL,#0 + setb bit_data_sim_wr + mov pointer_TXH,#HIGH(XRAM_TX_BUFF) + mov pointer_TXL,#LOW (XRAM_TX_BUFF) + ajmp end_i2c_interrupt + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +main_start: + mov SP,#STACKKKKK +; --------------------------------------------------------------------------- + ;0-bidirect 1-push pull 0-input only 1-open drain + ;0 0 1 1 +; --------------------------------------------------------------------------- + mov P0M2,#01000000b ;?0 + mov P0M1,#11111111b ;P1.6-Tx0 SIM; + ; + mov P1M2,#01011111b ;1 + mov P1M1,#10111000b ;P1.6-Tx1 DEBUG; P1.4,P1.3 - I2C; + + mov P3M2,#00000000b ;P3 + mov P3M1,#11111111b ; +; --------------------------------------------------------------------------- + mov TMOD, #22h + mov TH0, #0 ;14400hz + mov TH1, #0E9h ;UART0 10800 Bit/sec + mov TCON, #55h +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + mov TA,#0AAh + mov TA,#055h + orl SFRS,#00000001b + + mov P1S, #00010000b ;P1.4 trigger schmiddt + + mov TA,#0AAh + mov TA,#055h + anl SFRS,#11111110b +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + ;------- CONFIG I2C --------- + mov I2CON, #44h ;set AA, set I2C enable + setb pin_sda + setb pin_scl + mov I2ADDR,#0C0h +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + ;mov SCON, #050h ;UART0 8bit + mov SCON, #0D0h ;UART0 9bit + ;mov PCON, #11000000b;FE_0 enable + mov PCON, #10000000b;FE_0 disable +; --------------------------------------------------------------------------- + mov SCON_1,#050h ;UART1 + ;mov T3CON, #01101000b;FE_1 enable TIMER3 UART0 BAUD + ;mov T3CON, #00101000b;FE_1 disable TIMER3 UART0 BAUD + mov T3CON, #00001000b;FE_1 disable TIMER1 UART0 BAUD + ;mov RL3,#0E9h ;10800/21600 + ;mov RH3,#0FFh +; --------------------------------------------------------------------------- + ;UART1 + mov RL3,#0F7h ;27777/55556 + mov RH3,#0FFh +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + mov CKDIV,#2 ;Fsys=4.00MHZ + ;mov CKDIV,#1 ;Fsys=8.00MHZ +; --------------------------------------------------------------------------- + mov bit_32,#0 + mov bit_33,#0 + setb time_data_read.7 +; --------------------------------------------------------------------------- + ;orl CKCON,#00000010b ;ENABLE CLC TIMER1 Fsys/12 + orl CKCON,#00010010b ;ENABLE CLC TIMER1 Fsys +; --------------------------------------------------------------------------- + ;mov a,RCTRIM0 + ;add a,#31 + ;mov TA,#0AAh + ;mov TA,#055h + ;mov RCTRIM0,a +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + acall clr_buffer +; --------------------------------------------------------------------------- + mov EIE, #00000001b ;I2C Interrupt + ;mov IE, #10010000b ;EA, SERIAL0 + mov IE, #10010010b ;EA, SERIAL0, TIMER0 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +main_loop: + acall control_ATR + acall control_send_to_sim + acall control_command + sjmp main_loop + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +control_command: + jbc i2c_command_done,$+3+1 ;3 + ret ;1 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + ;Control Length command=1 + mov a,length_command + cjne a,#1,next_commandEND ;error length_command != 1 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + mov a,cmd_command + cjne a,#CMD_SET_BAUD_RATE,next_command001 + mov TH1,data_command ;Timer1 HIGH byte + ret +next_command001: +; --------------------------------------------------------------------------- + cjne a,#CMD_SET_SIM_CLC, next_command002 + mov CKDIV,data_command ;Fsys DIV + ret +next_command002: +; --------------------------------------------------------------------------- +next_commandEND: + ret + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +control_send_to_sim: + jb i2c_write_done,$+3+1 ;3 + ret ;1 +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + jbc bit_wait_confirm,jmp_wait_confirm +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + mov DPTR,#XRAM_TX_BUFF +looop_send: + movx a,@DPTR + inc DPTR + acall for_coooooom0 + + clr c + mov a,length_send_to_simL + subb a,#1 + mov length_send_to_simL,a + mov a,length_send_to_simH + subb a,#0 + mov length_send_to_simH,a + orl a,length_send_to_simL + jnz looop_send +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + jnb bit_RX0,$ + clr i2c_write_done + ret + + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +jmp_wait_confirm: + mov DPTR,#(XRAM_TX_BUFF+1) + movx a,@DPTR + mov R3,a + mov R4,#5 +; --------------------------------------------------------------------------- + mov DPTR,#XRAM_TX_BUFF +looop_seend: + movx a,@DPTR + inc DPTR + acall for_coooooom0 + djnz R4,jmp_not_5byte + + jnb bit_RX0,$ + clr bit_RX0 + ;18/12/2018 + mov time_confirm,#65 ;New timeout 50mS +looop_waitconf: + jb time_confirm.7,jmp_no_answer + jnb bit_RX0,looop_waitconf + + ;clr pin_scl ;TEST PULSE! + mov a,SBUF + xrl a,R3 + ;setb pin_scl ;TEST PULSE! + + jnz jmp_no_correct_answer ;18/12/2018 + + ;pause for next byte 17/12/2018 + mov R7,#0 + djnz R7,$ ;~260mkSec + djnz R7,$ ;~260mkSec + djnz R7,$ ;~260mkSec + +jmp_not_5byte: + + clr c + mov a,length_send_to_simL + subb a,#1 + mov length_send_to_simL,a + mov a,length_send_to_simH + subb a,#0 + mov length_send_to_simH,a + orl a,length_send_to_simL + jnz looop_seend +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- + jnb bit_RX0,$ + clr bit_RX0 +jmp_no_answer: + clr i2c_write_done + ret + +; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- +;18/12/2018 +jmp_no_correct_answer: + clr EA + clr i2c_write_done + + mov DPL,pointer_RX1L + mov DPH,pointer_RX1H + mov a,SBUF + movx @DPTR,a + inc DPTR + mov pointer_RX1L,DPL + mov pointer_RX1H,DPH + + clr pin_scl + mov time_data_read,#52 ;52/1302Hz = 40mS + + inc length_answer_simL + mov a,length_answer_simL + jnz $+2+2 ;2 + inc length_answer_simH ;2 + + setb EA + ret + + + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +control_ATR: + jbc bit_generate_ATR,$+3+1 ;3 + ret ;1 +; --------------------------------------------------------------------------- + clr pin_RST + ;acall clr_buffer + ; Add rezet pause 17/12/2018 + + mov R6,#200 +looop_pause50mS: + djnz R7,$ ;~260mkSec + djnz R6,looop_pause50mS + + ;Prepare to answer 11/03/2019 + acall clr_buffer + + mov length_answer_simH,#0 + mov length_answer_simL,#0 + mov pointer_RX1H,#HIGH(XRAM_RX_BUFF) + mov pointer_RX1L,#LOW (XRAM_RX_BUFF) + mov pointer_RX2H,#HIGH(XRAM_RX_BUFF) + mov pointer_RX2L,#LOW (XRAM_RX_BUFF) + setb bit_first_ATR + setb pin_RST + ret + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +for_coooooom0: + clr bit_RX0 + mov c,P + mov TB8,c ;9bit parity + mov SBUF,a + jnb bit_TX0,$ + clr bit_TX0 + mov R7,#100 + djnz R7,$ + ret + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +clr_buffer: + mov DPTR,#XRAM_RX_BUFF ;Receive SIM buffer 192+192 = 384b + acall clr_192buffer ;06/12/2022 +; --------------------------------------------------------------------------- +clr_192buffer: + mov R7,#192 + clr a +looop_clr_bufff: + movx @DPTR,a + inc DPTR + djnz R7,looop_clr_bufff + ret + +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- +;for_coooooom1: +; mov SBUF_1,a +; jnb TI_1,$ +; clr TI_1 +; ret +; +; --------------------------------------------------------------------------- +; =========================================================================== +; --------------------------------------------------------------------------- + +end. diff --git a/traces/README.md b/traces/README.md index b6cad9fe8..939905017 100644 --- a/traces/README.md +++ b/traces/README.md @@ -102,3 +102,4 @@ |hf_visa_apple_transit_bypass.trace |Sniff of VISA Apple transaction bypass| |hf_mfdes_sniff.trace |Sniff of HID reader reading a MIFARE DESFire SIO card| |hf_iclass_sniff.trace |Sniff of HID reader reading a Picopass 2k card| +|hf_mf_hid_sio_sim.trace |Simulation of a HID SIO MFC 1K card| diff --git a/traces/hf_mf_hid_sio_sim.trace b/traces/hf_mf_hid_sio_sim.trace new file mode 100644 index 000000000..4477a9a4b Binary files /dev/null and b/traces/hf_mf_hid_sio_sim.trace differ