diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 78ee6c3cf..d3e6ee423 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -27,7 +27,7 @@ jobs: - name: ProxSpace version run: | - $psversion = (Select-String -Pattern 'PSVERSION=' -SimpleMatch -Path "C:\ProxSpace\msys2\ps\09-proxspace_setup.post").Line.Split("""")[1] + $psversion = (Select-String -Pattern 'PSVERSION=' -SimpleMatch -Path "C:\ProxSpace\setup\09-proxspace_setup.post").Line.Split("""")[1] Write-Host "ProxSpace version: $psversion" - name: ProxSpace initial startup diff --git a/.gitignore b/.gitignore index 543624a4f..8ff65578d 100644 --- a/.gitignore +++ b/.gitignore @@ -73,6 +73,7 @@ tools/cryptorf/sm tools/cryptorf/sma tools/cryptorf/sma_multi tools/mf_nonce_brute/mf_nonce_brute +tools/mf_nonce_brute/mf_trace_brute fpga/* !fpga/tests diff --git a/.travis.yml.old b/.travis.yml.old deleted file mode 100644 index fbf7e1df3..000000000 --- a/.travis.yml.old +++ /dev/null @@ -1,64 +0,0 @@ -# Travis-CI Build for RfidResearchGroup/Proxmark3 -language: c - -#default linux build env is: xenial -compiler: gcc - -os: - - linux - - osx - -dist: bionic - -osx_image: xcode11 - -# move some env variables to homebrew env -env: - global: - - HOMEBREW_TRAVIS_BRANCH=$TRAVIS_BRANCH - - HOMEBREW_TRAVIS_COMMIT=$TRAVIS_COMMIT - jobs: - - TO_TEST=MAKEFILE MAKE_PARAMS='PLATFORM_EXTRAS=' - - TO_TEST=MAKEFILE MAKE_PARAMS='PLATFORM_EXTRAS=BTADDON' - - TO_TEST=CMAKE - -addons: - apt: - packages: - - gcc-arm-none-eabi - - libnewlib-dev - - libbluetooth-dev - - python3-dev - - libbz2-dev - homebrew: - packages: - - readline - - qt5 - - RfidResearchGroup/proxmark3/arm-none-eabi-gcc - taps: RfidResearchGroup/proxmark3 -# update trick to fix https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296/14 - update: true - -before_install: - # bug? - # homebrew update replaced python2.7 by python3.7 but - # python3 link failed while python@2 still present, so let's do it again: - if [ "$TRAVIS_OS_NAME" == "osx" ]; then - brew link --overwrite python; - fi - -install: - if ! arm-none-eabi-gcc -v; then - echo "arm-none-eabi-gcc [ERROR]"; - travis_terminate 1; - fi - -script: - - if [ "$TO_TEST" = "MAKEFILE" ]; then - make clean && make V=1 "$MAKE_PARAMS"; - make check; - fi - - if [ "$TO_TEST" = "CMAKE" ]; then - mkdir -p client/build && ( cd client/build && cmake .. && make VERBOSE=1 ); - make client/check CHECKARGS="--clientbin ./client/build/proxmark3"; - fi diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 67f0f235e..0bbc07ac9 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -36,7 +36,7 @@ { "label": "client: Debug: make", "type": "shell", - "command": "make client -j DEBUG=1", + "command": "make client -j DEBUG=1 SANITIZE=1", "problemMatcher": [ "$gcc" ], @@ -45,7 +45,7 @@ { "label": "client: Debug: clean & make", "type": "shell", - "command": "make client/clean && make client -j DEBUG=1", + "command": "make client/clean && make client -j DEBUG=1 SANITIZE=1", "problemMatcher": [ "$gcc" ], diff --git a/CHANGELOG.md b/CHANGELOG.md index bced7ef1a..327ac9f61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,79 @@ 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] + - Change `hf 14a/14b/15 list etc alias commands now unified helptext (@doegox) + - Change `trace list` - now colors whole reader line (@iceman1001) + - Change `lf search` - add option `-c` to continue searching after first hit (@doegox) + - Fix DESFire mis-annotation (@VortixDev) + - Change `lf pac demod` - now also search for inverted bitstreams (@iceman1001) + - Change `hf 14b reader` - now supports continous mode (@iceman1001) + - Fix `hf search` - now doesn't false identify ISO15693 (@iceman1001) + - Change emv commands now works with tokenized cards (@merlokk) + - Change `hf 15 restore` - now also support EML/JSON (@iceman1001) + - Change - all commands now use cliparser (@iceman1001) + - Change `lf t55xx restore` - now also support JSON (@iceman1001) + - Change `hf mf csetuid` - adapted to accept 7byte uids ~untested~ (@iceman1001) + - Added `hf mf view/eload/cload` - now accepts bin/eml/json (@iceman1001) + - Added `hf mf eload/cload' - now accepts bin/eml/json (@iceman1001) + - Fix RESTORE mis-annotation (@VortixDev) + - Changed `smart raw` - now uses NG (@iceman1001) + - Added `hf iclass configcard` - now can download / generate config card dumps with a cardhelper [WIP] (@iceman1001) + - Fix swapped DESELECT and WTX annotations (@VortixDev) + - Fixed `hf 15 findafi` - cliparser bug, and loop is user interruptable (@iceman1001) + - Added `hf mf wipe` - takes a keyfile and wipes a MFC to default values (@iceman1001) + - Changed `pm3 shell` - now prioritise USB connections in WSL1 (@gator96100) + - Added `hf mf restore -w` - now supports usage of specified keyfile to authenticate with instead of default FFFFFF key (@iceman1001) + - Added `mem info --pem`- now supports loading of private key in PEM format (@iceman1001) + - Changed `hf mfp auth` - more verbose message (@vortixdev) + - Fix `hf mfdes rd` - AES chained mode fixes (@anon) + - Added `weigand decode -b` - now supports binary wiegand string as input (@iceman1001) + - Updated `README.md` - new link to a GUI project, and textual update (@iceman1001) + - Changed `hf search` - reenabled FeliCa search (@iceman1001) + - Changed `auto -a` - now allows for keep on searching, useful when detecting unknown multi tech cards (@iceman1001) + - Added more aid (@anon) + - Changed `hw version` - now also looks for FPGA chip id match (@iceman1001) + - Added WSL checks for outdated bootloader (@gator96100) + - Changed many, many more commands uses cliparser (@iceman1001) + - Changed many many cppchecker fixes (@iceman1001) + - Changed some coverity scan fixes (@iceman1001) + - Added `hw break` - to break device side loops from client (@iceman1001) + - Changed `hf mf sim` - removed field detection on device side (@iceman1001) + - Changed `mf_nonce_brute` - to recover key for nested authentications from traces (@iceman1001) + - Changed `trace list -t mf` - now prints needed data for mf_nonce_brute (@iceman1001) + - Fix `trace list -t mf` crc overwrote first hex char (@iceman1001) + - Fix `trace list -t mf` to print correct parity "!" (@doegox) + - Fix `lf em 4x05 unlock` not to break when tear off happens (@doegox) + - Fix `mem spiffs view` to not print when file doesn't exist on flash mem (@iceman1001) + - Changed `hf mf rdsc, egetsc, cgetsc` to have unified output (@iceman1001) + - Changed `hf mf rdsc` output to be more clear (@iceman1001) + - Changed `mem dump -c` now supports optional column breaks of output (@iceman1001) + - Changed unified client menu's to use curly brackets to indicate a sub category (@iceman1001) + - Added `mem spiffs view` to view external flash memory (@iceman1001) + - Fix 'lf t55xx config, chk, bruteforce' some logic to got mixed up in cliparser conversion (@mwalker33) + - Moved countones / bitcount fcts to utils.c (@doegox) + - Changed tunings in `hf mfu opttear` (@doegox) + - Updated the text in T5577_Guide.md to match new output (@iceman1001) + - Added a new t55xx configblock found in the wild (@iceman1001) + - Added `mem info -d` to support manual input of data (@iceman1001) + - Fix 'lf t55xx detect' some logic to got mixed up in cliparser conversion (@mwalker33) + - Added `HF_CRAFTBYTE` standalone mode that reads and emulates 14a UID (@craftbyte) + - Added cryptoRF trace (@iceman1001) + - Changed default tcp port to 18888 (@iceman1001) + - Added more default keys to MFC dictionary (@iceman1001) + - Added one more icode slix2 signature to recover_pk.py (@iceman1001) + - Changed C15001 format to use OEM = 900 by default (@iceman1001) - Added a script to dump originality signatures from MFU EV1s and NTAGs (@aveao) + - Fix `hf emrtd` segfaults (@aveao) + - Fix `lf em 4150` timings on device side (@tharexde) + -Changed dependecies fixes in build scripts (@doegox, @iceman1001, @gator96100) + - Changed `recover_pk.py` to support colors (@iceman1001) + - Changed flashing feedback text to include needed steps (@iceman1001) + - Changed `lf_em4100_bulk.lua` in order to handle Proxspace odd 32b limit (@iceman1001) + - Added `script run lf_em4100_bulk.lua` a EM4100 bulk enrolling script (@iceman1001) + - Changed amiitool now linked, compiles in cmake, and outlined code usage in cmdhfmfu.c (@iceman1001) + - Changed `hf mfu info` - to have a fingerprint fct (@iceman1001) - Added parsing of EF_CardAccess to display PACE algorithm, version and parameter in `hf emrtd info` (@aveao) - - Change, numerous commands more uses cliparser (@tcprst, @iceman1001) + - Changed numerous commands more uses cliparser (@tcprst, @iceman1001) - Added more originality public keys (@anon) - Added `hf 14a info` - now also verify MFC Ev1 signatures (@iceman1001) - Added `LF_THAREXDE` standalone mode which simulates and reads EM4x50 cards (@tharexde) @@ -19,7 +89,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added support for older vid/pid detection (@Gator96100) - Added `hf mfdes bruteaid` - proper bruteforce of DESFire AID when no reading of files is possible (@craftbyte) - Added support for bidirectional communication for `lf em 4x50 sim` (@tharexde) - - Change `PLATFORM=PM3OTHER` to `PLATFORM=PM3GENERIC` (@iceman1001) + - Changed `PLATFORM=PM3OTHER` to `PLATFORM=PM3GENERIC` (@iceman1001) - Added `tools/hitag2crack/crack5opencl`, an optimized version of `crack5gpu` (@matrix) - Fixed Makefile to account for changes when running on Apple Silicon (@tcprst) - Added support for debugging ARM with JTAG & VSCode (@Gator96100) diff --git a/Makefile b/Makefile index e42c0caa9..77b7b9741 100644 --- a/Makefile +++ b/Makefile @@ -249,14 +249,6 @@ endif # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) -cliparser: - # Get list of all commands - cat doc/commands.md | grep -e ^\|\` | cut -f 2 -d "\`" | grep -v 'help\|list\|mem spiffs\|quit\|exit' | awk '{$$1=$$1};1' > cliparser_all_commands.tmp - # Get list of cliparserized commands - grep -r CLIParserInit ./client/src/ | cut -f 2 -d "\"" | awk '{$$1=$$1};1' > cliparser_done.tmp - # Determine commands that still need cliparser conversion - grep -xvf cliparser_done.tmp cliparser_all_commands.tmp > ./doc/cliparser_todo.txt - style: # Make sure astyle is installed @which astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 ) diff --git a/README.md b/README.md index 224e822af..515c2cbff 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ | ------------------- |:-------------------:| -------------------:| |[What has changed?](#what-has-changed) | **[Setup and build for Linux](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md)** | [Compilation Instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md)| |[Development](#development) | **[Important notes on ModemManager for Linux users](/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md)** | [Validating proxmark client functionality](/doc/md/Use_of_Proxmark/1_Validation.md) | -|[Why didn't you base it on official Proxmark3 Master?](#why-didnt-you-base-it-on-official-proxmark3-master)| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| +|[Maintainers](#maintainers--package-distro-)| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| |[Proxmark3 GUI](#proxmark3-gui)|**[Setup and build for Windows](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md)**|[Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)| -|[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) || -|[Donations](#Donations)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.md)| +|[Pre-compiled binaries](#precompiled-binaries)|[Blue shark manual](/doc/bt_manual_v10.md) || +|[Donations](#donations)||[Command Cheat sheet](/doc/cheatsheet.md)| ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| -||**[JTAG](/doc/jtag_notes.md)**|[T55xx Guide](/doc/T5577_Guide.md)| +||**[JTAG](/doc/jtag_notes.md)**|[T5577 Introduction Guide](/doc/T5577_Guide.md)| ## Notes / helpful documents @@ -39,68 +39,107 @@ |[Notes on Color usage](/doc/colors_notes.md)|[Makefile vs CMake](/doc/md/Development/Makefile-vs-CMake.md)|[Notes on Cloner guns](/doc/cloner_notes.md)| |[Notes on cliparser usage](/doc/cliparser.md)|[Notes on clocks](/doc/clocks.md)|| - -## Build for Proxmark3 RDV4 +# How to build? +## Proxmark3 RDV4 See the instruction links in the tables above to build, flash and run for your Proxmark3 RDV4 device. -## Build for generic Proxmark3 platforms +## Generic Proxmark3 platforms In order to build this repo for generic Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) -With generic Proxmark3 platforms we mean: - - RDV1 - - RDV2 - - RDV3 easy - - Proxmark Evolution (needs extra care) - - Radiowar black PCB version +We define generic Proxmark3 platforms as following devices. + +**Supported** + - RDV1, RDV2, RDV3 easy - Ryscorp green PCB version - - Ryscorp Pm3Pro - - VX + - Radiowar black PCB version - numerous Chinese adapted versions of the RDV3 easy (kkmoon, pisworks etc) -> ⚠ **Note**: About flash memory size of other Proxmark3 platforms. You need to keep a eye on how large your ARM chip built-in flash memory is. With 512kb you are fine but if its 256kb you need to compile this repo with even less functionality. When running the `./pm3-flash-all` you can see which size your device have if you have the bootloader from this repo installed. Otherwise you will find the size reported in the start message when running the Proxmark3 client `./pm3`. +**Not supported** + - ⚠ Proxmark Evolution (EVO) + - **Note**: unknown pin assignments. + - ⚠ Ryscorp Proxmark3 Pro + - **Note**: device has different fpga and unknown pin assignments. + +**Unknown support status** + - ⚠ VX + +**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. +> 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) + ## What has changed? -On the hardware side: - - * added flash memory 256kb. +Proxmark3 RDV4 hardware modifications: + * added flash memory 256kb * added smart card module - * added FPC connector + * added FPC connector for peripherals such as Bluetooth+battery addon + * improved antennas + * swappable + * LF Q factor switch + * LF 125/134 frequency switch + * tiny PCB form factor + * ABS case -On the software side: +This repo vs official Proxmark3 repo: +see the [Changelog file](CHANGELOG.md) which we try to keep updated. In short this repo gives you a completely different user experience when it comes to Proxmark3. + * richer CLI with use of colors / emojis + * help text system implemented everywhere + * hints system + * user preference settings + * extensive testing with continuous integration build systems on Linux, OSX and Windows, and regular usage of static analysis tools like + * [Coverity Scan](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo/) + * Cppcheck + * GCC and Clang aggressive enforcement of diagnostic flags + * auto detection of serial ports and seamless integration with Bluetooth addon + * reconnect to device from inside client + * Supports tearoff attacks + * the most comprehensive compiled known keys dictionaries + * Slimed down usb communications with NG-frames + * the most compiled public known key recovery software + * the fastest implementations of said software + * support multiple fileformats for dump files (BIN/EML/JSON) + * interoperability of said fileformats with libnfc, MFC tool app etc + * Supports more RFID based protocols than ever + * Easy install for package maintainers, distro maintainers + * More documentation + + + +All of this and more is what makes this repo different from any other Proxmark3 firmware / client software. It is truely bleeding edge in that sense of available functionality and experience with it. With all extensive testing its also quite more stable than any other Proxmark3 repo. -quite a lot, see the [Changelog file](CHANGELOG.md) which we try to keep updated. ## Development > ⚠ **Note**: 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**. +> 👉 **Remember!** If you intend to contribute to the code, please read the [coding style notes](HACKING.md) 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. + +The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-Roadmap) is an excellent start to read if you are interesting in contributing. + + +## Supported operative systems This repo compiles nicely on - - Proxspace v3.x - - [latest release v3.8](https://github.com/Gator96100/ProxSpace/releases) - - Windows/mingw environment with Qt5.6.1 & GCC 4.9 - - Ubuntu 16.04 -> 20.04 - - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian - - Rasbian + - WSL1 on Windows 10 + - Proxspace v3.9 [release v3.9](https://github.com/Gator96100/ProxSpace/releases) + - Windows/MinGW environment + - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian - Android / Termux - - Mac OS X / Homebrew / Apple Silicon - - WSL1 (Windows subsystem linux) on Windows 10 + - Mac OS X / Homebrew / Apple Silicon M1 - Docker container - [ RRG / Iceman repo based ubuntu 18.04 container ](https://hub.docker.com/r/secopsconsult/proxmark3) - [ Iceman fork based container v1.7 ](https://hub.docker.com/r/iceman1001/proxmark3/) -Hardware to run client on - - PC - - Android - - Raspberry Pi, Raspberry Pi Zero - - Nvidia Jetson Nano ## Precompiled binaries -We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where [@gator96100](https://github.com/gator96100) has set up a AWS bucket with precompiled Proxspace (Mingw) binaries which is recompiled every night and with that also up-to-date. We link to these files here as to make it easier for users. - -_If you use his pre-compiled Proxspace binaries do consider buy him a coffee for his efforts. Remember nothing says thank you as good as a donation._ - -If you are having troubles with these files, contact the package maintainer [@gator96100](https://github.com/gator96100) and read the [homepage of his proxmark builds](https://www.proxmarkbuilds.org/) or read the [sticky thread at forum](http://www.proxmark.org/forum/viewtopic.php?pid=24763#p24763) where known issues has been documented with regards to the precompiled builds. +We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where package maintainer [@gator96100](https://github.com/gator96100) has set up a AWS bucket with precompiled Proxspace (MinGW) binaries which are recompiled every night and with that also up-to-date. We link to these files here as to make it easier for users. If you are having troubles with these files, we suggest to read the [homepage of his proxmark builds](https://www.proxmarkbuilds.org/) or read the [sticky thread at forum](http://www.proxmark.org/forum/viewtopic.php?pid=24763#p24763) ### Proxmark3 RDV4 devices - [Precompiled builds for RDV40 dedicated x64](https://www.proxmarkbuilds.org/#rdv40-64/) @@ -110,43 +149,14 @@ If you are having troubles with these files, contact the package maintainer [@ga - [Precompiled builds for RRG / Iceman repository x64](https://www.proxmarkbuilds.org/#rrg_other-64/) -## Roadmap -The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-Roadmap) is an excellent start to read if you are interesting in contributing. - -> 👉 **Remember!** If you intend to contribute to the code, please read the [coding style notes](HACKING.md) 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. - - -## Issues & Troubleshooting -Please search the [issues](https://github.com/rfidresearchgroup/proxmark3/issues) page here and see if your issue is listed in the first instance. -Read the [Troubleshooting guide](/doc/md/Installation_Instructions/Troubleshooting.md) to weed out most known problems. - -Next place to visit is the [Proxmark3 Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) -You will find many blogposts, youtube videos, tweets, reddit - -### Offical channels +## Official channels +Where do you find the community? - [RFID Hacking community discord server](https://discord.gg/QfPvGFRQxH) - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - - [Proxmark3 Twitter](https://twitter.com/proxmark3/) - [Proxmark3 forum](http://www.proxmark.org/forum/index.php) - - _no slack channel_ -### Youtube channels -Iceman has quite a few videos on his channel and Quentyn has risen up the last year with good informative videos. We suggest you check them out and smash that subscribe buttons! - - - [Iceman channel](https://www.youtube.com/c/ChrisHerrmann1001) - - [Quentyn Taylor](https://www.youtube.com/channel/UCL91C3IZDv3wfj2ABhdRIrw) - - [Hacker warehouse channel](https://www.youtube.com/channel/UCimS6P854cQ23j6c_xst7EQ) - -_if you think of some more good youtube channels to be on this list, let us know!_ - -## Cheat sheet - -You can enjoy a [command cheat sheet](/doc/cheatsheet.md) and we are trying to keep it updated. -[Thanks to Alex Dib!](https://github.com/scund00r) - ## Maintainers ( package, distro ) To all distro, package maintainers, we tried to make your life easier. @@ -154,19 +164,19 @@ To all distro, package maintainers, we tried to make your life easier. `make install` is now available and if you want to know more. - [Notes for maintainers](/doc/md/Development/Maintainers.md) -## Why didn't you base it on official Proxmark3 Master? - -The separation from official Proxmark3 repo gives us a lot of freedom to create a firmware/client that suits the RDV40 features. We don't want to mess up the official Proxmark3 repo with RDV40 specific code. - ## Proxmark3 GUI The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer. -The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers are. There has been brought to our attention that there is quite a few Chinese Windows GUI available. Usually you find them on alibaba / taobao ads but we have no idea which fw/client they are compatible with. Proceed with caution if you decide to go down that road. +- [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. + +- [Proxmark3 GUI cross-compiled](https://github.com/wh201906/Proxmark3GUI/) which is recently updated and claims to support latest source of this repo. + # Donations -Nothing says thank you as much as a donation. So if you feel the love, do feel free to become a iceman patron. For some tiers it comes with rewards. +Nothing says thank you as much as a donation. -https://www.patreon.com/iceman1001 +If you feel the love, do feel free to become a [Iceman patron](https://www.patreon.com/iceman1001). +If you are using @gator96100 's Proxspace with pre-compiled binaries do consider buy him a coffee for his efforts. diff --git a/appveyor.yml b/appveyor.yml index 899202bf1..b07bce303 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -106,7 +106,7 @@ clone_script: Write-Host "[ OK ]" -ForegroundColor Gree - $psversion = (Select-String -Pattern 'PSVERSION=' -SimpleMatch -Path "$env:proxspace_path\msys2\ps\09-proxspace_setup.post").Line.Split("""")[1] + $psversion = (Select-String -Pattern 'PSVERSION=' -SimpleMatch -Path "$env:proxspace_path\setup\09-proxspace_setup.post").Line.Split("""")[1] Write-Host "ProxSpace version: $psversion" -ForegroundColor Yellow diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 95a19dbb7..b4dd2bc05 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -206,7 +206,7 @@ uint32_t BigBuf_get_traceLen(void) { /** This is a function to store traces. All protocols can use this generic tracer-function. The traces produced by calling this function can be fetched on the client-side - by 'hf list raw', alternatively 'hf list ' for protocol-specific + by 'hf list -t raw', alternatively 'hf list -t ' for protocol-specific annotation of commands/responses. **/ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) { diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index 552267611..b0e343b0b 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -50,6 +50,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_COLIN | Mifare ultra fast sniff/sim/clone | | (RDV4 only) | - Colin Brigato | +----------------------------------------------------------+ +| HF_CRAFTBYTE | UID stealer - Emulates scanned 14a UID | +| | - Anze Jensterle | ++----------------------------------------------------------+ | HF_ICECLASS | Simulate HID iCLASS legacy ags | | (RDV4 only) | storing in flashmem | +----------------------------------------------------------+ @@ -71,7 +74,7 @@ define KNOWN_STANDALONE_DEFINITIONS endef STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE -STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_YOUNG +STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_YOUNG STANDALONE_MODES_REQ_SMARTCARD := STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 519dc2087..2d77c7c03 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -77,3 +77,7 @@ endif 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 +endif diff --git a/armsrc/Standalone/hf_14asniff.c b/armsrc/Standalone/hf_14asniff.c index c52928825..16d351ef0 100644 --- a/armsrc/Standalone/hf_14asniff.c +++ b/armsrc/Standalone/hf_14asniff.c @@ -31,14 +31,14 @@ * * To retrieve trace data from flash: * - * 1. mem spiffs dump o hf_14asniff.trc f trace.trc + * 1. mem spiffs dump -s hf_14asniff.trc -d trace.trc * Copies trace data file from flash to your PC. * * 2. trace load trace.trc * Loads trace data from a file into PC-side buffers. * - * 3. For ISO14a: trace list 14a 1 - * For MIFARE Classic: trace list mf 1 + * 3. For ISO14a: trace list -t 14a -1 + * For MIFARE Classic: trace list -t mf -1 * * Lists trace data from buffer without requesting it from PM3. * @@ -71,20 +71,20 @@ static void DownloadTraceInstructions(void) { Dbprintf(""); Dbprintf("To get the trace from flash and display it:"); - Dbprintf("1. mem spiffs dump o "HF_14ASNIFF_LOGFILE" f trace.trc"); - Dbprintf("2. trace load trace.trc"); - Dbprintf("3. trace list 14a 1"); + Dbprintf("1. mem spiffs dump -s "HF_14ASNIFF_LOGFILE" -d trace.trc"); + Dbprintf("2. trace load -f trace.trc"); + Dbprintf("3. trace list -t 14a -1"); } void ModInfo(void) { - DbpString("hf_14asniff: standalone 'hf 14a sniff', storing in flashmem"); + DbpString(" HF 14A SNIFF, a ISO14443a sniffer with storing in flashmem"); DownloadTraceInstructions(); } void RunMod(void) { StandAloneMode(); - Dbprintf("Starting standalone mode: hf_14asniff"); + Dbprintf(_YELLOW_("HF 14A SNIFF started")); rdv40_spiffs_lazy_mount(); SniffIso14443a(0); @@ -118,7 +118,7 @@ void RunMod(void) { SpinErr(LED_A, 200, 5); SpinDelay(100); + Dbprintf("-=[ exit ]=-"); LEDsoff(); - SpinDelay(300); DownloadTraceInstructions(); } diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c new file mode 100644 index 000000000..83bc01896 --- /dev/null +++ b/armsrc/Standalone/hf_craftbyte.c @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright 2020 Anze Jensterle +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for hf_craftbyte +//----------------------------------------------------------------------------- +// +// +// `hf_craftyte` continuesly scans for ISO14443a card UID and then emulates it. +// + +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "string.h" +#include "BigBuf.h" +#include "iso14443a.h" +#include "protocols.h" +#include "cmd.h" + +#define STATE_READ 0 +#define STATE_EMUL 1 + +void ModInfo(void) { + DbpString("HF CRAFTBYTE mode - scans and emulates ISO14443a UID (craftbyte)"); +} + +void RunMod(void) { + StandAloneMode(); + Dbprintf(_YELLOW_("HF CRAFTBYTE mode started")); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // the main loop for your standalone mode + for (;;) { + WDT_HIT(); + + // exit from RunMod, send a usbcommand. + if (data_available()) break; + + iso14a_card_select_t card; + + SpinDelay(500); + + // 0 = search, 1 = read, 2 = emul + int state = STATE_READ; + + DbpString("Scanning..."); + int button_pressed = BUTTON_NO_CLICK; + for (;;) { + // Was our button held down or pressed? + button_pressed = BUTTON_HELD(1000); + + if (button_pressed != BUTTON_NO_CLICK || data_available()) + break; + else if (state == STATE_READ) { + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + if (iso14443a_select_card(NULL, &card, NULL, true, 0, true) == false) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + SpinDelay(500); + continue; + } else { + Dbprintf("Found card with SAK: %02X, ATQA: %02X %02X, UID: ", card.sak, card.atqa[0], card.atqa[1]); + Dbhexdump(card.uidlen, card.uid, 0); + state = STATE_EMUL; + } + } else if (state == STATE_EMUL) { + uint8_t flags = 0; + if (card.uidlen == 4) { + flags |= FLAG_4B_UID_IN_DATA; + } else if (card.uidlen == 7) { + flags |= FLAG_7B_UID_IN_DATA; + } else if (card.uidlen == 10) { + flags |= FLAG_10B_UID_IN_DATA; + } else { + Dbprintf("Unusual UID length, something is wrong. Try again please."); + state = STATE_READ; + continue; + } + + Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); + SimulateIso14443aTag(3, flags, card.uid, 0); + + // Go back to search state if user presses pm3-button + state = STATE_READ; + } + } + if (button_pressed == BUTTON_HOLD) //Holding down the button + break; + } + + Dbprintf("-=[ exit ]=-"); + LEDsoff(); +} diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 27e1bf5ab..ee34b98e8 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -109,7 +109,7 @@ static bool have_aa2(void) { return memcmp(aa2_key, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); } -static uint8_t get_pagemap(const picopass_hdr *hdr) { +static uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; } @@ -130,27 +130,27 @@ static void download_instructions(uint8_t t) { switch (t) { case ICE_STATE_FULLSIM: { DbpString("The emulator memory was saved to SPIFFS"); - DbpString("1. " _YELLOW_("mem spiffs dump o " HF_ICLASS_FULLSIM_MOD_BIN " f " HF_ICLASS_FULLSIM_MOD" e")); + DbpString("1. " _YELLOW_("mem spiffs dump -s " HF_ICLASS_FULLSIM_MOD_BIN " -d " HF_ICLASS_FULLSIM_MOD" -e")); DbpString("2. " _YELLOW_("hf iclass view -f " HF_ICLASS_FULLSIM_MOD_BIN)); break; } case ICE_STATE_ATTACK: { DbpString("The collected data was saved to SPIFFS. The file names below may differ"); DbpString("1. " _YELLOW_("mem spiffs tree")); - DbpString("2. " _YELLOW_("mem spiffs dump o " HF_ICLASS_ATTACK_BIN " f " HF_ICLASS_ATTACK_BIN)); + DbpString("2. " _YELLOW_("mem spiffs dump -s " HF_ICLASS_ATTACK_BIN " -d " HF_ICLASS_ATTACK_BIN)); DbpString("3. " _YELLOW_("hf iclass loclass -f " HF_ICLASS_ATTACK_BIN)); break; } case ICE_STATE_READER: { DbpString("The found tags was saved to SPIFFS"); DbpString("1. " _YELLOW_("mem spiffs tree")); - DbpString("2. " _YELLOW_("mem spiffs dump h")); + DbpString("2. " _YELLOW_("mem spiffs dump -h")); break; } case ICE_STATE_DUMP_SIM: { DbpString("The found tag will be dumped to " HF_ICALSSS_READSIM_TEMP_BIN); DbpString("1. " _YELLOW_("mem spiffs tree")); - DbpString("2. " _YELLOW_("mem spiffs dump h")); + DbpString("2. " _YELLOW_("mem spiffs dump -h")); break; } } @@ -322,7 +322,7 @@ static int reader_dump_mode(void) { set_tracing(false); - picopass_hdr *hdr = (picopass_hdr *)card_data; + picopass_hdr_t *hdr = (picopass_hdr_t *)card_data; // select tag. uint32_t eof_time = 0; @@ -458,7 +458,7 @@ static int dump_sim_mode(void) { set_tracing(false); - picopass_hdr *hdr = (picopass_hdr *)card_data; + picopass_hdr_t *hdr = (picopass_hdr_t *)card_data; // select tag. uint32_t eof_time = 0; diff --git a/armsrc/Standalone/hf_legic.c b/armsrc/Standalone/hf_legic.c index 6ebe6eff6..aa694b029 100644 --- a/armsrc/Standalone/hf_legic.c +++ b/armsrc/Standalone/hf_legic.c @@ -30,7 +30,7 @@ * * To retrieve dump files from flash: * - * 1. mem spiffs dump o hf-legic-XXYYZZWW-dump.bin f hf-legic-XXYYZZWW-dump.bin + * 1. mem spiffs dump -s hf-legic-XXYYZZWW-dump.bin -d hf-legic-XXYYZZWW-dump.bin * Copies log file from flash to your client. * * @@ -39,7 +39,7 @@ * * To delete a dump file from flash: * - * 1. mem spiffs remove hf-legic-XXYYZZWW-dump.bin + * 1. mem spiffs remove -f hf-legic-XXYYZZWW-dump.bin * */ @@ -50,7 +50,7 @@ static void DownloadLogInstructions(void) { Dbprintf("[=] " _YELLOW_("-") " mem spiffs tree"); Dbprintf(""); Dbprintf("[=] To save a dump file from flash to client:"); - Dbprintf("[=] " _YELLOW_("-") " mem spiffs dump o hf-legic-UID-dump.bin f hf-legic-UID-dump.bin"); + Dbprintf("[=] " _YELLOW_("-") " mem spiffs dump -s hf-legic-UID-dump.bin -d hf-legic-UID-dump.bin"); } #endif diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index e14e89789..8d03c911d 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -55,9 +55,9 @@ static iso14a_card_select_t p_card; // Pseudo-configuration block. static bool printKeys = false; // Prints keys -static bool transferToEml = true; // Transfer keys to emulator memory +//static bool transferToEml = true; // Transfer keys to emulator memory static bool ecfill = true; // Fill emulator memory with cards content. -static bool simulation = true; // Simulates an exact copy of the target tag +//static bool simulation = true; // Simulates an exact copy of the target tag static bool fillFromEmulator = false; // Dump emulator memory. //----------------------------------------------------------------------------- @@ -482,7 +482,8 @@ void RunMod(void) { // If enabled, transfers found keys to memory and loads target content in emulator memory. Then it simulates to be the tag it has basically cloned. - if ((transferToEml) && (allKeysFound)) { +// if ((transferToEml) && (allKeysFound)) { + if (allKeysFound) { emlClearMem(); @@ -517,7 +518,8 @@ void RunMod(void) { } } - if ((filled == PM3_SUCCESS) && simulation) { +// if ((filled == PM3_SUCCESS) && simulation) { + if (filled == PM3_SUCCESS) { Dbprintf("\t [✓] Emulator memory filled, simulation started."); // This will tell the fpga to emulate using previous keys and current target tag content. diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index f9d8b0b59..a15e4a7eb 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -326,7 +326,8 @@ void RunMod(void) { if (i == 4) { - if (apdubuffer[1] == 0x1b && apdubuffer[2] == 0xd1 && !gotndef) { //Get NDEF Data + // Get NDEF Data + if (apdubuffer[1] == 0x1b && apdubuffer[2] == 0xd1) { gotndef = true; memcpy(&ndef, &apdubuffer, apdulen - 2); break; diff --git a/armsrc/Standalone/lf_em4100rswb.c b/armsrc/Standalone/lf_em4100rswb.c index aaf82b5b5..b3f2c571e 100644 --- a/armsrc/Standalone/lf_em4100rswb.c +++ b/armsrc/Standalone/lf_em4100rswb.c @@ -272,7 +272,7 @@ static int ExecuteMode(int mode, int slot) { case LF_RWSB_MODE_SIM: Dbprintf("[=] >> Sim mode started <<"); construct_EM410x_emul(rev_quads(low[slot])); - SimulateTagLowFrequency(buflen, 0, 1); + SimulateTagLowFrequency(buflen, 0, true); return LF_RWSB_UNKNOWN_RESULT; case LF_RWSB_MODE_WRITE: Dbprintf("[!!] >> Write mode started <<"); diff --git a/armsrc/Standalone/lf_em4100rwc.c b/armsrc/Standalone/lf_em4100rwc.c index 5df6e1a90..35f9b32b0 100644 --- a/armsrc/Standalone/lf_em4100rwc.c +++ b/armsrc/Standalone/lf_em4100rwc.c @@ -194,7 +194,7 @@ void RunMod(void) { construct_EM410x_emul(rev_quads(low[selected])); flash_leds(100, 5); - SimulateTagLowFrequency(buflen, 0, 1); + SimulateTagLowFrequency(buflen, 0, true); led_slot(selected); state = 0; // Switch to select mode } diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index d125b0c03..0b1e6cbfe 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -36,7 +36,7 @@ * * To retrieve log file from flash: * - * 1. mem spiffs dump o lf_hidcollect.log f lf_hidcollect.log + * 1. mem spiffs dump -s lf_hidcollect.log -d lf_hidcollect.log * Copies log file from flash to your client. * * 2. exit the Proxmark3 client @@ -48,7 +48,7 @@ * * To delete the log file from flash: * - * 1. mem spiffs remove lf_hidcollect.log + * 1. mem spiffs remove -f lf_hidcollect.log */ #define LF_HIDCOLLECT_LOGFILE "lf_hidcollect.log" @@ -57,7 +57,7 @@ static void DownloadLogInstructions(void) { Dbprintf(""); Dbprintf("[=] To get the logfile from flash and display it:"); - Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump o "LF_HIDCOLLECT_LOGFILE" f "LF_HIDCOLLECT_LOGFILE); + Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump -s "LF_HIDCOLLECT_LOGFILE" -d "LF_HIDCOLLECT_LOGFILE); Dbprintf("[=] " _YELLOW_("2.") " exit proxmark3 client"); Dbprintf("[=] " _YELLOW_("3.") " cat "LF_HIDCOLLECT_LOGFILE); } diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c index 2a7a686fb..f06cd9b07 100644 --- a/armsrc/Standalone/lf_tharexde.c +++ b/armsrc/Standalone/lf_tharexde.c @@ -42,25 +42,25 @@ * - LED D: unmounting/sync'ing flash (normally < 100ms) * * To upload input file (eml format) to flash: - * - mem spiffs load f o lf_em4x50_simulate.eml + * - mem spiffs upload -s -d lf_em4x50_simulate.eml * * To retrieve password file from flash: - * - mem spiffs dump o lf_em4x50_passwords.log f + * - mem spiffs dump -s lf_em4x50_passwords.log * * To retrieve log file from flash: - * - mem spiffs dump o lf_em4x50_collect.log f + * - mem spiffs dump -s lf_em4x50_collect.log * * This module emits debug strings during normal operation -- so try it out in * the lab connected to PM3 client before taking it into the field. * * To delete the input file from flash: - * - mem spiffs remove lf_em4x50_simulate.eml + * - mem spiffs remove -f lf_em4x50_simulate.eml * * To delete the log file from flash: - * - mem spiffs remove lf_em4x50_passwords.log + * - mem spiffs remove -f lf_em4x50_passwords.log * * To delete the log file from flash: - * - mem spiffs remove lf_em4x50_collect.log + * - mem spiffs remove -f lf_em4x50_collect.log */ #define STATE_SIM 0 @@ -77,14 +77,14 @@ static void LoadDataInstructions(const char *inputfile) { Dbprintf("To load datafile to flash and display it:"); Dbprintf("1. edit input file %s", inputfile); Dbprintf("2. start proxmark3 client"); - Dbprintf("3. mem spiffs load f o %s", inputfile); + Dbprintf("3. mem spiffs upload -s -d %s", inputfile); Dbprintf("4. start standalone mode"); } static void DownloadLogInstructions(const char *logfile) { Dbprintf(""); Dbprintf("To get the logfile from flash and display it:"); - Dbprintf("1. mem spiffs dump o %s f ", logfile); + Dbprintf("1. mem spiffs dump -s %s", logfile); Dbprintf("2. exit proxmark3 client"); Dbprintf("3. cat "); } diff --git a/armsrc/Standalone/readme.md b/armsrc/Standalone/readme.md index 3775d848c..69880f1a3 100644 --- a/armsrc/Standalone/readme.md +++ b/armsrc/Standalone/readme.md @@ -77,6 +77,7 @@ If your mode is using one of the unique features of the RDV4, add it to the prop ``` STANDALONE_MODES_REQ_SMARTCARD := STANDALONE_MODES_REQ_FLASH := +STANDALONE_MODES_REQ_BT := ``` ## Update MAKEFILE.INC diff --git a/armsrc/appmain.c b/armsrc/appmain.c index e9c34e2cc..f5e9ba489 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -49,6 +49,7 @@ #include "commonutil.h" #include "crc16.h" + #ifdef WITH_LCD #include "LCD_disabled.h" #endif @@ -194,7 +195,7 @@ static void MeasureAntennaTuning(void) { payload.v_lf134 = adcval; // voltage at 134kHz if (i == sc->divisor) - payload.v_lfconf = adcval; // voltage at `lf config q` + payload.v_lfconf = adcval; // voltage at `lf config --divisor` payload.results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes @@ -940,7 +941,7 @@ static void PacketReceived(PacketCommandNG *packet) { } PACKED; struct p *payload = (struct p *)packet->data.asBytes; // length, start gap, led control - SimulateTagLowFrequency(payload->len, payload->gap, 1); + SimulateTagLowFrequency(payload->len, payload->gap, true); reply_ng(CMD_LF_SIMULATE, PM3_EOPABORTED, NULL, 0); LED_A_OFF(); break; @@ -1084,7 +1085,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_HITAG_SIMULATE: { // Simulate Hitag tag, args = memory content - SimulateHitag2((bool)packet->oldarg[0], packet->data.asBytes); + SimulateHitag2(); break; } case CMD_LF_HITAG_READER: { // Reader for Hitag tags, args = type and function @@ -1111,6 +1112,18 @@ static void PacketReceived(PacketCommandNG *packet) { } break; } + case CMD_LF_HITAG_ELOAD: { + /* + struct p { + uint16_t len; + uint8_t *data; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + uint8_t *mem = BigBuf_get_EM_addr(); + memcpy((uint8_t *)mem.sectors, payload->data, payload->len); + */ + break; + } #endif #ifdef WITH_EM4x50 @@ -1311,12 +1324,20 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_FELICALITE_SIMULATE: { - felica_sim_lite(packet->oldarg[0]); + struct p { + uint8_t uid[8]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + felica_sim_lite(payload->uid); break; } case CMD_HF_FELICA_SNIFF: { - felica_sniff(packet->oldarg[0], packet->oldarg[1]); - reply_ng(CMD_HF_FELICA_SNIFF, PM3_SUCCESS, NULL, 0); + struct p { + uint32_t samples; + uint32_t triggers; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + felica_sniff(payload->samples, payload->triggers); break; } case CMD_HF_FELICALITE_DUMP: { @@ -1725,7 +1746,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_SMART_RAW: { - SmartCardRaw(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + SmartCardRaw((smart_card_raw_t *)packet->data.asBytes); break; } case CMD_SMART_UPLOAD: { @@ -2048,7 +2069,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_SPIFFS_PRINT_TREE: { - rdv40_spiffs_safe_print_tree(false); + rdv40_spiffs_safe_print_tree(); break; } case CMD_SPIFFS_PRINT_FSINFO: { @@ -2078,7 +2099,7 @@ static void PacketReceived(PacketCommandNG *packet) { 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_mix(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } @@ -2087,78 +2108,94 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs STAT : %s", filename); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Filename received for spiffs STAT : %s", filename); + } + int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)filename); - if (changed) rdv40_spiffs_lazy_unmount(); - reply_mix(CMD_ACK, size, 0, 0, 0, 0); + if (changed) { + rdv40_spiffs_lazy_unmount(); + } + + reply_ng(CMD_SPIFFS_STAT, PM3_SUCCESS, (uint8_t *)&size, sizeof(uint32_t)); LED_B_OFF(); break; } case CMD_SPIFFS_REMOVE: { LED_B_ON(); - uint8_t filename[32]; - uint8_t *pfilename = packet->data.asBytes; - memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs REMOVE : %s", filename); - rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE); + + struct p { + uint8_t len; + uint8_t fn[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Filename received for spiffs REMOVE : %s", payload->fn); + } + + rdv40_spiffs_remove((char *)payload->fn, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_REMOVE, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } case CMD_SPIFFS_RENAME: { LED_B_ON(); - uint8_t src[32]; - uint8_t dest[32]; - uint8_t *pfilename = packet->data.asBytes; - char *token; - token = strtok((char *)pfilename, ","); - strncpy((char *)src, token, sizeof(src) - 1); - token = strtok(NULL, ","); - strncpy((char *)dest, token, sizeof(dest) - 1); + struct p { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Filename received as source for spiffs RENAME : %s", src); - Dbprintf("Filename received as destination for spiffs RENAME : %s", dest); + Dbprintf("SPIFFS RENAME"); + Dbprintf("Source........ %s", payload->src); + Dbprintf("Destination... %s", payload->dest); } - rdv40_spiffs_rename((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_rename((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_RENAME, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } case CMD_SPIFFS_COPY: { LED_B_ON(); - uint8_t src[32]; - uint8_t dest[32]; - uint8_t *pfilename = packet->data.asBytes; - char *token; - token = strtok((char *)pfilename, ","); - strncpy((char *)src, token, sizeof(src) - 1); - token = strtok(NULL, ","); - strncpy((char *)dest, token, sizeof(dest) - 1); + struct p { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Filename received as source for spiffs COPY : %s", src); - Dbprintf("Filename received as destination for spiffs COPY : %s", dest); + Dbprintf("SPIFFS COPY"); + Dbprintf("Source........ %s", payload->src); + Dbprintf("Destination... %s", payload->dest); } - rdv40_spiffs_copy((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_copy((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_COPY, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } case CMD_SPIFFS_WRITE: { LED_B_ON(); - uint8_t filename[32]; - uint32_t append = packet->oldarg[0]; - uint32_t size = packet->oldarg[1]; - uint8_t *data = packet->data.asBytes; - uint8_t *pfilename = packet->data.asBytes; - memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - data += SPIFFS_OBJ_NAME_LEN; - if (DBGLEVEL >= DBG_DEBUG) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); + flashmem_write_t *payload = (flashmem_write_t *)packet->data.asBytes; - if (!append) { - rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); - } else { - rdv40_spiffs_append((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("SPIFFS WRITE, dest `%s` with APPEND set to: %c", payload->fn, payload->append ? 'Y' : 'N'); } - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + + if (payload->append) { + rdv40_spiffs_append((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_write((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE); + } + + reply_ng(CMD_SPIFFS_WRITE, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } diff --git a/armsrc/cmd.c b/armsrc/cmd.c index b0dc4aef8..7a94cf536 100644 --- a/armsrc/cmd.c +++ b/armsrc/cmd.c @@ -101,7 +101,9 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, uint8_t *data, size_t // overwrite status txBufferNG.pre.status = PM3_EOVFLOW; } - txBufferNG.pre.length = len; + + // length is only 15bit (32768) + txBufferNG.pre.length = (len & 0x7FFF); // Add the (optional) content to the frame, with a maximum size of PM3_CMD_DATA_SIZE if (data && len) { @@ -115,7 +117,7 @@ static int reply_ng_internal(uint16_t cmd, int16_t status, uint8_t *data, size_t if ((g_reply_via_fpc && g_reply_with_crc_on_fpc) || ((g_reply_via_usb) && g_reply_with_crc_on_usb)) { uint8_t first, second; compute_crc(CRC_14443_A, (uint8_t *)&txBufferNG, sizeof(PacketResponseNGPreamble) + len, &first, &second); - tx_post->crc = (first << 8) + second; + tx_post->crc = ((first << 8) | second); } else { tx_post->crc = RESPONSENG_POSTAMBLE_MAGIC; } @@ -150,7 +152,7 @@ int reply_ng(uint16_t cmd, int16_t status, 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) { - uint16_t status = PM3_SUCCESS; + int16_t status = PM3_SUCCESS; uint64_t arg[3] = {arg0, arg1, arg2}; if (len > PM3_CMD_DATA_SIZE - sizeof(arg)) { len = PM3_CMD_DATA_SIZE - sizeof(arg); @@ -159,9 +161,9 @@ int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *d uint8_t cmddata[PM3_CMD_DATA_SIZE]; memcpy(cmddata, arg, sizeof(arg)); if (len && data) - memcpy(cmddata + sizeof(arg), data, len); + memcpy(cmddata + sizeof(arg), data, (int)len); - return reply_ng_internal(cmd, status, cmddata, len + sizeof(arg), false); + return reply_ng_internal((cmd & 0xFFFF), status, cmddata, len + sizeof(arg), false); } static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *data, size_t len), bool usb, bool fpc) { @@ -230,7 +232,7 @@ static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *da rx->ng = false; rx->magic = 0; rx->crc = 0; - rx->cmd = rx_old.cmd; + rx->cmd = (rx_old.cmd & 0xFFFF); rx->oldarg[0] = rx_old.arg[0]; rx->oldarg[1] = rx_old.arg[1]; rx->oldarg[2] = rx_old.arg[2]; diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index d414835c0..782466be2 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -430,7 +430,7 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, break; // Append MAC size_t bla = maced_data_length(DESFIRE(tag)->session_key, *nbytes - offset) + offset; - bla++; + (void)bla++; memcpy(res + *nbytes, mac, 4); @@ -443,7 +443,7 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, if (append_mac) { size_t len = maced_data_length(key, *nbytes); - ++len; + (void)++len; memcpy(res, data, *nbytes); memcpy(res + *nbytes, DESFIRE(tag)->cmac, CMAC_LENGTH); *nbytes += CMAC_LENGTH; diff --git a/armsrc/felica.c b/armsrc/felica.c index 7aa524e62..55995cbb1 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -208,9 +208,9 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // 0x00 = timeslot // 0x09 0x21 = crc static uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21}; - int len = 20; + int len = 10; - // We try 20 times, or if answer was received. + // We try 10 times, or if answer was received. do { // end-of-reception response packet data, wait approx. 501μs // end-of-transmission command packet data, wait approx. 197μs @@ -572,23 +572,53 @@ void felica_sendraw(PacketCommandNG *c) { } void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { - int remFrames = (samplesToSkip) ? samplesToSkip : 0; - Dbprintf("Sniff Felica: Getting first %d frames, Skipping after %d triggers.\n", samplesToSkip, triggersToSkip); + clear_trace(); set_tracing(true); iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD); + LED_D_ON(); - uint16_t numbts = 0; + + int retval = PM3_SUCCESS; + int remFrames = (samplesToSkip) ? samplesToSkip : 0; int trigger_cnt = 0; uint32_t timeout = iso18092_get_timeout(); bool isReaderFrame = true; - while (!BUTTON_PRESS()) { + + uint8_t flip = 0; + uint16_t checker = 0; + for (;;) { + WDT_HIT(); + + // since simulation is a tight time critical loop, + // we only check for user request to end at iteration 3000, 9000. + if (flip == 3) { + if (data_available()) { + retval = PM3_EOPABORTED; + break; + } + flip = 0; + } + + if (checker >= 3000) { + + if (BUTTON_PRESS()) { + retval = PM3_EOPABORTED; + break; + } + flip++; + checker = 0; + } + ++checker; + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); Process18092Byte(dist); + if ((dist >= 178) && (++trigger_cnt > triggersToSkip)) { - Dbprintf("triggersToSkip kicked %d", dist); + Dbprintf("triggers To skip kicked %d", dist); break; } if (FelicaFrame.state == STATE_FULL) { @@ -599,7 +629,7 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { } remFrames--; if (remFrames <= 0) { - Dbprintf("Stop Sniffing - samplesToSkip reached!"); + Dbprintf("Stop Sniffing - samples To skip reached!"); break; } LogTrace(FelicaFrame.framebytes, @@ -609,7 +639,6 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { NULL, isReaderFrame ); - numbts += FelicaFrame.len; FelicaFrameReset(); } } @@ -617,11 +646,9 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { switch_off(); //reset framing AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); - set_tracelen(numbts); - set_tracelen(BigBuf_max_traceLen()); - Dbprintf("Felica sniffing done, tracelen: %i, use " _YELLOW_("`hf felica list`") " for annotations", BigBuf_get_traceLen()); - reply_mix(CMD_ACK, 1, numbts, 0, 0, 0); + Dbprintf("Felica sniffing done, tracelen: %i", BigBuf_get_traceLen()); + reply_ng(CMD_HF_FELICA_SNIFF, retval, NULL, 0); LED_D_OFF(); } @@ -630,51 +657,74 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { #define R_READBLK_LEN 0x21 //simulate NFC Tag3 card - for now only poll response works // second half (4 bytes) of NDEF2 goes into nfcid2_0, first into nfcid2_1 -void felica_sim_lite(uint64_t uid) { +void felica_sim_lite(uint8_t *uid) { - int i, curlen = 0; - uint8_t *curresp = 0; - - uint8_t ndef[8]; - num_to_bytes(uid, 8, ndef); - - //prepare our 3 responses... + // prepare our 3 responses... uint8_t resp_poll0[R_POLL0_LEN] = { 0xb2, 0x4d, 0x12, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0xb3, 0x7f}; uint8_t resp_poll1[R_POLL1_LEN] = { 0xb2, 0x4d, 0x14, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0x88, 0xb4, 0xb3, 0x7f}; uint8_t resp_readblk[R_READBLK_LEN] = { 0xb2, 0x4d, 0x1d, FELICA_RDBLK_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x04, 0x01, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x23, 0xcb, 0x6e}; - //NFC tag 3/ ISo technically. Many overlapping standards - DbpString("Felica Lite-S sim start"); + // NFC tag 3/ ISo technically. Many overlapping standards + DbpString("Felica Lite-S simulation start"); Dbprintf("NDEF2 UID: %02x %02x %02x %02x %02x %02x %02x %02x", - ndef[0], ndef[1], ndef[2], ndef[3], ndef[4], ndef[5], ndef[6], ndef[7] + uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7] ); - //fill in blanks - for (i = 0; i < 8; i++) { - resp_poll0[i + 4] = ndef[i]; - resp_poll1[i + 4] = ndef[i]; - resp_readblk[i + 4] = ndef[i]; + // fill in blanks + for (uint8_t i = 0; i < 8; i++) { + resp_poll0[i + 4] = uid[i]; + resp_poll1[i + 4] = uid[i]; + resp_readblk[i + 4] = uid[i]; } - //calculate and set CRC + // calculate and set CRC AddCrc(resp_poll0, resp_poll0[2]); AddCrc(resp_poll1, resp_poll1[2]); AddCrc(resp_readblk, resp_readblk[2]); iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD); + int retval = PM3_SUCCESS; + int curlen = 0; + uint8_t *curresp = NULL; bool listenmode = true; - //uint32_t frtm = GetCountSspClk(); + // uint32_t frtm = GetCountSspClk(); + + uint8_t flip = 0; + uint16_t checker = 0; for (;;) { - if (BUTTON_PRESS()) break; + + WDT_HIT(); + + // since simulation is a tight time critical loop, + // we only check for user request to end at iteration 3000, 9000. + if (flip == 3) { + if (data_available()) { + retval = PM3_EOPABORTED; + break; + } + flip = 0; + } + + if (checker >= 3000) { + + if (BUTTON_PRESS()) { + retval = PM3_EOPABORTED; + break; + } + flip++; + checker = 0; + } + ++checker; + WDT_HIT(); if (listenmode) { - //waiting for request... + // waiting for request... if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); - //frtm = GetCountSspClk(); + // frtm = GetCountSspClk(); Process18092Byte(dist); if (FelicaFrame.state == STATE_FULL) { @@ -683,7 +733,7 @@ void felica_sim_lite(uint64_t uid) { if (FelicaFrame.framebytes[2] == 6 && FelicaFrame.framebytes[3] == 0) { - //polling... there are two types of polling we answer to + // polling... there are two types of polling we answer to if (FelicaFrame.framebytes[6] == 0) { curresp = resp_poll0; curlen = R_POLL0_LEN; @@ -697,28 +747,30 @@ void felica_sim_lite(uint64_t uid) { } if (FelicaFrame.framebytes[2] > 5 && FelicaFrame.framebytes[3] == 0x06) { - //we should rebuild it depending on page size, but... - //Let's see first + // we should rebuild it depending on page size, but... + // Let's see first curresp = resp_readblk; curlen = R_READBLK_LEN; listenmode = false; } - //clear frame + // clear frame FelicaFrameReset(); } else { - //frame invalid, clear it out to allow for the next one + // frame invalid, clear it out to allow for the next one FelicaFrameReset(); } } } } - if (!listenmode) { - //trying to answer... here to start answering immediately. - //this one is a bit finicky. Seems that being a bit late is better than earlier - //TransmitFor18092_AsReader(curresp, curlen, frtm+512, 0, 0); + + + if (listenmode == false) { + // trying to answer... here to start answering immediately. + // this one is a bit finicky. Seems that being a bit late is better than earlier + // TransmitFor18092_AsReader(curresp, curlen, frtm+512, 0, 0); TransmitFor18092_AsReader(curresp, curlen, NULL, 0, 0); - //switch back + // switch back FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_NOMOD); FelicaFrameReset(); @@ -730,10 +782,11 @@ void felica_sim_lite(uint64_t uid) { switch_off(); - //reset framing + // reset framing AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); - DbpString("Felica Lite-S sim end"); + Dbprintf("FeliCa Lite-S emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); + reply_ng(CMD_HF_FELICALITE_SIMULATE, retval, NULL, 0); } #define RES_SVC_LEN 11 + 3 diff --git a/armsrc/felica.h b/armsrc/felica.h index 885815abb..f30de4b6f 100644 --- a/armsrc/felica.h +++ b/armsrc/felica.h @@ -6,7 +6,7 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Definitions internal to the app source. +// Definitions internal to the FeliCa functionality //----------------------------------------------------------------------------- #ifndef __FELICA_H #define __FELICA_H @@ -16,7 +16,7 @@ void felica_sendraw(PacketCommandNG *c); void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip); -void felica_sim_lite(uint64_t uid); +void felica_sim_lite(uint8_t *uid); void felica_dump_lite_s(void); #endif diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index c2916bfe2..e64b08338 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -106,15 +106,13 @@ static int16_t blocknr; static size_t flipped_bit = 0; static uint32_t byte_value = 0; -static int hitag2_reset(void) { +static void hitag2_reset(void) { tag.state = TAG_STATE_RESET; tag.crypto_active = 0; - return 0; } -static int hitag2_init(void) { +static void hitag2_init(void) { hitag2_reset(); - return 0; } // Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) @@ -126,14 +124,13 @@ static int hitag2_init(void) { #endif #define HITAG_FRAME_LEN 20 -#define HITAG_T_STOP 36 /* T_EOF should be > 36 */ -#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ -#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ +#define HITAG_T_STOP 37 /* T_EOF should be > 36 */ +#define HITAG_T_LOW 4 /* T_LOW should be 4..10 */ +#define HITAG_T_0_MIN 17 /* T[0] should be 18..22 */ #define HITAG_T_0 20 /* T[0] should be 18..22 */ #define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */ #define HITAG_T_1 30 /* T[1] should be 26..30 */ -//#define HITAG_T_EOF 40 /* T_EOF should be > 36 */ -#define HITAG_T_EOF 80 /* T_EOF should be > 36 */ +#define HITAG_T_EOF 80 /* T_EOF should be > 36 and must be larger than HITAG_T_TAG_CAPTURE_FOUR_HALF */ #define HITAG_T_WAIT_1_MIN 199 /* T_wresp should be 199..206 */ #define HITAG_T_WAIT_2_MIN 90 /* T_wait2 should be at least 90 */ #define HITAG_T_WAIT_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */ @@ -987,84 +984,330 @@ static bool hitag2_read_uid(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t return true; } +void EloadHitag(uint8_t *data, uint16_t len) { + memcpy(tag.sectors, data, sizeof(tag.sectors)); +} + // Hitag2 Sniffing + +// T0 18-22 fc (total time ZERO) +// T1 26-32 fc (total time ONE) +// Tstop 36 > fc (high field stop limit) +// Tlow 4-10 fc (reader field low time) void SniffHitag2(void) { + DbpString("Starting Hitag2 sniffing"); + LED_D_ON(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); BigBuf_free(); BigBuf_Clear_ext(false); clear_trace(); set_tracing(true); + /* + lf_init(false, false); + + // no logging of the raw signal + g_logging = lf_get_reader_modulation(); + uint32_t total_count = 0; + + uint8_t rx[20 * 8 * 2]; + while (BUTTON_PRESS() == false) { + + lf_reset_counter(); + + WDT_HIT(); + + size_t periods = 0; + uint16_t rxlen = 0; + memset(rx, 0x00, sizeof(rx)); + + // Use the current modulation state as starting point + uint8_t mod_state = lf_get_reader_modulation(); + + while (rxlen < sizeof(rx)) { + periods = lf_count_edge_periods(64); + // Evaluate the number of periods before the next edge + if (periods >= 24 && periods < 64) { + // Detected two sequential equal bits and a modulation switch + // NRZ modulation: (11 => --|) or (11 __|) + rx[rxlen++] = mod_state; + rx[rxlen++] = mod_state; + // toggle tag modulation state + mod_state ^= 1; + } else if (periods > 0 && periods < 24) { + // Detected one bit and a modulation switch + // NRZ modulation: (1 => -|) or (0 _|) + rx[rxlen++] = mod_state; + mod_state ^= 1; + } else { + mod_state ^= 1; + break; + } + } + + if (rxlen == 0) + continue; + + // tag sends 11111 + uid, + bool got_tag = ((memcmp(rx, "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00", 10) == 0)); + + if (got_tag) { + // mqnchester decode + bool bad_man = false; + uint16_t bitnum = 0; + for (uint16_t i = 0; i < rxlen; i += 2) { + if (rx[i] == 1 && (rx[i + 1] == 0)) { + rx[bitnum++] = 0; + } else if ((rx[i] == 0) && rx[i + 1] == 1) { + rx[bitnum++] = 1; + } else { + bad_man = true; + } + } + + if (bad_man) { + DBG DbpString("bad manchester"); + continue; + } + + if (bitnum < 5) { + DBG DbpString("too few bits"); + continue; + } + + // skip header 11111 + uint16_t i = 0; + if (got_tag) { + i = 5; + } + + // Pack the response into a byte array + rxlen = 0; + for (; i < bitnum; i++) { + uint8_t b = rx[i]; + rx[rxlen >> 3] |= b << (7 - (rxlen % 8)); + rxlen++; + } + + // skip spurious bit + if (rxlen % 8 == 1) { + rxlen--; + } + + // nothing to log + if (rxlen == 0) + continue; + + LogTrace(rx, nbytes(rxlen), 0, 0, NULL, false); + total_count += nbytes(rxlen); + } else { + // decode reader comms + LogTrace(rx, rxlen, 0, 0, NULL, true); + total_count += rxlen; + // Pack the response into a byte array + + // LogTrace(rx, nbytes(rdr), 0, 0, NULL, true); + // total_count += nbytes(rdr); + } + LED_A_INV(); + } + + lf_finalize(); + + Dbprintf("Collected %u bytes", total_count); + + */ + + // Set up eavesdropping mode, frequency divisor which will drive the FPGA + // and analog mux selection. + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); // 125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Configure output pin that is connected to the FPGA (for modulating) + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // Disable modulation, we are going to eavesdrop, not modulate ;) + LOW(GPIO_SSC_DOUT); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the reader frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // external trigger rising edge, load RA on rising edge of TIOA. + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_BOTH | AT91C_TC_ABETRG | AT91C_TC_LDRA_BOTH; + + // Enable and reset counter + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + int frame_count = 0, response = 0, overflow = 0, lastbit = 1, tag_sof = 4; + bool rising_edge = false, reader_frame = false, bSkip = true; + uint8_t rx[HITAG_FRAME_LEN]; + size_t rxlen = 0; + auth_table_len = 0; auth_table_pos = 0; + // Reset the received frame, frame count and timing info + memset(rx, 0x00, sizeof(rx)); + auth_table = (uint8_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); memset(auth_table, 0x00, AUTH_TABLE_LENGTH); - DbpString("Starting Hitag2 sniffing"); - LED_D_ON(); - - lf_init(false, false); - - g_logging = false; - - uint8_t periods_bytes[4]; - -// int16_t checked = 0; - - /*bool waiting_for_first_edge = true;*/ - LED_C_ON(); - - uint32_t signal_size = 10000; while (BUTTON_PRESS() == false) { - // use malloc - initSampleBufferEx(&signal_size, false); - WDT_HIT(); + memset(rx, 0x00, sizeof(rx)); - /* - // only every 1000th times, in order to save time when collecting samples. - if (checked == 1000) { - if (data_available()) { - checked = -1; - break; - } else { - checked = 0; + // Receive frame, watch for at most T0 * EOF periods + while (AT91C_BASE_TC1->TC_CV < (HITAG_T0 * HITAG_T_EOF)) { + // Check if rising edge in modulation is detected + if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values + int ra = (AT91C_BASE_TC1->TC_RA / HITAG_T0); + + // Find out if we are dealing with a rising or falling edge + rising_edge = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME) > 0; + + // Shorter periods will only happen with reader frames + if (reader_frame == false && rising_edge && ra < HITAG_T_TAG_CAPTURE_ONE_HALF) { + // Switch from tag to reader capture + LED_C_OFF(); + reader_frame = true; + rxlen = 0; + } + + // Only handle if reader frame and rising edge, or tag frame and falling edge + if (reader_frame == rising_edge) { + overflow += ra; + continue; + } + + // Add the buffered timing values of earlier captured edges which were skipped + ra += overflow; + overflow = 0; + + if (reader_frame) { + LED_B_ON(); + // Capture reader frame + if (ra >= HITAG_T_STOP) { +// if (rxlen != 0) { + //DbpString("wierd0?"); +// } + // Capture the T0 periods that have passed since last communication or field drop (reset) + response = (ra - HITAG_T_LOW); + } else if (ra >= HITAG_T_1_MIN) { + // '1' bit + rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); + rxlen++; + } else if (ra >= HITAG_T_0_MIN) { + // '0' bit + rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); + rxlen++; + } + + } else { + LED_C_ON(); + // Capture tag frame (manchester decoding using only falling edges) + if (ra >= HITAG_T_EOF) { +// if (rxlen != 0) { + //DbpString("wierd1?"); +// } + // Capture the T0 periods that have passed since last communication or field drop (reset) + // We always recieve a 'one' first, which has the falling edge after a half period |-_| + response = ra - HITAG_T_TAG_HALF_PERIOD; + + } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { + // Manchester coding example |-_|_-|-_| (101) + rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); + rxlen++; + rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); + rxlen++; + + } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { + // Manchester coding example |_-|...|_-|-_| (0...01) + rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); + rxlen++; + // We have to skip this half period at start and add the 'one' the second time + if (bSkip == false) { + rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); + rxlen++; + } + lastbit = !lastbit; + bSkip = !bSkip; + + } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + if (tag_sof) { + // Ignore bits that are transmitted during SOF + tag_sof--; + } else { + // bit is same as last bit + rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); + rxlen++; + } } } - ++checked; - */ - - - // Receive frame, watch for at most T0*EOF periods -// lf_reset_counter(); - - // Wait "infinite" for reader modulation - size_t periods = lf_detect_gap(10000); - - // Test if we detected the first reader modulation edge - if (periods != 0) { - if (g_logging == false) { - g_logging = true; - LED_D_ON(); } } - /*lf_count_edge_periods(10000);*/ - while ((periods = lf_detect_gap(64)) != 0) { - num_to_bytes(periods, 4, periods_bytes); - LogTrace(periods_bytes, 4, 0, 0, NULL, true); + // Check if frame was captured + if (rxlen) { + frame_count++; + LogTrace(rx, nbytes(rxlen), response, 0, NULL, reader_frame); + + // Check if we recognize a valid authentication attempt + if (nbytes(rxlen) == 8) { + // Store the authentication attempt + if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) { + memcpy(auth_table + auth_table_len, rx, 8); + auth_table_len += 8; + } + } + + // Reset the received frame and response timing info + memset(rx, 0x00, sizeof(rx)); + response = 0; + reader_frame = false; + lastbit = 1; + bSkip = true; + tag_sof = 4; + overflow = 0; + + LED_B_OFF(); + LED_C_OFF(); + } else { + // Save the timer overflow, will be 0 when frame was received + overflow += (AT91C_BASE_TC1->TC_CV / HITAG_T0); } + // Reset the frame length + rxlen = 0; + // Reset the timer to restart while-loop that receives frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; } - lf_finalize(); + LEDsoff(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + set_tracing(false); + + Dbprintf("frame received: %d", frame_count); + Dbprintf("Authentication Attempts: %d", (auth_table_len / 8)); - DbpString("Hitag2 sniffing finish. Use `lf hitag list` for annotations"); } // Hitag2 simulation -void SimulateHitag2(bool tag_mem_supplied, uint8_t *data) { +void SimulateHitag2(void) { BigBuf_free(); BigBuf_Clear_ext(false); @@ -1092,12 +1335,6 @@ void SimulateHitag2(bool tag_mem_supplied, uint8_t *data) { // hitag2 state machine? hitag2_init(); - // copy user supplied emulation data - if (tag_mem_supplied) { - DbpString("Loading hitag2 memory..."); - memcpy((uint8_t *)tag.sectors, data, 48); - } - // printing uint32_t block = 0; for (size_t i = 0; i < 12; i++) { @@ -1660,7 +1897,7 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false); -// TODO when using cumulative time for command_start, pm3 doesn't reply anymore, e.g. on lf hitag read 23 4F4E4D494B52 +// TODO when using cumulative time for command_start, pm3 doesn't reply anymore, e.g. on lf hitag reader --23 -k 4F4E4D494B52 // Use delta time? // command_start = response_start + response_duration; command_start = 0; diff --git a/armsrc/hitag2.h b/armsrc/hitag2.h index ec3d0d7f3..ef92f0560 100644 --- a/armsrc/hitag2.h +++ b/armsrc/hitag2.h @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // (c) 2012 Roel Verdult +// modified 2021 Iceman // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -15,8 +16,8 @@ #include "hitag.h" void SniffHitag2(void); -void SimulateHitag2(bool tag_mem_supplied, uint8_t *data); +void SimulateHitag2(void); void ReaderHitag(hitag_function htf, hitag_data *htd); void WriterHitag(hitag_function htf, hitag_data *htd, int page); - +void EloadHitag(uint8_t *data, uint16_t len); #endif diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index 022730075..7d396196e 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -1542,7 +1542,7 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { //check for valid input if (page == 0) { Dbprintf( - "usage: lf hitag writer [03 | 04] [CHALLENGE | KEY] [page] [byte0] [byte1] [byte2] [byte3]"); + "usage: lf hitag writer [--03 | --04] [--nrar CHALLENGE | -k KEY] [-p page] -d [4 hex bytes]"); bStop = !false; } diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 162126d76..1b5a001ba 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -32,13 +32,12 @@ #define I2C_ERROR "I2C_WaitAck Error" -static volatile uint32_t c; - // Direct use the loop to delay. 6 instructions loop, Masterclock 48MHz, // delay=1 is about 200kbps // timer. // I2CSpinDelayClk(4) = 12.31us // I2CSpinDelayClk(1) = 3.07us +static volatile uint32_t c; static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { for (c = delay * 2; c; c--) {}; } @@ -100,7 +99,7 @@ void I2C_init(void) { AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); bool isok = (SCL_read && SDA_read); - if (!isok) + if (isok == false) I2C_recovery(); } @@ -181,7 +180,7 @@ static bool WaitSCL_L(void) { // It timeout reading response from card // Which ever comes first static bool WaitSCL_L_timeout(void) { - volatile uint32_t delay = 1800; + volatile uint32_t delay = 1700; while (delay--) { // exit on SCL LOW if (!SCL_read) @@ -194,7 +193,8 @@ static bool WaitSCL_L_timeout(void) { static bool I2C_Start(void) { - I2C_DELAY_XCLK(4); + I2C_DELAY_2CLK; + I2C_DELAY_2CLK; SDA_H; I2C_DELAY_1CLK; SCL_H; @@ -219,10 +219,7 @@ static bool I2C_WaitForSim(void) { // 8051 speaks with smart card. // 1000*50*3.07 = 153.5ms // 1byte transfer == 1ms with max frame being 256bytes - if (!WaitSCL_H_delay(10 * 1000 * 50)) - return false; - - return true; + return WaitSCL_H_delay(1000 * 300); } // send i2c STOP @@ -235,7 +232,10 @@ static void I2C_Stop(void) { I2C_DELAY_2CLK; if (!WaitSCL_H()) return; SDA_H; - I2C_DELAY_XCLK(8); + I2C_DELAY_2CLK; + I2C_DELAY_2CLK; + I2C_DELAY_2CLK; + I2C_DELAY_2CLK; } // Send i2c ACK @@ -626,7 +626,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) { - uint8_t i = 3; + uint8_t i = 5; int16_t len = 0; while (i--) { @@ -634,7 +634,6 @@ bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { len = I2C_BufferRead(dest, *destlen, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); - LED_C_ON(); if (len > 1) { @@ -656,7 +655,7 @@ bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { - if (!card_ptr) + if (card_ptr == NULL) return false; card_ptr->atr_len = 0; @@ -713,19 +712,23 @@ void SmartCardAtr(void) { set_tracing(true); I2C_Reset_EnterMainProgram(); smart_card_atr_t card; - int res = GetATR(&card, true) ? PM3_SUCCESS : PM3_ETIMEOUT; - reply_ng(CMD_SMART_ATR, res, (uint8_t *)&card, sizeof(smart_card_atr_t)); + if (GetATR(&card, true)) { + reply_ng(CMD_SMART_ATR, PM3_SUCCESS, (uint8_t *)&card, sizeof(smart_card_atr_t)); + } else { + reply_ng(CMD_SMART_ATR, PM3_ETIMEOUT, NULL, 0); + } set_tracing(false); LEDsoff(); +// StopTicks(); } -void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data) { - +void SmartCardRaw(smart_card_raw_t *p) { LED_D_ON(); uint8_t len = 0; uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME); - smartcard_command_t flags = arg0; + // check if alloacted... + smartcard_command_t flags = p->flags; if ((flags & SC_CLEARLOG) == SC_CLEARLOG) clear_trace(); @@ -735,28 +738,36 @@ void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data) { else set_tracing(false); - if ((flags & SC_CONNECT)) { + if ((flags & SC_CONNECT) == SC_CONNECT) { I2C_Reset_EnterMainProgram(); - if ((flags & SC_SELECT)) { + if ((flags & SC_SELECT) == SC_SELECT) { smart_card_atr_t card; bool gotATR = GetATR(&card, true); //reply_old(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); - if (!gotATR) + if (gotATR == false) { + reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0); goto OUT; + } } } if ((flags & SC_RAW) || (flags & SC_RAW_T0)) { - LogTrace(data, arg1, 0, 0, NULL, true); + LogTrace(p->data, p->len, 0, 0, NULL, true); - // Send raw bytes - // asBytes = A0 A4 00 00 02 - // arg1 = len 5 - bool res = I2C_BufferWrite(data, arg1, ((flags & SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), I2C_DEVICE_ADDRESS_MAIN); - if (!res && DBGLEVEL > 3) DbpString(I2C_ERROR); + bool res = I2C_BufferWrite( + p->data, + p->len, + ((flags & SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), + I2C_DEVICE_ADDRESS_MAIN + ); + if (res == false && DBGLEVEL > 3) { + DbpString(I2C_ERROR); + reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0); + goto OUT; + } // read bytes from module len = ISO7618_MAX_FRAME; @@ -767,8 +778,10 @@ void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data) { len = 0; } } + + reply_ng(CMD_SMART_RAW, PM3_SUCCESS, resp, len); + OUT: - reply_mix(CMD_ACK, len, 0, 0, resp, len); BigBuf_free(); set_tracing(false); LEDsoff(); diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 5f0f6dd18..bf80585eb 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -2,7 +2,7 @@ #define __I2C_H #include "common.h" -#include "mifare.h" +#include "pm3_cmd.h" #define I2C_DEVICE_ADDRESS_BOOT 0xB0 #define I2C_DEVICE_ADDRESS_MAIN 0xC0 @@ -39,7 +39,7 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose); // generice functions void SmartCardAtr(void); -void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data); +void SmartCardRaw(smart_card_raw_t *packet); void SmartCardUpgrade(uint64_t arg0); void SmartCardSetBaud(uint64_t arg0); void SmartCardSetClock(uint64_t arg0); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index e149254f7..5006436c1 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -35,7 +35,7 @@ #include "ticks.h" #include "iso15693.h" -static uint8_t get_pagemap(const picopass_hdr *hdr) { +static uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; } @@ -217,7 +217,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command - picopass_hdr *hdr = (picopass_hdr *)BigBuf_get_EM_addr(); + picopass_hdr_t *hdr = (picopass_hdr_t *)BigBuf_get_EM_addr(); uint8_t pagemap = get_pagemap(hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { do_iclass_simulation_nonsec(); @@ -678,7 +678,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { if (_mac[0] != receivedCmd[5] || _mac[1] != receivedCmd[6] || _mac[2] != receivedCmd[7] || _mac[3] != receivedCmd[8]) { Dbprintf("reader auth " _RED_("failed")); - Dbprintf("hf iclass lookup u %02x%02x%02x%02x%02x%02x%02x%02x p %02x%02x%02x%02x%02x%02x%02x%02x m %02x%02x%02x%02x%02x%02x%02x%02x f iclass_default_keys.dic", + Dbprintf("hf iclass lookup --csn %02x%02x%02x%02x%02x%02x%02x%02x --epurse %02x%02x%02x%02x%02x%02x%02x%02x --macs %02x%02x%02x%02x%02x%02x%02x%02x f iclass_default_keys.dic", csn_data[0], csn_data[1], csn_data[2], csn_data[3], csn_data[4], csn_data[5], csn_data[6], csn_data[7], card_challenge_data[0], card_challenge_data[1], card_challenge_data[2], card_challenge_data[3], card_challenge_data[4], card_challenge_data[5], card_challenge_data[6], card_challenge_data[7], @@ -860,7 +860,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (cmd == ICLASS_CMD_DETECT) { // 0x0F // not supported yet, ignore - } else if (cmd == 0x26 && len == 5) { +// } else if (cmd == 0x26 && len == 5) { // standard ISO15693 INVENTORY command. Ignore. } else { // Never seen this command before @@ -1021,7 +1021,7 @@ int do_iclass_simulation_nonsec(void) { bool button_pressed = false; uint8_t cmd, options, block; - int len = 0; + int len; bool exit_loop = false; while (exit_loop == false) { @@ -1213,7 +1213,7 @@ int do_iclass_simulation_nonsec(void) { goto send; // } else if(cmd == ICLASS_CMD_DETECT) { // 0x0F - } else if (cmd == 0x26 && len == 5) { +// } else if (cmd == 0x26 && len == 5) { // standard ISO15693 INVENTORY command. Ignore. } else { // Never seen this command before @@ -1275,7 +1275,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 *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) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; @@ -1393,7 +1393,7 @@ static bool select_iclass_tag_ex(picopass_hdr *hdr, bool use_credit_key, uint32_ return true; } -bool select_iclass_tag(picopass_hdr *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) { uint8_t result = 0; return select_iclass_tag_ex(hdr, use_credit_key, eof_time, &result); } @@ -1402,7 +1402,7 @@ bool select_iclass_tag(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_tim // turn off afterwards void ReaderIClass(uint8_t flags) { - picopass_hdr hdr = {0}; + picopass_hdr_t hdr = {0}; // uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; memset(resp, 0xFF, sizeof(resp)); @@ -1470,7 +1470,7 @@ void ReaderIClass(uint8_t flags) { switch_off(); } -bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out) { +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) { uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; uint8_t mac[4] = {0}; @@ -1538,7 +1538,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; // select card / e-purse - picopass_hdr hdr = {0}; + picopass_hdr_t hdr = {0}; iclass_premac_t *keys = (iclass_premac_t *)datain; @@ -1628,7 +1628,7 @@ void iClass_ReadBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; - picopass_hdr hdr = {0}; + picopass_hdr_t hdr = {0}; bool res = select_iclass_tag(&hdr, payload->use_credit_key, &eof_time); if (res == false) { if (payload->send_reply) { @@ -1701,7 +1701,7 @@ void iClass_Dump(uint8_t *msg) { // select tag. uint32_t eof_time = 0; - picopass_hdr hdr = {0}; + picopass_hdr_t hdr = {0}; bool res = select_iclass_tag(&hdr, req->use_credit_key, &eof_time); if (res == false) { if (req->send_reply) { @@ -1828,7 +1828,7 @@ void iClass_WriteBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; - picopass_hdr hdr = {0}; + picopass_hdr_t hdr = {0}; uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time); if (res == false) { goto out; @@ -1950,7 +1950,7 @@ void iClass_Restore(iclass_restore_req_t *msg) { uint16_t written = 0; uint32_t eof_time = 0; - picopass_hdr hdr = {0}; + picopass_hdr_t hdr = {0}; // select bool res = select_iclass_tag(&hdr, msg->req.use_credit_key, &eof_time); diff --git a/armsrc/iclass.h b/armsrc/iclass.h index b41b79ce8..83d6df302 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -34,6 +34,6 @@ 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 select_iclass_tag(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time); -bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); +bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time); +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 1509cc80c..8f419aebc 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -682,12 +682,12 @@ void RAMFUNC SniffIso14443a(uint8_t param) { uint32_t rx_samples = 0; // loop and listen - while (!BUTTON_PRESS()) { + while (BUTTON_PRESS() == false) { WDT_HIT(); LED_A_ON(); - int register readBufDataP = data - dma->buf; - int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + register int readBufDataP = data - dma->buf; + register int dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; if (readBufDataP <= dmaBufDataP) dataLen = dmaBufDataP - readBufDataP; else @@ -2006,7 +2006,7 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { analogCnt++; - analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF_RDV40]; + analogAVG += (AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF_RDV40] & 0x3FF); AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; @@ -2034,7 +2034,7 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { analogCnt++; - analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF]; + analogAVG += (AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF] & 0x3FF); AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 66539c807..42edf63e9 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -245,7 +245,7 @@ void CodeIso15693AsTag(uint8_t *cmd, size_t len) { ts->buf[++ts->max] = 0x1D; // 00011101 // data - for (int i = 0; i < len; i += 2) { + for (size_t i = 0; i < len; i += 2) { ts->buf[++ts->max] = encode_4bits[cmd[i] & 0xF]; ts->buf[++ts->max] = encode_4bits[cmd[i] >> 4]; ts->buf[++ts->max] = encode_4bits[cmd[i + 1] & 0xF]; @@ -515,7 +515,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta tag->shiftReg |= 0x80; tag->bitCount++; if (tag->bitCount == 8) { - tag->output[tag->len] = tag->shiftReg; + tag->output[tag->len] = tag->shiftReg & 0xFF; tag->len++; if (tag->len > tag->max_len) { @@ -540,7 +540,7 @@ static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *ta tag->bitCount++; if (tag->bitCount == 8) { - tag->output[tag->len] = tag->shiftReg; + tag->output[tag->len] = (tag->shiftReg & 0xFF); tag->len++; if (tag->len > tag->max_len) { @@ -1853,7 +1853,7 @@ void BruteforceIso15693Afi(uint32_t speed) { Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, recv + 2)); } else { DbpString("Failed to select card"); - reply_ng(CMD_ACK, PM3_ESOFT, NULL, 0); + reply_ng(CMD_HF_ISO15693_FINDAFI, PM3_ESOFT, NULL, 0); switch_off(); return; } @@ -1881,10 +1881,8 @@ void BruteforceIso15693Afi(uint32_t speed) { Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, recv + 2)); } - aborted = BUTTON_PRESS(); - + aborted = BUTTON_PRESS() && data_available(); if (aborted) { - DbpString("button pressed, aborting.."); break; } } @@ -1893,9 +1891,9 @@ void BruteforceIso15693Afi(uint32_t speed) { switch_off(); if (aborted) { - reply_ng(CMD_ACK, PM3_EOPABORTED, NULL, 0); + reply_ng(CMD_HF_ISO15693_FINDAFI, PM3_EOPABORTED, NULL, 0); } else { - reply_ng(CMD_ACK, PM3_SUCCESS, NULL, 0); + reply_ng(CMD_HF_ISO15693_FINDAFI, PM3_SUCCESS, NULL, 0); } } diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 168cee42c..29909dfc2 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -39,7 +39,6 @@ void ReaderIso15693(uint32_t parameter); // Simulate an ISO15693 reader - greg void SimTagIso15693(uint8_t *uid); // simulate an ISO15693 tag - greg void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag - atrox void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data); // send arbitrary commands from CLI - atrox -void Iso15693InitReader(void); void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string); diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 1fb1bccde..6a351bfca 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -52,9 +52,12 @@ bool lf_test_periods(size_t expected, size_t count) { ////////////////////////////////////////////////////////////////////////////// // Low frequency (LF) adc passthrough functionality ////////////////////////////////////////////////////////////////////////////// -static uint8_t previous_adc_val = 0; +static uint8_t previous_adc_val = 0; //0xFF; static uint8_t adc_avg = 0; +uint8_t get_adc_avg(void) { + return adc_avg; +} void lf_sample_mean(void) { uint8_t periods = 0; uint32_t adc_sum = 0; @@ -66,20 +69,44 @@ void lf_sample_mean(void) { } // division by 32 adc_avg = adc_sum >> 5; + previous_adc_val = adc_avg; if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("LF ADC average %u", adc_avg); } static size_t lf_count_edge_periods_ex(size_t max, bool wait, bool detect_gap) { + +#define LIMIT_DEV 20 + + // timeout limit to 100 000 w/o + uint32_t timeout = 100000; size_t periods = 0; - uint8_t avg_peak = adc_avg + 3, avg_through = adc_avg - 3; + uint8_t avg_peak = adc_avg + LIMIT_DEV; + uint8_t avg_through = adc_avg - LIMIT_DEV; while (BUTTON_PRESS() == false) { + WDT_HIT(); + + timeout--; + if (timeout == 0) { + return 0; + } + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; + continue; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; + periods++; + // reset timeout + timeout = 100000; + + volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; + if (g_logging) logSampleSimple(adc_val); // Only test field changes if state of adc values matter @@ -94,21 +121,28 @@ static size_t lf_count_edge_periods_ex(size_t max, bool wait, bool detect_gap) { } else { // Trigger on a modulation swap by observing an edge change if (rising_edge) { + if ((previous_adc_val > avg_peak) && (adc_val <= previous_adc_val)) { rising_edge = false; return periods; } + } else { + if ((previous_adc_val < avg_through) && (adc_val >= previous_adc_val)) { rising_edge = true; return periods; } + } } } + previous_adc_val = adc_val; - if (periods >= max) return 0; + if (periods >= max) { + return 0; + } } } @@ -169,10 +203,10 @@ void lf_init(bool reader, bool simulate) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); } else { if (simulate) -// FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); else // Sniff + //FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); } @@ -185,10 +219,10 @@ void lf_init(bool reader, bool simulate) { // When in reader mode, give the field a bit of time to settle. // 313T0 = 313 * 8us = 2504us = 2.5ms Hitag2 tags needs to be fully powered. - if (reader) { - // 10 ms - SpinDelay(10); - } +// if (reader) { + // 10 ms + SpinDelay(10); +// } // Steal this pin from the SSP (SPI communication channel with fpga) and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; diff --git a/armsrc/lfadc.h b/armsrc/lfadc.h index 66c6da534..ee2ffbbab 100644 --- a/armsrc/lfadc.h +++ b/armsrc/lfadc.h @@ -17,6 +17,7 @@ extern bool g_logging; +uint8_t get_adc_avg(void); void lf_sample_mean(void); bool lf_test_periods(size_t expected, size_t count); size_t lf_count_edge_periods(size_t max); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 9555e59c1..b03ee9f43 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -874,8 +874,8 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl //wait until SSC_CLK goes LOW while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { WDT_HIT(); - if (check == 1000) { - if (data_available() || BUTTON_PRESS()) + if (check == 2000) { + if (BUTTON_PRESS() || data_available()) goto OUT; check = 0; } @@ -2107,7 +2107,7 @@ void T55xx_ChkPwds(uint8_t flags) { #endif - uint64_t curr = 0, prev = 0; + uint64_t curr, prev = 0; int32_t idx = -1; for (uint32_t i = 0; i < pwd_count; i++) { diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index bcb47aa24..4330f2c4f 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -663,10 +663,10 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { // Return 1 if the nonce is invalid else return 0 static int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) { return ( - (oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \ - (oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \ - (oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0))) - ) ? 1 : 0; + (oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \ + (oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \ + (oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0))) + ) ? 1 : 0; } void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { @@ -2485,8 +2485,7 @@ void MifareGen3UID(uint8_t uidlen, uint8_t *uid) { clear_trace(); set_tracing(true); - if (!iso14443a_select_card(old_uid, card_info, NULL, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + if (iso14443a_select_card(old_uid, card_info, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } @@ -2521,8 +2520,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { clear_trace(); set_tracing(true); - if (!iso14443a_select_card(uid, card_info, NULL, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + if (iso14443a_select_card(uid, card_info, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } @@ -2558,7 +2556,6 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { if (doReselect) { if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); retval = PM3_ESOFT; goto OUT; } @@ -2575,16 +2572,15 @@ OUT: } void MifareGen3Freez(void) { - int retval = PM3_SUCCESS; - uint8_t freeze_cmd[7] = { 0x90, 0xfd, 0x11, 0x11, 0x00, 0xe7, 0x91 }; - uint8_t *uid = BigBuf_malloc(10); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + int retval = PM3_SUCCESS; + uint8_t freeze_cmd[7] = { 0x90, 0xfd, 0x11, 0x11, 0x00, 0xe7, 0x91 }; + uint8_t *uid = BigBuf_malloc(10); + + if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index b6ca949c6..b22f4db09 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -271,7 +271,7 @@ void MifareDES_Auth1(uint8_t *datain) { memcpy(keybytes, PICC_MASTER_KEY8, 8); } else if (payload->algo == MFDES_ALGO_AES || payload->algo == MFDES_ALGO_3DES) { memcpy(keybytes, PICC_MASTER_KEY16, 16); - } else if (payload->algo == MFDES_ALGO_3DES) { + } else if (payload->algo == MFDES_ALGO_3K3DES) { memcpy(keybytes, PICC_MASTER_KEY24, 24); } } else { diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index bba3132a7..ee8560994 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -452,7 +452,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t uid_len = 0; // 4,7, 10 uint32_t cuid = 0; - int vHf = 0; // in mV +// int vHf = 0; // in mV uint32_t selTimer = 0; uint32_t authTimer = 0; @@ -535,7 +535,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 WDT_HIT(); - if (counter == 1000) { + if (counter == 3000) { if (data_available()) { Dbprintf("----------- " _GREEN_("BREAKING") " ----------"); break; @@ -545,22 +545,24 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 counter++; } - // find reader field - if (cardSTATE == MFEMUL_NOFIELD) { + /* + // find reader field + if (cardSTATE == MFEMUL_NOFIELD) { -#if defined RDV4 - vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * SumAdc(ADC_CHAN_HF_RDV40, 32)) >> 15; -#else - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; -#endif + #if defined RDV4 + vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * SumAdc(ADC_CHAN_HF_RDV40, 32)) >> 15; + #else + vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; + #endif - if (vHf > MF_MINFIELDV) { - cardSTATE_TO_IDLE(); - LED_A_ON(); - } - button_pushed = BUTTON_PRESS(); - continue; - } + if (vHf > MF_MINFIELDV) { + cardSTATE_TO_IDLE(); + LED_A_ON(); + } + button_pushed = BUTTON_PRESS(); + continue; + } + */ FpgaEnableTracing(); //Now, get data diff --git a/armsrc/printf.c b/armsrc/printf.c index 2d18a9c53..25d4a4df8 100644 --- a/armsrc/printf.c +++ b/armsrc/printf.c @@ -34,7 +34,6 @@ * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 */ #include "printf.h" - #include "string.h" typedef unsigned char u_char; diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index a1b3e8e39..d5af6c399 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -208,15 +208,12 @@ static void remove_from_spiffs(const char *filename) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); } -static spiffs_stat stat_in_spiffs(const char *filename) { - spiffs_stat s; - if (SPIFFS_stat(&fs, filename, &s) < 0) - Dbprintf("errno %i\n", SPIFFS_errno(&fs)); - return s; -} - uint32_t size_in_spiffs(const char *filename) { - spiffs_stat s = stat_in_spiffs(filename); + spiffs_stat s; + if (SPIFFS_stat(&fs, filename, &s) < 0) { + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + return 0; + } return s.size; } @@ -552,17 +549,13 @@ void rdv40_spiffs_safe_print_fsinfo(void) { rdv40_spiffs_fsinfo fsinfo; rdv40_spiffs_getfsinfo(&fsinfo, RDV40_SPIFFS_SAFETY_SAFE); - DbpString(_CYAN_("Flash Memory FileSystem Info (SPIFFS)")); - - - Dbprintf(" Logical Block Size........." _YELLOW_("%d")" bytes", fsinfo.blockSize); - Dbprintf(" Logical Page Size.........." _YELLOW_("%d")" bytes", fsinfo.pageSize); - Dbprintf(""); - 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"); - Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /" + Dbprintf(" Filesystem size used available use% mounted"); + Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /" , fsinfo.totalBytes , fsinfo.usedBytes , fsinfo.freeBytes @@ -577,20 +570,13 @@ void rdv40_spiffs_safe_print_fsinfo(void) { // maintaining prefix list sorting, unique_checking, THEN outputing precomputed // tree Other solutio nwould be to add directory support to SPIFFS, but that we // dont want, as prefix are way easier and lighter in every aspect. -void rdv40_spiffs_safe_print_tree(uint8_t banner) { - - if (banner) { - DbpString(_CYAN_("Flash Memory FileSystem tree (SPIFFS)")); - Dbprintf("-------------------------------------"); - } - +void rdv40_spiffs_safe_print_tree(void) { int changed = rdv40_spiffs_lazy_mount(); spiffs_DIR d; struct spiffs_dirent e; struct spiffs_dirent *pe = &e; SPIFFS_opendir(&fs, "/", &d); - Dbprintf(" \t \t/"); while ((pe = SPIFFS_readdir(&d, pe))) { char resolvedlink[11 + SPIFFS_OBJ_NAME_LEN]; @@ -604,10 +590,9 @@ void rdv40_spiffs_safe_print_tree(uint8_t banner) { memset(resolvedlink, 0, sizeof(resolvedlink)); } - Dbprintf("[%04x]\t %ibytes \t|-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); + Dbprintf("[%04x]\t " _YELLOW_("%i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); } SPIFFS_closedir(&d); - rdv40_spiffs_lazy_mount_rollback(changed); } @@ -654,7 +639,7 @@ void test_spiffs(void) { int changed = rdv40_spiffs_lazy_mount(); Dbprintf(" Printing tree.............."); - rdv40_spiffs_safe_print_tree(false); + rdv40_spiffs_safe_print_tree(); Dbprintf(" Writing 'I love Proxmark3 RDV4' in a testspiffs.txt"); @@ -663,13 +648,13 @@ void test_spiffs(void) { rdv40_spiffs_write((char *)"testspiffs.txt", (uint8_t *)"I love Proxmark3 RDV4", 21, RDV40_SPIFFS_SAFETY_SAFE); Dbprintf(" Printing tree again......."); - rdv40_spiffs_safe_print_tree(false); + rdv40_spiffs_safe_print_tree(); Dbprintf(" Making a symlink to testspiffs.txt"); rdv40_spiffs_make_symlink((char *)"testspiffs.txt", (char *)"linktotestspiffs.txt", RDV40_SPIFFS_SAFETY_SAFE); Dbprintf(" Printing tree again......."); - rdv40_spiffs_safe_print_tree(false); + rdv40_spiffs_safe_print_tree(); // TODO READBACK, rename,print tree read back, remove, print tree; Dbprintf(" Rollbacking The mount status IF things have changed"); diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index 8754cb432..520ff8809 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -45,7 +45,7 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size); void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size); void test_spiffs(void); -void rdv40_spiffs_safe_print_tree(uint8_t banner); +void rdv40_spiffs_safe_print_tree(void); int rdv40_spiffs_unmount(void); int rdv40_spiffs_mount(void); int rdv40_spiffs_is_symlink(const char *s); diff --git a/armsrc/string.c b/armsrc/string.c index a3b49dcb1..d65cb6839 100644 --- a/armsrc/string.c +++ b/armsrc/string.c @@ -35,7 +35,7 @@ void *memmove(void *dest, const void *src, size_t len) { return dest; } -void *memset(void *dest, int c, int len) { +void *memset(void *dest, uint8_t c, int len) { uint8_t *d = dest; while ((len--) > 0) { *d = c; @@ -70,18 +70,19 @@ int strlen(const char *str) { } char *strncat(char *dest, const char *src, unsigned int n) { - unsigned int dest_len = strlen(dest); + int dest_len = strlen(dest); unsigned int i; for (i = 0 ; i < n && src[i] != '\0' ; i++) dest[dest_len + i] = src[i]; + dest[dest_len + i] = '\0'; return dest; } char *strcat(char *dest, const char *src) { - unsigned int dest_len = strlen(dest); + int dest_len = strlen(dest); unsigned int i; for (i = 0 ; src[i] != '\0' ; i++) @@ -97,7 +98,7 @@ void strreverse(char s[]) { int j = strlen(s) - 1; for (int i = 0; i < j; i++, j--) { - int c = s[i]; + char c = s[i]; s[i] = s[j]; s[j] = c; } @@ -105,11 +106,11 @@ void strreverse(char s[]) { /* itoa: convert n to characters in s */ void itoa(int n, char s[]) { - int i, sign; - + int sign; if ((sign = n) < 0) /* record sign */ n = -n; /* make n positive */ - i = 0; + + int i = 0; do { /* generate digits in reverse order */ s[i++] = n % 10 + '0'; /* get next digit */ } while ((n /= 10) > 0); /* delete it */ diff --git a/armsrc/string.h b/armsrc/string.h index f4d6e3fc1..9232508dd 100644 --- a/armsrc/string.h +++ b/armsrc/string.h @@ -17,7 +17,7 @@ int strlen(const char *str); void *memcpy(void *dest, const void *src, int len); void *memmove(void *dest, const void *src, size_t len); -void *memset(void *dest, int c, int len); +void *memset(void *dest, uint8_t c, int len); int memcmp(const void *av, const void *bv, int len); void memxor(uint8_t *dest, uint8_t *src, size_t len); char *strncat(char *dest, const char *src, unsigned int n); diff --git a/client/Makefile b/client/Makefile index ab26f970e..a4546cf86 100644 --- a/client/Makefile +++ b/client/Makefile @@ -4,9 +4,6 @@ # the license. #----------------------------------------------------------------------------- -# Must be called before any Makefile include -ROOT_DIR:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) - include ../Makefile.defs INSTALLBIN = proxmark3 @@ -18,6 +15,7 @@ OBJDIR = obj ifeq ($(platform),Darwin) PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt5/lib/pkgconfig endif ################### @@ -91,6 +89,7 @@ WHEREAMILIBLD = MBEDTLSLIBPATH = ../common/mbedtls MBEDTLSLIBINC = -I$(MBEDTLSLIBPATH) MBEDTLSLIB = $(OBJDIR)/libmbedtls.a +MBEDTLSLIBCLIENTRELPATH = ../../client ######################################################## # optional system libraries to replace local libraries # @@ -737,7 +736,7 @@ endif $(MBEDTLSLIB): .FORCE $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) OBJDIR=$(ROOT_DIR)$(OBJDIR) BINDIR=$(ROOT_DIR)$(OBJDIR) all + $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) OBJDIR=$(MBEDTLSLIBCLIENTRELPATH)/$(OBJDIR) BINDIR=$(MBEDTLSLIBCLIENTRELPATH)/$(OBJDIR) all $(REVENGLIB): .FORCE $(info [*] MAKE $@) diff --git a/client/cmdscripts/test_psk_clone.cmd b/client/cmdscripts/test_psk_clone.cmd index 784fecd1e..834106f91 100644 --- a/client/cmdscripts/test_psk_clone.cmd +++ b/client/cmdscripts/test_psk_clone.cmd @@ -2,13 +2,13 @@ clear rem Test of Motorola clone & read lf t55xx wipe -lf motorola clone a0000000a0002021 +lf motorola clone --raw a0000000a0002021 lf motorola read lf search rem Test of Nexwatch clone & read lf t55xx wipe -lf nexwatch clone c 1337 m 1 n +lf nexwatch clone --cn 1337 -m 1 -n lf nexwatch read lf search diff --git a/client/deps/amiitool/amiibo.c b/client/deps/amiitool/amiibo.c index edf7eb45f..7ef471f48 100644 --- a/client/deps/amiitool/amiibo.c +++ b/client/deps/amiitool/amiibo.c @@ -148,7 +148,7 @@ bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys) { return false; } - if ((amiiboKeys->data.magicBytesSize > 16) || (amiiboKeys->tag.magicBytesSize > 16)) { + if ((amiiboKeys->data.magicBytesSize > 16) || (amiiboKeys->tag.magicBytesSize > 16)) { free(dump); return false; } diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index f3f3c581e..ec9f50e20 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -47,6 +47,55 @@ int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char * return 0; } +void CLIParserPrintHelp(CLIParserContext *ctx) { + 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. + int egWidth = 30; + for (int i = 0; i <= strlen(ctx->programHelp); i++) { // <= so to get string terminator. + buf[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. + if (p2 != NULL) { + *(p2 - 1) = 0x00; + + if (strlen(buf) > 28) + egWidth = strlen(buf) + 5; + else + egWidth = 30; + + PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, buf, p2); + } else { + PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, buf); + } + idx = 0; + } + } + + PrintAndLogEx(NORMAL, ""); + free(buf); + } + + fflush(stdout); +} + int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargtable[], size_t vargtableLen, bool allowEmptyExec) { int nerrors; @@ -65,52 +114,7 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta /* special case: '--help' takes precedence over error reporting */ if ((argc < 2 && !allowEmptyExec) || ((struct arg_lit *)(ctx->argtable)[0])->count > 0) { // help must be the first record - 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. - int egWidth = 30; - for (int i = 0; i <= strlen(ctx->programHelp); i++) { // <= so to get string terminator. - buf[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. - if (p2 != NULL) { - *(p2 - 1) = 0x00; - - if (strlen(buf) > 28) - egWidth = strlen(buf) + 5; - else - egWidth = 30; - - PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, buf, p2); - } else { - PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, buf); - } - idx = 0; - } - } - - PrintAndLogEx(NORMAL, ""); - free(buf); - } - - fflush(stdout); + CLIParserPrintHelp(ctx); return 1; } @@ -231,12 +235,37 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int return res; } +int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { + *datalen = 0; + + int tmplen = 0; + uint8_t tmpstr[(256 * 2) + 1] = {0}; + + // concat all strings in argstr into tmpstr[] + // + int res = CLIParamStrToBuf(argstr, tmpstr, sizeof(tmpstr), &tmplen); + if (res || tmplen == 0) { + return res; + } + + res = param_getbin_to_eol((char *)tmpstr, 0, data, maxdatalen, datalen); + switch (res) { + case 1: + PrintAndLogEx(ERR, "Parameter error: Invalid BINARY value\n"); + break; + case 2: + PrintAndLogEx(ERR, "Parameter error: parameter too large\n"); + break; + } + return res; +} + int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) { *datalen = 0; if (!argstr->count) return 0; - uint8_t tmpstr[(256 * 2) + 1] = {0}; + uint8_t tmpstr[(512 * 2) + 1] = {0}; int ibuf = 0; for (int i = 0; i < argstr->count; i++) { diff --git a/client/deps/cliparser/cliparser.h b/client/deps/cliparser/cliparser.h index 78bb59f32..39a5792f8 100644 --- a/client/deps/cliparser/cliparser.h +++ b/client/deps/cliparser/cliparser.h @@ -60,12 +60,14 @@ typedef struct { char buf[1024 + 60]; } CLIParserContext; int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp); +void CLIParserPrintHelp(CLIParserContext *ctx); int CLIParserParseString(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec); int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData); int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargtable[], size_t vargtableLen, bool allowEmptyExec); int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); +int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def); int arg_get_u64_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint64_t def, uint64_t *out, uint8_t nlen, bool optional); diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index a589040ac..84f34e808 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -164,7 +164,7 @@ crack_states_thread(void *x) { statelist_t *bucket = buckets[current_bucket]; if (bucket) { #if defined (DEBUG_BRUTE_FORCE) - PrintAndLogEx(INFO, "Thread %u starts working on bucket %u\n", thread_id, current_bucket); + PrintAndLogEx(INFO, "Thread " _YELLOW_("%u") " starts working on bucket " _YELLOW_("%u") "\n", thread_id, current_bucket); #endif const uint64_t key = crack_states_bitsliced(thread_arg->cuid, thread_arg->best_first_bytes, bucket, &keys_found, &num_keys_tested, nonces_to_bruteforce, bf_test_nonce_2nd_byte, thread_arg->nonces); if (key != -1) { @@ -174,7 +174,7 @@ crack_states_thread(void *x) { char progress_text[80]; char keystr[19]; sprintf(keystr, "%012" PRIx64 " ", key); - sprintf(progress_text, "Brute force phase completed. Key found: " _YELLOW_("%s"), keystr); + sprintf(progress_text, "Brute force phase completed. Key found: " _GREEN_("%s"), keystr); hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0); break; } else if (keys_found) { diff --git a/client/deps/hardnested/hardnested_tables.c b/client/deps/hardnested/hardnested_tables.c index f0155946f..1ab600275 100644 --- a/client/deps/hardnested/hardnested_tables.c +++ b/client/deps/hardnested/hardnested_tables.c @@ -376,7 +376,7 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", bitflip, - (1 << 24) - count[odd_even], + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip, sum_a0, test_bitarray[odd_even], count[odd_even]); @@ -404,7 +404,7 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", bitflip | BITFLIP_2ND_BYTE, - (1 << 24) - count[odd_even], + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip | BITFLIP_2ND_BYTE, sum_a0, test_bitarray_2nd, count[odd_even]); @@ -490,7 +490,7 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", bitflip | 0x100, - (1 << 24) - count[odd_even], + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip | 0x100, sum_a0, test_not_bitarray[odd_even], count[odd_even]); @@ -517,7 +517,7 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t printf("Writing %u possible %s states for bitflip property %03x (%u (%1.2f%%) states eliminated)\n", count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", - bitflip | 0x100 | BITFLIP_2ND_BYTE, + bitflip | 0x100 | BITFLIP_2ND_BYTE, (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index a0c8b9943..d22d2e7c2 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1367,4 +1367,8 @@ F678905568C3 2E71D3BD262A 540D5E6355CC D1417E431949 -4BF6DE347FB6 \ No newline at end of file +4BF6DE347FB6 +# +3a471b2192bf +a297ceb7d34b +ae76242931f1 diff --git a/client/lualibs/read15.lua b/client/lualibs/read15.lua index 5d7400e2d..feb35d3eb 100644 --- a/client/lualibs/read15.lua +++ b/client/lualibs/read15.lua @@ -72,7 +72,7 @@ local function read15693(slow, dont_readresponse) We start by trying this command: MANDATORY (present in ALL iso15693 tags) command (the example below is sent to a tag different from the above one): - pm3> hf 15 info u + pm3> hf 15 info --ua UID=E007C1A257394244 Tag Info: Texas Instrument; Tag-it HF-I Standard; 8x32bit pm3> diff --git a/client/luascripts/hf_legic_buffer2card.lua b/client/luascripts/hf_legic_buffer2card.lua index eac8f1ed2..b6eee9f86 100644 --- a/client/luascripts/hf_legic_buffer2card.lua +++ b/client/luascripts/hf_legic_buffer2card.lua @@ -5,10 +5,12 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Mosci' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ -This is a script which writes value 0x01 to bytes from position 0x07 until 0xFF on a Legic Prime Tag (MIM256 or MIM1024) -- (created with 'hf legic save my_dump.hex') -- +This is a script which writes value 0x01 to bytes from +position 0x07 until 0xFF on a Legic Prime Tag (MIM256 or MIM1024) +-- (created with 'hf legic dump -f my_dump.hex') -- ]] example = [[ script run hf_legic_buffer2card @@ -53,7 +55,7 @@ function main() local cmd = '' local i for i = 7, 255 do - cmd = ('hf legic write o %02x d 01'):format(i) + cmd = ('hf legic wrbl -o %02x -d 01'):format(i) print(cmd) core.clearCommandBuffer() core.console(cmd) diff --git a/client/luascripts/hf_mf_autopwn.lua b/client/luascripts/hf_mf_autopwn.lua index 029d16d7a..b5d6f850d 100644 --- a/client/luascripts/hf_mf_autopwn.lua +++ b/client/luascripts/hf_mf_autopwn.lua @@ -6,11 +6,13 @@ local ansicolors = require('ansicolors') copyright = '' author = "Martin Holst Swende" -version = 'v1.0.3' +version = 'v1.0.4' desc = [[ This is a script which automates cracking and dumping mifare classic cards. It sets itself into 'listening'-mode, after which it cracks and dumps any mifare classic card that you place by the device. + +Please consider using the native command `hf mf autopwn` ]] example = [[ 1. script run hf_mf_autopwn @@ -84,46 +86,40 @@ local function wait_for_mifare() return nil, 'Aborted by user' end -local function nested(key,sak) - local typ = 1 +local function get_mf_size(sak) + local foo = "--1k" if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k | Ev1 4k - typ = 4 + foo = "--4k" elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k | Ev1 1K - typ = 1 + foo = "--1k" elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k - typ = 0 + foo = "--mini" elseif 0x10 == sak then-- "NXP MIFARE Plus 2k" - typ = 2 + foo = "--2k" elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K" - typ = 1 + foo = "--1k" else print("I don't know how many sectors there are on this type of card, defaulting to 16") end - local cmd = string.format('hf mf nested -t %d -b 0 --keya -k %s --dumpkeys', typ, key) + return foo +end + +local function nested(key, sak) + local mfs = get_mf_size(sak) + local cmd = string.format('hf mf nested %s --blk 0 -k %s --dump', mfs, key) core.console(cmd) end -local function dump_tag(uid, numsectors) +local function dump_tag(uid, sak) dbg('dumping tag memory') - local typ = 1 - if 0x18 == sak then --NXP MIFARE Classic 4k | Plus 4k | Ev1 4k - typ = 4 - elseif 0x08 == sak then -- NXP MIFARE CLASSIC 1k | Plus 2k | Ev1 1K - typ = 1 - elseif 0x09 == sak then -- NXP MIFARE Mini 0.3k - typ = 0 - elseif 0x10 == sak then-- "NXP MIFARE Plus 2k" - typ = 2 - elseif 0x01 == sak then-- "NXP MIFARE TNP3xxx 1K" - typ = 1 - end if utils.confirm('Do you wish to create a memory dump of tag?') then local dumpfile = 'hf-mf-'..uid..'-dump' - local dmp = ('hf mf dump -t %s -f %s'):format(typ, dumpfile) + local mfs = get_mf_size(sak) + local dmp = ('hf mf dump %s -f %s'):format(mfs, dumpfile) core.console(dmp) -- Save the global args, those are *our* arguments diff --git a/client/luascripts/hf_mf_dump-luxeo.lua b/client/luascripts/hf_mf_dump-luxeo.lua index 70f1b42e0..2b2c5d9c7 100644 --- a/client/luascripts/hf_mf_dump-luxeo.lua +++ b/client/luascripts/hf_mf_dump-luxeo.lua @@ -12,9 +12,10 @@ local ansicolors = require('ansicolors') copyright = '' author = '0xdrrb' -version = 'v0.1.1' +version = 'v0.1.2' 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. +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 @@ -69,9 +70,9 @@ end local function setdevicedebug( status ) local c = 'hw dbg ' if status then - c = c..'1' + c = c..'-1' else - c = c..'0' + c = c..'-0' end core.console(c) end diff --git a/client/luascripts/hf_mf_format.lua b/client/luascripts/hf_mf_format.lua index 7aed6b757..155a586ec 100644 --- a/client/luascripts/hf_mf_format.lua +++ b/client/luascripts/hf_mf_format.lua @@ -7,9 +7,9 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ -This script will generate 'hf mf wrbl' commands for each block to format a Mifare card. +This script will generate 'hf mf wrbl' commands for each block to format a Mifare Classic card. Alla datablocks gets 0x00 As default the script sets the keys A/B to 0xFFFFFFFFFFFF @@ -41,7 +41,7 @@ arguments = [[ local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds local DEBUG = true -- the debug flag -local CmdString = 'hf mf wrbl %d B %s %s' +local CmdString = 'hf mf wrbl --blk %d -b -k %s -d %s' local numBlocks = 64 local numSectors = 16 --- @@ -89,7 +89,7 @@ local function ExitMsg(msg) end -- -- Read information from a card -function GetCardInfo() +local function GetCardInfo() result, err = lib14a.read(false, true) if not result then print(err) @@ -200,9 +200,9 @@ local function main(args) local reminder = (block+1) % 4 local cmd if reminder == 0 then - cmd = CmdString:format(block, OldKey , EMPTY_SECTORTRAIL) + cmd = CmdString:format(block, OldKey, EMPTY_SECTORTRAIL) else - cmd = CmdString:format(block, OldKey , EMPTY_BL) + cmd = CmdString:format(block, OldKey, EMPTY_BL) end if block ~= 0 then diff --git a/client/luascripts/hf_mf_gen3_writer.lua b/client/luascripts/hf_mf_gen3_writer.lua index 1257f3413..a70a5c23d 100644 --- a/client/luascripts/hf_mf_gen3_writer.lua +++ b/client/luascripts/hf_mf_gen3_writer.lua @@ -30,7 +30,7 @@ local read14a = require('read14a') -- copyright = 'RRG Team' author = 'Winds' -version = 'v1.0.0' +version = 'v1.0.1' desc = [[ This script gives you an easy way to write your *.eml dumps into normal MIFARE Classic and Magic Gen3 cards. @@ -46,11 +46,6 @@ desc = [[ 5. Erase all data at the card and set the FF FF FF FF FF FF keys, and Access Conditions to 78778800. Script works in a wizard styled way. - - Author Youtube channel: https://yev.ooo/ - - Many Thanks, - Best Regards ]] example = [[ 1. script run mfc_gen3_writer @@ -80,8 +75,8 @@ local default_key_type = '01' --KeyA: 00, KeyB: 01 local default_key_blk = 'FFFFFFFFFFFF7C378800FFFFFFFFFFFF' -- Writing blocks local piswords_uid_lock = 'hf 14a raw -s -c -t 2000 90fd111100' local piswords_uid_change = 'hf 14a raw -s -c -t 2000 90f0cccc10' -local cmd_wrbl_a = 'hf mf wrbl %d A %s %s' -- Writing blocks by A key -local cmd_wrbl_b = 'hf mf wrbl %d B %s %s' -- Writing blocks by B key +local cmd_wrbl_a = 'hf mf wrbl --blk %d -a -k %s -d %s' -- Writing blocks by A key +local cmd_wrbl_b = 'hf mf wrbl --blk %d -b -k %s -d %s' -- Writing blocks by B key -- --- ------------------------------- diff --git a/client/luascripts/hf_mf_magicrevive.lua b/client/luascripts/hf_mf_magicrevive.lua index 9354b0f96..b50422123 100644 --- a/client/luascripts/hf_mf_magicrevive.lua +++ b/client/luascripts/hf_mf_magicrevive.lua @@ -3,7 +3,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This is a script that tries to bring back a chinese magic card (1k generation1) from the dead when it's block 0 has been written with bad values. @@ -84,7 +84,7 @@ local function cmdRestoreST() local arr = {} for i = 0, 15 do local blk = 3 + (4*i) - arr[i] = 'hf mf csetbl '..blk..' FFFFFFFFFFFFFF078000FFFFFFFFFFFF' + arr[i] = 'hf mf csetbl --blk '..blk..' -d FFFFFFFFFFFFFF078000FFFFFFFFFFFF' end return arr end diff --git a/client/luascripts/hf_mf_mini_dumpdecrypt.lua b/client/luascripts/hf_mf_mini_dumpdecrypt.lua index ec6006ed2..c75b92304 100644 --- a/client/luascripts/hf_mf_mini_dumpdecrypt.lua +++ b/client/luascripts/hf_mf_mini_dumpdecrypt.lua @@ -10,7 +10,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This is a script to dump and decrypt the data of a specific type of Mifare Mini token. The dump is decrypted. If a raw dump is wanted, use the -r parameter @@ -557,9 +557,9 @@ end local function setdevicedebug( status ) local c = 'hw dbg ' if status then - c = c..'1' + c = c..'-1' else - c = c..'0' + c = c..'-0' end core.console(c) end diff --git a/client/luascripts/hf_mf_tnp3_clone.lua b/client/luascripts/hf_mf_tnp3_clone.lua index 490c49242..22c626e80 100644 --- a/client/luascripts/hf_mf_tnp3_clone.lua +++ b/client/luascripts/hf_mf_tnp3_clone.lua @@ -13,7 +13,7 @@ local band = bit32.band copyright = '' author = "Iceman" -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script will try to make a barebones clone of a tnp3 tag on to a magic generation1 card. ]] @@ -160,7 +160,7 @@ local function main(args) core.clearCommandBuffer() -- wipe card. - local cmd = (csetuid..'%s %s %s w'):format(card.uid, atqa, sak) + local cmd = (csetuid..' -u %s -a %s -s %s -w'):format(card.uid, atqa, sak) core.console(cmd) core.clearCommandBuffer() @@ -169,7 +169,7 @@ local function main(args) local calc = utils.Crc16(b0..b1) local calcEndian = bor(rsh(calc,8), lsh(band(calc, 0xff), 8)) - local cmd = (cset..'1 %s%04x'):format( b1, calcEndian) + local cmd = (cset..'--blk 1 -d %s%04x'):format( b1, calcEndian) core.console(cmd) core.clearCommandBuffer() @@ -178,14 +178,14 @@ local function main(args) pos = (math.floor( blockNo / 4 ) * 12)+1 key = akeys:sub(pos, pos + 11 ) if blockNo%4 == 3 then - cmd = ('%s %d %s%s'):format(cset,blockNo,key,AccAndKeyB) + cmd = ('%s --blk %d -d %s%s'):format(cset,blockNo,key,AccAndKeyB) core.console(cmd) end end core.clearCommandBuffer() -- Set sector trailer S0, since it has different access rights - cmd = ('%s 3 %s0f0f0f69000000000000'):format(cset, keyA) + cmd = ('%s --blk 3 -d %s0f0f0f69000000000000'):format(cset, keyA) core.console(cmd) core.clearCommandBuffer() end diff --git a/client/luascripts/hf_mf_tnp3_dump.lua b/client/luascripts/hf_mf_tnp3_dump.lua index 54a19da82..8e6a6bfb3 100644 --- a/client/luascripts/hf_mf_tnp3_dump.lua +++ b/client/luascripts/hf_mf_tnp3_dump.lua @@ -10,7 +10,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script will try to dump the contents of a Mifare TNP3xxx card. It will need a valid KeyA in order to find the other keys and decode the card. @@ -112,7 +112,7 @@ local function main(args) local keyA, cmd, err local useNested = false local usePreCalc = false - local cmdReadBlockString = 'hf mf rdbl %d A %s' + local cmdReadBlockString = 'hf mf rdbl --blk %d -k %s' local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S"); -- Arguments for the script @@ -131,7 +131,7 @@ local function main(args) end -- Turn off Debug - local cmdSetDbgOff = "hw dbg 0" + local cmdSetDbgOff = "hw dbg -0" core.console( cmdSetDbgOff) utils.Sleep(0.5) diff --git a/client/luascripts/hf_mf_tnp3_sim.lua b/client/luascripts/hf_mf_tnp3_sim.lua index 33f82b2d4..2351bf1d5 100644 --- a/client/luascripts/hf_mf_tnp3_sim.lua +++ b/client/luascripts/hf_mf_tnp3_sim.lua @@ -10,7 +10,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script will try to load a binary datadump of a Mifare TNP3xxx card. It will try to validate all checksums and view some information stored in the dump @@ -372,7 +372,7 @@ local function main(args) end -- Turn off Debug - local cmdSetDbgOff = 'hw dbg 0' + local cmdSetDbgOff = 'hw dbg -0' core.console( cmdSetDbgOff) utils.Sleep(0.5) diff --git a/client/luascripts/hf_ntag-3d.lua b/client/luascripts/hf_ntag-3d.lua index e46705bd1..1ee21fe37 100644 --- a/client/luascripts/hf_ntag-3d.lua +++ b/client/luascripts/hf_ntag-3d.lua @@ -5,7 +5,7 @@ local ansicolors = require('ansicolors') copyright = 'Copyright (c) 2017 IceSQL AB. All rights reserved.' author = "Christian Herrmann" -version = 'v1.0.5' +version = 'v1.0.6' desc = [[ This script writes a empty template for 3D printing system onto a empty NTAG213 or MAGIC NTAG21* @@ -189,25 +189,25 @@ end local function write_tag(uid, t) print('Writing to tag') - core.console('hw dbg 0') + core.console('hw dbg -0') utils.Sleep(0.5) local cmd = '' local pwd, pack = core.keygen_algo_d(uid) for i= 8, 23 do - cmd = ('hf mfu wrbl b %02d d %s k %08X'):format(i, t[i], pwd) + cmd = ('hf mfu wrbl --blk %02d -d %s -k %08X'):format(i, t[i], pwd) core.console(cmd) end --cfg1 - core.console(('hf mfu wrbl b 42 d %s k %08X'):format(t[42], pwd)) + core.console(('hf mfu wrbl --blk 42 -d %s -k %08X'):format(t[42], pwd)) --cfg0 - core.console(('hf mfu wrbl b 41 d %s k %08X'):format(t[41], pwd)) + core.console(('hf mfu wrbl --blk 41 -d %s -k %08X'):format(t[41], pwd)) --dynamic - core.console(('hf mfu wrbl b 40 d %s k %08X'):format(t[40], pwd)) + core.console(('hf mfu wrbl --blk 40 -d %s -k %08X'):format(t[40], pwd)) - core.console('hw dbg 1') + core.console('hw dbg -1') utils.Sleep(0.5) print('Done') end diff --git a/client/luascripts/lf_em4100_bulk.lua b/client/luascripts/lf_em4100_bulk.lua index d4cdfdd8d..3ec8a8387 100644 --- a/client/luascripts/lf_em4100_bulk.lua +++ b/client/luascripts/lf_em4100_bulk.lua @@ -14,12 +14,12 @@ Any other input char will exit the script. You can supply a password, which will set the config block / block 7 on the T5577. -The verify option will issue a 'lf em 410x reader' command, so you can manually verify +The verify option will issue a 'lf em 410x reader' command, so you can manually verify that the write worked. ]] example = [[ - -- resets and start enrolling EM410x id 11CC334455 + -- resets and start enrolling EM410x id 11CC334455 script run lf_em4100_bulk.lua -s 11CC334455 -- continue enrolling from where last iteration @@ -28,7 +28,7 @@ example = [[ -- reset and start enrolling from 11223344, -- protecting the tag with password 010203 -- and verify the em id write. - script run lf_em4100_bulk.lua -s 1122334455 -p 01020304 -v + script run lf_em4100_bulk.lua -s 1122334455 -p 01020304 -v ]] usage = [[ script run lf_en4100_bulk.lua [-h] [-c] [-p password] [-s ] [-v] @@ -143,7 +143,7 @@ local function main(args) end -- if reset/start over, check -s - if not shall_continue then + if not shall_continue then if startid == nil then return oops('empty card number string') end if #startid == 0 then return oops('empty card number string') end if #startid ~= 10 then return oops('card number wrong length. Must be 5 hex bytes') end @@ -159,7 +159,7 @@ local function main(args) print(ac.red..'disable hints for less output'..ac.reset) core.console('pref set hint --off') print('') - + local hi = tonumber(startid:sub(1, 2), 16) local low = tonumber(startid:sub(3, 10), 16) local pwd = tonumber(ipwd, 16) @@ -178,7 +178,7 @@ local function main(args) else print('reset & starting enrolling from refresh') end - + local template = 'EM4100 ID '..ac.green..'%02X%08X'..ac.reset for i = low, low + 10000, 1 do print('') @@ -186,7 +186,7 @@ local function main(args) local msg = (template):format(hi, i) local ans = utils.input(msg, 'y'):lower() if ans == 'y' then - core.console( ('lf em 410x clone --id %02X%08X'):format(hi, i) ) + core.console( ('lf em 410x clone --id %02X%08X'):format(hi, i) ) -- print ( ('lf em 410x clone --id %02X%08X'):format(hi, i) ) if got_pwd then diff --git a/client/luascripts/lf_em_tearoff_protect.lua b/client/luascripts/lf_em_tearoff_protect.lua index 8d18ec5da..ed2dcf5f1 100644 --- a/client/luascripts/lf_em_tearoff_protect.lua +++ b/client/luascripts/lf_em_tearoff_protect.lua @@ -6,7 +6,7 @@ author = [[ 'Author Iceman CoAuthor Doegox ]] -version = 'v1.0.1' +version = 'v1.0.2' desc = [[ This is scripts loops though a tear attack and reads expected value. ]] @@ -37,7 +37,7 @@ arguments = [[ ]] local set_tearoff_delay = 'hw tearoff --on --delay %d' -local wr_template = 'lf em 4x05_write %s %s %s' +local wr_template = 'lf em 4x05 write --po -d %s -p %s' --- -- This is only meant to be used when errors occur @@ -71,7 +71,7 @@ end local function reset(wr_value, password) print('[=] '..ansicolors.red..'resetting the active lock block'..ansicolors.reset) - core.console(wr_template:format(99, wr_value, password)) + core.console(wr_template:format(wr_value, password)) end local function main(args) @@ -201,7 +201,7 @@ local function main(args) local c = set_tearoff_delay:format(sd) core.console(c); - c = wr_template:format(99, wr_value, password) + c = wr_template:format(wr_value, password) core.console(c) word14, err14 = core.em4x05_read(14, password) diff --git a/client/luascripts/mem_spiffs_readpwd.lua b/client/luascripts/mem_spiffs_readpwd.lua index b4995e406..ef529440c 100644 --- a/client/luascripts/mem_spiffs_readpwd.lua +++ b/client/luascripts/mem_spiffs_readpwd.lua @@ -76,7 +76,7 @@ local function main(args) if removeflag then print('Deleting file '..filename.. ' from SPIFFS if exists') - core.console("mem spiffs remove " ..filename) + core.console("mem spiffs remove -f " ..filename) return end diff --git a/client/luascripts/mfc_hammerlite.lua b/client/luascripts/mfc_hammerlite.lua index c2f34f04c..10a528a3d 100644 --- a/client/luascripts/mfc_hammerlite.lua +++ b/client/luascripts/mfc_hammerlite.lua @@ -5,7 +5,7 @@ local ansicolors = require('ansicolors') copyright = 'Copyright 2020 A. Ozkal, released under GPLv2+.' author = 'Ave' -version = 'v0.1.1' +version = 'v0.1.2' desc = [[ This script writes a bunch of random blocks to a Mifare Classic card ]] @@ -70,8 +70,8 @@ function main(args) do if ((a + 1) % 4 ~= 0) and a ~= 0 then -- :) data = randhex(32) - -- core.console('hf mf rdbl '..a..' A FFFFFFFFFFFF') - core.console('hf mf wrbl '..a..' A '..key..' '..data) + -- core.console('hf mf rdbl --blk '..a..' -k FFFFFFFFFFFF') + core.console('hf mf wrbl --blk '..a..' -k '..key..' -d '..data) end end end diff --git a/client/luascripts/multi_bruteforce.lua b/client/luascripts/multi_bruteforce.lua index e9b320058..a2723a44c 100644 --- a/client/luascripts/multi_bruteforce.lua +++ b/client/luascripts/multi_bruteforce.lua @@ -3,7 +3,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Kenzy Carey' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ .-----------------------------------------------------------------. @@ -193,7 +193,7 @@ local function main(args) rfidtagname = 'Noralsy' facilityrequired = 0 elseif rfidtag == 'presco' then - consolecommand = 'lf presco sim d' + consolecommand = 'lf presco sim -d' rfidtagname = 'Presco' facilityrequired = 0 elseif rfidtag == 'visa2000' then @@ -201,7 +201,7 @@ local function main(args) rfidtagname = 'Visa2000' facilityrequired = 0 elseif rfidtag == '14a' then - consolecommand = 'hf 14a sim' + consolecommand = 'hf 14a sim -t' if facility == '1' then rfidtagname = 'MIFARE Classic' -- Here we use the -f option to read the 14a type instead of the facility code elseif facility == '2' then rfidtagname = 'MIFARE Ultralight' elseif facility == '3' then rfidtagname = 'MIFARE Desfire' diff --git a/client/luascripts/tests/data_tracetest.lua b/client/luascripts/tests/data_tracetest.lua index 5773e0bcf..34341518d 100644 --- a/client/luascripts/tests/data_tracetest.lua +++ b/client/luascripts/tests/data_tracetest.lua @@ -7,7 +7,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.4' +version = 'v1.0.5' desc = [[ This script will load several traces files in current working directory/traces/ folder and do "data load" @@ -115,7 +115,7 @@ local function main(args) io.write('Starting to test traces > ') for _,file in pairs(files) do - local x = 'data load '..file + local x = 'data load -f '..file dbg(x) core.console(x) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index c9360ae6a..e621cc43a 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -351,5 +351,13 @@ "Name": "Access control", "Description": "Cardax Card Data Application", "Type": "" - } + }, + { + "AID": "6F706C", + "Vendor": "Openpath", + "Country": "US", + "Name": "Access control", + "Description": "Openpath PACS Application", + "Type": "" + } ] diff --git a/client/src/aidsearch.c b/client/src/aidsearch.c index 3a9d55ace..377bc2f9e 100644 --- a/client/src/aidsearch.c +++ b/client/src/aidsearch.c @@ -56,10 +56,10 @@ json_t *AIDSearchInit(bool verbose) { return root; } -json_t *AIDSearchGetElm(json_t *root, int elmindx) { +json_t *AIDSearchGetElm(json_t *root, size_t elmindx) { json_t *data = json_array_get(root, elmindx); if (!json_is_object(data)) { - PrintAndLogEx(ERR, "data [%d] is not an object\n", elmindx); + PrintAndLogEx(ERR, "data [%zu] is not an object\n", elmindx); return NULL; } return data; @@ -103,7 +103,7 @@ bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen) { if (hexaid == NULL || strlen(hexaid) == 0) return false; - int res = param_gethex_to_eol(hexaid, 0, aid, aidmaxlen, aidlen); + int res = param_gethex_to_eol(hexaid, 0, aid, (int)aidmaxlen, aidlen); if (res) return false; @@ -120,8 +120,8 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { goto out; json_t *elm = NULL; - uint32_t maxaidlen = 0; - for (uint32_t elmindx = 0; elmindx < json_array_size(root); elmindx++) { + size_t maxaidlen = 0; + for (size_t elmindx = 0; elmindx < json_array_size(root); elmindx++) { json_t *data = AIDSearchGetElm(root, elmindx); if (data == NULL) continue; diff --git a/client/src/aidsearch.h b/client/src/aidsearch.h index 585e173c6..e3783fd71 100644 --- a/client/src/aidsearch.h +++ b/client/src/aidsearch.h @@ -20,7 +20,7 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose); int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose); json_t *AIDSearchInit(bool verbose); -json_t *AIDSearchGetElm(json_t *root, int elmindx); +json_t *AIDSearchGetElm(json_t *root, size_t elmindx); bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen); int AIDSearchFree(json_t *root); diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index eb83fdc9c..1ddf77350 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -32,10 +32,10 @@ static int CmdHelp(const char *Cmd); static uint8_t calculateLRC(uint8_t *bytes, uint8_t len) { - uint8_t LRC = 0; + uint8_t lcr = 0; for (uint8_t i = 0; i < len; i++) - LRC ^= bytes[i]; - return LRC; + lcr ^= bytes[i]; + return lcr; } /* static uint16_t matrixadd ( uint8_t* bytes, uint8_t len){ @@ -56,7 +56,7 @@ static uint16_t shiftadd ( uint8_t* bytes, uint8_t len){ } */ static uint16_t calcSumCrumbAdd(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum += CRUMB(bytes[i], 0); sum += CRUMB(bytes[i], 2); @@ -64,25 +64,25 @@ static uint16_t calcSumCrumbAdd(uint8_t *bytes, uint8_t len, uint32_t mask) { sum += CRUMB(bytes[i], 6); } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumCrumbAddOnes(uint8_t *bytes, uint8_t len, uint32_t mask) { return (~calcSumCrumbAdd(bytes, len, mask) & mask); } static uint16_t calcSumNibbleAdd(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum += NIBBLE_LOW(bytes[i]); sum += NIBBLE_HIGH(bytes[i]); } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumNibbleAddOnes(uint8_t *bytes, uint8_t len, uint32_t mask) { return (~calcSumNibbleAdd(bytes, len, mask) & mask); } static uint16_t calcSumCrumbXor(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum ^= CRUMB(bytes[i], 0); sum ^= CRUMB(bytes[i], 2); @@ -90,32 +90,32 @@ static uint16_t calcSumCrumbXor(uint8_t *bytes, uint8_t len, uint32_t mask) { sum ^= CRUMB(bytes[i], 6); } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumNibbleXor(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum ^= NIBBLE_LOW(bytes[i]); sum ^= NIBBLE_HIGH(bytes[i]); } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumByteXor(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum ^= bytes[i]; } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumByteAdd(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum += bytes[i]; } sum &= mask; - return sum; + return (sum & 0xFFFF); } // Ones complement static uint16_t calcSumByteAddOnes(uint8_t *bytes, uint8_t len, uint32_t mask) { @@ -123,24 +123,24 @@ static uint16_t calcSumByteAddOnes(uint8_t *bytes, uint8_t len, uint32_t mask) { } static uint16_t calcSumByteSub(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint8_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum -= bytes[i]; } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumByteSubOnes(uint8_t *bytes, uint8_t len, uint32_t mask) { return (~calcSumByteSub(bytes, len, mask) & mask); } static uint16_t calcSumNibbleSub(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint8_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum -= NIBBLE_LOW(bytes[i]); sum -= NIBBLE_HIGH(bytes[i]); } sum &= mask; - return sum; + return (sum & 0xFFFF); } static uint16_t calcSumNibbleSubOnes(uint8_t *bytes, uint8_t len, uint32_t mask) { return (~calcSumNibbleSub(bytes, len, mask) & mask); @@ -148,18 +148,18 @@ static uint16_t calcSumNibbleSubOnes(uint8_t *bytes, uint8_t len, uint32_t mask) // BSD shift checksum 8bit version static uint16_t calcBSDchecksum8(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum = ((sum & 0xFF) >> 1) | ((sum & 0x1) << 7); // rotate accumulator sum += bytes[i]; // add next byte sum &= 0xFF; // } sum &= mask; - return sum; + return (sum & 0xFFFF); } // BSD shift checksum 4bit version static uint16_t calcBSDchecksum4(uint8_t *bytes, uint8_t len, uint32_t mask) { - uint16_t sum = 0; + uint32_t sum = 0; for (uint8_t i = 0; i < len; i++) { sum = ((sum & 0xF) >> 1) | ((sum & 0x1) << 3); // rotate accumulator sum += NIBBLE_HIGH(bytes[i]); // add high nibble @@ -169,7 +169,7 @@ static uint16_t calcBSDchecksum4(uint8_t *bytes, uint8_t len, uint32_t mask) { sum &= 0xF; // } sum &= mask; - return sum; + return (sum & 0xFFFF); } // 0xFF - ( n1 ^ n... ) @@ -227,7 +227,7 @@ static int CmdAnalyseLfsr(const char *Cmd) { for (uint8_t i = 0x01; i < 0x30; i += 1) { legic_prng_init(iv); legic_prng_forward(i); - uint16_t lfsr = legic_prng_get_bits(12); /* Any nonzero start state will work. */ + uint32_t lfsr = legic_prng_get_bits(12); /* Any nonzero start state will work. */ PrintAndLogEx(INFO, " %02X | %03X | %03X | %03X", i, lfsr, 0x40 ^ lfsr, find ^ lfsr); } PrintAndLogEx(INFO, "----+------+-------+--------------"); @@ -258,7 +258,7 @@ static int CmdAnalyseLCR(const char *Cmd) { return PM3_EINVARG; } - uint8_t finalXor = calculateLRC(data, dlen); + uint8_t finalXor = calculateLRC(data, (uint8_t)dlen); PrintAndLogEx(SUCCESS, "Target [%02X] requires final LRC XOR byte value: " _YELLOW_("0x%02X"), data[dlen - 1], finalXor); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -288,15 +288,15 @@ static int CmdAnalyseCRC(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "\nTests with (%d) | %s", dlen, sprint_hex(data, dlen)); + PrintAndLogEx(INFO, "\nTests with (%d) | %s", dlen, sprint_hex(data, (size_t)dlen)); // 51 f5 7a d6 uint8_t uid[] = {0x51, 0xf5, 0x7a, 0xd6}; //12 34 56 init_table(CRC_LEGIC); - uint8_t legic8 = CRC8Legic(uid, sizeof(uid)); - PrintAndLogEx(INFO, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, dlen, legic8), legic8); + uint8_t legic8 = CRC8Legic(uid, sizeof(uid)) & 0xFF; + PrintAndLogEx(INFO, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, (size_t)dlen, legic8), legic8); init_table(CRC_FELICA); - PrintAndLogEx(INFO, "FeliCa | %X ", crc16_xmodem(data, dlen)); + PrintAndLogEx(INFO, "FeliCa | %X ", crc16_xmodem(data, (size_t)dlen)); PrintAndLogEx(INFO, "\nTests of reflection. Current methods in source code"); PrintAndLogEx(INFO, " reflect(0x3e23L,3) is %04X == 0x3e26", reflect(0x3e23L, 3)); @@ -305,9 +305,9 @@ static int CmdAnalyseCRC(const char *Cmd) { uint8_t b1, b2; // ISO14443 crc B - compute_crc(CRC_14443_B, data, dlen, &b1, &b2); - uint16_t crcBB_1 = b1 << 8 | b2; - uint16_t bbb = Crc16ex(CRC_14443_B, data, dlen); + compute_crc(CRC_14443_B, data, (size_t)dlen, &b1, &b2); + uint16_t crcBB_1 = (uint16_t)(b1 << 8 | b2); + uint16_t bbb = Crc16ex(CRC_14443_B, data, (size_t)dlen); PrintAndLogEx(INFO, "ISO14443 crc B | %04x == %04x \n", crcBB_1, bbb); @@ -316,7 +316,7 @@ static int CmdAnalyseCRC(const char *Cmd) { PrintAndLogEx(INFO, "\n\nStandard test with 31 32 33 34 35 36 37 38 39 '123456789'\n\n"); uint8_t dataStr[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; - legic8 = CRC8Legic(dataStr, sizeof(dataStr)); + legic8 = CRC8Legic(dataStr, sizeof(dataStr)) & 0xFF; //these below has been tested OK. PrintAndLogEx(INFO, "Confirmed CRC Implementations"); @@ -340,27 +340,27 @@ static int CmdAnalyseCRC(const char *Cmd) { // ISO14443 crc A compute_crc(CRC_14443_A, dataStr, sizeof(dataStr), &b1, &b2); - uint16_t crcAA = b1 << 8 | b2; + uint16_t crcAA = (uint16_t)(b1 << 8 | b2); PrintAndLogEx(INFO, "ISO14443 crc A | %04x or %04x (BF05 expected)\n", crcAA, Crc16ex(CRC_14443_A, dataStr, sizeof(dataStr))); // ISO14443 crc B compute_crc(CRC_14443_B, dataStr, sizeof(dataStr), &b1, &b2); - uint16_t crcBB = b1 << 8 | b2; + uint16_t crcBB = (uint16_t)(b1 << 8 | b2); PrintAndLogEx(INFO, "ISO14443 crc B | %04x or %04x (906E expected)\n", crcBB, Crc16ex(CRC_14443_B, dataStr, sizeof(dataStr))); // ISO15693 crc (x.25) compute_crc(CRC_15693, dataStr, sizeof(dataStr), &b1, &b2); - uint16_t crcCC = b1 << 8 | b2; + uint16_t crcCC = (uint16_t)(b1 << 8 | b2); PrintAndLogEx(INFO, "ISO15693 crc X25| %04x or %04x (906E expected)\n", crcCC, Crc16ex(CRC_15693, dataStr, sizeof(dataStr))); // ICLASS compute_crc(CRC_ICLASS, dataStr, sizeof(dataStr), &b1, &b2); - uint16_t crcDD = b1 << 8 | b2; + uint16_t crcDD = (uint16_t)(b1 << 8 | b2); PrintAndLogEx(INFO, "ICLASS crc | %04x or %04x\n", crcDD, Crc16ex(CRC_ICLASS, dataStr, sizeof(dataStr))); // FeliCa compute_crc(CRC_FELICA, dataStr, sizeof(dataStr), &b1, &b2); - uint16_t crcEE = b1 << 8 | b2; + uint16_t crcEE = (uint16_t)(b1 << 8 | b2); PrintAndLogEx(INFO, "FeliCa | %04x or %04x (31C3 expected)\n", crcEE, Crc16ex(CRC_FELICA, dataStr, sizeof(dataStr))); @@ -399,25 +399,25 @@ static int CmdAnalyseCHKSUM(const char *Cmd) { PrintAndLogEx(FAILED, "Error parsing bytes"); return PM3_EINVARG; } - const char *s = arg_get_str(ctx, 2)->sval[0]; + const char *m = arg_get_str(ctx, 2)->sval[0]; bool verbose = arg_get_lit(ctx, 3); CLIParserFree(ctx); - uint32_t mlen = 0; - if (s) - mlen = strlen(s); + size_t mlen = 0; + if (m) + mlen = strlen(m); if (mlen > 8) { PrintAndLogEx(FAILED, "Mask value is max 4 hex bytes"); return PM3_EINVARG; } - uint32_t mask = 0; + uint16_t mask = 0; if (mlen == 0) { mask = 0xFFFF; } else { - for (int i = 0; i < mlen; i++) { - char c = s[i]; + for (size_t i = 0; i < mlen; i++) { + char c = m[i]; // capitalize if (c >= 'a' && c <= 'f') c -= 32; @@ -430,7 +430,7 @@ static int CmdAnalyseCHKSUM(const char *Cmd) { continue; mask <<= 4; - mask |= c; + mask |= (uint8_t)c; } } @@ -443,22 +443,22 @@ static int CmdAnalyseCHKSUM(const char *Cmd) { PrintAndLogEx(INFO, "------------------+-------------+------------------+-----------------+------------------+-----------+-------------"); } PrintAndLogEx(INFO, "0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X\n", - calcSumByteAdd(data, dlen, mask) - , calcSumNibbleAdd(data, dlen, mask) - , calcSumCrumbAdd(data, dlen, mask) - , calcSumByteSub(data, dlen, mask) - , calcSumNibbleSub(data, dlen, mask) - , calcSumByteAddOnes(data, dlen, mask) - , calcSumNibbleAddOnes(data, dlen, mask) - , calcSumCrumbAddOnes(data, dlen, mask) - , calcSumByteSubOnes(data, dlen, mask) - , calcSumNibbleSubOnes(data, dlen, mask) - , calcSumByteXor(data, dlen, mask) - , calcSumNibbleXor(data, dlen, mask) - , calcSumCrumbXor(data, dlen, mask) - , calcBSDchecksum8(data, dlen, mask) - , calcBSDchecksum4(data, dlen, mask) - , calcXORchecksum(data, dlen, mask) + calcSumByteAdd(data, (uint8_t)dlen, mask) + , calcSumNibbleAdd(data, (uint8_t)dlen, mask) + , calcSumCrumbAdd(data, (uint8_t)dlen, mask) + , calcSumByteSub(data, (uint8_t)dlen, mask) + , calcSumNibbleSub(data, (uint8_t)dlen, mask) + , calcSumByteAddOnes(data, (uint8_t)dlen, mask) + , calcSumNibbleAddOnes(data, (uint8_t)dlen, mask) + , calcSumCrumbAddOnes(data, (uint8_t)dlen, mask) + , calcSumByteSubOnes(data, (uint8_t)dlen, mask) + , calcSumNibbleSubOnes(data, (uint8_t)dlen, mask) + , calcSumByteXor(data, (uint8_t)dlen, mask) + , calcSumNibbleXor(data, (uint8_t)dlen, mask) + , calcSumCrumbXor(data, (uint8_t)dlen, mask) + , calcBSDchecksum8(data, (uint8_t)dlen, mask) + , calcBSDchecksum4(data, (uint8_t)dlen, mask) + , calcXORchecksum(data, (uint8_t)dlen, mask) ); return PM3_SUCCESS; } @@ -946,7 +946,7 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); const char *s = arg_get_str(ctx, 1)->sval[0]; - int len = MIN(strlen(s), MAX_DEMOD_BUF_LEN); + size_t len = MIN(strlen(s), MAX_DEMOD_BUF_LEN); // add 1 for null terminator. uint8_t *data = calloc(len + 1, sizeof(uint8_t)); @@ -955,7 +955,7 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { return PM3_EMALLOC; } - for (int i = 0; i <= strlen(s); i++) { + for (size_t i = 0; i <= strlen(s); i++) { char c = s[i]; if (c == '1') DemodBuffer[i] = 1; @@ -1007,7 +1007,7 @@ static int CmdAnalyseFreq(const char *Cmd) { static int CmdAnalyseFoo(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "analyze foo", + CLIParserInit(&ctx, "analyse foo", "experiments of cliparse", "analyse foo -r a0000000a0002021" ); @@ -1031,7 +1031,7 @@ static int CmdAnalyseFoo(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(INFO, "-r"); - PrintAndLogEx(INFO, "Got: %s", sprint_hex_inrow(data, datalen)); + PrintAndLogEx(INFO, "Got: %s", sprint_hex_inrow(data, (size_t)datalen)); PrintAndLogEx(INFO, "Got: %s", data3); ClearGraph(false); diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 82f05bb59..1e96e92d6 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh -// -// iceman 2019 +// modified Marshmellow, +// modified Iceman 2019, 2020, 2021 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -36,232 +36,7 @@ int g_DemodClock = 0; static int CmdHelp(const char *Cmd); -static int usage_data_printdemodbuf(void) { - PrintAndLogEx(NORMAL, "Usage: data print x o l "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " i invert Demodbuffer before printing"); - PrintAndLogEx(NORMAL, " x output in hex (omit for binary output)"); - PrintAndLogEx(NORMAL, " o enter offset in # of bits"); - PrintAndLogEx(NORMAL, " l enter length to print in # of bits or hex characters respectively"); - PrintAndLogEx(NORMAL, " s strip leading zeroes, i.e. set offset to first bit equal to one"); - return PM3_SUCCESS; -} -static int usage_data_manrawdecode(void) { - PrintAndLogEx(NORMAL, "Usage: data manrawdecode [invert] [maxErr]"); - PrintAndLogEx(NORMAL, " Takes 10 and 01 and converts to 0 and 1 respectively"); - PrintAndLogEx(NORMAL, " --must have binary sequence in demodbuffer (run data askrawdemod first)"); - PrintAndLogEx(NORMAL, " [invert] invert output"); - PrintAndLogEx(NORMAL, " [maxErr] set number of errors allowed (default = 20)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data manrawdecode = decode manchester bitstream from the demodbuffer"); - return PM3_SUCCESS; -} -static int usage_data_biphaserawdecode(void) { - PrintAndLogEx(NORMAL, "Usage: data biphaserawdecode [offset] [invert] [maxErr]"); - PrintAndLogEx(NORMAL, " Converts 10 or 01 to 1 and 11 or 00 to 0"); - PrintAndLogEx(NORMAL, " --must have binary sequence in demodbuffer (run data askrawdemod first)"); - PrintAndLogEx(NORMAL, " --invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " [offset <0|1>], set to 0 not to adjust start position or to 1 to adjust decode start position"); - PrintAndLogEx(NORMAL, " [invert <0|1>], set to 1 to invert output"); - PrintAndLogEx(NORMAL, " [maxErr int], set max errors tolerated - default=20"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data biphaserawdecode = decode biphase bitstream from the demodbuffer"); - PrintAndLogEx(NORMAL, " Example: data biphaserawdecode 1 1 = decode biphase bitstream from the demodbuffer, set offset, and invert output"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod [modulation] |"); - PrintAndLogEx(NORMAL, " [modulation] as 2 char,"); - PrintAndLogEx(NORMAL, " "_YELLOW_("ab")" - ask/biphase"); - PrintAndLogEx(NORMAL, " "_YELLOW_("am")" - ask/manchester"); - PrintAndLogEx(NORMAL, " "_YELLOW_("ar")" - ask/raw"); - PrintAndLogEx(NORMAL, " "_YELLOW_("fs")" - fsk"); - PrintAndLogEx(NORMAL, " "_YELLOW_("nr")" - nrz/direct"); - PrintAndLogEx(NORMAL, " "_YELLOW_("p1")" - psk1"); - PrintAndLogEx(NORMAL, " "_YELLOW_("p2")" - psk2"); - PrintAndLogEx(NORMAL, " as 'h', prints the help for the specific modulation"); - PrintAndLogEx(NORMAL, " see specific modulation help for optional parameters"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod fs h = print help specific to fsk demod"); - PrintAndLogEx(NORMAL, " data rawdemod fs = demod GraphBuffer using: fsk - autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod ab = demod GraphBuffer using: ask/biphase - autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod am = demod GraphBuffer using: ask/manchester - autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod ar = demod GraphBuffer using: ask/raw - autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod nr = demod GraphBuffer using: nrz/direct - autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod p1 = demod GraphBuffer using: psk1 - autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod p2 = demod GraphBuffer using: psk2 - autodetect"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_am(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod am [clock] [maxError] [maxLen] [amplify]"); - PrintAndLogEx(NORMAL, " ['s'] optional, check for Sequence Terminator"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect"); - PrintAndLogEx(NORMAL, " , 1 to invert output"); - PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100"); - PrintAndLogEx(NORMAL, " [set maximum Samples to read], default = 32768 (512 bits at rf/64)"); - PrintAndLogEx(NORMAL, " , 'a' to attempt demod with ask amplification, default = no amp"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod am = demod an ask/manchester tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod am 32 = demod an ask/manchester tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " data rawdemod am 32 1 = demod an ask/manchester tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod am 1 = demod an ask/manchester tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod am 64 1 0 = demod an ask/manchester tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_ab(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod ab [offset] [clock] [maxError] [maxLen] "); - PrintAndLogEx(NORMAL, " [offset], offset to begin biphase, default=0"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect"); - PrintAndLogEx(NORMAL, " , 1 to invert output"); - PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100"); - PrintAndLogEx(NORMAL, " [set maximum Samples to read], default = 32768 (512 bits at rf/64)"); - PrintAndLogEx(NORMAL, " , 'a' to attempt demod with ask amplification, default = no amp"); - PrintAndLogEx(NORMAL, " NOTE: can be entered as second or third argument"); - PrintAndLogEx(NORMAL, " NOTE: can be entered as first, second or last argument"); - PrintAndLogEx(NORMAL, " NOTE: any other arg must have previous args set to work"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " NOTE: --invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod ab = demod an ask/biph tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod ab 0 a = demod an ask/biph tag from GraphBuffer, amplified"); - PrintAndLogEx(NORMAL, " data rawdemod ab 1 32 = demod an ask/biph tag from GraphBuffer using an offset of 1 and a clock of RF/32"); - PrintAndLogEx(NORMAL, " data rawdemod ab 0 32 1 = demod an ask/biph tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod ab 0 1 = demod an ask/biph tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod ab 0 64 1 0 = demod an ask/biph tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - PrintAndLogEx(NORMAL, " data rawdemod ab 0 64 1 0 0 a = demod an ask/biph tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors, and amp"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_ar(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod ar [clock] [maxError] [maxLen] [amplify]"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect"); - PrintAndLogEx(NORMAL, " , 1 to invert output"); - PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100"); - PrintAndLogEx(NORMAL, " [set maximum Samples to read], default = 32768 (1024 bits at rf/64)"); - PrintAndLogEx(NORMAL, " , 'a' to attempt demod with ask amplification, default = no amp"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod ar = demod an ask tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod ar a = demod an ask tag from GraphBuffer, amplified"); - PrintAndLogEx(NORMAL, " data rawdemod ar 32 = demod an ask tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " data rawdemod ar 32 1 = demod an ask tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod ar 1 = demod an ask tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod ar 64 1 0 = demod an ask tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - PrintAndLogEx(NORMAL, " data rawdemod ar 64 1 0 0 a = demod an ask tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors, and amp"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_fs(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod fs [clock] [fchigh] [fclow]"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, omit for autodetect."); - PrintAndLogEx(NORMAL, " , 1 for invert output, can be used even if the clock is omitted"); - PrintAndLogEx(NORMAL, " [fchigh], larger field clock length, omit for autodetect"); - PrintAndLogEx(NORMAL, " [fclow], small field clock length, omit for autodetect"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod fs = demod an fsk tag from GraphBuffer using autodetect"); - PrintAndLogEx(NORMAL, " data rawdemod fs 32 = demod an fsk tag from GraphBuffer using a clock of RF/32, autodetect fc"); - PrintAndLogEx(NORMAL, " data rawdemod fs 1 = demod an fsk tag from GraphBuffer using autodetect, invert output"); - PrintAndLogEx(NORMAL, " data rawdemod fs 32 1 = demod an fsk tag from GraphBuffer using a clock of RF/32, invert output, autodetect fc"); - PrintAndLogEx(NORMAL, " data rawdemod fs 64 0 8 5 = demod an fsk1 RF/64 tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod fs 50 0 10 8 = demod an fsk2 RF/50 tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod fs 50 1 10 8 = demod an fsk2a RF/50 tag from GraphBuffer"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_nr(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod nr [clock] <0|1> [maxError]"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect."); - PrintAndLogEx(NORMAL, " , 1 for invert output"); - PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod nr = demod a nrz/direct tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod nr 32 = demod a nrz/direct tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " data rawdemod nr 32 1 = demod a nrz/direct tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod nr 1 = demod a nrz/direct tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod nr 64 1 0 = demod a nrz/direct tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_p1(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod p1 [clock] <0|1> [maxError]"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect."); - PrintAndLogEx(NORMAL, " , 1 for invert output"); - PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod p1 = demod a psk1 tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " data rawdemod p1 32 = demod a psk1 tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " data rawdemod p1 32 1 = demod a psk1 tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod p1 1 = demod a psk1 tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " data rawdemod p1 64 1 0 = demod a psk1 tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - return PM3_SUCCESS; -} -static int usage_data_rawdemod_p2(void) { - PrintAndLogEx(NORMAL, "Usage: data rawdemod p2 [clock] <0|1> [maxError]"); - PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect."); - PrintAndLogEx(NORMAL, " , 1 for invert output"); - PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " data rawdemod p2 = demod a psk2 tag from GraphBuffer, autodetect clock"); - PrintAndLogEx(NORMAL, " data rawdemod p2 32 = demod a psk2 tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " data rawdemod p2 32 1 = demod a psk2 tag from GraphBuffer using a clock of RF/32 and inverting output"); - PrintAndLogEx(NORMAL, " data rawdemod p2 1 = demod a psk2 tag from GraphBuffer, autodetect clock and invert output"); - PrintAndLogEx(NORMAL, " data rawdemod p2 64 1 0 = demod a psk2 tag from GraphBuffer using a clock of RF/64, inverting output and allowing 0 demod errors"); - return PM3_SUCCESS; -} -static int usage_data_autocorr(void) { - PrintAndLogEx(NORMAL, "Autocorrelate is used to detect repeating sequences. We use it as detection of length in bits a message inside the signal is"); - PrintAndLogEx(NORMAL, "Usage: data autocorr w [g]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " w window length for correlation - default = 4000"); - PrintAndLogEx(NORMAL, " g save back to GraphBuffer (overwrite)"); - return PM3_SUCCESS; -} -static int usage_data_detectclock(void) { - PrintAndLogEx(NORMAL, "Usage: data detectclock [modulation] "); - PrintAndLogEx(NORMAL, " [modulation as char], specify the modulation type you want to detect the clock of"); - PrintAndLogEx(NORMAL, " , specify the clock (optional - to get best start position only)"); - PrintAndLogEx(NORMAL, " 'a' = ask, 'f' = fsk, 'n' = nrz/direct, 'p' = psk"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data detectclock a = detect the clock of an ask modulated wave in the GraphBuffer"); - PrintAndLogEx(NORMAL, " data detectclock f = detect the clock of an fsk modulated wave in the GraphBuffer"); - PrintAndLogEx(NORMAL, " data detectclock p = detect the clock of an psk modulated wave in the GraphBuffer"); - PrintAndLogEx(NORMAL, " data detectclock n = detect the clock of an nrz/direct modulated wave in the GraphBuffer"); - return PM3_SUCCESS; -} -static int usage_data_hex2bin(void) { - PrintAndLogEx(NORMAL, "Usage: data hex2bin "); - PrintAndLogEx(NORMAL, " This function will ignore all non-hexadecimal characters (but stop reading on whitespace)"); - return PM3_SUCCESS; -} -static int usage_data_bin2hex(void) { - PrintAndLogEx(NORMAL, "Usage: data bin2hex "); - PrintAndLogEx(NORMAL, " This function will ignore all characters not 1 or 0 (but stop reading on whitespace)"); - return PM3_SUCCESS; -} -static int usage_data_buffclear(void) { - PrintAndLogEx(NORMAL, "This function clears the bigbuff on deviceside"); - PrintAndLogEx(NORMAL, "Usage: data clear [h]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - return PM3_SUCCESS; -} -static int usage_data_fsktonrz(void) { - PrintAndLogEx(NORMAL, "Usage: data fsktonrz c l f "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " c enter the a clock (omit to autodetect)"); - PrintAndLogEx(NORMAL, " l enter a field clock (omit to autodetect)"); - PrintAndLogEx(NORMAL, " f enter a field clock (omit to autodetect)"); - return PM3_SUCCESS; -} - -//set the demod buffer with given array of binary (one bit per byte) +//set the demod buffer with given array ofq binary (one bit per byte) //by marshmellow void setDemodBuff(uint8_t *buff, size_t size, size_t start_idx) { if (buff == NULL) return; @@ -389,13 +164,54 @@ void save_restoreDB(uint8_t saveOpt) { } static int CmdSetDebugMode(const char *Cmd) { - int demod = 0; - sscanf(Cmd, "%i", &demod); - g_debugMode = (uint8_t)demod; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data setdebugmode", + "Set debugging level on client side", + "data setdebugmode" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("0", NULL, "no debug messages"), + arg_lit0("1", NULL, "debug"), + arg_lit0("2", NULL, "verbose debugging"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool dg_0 = arg_get_lit(ctx, 1); + bool dg_1 = arg_get_lit(ctx, 2); + bool dg_2 = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (dg_0 + dg_1 + dg_2 > 1) { + PrintAndLogEx(INFO, "Select only one option"); + return PM3_EINVARG; + } + if (dg_0) + g_debugMode = 0; + + if (dg_1) + g_debugMode = 1; + + if (dg_2) + g_debugMode = 2; + + switch (g_debugMode) { + case 0: + PrintAndLogEx(INFO, "client debug level... %u ( no debug messages )", g_debugMode); + break; + case 1: + PrintAndLogEx(INFO, "client debug level... %u ( debug messages )", g_debugMode); + break; + case 2: + PrintAndLogEx(INFO, "client debug level... %u ( verbose debug messages )", g_debugMode); + break; + default: + break; + } return PM3_SUCCESS; } -//by marshmellow // max output to 512 bits if we have more // doesn't take inconsideration where the demod offset or bitlen found. int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_hex) { @@ -405,17 +221,24 @@ int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_h return PM3_EINVARG; } - uint8_t *buf = NULL; + uint8_t *buf = calloc(len, sizeof(uint8_t)); + if (buf == NULL) { + PrintAndLogEx(WARNING, "dail, cannot allocate memory"); + return PM3_EMALLOC; + } + memcpy(buf, DemodBuffer, len); + + uint8_t *p = NULL; if (strip_leading) { - buf = (DemodBuffer + offset); + p = (buf + offset); if (len > (DemodBufferLen - offset)) len = (DemodBufferLen - offset); size_t i; for (i = 0; i < len; i++) { - if (buf[i] == 1) break; + if (p[i] == 1) break; } offset += i; } @@ -429,89 +252,103 @@ int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_h } if (invert) { - buf = (DemodBuffer + offset); + p = (buf + offset); for (size_t i = 0; i < len; i++) { - if (buf[i] == 1) - buf[i] = 0; + if (p[i] == 1) + p[i] = 0; else { - if (buf[i] == 0) - buf[i] = 1; + if (p[i] == 0) + p[i] = 1; } } } if (print_hex) { - buf = (DemodBuffer + offset); + p = (buf + offset); char hex[512] = {0x00}; - int num_bits = binarraytohex(hex, sizeof(hex), (char *)buf, len); + int num_bits = binarraytohex(hex, sizeof(hex), (char *)p, len); if (num_bits == 0) { + p = NULL; + free(buf); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "DemodBuffer: %s", hex); + PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", hex); } else { - PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", sprint_bin_break(DemodBuffer + offset, len, 32)); + PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", sprint_bin_break(buf + offset, len, 32)); } + + p = NULL; + free(buf); return PM3_SUCCESS; } int CmdPrintDemodBuff(const char *Cmd) { - bool print_hex = false; - bool errors = false; - bool lstrip = false; - bool invert = false; - uint32_t offset = 0; - uint32_t length = 512; - char cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_data_printdemodbuf(); - case 'x': - print_hex = true; - cmdp++; - break; - case 'o': - offset = param_get32ex(Cmd, cmdp + 1, 0, 10); - if (!offset) errors = true; - cmdp += 2; - break; - case 'l': - length = param_get32ex(Cmd, cmdp + 1, 512, 10); - if (!length) errors = true; - cmdp += 2; - break; - case 's': - lstrip = true; - cmdp++; - break; - case 'i': - invert = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - //Validations - if (errors) return usage_data_printdemodbuf(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data print", + "Print the data in the DemodBuffer as hex or binary.\n" + "Defaults to binary output", + "data print" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("i", "inv", "invert Demodbuffer before printing"), +// arg_int0("l","len", "", "length to print in # of bits or hex characters respectively"), + arg_int0("o", "offset", "", "offset in # of bits"), + arg_lit0("s", "strip", "strip leading zeroes, i.e. set offset to first bit equal to one"), + arg_lit0("x", "hex", "output in hex (omit for binary output)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool invert = arg_get_lit(ctx, 1); + int os = arg_get_int_def(ctx, 2, 0); + bool lstrip = arg_get_lit(ctx, 3); + bool print_hex = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + uint8_t offset = (os & 0xFF); return printDemodBuff(offset, lstrip, invert, print_hex); } -//by marshmellow -//this function strictly converts >1 to 1 and <1 to 0 for each sample in the graphbuffer +// this function strictly converts >1 to 1 and <1 to 0 for each sample in the graphbuffer int CmdGetBitStream(const char *Cmd) { - CmdHpf(Cmd); - for (uint32_t i = 0; i < GraphTraceLen; i++) - GraphBuffer[i] = (GraphBuffer[i] >= 1) ? 1 : 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data getbitstream", + "Convert GraphBuffer's value accordingly\n" + " - larger or equal to ONE becomes ONE\n" + " - less than ONE becomes ZERO", + "data getbitstream" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + CmdHpf(""); + for (uint32_t i = 0; i < GraphTraceLen; i++) { + GraphBuffer[i] = (GraphBuffer[i] >= 1) ? 1 : 0; + } RepaintGraphWindow(); return PM3_SUCCESS; } + static int CmdConvertBitStream(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data convertbitstream", + "Convert GraphBuffer's 0|1 values to 127|-127", + "data convertbitstream" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + if (isGraphBitstream()) { convertGraphFromBitstream(); } else { @@ -521,44 +358,55 @@ static int CmdConvertBitStream(const char *Cmd) { return PM3_SUCCESS; } -//by marshmellow -//Cmd Args: Clock, invert, maxErr, maxLen as integers and amplify as char == 'a' +// Cmd Args: Clock, invert, maxErr, maxLen as integers and amplify as char == 'a' // (amp may not be needed anymore) -//verbose will print results and demoding messages -//emSearch will auto search for EM410x format in bitstream -//askType switches decode: ask/raw = 0, ask/manchester = 1 -int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxLen, bool amplify, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) { - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) clk %i invert %i maxErr %i maxLen %zu amplify %i verbose %i emSearch %i askType %i ", clk, invert, maxErr, maxLen, amplify, verbose, emSearch, askType); +// verbose will print results and demoding messages +// emSearch will auto search for EM410x format in bitstream +// askType switches decode: ask/raw = 0, ask/manchester = 1 +int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) { + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) clk %i invert %i maxErr %i maxLen %zu amplify %i verbose %i emSearch %i askType %i " + , clk + , invert + , maxErr + , maxlen + , amplify + , verbose + , emSearch + , askType + ); uint8_t askamp = 0; - if (!maxLen) maxLen = pm3_capabilities.bigbuf_size; + if (maxlen == 0) + maxlen = pm3_capabilities.bigbuf_size; uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { + PrintAndLogEx(INFO, "failed to allocate memory"); return PM3_EMALLOC; } - size_t BitLen = getFromGraphBuf(bits); + size_t bitlen = getFromGraphBuf(bits); - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", BitLen); + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", bitlen); - if (BitLen < 255) { + if (bitlen < 255) { free(bits); return PM3_ESOFT; } - if (maxLen < BitLen && maxLen != 0) BitLen = maxLen; + if (maxlen < bitlen && maxlen != 0) + bitlen = maxlen; int foundclk = 0; //amplify signal before ST check if (amplify) { - askAmp(bits, BitLen); + askAmp(bits, bitlen); } size_t ststart = 0, stend = 0; // if (*stCheck) - bool st = DetectST(bits, &BitLen, &foundclk, &ststart, &stend); + bool st = DetectST(bits, &bitlen, &foundclk, &ststart, &stend); if (clk == 0) { if (foundclk == 32 || foundclk == 64) { @@ -574,34 +422,54 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxLen, bool amplify, b PrintAndLogEx(DEBUG, "Found Sequence Terminator - First one is shown by orange / blue graph markers"); } - int startIdx = 0; - int errCnt = askdemod_ext(bits, &BitLen, &clk, &invert, maxErr, askamp, askType, &startIdx); + int start_idx = 0; + int errCnt = askdemod_ext(bits, &bitlen, &clk, &invert, maxErr, askamp, askType, &start_idx); - if (errCnt < 0 || BitLen < 16) { //if fatal error (or -1) - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, invert:%c, bitlen:%zu, clock:%d", errCnt, (invert) ? 'Y' : 'N', BitLen, clk); + if (errCnt < 0 || bitlen < 16) { //if fatal error (or -1) + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, %s bitlen:%zu, clock:%d" + , errCnt + , (invert) ? "inverted," : "" + , bitlen + , clk + ); free(bits); return PM3_ESOFT; } if (errCnt > maxErr) { - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%d", errCnt, BitLen, clk); + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%d" + , errCnt + , bitlen + , clk + ); free(bits); return PM3_ESOFT; } - if (verbose) PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Using clock:%d, invert:%d, bits found:%zu, start index %d", clk, invert, BitLen, startIdx); + if (verbose) { + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) using clock:%d, %sbits found:%zu, start index %d" + , clk + , (invert) ? "inverted, " : "" + , bitlen + , start_idx + ); + } //output - setDemodBuff(bits, BitLen, 0); - setClockGrid(clk, startIdx); + setDemodBuff(bits, bitlen, 0); + setClockGrid(clk, start_idx); if (verbose) { if (errCnt > 0) - PrintAndLogEx(DEBUG, "# Errors during Demoding (shown as 7 in bit stream): %d", errCnt); - if (askType) - PrintAndLogEx(DEBUG, "ASK/Manchester - Clock: %d - Decoded bitstream:", clk); - else - PrintAndLogEx(DEBUG, "ASK/Raw - Clock: %d - Decoded bitstream:", clk); + PrintAndLogEx(DEBUG, "# Errors during demoding (shown as 7 in bit stream): %d", errCnt); + + if (askType) { + PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Manchester") " - clock %d - decoded bitstream", clk); + PrintAndLogEx(INFO, "---------------------------------------------"); + } else { + PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Raw") " - clock %d - decoded bitstream", clk); + PrintAndLogEx(INFO, "--------------------------------------"); + } printDemodBuff(0, false, false, false); } @@ -613,70 +481,79 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxLen, bool amplify, b free(bits); return PM3_SUCCESS; } -int ASKDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, bool verbose, bool emSearch, uint8_t askType) { + +int ASKDemod(int clk, int invert, int maxErr, size_t maxlen, bool amplify, bool verbose, bool emSearch, uint8_t askType) { bool st = false; - return ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, verbose, emSearch, askType, &st); + return ASKDemod_ext(clk, invert, maxErr, maxlen, amplify, verbose, emSearch, askType, &st); } -//by marshmellow -//takes 5 arguments - clock, invert, maxErr, maxLen as integers and amplify as char == 'a' -//attempts to demodulate ask while decoding manchester -//prints binary found and saves in graphbuffer for further commands +// takes 5 arguments - clock, invert, maxErr, maxLen as integers and amplify as char == 'a' +// attempts to demodulate ask while decoding manchester +// prints binary found and saves in graphbuffer for further commands static int Cmdaskmandemod(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --am", + "ASK/MANCHESTER demodulate the data in the GraphBuffer and output binary", + "data rawdemod --am --> demod a ask/manchester tag, using autodetect\n" + "data rawdemod --am -c 32 --> demod a ask/manchester tag, using a clock of RF/32\n" + "data rawdemod --am -i --> demod a ask/manchester tag, using autodetect, invert output\n" + "data rawdemod --am -c 32 -i --> demod a ask/manchester tag, using a clock of RF/32, invert output\n" + "data rawdemod --am -c 64 -i --max 0 --> demod a ask/manchester tag, using a clock of RF/64, inverting and allowing 0 demod errors\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"), + arg_int0("c", "clk", "", "set clock manually (def autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_lit0("s", "st", "check for sequence terminator"), + arg_int0(NULL, "max", "", "maximum allowed errors (def 100)"), + arg_int0(NULL, "samples", "", "maximum samples to read (def 32768) [512 bits at RF/64]"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - size_t slen = strlen(Cmd); + bool amplify = arg_get_lit(ctx, 1); + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 2, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 3); + bool st = arg_get_lit(ctx, 4); + uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF; + size_t max_len = (size_t)arg_get_int_def(ctx, 6, 0) & 0xFF; + CLIParserFree(ctx); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (slen > 45 || cmdp == 'h') return usage_data_rawdemod_am(); - - bool st = false, amplify = false; - int clk = 0, invert = 0, maxErr = 100; - size_t maxLen = 0; - - if (slen) { - - if (Cmd[0] == 's') { - st = true; - Cmd++; - } else if (slen > 1 && Cmd[1] == 's') { - st = true; - Cmd += 2; - } - - char amp = tolower(param_getchar(Cmd, 0)); - sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); - - amplify = (amp == 'a'); - } - - if (clk == 1) { - invert = 1; - clk = 0; - } - - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); - return PM3_EINVARG; - } - return ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, true, true, 1, &st); + return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, true, 1, &st); } -//by marshmellow -//manchester decode -//strictly take 10 and 01 and convert to 0 and 1 +// manchester decode +// strictly take 10 and 01 and convert to 0 and 1 static int Cmdmandecoderaw(const char *Cmd) { - size_t size = 0; - int high = 0, low = 0; - size_t i = 0; - uint16_t errCnt = 0; - int invert = 0, maxErr = 20; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 5 || cmdp == 'h') return usage_data_manrawdecode(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data manrawdecode", + "Manchester decode binary stream in DemodBuffer\n" + "Converts 10 and 01 and converts to 0 and 1 respectively\n" + " - must have binary sequence in demodbuffer (run `data rawdemod --ar` before)", + "data manrawdecode" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("i", "inv", "invert output"), + arg_int0(NULL, "err", "", "set max errors tolerated (def 20)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool invert = arg_get_lit(ctx, 1); + int max_err = arg_get_int_def(ctx, 2, 20); + CLIParserFree(ctx); - if (DemodBufferLen == 0) return PM3_ESOFT; + if (DemodBufferLen == 0) { + PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`")); + return PM3_ESOFT; + } uint8_t bits[MAX_DEMOD_BUF_LEN] = {0}; + // make sure its just binary data 0|1|7 in buffer + int high = 0, low = 0; + size_t i = 0; for (; i < DemodBufferLen; ++i) { if (DemodBuffer[i] > high) high = DemodBuffer[i]; @@ -686,23 +563,27 @@ static int Cmdmandecoderaw(const char *Cmd) { } if (high > 7 || low < 0) { - PrintAndLogEx(ERR, "Error: please raw demod the wave first then manchester raw decode"); + PrintAndLogEx(ERR, "Error: please first raw demod then manchester raw decode"); return PM3_ESOFT; } - sscanf(Cmd, "%i %i", &invert, &maxErr); - size = i; - uint8_t alignPos = 0; - errCnt = manrawdecode(bits, &size, invert, &alignPos); - if (errCnt >= maxErr) { - PrintAndLogEx(ERR, "Too many errors: %u", errCnt); + size_t size = i; + uint8_t offset = 0; + uint16_t err_cnt = manrawdecode(bits, &size, invert, &offset); + if (err_cnt > max_err) { + PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "Manchester Decoded - # errors:%d - data:", errCnt); - PrintAndLogEx(NORMAL, "%s", sprint_bin_break(bits, size, 32)); + if (err_cnt > 0) { + PrintAndLogEx(WARNING, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt); + } - if (errCnt == 0) { + PrintAndLogEx(INFO, "Manchester decoded %s", (invert) ? "( inverted )" : ""); + PrintAndLogEx(INFO, "%s", sprint_bin_break(bits, size, 32)); + + // try decode EM410x + if (err_cnt == 0) { uint64_t id = 0; uint32_t hi = 0; size_t idx = 0; @@ -712,6 +593,8 @@ static int Cmdmandecoderaw(const char *Cmd) { printEM410x(hi, id, false); } } + + setClockGrid(g_DemodClock, g_DemodStartIdx + g_DemodClock / 2); return PM3_SUCCESS; } @@ -725,47 +608,64 @@ static int Cmdmandecoderaw(const char *Cmd) { * param masxErr maximum tolerated errors */ static int CmdBiphaseDecodeRaw(const char *Cmd) { - size_t size = 0; - int offset = 0, invert = 0, maxErr = 20, errCnt = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 5 || cmdp == 'h') return usage_data_biphaserawdecode(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data biphaserawdecode", + "Biphase decode binary stream in DemodBuffer\n" + "Converts 10 or 01 -> 1 and 11 or 00 -> 0\n" + " - must have binary sequence in demodbuffer (run `data rawdemod --ar` before)\n" + " - invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester", + "data biphaserawdecode --> decode biphase bitstream from the demodbuffer\n" + "data biphaserawdecode -oi --> decode biphase bitstream from the demodbuffer, adjust offset, and invert output" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("o", "offset", "set to adjust decode start position"), + arg_lit0("i", "inv", "invert output"), + arg_int0(NULL, "err", "", "set max errors tolerated (def 20)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int offset = arg_get_lit(ctx, 1); + bool invert = arg_get_lit(ctx, 2); + int max_err = arg_get_int_def(ctx, 3, 20); + CLIParserFree(ctx); - sscanf(Cmd, "%i %i %i", &offset, &invert, &maxErr); if (DemodBufferLen == 0) { - PrintAndLogEx(WARNING, "DemodBuffer Empty - run " _YELLOW_("'data rawdemod ar'")" first"); + PrintAndLogEx(WARNING, "DemodBuffer empty, run " _YELLOW_("`data rawdemod --ar`")); return PM3_ESOFT; } uint8_t bits[MAX_DEMOD_BUF_LEN] = {0}; - size = sizeof(bits); + size_t size = sizeof(bits); if (!getDemodBuff(bits, &size)) return PM3_ESOFT; - errCnt = BiphaseRawDecode(bits, &size, &offset, invert); - if (errCnt < 0) { - PrintAndLogEx(ERR, "Error during decode:%d", errCnt); + int err_cnt = BiphaseRawDecode(bits, &size, &offset, invert); + if (err_cnt < 0) { + PrintAndLogEx(ERR, "Error during decode " _RED_("%i"), err_cnt); return PM3_ESOFT; } - if (errCnt > maxErr) { - PrintAndLogEx(ERR, "Too many errors attempting to decode: %d", errCnt); + if (err_cnt > max_err) { + PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt); return PM3_ESOFT; } - if (errCnt > 0) - PrintAndLogEx(WARNING, "# Errors found during Demod (shown as " _YELLOW_("7")" in bit stream): %d", errCnt); + if (err_cnt > 0) { + PrintAndLogEx(WARNING, "# %i errors found during demod (shown as " _YELLOW_(".")" in bit stream) ", err_cnt); + } - PrintAndLogEx(NORMAL, "Biphase Decoded using offset: %d - # invert:%d - data:", offset, invert); - PrintAndLogEx(NORMAL, "%s", sprint_bin_break(bits, size, 32)); + PrintAndLogEx(INFO, "Biphase decoded using offset %d%s", offset, (invert) ? "( inverted )" : ""); + PrintAndLogEx(INFO, "%s", sprint_bin_break(bits, size, 32)); //remove first bit from raw demod - if (offset) + if (offset) { setDemodBuff(DemodBuffer, DemodBufferLen - offset, offset); + } setClockGrid(g_DemodClock, g_DemodStartIdx + g_DemodClock * offset / 2); return PM3_SUCCESS; } -//by marshmellow -// - ASK Demod then Biphase decode GraphBuffer samples +// ASK Demod then Biphase decode GraphBuffer samples int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { //ask raw demod GraphBuffer first @@ -803,37 +703,70 @@ int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { } return PM3_SUCCESS; } -//by marshmellow - see ASKbiphaseDemod + +// see ASKbiphaseDemod static int Cmdaskbiphdemod(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 25 || cmdp == 'h') return usage_data_rawdemod_ab(); - int offset = 0, clk = 0, invert = 0, maxErr = 50; - sscanf(Cmd, "%i %i %i %i", &offset, &clk, &invert, &maxErr); - return ASKbiphaseDemod(offset, clk, invert, maxErr, true); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --ab", + "ASK/BIPHASE demodulate the data in the GraphBuffer and output binary\n" + "NOTE, `--invert` for Conditional Dephase Encoding (CDP) AKA Differential Manchester\n", + "data rawdemod --ab --> demod a ask/biphase tag, using autodetect\n" + "data rawdemod --ab -c 32 --> demod a ask/biphase tag, using a clock of RF/32\n" + "data rawdemod --ab -i --> demod a ask/biphase tag, using autodetect, invert output\n" + "data rawdemod --ab -c 32 -i --> demod a ask/biphase tag, using a clock of RF/32, invert output\n" + "data rawdemod --ab -c 64 -i --max 0 --> demod a ask/biphase tag, using a clock of RF/64, inverting and allowing 0 demod errors\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "set clock manually (def autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_int0("o", "offset", "", "offset to begin biphase (def 0)"), + arg_int0(NULL, "max", "", "maximum allowed errors (def 50)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 2); + int offset = arg_get_int_def(ctx, 3, 0); + uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 4, 50) & 0xFF; + CLIParserFree(ctx); + + return ASKbiphaseDemod(offset, clk, invert, max_err, true); } -//by marshmellow - see ASKDemod +// see ASKDemod static int Cmdaskrawdemod(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 25 || cmdp == 'h') return usage_data_rawdemod_ar(); - bool st = false; - int clk = 0; - int invert = 0; - int maxErr = 100; - size_t maxLen = 0; - bool amplify = false; - char amp = tolower(param_getchar(Cmd, 0)); - sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); - amplify = amp == 'a'; - if (clk == 1) { - invert = 1; - clk = 0; - } - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); - return PM3_EINVARG; - } - return ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, true, false, 0, &st); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --ar", + "ASK/RAW demodulate the data in the GraphBuffer and output binary", + "data rawdemod --ar -a --> demod a ask tag, using autodetect, amplified\n" + "data rawdemod --ar -c 32 --> demod a ask tag, using a clock of RF/32\n" + "data rawdemod --ar -i --> demod a ask tag, using autodetect, invert output\n" + "data rawdemod --ar -c 32 -i --> demod a ask tag, using a clock of RF/32, invert output\n" + "data rawdemod --ar -c 64 -i --max 0 --> demod a ask tag, using a clock of RF/64, inverting and allowing 0 demod errors\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "amp", "try attempt demod with ask amplification (def no amp)"), + arg_int0("c", "clk", "", "set clock manually (def autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_lit0("s", "st", "check for sequence terminator"), + arg_int0(NULL, "max", "", "maximum allowed errors (def 100)"), + arg_int0(NULL, "samples", "", "maximum samples to read (def 32768) [512 bits at RF/64]"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool amplify = arg_get_lit(ctx, 1); + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 2, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 3); + bool st = arg_get_lit(ctx, 4); + uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF; + size_t max_len = (size_t)arg_get_int_def(ctx, 6, 0) & 0xFF; + CLIParserFree(ctx); + + return ASKDemod_ext(clk, invert, max_err, max_len, amplify, true, false, 0, &st); } int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveGrph, bool verbose) { @@ -923,44 +856,55 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG } static int CmdAutoCorr(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data autocorr", + "Autocorrelate over window is used to detect repeating sequences.\n" + "We use it as detection of how long in bits a message inside the signal is", + "data autocorr -w 4000\n" + "data autocorr -w 4000 -g" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("g", NULL, "save back to GraphBuffer (overwrite)"), + arg_u64_0("w", "win", "", "window length for correlation. def 4000"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool updateGrph = arg_get_lit(ctx, 1); + uint32_t window = arg_get_u32_def(ctx, 2, 4000); + CLIParserFree(ctx); - uint32_t window = 4000; - uint8_t cmdp = 0; - bool updateGrph = false; - bool errors = false; + PrintAndLogEx(INFO, "Using window size " _YELLOW_("%u"), window); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_data_autocorr(); - case 'g': - updateGrph = true; - cmdp++; - break; - case 'w': - window = param_get32ex(Cmd, cmdp + 1, 4000, 10); - if (window >= GraphTraceLen) { - PrintAndLogEx(WARNING, "window must be smaller than trace (%zu samples)", GraphTraceLen); - errors = true; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + if (GraphTraceLen == 0) { + PrintAndLogEx(WARNING, "GraphBuffer is empty"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("lf read") "` to collect samples"); + return PM3_ESOFT; + } + + if (window >= GraphTraceLen) { + PrintAndLogEx(WARNING, "window must be smaller than trace (" _YELLOW_("%zu") " samples)", GraphTraceLen); + return PM3_EINVARG; } - //Validations - if (errors || cmdp == 0) return usage_data_autocorr(); AutoCorrelate(GraphBuffer, GraphBuffer, GraphTraceLen, window, updateGrph, true); - return PM3_SUCCESS; } static int CmdBitsamples(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data bitsamples", + "Get raw samples from device as bitstring", + "data bitsamples" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + int cnt = 0; uint8_t got[12288]; @@ -983,9 +927,18 @@ static int CmdBitsamples(const char *Cmd) { } static int CmdBuffClear(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_data_buffclear(); - + CLIParserContext *ctx; + CLIParserInit(&ctx, "data clear", + "This function clears the bigbuff on deviceside\n" + "and graph window", + "data clear" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_BUFF_CLEAR, NULL, 0); ClearGraph(true); @@ -998,12 +951,12 @@ static int CmdDecimate(const char *Cmd) { CLIParserInit(&ctx, "data decimate", "Performs decimation, by reducing samples N times in the grapbuf. Good for PSK\n", "data decimate\n" - "data decimate 4" + "data decimate -n 4" ); void *argtable[] = { arg_param_begin, - arg_int0(NULL, NULL, "", "factor to reduce sample set (default 2)"), + arg_int0("n", NULL, "", "factor to reduce sample set (default 2)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1030,12 +983,12 @@ static int CmdUndecimate(const char *Cmd) { CLIParserInit(&ctx, "data undecimate", "Performs un-decimation, by repeating each sample N times in the graphbuf", "data undecimate\n" - "data undecimate 4\n" + "data undecimate -n 4\n" ); void *argtable[] = { arg_param_begin, - arg_int0(NULL, NULL, "", "factor to repeat each sample (default 2)"), + arg_int0("n", NULL, "", "factor to repeat each sample (default 2)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1063,12 +1016,23 @@ static int CmdUndecimate(const char *Cmd) { return PM3_SUCCESS; } -//by marshmellow -//shift graph zero up or down based on input + or - +// shift graph zero up or down based on input + or - static int CmdGraphShiftZero(const char *Cmd) { - int shift = 0; - //set options from parameters entered with the command - sscanf(Cmd, "%i", &shift); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data shiftgraphzero", + "Shift 0 for Graphed wave + or - shift value", + "data shiftgraphzero -n 10 --> shift 10 points\n" + "data shiftgraphzero -n -22 --> shift negative 22 points" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("n", NULL, "", "shift + or -"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int shift = arg_get_int_def(ctx, 1, 0); + CLIParserFree(ctx); for (size_t i = 0; i < GraphTraceLen; i++) { int shiftedVal = GraphBuffer[i] + shift; @@ -1095,48 +1059,79 @@ int AskEdgeDetect(const int *in, int *out, int len, int threshold) { return PM3_SUCCESS; } -//by marshmellow -//use large jumps in read samples to identify edges of waves and then amplify that wave to max -//similar to dirtheshold, threshold commands -//takes a threshold length which is the measured length between two samples then determines an edge +// use large jumps in read samples to identify edges of waves and then amplify that wave to max +// similar to dirtheshold, threshold commands +// takes a threshold length which is the measured length between two samples then determines an edge static int CmdAskEdgeDetect(const char *Cmd) { - int thresLen = 25; - int ans = 0; - sscanf(Cmd, "%i", &thresLen); - ans = AskEdgeDetect(GraphBuffer, GraphBuffer, GraphTraceLen, thresLen); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data askedgedetect", + "Adjust Graph for manual ASK demod using the length of sample differences\n" + "to detect the edge of a wave", + "data askedgedetect -t 20" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("t", "thres", "", "threshold, use 20 - 45 (def 25)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int threshold = arg_get_int_def(ctx, 1, 25); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "using threshold " _YELLOW_("%i"), threshold); + int res = AskEdgeDetect(GraphBuffer, GraphBuffer, GraphTraceLen, threshold); RepaintGraphWindow(); - return ans; + return res; } -/* Print our clock rate */ +// Print our clock rate // uses data from graphbuffer // adjusted to take char parameter for type of modulation to find the clock - by marshmellow. static int CmdDetectClockRate(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 6 || strlen(Cmd) == 0 || cmdp == 'h') - return usage_data_detectclock(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data detectclock", + "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer", + "data detectclock -A --> detect clock of an ask wave in GraphBuffer\n" + "data detectclock -F --> detect clock of an fsk wave in GraphBuffer\n" + "data detectclock -N --> detect clock of an psk wave in GraphBuffer\n" + "data detectclock -P --> detect clock of an nrz/direct wave in GraphBuffer" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("A", "ASK", "specify ASK modulation clock detection"), + arg_lit0("F", "FSK", "specify FSK modulation clock detection"), + arg_lit0("N", "NZR", "specify NZR/DIRECT modulation clock detection"), + arg_lit0("P", "PSK", "specify PSK modulation clock detection"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + bool a = arg_get_lit(ctx, 1); + bool f = arg_get_lit(ctx, 2); + bool n = arg_get_lit(ctx, 3); + bool p = arg_get_lit(ctx, 4); + CLIParserFree(ctx); - int clock1 = 0; - switch (cmdp) { - case 'a' : - clock1 = GetAskClock(Cmd + 1, true); - break; - case 'f' : - clock1 = GetFskClock("", true); - break; - case 'n' : - clock1 = GetNrzClock("", true); - break; - case 'p' : - clock1 = GetPskClock("", true); - break; - default : - PrintAndLogEx(NORMAL, "Please specify a valid modulation to detect the clock of - see option h for help"); - break; + int tmp = (a + f + n + p); + if (tmp > 1) { + PrintAndLogEx(WARNING, "Only specify one modulation"); + return PM3_EINVARG; } + + if (a) + GetAskClock("", true); + + if (f) + GetFskClock("", true); + + if (n) + GetNrzClock("", true); + + if (p) + GetPskClock("", true); + RepaintGraphWindow(); - return clock1; + return PM3_SUCCESS; } static char *GetFSKType(uint8_t fchigh, uint8_t fclow, uint8_t invert) { @@ -1164,29 +1159,34 @@ static char *GetFSKType(uint8_t fchigh, uint8_t fclow, uint8_t invert) { return fskType; } -//by marshmellow -//fsk raw demod and print binary -//takes 4 arguments - Clock, invert, fchigh, fclow -//defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) +// fsk raw demod and print binary +// takes 4 arguments - Clock, invert, fchigh, fclow +// defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bool verbose) { //raw fsk demod no manchester decoding no start bit finding just get binary from wave - if (getSignalProperties()->isnoise) + if (getSignalProperties()->isnoise) { + if (verbose) { + PrintAndLogEx(INFO, "signal looks like noise"); + } return PM3_ESOFT; + } uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); return PM3_EMALLOC; } - size_t BitLen = getFromGraphBuf(bits); - if (BitLen == 0) { + size_t bitlen = getFromGraphBuf(bits); + if (bitlen == 0) { + PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf"); free(bits); return PM3_ESOFT; } //get field clock lengths if (!fchigh || !fclow) { - uint16_t fcs = countFC(bits, BitLen, true); + uint16_t fcs = countFC(bits, bitlen, true); if (!fcs) { fchigh = 10; fclow = 8; @@ -1198,19 +1198,27 @@ int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bo //get bit clock length if (!rfLen) { int firstClockEdge = 0; //todo - align grid on graph with this... - rfLen = detectFSKClk(bits, BitLen, fchigh, fclow, &firstClockEdge); + rfLen = detectFSKClk(bits, bitlen, fchigh, fclow, &firstClockEdge); if (!rfLen) rfLen = 50; } - int startIdx = 0; - int size = fskdemod(bits, BitLen, rfLen, invert, fchigh, fclow, &startIdx); + + int start_idx = 0; + int size = fskdemod(bits, bitlen, rfLen, invert, fchigh, fclow, &start_idx); if (size > 0) { setDemodBuff(bits, size, 0); - setClockGrid(rfLen, startIdx); + setClockGrid(rfLen, start_idx); // Now output the bitstream to the scrollback by line of 16 bits if (verbose || g_debugMode) { - PrintAndLogEx(DEBUG, "DEBUG: (FSKrawDemod) Using Clock:%u, invert:%u, fchigh:%u, fclow:%u", rfLen, invert, fchigh, fclow); - PrintAndLogEx(NORMAL, "%s decoded bitstream:", GetFSKType(fchigh, fclow, invert)); + PrintAndLogEx(DEBUG, "DEBUG: (FSKrawDemod) using clock:%u, %sfc high:%u, fc low:%u" + , rfLen + , (invert) ? "inverted, " : "" + , fchigh + , fclow + ); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _YELLOW_("%s") " decoded bitstream", GetFSKType(fchigh, fclow, invert)); + PrintAndLogEx(INFO, "-----------------------"); printDemodBuff(0, false, invert, false); } goto out; @@ -1223,39 +1231,52 @@ out: return PM3_SUCCESS; } -//by marshmellow -//fsk raw demod and print binary -//takes 4 arguments - Clock, invert, fchigh, fclow -//defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) +// fsk raw demod and print binary +// takes 4 arguments - Clock, invert, fchigh, fclow +// defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) static int CmdFSKrawdemod(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 20 || cmdp == 'h') return usage_data_rawdemod_fs(); - uint8_t rfLen, invert, fchigh, fclow; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --fs", + "FSK demodulate the data in the GraphBuffer and output binary", + "data rawdemod --fs --> demod an fsk tag, using autodetect\n" + "data rawdemod --fs -c 32 --> demod an fsk tag, using a clock of RF/32, autodetect fc\n" + "data rawdemod --fs -i --> demod an fsk tag, using autodetect, invert output\n" + "data rawdemod --fs -c 32 -i --> demod an fsk tag, using a clock of RF/32, invert output, autodetect fc\n" + "data rawdemod --fs -c 64 --hi 8 --lo 5 --> demod an fsk1 RF/64 tag\n" + "data rawdemod --fs -c 50 --hi 10 --lo 8 --> demod an fsk2 RF/50 tag\n" + "data rawdemod --fs -c 50 -i --hi 10 --lo 8 --> demod an fsk2a RF/50 tag\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "set clock manually (def: autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_int0(NULL, "hi", "", "larger field clock length (def: autodetect)"), + arg_int0(NULL, "lo", "", "small field clock length (def: autodetect)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - //set defaults - //set options from parameters entered with the command - rfLen = param_get8(Cmd, 0); - invert = param_get8(Cmd, 1); - fchigh = param_get8(Cmd, 2); - fclow = param_get8(Cmd, 3); + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 2); + uint8_t fchigh = (uint8_t)arg_get_int_def(ctx, 3, 0) & 0xFF; + uint8_t fclow = (uint8_t)arg_get_int_def(ctx, 4, 0) & 0xFF; + CLIParserFree(ctx); - if (strlen(Cmd) > 0 && strlen(Cmd) <= 2) { - if (rfLen == 1) { - invert = 1; //if invert option only is used - rfLen = 0; - } - } - return FSKrawDemod(rfLen, invert, fchigh, fclow, true); + return FSKrawDemod(clk, invert, fchigh, fclow, true); } -//by marshmellow -//attempt to psk1 demod graph buffer +// attempt to psk1 demod graph buffer int PSKDemod(int clk, int invert, int maxErr, bool verbose) { - if (getSignalProperties()->isnoise) + if (getSignalProperties()->isnoise) { + if (verbose) { + PrintAndLogEx(INFO, "signal looks like noise"); + } return PM3_ESOFT; + } uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); return PM3_EMALLOC; } size_t bitlen = getFromGraphBuf(bits); @@ -1289,7 +1310,6 @@ int PSKDemod(int clk, int invert, int maxErr, bool verbose) { return PM3_SUCCESS; } -// by marshmellow // takes 3 arguments - clock, invert, maxErr as integers // attempts to demodulate nrz only // prints binary found and saves in demodbuffer for further commands @@ -1297,36 +1317,41 @@ int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) { int errCnt = 0, clkStartIdx = 0; - if (getSignalProperties()->isnoise) + if (getSignalProperties()->isnoise) { + if (verbose) { + PrintAndLogEx(INFO, "signal looks like noise"); + } return PM3_ESOFT; + } uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); return PM3_EMALLOC; } - size_t BitLen = getFromGraphBuf(bits); + size_t bitlen = getFromGraphBuf(bits); - if (BitLen == 0) { + if (bitlen == 0) { free(bits); return PM3_ESOFT; } - errCnt = nrzRawDemod(bits, &BitLen, &clk, &invert, &clkStartIdx); + errCnt = nrzRawDemod(bits, &bitlen, &clk, &invert, &clkStartIdx); if (errCnt > maxErr) { - PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, BitLen, errCnt); + PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt); free(bits); return PM3_ESOFT; } - if (errCnt < 0 || BitLen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first) - PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, BitLen, errCnt); + if (errCnt < 0 || bitlen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first) + PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt); free(bits); return PM3_ESOFT; } - if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %zu", clk, invert, BitLen); + if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %zu", clk, invert, bitlen); //prime demod buffer for output - setDemodBuff(bits, BitLen, 0); + setDemodBuff(bits, bitlen, 0); setClockGrid(clk, clkStartIdx); @@ -1342,95 +1367,179 @@ int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) { } static int CmdNRZrawDemod(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 16 || cmdp == 'h') return usage_data_rawdemod_nr(); - int invert = 0, clk = 0, maxErr = 100; - sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); - if (clk == 1) { - invert = 1; - clk = 0; - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --nr", + "NRZ/DIRECT demodulate the data in the GraphBuffer and output binary", + "data rawdemod --nr --> demod a nrz/direct tag, using autodetect\n" + "data rawdemod --nr -c 32 --> demod a nrz/direct tag, using a clock of RF/32\n" + "data rawdemod --nr -i --> demod a nrz/direct tag, using autodetect, invert output\n" + "data rawdemod --nr -c 32 -i --> demod a nrz/direct tag, using a clock of RF/32, invert output\n" + "data rawdemod --nr -c 64 -i --max 0 --> demod a nrz/direct tag, using a clock of RF/64, inverting and allowing 0 demod errors\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "set clock manually (def autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_int0(NULL, "max", "", "maximum allowed errors (def 100)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "(NRZrawDemod) Invalid argument: %s", Cmd); - return PM3_EINVARG; - } - return NRZrawDemod(clk, invert, maxErr, true); + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 2); + uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF; + CLIParserFree(ctx); + + return NRZrawDemod(clk, invert, max_err, true); } -// by marshmellow // takes 3 arguments - clock, invert, max_err as integers // attempts to demodulate psk only // prints binary found and saves in demodbuffer for further commands int CmdPSK1rawDemod(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 16 || cmdp == 'h') return usage_data_rawdemod_p1(); - int clk = 0, invert = 0, max_err = 100; - sscanf(Cmd, "%i %i %i", &clk, &invert, &max_err); - if (clk == 1) { - invert = 1; - clk = 0; - } - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); - return PM3_EINVARG; - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --p1", + "PSK1 demodulate the data in the GraphBuffer and output binary", + "data rawdemod --p1 --> demod a psk1 tag, using autodetect\n" + "data rawdemod --p1 -c 32 --> demod a psk1 tag, using a clock of RF/32\n" + "data rawdemod --p1 -i --> demod a psk1 tag, using autodetect, invert output\n" + "data rawdemod --p1 -c 32 -i --> demod a psk1 tag, using a clock of RF/32, invert output\n" + "data rawdemod --p1 -c 64 -i --max 0 --> demod a psk1 tag, using a clock of RF/64, inverting and allowing 0 demod errors\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "set clock manually (def autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_int0(NULL, "max", "", "maximum allowed errors (def 100)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 2); + uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF; + CLIParserFree(ctx); + int ans = PSKDemod(clk, invert, max_err, true); //output if (ans != PM3_SUCCESS) { if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "PSK1 demoded bitstream:"); + PrintAndLogEx(SUCCESS, _YELLOW_("PSK1") " demoded bitstream"); + PrintAndLogEx(INFO, "----------------------"); // Now output the bitstream to the scrollback by line of 16 bits printDemodBuff(0, false, invert, false); return PM3_SUCCESS; } -// by marshmellow // takes same args as cmdpsk1rawdemod static int CmdPSK2rawDemod(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 16 || cmdp == 'h') return usage_data_rawdemod_p2(); - int clk = 0, invert = 0, max_err = 100; - sscanf(Cmd, "%i %i %i", &clk, &invert, &max_err); - if (clk == 1) { - invert = 1; - clk = 0; - } - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); - return PM3_EINVARG; - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod --p2", + "PSK2 demodulate the data in the GraphBuffer and output binary", + "data rawdemod --p2 --> demod a psk2 tag, using autodetect\n" + "data rawdemod --p2 -c 32 --> demod a psk2 tag, using a clock of RF/32\n" + "data rawdemod --p2 -i --> demod a psk2 tag, using autodetect, invert output\n" + "data rawdemod --p2 -c 32 -i --> demod a psk2 tag, using a clock of RF/32, invert output\n" + "data rawdemod --p2 -c 64 -i --max 0 --> demod a psk2 tag, using a clock of RF/64, inverting and allowing 0 demod errors\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "set clock manually (def autodetect)"), + arg_lit0("i", "inv", "invert output"), + arg_int0(NULL, "max", "", "maximum allowed errors (def 100)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + bool invert = arg_get_lit(ctx, 2); + uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF; + CLIParserFree(ctx); + int ans = PSKDemod(clk, invert, max_err, true); if (ans != PM3_SUCCESS) { if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans); return PM3_ESOFT; } psk1TOpsk2(DemodBuffer, DemodBufferLen); - PrintAndLogEx(NORMAL, "PSK2 demoded bitstream:"); + PrintAndLogEx(SUCCESS, _YELLOW_("PSK2") " demoded bitstream"); + PrintAndLogEx(INFO, "----------------------"); // Now output the bitstream to the scrollback by line of 16 bits printDemodBuff(0, false, invert, false); return PM3_SUCCESS; } -// by marshmellow - combines all raw demod functions into one menu command +// combines all raw demod functions into one menu command static int CmdRawDemod(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rawdemod", + "Demodulate the data in the GraphBuffer and output binary", + "data rawdemod --fs --> demod FSK - autodetect\n" + "data rawdemod --ab --> demod ASK/BIPHASE - autodetect\n" + "data rawdemod --am --> demod ASK/MANCHESTER - autodetect\n" + "data rawdemod --ar --> demod ASK/RAW - autodetect\n" + "data rawdemod --nr --> demod NRZ/DIRECT - autodetect\n" + "data rawdemod --p1 --> demod PSK1 - autodetect\n" + "data rawdemod --p2 --> demod PSK2 - autodetect\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "ab", "ASK/Biphase demodulation"), + arg_lit0(NULL, "am", "ASK/Manchester demodulation"), + arg_lit0(NULL, "ar", "ASK/Raw demodulation"), + arg_lit0(NULL, "fs", "FSK demodulation"), + arg_lit0(NULL, "nr", "NRZ/Direct demodulation"), + arg_lit0(NULL, "p1", "PSK 1 demodulation"), + arg_lit0(NULL, "p2", "PSK 2 demodulation"), + arg_strn(NULL, NULL, "", 0, 35, "params for sub command"), + arg_param_end + }; + + // + size_t n = MIN(strlen(Cmd), 4); + char tmp[7]; + memset(tmp, 0, sizeof(tmp)); + strncpy(tmp, Cmd, n); + + CLIExecWithReturn(ctx, tmp, argtable, false); + bool ab = arg_get_lit(ctx, 1); + bool am = arg_get_lit(ctx, 2); + bool ar = arg_get_lit(ctx, 3); + bool fs = arg_get_lit(ctx, 4); + bool nr = arg_get_lit(ctx, 5); + bool p1 = arg_get_lit(ctx, 6); + bool p2 = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + int foo = (ab + am + ar + fs + nr + p1 + p2); + if (foo > 1) { + PrintAndLogEx(WARNING, "please, select only one modulation"); + return PM3_EINVARG; + } + if (foo == 0) { + PrintAndLogEx(WARNING, "please, select a modulation"); + return PM3_EINVARG; + } + int ans = 0; - - if (strlen(Cmd) > 35 || strlen(Cmd) < 2) - return usage_data_rawdemod(); - - str_lower((char *)Cmd); - - if (str_startswith(Cmd, "fs") || Cmd[0] == 'f') ans = CmdFSKrawdemod(Cmd + 2); - else if (str_startswith(Cmd, "ab")) ans = Cmdaskbiphdemod(Cmd + 2); - else if (str_startswith(Cmd, "am")) ans = Cmdaskmandemod(Cmd + 2); - else if (str_startswith(Cmd, "ar")) ans = Cmdaskrawdemod(Cmd + 2); - else if (str_startswith(Cmd, "nr") || Cmd[0] == 'n') ans = CmdNRZrawDemod(Cmd + 2); - else if (str_startswith(Cmd, "p1")) ans = CmdPSK1rawDemod(Cmd + 2); - else if (str_startswith(Cmd, "p2")) ans = CmdPSK2rawDemod(Cmd + 2); - else PrintAndLogEx(WARNING, "Unknown modulation entered - see help ('h') for parameter structure"); + const char *s = Cmd + n; + if (fs) + ans = CmdFSKrawdemod(s); + else if (ab) + ans = Cmdaskbiphdemod(s); + else if (am) + ans = Cmdaskmandemod(s); + else if (ar) + ans = Cmdaskrawdemod(s); + else if (nr) + ans = CmdNRZrawDemod(s); + else if (p1) + ans = CmdPSK1rawDemod(s); + else if (p2) + ans = CmdPSK2rawDemod(s); return ans; } @@ -1463,7 +1572,24 @@ void setClockGrid(uint32_t clk, int offset) { } int CmdGrid(const char *Cmd) { - sscanf(Cmd, "%lf %lf", &PlotGridX, &PlotGridY); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data grid", + "This function overlay grid on graph plot window.\n" + "use zero value to turn off either", + "data grid --> turn off\n" + "data grid -x 64 -y 50" + ); + void *argtable[] = { + arg_param_begin, + arg_dbl0("x", NULL, "", "plot grid X coord"), + arg_dbl0("y", NULL, "", "plot grid Y coord"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + PlotGridX = arg_get_dbl_def(ctx, 1, 0); + PlotGridY = arg_get_dbl_def(ctx, 2, 0); + CLIParserFree(ctx); + PrintAndLogEx(INFO, "Setting X %.0f Y %.0f", PlotGridX, PlotGridY); PlotGridXdefault = PlotGridX; PlotGridYdefault = PlotGridY; RepaintGraphWindow(); @@ -1471,28 +1597,60 @@ int CmdGrid(const char *Cmd) { } static int CmdSetGraphMarkers(const char *Cmd) { - sscanf(Cmd, "%i %i", &CursorCPos, &CursorDPos); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data setgraphmarkers", + "Set blue and orange marker in graph window", + "data setgraphmarkers --> turn off\n" + "data setgraphmarkers -a 64 -b 50" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_0("a", NULL, "", "orange marker"), + arg_u64_0("b", NULL, "", "blue marker"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CursorCPos = arg_get_u32_def(ctx, 1, 0); + CursorDPos = arg_get_u32_def(ctx, 2, 0); + CLIParserFree(ctx); + PrintAndLogEx(INFO, "Setting orange %u blue %u", CursorCPos, CursorDPos); RepaintGraphWindow(); return PM3_SUCCESS; } static int CmdHexsamples(const char *Cmd) { - uint32_t requested = 0; - uint32_t offset = 0; - char string_buf[25]; - char *string_ptr = string_buf; - uint8_t got[pm3_capabilities.bigbuf_size]; - sscanf(Cmd, "%u %u", &requested, &offset); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data hexsamples", + "Dump big buffer as hex bytes", + "data hexsamples -n 128 --> dumps 128 bytes from offset 0" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_0("b", "breaks", "", "row break, def 16"), + arg_u64_0("n", NULL, "", "num of bytes to download"), + arg_u64_0("o", "offset", "", "offset in big buffer"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t breaks = arg_get_u32_def(ctx, 1, 16); + uint32_t requested = arg_get_u32_def(ctx, 2, 8); + uint32_t offset = arg_get_u32_def(ctx, 3, 0); + CLIParserFree(ctx); - /* if no args send something */ - if (requested == 0) - requested = 8; - if (requested > pm3_capabilities.bigbuf_size) + // sanity checks + if (requested > pm3_capabilities.bigbuf_size) { requested = pm3_capabilities.bigbuf_size; + PrintAndLogEx(INFO, "n is larger than big buffer size, will use %u", requested); + } + uint8_t got[pm3_capabilities.bigbuf_size]; if (offset + requested > sizeof(got)) { - PrintAndLogEx(NORMAL, "Tried to read past end of buffer, + > %d", pm3_capabilities.bigbuf_size); + PrintAndLogEx(NORMAL, "Tried to read past end of buffer, + > %d" + , requested + , offset + , pm3_capabilities.bigbuf_size + ); return PM3_EINVARG; } @@ -1501,35 +1659,40 @@ static int CmdHexsamples(const char *Cmd) { return PM3_ESOFT; } - uint8_t i = 0; - for (uint32_t j = 0; j < requested; j++) { - i++; - string_ptr += sprintf(string_ptr, "%02x ", got[j]); - if (i == 8) { - *(string_ptr - 1) = '\0'; // remove the trailing space - PrintAndLogEx(NORMAL, "%s", string_buf); - string_buf[0] = '\0'; - string_ptr = string_buf; - i = 0; - } - if (j == requested - 1 && string_buf[0] != '\0') { // print any remaining bytes - *(string_ptr - 1) = '\0'; - PrintAndLogEx(NORMAL, "%s", string_buf); - string_buf[0] = '\0'; - } - } + print_hex_break(got, requested, breaks); return PM3_SUCCESS; } static int CmdHide(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "data hide", + "Show graph window", + "data hide" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); HideGraphWindow(); return PM3_SUCCESS; } -//zero mean GraphBuffer +// zero mean GraphBuffer int CmdHpf(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "data hpf", + "Remove DC offset from trace. It should centralize around 0", + "data hpf" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + uint8_t bits[GraphTraceLen]; size_t size = getFromGraphBuf(bits); removeSignalOffset(bits, size); @@ -1632,12 +1795,43 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose) { } static int CmdSamples(const char *Cmd) { - int n = strtol(Cmd, NULL, 0); - return getSamples(n, false); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data samples", + "Get raw samples for graph window (GraphBuffer) from device.\n" + "If 0, then get whole big buffer from device.", + "data samples\n" + "data samples -n 10000" + ); + void *argtable[] = { + arg_param_begin, + arg_int0("n", NULL, "", "num of samples (512 - 40000)"), + arg_lit0("v", "verbose", "verbose"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int n = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + return getSamples(n, verbose); } int CmdTuneSamples(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data tune", + "Measure tuning of device antenna. Results shown in graph window.\n" + "This command doesn't actively tune your antennas, \n" + "it's only informative by measuring voltage that the antennas will generate", + "data tune" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + #define NON_VOLTAGE 1000 #define LF_UNUSABLE_V 2000 #define LF_MARGINAL_V 10000 @@ -1652,7 +1846,9 @@ int CmdTuneSamples(const char *Cmd) { int timeout = 0; int timeout_max = 20; - PrintAndLogEx(INFO, "REMINDER: " _YELLOW_("'hw tune' doesn't actively tune your antennas") ", it's only informative"); + PrintAndLogEx(INFO, "---------- " _CYAN_("Reminder") " ------------------------"); + PrintAndLogEx(INFO, "`" _YELLOW_("hw tune") "` doesn't actively tune your antennas,"); + PrintAndLogEx(INFO, "it's only informative."); PrintAndLogEx(INFO, "Measuring antenna characteristics, please wait..."); clearCommandBuffer(); @@ -1744,11 +1940,15 @@ int CmdTuneSamples(const char *Cmd) { const double approx_vdd_other_max = 8840; // 1% over threshold and supposedly non-RDV4 - if ((approx_vdd > approx_vdd_other_max * 1.01) && (! IfPm3Rdv4Fw())) - PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_OTHER firmware on a RDV4") ", please check your setup"); + if ((approx_vdd > approx_vdd_other_max * 1.01) && (!IfPm3Rdv4Fw())) { + PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_GENERIC firmware on a RDV4")); + PrintAndLogEx(WARNING, "False positives is possible but please check your setup"); + } // 1% below threshold and supposedly RDV4 - if ((approx_vdd < approx_vdd_other_max * 0.99) && (IfPm3Rdv4Fw())) - PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_RDV4 firmware on a non-RDV4") ", please check your setup"); + if ((approx_vdd < approx_vdd_other_max * 0.99) && (IfPm3Rdv4Fw())) { + PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_RDV4 firmware on a generic device")); + PrintAndLogEx(WARNING, "False positives is possible but please check your setup"); + } } } @@ -1820,7 +2020,7 @@ static int CmdLoad(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_strx0("f", "file", "", "file to load"), + arg_str1("f", "file", "", "file to load"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1873,11 +2073,25 @@ static int CmdLoad(const char *Cmd) { // trim graph from the end int CmdLtrim(const char *Cmd) { - - uint32_t ds = strtoul(Cmd, NULL, 10); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data ltrim", + "Trim samples from left of trace", + "data ltrim -i 300 --> keep 300 - end" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_1("i", "idx", "", "from index to beginning trace"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t ds = arg_get_u32(ctx, 1); + CLIParserFree(ctx); // sanitycheck - if (GraphTraceLen <= ds) return PM3_ESOFT; + if (GraphTraceLen <= ds) { + PrintAndLogEx(WARNING, "index out of bounds"); + return PM3_EINVARG; + } for (uint32_t i = ds; i < GraphTraceLen; ++i) GraphBuffer[i - ds] = GraphBuffer[i]; @@ -1891,10 +2105,25 @@ int CmdLtrim(const char *Cmd) { // trim graph from the beginning static int CmdRtrim(const char *Cmd) { - uint32_t ds = strtoul(Cmd, NULL, 10); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data rtrim", + "Trim samples from right of trace", + "data rtrim -i 4000 --> keep 0 - 4000" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_1("i", "idx", "", "from index to end trace"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t ds = arg_get_u32(ctx, 1); + CLIParserFree(ctx); // sanitycheck - if (GraphTraceLen <= ds) return PM3_ESOFT; + if (GraphTraceLen <= ds) { + PrintAndLogEx(WARNING, "index out of bounds"); + return PM3_EINVARG; + } GraphTraceLen = ds; RepaintGraphWindow(); @@ -1903,24 +2132,53 @@ static int CmdRtrim(const char *Cmd) { // trim graph (middle) piece static int CmdMtrim(const char *Cmd) { - uint32_t start = 0, stop = 0; - sscanf(Cmd, "%u %u", &start, &stop); - if (start > GraphTraceLen || stop > GraphTraceLen || start >= stop) - return PM3_ESOFT; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data mtrim", + "Trim out samples from the specified start to the specified end point", + "data mtrim -s 1000 -e 2000 --> keep between 1000 and 2000" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_1("s", "start", "", "start point"), + arg_u64_1("e", "end", "", "end point"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint32_t start = arg_get_u32(ctx, 1); + uint32_t stop = arg_get_u32(ctx, 2); + CLIParserFree(ctx); + + if (start > GraphTraceLen || stop > GraphTraceLen || start >= stop) { + PrintAndLogEx(WARNING, "start and end points doesn't align"); + return PM3_EINVARG; + } // leave start position sample start++; GraphTraceLen = stop - start; - for (uint32_t i = 0; i < GraphTraceLen; i++) + for (uint32_t i = 0; i < GraphTraceLen; i++) { GraphBuffer[i] = GraphBuffer[start + i]; + } return PM3_SUCCESS; } int CmdNorm(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data norm", + "Normalize max/min to +/-128", + "data norm" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + int max = INT_MIN, min = INT_MAX; // Find local min, max @@ -1946,7 +2204,18 @@ int CmdNorm(const char *Cmd) { } int CmdPlot(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "data plot", + "Show graph window \n" + "hit 'h' in window for detail keystroke help available", + "data plot" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); ShowGraphWindow(); return PM3_SUCCESS; } @@ -2046,10 +2315,23 @@ int directionalThreshold(const int *in, int *out, size_t len, int8_t up, int8_t } static int CmdDirectionalThreshold(const char *Cmd) { - int8_t up = param_get8(Cmd, 0); - int8_t down = param_get8(Cmd, 1); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data dirthreshold", + "Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.", + "data dirthreshold -u 10 -d -10" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("d", "down", "", "threshold down"), + arg_int1("u", "up", "", "threshold up"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int8_t down = arg_get_int(ctx, 1); + int8_t up = arg_get_int(ctx, 2); + CLIParserFree(ctx); - PrintAndLogEx(INFO, "Applying Up Threshold: %d, Down Threshold: %d\n", up, down); + PrintAndLogEx(INFO, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up, down); directionalThreshold(GraphBuffer, GraphBuffer, GraphTraceLen, up, down); @@ -2064,7 +2346,18 @@ static int CmdDirectionalThreshold(const char *Cmd) { } static int CmdZerocrossings(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "data zerocrossings", + "Count time between zero-crossings", + "data zerocrossings" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + // Zero-crossings aren't meaningful unless the signal is zero-mean. CmdHpf(""); @@ -2090,7 +2383,6 @@ static int CmdZerocrossings(const char *Cmd) { size_t size = getFromGraphBuf(bits); // set signal properties low/high/mean/amplitude and is_noise detection computeSignalProperties(bits, size); - RepaintGraphWindow(); return PM3_SUCCESS; } @@ -2101,41 +2393,88 @@ static int CmdZerocrossings(const char *Cmd) { * @return */ static int Cmdbin2hex(const char *Cmd) { - int bg = 0, en = 0; - if (param_getptr(Cmd, &bg, &en, 0)) - return usage_data_bin2hex(); - //Number of digits supplied as argument - size_t length = en - bg + 1; - size_t bytelen = (length + 7) / 8; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data bin2hex", + "This function converts binary to hexadecimal. It will ignore all\n" + "characters not 1 or 0 but stop reading on whitespace", + "data bin2hex -d 0101111001010" + ); + void *argtable[] = { + arg_param_begin, + arg_strx0("d", "data", "", "binary string to convert"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int blen = 0; + uint8_t binarr[400] = {0x00}; + int res = CLIParamBinToBuf(arg_get_str(ctx, 1), binarr, sizeof(binarr), &blen); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing binary string"); + return PM3_EINVARG; + } + + // Number of digits supplied as argument + size_t bytelen = (blen + 7) / 8; uint8_t *arr = (uint8_t *) calloc(bytelen, sizeof(uint8_t)); memset(arr, 0, bytelen); BitstreamOut bout = { arr, 0, 0 }; - for (; bg <= en; bg++) { - char c = Cmd[bg]; - if (c == '1') + for (int i = 0; i < blen; i++) { + uint8_t c = binarr[i]; + if (c == 1) pushBit(&bout, 1); - else if (c == '0') + else if (c == 0) pushBit(&bout, 0); else - PrintAndLogEx(NORMAL, "Ignoring '%c'", c); + PrintAndLogEx(INFO, "Ignoring '%d' at pos %d", c, i); } if (bout.numbits % 8 != 0) - PrintAndLogEx(NORMAL, "[padded with %d zeroes]", 8 - (bout.numbits % 8)); + PrintAndLogEx(INFO, "[right padded with %d zeroes]", 8 - (bout.numbits % 8)); - PrintAndLogEx(NORMAL, "%s", sprint_hex(arr, bytelen)); + PrintAndLogEx(SUCCESS, _YELLOW_("%s"), sprint_hex(arr, bytelen)); free(arr); return PM3_SUCCESS; } static int Cmdhex2bin(const char *Cmd) { - int bg = 0, en = 0; - if (param_getptr(Cmd, &bg, &en, 0)) return usage_data_hex2bin(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data hex2bin", + "This function converts hexadecimal to binary. It will ignore all\n" + "non-hexadecimal characters but stop reading on whitespace", + "data hex2bin -d 01020304" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("d", "data", "", "bytes to convert"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int dlen = 0; + char data[200] = {0x00}; + int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)data, sizeof(data), &dlen); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + 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; + } + } + + PrintAndLogEx(SUCCESS, "" NOLF); + for (int i = 0; i < dlen; i++) { + char x = data[i]; - while (bg <= en) { - char x = Cmd[bg++]; // capitalize if (x >= 'a' && x <= 'f') x -= 32; @@ -2147,8 +2486,9 @@ static int Cmdhex2bin(const char *Cmd) { else continue; - for (int i = 0 ; i < 4 ; ++i) - PrintAndLogEx(NORMAL, "%d" NOLF, (x >> (3 - i)) & 1); + for (int j = 0 ; j < 4 ; ++j) { + PrintAndLogEx(NORMAL, "%d" NOLF, (x >> (3 - j)) & 1); + } } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -2295,36 +2635,27 @@ static int FSKToNRZ(int *data, size_t *dataLen, uint8_t clk, uint8_t LowToneFC, } static int CmdFSKToNRZ(const char *Cmd) { - // take clk, fc_low, fc_high - // blank = auto; - bool errors = false; - char cmdp = 0; - int clk = 0, fc_low = 10, fc_high = 8; - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_data_fsktonrz(); - case 'c': - clk = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'f': - fc_high = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'l': - fc_low = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - if (errors) break; - } - //Validations - if (errors) return usage_data_fsktonrz(); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data fsktonrz", + "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n" + "Omitted values are autodetect instead", + "data fsktonrz\n" + "data fsktonrz -c 32 --low 8 --hi 10"); + + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "clock"), + arg_int0(NULL, "low", "", "low field clock"), + arg_int0(NULL, "hi", "", "high field clock"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int clk = arg_get_int_def(ctx, 1, 0); + int fc_low = arg_get_int_def(ctx, 2, 0); + int fc_high = arg_get_int_def(ctx, 3, 0); + CLIParserFree(ctx); setClockGrid(0, 0); DemodBufferLen = 0; @@ -2335,8 +2666,21 @@ static int CmdFSKToNRZ(const char *Cmd) { } static int CmdDataIIR(const char *Cmd) { - uint8_t k = param_get8(Cmd, 0); - //iceIIR_Butterworth(GraphBuffer, GraphTraceLen); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data iir", + "Apply IIR buttersworth filter on plot data", + "data iir -n 2" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_1("n", NULL, "", "factor n"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint8_t k = (arg_get_u32_def(ctx, 1, 0) & 0xFF); + CLIParserFree(ctx); + iceSimple_Filter(GraphBuffer, GraphTraceLen, k); uint8_t bits[GraphTraceLen]; @@ -2394,20 +2738,21 @@ typedef struct { } lf_modulation_t; static int print_modulation(lf_modulation_t b) { - PrintAndLogEx(INFO, " Modulation.... " _GREEN_("%s"), GetSelectedModulationStr(b.modulation)); - PrintAndLogEx(INFO, " Bit clock..... " _GREEN_("RF/%d"), b.bitrate); + PrintAndLogEx(INFO, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b.modulation)); + PrintAndLogEx(INFO, " Bit clock......... " _GREEN_("RF/%d"), b.bitrate); + PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") "bauds", (125000 / (float)b.bitrate)); switch (b.modulation) { case DEMOD_PSK1: case DEMOD_PSK2: case DEMOD_PSK3: - PrintAndLogEx(SUCCESS, " Carrier rate.. %d", b.carrier); + PrintAndLogEx(SUCCESS, " Carrier rate...... %d", b.carrier); break; case DEMOD_FSK: case DEMOD_FSK1: case DEMOD_FSK1a: case DEMOD_FSK2: case DEMOD_FSK2a: - PrintAndLogEx(SUCCESS, " Field Clocks.. FC/%u, FC/%u", b.fc1, b.fc2); + PrintAndLogEx(SUCCESS, " Field Clocks...... FC/%u, FC/%u", b.fc1, b.fc2); break; case DEMOD_NRZ: case DEMOD_ASK: @@ -2494,7 +2839,7 @@ static int try_detect_modulation(void) { // allow undo save_restoreGB(GRAPH_SAVE); // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - CmdLtrim("160"); + CmdLtrim("-i 160"); if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS)) { tests[hits].modulation = DEMOD_PSK1; tests[hits].bitrate = clk; @@ -2549,41 +2894,39 @@ static command_t CommandTable[] = { {"rawdemod", CmdRawDemod, AlwaysAvailable, "Demodulate the data in the GraphBuffer and output binary"}, {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Graph") "-------------------------"}, - {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "[threshold] Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave (use 20-45, def:25)"}, + {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave"}, {"autocorr", CmdAutoCorr, AlwaysAvailable, "Autocorrelation over window"}, - {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, " -- Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, + {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, "Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, {"decimate", CmdDecimate, AlwaysAvailable, "Decimate samples"}, {"undecimate", CmdUndecimate, AlwaysAvailable, "Un-decimate samples"}, {"hide", CmdHide, AlwaysAvailable, "Hide graph window"}, {"hpf", CmdHpf, AlwaysAvailable, "Remove DC offset from trace"}, - {"iir", CmdDataIIR, AlwaysAvailable, "apply IIR buttersworth filter on plotdata"}, - {"grid", CmdGrid, AlwaysAvailable, " -- overlay grid on graph window, use zero value to turn off either"}, - {"ltrim", CmdLtrim, AlwaysAvailable, " -- Trim samples from left of trace"}, - {"mtrim", CmdMtrim, AlwaysAvailable, " -- Trim out samples from the specified start to the specified stop"}, + {"iir", CmdDataIIR, AlwaysAvailable, "Apply IIR buttersworth filter on plot data"}, + {"grid", CmdGrid, AlwaysAvailable, "overlay grid on graph window"}, + {"ltrim", CmdLtrim, AlwaysAvailable, "Trim samples from left of trace"}, + {"mtrim", CmdMtrim, AlwaysAvailable, "Trim out samples from the specified start to the specified stop"}, {"norm", CmdNorm, AlwaysAvailable, "Normalize max/min to +/-128"}, - {"plot", CmdPlot, AlwaysAvailable, "Show graph window (hit 'h' in window for keystroke help)"}, - {"rtrim", CmdRtrim, AlwaysAvailable, " -- Trim samples from right of trace"}, - {"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "[orange_marker] [blue_marker] (in graph window)"}, - {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, " -- Shift 0 for Graphed wave + or - shift value"}, - {"timescale", CmdTimeScale, AlwaysAvailable, "Set a timescale to get a differential reading between the yellow and purple markers as time duration\n"}, + {"plot", CmdPlot, AlwaysAvailable, "Show graph window"}, + {"rtrim", CmdRtrim, AlwaysAvailable, "Trim samples from right of trace"}, + {"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "Set blue and orange marker in graph window"}, + {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "Shift 0 for Graphed wave + or - shift value"}, + {"timescale", CmdTimeScale, AlwaysAvailable, "Set a timescale to get a differential reading between the yellow and purple markers as time duration"}, {"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"}, - {"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"}, {"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"}, - {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"}, {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"}, {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, {"clear", CmdBuffClear, AlwaysAvailable, "Clears bigbuf on deviceside and graph window"}, - {"hexsamples", CmdHexsamples, IfPm3Present, " [] -- Dump big buffer as hex bytes"}, + {"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"}, {"ndef", CmdDataNDEF, AlwaysAvailable, "Decode NDEF records"}, - {"print", CmdPrintDemodBuff, AlwaysAvailable, "print the data in the DemodBuffer"}, - {"samples", CmdSamples, IfPm3Present, "[512 - 40000] -- Get raw samples for graph window (GraphBuffer)"}, + {"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)"}, - {"setdebugmode", CmdSetDebugMode, AlwaysAvailable, "<0|1|2> -- Set Debugging Level on client side"}, + {"setdebugmode", CmdSetDebugMode, AlwaysAvailable, "Set Debugging Level on client side"}, {"tune", CmdTuneSamples, IfPm3Present, "Measure tuning of device antenna. Results shown in graph window"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 5bb3b8cd9..dfd5517d0 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -17,6 +17,7 @@ #include "cmdflashmemspiffs.h" // spiffs commands #include "rsa.h" #include "sha1.h" +#include "pk.h" // PEM key load functions #define MCK 48000000 #define FLASH_MINFAST 24000000 //33000000 @@ -40,48 +41,9 @@ static int CmdHelp(const char *Cmd); "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" //------------------------------------------------------------------------------------- -// Sample private RSA Key -// Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) - -// private key - Exponent D -#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ - "66CA472BC44D253102F8B4A9D3BFA750" \ - "91386C0077937FE33FA3252D28855837" \ - "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ - "DF79C5CE07EE72C7F123142198164234" \ - "CABB724CF78B8173B9F880FC86322407" \ - "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ - "071513A1E85B5DFA031F21ECAE91A34D" - -// prime P -#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ - "2C01CAD19EA484A87EA4377637E75500" \ - "FCB2005C5C7DD6EC4AC023CDA285D796" \ - "C3D9E75E1EFC42488BB4F1D13AC30A57" - -// prime Q -#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ - "E211C2B9E5DB1ED0BF61D0D9899620F4" \ - "910E4168387E3C30AA1E00C339A79508" \ - "8452DD96A9A5EA5D9DCA68DA636032AF" - -#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ - "3C94D22288ACD763FD8E5600ED4A702D" \ - "F84198A5F06C2E72236AE490C93F07F8" \ - "3CC559CD27BC2D1CA488811730BB5725" - -#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ - "D8AAEA56749EA28623272E4F7D0592AF" \ - "7C1F1313CAC9471B5C523BFE592F517B" \ - "407A1BD76C164B93DA2D32A383E58357" - -#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ - "F38D18D2B2F0E2DD275AA977E2BF4411" \ - "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ - "A74206CEC169D74BF5A8C50D6F48EA08" int rdv4_get_signature(rdv40_validation_t *out) { - if (out == NULL) { + if (out == NULL) { return PM3_EINVARG; } @@ -106,7 +68,6 @@ int rdv4_get_signature(rdv40_validation_t *out) { // validate signature int rdv4_validate(rdv40_validation_t *mem) { - // Flash ID hash (sha1) uint8_t sha_hash[20] = {0}; mbedtls_sha1(mem->flashid, sizeof(mem->flashid), sha_hash); @@ -128,7 +89,7 @@ int rdv4_validate(rdv40_validation_t *mem) { return PM3_EFAILED; } -static int rdv4_sign_write(uint8_t *signature, uint8_t slen){ +static int rdv4_sign_write(uint8_t *signature, uint8_t slen) { // save to mem clearCommandBuffer(); PacketResponseNG resp; @@ -146,7 +107,7 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen){ return PM3_EFAILED; } -static int CmdFlashmemSpiBaudrate(const char *Cmd) { +static int CmdFlashmemSpiBaud(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem baudrate", @@ -355,6 +316,7 @@ static int CmdFlashMemDump(const char *Cmd) { arg_int0("l", "len", "", "length"), arg_lit0("v", "view", "view dump"), arg_strx0("f", "file", "", "file name"), + arg_int0("c", "cols", "", "column breaks (def 32)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -365,6 +327,7 @@ static int CmdFlashMemDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + int breaks = arg_get_int_def(ctx, 5, 32); CLIParserFree(ctx); uint8_t *dump = calloc(len, sizeof(uint8_t)); @@ -382,7 +345,7 @@ static int CmdFlashMemDump(const char *Cmd) { if (view) { PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------"); - print_hex_break(dump, len, 32); + print_hex_break(dump, len, breaks); } if (filename[0] != '\0') { @@ -399,9 +362,9 @@ static int CmdFlashMemWipe(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem wipe", "Wipe flash memory on device, which fills it with 0xFF\n" - _WHITE_("[ ") _RED_("!!! OBS") " ] use with caution", - "mem wipe -p 0 -> wipes first page" -// "mem wipe -i -> inital total wipe" + _WHITE_("[ ") _RED_("!!! OBS") _WHITE_(" ] use with caution"), + "mem wipe -p 0 -> wipes first page" +// "mem wipe -i -> inital total wipe" ); void *argtable[] = { @@ -448,39 +411,90 @@ static int CmdFlashMemInfo(const char *Cmd) { CLIParserInit(&ctx, "mem info", "Collect signature and verify it from flash memory", "mem info" -// "mem info -s -d 0102030405060708" ); void *argtable[] = { arg_param_begin, arg_lit0("s", "sign", "create a signature"), arg_str0("d", NULL, "", "flash memory id, 8 hex bytes"), + arg_str0("p", "pem", "", "key in PEM format"), + arg_lit0("v", "verbose", "verbose output"), // arg_lit0("w", "write", "write signature to flash memory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool shall_sign = false, shall_write = false; - shall_sign = arg_get_lit(ctx, 1); + bool shall_sign = arg_get_lit(ctx, 1); int dlen = 0; uint8_t id[8] = {0}; int res = CLIParamHexToBuf(arg_get_str(ctx, 2), id, sizeof(id), &dlen); -// shall_write = arg_get_lit(ctx, 3); + int pemlen = 0; + char pem_fn[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)pem_fn, FILE_PATH_SIZE, &pemlen); + + bool verbose = arg_get_lit(ctx, 4); + bool shall_write = false; +// shall_write = arg_get_lit(ctx, 5); CLIParserFree(ctx); - if (dlen > 0 && dlen < sizeof(id) ) { + if (res || (dlen > 0 && dlen < sizeof(id))) { PrintAndLogEx(FAILED, "Error parsing flash memory id, expect 8, got %d", dlen); return PM3_EINVARG; } + // set up PK key context now. + mbedtls_pk_context pkctx; + mbedtls_pk_init(&pkctx); + bool got_private = false; + // PEM + if (pemlen) { + // PEM file + char *path = NULL; + if (searchFile(&path, RESOURCES_SUBDIR, pem_fn, ".pem", true) != PM3_SUCCESS) { + if (searchFile(&path, RESOURCES_SUBDIR, pem_fn, "", false) != PM3_SUCCESS) { + return PM3_EFILE; + } + } + + PrintAndLogEx(INFO, "loading file `" _YELLOW_("%s") "`" NOLF, path); + + // load private + res = mbedtls_pk_parse_keyfile(&pkctx, path, NULL); + //res = mbedtls_pk_parse_public_keyfile(&pkctx, path); + if (res == 0) { + PrintAndLogEx(NORMAL, " ( " _GREEN_("ok") " )"); + } else { + PrintAndLogEx(NORMAL, " ( " _RED_("fail") " )"); + mbedtls_pk_free(&pkctx); + free(path); + return PM3_EFILE; + } + + mbedtls_rsa_context *rsa = (mbedtls_rsa_context *)pkctx.pk_ctx; + if (rsa == NULL) { + PrintAndLogEx(FAILED, "failed to allocate rsa context memory"); + return PM3_EMALLOC; + } + free(path); + got_private = true; + } else { + + // it not loaded, we need to setup the context manually + if (mbedtls_pk_setup(&pkctx, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_RSA)) != 0) { + PrintAndLogEx(FAILED, "failed, mbedtls_pk_setup returned "); + return PM3_ESOFT; + } + } + // validate devicesignature data rdv40_validation_t mem; res = rdv4_get_signature(&mem); - if (res != PM3_SUCCESS) { + if (res != PM3_SUCCESS) { return res; } + res = rdv4_validate(&mem); // Flash ID hash (sha1) @@ -492,66 +506,69 @@ static int CmdFlashMemInfo(const char *Cmd) { PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------"); + for (int i = 0; i < (sizeof(mem.signature) / 32); i++) { + PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32)); + } PrintAndLogEx( (res == PM3_SUCCESS) ? SUCCESS : FAILED, "Signature............ ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail") ); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------"); - for (int i = 0; i < (sizeof(mem.signature) / 32); i++) { - PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32)); + + mbedtls_rsa_context *rsa = NULL; + + if (got_private) { + rsa = mbedtls_pk_rsa(pkctx); + rsa->padding = MBEDTLS_RSA_PKCS_V15; + rsa->hash_id = 0; + rsa->len = RRG_RSA_KEY_LEN; + } else { + + rsa = (mbedtls_rsa_context *)calloc(1, sizeof(mbedtls_rsa_context)); + mbedtls_rsa_init(rsa, MBEDTLS_RSA_PKCS_V15, 0); + rsa->len = RRG_RSA_KEY_LEN; + + // add public key + mbedtls_mpi_read_string(&rsa->N, 16, RRG_RSA_N); + mbedtls_mpi_read_string(&rsa->E, 16, RRG_RSA_E); } - mbedtls_rsa_context rsa; - mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); - - rsa.len = RRG_RSA_KEY_LEN; - - // add public key - mbedtls_mpi_read_string(&rsa.N, 16, RRG_RSA_N); - mbedtls_mpi_read_string(&rsa.E, 16, RRG_RSA_E); - - // add private key - mbedtls_mpi_read_string(&rsa.D, 16, RSA_D); - mbedtls_mpi_read_string(&rsa.P, 16, RSA_P); - mbedtls_mpi_read_string(&rsa.Q, 16, RSA_Q); - mbedtls_mpi_read_string(&rsa.DP, 16, RSA_DP); - mbedtls_mpi_read_string(&rsa.DQ, 16, RSA_DQ); - mbedtls_mpi_read_string(&rsa.QP, 16, RSA_QP); - - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------"); + if (verbose) { + char str_exp[10]; + char str_pk[261]; + size_t exlen = 0, pklen = 0; + mbedtls_mpi_write_string(&rsa->E, 16, str_exp, sizeof(str_exp), &exlen); + mbedtls_mpi_write_string(&rsa->N, 16, str_pk, sizeof(str_pk), &pklen); - char str_exp[10]; - char str_pk[261]; - size_t exlen = 0, pklen = 0; - mbedtls_mpi_write_string(&rsa.E, 16, str_exp, sizeof(str_exp), &exlen); - mbedtls_mpi_write_string(&rsa.N, 16, str_pk, sizeof(str_pk), &pklen); + PrintAndLogEx(INFO, "Len.................. %"PRIu64, rsa->len); + PrintAndLogEx(INFO, "Exponent............. %s", str_exp); + PrintAndLogEx(INFO, "Public key modulus N"); + PrintAndLogEx(INFO, " %.64s", str_pk); + PrintAndLogEx(INFO, " %.64s", str_pk + 64); + PrintAndLogEx(INFO, " %.64s", str_pk + 128); + PrintAndLogEx(INFO, " %.64s", str_pk + 192); + PrintAndLogEx(NORMAL, ""); + } - PrintAndLogEx(INFO, "Len.................. %"PRIu64, rsa.len); - PrintAndLogEx(INFO, "Exponent............. %s", str_exp); - PrintAndLogEx(INFO, "Public key modulus N"); - PrintAndLogEx(INFO, " %.64s", str_pk); - PrintAndLogEx(INFO, " %.64s", str_pk + 64); - PrintAndLogEx(INFO, " %.64s", str_pk + 128); - PrintAndLogEx(INFO, " %.64s", str_pk + 192); - PrintAndLogEx(NORMAL, ""); - - bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0); + bool is_keyok = (mbedtls_rsa_check_pubkey(rsa) == 0); PrintAndLogEx( (is_keyok) ? SUCCESS : FAILED, - "RSA public key validation.... ( %s )", - (is_keyok) ? _GREEN_("ok") : _RED_("fail") - ); - - is_keyok = (mbedtls_rsa_check_privkey(&rsa) == 0); - PrintAndLogEx( - (is_keyok) ? SUCCESS : FAILED, - "RSA private key validation... ( %s )", + "RRG/Iceman RSA public key check.... ( %s )", (is_keyok) ? _GREEN_("ok") : _RED_("fail") ); + is_keyok = (mbedtls_rsa_check_privkey(rsa) == 0); + if (verbose) { + PrintAndLogEx( + (is_keyok) ? SUCCESS : FAILED, + "RRG/Iceman RSA private key check... ( %s )", + (is_keyok) ? _GREEN_("ok") : _YELLOW_("N/A") + ); + } // to be verified uint8_t from_device[RRG_RSA_KEY_LEN]; @@ -563,55 +580,59 @@ static int CmdFlashMemInfo(const char *Cmd) { // Signing (private key) if (shall_sign) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Enter signing") " --------------------"); - if (dlen == 8) { - mbedtls_sha1(id, sizeof(id), sha_hash); - } - PrintAndLogEx(INFO, "Signing %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); + if (is_keyok) { - int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); - PrintAndLogEx( - (is_signed == 0) ? SUCCESS : FAILED, - "RSA signing.......... ( %s )", - (is_signed == 0) ? _GREEN_("ok") : _RED_("fail") - ); - - if (shall_write) { - rdv4_sign_write(sign, RRG_RSA_KEY_LEN); - } - PrintAndLogEx(INFO, "Signed"); - for (int i = 0; i < (sizeof(sign) / 32); i++) { - PrintAndLogEx(INFO, " %s", sprint_hex_inrow(sign + (i * 32), 32)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter signing") " --------------------"); + + if (dlen == 8) { + mbedtls_sha1(id, sizeof(id), sha_hash); + } + PrintAndLogEx(INFO, "Signing....... %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); + + int is_signed = mbedtls_rsa_pkcs1_sign(rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); + PrintAndLogEx( + (is_signed == 0) ? SUCCESS : FAILED, + "RSA signing... ( %s )", + (is_signed == 0) ? _GREEN_("ok") : _RED_("fail") + ); + + if (shall_write) { + rdv4_sign_write(sign, RRG_RSA_KEY_LEN); + } + PrintAndLogEx(INFO, "New signature"); + for (int i = 0; i < (sizeof(sign) / 32); i++) { + PrintAndLogEx(INFO, " %s", sprint_hex_inrow(sign + (i * 32), 32)); + } + } else { + PrintAndLogEx(FAILED, "no private key available to sign"); } } // 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); - mbedtls_rsa_free(&rsa); + bool is_verified = (mbedtls_rsa_pkcs1_verify(rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device) == 0); + mbedtls_pk_free(&pkctx); + + PrintAndLogEx(NORMAL, ""); PrintAndLogEx( (is_verified) ? SUCCESS : FAILED, - "RSA verification..... ( %s )", - (is_verified) ? _GREEN_("ok") : _RED_("fail") + "Genuine Proxmark3 RDV4 signature detected... %s", + (is_verified) ? ":heavy_check_mark:" : ":x:" ); - if (is_verified) { - PrintAndLogEx(SUCCESS, "Genuine Proxmark3 RDV4 signature detected"); - } - PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"baudrate", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate"}, - {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "High level SPI FileSystem Flash manipulation"}, - {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, - {"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory"}, - {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, - {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"}, + {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"}, + {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, + {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, + {"load", CmdFlashMemLoad, IfPm3Flash, "Load data to flash memory"}, + {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index 11c4db2d7..a3695e658 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -8,352 +8,16 @@ // Proxmark3 RDV40 Flash memory commands //----------------------------------------------------------------------------- #include "cmdflashmemspiffs.h" - #include - -#include "cmdparser.h" // command_t +#include "cmdparser.h" // command_t #include "pmflash.h" #include "fileutils.h" //saveFile -#include "comms.h" //getfromdevice +#include "comms.h" //getfromdevice +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_flashmemspiffs_remove(void) { - PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs remove "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs remove lasttag.bin")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_rename(void) { - PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs rename "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs rename lasttag.bin oldtag.bin")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_copy(void) { - PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs copy "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs copy lasttag.bin lasttag_cpy.bin")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_dump(void) { - PrintAndLogEx(NORMAL, "Dumps flash memory on device into a file or in console"); - PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs dump o [f [e]] [p]"); - PrintAndLogEx(NORMAL, " o - filename in SPIFFS"); - PrintAndLogEx(NORMAL, " f - file name to save to "); - PrintAndLogEx(NORMAL, " p - print dump in console"); - PrintAndLogEx(NORMAL, " e - also save in EML format (good for tags save and dictonnary files)"); - PrintAndLogEx(NORMAL, " You must specify at lease option f or option p, both if you wish"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin f lasttag e")); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin p")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_load(void) { - PrintAndLogEx(NORMAL, "Uploads binary-wise file into device filesystem"); - PrintAndLogEx(NORMAL, "Warning: mem area to be written must have been wiped first"); - PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); - PrintAndLogEx(NORMAL, " o - destination filename"); - PrintAndLogEx(NORMAL, " f - local filename"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs load f myfile o myapp.conf")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_wipe(void) { - PrintAndLogEx(NORMAL, "wipes all files on the device filesystem " _RED_("* Warning *")); - PrintAndLogEx(NORMAL, "Usage: mem spiffs wipe [h]"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs wipe")); - return PM3_SUCCESS; -} -static int CmdFlashMemSpiFFSMount(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSUnmount(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSTest(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_TEST, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSCheck(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSTree(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSInfo(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSRemove(const char *Cmd) { - - int len = strlen(Cmd); - if (len < 1) { - return usage_flashmemspiffs_remove(); - } - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (len == 1 && ctmp == 'h') { - return usage_flashmemspiffs_remove(); - } - - char filename[32] = {0}; - bool errors = false; - - if (param_getstr(Cmd, 0, filename, 32) >= 32) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - } - - // check null filename ? - if (errors) { - usage_flashmemspiffs_remove(); - return PM3_EINVARG; - } - - SendCommandMIX(CMD_SPIFFS_REMOVE, 0, 0, 0, (uint8_t *)filename, 32); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSRename(const char *Cmd) { - - int len = strlen(Cmd); - if (len < 1) { - return usage_flashmemspiffs_rename(); - } - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (len == 1 && ctmp == 'h') { - return usage_flashmemspiffs_rename(); - } - - char srcfilename[32] = {0}; - char destfilename[32] = {0}; - bool errors = false; - - if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (srcfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - // check null filename ? - if (errors) { - usage_flashmemspiffs_rename(); - return PM3_EINVARG; - } - - char data[65]; - sprintf(data, "%s,%s", srcfilename, destfilename); - SendCommandMIX(CMD_SPIFFS_RENAME, 0, 0, 0, (uint8_t *)data, 65); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSCopy(const char *Cmd) { - int len = strlen(Cmd); - if (len < 1) { - return usage_flashmemspiffs_copy(); - } - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (len == 1 && ctmp == 'h') { - return usage_flashmemspiffs_copy(); - } - - - char srcfilename[32] = {0}; - char destfilename[32] = {0}; - bool errors = false; - - if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (srcfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - // check null filename ? - if (errors) { - usage_flashmemspiffs_copy(); - return PM3_EINVARG; - } - - char data[65]; - sprintf(data, "%s,%s", srcfilename, destfilename); - SendCommandMIX(CMD_SPIFFS_COPY, 0, 0, 0, (uint8_t *)data, 65); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSDump(const char *Cmd) { - - char filename[FILE_PATH_SIZE] = {0}; - uint8_t cmdp = 0; - bool errors = false; - bool print = false; - uint32_t start_index = 0, len = FLASH_MEM_MAX_SIZE; - char destfilename[32] = {0}; - bool eml = false; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmemspiffs_dump(); - /*case 'l': - len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); - cmdp += 2; - break;*/ - case 'o': - param_getstr(Cmd, cmdp + 1, destfilename, 32); - cmdp += 2; - break; - case 'p': - print = true; - cmdp += 1; - break; - case 'e': - eml = true; - cmdp += 1; - break; - case 'f': - // File handling - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - break; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - if ((filename[0] == '\0') && (!print)) { - PrintAndLogEx(FAILED, "No print asked and local dump filename missing or invalid"); - errors = true; - } - - if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "SPIFFS filename missing or invalid"); - errors = true; - } - - // Validations - if (errors || cmdp == 0) { - usage_flashmemspiffs_dump(); - return PM3_EINVARG; - } - - // get size from spiffs itself ! - SendCommandMIX(CMD_SPIFFS_STAT, 0, 0, 0, (uint8_t *)destfilename, 32); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - len = resp.oldarg[0]; - - uint8_t *dump = calloc(len, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); - return PM3_EMALLOC; - } - - PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from spiffs (flashmem)", len); - if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)destfilename, 32, NULL, -1, true)) { - PrintAndLogEx(FAILED, "ERROR; downloading from spiffs(flashmemory)"); - free(dump); - return PM3_EFLASH; - } - - if (print) { - print_hex_break(dump, len, 32); - } - - if (filename[0] != '\0') { - saveFile(filename, ".bin", dump, len); - if (eml) { - uint8_t eml_len = 16; - - if (strstr(filename, "class") != NULL) - eml_len = 8; - else if (strstr(filename, "mfu") != NULL) - eml_len = 4; - - saveFileEML(filename, dump, len, eml_len); - } - } - - free(dump); - return PM3_SUCCESS; -} - -int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { +int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen) { int ret_val = PM3_SUCCESS; @@ -364,33 +28,38 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { // Send to device uint32_t bytes_sent = 0; uint32_t bytes_remaining = datalen; - uint32_t append = 0; // fast push mode conn.block_after_ACK = true; while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + flashmem_write_t *payload = calloc(1, sizeof(flashmem_write_t) + bytes_in_packet); + + payload->append = (bytes_sent > 0); + + uint8_t fnlen = MIN(sizeof(payload->fn), strlen(destfn)); + + payload->fnlen = fnlen; + memcpy(payload->fn, destfn, fnlen); + + payload->bytes_in_packet = bytes_in_packet; + memset(payload->data, 0, bytes_in_packet); + memcpy(payload->data, data + bytes_sent, bytes_in_packet); + + PacketResponseNG resp; clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_WRITE, (uint8_t *)payload, sizeof(flashmem_write_t) + bytes_in_packet); - char fdata[32 + bytes_in_packet]; - memset(fdata, 0, sizeof(fdata)); - memcpy(fdata, destfn, 32); - memcpy(fdata + 32, data + bytes_sent, bytes_in_packet); - - if (bytes_sent > 0) - append = 1; - - SendCommandOLD(CMD_SPIFFS_WRITE, append, bytes_in_packet, 0, fdata, 32 + bytes_in_packet); + free(payload); bytes_remaining -= bytes_in_packet; bytes_sent += bytes_in_packet; - PacketResponseNG resp; - uint8_t retry = 3; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); retry--; if (retry == 0) { @@ -398,13 +67,6 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { goto out; } } - - uint8_t isok = resp.oldarg[0] & 0xFF; - if (!isok) { - PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); - ret_val = PM3_EFLASH; - break; - } } out: @@ -416,99 +78,474 @@ out: // We want to unmount after these to set things back to normal but more than this // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); - return ret_val; } -static int CmdFlashMemSpiFFSWipe(const char *Cmd) { +static int CmdFlashMemSpiFFSMount(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs mount", + "Mount the SPIFFS file system if not already mounted", + "mem spiffs mount"); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') { - return usage_flashmemspiffs_wipe(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSUnmount(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs unmount", + "Un-mount the SPIFFS file system", + "mem spiffs unmount"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs test", + "Test SPIFFS Operations, require wiping pages 0 and 1", + "mem spiffs test"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_TEST, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCheck(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs check", + "Check/try to defrag faulty/fragmented SPIFFS file system", + "mem spiffs check"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTree(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs tree", + "Print the Flash memory file system tree", + "mem spiffs tree"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "--- " _CYAN_("Flash Memory tree (SPIFFS)") " -----------------"); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs info", + "Print file system info and usage statistics", + "mem spiffs info"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "--- " _CYAN_("Flash Memory info (SPIFFS)") " -----------------"); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRemove(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs remove", + "Remove a file from SPIFFS filesystem", + "mem spiffs remove -f lasttag.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "filename", "", "file to remove"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, 32, &fnlen); + CLIParserFree(ctx); + + PrintAndLogEx(DEBUG, "Removing `" _YELLOW_("%s") "`", filename); + struct { + uint8_t len; + uint8_t fn[32]; + } PACKED payload; + payload.len = fnlen; + memcpy(payload.fn, filename, fnlen); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_REMOVE, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_SPIFFS_REMOVE, &resp); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRename(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs rename", + "Rename/move a file from SPIFFS filesystem.", + "mem spiffs rename -s aaa.bin -d bbb.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "source file name"), + arg_str1("d", "dest", "", "destination file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int dlen = 0; + char dest[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen); + CLIParserFree(ctx); + + PrintAndLogEx(DEBUG, "Rename from `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest); + + struct { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED payload; + payload.slen = slen; + payload.dlen = dlen; + + memcpy(payload.src, src, slen); + memcpy(payload.dest, dest, dlen); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_RENAME, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_SPIFFS_RENAME, &resp); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCopy(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs copy", + "Copy a file to another (destructively) in SPIFFS file system", + "mem spiffs copy -s aaa.bin -d aaa_cpy.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "source file name"), + arg_str1("d", "dest", "", "destination file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int dlen = 0; + char dest[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen); + CLIParserFree(ctx); + + struct { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED payload; + payload.slen = slen; + payload.dlen = dlen; + + memcpy(payload.src, src, slen); + memcpy(payload.dest, dest, dlen); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_COPY, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_SPIFFS_COPY, &resp); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs dump", + "Dumps device SPIFFS file to a local file\n" + "Size is handled by first sending a STAT command against file to verify existence", + "mem spiffs dump -s tag.bin --> download binary file from device\n" + "mem spiffs dump -s tag.bin -d aaa -e --> download tag.bin, save as aaa.eml format" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "SPIFFS file to save"), + arg_str0("d", "dest", "", "file name to save to "), + arg_lit0("e", "eml", "also save in EML format"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int dlen = 0; + char dest[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, FILE_PATH_SIZE, &dlen); + + bool eml = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + // get size from spiffs itself ! + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; } - PrintAndLogEx(INFO, "Wiping all files from SPIFFS FileSystem"); + uint32_t len = resp.data.asDwords[0]; + uint8_t *dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + // download from device + uint32_t start_index = 0; + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src); + if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true)) { + PrintAndLogEx(FAILED, "error, downloading from spiffs"); + free(dump); + return PM3_EFLASH; + } + + // save to file + char fn[FILE_PATH_SIZE] = {0}; + if (dlen == 0) { + strncpy(fn, src, slen); + } else { + strncpy(fn, dest, dlen); + } + + saveFile(fn, ".bin", dump, len); + if (eml) { + uint8_t eml_len = 16; + if (strstr(fn, "class") != NULL) + eml_len = 8; + else if (strstr(fn, "mfu") != NULL) + eml_len = 4; + + saveFileEML(fn, dump, len, eml_len); + } + free(dump); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSWipe(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs wipe", + _RED_("* * * Warning * * *") " \n" + _CYAN_("This command wipes all files on the device SPIFFS file system"), + "mem spiffs wipe"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Wiping all files from SPIFFS file system"); PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0); WaitForResponse(CMD_SPIFFS_WIPE, &resp); - PrintAndLogEx(INFO, "Done!"); - PrintAndLogEx(HINT, "Try use '" _YELLOW_("mem spiffs tree") "' to verify."); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); return PM3_SUCCESS; } -static int CmdFlashMemSpiFFSLoad(const char *Cmd) { +static int CmdFlashMemSpiFFSUpload(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs upload", + "Uploads binary-wise file into device file system\n" + "Warning: mem area to be written must have been wiped first.\n" + "This is already taken care when loading dictionaries.\n" + "File names can only be 32 bytes long on device SPIFFS", + "mem spiffs upload -s local.bin -d dest.bin" + ); - char filename[FILE_PATH_SIZE] = {0}; - uint8_t destfilename[32] = {0}; - bool errors = false; - uint8_t cmdp = 0; + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "source file name"), + arg_str1("d", "dest", "", "destination file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmemspiffs_load(); - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - } - cmdp += 2; - break; - case 'o': - param_getstr(Cmd, cmdp + 1, (char *)destfilename, 32); - if (strlen((char *)destfilename) == 0) { - PrintAndLogEx(FAILED, "Destination Filename missing or invalid"); - errors = true; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + int slen = 0; + char src[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, FILE_PATH_SIZE, &slen); - // Validations - if (errors || cmdp == 0) - return usage_flashmemspiffs_load(); + int dlen = 0; + char dest[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen); + CLIParserFree(ctx); + + PrintAndLogEx(DEBUG, "Upload `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest); size_t datalen = 0; uint8_t *data = NULL; - int res = loadFile_safe(filename, "", (void **)&data, &datalen); - // int res = loadFileEML( filename, data, &datalen); + int res = loadFile_safe(src, "", (void **)&data, &datalen); if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } - res = flashmem_spiffs_load(destfilename, data, datalen); - + res = flashmem_spiffs_load(dest, data, datalen); free(data); if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, destfilename); + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, dest); + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); return res; } -static command_t CommandTable[] = { +static int CmdFlashMemSpiFFSView(const char *Cmd) { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented Filesystem"}, - {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, - {"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, - {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, - {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"}, - {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, - {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, - {"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS FileSystem." _RED_("* dangerous *") }, + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs view", + "View a file on flash memory on devicer in console", + "mem spiffs view -f tag.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "SPIFFS file to view"), + arg_int0("c", "cols", "", "column breaks (def 32)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int breaks = arg_get_int_def(ctx, 2, 32); + CLIParserFree(ctx); + + // get size from spiffs itself ! + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + uint32_t len = resp.data.asDwords[0]; + if (len == 0) { + PrintAndLogEx(ERR, "error, failed to retrieve file stats on SPIFFSS"); + return PM3_EFAILED; + } + + uint8_t *dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + uint32_t start_index = 0; + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src); + + if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true)) { + PrintAndLogEx(FAILED, "error, downloading from spiffs"); + free(dump); + return PM3_EFLASH; + } + + PrintAndLogEx(NORMAL, ""); + print_hex_break(dump, len, breaks); + PrintAndLogEx(NORMAL, ""); + free(dump); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"}, + {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"}, + {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"}, + {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"}, + {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"}, + {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"}, + {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"}, + {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations"}, + {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash memory file system tree"}, + {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS file system"}, + {"upload", CmdFlashMemSpiFFSUpload, IfPm3Flash, "Upload file into SPIFFS file system"}, + {"view", CmdFlashMemSpiFFSView, IfPm3Flash, "View file on SPIFFS file system"}, + {"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS file system * " _RED_("dangerous") " *" }, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdflashmemspiffs.h b/client/src/cmdflashmemspiffs.h index 9324a541b..385d78401 100644 --- a/client/src/cmdflashmemspiffs.h +++ b/client/src/cmdflashmemspiffs.h @@ -14,6 +14,6 @@ #include "common.h" int CmdFlashMemSpiFFS(const char *Cmd); -int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen); +int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen); #endif diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 2bc6edbfe..0cf9bcc37 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -86,7 +86,7 @@ int CmdHFSearch(const char *Cmd) { PrintAndLogEx(INPLACE, " Searching for ISO14443-A tag..."); if (IfPm3Iso14443a()) { if (infoHF14A(false, false, false) > 0) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-A tag") " found\n"); + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-A tag") " found\n"); res = PM3_SUCCESS; } } @@ -95,7 +95,7 @@ int CmdHFSearch(const char *Cmd) { PrintAndLogEx(INPLACE, " Searching for ISO15693 tag..."); if (IfPm3Iso15693()) { if (readHF15Uid(false, false)) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO15693 tag") " found\n"); + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 15693 tag") " found\n"); res = PM3_SUCCESS; } } @@ -127,35 +127,34 @@ int CmdHFSearch(const char *Cmd) { } } + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for FeliCa tag..."); + if (IfPm3Felica()) { + if (read_felica_uid(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 18092 / FeliCa tag") " found\n"); + res = PM3_SUCCESS; + } + } + // 14b is the longest test (put last) PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag..."); if (IfPm3Iso14443b()) { - if (readHF14B(false) == 1) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") " found\n"); + if (readHF14B(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-B tag") " found\n"); res = PM3_SUCCESS; } } /* - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for FeliCa tag..."); - if (IfPm3Felica()) { - if (readFelicaUid(false) == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, "\nValid " _GREEN_("ISO18092 / FeliCa tag") " found\n"); - res = PM3_SUCCESS; - } - } - */ - /* - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for CryptoRF tag..."); - if (IfPm3Iso14443b()) { - if (readHFCryptoRF(false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("CryptoRF tag") " found\n"); - res = PM3_SUCCESS; - } + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for CryptoRF tag..."); + if (IfPm3Iso14443b()) { + if (readHFCryptoRF(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("CryptoRF tag") " found\n"); + res = PM3_SUCCESS; } + } */ PROMPT_CLEARLINE; @@ -387,6 +386,10 @@ int CmdHFPlot(const char *Cmd) { return handle_hf_plot(); } +static int CmdHFList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf", "raw"); +} + static command_t CommandTable[] = { {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("High Frequency") " -----------------------"}, @@ -406,13 +409,13 @@ static command_t CommandTable[] = { {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, - {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, + {"st", CmdHFST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, + {"list", CmdHFList, AlwaysAvailable, "List protocol data in trace buffer"}, {"plot", CmdHFPlot, IfPm3Hfplot, "Plot signal"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, {"search", CmdHFSearch, AlwaysAvailable, "Search for known HF tags"}, diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 2b1631963..74fe60281 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -171,13 +171,7 @@ static uint16_t frameLength = 0; uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; static int CmdHF14AList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 14a"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf 14a", "14a"); } int hf14a_getconfig(hf14a_config *config) { @@ -218,23 +212,23 @@ static int hf_14a_config_example(void) { PrintAndLogEx(NORMAL, "\nExamples to revive Gen2/DirectWrite magic cards failing at anticollision:"); PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 4b UID")":"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344440804006263646566676869")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 4b UID")":"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344441802006263646566676869")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 7b UID")":"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566084400626364656667")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 7b UID")":"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa forcce --bcc ignore --cl2 force --cl3 skip --rats skip")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566184200626364656667")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip -rats skip")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 04112233445566")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid --uid 04112233445566")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); return PM3_SUCCESS; } @@ -404,7 +398,7 @@ static int CmdHF14AReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a reader", "Reader for ISO 14443A based tags", - "hf 14a reader -@ <- Continuous mode"); + "hf 14a reader -@ -> Continuous mode"); void *argtable[] = { arg_param_begin, @@ -412,7 +406,7 @@ static int CmdHF14AReader(const char *Cmd) { arg_lit0("s", "silent", "silent (no messages)"), arg_lit0(NULL, "drop", "just drop the signal field"), arg_lit0(NULL, "skip", "ISO14443-3 select only (skip RATS)"), - arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_lit0("@", NULL, "continuous reader mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -557,7 +551,7 @@ static int CmdHF14ACUIDs(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a cuids", "Collect n>0 ISO14443-a UIDs in one go", - "hf 14a cuids -n 5 <-- Collect 5 UIDs"); + "hf 14a cuids -n 5 --> Collect 5 UIDs"); void *argtable[] = { arg_param_begin, @@ -627,7 +621,7 @@ int CmdHF14ASim(const char *Cmd) { arg_int1("t", "type", "<1-10> ", "Simulation type to use"), arg_str0("u", "uid", "", "4, 7 or 10 byte UID"), arg_int0("n", "num", "", "Exit simulation after blocks have been read by reader. 0 = infinite"), - arg_lit0(NULL, "nr", "Performs the 'reader attack', nr/ar attack against a reader"), + arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -1198,7 +1192,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a raw", - "Sends an raw bytes over ISO14443a. With option to use TOPAZ 14a mode.", + "Sends raw bytes over ISO14443a. With option to use TOPAZ 14a mode.", "hf 14a raw -sc 3000 -> select, crc, where 3000 == 'read block 00'\n" "hf 14a raw -ak -b 7 40 -> send 7 bit byte 0x40\n" ); @@ -1628,8 +1622,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (verbose) { - PrintAndLogEx(SUCCESS, "------ " _CYAN_("ISO14443-a Information") "------------------"); - PrintAndLogEx(SUCCESS, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") "---------------------"); } PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); @@ -1990,7 +1983,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(INFO, "-------------------- " _CYAN_("AID Search") " --------------------"); bool found = false; - int elmindx = 0; + size_t elmindx = 0; json_t *root = AIDSearchInit(verbose); if (root != NULL) { bool ActivateField = true; @@ -2105,7 +2098,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { uint8_t signature[32] = {0}; res = detect_mfc_ev1_signature(signature); if (res == PM3_SUCCESS) { - mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); + mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); } PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands"); diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index a9524abb4..959dfb489 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -26,7 +26,7 @@ #include "mifare/ndef.h" // NDEFRecordsDecodeAndPrint #include "aidsearch.h" -#define MAX_14B_TIMEOUT 40542464U // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s +#define MAX_14B_TIMEOUT (4949000U >> 2) #define TIMEOUT 2000 #define APDU_TIMEOUT 2000 @@ -53,7 +53,6 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { static void hf14b_aid_search(bool verbose) { - int elmindx = 0; json_t *root = AIDSearchInit(verbose); if (root == NULL) { switch_off_field_14b(); @@ -65,7 +64,7 @@ static void hf14b_aid_search(bool verbose) { bool found = false; bool leave_signal_on = true; bool activate_field = true; - for (elmindx = 0; elmindx < json_array_size(root); elmindx++) { + for (size_t elmindx = 0; elmindx < json_array_size(root); elmindx++) { if (kbd_enter_pressed()) { break; @@ -200,13 +199,7 @@ static bool wait_cmd_14b(bool verbose, bool is_select) { } static int CmdHF14BList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 14b"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf 14b", "14b"); } static int CmdHF14BSim(const char *Cmd) { @@ -263,31 +256,30 @@ static int CmdHF14BSniff(const char *Cmd) { WaitForResponse(CMD_HF_ISO14443B_SNIFF, &resp); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14b list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } static int CmdHF14BCmdRaw(const char *Cmd) { - CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b raw", "Sends raw bytes to card", - "hf 14b raw -cks --data 0200a40400 -> standard select\n" + "hf 14b raw -cks --data 0200a40400 -> standard select, apdu 0200a4000 (7816)\n" "hf 14b raw -ck --sr --data 0200a40400 -> SRx select\n" "hf 14b raw -ck --cts --data 0200a40400 -> C-ticket select\n" ); void *argtable[] = { arg_param_begin, - arg_lit0("k", "keep", "leave the signal field ON after receive response"), - 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("c", "crc", "calculate and append CRC"), - arg_lit0(NULL, "noresponse", "do not read response from card"), - arg_int0("t", "timeout", "", "timeout in ms"), - arg_lit0("v", "verbose", "verbose"), - arg_strx0("d", "data", "", "data, bytes to send"), + arg_lit0("k", "keep", "leave the signal field ON after receive response"), + 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("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_strx0("d", "data", "", "data, bytes to send"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -297,7 +289,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { 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); + 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); @@ -457,7 +449,7 @@ static int print_atqb_resp(uint8_t *data, uint8_t cid) { PrintAndLogEx(SUCCESS, " Protocol Type: Protocol is %scompliant with ISO/IEC 14443-4", (protocolT) ? "" : "not "); uint8_t fwt = data[6] >> 4; - if (fwt < 16) { + if (fwt < 15) { uint32_t etus = (32 << fwt); uint32_t fwt_time = (302 << fwt); PrintAndLogEx(SUCCESS, "Frame Wait Integer: %u - %u ETUs | %u us", fwt, etus, fwt_time); @@ -1048,13 +1040,20 @@ static int CmdHF14BReader(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "verbose"), + arg_lit0("s", "silent", "silent (no messages)"), + arg_lit0("@", NULL, "optional - continuous reader mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool verbose = arg_get_lit(ctx, 1); + bool verbose = (arg_get_lit(ctx, 1) == false); + bool cm = arg_get_lit(ctx, 2); CLIParserFree(ctx); - return readHF14B(verbose); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + return readHF14B(cm, verbose); } // Read SRI512|SRIX4K block @@ -1126,7 +1125,7 @@ static int CmdHF14BWriteSri(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b sriwrite", - "Write data to a SRI512 | SRIX4K block", + "Write data to a SRI512 or SRIX4K block", "hf 14b sriwrite --4k -b 100 -d 11223344\n" "hf 14b sriwrite --4k --sb -d 11223344 --> special block write\n" "hf 14b sriwrite --512 -b 15 -d 11223344\n" @@ -1145,7 +1144,7 @@ static int CmdHF14BWriteSri(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); int blockno = arg_get_int_def(ctx, 1, -1); int dlen = 0; - uint8_t data[4] = {0,0,0,0}; + uint8_t data[4] = {0, 0, 0, 0}; int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &dlen); if (res) { CLIParserFree(ctx); @@ -1164,7 +1163,7 @@ static int CmdHF14BWriteSri(const char *Cmd) { if (use_sri512 + use_srix4k > 1) { PrintAndLogEx(FAILED, "Select only one card type"); - return PM3_EINVARG; + return PM3_EINVARG; } if (use_srix4k && blockno > 0x7F) { @@ -1932,7 +1931,7 @@ static command_t CommandTable[] = { {"sim", CmdHF14BSim, IfPm3Iso14443b, "Fake ISO 14443B tag"}, {"sniff", CmdHF14BSniff, IfPm3Iso14443b, "Eavesdrop ISO 14443B"}, {"rdbl", CmdHF14BSriRdBl, IfPm3Iso14443b, "Read SRI512/SRIX4x block"}, - {"sriwrite", CmdHF14BWriteSri, IfPm3Iso14443b, "Write data to a SRI512 | SRIX4K tag"}, + {"sriwrite", CmdHF14BWriteSri, IfPm3Iso14443b, "Write data to a SRI512 or SRIX4K tag"}, // {"valid", srix4kValid, AlwaysAvailable, "srix4k checksum test"}, {NULL, NULL, NULL, NULL} }; @@ -1966,25 +1965,30 @@ int infoHF14B(bool verbose, bool do_aid_search) { } // get and print general info about all known 14b chips -int readHF14B(bool verbose) { +int readHF14B(bool loop, bool verbose) { - // try std 14b (atqb) - if (HF14B_std_reader(verbose)) - return 1; + do { + // try std 14b (atqb) + if (HF14B_std_reader(verbose)) + return PM3_SUCCESS; - // try ST Microelectronics 14b - if (HF14B_st_reader(verbose)) - return 1; + // try ST Microelectronics 14b + if (HF14B_st_reader(verbose)) + return PM3_SUCCESS; - // try ASK CT 14b - if (HF14B_ask_ct_reader(verbose)) - return 1; + // try ASK CT 14b + if (HF14B_ask_ct_reader(verbose)) + return PM3_SUCCESS; - // 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 1; + // 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 (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); - return 0; + } while (loop && kbd_enter_pressed() == false); + + if (verbose) { + PrintAndLogEx(FAILED, "no ISO 14443-B tag found"); + } + return PM3_EOPABORTED; } diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index bd97bbf5c..ca05bc02e 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -18,5 +18,5 @@ int CmdHF14B(const char *Cmd); 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 infoHF14B(bool verbose, bool do_aid_search); -int readHF14B(bool verbose); +int readHF14B(bool loop, bool verbose); #endif diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 1fd1fb034..30d2db275 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -35,6 +35,7 @@ #include "cmddata.h" // getsamples #include "fileutils.h" // savefileEML #include "cliparser.h" +#include "util_posix.h" // msleep #define FrameSOF Iso15693FrameSOF #define Logic0 Iso15693Logic0 @@ -210,117 +211,6 @@ const productName_t uidmapping[] = { static int CmdHF15Help(const char *Cmd); -static int usage_15_info(void) { - PrintAndLogEx(NORMAL, "Uses the optional command 'get_systeminfo' 0x2B to try and extract information\n" - "command may fail, depending on tag.\n" - "defaults to '1 out of 4' mode\n" - "\n" - "Usage: hf 15 info [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "Examples:\n" - _YELLOW_("\thf 15 info u")); - return PM3_SUCCESS; -} - -static int usage_15_writeafi(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 writeafi \n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\t AFI number 0-255"); - return PM3_SUCCESS; -} -static int usage_15_writedsfid(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 writedsfid \n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\t DSFID number 0-255"); - return PM3_SUCCESS; -} -static int usage_15_dump(void) { - PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-15693 tag and save it to file\n" - "\n" - "Usage: hf 15 dump [h] \n" - "Options:\n" - "\th this help\n" - "\tf filename, if no UID will be used as filename\n" - "\n" - "Example:\n" - _YELLOW_("\thf 15 dump f\n") - _YELLOW_("\thf 15 dump f mydump")); - return PM3_SUCCESS; -} -static int usage_15_restore(void) { - const char *options[][2] = { - {"h", "this help"}, - {"-2", "use slower '1 out of 256' mode"}, - {"-o", "set OPTION Flag (needed for TI)"}, - {"a", "use addressed mode"}, - {"r ", "numbers of retries on error, default is 3"}, - {"f ", "load "}, - {"b ", "block size, default is 4"} - }; - PrintAndLogEx(NORMAL, "Usage: hf 15 restore [-2] [-o] [h] [r ] [u ] [f ] [b ]"); - PrintAndLogOptions(options, 7, 3); - return PM3_SUCCESS; -} -static int usage_15_raw(void) { - const char *options[][2] = { - {"-r", "do not read response" }, - {"-2", "use slower '1 out of 256' mode" }, - {"-c", "calculate and append CRC" }, - {"-k", "keep signal field ON after receive" }, - {"", "Tip: turn on debugging for verbose output"}, - }; - PrintAndLogEx(NORMAL, "Usage: hf 15 raw [-r] [-2] [-k] [-c] <0A 0B 0C ... hex>\n"); - PrintAndLogOptions(options, 4, 3); - return PM3_SUCCESS; -} -static int usage_15_read(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 rdbl [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\t page number 0-255"); - return PM3_SUCCESS; -} -static int usage_15_write(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 wrbl [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\t-o set OPTION Flag (needed for TI)\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\t page number 0-255\n" - "\t data to be written eg AA BB CC DD"); - return PM3_SUCCESS; -} -static int usage_15_readmulti(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 readmulti [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\t 0-255, page number to start\n" - "\t 1-6, number of pages"); - return PM3_SUCCESS; -} - static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { #define PUBLIC_ECDA_KEYLEN 33 @@ -514,7 +404,7 @@ static const char *TagErrorStr(uint8_t error) { // fast method to just read the UID of a tag (collision detection not supported) // *buf should be large enough to fit the 64bit uid // returns 1 if succeeded -static bool getUID(bool loop, uint8_t *buf) { +static int getUID(bool loop, uint8_t *buf) { uint8_t data[5]; data[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; @@ -527,7 +417,9 @@ static bool getUID(bool loop, uint8_t *buf) { uint8_t fast = 1; uint8_t reply = 1; - while (kbd_enter_pressed() == false) { + int res = PM3_ESOFT; + + do { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO15693_COMMAND, sizeof(data), fast, reply, data, sizeof(data)); PacketResponseNG resp; @@ -545,107 +437,55 @@ static bool getUID(bool loop, uint8_t *buf) { PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), iso15693_sprintUID(NULL, buf)); PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("%s"), getTagInfo_15(buf)); + res = PM3_SUCCESS; + if (loop == false) { - return true; + break; } } } - if (loop == false) { - break; - } - } + } while (loop && kbd_enter_pressed() == false); DropField(); - return false; + return res; } // used with 'hf search' bool readHF15Uid(bool loop, bool verbose) { uint8_t uid[8] = {0}; - if (getUID(loop, uid) == false) { - if (verbose) PrintAndLogEx(WARNING, "No tag found"); + if (getUID(loop, uid) != PM3_SUCCESS) { + if (verbose) PrintAndLogEx(WARNING, "no tag found"); return false; } return true; } -/** - * parses common HF 15 CMD parameters and prepares some data structures - * Parameters: - * **cmd command line - */ -static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t *req, uint8_t iso15cmd) { // reqlen arg0 - - if (*cmd == NULL || strlen(*cmd) == 0) - return false; - - int temp; - uint8_t uid[8] = {0x00}; - uint32_t tmpreqlen = 0; - - // strip - while (**cmd == ' ' || **cmd == '\t')(*cmd)++; - - if (strstr(*cmd, "-2") == *cmd) { - *arg1 = 0; // use 1of256 - (*cmd) += 2; +// adds 6 +static uint8_t arg_add_default(void *at[]) { + at[0] = arg_param_begin; + at[1] = arg_str0("u", "uid", "", "full UID, 8 bytes"); + at[2] = arg_lit0(NULL, "ua", "unaddressed mode"); + at[3] = arg_lit0("*", NULL, "scan for tag"); + at[4] = arg_lit0("2", NULL, "use slower '1 out of 256' mode"); + at[5] = arg_lit0("o", "opt", "set OPTION Flag (needed for TI)"); + return 6; +} +static uint16_t arg_get_raw_flag(uint8_t uidlen, bool unaddressed, bool scan, bool add_option) { + uint16_t flags = 0; + if (unaddressed) { + // unaddressed mode may not be supported by all vendors + flags |= (ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY); } - - // strip - while (**cmd == ' ' || **cmd == '\t')(*cmd)++; - - if (strstr(*cmd, "-o") == *cmd) { - req[tmpreqlen] = ISO15_REQ_OPTION; - (*cmd) += 2; + if (uidlen == 8) { + flags |= (ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS); } - - // strip - while (**cmd == ' ' || **cmd == '\t')(*cmd)++; - - char c = tolower(**cmd); - switch (c) { - case 0: - PrintAndLogEx(WARNING, "missing addr"); - return false; - case 'u': - // unaddressed mode may not be supported by all vendors - req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY; - req[tmpreqlen++] = iso15cmd; - break; - case '*': - // we scan for the UID ourself - req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[tmpreqlen++] = iso15cmd; - - if (getUID(false, uid) == false) { - PrintAndLogEx(WARNING, "No tag found"); - return false; - } - memcpy(&req[tmpreqlen], uid, sizeof(uid)); - PrintAndLogEx(SUCCESS, "Detected UID " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); - tmpreqlen += sizeof(uid); - break; - default: - req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[tmpreqlen++] = iso15cmd; - // parse UID - for (int i = 0; i < 8 && (*cmd)[i * 2] && (*cmd)[i * 2 + 1]; i++) { - sscanf((char[]) {(*cmd)[i * 2], (*cmd)[i * 2 + 1], 0}, "%X", &temp); - uid[7 - i] = temp & 0xff; - } - - PrintAndLogEx(SUCCESS, "Using UID " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); - memcpy(&req[tmpreqlen], uid, sizeof(uid)); - tmpreqlen += sizeof(uid); - break; + if (scan) { + flags |= (ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS); } - // skip to next space - while (**cmd != '\0' && **cmd != ' ' && **cmd != '\t')(*cmd)++; - // skip over the space - while (**cmd != '\0' && (**cmd == ' ' || **cmd == '\t'))(*cmd)++; - - *reqlen = tmpreqlen; - return true; + if (add_option) { + flags |= (ISO15_REQ_OPTION); + } + return flags; } // Mode 3 @@ -653,7 +493,7 @@ static int CmdHF15Demod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 demod", - "Tries to demodulate / decode ISO15693, from downloaded samples.\n" + "Tries to demodulate / decode ISO-15693, from downloaded samples.\n" "Gather samples with 'hf 15 samples' / 'hf 15 sniff'", "hf 15 demod\n"); @@ -960,29 +800,70 @@ static int NxpSysInfo(uint8_t *uid) { */ static int CmdHF15Info(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || cmdp == 'h') return usage_15_info(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 info", + "Uses the optional command `get_systeminfo` 0x2B to try and extract information", + "hf 15 info\n" + "hf 15 info -*\n" + "hf 15 info -u E011223344556677" + ); - PacketResponseNG resp; - uint8_t req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen; - uint8_t fast = 1; - uint8_t reply = 1; - uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + void *argtable[6 + 1] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_param_end; - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; - strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + 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); + + CLIParserFree(ctx); + + // sanity checks + if ((scan + unaddressed + uidlen) > 1) { + PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + return PM3_EINVARG; + } + + // default fallback to scan for tag. + if (unaddressed == false && 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, ISO15_CMD_SYSINFO}; + uint16_t reqlen = 2; + + 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 (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_SYSINFO) == false) - return PM3_SUCCESS; AddCrc15(req, reqlen); reqlen += 2; + uint8_t read_response = 1; + PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_response, req, reqlen); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(WARNING, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; @@ -1073,29 +954,30 @@ static int CmdHF15Sniff(const char *Cmd) { WaitForResponse(CMD_HF_ISO15693_SNIFF, &resp); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 15 list") "` to view captured tracelog"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save h") "` to save tracelog for later analysing"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } static int CmdHF15Reader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 reader", - "Act as a ISO15693 reader. Look for ISO15693 tags until Enter or the pm3 button is pressed\n", + "Act as a ISO-15693 reader. Look for ISO-15693 tags until Enter or the pm3 button is pressed\n", "hf 15 reader\n" - "hf 15 reader -1"); + "hf 15 reader -@ -> Continuous mode"); void *argtable[] = { arg_param_begin, - arg_lit0("1", "one", "read once"), + arg_lit0("@", NULL, "continuous reader mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool read_once = arg_get_lit(ctx, 1); + bool cm = arg_get_lit(ctx, 1); CLIParserFree(ctx); - PrintAndLogEx(INFO, "Starting ISO15 reader mode"); - PrintAndLogEx(INFO, "press " _YELLOW_("`enter`") " to cancel"); - readHF15Uid(!read_once, true); + if (cm) { + PrintAndLogEx(INFO, "press " _GREEN_("`Enter`") " to exit"); + } + readHF15Uid(cm, true); return PM3_SUCCESS; } @@ -1105,7 +987,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 sim", - "Simulate a ISO15693 tag\n", + "Simulate a ISO-15693 tag\n", "hf 15 sim -u E011223344556677"); void *argtable[] = { @@ -1144,74 +1026,126 @@ static int CmdHF15Sim(const char *Cmd) { static int CmdHF15FindAfi(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 findafi", - "This command attempts to brute force AFI of an ISO15693 tag\n", + "This command attempts to brute force AFI of an ISO-15693 tag\n" + "Estimated execution time is around 2 minutes", "hf 15 findafi"); void *argtable[] = { arg_param_begin, arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - PrintAndLogEx(SUCCESS, "press pm3-button to cancel"); - + PrintAndLogEx(INFO, "click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit"); clearCommandBuffer(); PacketResponseNG resp; SendCommandMIX(CMD_HF_ISO15693_FINDAFI, strtol(Cmd, NULL, 0), 0, 0, NULL, 0); uint32_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - timeout++; + for (;;) { + + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + msleep(300); + break; + } + + if (WaitForResponseTimeout(CMD_HF_ISO15693_FINDAFI, &resp, 2000)) { + if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(DEBUG, "Button pressed, user aborted"); + } + break; + } // should be done in about 2 minutes if (timeout > 180) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); - DropField(); - return PM3_ETIMEOUT; + break; } + timeout++; } DropField(); - return resp.status; // PM3_EOPABORTED or PM3_SUCCESS + PrintAndLogEx(INFO, "Done"); + return PM3_SUCCESS; } // Writes the AFI (Application Family Identifier) of a card static int CmdHF15WriteAfi(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 writeafi", + "Write AFI on card", + "hf 15 writeafi -* --afi 12\n" + "hf 15 writeafi -u E011223344556677 --afi 12" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_writeafi(); + 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; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + 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); + + int afi = arg_get_int_def(ctx, 6, 0); + CLIParserFree(ctx); + + // sanity checks + if ((scan + unaddressed + uidlen) > 1) { + PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + 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, ISO15_CMD_WRITEAFI}; + 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)); + } + + // 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 req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen = 0; - uint8_t fast = 1; - uint8_t reply = 1; - - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; - strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - - if (!prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_WRITEAFI)) - return PM3_SUCCESS; - - req[0] |= ISO15_REQ_OPTION; // Since we are writing - - int afinum = strtol(cmd, NULL, 0); - - req[reqlen++] = (uint8_t)afinum; - - AddCrc15(req, reqlen); - reqlen += 2; + uint8_t read_respone = 1; PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_respone, req, reqlen); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(ERR, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; @@ -1231,48 +1165,86 @@ static int CmdHF15WriteAfi(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Wrote AFI 0x%02X", afinum); + PrintAndLogEx(SUCCESS, "Wrote AFI 0x%02X", afi); return PM3_SUCCESS; } // Writes the DSFID (Data Storage Format Identifier) of a card static int CmdHF15WriteDsfid(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 writedsfid", + "Write DSFID on card", + "hf 15 writedsfid -* --dsfid 12\n" + "hf 15 writedsfid -u E011223344556677 --dsfid 12" + ); + + void *argtable[6 + 2] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_int1(NULL, "dsfid", "", "DSFID number (0-255)"); + argtable[arglen++] = arg_param_end; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + 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); + + int dsfid = arg_get_int_def(ctx, 6, 0); + CLIParserFree(ctx); + + // sanity checks + if ((scan + unaddressed + uidlen) > 1) { + PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + 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, ISO15_CMD_WRITEDSFID}; + // enforce, since we are writing + req[0] |= ISO15_REQ_OPTION; + 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)); + } + + // dsfid + req[reqlen++] = (uint8_t)dsfid; + + AddCrc15(req, reqlen); + reqlen += 2; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_writedsfid(); // 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 req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen = 0; - uint8_t fast = 1; - uint8_t reply = 1; - int dsfidnum; + uint8_t read_respone = 1; - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; - strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - - if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_WRITEDSFID) == false) - return PM3_SUCCESS; - - req[0] |= ISO15_REQ_OPTION; // Since we are writing - - dsfidnum = strtol(cmd, NULL, 0); - - req[reqlen++] = (uint8_t)dsfidnum; - - AddCrc15(req, reqlen); - reqlen += 2; - - // PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); + PrintAndLogEx(DEBUG, "cmd %s", sprint_hex(req, reqlen)); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_respone, req, reqlen); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(ERR, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; @@ -1292,78 +1264,99 @@ static int CmdHF15WriteDsfid(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Wrote DSFID 0x%02X", dsfidnum); + PrintAndLogEx(SUCCESS, "Wrote DSFID 0x%02X", dsfid); return PM3_SUCCESS; } // Reads all memory pages // need to write to file static int CmdHF15Dump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 dump", + "This command dumps the contents of a ISO-15693 tag and save it to file", + "hf 15 dump\n" + "hf 15 dump -*\n" + "hf 15 dump -u E011223344556677 -f hf-15-my-dump.bin" + ); - uint8_t fileNameLen = 0; + void *argtable[6 + 2] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_str0("f", "file", "", "filename of dump"), + argtable[arglen++] = arg_param_end; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + + 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); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char *fptr = filename; - bool errors = false; - uint8_t cmdp = 0; - uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_15_dump(); - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; + CLIParserFree(ctx); + + // sanity checks + if ((scan + unaddressed + uidlen) > 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[13] = {flags, ISO15_CMD_READ}; + uint16_t reqlen = 2; + + if (scan) { + if (getUID(false, uid) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "no tag found"); + return PM3_EINVARG; } + uidlen = 8; } - //Validations - if (errors) return usage_15_dump(); - - if (getUID(false, uid) == false) { - PrintAndLogEx(WARNING, "No tag found."); - return PM3_ESOFT; + 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 (fileNameLen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - fptr += sprintf(fptr, "hf-15-"); - FillFileNameByUID(fptr, SwapEndian64(uid, sizeof(uid), 8), "-dump", sizeof(uid)); - } // detect blocksize from card :) PrintAndLogEx(SUCCESS, "Reading memory from tag UID " _YELLOW_("%s"), iso15693_sprintUID(NULL, uid)); int blocknum = 0; - uint8_t *recv = NULL; - // memory. t15memory_t mem[256]; uint8_t data[256 * 4] = {0}; memset(data, 0, sizeof(data)); - PacketResponseNG resp; - uint8_t req[13]; - req[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[1] = ISO15_CMD_READ; - - // copy uid to read command - memcpy(req + 2, uid, sizeof(uid)); - PrintAndLogEx(INFO, "." NOLF); for (int retry = 0; retry < 5; retry++) { req[10] = blocknum; AddCrc15(req, 11); + // 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, sizeof(req), 1, 1, req, sizeof(req)); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, sizeof(req), fast, read_respone, req, sizeof(req)); if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { @@ -1377,7 +1370,7 @@ static int CmdHF15Dump(const char *Cmd) { continue; } - recv = resp.data.asBytes; + uint8_t *recv = resp.data.asBytes; if (CheckCrc15(recv, len) == false) { PrintAndLogEx(NORMAL, ""); @@ -1409,10 +1402,30 @@ static int CmdHF15Dump(const char *Cmd) { PrintAndLogEx(INFO, "block# | data |lck| ascii"); PrintAndLogEx(INFO, "---------+--------------+---+----------"); for (int i = 0; i < blocknum; i++) { - PrintAndLogEx(INFO, "%3d/0x%02X | %s | %d | %s", i, i, sprint_hex(mem[i].block, 4), mem[i].lock, sprint_ascii(mem[i].block, 4)); + char lck[16] = {0}; + if (mem[i].lock) { + sprintf(lck, _RED_("%d"), mem[i].lock); + } else { + sprintf(lck, "%d", mem[i].lock); + } + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s" + , i + , i + , sprint_hex(mem[i].block, 4) + , lck + , sprint_ascii(mem[i].block, 4) + ); } PrintAndLogEx(NORMAL, ""); + // user supplied filename ? + if (strlen(filename) < 1) { + char *fptr = filename; + PrintAndLogEx(INFO, "Using UID as filename"); + fptr += snprintf(fptr, sizeof(filename), "hf-15-"); + FillFileNameByUID(fptr, SwapEndian64(uid, sizeof(uid), 8), "-dump", sizeof(uid)); + } + size_t datalen = blocknum * 4; saveFile(filename, ".bin", data, datalen); saveFileEML(filename, data, datalen, 4); @@ -1421,81 +1434,50 @@ static int CmdHF15Dump(const char *Cmd) { } static int CmdHF15List(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 15"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf 15", "15"); } static int CmdHF15Raw(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 raw", + "Sends raw bytes over ISO-15693 to card", + "hf 15 raw -c -d 260100 --> add crc\n" + "hf 15 raw -krc -d 260100 --> add crc, keep field on, skip response" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_raw(); - - int reply = 1, fast = 1, i = 0; - bool crc = false, keep_field_on = false; - char buf[5] = ""; - uint8_t data[100]; - uint32_t datalen = 0, temp; - - // strip - while (*Cmd == ' ' || *Cmd == '\t') Cmd++; - - while (Cmd[i] != '\0') { - if (Cmd[i] == ' ' || Cmd[i] == '\t') { i++; continue; } - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'r': - reply = 0; - break; - case '2': - fast = 0; - break; - case 'c': - crc = true; - break; - case 'k': - keep_field_on = true; - break; - default: - PrintAndLogEx(WARNING, "Invalid option"); - return PM3_EINVARG; - } - i += 2; - continue; - } - if ((Cmd[i] >= '0' && Cmd[i] <= '9') || - (Cmd[i] >= 'a' && Cmd[i] <= 'f') || - (Cmd[i] >= 'A' && Cmd[i] <= 'F')) { - buf[strlen(buf) + 1] = 0; - buf[strlen(buf)] = Cmd[i]; - i++; - - if (strlen(buf) >= 2) { - sscanf(buf, "%x", &temp); - data[datalen] = (uint8_t)(temp & 0xff); - datalen++; - *buf = 0; - } - continue; - } - PrintAndLogEx(WARNING, "Invalid char on input"); - return PM3_EINVARG; - } + void *argtable[] = { + arg_param_begin, + arg_lit0("2", NULL, "use slower '1 out of 256' mode"), + arg_lit0("c", "crc", "calculate and append CRC"), + arg_lit0("k", NULL, "keep signal field ON after receive"), + arg_lit0("r", NULL, "do not read response"), + arg_strx1("d", "data", "", "raw bytes to send"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fast = (arg_get_lit(ctx, 1) == false); + bool crc = arg_get_lit(ctx, 2); + bool keep_field_on = arg_get_lit(ctx, 3); + bool read_respone = (arg_get_lit(ctx, 4) == false); + int datalen = 0; + uint8_t data[300]; + CLIGetHexWithReturn(ctx, 5, data, &datalen); + CLIParserFree(ctx); if (crc) { AddCrc15(data, datalen); datalen += 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) PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, datalen, fast, reply, data, datalen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, datalen, fast, read_respone, data, datalen); - if (reply) { + if (read_respone) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { int len = resp.oldarg[0]; if (len == PM3_ETEAROFF) { @@ -1513,9 +1495,9 @@ static int CmdHF15Raw(const char *Cmd) { } } - if (keep_field_on == false) + if (keep_field_on == false) { DropField(); - + } return PM3_SUCCESS; } @@ -1524,48 +1506,85 @@ static int CmdHF15Raw(const char *Cmd) { * Read multiple blocks at once (not all tags support this) */ static int CmdHF15Readmulti(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 rdmulti", + "Read multiple pages on a ISO-15693 tag ", + "hf 15 rdmulti -* -b 1 --cnt 6 -> read 6 blocks\n" + "hf 15 rdmulti -u E011223344556677 -b 12 --cnt 3 -> read three blocks" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_readmulti(); + void *argtable[6 + 3] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_int1("b", NULL, "", "first page number (0-255)"); + argtable[arglen++] = arg_int1(NULL, "cnt", "", "number of pages (1-6)"); + argtable[arglen++] = arg_param_end; - uint8_t req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen = 0; - uint8_t fast = 1; - uint8_t reply = 1; + CLIExecWithReturn(ctx, Cmd, argtable, false); - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; - strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); + 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); - if (!prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_READMULTI)) - return PM3_SUCCESS; + int block = arg_get_int_def(ctx, 6, 0); + int blockcnt = arg_get_int_def(ctx, 7, 0); - // add OPTION flag, in order to get lock-info - req[0] |= ISO15_REQ_OPTION; + CLIParserFree(ctx); - // decimal - uint8_t pagenum = param_get8ex(cmd, 0, 0, 10); - uint8_t pagecount = param_get8ex(cmd, 1, 0, 10); - - if (pagecount > 6) { - PrintAndLogEx(WARNING, "Page count must be 6 or less (%d)", pagecount); + // sanity checks + if (blockcnt > 6) { + PrintAndLogEx(WARNING, "Page count must be 6 or less (%d)", blockcnt); return PM3_EINVARG; } + if ((scan + unaddressed + uidlen) > 1) { + PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + 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[PM3_CMD_DATA_SIZE] = {flags, ISO15_CMD_READMULTI}; + 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)); + } + // add OPTION flag, in order to get lock-info + req[0] |= ISO15_REQ_OPTION; + // 0 means 1 page, // 1 means 2 pages, ... - if (pagecount > 0) pagecount--; + if (blockcnt > 0) blockcnt--; + + req[reqlen++] = block; + req[reqlen++] = blockcnt; - req[reqlen++] = pagenum; - req[reqlen++] = pagecount; AddCrc15(req, reqlen); reqlen += 2; + uint8_t read_respone = 1; PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_respone, req, reqlen); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(FAILED, "iso15693 card timeout"); DropField(); return PM3_ETIMEOUT; @@ -1597,19 +1616,23 @@ static int CmdHF15Readmulti(const char *Cmd) { // skip status byte int start = 1; - int stop = (pagecount + 1) * 5; - int currblock = pagenum; - + int stop = (blockcnt + 1) * 5; + int currblock = block; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "block# | data |lck| ascii"); + PrintAndLogEx(INFO, " # | data |lck| ascii"); PrintAndLogEx(INFO, "---------+--------------+---+----------"); for (int i = start; i < stop; i += 5) { - PrintAndLogEx(INFO, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(data + i + 1, 4), data[i], sprint_ascii(data + i + 1, 4)); + char lck[16] = {0}; + if (data[i]) { + sprintf(lck, _RED_("%d"), data[i]); + } else { + sprintf(lck, "%d", data[i]); + } + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s", currblock, currblock, sprint_hex(data + i + 1, 4), lck, sprint_ascii(data + i + 1, 4)); currblock++; } - PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -1618,42 +1641,83 @@ static int CmdHF15Readmulti(const char *Cmd) { * Commandline handling: HF15 CMD READ * Reads a single Block */ -static int CmdHF15Read(const char *Cmd) { +static int CmdHF15Readblock(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 rdbl", + "Read page on ISO-15693 tag", + "hf 15 rdbl -* -b 12\n" + "hf 15 rdbl -u E011223344556677 -b 12" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_read(); + void *argtable[6 + 2] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_int1("b", "blk", "", "page number (0-255)"); + argtable[arglen++] = arg_param_end; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + 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); + + int block = arg_get_int_def(ctx, 6, 0); + CLIParserFree(ctx); + + // sanity checks + if ((scan + unaddressed + uidlen) > 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, ISO15_CMD_READ}; + 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)); + } + // add OPTION flag, in order to get lock-info + req[0] |= ISO15_REQ_OPTION; + + req[reqlen++] = (uint8_t)block; + + 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 req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen = 0; - uint8_t fast = 1; - uint8_t reply = 1; - int blocknum; - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; - strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - - if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_READ) == false) - return PM3_SUCCESS; - - // add OPTION flag, in order to get lock-info - req[0] |= ISO15_REQ_OPTION; - - blocknum = strtol(cmd, NULL, 0); - - req[reqlen++] = (uint8_t)blocknum; - - AddCrc15(req, reqlen); - reqlen += 2; - + uint8_t read_respone = 1; PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_respone, req, reqlen); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(ERR, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; @@ -1683,206 +1747,305 @@ static int CmdHF15Read(const char *Cmd) { } // print response + char lck[16] = {0}; + if (data[1]) { + sprintf(lck, _RED_("%d"), data[1]); + } else { + sprintf(lck, "%d", data[1]); + } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "block #%3d |lck| ascii", blocknum); + PrintAndLogEx(INFO, " #%3d |lck| ascii", block); PrintAndLogEx(INFO, "------------+---+------"); - PrintAndLogEx(INFO, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4)); + PrintAndLogEx(INFO, "%s| %s | %s", sprint_hex(data + 2, status - 4), lck, sprint_ascii(data + 2, status - 4)); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } +static int hf_15_write_blk(bool verbose, bool fast, uint8_t *req, uint8_t reqlen) { + + uint8_t read_response = 1; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, read_response, req, reqlen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(FAILED, "iso15693 card timeout, data may be written anyway"); + DropField(); + return PM3_ETIMEOUT; + } + + DropField(); + int status = resp.oldarg[0]; + if (status == PM3_ETEAROFF) { + return status; + } + + if (status < 2) { + if (verbose) { + PrintAndLogEx(FAILED, "iso15693 command failed"); + } + return PM3_EWRONGANSWER; + } + + uint8_t *recv = resp.data.asBytes; + if (CheckCrc15(recv, status) == false) { + if (verbose) { + PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); + } + return PM3_ESOFT; + } + + if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + if (verbose) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + } + return PM3_EWRONGANSWER; + } + return PM3_SUCCESS; +} + /** * Commandline handling: HF15 CMD WRITE * Writes a single Block - might run into timeout, even when successful */ static int CmdHF15Write(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 wrbl", + "Write block on ISO-15693 tag", + "hf 15 wrbl -* -b 12 -d AABBCCDD\n" + "hf 15 wrbl -u E011223344556677 -b 12 -d AABBCCDD" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_write(); + void *argtable[6 + 4] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_int1("b", "blk", "", "page number (0-255)"); + argtable[arglen++] = arg_str1("d", "data", "", "data, 4 bytes"); + argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); + argtable[arglen++] = arg_param_end; + CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen = 0; - uint8_t fast = 1; - uint8_t reply = 1; - int pagenum, temp; + 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); - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; - char *cmd2; + int block = arg_get_int_def(ctx, 6, 0); + uint8_t d[4]; + int dlen = 0; + CLIGetHexWithReturn(ctx, 7, d, &dlen); - strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); + bool verbose = arg_get_lit(ctx, 8); + CLIParserFree(ctx); - if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_WRITE) == false) - return PM3_SUCCESS; - - // *cmd -> page num ; *cmd2 -> data - cmd2 = cmd; - while (*cmd2 != ' ' && *cmd2 != '\t' && *cmd2) cmd2++; - *cmd2 = 0; - cmd2++; - - pagenum = strtol(cmd, NULL, 0); - - req[reqlen++] = (uint8_t)pagenum; - - while (cmd2[0] && cmd2[1]) { // hexdata, read by 2 hexchars - if (*cmd2 == ' ') { - cmd2++; - continue; - } - sscanf((char[]) {cmd2[0], cmd2[1], 0}, "%X", &temp); - req[reqlen++] = temp & 0xff; - cmd2 += 2; + // sanity checks + if ((scan + unaddressed + uidlen) > 1) { + PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + return PM3_EINVARG; } + + if (dlen != 4) { + PrintAndLogEx(WARNING, "expected data, 4 bytes, got %d", dlen); + 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[17] = {flags, ISO15_CMD_WRITE}; + + // enforce, since we are writing + req[0] |= ISO15_REQ_OPTION; + 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)); + } + + + req[reqlen++] = (uint8_t)block; + memcpy(req + reqlen, d, sizeof(d)); + reqlen += sizeof(d); + AddCrc15(req, reqlen); reqlen += 2; - PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data [ %s ] ", pagenum, pagenum, sprint_hex(req, reqlen)); + PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data [ %s ] ", block, block, sprint_hex(req, reqlen)); - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + int res = hf_15_write_blk(verbose, fast, req, reqlen); + if (res == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + else + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(FAILED, "iso15693 card timeout, data may be written anyway"); - DropField(); - return PM3_ETIMEOUT; - } - - DropField(); - - int status = resp.oldarg[0]; - if (status == PM3_ETEAROFF) { - return status; - } - - if (status < 2) { - PrintAndLogEx(FAILED, "iso15693 command failed"); - return PM3_EWRONGANSWER; - } - - uint8_t *data = resp.data.asBytes; - - if (CheckCrc15(data, status) == false) { - PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); - return PM3_ESOFT; - } - - 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; - } - - PrintAndLogEx(SUCCESS, "Write " _GREEN_("OK")); return PM3_SUCCESS; } static int CmdHF15Restore(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 restore", + "This command restore the contents of a dump file onto a ISO-15693 tag", + "hf 15 restore\n" + "hf 15 restore -*\n" + "hf 15 restore -u E011223344556677 -f hf-15-my-dump.bin" + ); - char newPrefix[60] = {0x00}; - char filename[FILE_PATH_SIZE] = {0x00}; - size_t blocksize = 4; - uint8_t cmdp = 0, retries = 3; - bool addressed_mode = false; + void *argtable[6 + 5] = {}; + uint8_t arglen = arg_add_default(argtable); + argtable[arglen++] = arg_str0("f", "file", "", "filename of dump"), + argtable[arglen++] = arg_int0("r", "retry", "", "number of retries (def 3)"), + argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"), + argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); + argtable[arglen++] = arg_param_end; + CLIExecWithReturn(ctx, Cmd, argtable, true); - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case '-': { - char param[3] = ""; - param_getstr(Cmd, cmdp, param, sizeof(param)); - switch (param[1]) { - case '2': - case 'o': - sprintf(newPrefix, " %s", param); - break; - default: - PrintAndLogEx(WARNING, "11 unknown parameter " _YELLOW_("'%s'"), param); - return usage_15_restore(); - } - break; - } - case 'f': - param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - cmdp++; - break; - case 'r': - retries = param_get8ex(Cmd, cmdp + 1, 3, 10); - cmdp++; - break; - case 'b': - blocksize = param_get8ex(Cmd, cmdp + 1, 4, 10); - cmdp++; - break; - case 'a': - addressed_mode = true; - break; - case 'h': - return usage_15_restore(); - default: - PrintAndLogEx(WARNING, "unknown parameter " _YELLOW_("'%c'"), param_getchar(Cmd, cmdp)); - return usage_15_restore(); - } - cmdp++; + 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); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + int retries = arg_get_int_def(ctx, 7, 3); + int blocksize = arg_get_int_def(ctx, 8, 4); + bool verbose = arg_get_lit(ctx, 9); + CLIParserFree(ctx); + + // sanity checks + if ((scan + unaddressed + uidlen) > 1) { + PrintAndLogEx(WARNING, "Select only one option /scan/unaddress/uid"); + return PM3_EINVARG; } - - PrintAndLogEx(INFO, "blocksize: %zu", blocksize); - - if (!strlen(filename)) { + if (fnlen == 0) { PrintAndLogEx(WARNING, "please provide a filename"); - return usage_15_restore(); + return PM3_EINVARG; } - uint8_t uid[8] = {0x00}; - if (getUID(false, uid) == false) { - PrintAndLogEx(WARNING, "no tag found"); - return PM3_ESOFT; + // 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[17] = {flags, ISO15_CMD_WRITE}; + // enforce, since we are writing + req[0] |= ISO15_REQ_OPTION; + 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)); + } else { + PrintAndLogEx(SUCCESS, "Using unaddressed mode"); + } + PrintAndLogEx(INFO, "Using block size... %d", blocksize); + + // 4bytes * 256 blocks. Should be enough.. + uint8_t *data = calloc(4 * 256, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; } size_t datalen = 0; - uint8_t *data = NULL; - if (loadFile_safe(filename, ".bin", (void **)&data, &datalen) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "could not find file " _YELLOW_("%s"), filename); + int res = PM3_SUCCESS; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&data, &datalen); + break; + } + case JSON: { + res = loadFileJSON(filename, data, 256 * 4, &datalen, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + free(data); + return PM3_EINVARG; + } + } + + if (res != PM3_SUCCESS) { + free(data); return PM3_EFILE; } if ((datalen % blocksize) != 0) { - PrintAndLogEx(WARNING, "datalen %zu isn't dividable with blocksize %zu", datalen, blocksize); + PrintAndLogEx(WARNING, "datalen %zu isn't dividable with blocksize %d", datalen, blocksize); free(data); return PM3_ESOFT; } PrintAndLogEx(INFO, "restoring data blocks"); + PrintAndLogEx(INFO, "." NOLF); + fflush(stdout); int retval = PM3_SUCCESS; size_t bytes = 0; uint16_t i = 0; while (bytes < datalen) { + req[reqlen + 1] = i; + // copy over the data to the request + memcpy(req + reqlen + 1, data + bytes, blocksize); + AddCrc15(req, reqlen + 1 + blocksize); + uint8_t tried = 0; - char hex[40] = {0x00}; - char tmpCmd[200] = {0x00}; - - if (addressed_mode) { - char uidhex[17] = {0x00}; - hex_to_buffer((uint8_t *)uidhex, uid, sizeof(uid), sizeof(uidhex) - 1, 0, false, true); - hex_to_buffer((uint8_t *)hex, data + bytes, blocksize, sizeof(hex) - 1, 0, false, true); - snprintf(tmpCmd, sizeof(tmpCmd), "%s %s %u %s", newPrefix, uidhex, i, hex); - } else { - hex_to_buffer((uint8_t *)hex, data + bytes, blocksize, sizeof(hex) - 1, 0, false, true); - snprintf(tmpCmd, sizeof(tmpCmd), "%s u %u %s", newPrefix, i, hex); - } - - PrintAndLogEx(DEBUG, "hf 15 write %s", tmpCmd); - for (tried = 0; tried < retries; tried++) { - retval = CmdHF15Write(tmpCmd); - if (retval == false) { + + retval = hf_15_write_blk(verbose, fast, req, (reqlen + 1 + blocksize + 2)); + if (retval == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); break; } } if (tried >= retries) { free(data); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "restore failed. Too many retries."); return retval; } @@ -1890,6 +2053,8 @@ static int CmdHF15Restore(const char *Cmd) { i++; } free(data); + + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "done"); PrintAndLogEx(HINT, "try `" _YELLOW_("hf 15 dump") "` to read your card to verify"); return PM3_SUCCESS; @@ -1935,8 +2100,8 @@ static int CmdHF15CSetUID(const char *Cmd) { PrintAndLogEx(INFO, "getting current card details..."); uint8_t carduid[8] = {0x00}; - if (getUID(false, carduid) == false) { - PrintAndLogEx(FAILED, "can't read card UID"); + if (getUID(false, carduid) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "no tag found"); return PM3_ESOFT; } @@ -1953,8 +2118,8 @@ static int CmdHF15CSetUID(const char *Cmd) { PrintAndLogEx(INFO, "getting updated card details..."); - if (getUID(false, carduid) == false) { - PrintAndLogEx(FAILED, "can't read card UID"); + if (getUID(false, carduid) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "no tag found"); return PM3_ESOFT; } @@ -1978,25 +2143,25 @@ static int CmdHF15CSetUID(const char *Cmd) { static command_t CommandTable[] = { {"-----------", CmdHF15Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHF15Help, AlwaysAvailable, "This help"}, - {"list", CmdHF15List, AlwaysAvailable, "List ISO15693 history"}, - {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO15693 from tag"}, - {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO15693 tag, save to file"}, + {"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 ISO15693 traffic"}, + {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO-15693 traffic"}, {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, - {"rdbl", CmdHF15Read, IfPm3Iso15693, "Read a block"}, - {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO15693 reader"}, - {"readmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple Blocks"}, - {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO15693 tag"}, - {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire Samples as Reader (enables carrier, sends inquiry)"}, - {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO15693 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"}, {"wrbl", CmdHF15Write, IfPm3Iso15693, "Write a block"}, {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("afi") " -----------------------"}, - {"findafi", CmdHF15FindAfi, IfPm3Iso15693, "Brute force AFI of an ISO15693 tag"}, - {"writeafi", CmdHF15WriteAfi, IfPm3Iso15693, "Writes the AFI on an ISO15693 tag"}, - {"writedsfid", CmdHF15WriteDsfid, IfPm3Iso15693, "Writes the DSFID on an ISO15693 tag"}, + {"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 Chinese card"}, + {"csetuid", CmdHF15CSetUID, IfPm3Iso15693, "Set UID for magic card"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index bf2df530c..4b1155870 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -18,94 +18,21 @@ #include "cmdtrace.h" #include "crc16.h" #include "cmdhf14a.h" -#include "protocols.h" // definitions of ISO14B protocol +#include "protocols.h" // definitions of ISO14B protocol #include "iso14b.h" +#include "cliparser.h" // cliparsing #define TIMEOUT 2000 + +#ifndef CRYPTORF_MEM_SIZE +# define CRYPTORF_MEM_SIZE 1024 +#endif + static int CmdHelp(const char *Cmd); -static int usage_hf_cryptorf_info(void) { - PrintAndLogEx(NORMAL, "Usage: hf cryptorf info [h] [v]\n" - "Options:\n" - " h this help\n" - " v verbose\n" - "\n" - "Example:\n" - _YELLOW_(" hf cryptorf info") - ); - return PM3_SUCCESS; -} -static int usage_hf_cryptorf_reader(void) { - PrintAndLogEx(NORMAL, "Usage: hf cryptorf reader [h] [v]\n" - "Options:\n" - " h this help\n" - " v verbose\n" - "\n" - "Example:\n" - _YELLOW_(" hf cryptorf reader") - ); - return PM3_SUCCESS; -} -static int usage_hf_cryptorf_sniff(void) { - PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer\n" - "Buffer accessible from command " _YELLOW_("'hf list cryptorf'") "\n" - "Usage: hf cryptorf sniff [h]\n" - "Options:\n" - " h this help\n" - "\n" - "Example:\n" - _YELLOW_(" hf cryptorf sniff") - ); - return PM3_SUCCESS; -} -static int usage_hf_cryptorf_sim(void) { - PrintAndLogEx(NORMAL, "Emulating CryptoRF tag with emulator memory\n" - "Usage: hf cryptorf sim [h] \n" - "Options:\n" - " h this help\n" - "\n" - "Example:\n" - _YELLOW_(" hf cryptorf sim") - ); - return PM3_SUCCESS; -} -static int usage_hf_cryptorf_dump(void) { - PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-14443-B tag and save it to file\n" - "\n" - "Usage: hf cryptorf dump [h] [card memory] \n" - "Options:\n" - " h this help\n" - " f filename, if no UID will be used as filename\n" - "\n" - "Examples:\n" - "\thf cryptorf dump\n" - "\thf cryptorf dump f mydump"); - return PM3_SUCCESS; -} -static int usage_hf_cryptorf_eload(void) { - PrintAndLogEx(NORMAL, "It loads a binary dump into emulator memory\n" - "Usage: hf cryptorf eload [f ]\n" - "Options:\n" - " h this help\n" - " f filename, if no UID will be used as filename\n" - "\n" - "Examples:\n" - _YELLOW_(" hf cryptorf eload f filename") - ); - return PM3_SUCCESS; -} -static int usage_hf_cryptorf_esave(void) { - PrintAndLogEx(NORMAL, "It saves bin/eml/json dump file of emulator memory\n" - " Usage: hf cryptorf esave [f ]\n" - "Options:\n" - " h this help\n" - " f filename, if no UID will be used as filename\n" - "\n" - "Examples:\n" - _YELLOW_(" hf cryptorf esave ") - _YELLOW_(" hf cryptorf esave f filename") - ); - return PM3_SUCCESS; +static iso14b_card_select_t last_known_card; +static void set_last_known_card(iso14b_card_select_t card) { + last_known_card = card; } static int switch_off_field_cryptorf(void) { @@ -115,18 +42,21 @@ static int switch_off_field_cryptorf(void) { } static int CmdHFCryptoRFList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t cryptorf"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf cryptorf", "cryptorf"); } static int CmdHFCryptoRFSim(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_cryptorf_sim(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf sim", + "Simulate a CryptoRF tag", + "hf cryptorf sim"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIParserFree(ctx); clearCommandBuffer(); SendCommandMIX(CMD_HF_CRYPTORF_SIM, 0, 0, 0, NULL, 0); @@ -134,12 +64,24 @@ static int CmdHFCryptoRFSim(const char *Cmd) { } static int CmdHFCryptoRFSniff(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf sniff", + "Sniff the communication reader and tag", + "hf cryptorf sniff\n" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_cryptorf_sniff(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_SNIFF, NULL, 0); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf cryptorf list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -f hf_cryptorf_mytrace") "` to save tracelog for later analysing"); return PM3_SUCCESS; } @@ -172,343 +114,8 @@ static bool get_14b_UID(iso14b_card_select_t *card) { return false; } -static int CmdHFCryptoRFInfo(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_cryptorf_info(); - - bool verbose = (cmdp == 'v'); - - int res = infoHFCryptoRF(verbose); - if (res != PM3_SUCCESS && verbose) { - PrintAndLogEx(FAILED, "no 14443-B tag found"); - } - return res; -} - -static int CmdHFCryptoRFReader(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_cryptorf_reader(); - - bool verbose = (cmdp == 'v'); - - int res = readHFCryptoRF(verbose); - if (res != PM3_SUCCESS && verbose) { - PrintAndLogEx(FAILED, "no 14443-B tag found"); - } - return res; -} - -// need to write to file -static int CmdHFCryptoRFDump(const char *Cmd) { - - uint8_t fileNameLen = 0; - char filename[FILE_PATH_SIZE] = {0}; - char *fptr = filename; - bool errors = false; - uint8_t cmdp = 0, cardtype = 1; - uint16_t cardsize = 0; - uint8_t blocks = 0; - iso14b_card_select_t card; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_cryptorf_dump(); - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - if (cmdp == 0) { - cardtype = param_get8ex(Cmd, cmdp, 1, 10); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - } - - //Validations - if (errors) return usage_hf_cryptorf_dump(); - - switch (cardtype) { - case 2: - cardsize = (512 / 8) + 4; - blocks = 0x0F; - break; - case 1: - default: - cardsize = (4096 / 8) + 4; - blocks = 0x7F; - break; - } - - if (!get_14b_UID(&card)) { - PrintAndLogEx(WARNING, "No tag found."); - return PM3_SUCCESS; - } - - if (fileNameLen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - fptr += sprintf(fptr, "hf-cryptorf-"); - FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); - } - - // detect blocksize from card :) - PrintAndLogEx(NORMAL, "Reading memory from tag UID %s", sprint_hex(card.uid, card.uidlen)); - - uint8_t data[cardsize]; - memset(data, 0, sizeof(data)); - - int blocknum = 0; - uint8_t *recv = NULL; - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); - - //select - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - if (resp.oldarg[0]) { - PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); - goto out; - } - } - - uint8_t req[2] = {ISO14443B_READ_BLK}; - - for (int retry = 0; retry < 5; retry++) { - - req[1] = blocknum; - - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); - - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - - uint8_t status = resp.oldarg[0] & 0xFF; - if (status > 0) { - continue; - } - - uint16_t len = (resp.oldarg[1] & 0xFFFF); - recv = resp.data.asBytes; - - if (!check_crc(CRC_14443_B, recv, len)) { - PrintAndLogEx(FAILED, "crc fail, retrying one more time"); - continue; - } - - memcpy(data + (blocknum * 4), resp.data.asBytes, 4); - - if (blocknum == 0xFF) { - //last read. - break; - } - - - retry = 0; - blocknum++; - if (blocknum > blocks) { - // read config block - blocknum = 0xFF; - } - - printf("."); - fflush(stdout); - } - } - - if (blocknum != 0xFF) { - PrintAndLogEx(NORMAL, "\n Dump failed"); - goto out; - } - - PrintAndLogEx(NORMAL, "\n"); - PrintAndLogEx(NORMAL, "block# | data | ascii"); - PrintAndLogEx(NORMAL, "---------+--------------+----------"); - - for (int i = 0; i <= blocks; i++) { - PrintAndLogEx(NORMAL, - "%3d/0x%02X | %s | %s", - i, - i, - sprint_hex(data + (i * 4), 4), - sprint_ascii(data + (i * 4), 4) - ); - } - - PrintAndLogEx(NORMAL, "\n"); - - - size_t datalen = (blocks + 1) * 4; - saveFileEML(filename, data, datalen, 4); - saveFile(filename, ".bin", data, datalen); -out: - return switch_off_field_cryptorf(); -} - -static int CmdHFCryptoRFELoad(const char *Cmd) { - - size_t datalen = 1024; - char filename[FILE_PATH_SIZE] = {0x00}; - bool errors = false, has_filename = false; - uint8_t cmdp = 0; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h' : - return usage_hf_cryptorf_eload(); - case 'f' : - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - break; - } - has_filename = true; - cmdp += 2; - break; - default : - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (has_filename == false) - errors = true; - - //Validations - if (errors || strlen(Cmd) == 0) return usage_hf_cryptorf_eload(); - - // set up buffer - uint8_t *data = calloc(datalen, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; - } - - if (loadFile_safe(filename, ".bin", (void **)&data, &datalen) != PM3_SUCCESS) { - free(data); - PrintAndLogEx(WARNING, "Error, reading file"); - return PM3_EFILE; - } - - PrintAndLogEx(SUCCESS, "Uploading to emulator memory"); - - /* - // fast push mode - conn.block_after_ACK = true; - - //Send to device - uint32_t bytes_sent = 0; - uint32_t bytes_remaining = bytes_read; - - while (bytes_remaining > 0) { - uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); - if (bytes_in_packet == bytes_remaining) { - // Disable fast mode on last packet - conn.block_after_ACK = false; - } - clearCommandBuffer(); - SendCommandOLD(CMD_HF_CRYPTORF_EML_MEMSET, bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - } - */ - free(data); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Done"); - return PM3_SUCCESS; -} - -static int CmdHFCryptoRFESave(const char *Cmd) { - - char filename[FILE_PATH_SIZE] = {0}; - char *fptr = filename; - int fileNameLen = 0; - size_t numofbytes = 1024; - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h' : - return usage_hf_cryptorf_esave(); - case 'f' : - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (!fileNameLen) - errors = true; - if (fileNameLen > FILE_PATH_SIZE - 5) - fileNameLen = FILE_PATH_SIZE - 5; - cmdp += 2; - break; - default : - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - //Validations - if (errors || strlen(Cmd) == 0) return usage_hf_cryptorf_esave(); - - // set up buffer - uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; - } - - // download emulator memory - PrintAndLogEx(SUCCESS, "Reading emulator memory..."); - if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 0, NULL, 2500, false)) { - PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); - free(data); - return PM3_ETIMEOUT; - } - - // user supplied filename? - if (fileNameLen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - fptr += sprintf(fptr, "hf-cryptorf-"); - FillFileNameByUID(fptr, data, "-dump", 4); - } - - saveFile(filename, ".bin", data, numofbytes); - //needs to change - saveFileEML(filename, data, numofbytes, 8); - //needs to change - saveFileJSON(filename, jsfRaw, data, numofbytes, NULL); - return PM3_SUCCESS; -} - -static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"dump", CmdHFCryptoRFDump, IfPm3Iso14443b, "Read all memory pages of an CryptoRF tag, save to file"}, - {"info", CmdHFCryptoRFInfo, IfPm3Iso14443b, "Tag information"}, - {"list", CmdHFCryptoRFList, AlwaysAvailable, "List ISO 14443B history"}, - {"reader", CmdHFCryptoRFReader, IfPm3Iso14443b, "Act as a CryptoRF reader to identify a tag"}, - {"sim", CmdHFCryptoRFSim, IfPm3Iso14443b, "Fake CryptoRF tag"}, - {"sniff", CmdHFCryptoRFSniff, IfPm3Iso14443b, "Eavesdrop CryptoRF"}, - {"eload", CmdHFCryptoRFELoad, AlwaysAvailable, "Load binary dump to emulator memory"}, - {"esave", CmdHFCryptoRFESave, AlwaysAvailable, "Save emulator memory to binary file"}, - - {NULL, NULL, NULL, NULL} -}; - -static int CmdHelp(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdsHelp(CommandTable); - return PM3_SUCCESS; -} - -int CmdHFCryptoRF(const char *Cmd) { - clearCommandBuffer(); - return CmdsParse(CommandTable, Cmd); -} - // Print extented information about tag. -int infoHFCryptoRF(bool verbose) { +static int infoHFCryptoRF(bool verbose) { int res = PM3_ESOFT; @@ -550,43 +157,369 @@ int infoHFCryptoRF(bool verbose) { return res; } -// get and print general info cryptoRF -int readHFCryptoRF(bool verbose) { +static int CmdHFCryptoRFInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf info", + "Act as a CryptoRF reader.", + "hf cryptorf info"); - int res = PM3_ESOFT; - - // 14b get and print UID only (general info) - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); - PacketResponseNG resp; - - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); - return PM3_ETIMEOUT; - } - - iso14b_card_select_t card; - memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - - uint64_t status = resp.oldarg[0]; - - switch (status) { - case 0: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); - PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); - res = PM3_SUCCESS; - break; - case 2: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); - break; - case 3: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); - break; - default: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed"); - break; + 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); + int res = infoHFCryptoRF(verbose); + if (res != PM3_SUCCESS && verbose) { + PrintAndLogEx(FAILED, "no CryptoRF / ISO14443-B tag found"); } return res; } + +// get and print general info cryptoRF +int readHFCryptoRF(bool loop, bool verbose) { + + int res = PM3_ESOFT; + do { + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + uint8_t status = resp.oldarg[0] & 0xFF; + + if (loop) { + if (status != 0) { + continue; + } + } else { + // when not in continuous mode + if (status != 0) { + if (verbose) PrintAndLogEx(WARNING, "cryptoRF / ISO14443-b card select failed"); + res = PM3_EOPABORTED; + break; + } + } + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex_inrow(card.uid, card.uidlen)); + set_last_known_card(card); + } + } while (loop && kbd_enter_pressed() == false); + + DropField(); + return res; +} + +static int CmdHFCryptoRFReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf reader", + "Act as a cryptoRF reader. Look for cryptoRF tags until Enter or the pm3 button is pressed", + "hf cryptorf 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 readHFCryptoRF(cm, false); +} + +// need to write to file +static int CmdHFCryptoRFDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf dump", + "Dump all memory from a CryptoRF tag (512/4096 bit size)", + "hf cryptorf dump\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename to save dump to"), + arg_lit0(NULL, "64", "64byte / 512bit memory"), + arg_lit0(NULL, "512", "512byte / 4096bit memory"), + 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); + char *fnameptr = filename; + + bool m64 = arg_get_lit(ctx, 2); + bool m512 = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (m512 + m64 > 1) { + PrintAndLogEx(INFO, "Select only one card memory size"); + return PM3_EINVARG; + } + + uint16_t cardsize = 0; + uint8_t blocks = 0; + if (m64) { + cardsize = (512 / 8) + 4; + blocks = 0x0F; + } + if (m512) { + cardsize = (4096 / 8) + 4; + blocks = 0x7F; + } + + iso14b_card_select_t card; + if (get_14b_UID(&card) == false) { + PrintAndLogEx(WARNING, "No tag found."); + return PM3_SUCCESS; + } + + // detect blocksize from card :) + PrintAndLogEx(INFO, "Reading memory from tag UID " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + + uint8_t data[cardsize]; + memset(data, 0, sizeof(data)); + + int blocknum = 0; + uint8_t *recv = NULL; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); + + //select + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (resp.oldarg[0]) { + PrintAndLogEx(ERR, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); + goto out; + } + } + + uint8_t req[2] = {ISO14443B_READ_BLK}; + for (int retry = 0; retry < 5; retry++) { + + req[1] = blocknum; + + clearCommandBuffer(); + SendCommandOLD(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); + + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + uint8_t status = resp.oldarg[0] & 0xFF; + if (status > 0) { + continue; + } + + uint16_t len = (resp.oldarg[1] & 0xFFFF); + recv = resp.data.asBytes; + + if (!check_crc(CRC_14443_B, recv, len)) { + PrintAndLogEx(FAILED, "crc fail, retrying one more time"); + continue; + } + + memcpy(data + (blocknum * 4), resp.data.asBytes, 4); + + if (blocknum == 0xFF) { + //last read. + break; + } + + retry = 0; + blocknum++; + if (blocknum > blocks) { + // read config block + blocknum = 0xFF; + } + + printf("."); + fflush(stdout); + } + } + + if (blocknum != blocks) { + PrintAndLogEx(ERR, "\nDump failed"); + goto out; + } + + PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(INFO, "block# | data | ascii"); + PrintAndLogEx(INFO, "---------+--------------+----------"); + + for (int i = 0; i <= blocks; i++) { + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s", + i, + i, + sprint_hex(data + (i * 4), 4), + sprint_ascii(data + (i * 4), 4) + ); + } + + PrintAndLogEx(NORMAL, ""); + + size_t datalen = (blocks + 1) * 4; + + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + fnameptr += sprintf(fnameptr, "hf-cryptorf-"); + FillFileNameByUID(fnameptr, card.uid, "-dump", card.uidlen); + } + + saveFileEML(filename, data, datalen, 4); + saveFile(filename, ".bin", data, datalen); + //json +out: + return switch_off_field_cryptorf(); +} + +static int CmdHFCryptoRFELoad(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf eload", + "Loads CryptoRF tag dump into emulator memory on device", + "hf cryptorf eload -f hf-cryptorf-0102030405-dump.bin\n" + ); + + 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] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + if (fnlen == 0) { + PrintAndLogEx(ERR, "Error: Please specify a filename"); + return PM3_EINVARG; + } + + size_t datalen = CRYPTORF_MEM_SIZE; + // set up buffer + uint8_t *data = calloc(datalen, sizeof(uint8_t)); + if (!data) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + if (loadFile_safe(filename, ".bin", (void **)&data, &datalen) != PM3_SUCCESS) { + free(data); + PrintAndLogEx(WARNING, "Error, reading file"); + return PM3_EFILE; + } + + PrintAndLogEx(SUCCESS, "Uploading to emulator memory"); + + uint32_t bytes_sent = 0; + /* + //Send to device + uint32_t bytes_remaining = bytes_read; + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); + if (bytes_in_packet == bytes_remaining) { + // Disable fast mode on last packet + conn.block_after_ACK = false; + } + clearCommandBuffer(); + SendCommandOLD(CMD_HF_CRYPTORF_EML_MEMSET, bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + } + */ + free(data); + PrintAndLogEx(SUCCESS, "sent %d bytes of data to device emulator memory", bytes_sent); + return PM3_SUCCESS; +} + +static int CmdHFCryptoRFESave(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cryptorf esave", + "Save emulator memory to bin/eml/json file\n" + "if filename is not supplied, UID will be used.", + "hf cryptorf esave\n" + "hf cryptorf esave -f filename" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dumpfile"), + 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); + char *fnameptr = filename; + CLIParserFree(ctx); + + size_t numofbytes = CRYPTORF_MEM_SIZE; + + // set up buffer + uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); + if (!data) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + // download emulator memory + PrintAndLogEx(SUCCESS, "Reading emulator memory..."); + if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(data); + return PM3_ETIMEOUT; + } + + // user supplied filename? + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + fnameptr += sprintf(fnameptr, "hf-cryptorf-"); + FillFileNameByUID(fnameptr, data, "-dump", 4); + } + + saveFile(filename, ".bin", data, numofbytes); + //needs to change + saveFileEML(filename, data, numofbytes, 8); + //needs to change + saveFileJSON(filename, jsfRaw, data, numofbytes, NULL); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"dump", CmdHFCryptoRFDump, IfPm3Iso14443b, "Read all memory pages of an CryptoRF tag, save to file"}, + {"info", CmdHFCryptoRFInfo, IfPm3Iso14443b, "Tag information"}, + {"list", CmdHFCryptoRFList, AlwaysAvailable, "List ISO 14443B history"}, + {"reader", CmdHFCryptoRFReader, IfPm3Iso14443b, "Act as a CryptoRF reader to identify a tag"}, + {"sim", CmdHFCryptoRFSim, IfPm3Iso14443b, "Fake CryptoRF tag"}, + {"sniff", CmdHFCryptoRFSniff, IfPm3Iso14443b, "Eavesdrop CryptoRF"}, + {"eload", CmdHFCryptoRFELoad, AlwaysAvailable, "Load binary dump to emulator memory"}, + {"esave", CmdHFCryptoRFESave, AlwaysAvailable, "Save emulator memory to binary file"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFCryptoRF(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} + diff --git a/client/src/cmdhfcryptorf.h b/client/src/cmdhfcryptorf.h index 82508e415..544532d20 100644 --- a/client/src/cmdhfcryptorf.h +++ b/client/src/cmdhfcryptorf.h @@ -14,7 +14,5 @@ #include "common.h" int CmdHFCryptoRF(const char *Cmd); - -int infoHFCryptoRF(bool verbose); -int readHFCryptoRF(bool verbose); +int readHFCryptoRF(bool loop, bool verbose); #endif diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index feb8806e7..df4cb7273 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -262,13 +262,22 @@ static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD return datainlen; } else if (lenfield == 0x81) { - return ((int) * (datain + offset + 1)); + int tmp = (*(datain + offset + 1) << 8); + return tmp; + //return ((int) * (datain + offset + 1)); } else if (lenfield == 0x82) { - return ((int) * (datain + offset + 1) << 8) | ((int) * (datain + offset + 2)); + int tmp = (*(datain + offset + 1) << 8); + tmp |= *(datain + offset + 2); + return tmp; + //return ((int) * (datain + offset + 1) << 8) | ((int) * (datain + offset + 2)); } else if (lenfield == 0x83) { - return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3)); + int tmp = (*(datain + offset + 1) << 16); + tmp |= (*(datain + offset + 2) << 8); + tmp |= *(datain + offset + 3); + return tmp; + //return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3)); } - return false; + return 0; } static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offset) { @@ -284,7 +293,7 @@ static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offse } else if (lenfield == 0x83) { return 4; } - return false; + return 0; } static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { @@ -839,12 +848,13 @@ static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const c if (fieldlen + 1 > EMRTD_MAX_FILE_SIZE) { PrintAndLogEx(ERR, "error (emrtd_dump_ef_sod) fieldlen out-of-bounds"); - return PM3_SUCCESS; + return PM3_EOUTOFBOUND; } char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) return PM3_EMALLOC; + strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_SOD].filename); @@ -1177,11 +1187,12 @@ static void emrtd_print_legal_sex(char *legal_sex) { static int emrtd_mrz_determine_length(char *mrz, int offset, int max_length) { int i; - for (i = max_length; i >= 0; i--) { - if (mrz[offset + i - 1] != '<') { + for (i = max_length - 1; i >= 0; i--) { + if (mrz[offset + i] != '<') { break; } } + // if not found, it will return -1 return i; } @@ -1206,6 +1217,9 @@ static void emrtd_mrz_replace_pad(char *data, int datalen, char newchar) { static void emrtd_print_optional_elements(char *mrz, int offset, int length, bool verify_check_digit) { int i = emrtd_mrz_determine_length(mrz, offset, length); + if (i == -1) { + return; + } // Only print optional elements if they're available if (i != 0) { @@ -1219,6 +1233,9 @@ static void emrtd_print_optional_elements(char *mrz, int offset, int length, boo static void emrtd_print_document_number(char *mrz, int offset) { int i = emrtd_mrz_determine_length(mrz, offset, 9); + if (i == -1) { + return; + } PrintAndLogEx(SUCCESS, "Document Number.......: " _YELLOW_("%.*s"), i, mrz + offset); @@ -1230,6 +1247,9 @@ static void emrtd_print_document_number(char *mrz, int offset) { static void emrtd_print_name(char *mrz, int offset, int max_length, bool localized) { char final_name[100] = { 0x00 }; int namelen = emrtd_mrz_determine_length(mrz, offset, max_length); + if (namelen == -1) { + return; + } int sep = emrtd_mrz_determine_separator(mrz, offset, namelen); // Account for mononyms @@ -1482,37 +1502,37 @@ static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { emrtd_print_name((char *) tagdata, 0, tagdatalen, false); break; case 0x10: - PrintAndLogEx(SUCCESS, "Personal Number.......: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Personal Number.......: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x11: // TODO: acc for < separation - PrintAndLogEx(SUCCESS, "Place of Birth........: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Place of Birth........: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x42: // TODO: acc for < separation - PrintAndLogEx(SUCCESS, "Permanent Address.....: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Permanent Address.....: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x12: - PrintAndLogEx(SUCCESS, "Telephone.............: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Telephone.............: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x13: - PrintAndLogEx(SUCCESS, "Profession............: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Profession............: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x14: - PrintAndLogEx(SUCCESS, "Title.................: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Title.................: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x15: - PrintAndLogEx(SUCCESS, "Personal Summary......: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Personal Summary......: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x16: saveFile("ProofOfCitizenship", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen); break; case 0x17: // TODO: acc for < separation - PrintAndLogEx(SUCCESS, "Other valid TDs nums..: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Other valid TDs nums..: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x18: - PrintAndLogEx(SUCCESS, "Custody Information...: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Custody Information...: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x2b: emrtd_print_dob((char *) tagdata, 0, true, tagdatalen != 4); @@ -1557,16 +1577,16 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { // ...and I doubt many states are using them. switch (taglist[i + 1]) { case 0x19: - PrintAndLogEx(SUCCESS, "Issuing Authority.....: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Issuing Authority.....: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x26: emrtd_print_issuance((char *) tagdata, tagdatalen != 4); break; case 0x1b: - PrintAndLogEx(SUCCESS, "Endorsements & Observations: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Endorsements & Observations: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x1c: - PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x1d: saveFile("FrontOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen); @@ -1578,7 +1598,7 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { emrtd_print_personalization_timestamp(tagdata); break; case 0x56: - PrintAndLogEx(SUCCESS, "Serial of Personalization System: " _YELLOW_("%.*s"), tagdatalen, tagdata); + PrintAndLogEx(SUCCESS, "Serial of Personalization System: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x85: emrtd_print_unknown_timestamp_5f85(tagdata); @@ -1668,7 +1688,7 @@ static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hash PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name); // We're only interested in checking if the length matches to avoid memory shenanigans if (hashalg_table[hashi].descriptorlen != hashalgosetlen) { - PrintAndLogEx(DEBUG, "len mismatch: %i", hashalgosetlen); + PrintAndLogEx(DEBUG, "len mismatch: %zu", hashalgosetlen); continue; } @@ -2055,7 +2075,7 @@ static bool validate_date(uint8_t *data, int datalen) { return !(day <= 0 || day > 31 || month <= 0 || month > 12); } -static int cmd_hf_emrtd_dump(const char *Cmd) { +static int CmdHFeMRTDDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd dump", "Dump all files on an eMRTD", @@ -2147,7 +2167,7 @@ static int cmd_hf_emrtd_dump(const char *Cmd) { return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path); } -static int cmd_hf_emrtd_info(const char *Cmd) { +static int CmdHFeMRTDInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf emrtd info", "Display info about an eMRTD", @@ -2238,21 +2258,15 @@ static int cmd_hf_emrtd_info(const char *Cmd) { } } -static int cmd_hf_emrtd_list(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 7816"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); +static int CmdHFeMRTDList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf emrtd", "7816"); } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"dump", cmd_hf_emrtd_dump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, - {"info", cmd_hf_emrtd_info, AlwaysAvailable, "Display info about an eMRTD"}, - {"list", cmd_hf_emrtd_list, AlwaysAvailable, "List ISO 14443A/7816 history"}, + {"dump", CmdHFeMRTDDump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, + {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Display info about an eMRTD"}, + {"list", CmdHFeMRTDList, AlwaysAvailable, "List ISO 14443A/7816 history"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index b051f58c0..04ddd94c9 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2017 October, Satsuoni -// 2017 iceman +// 2017,2021 iceman // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. @@ -8,21 +8,22 @@ // High frequency ISO18092 / FeliCa commands //----------------------------------------------------------------------------- #include "cmdhffelica.h" - #include #include #include #include #include - -#include "cmdparser.h" // command_t +#include "cmdparser.h" // command_t #include "comms.h" #include "cmdtrace.h" #include "crc16.h" #include "util.h" #include "ui.h" -#include "iso18.h" // felica_card_select_t struct +#include "iso18.h" // felica_card_select_t struct #include "des.h" +#include "cliparser.h" // cliparser +#include "util_posix.h" // msleep + #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) static int CmdHelp(const char *Cmd); @@ -32,306 +33,138 @@ static void set_last_known_card(felica_card_select_t card) { last_known_card = card; } +static void print_status_flag1_interpretation(void) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _CYAN_("Status Flag 1")); + PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); + PrintAndLogEx(INFO, " 00 | Indicates the successful completion of a command."); + PrintAndLogEx(INFO, " FF | If an error occurs during the processing of a command that includes no list in the command packet, \n" + " | or if an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); + PrintAndLogEx(INFO, " XX | If an error occurs while processing a command that includes Service Code List or Block List \n" + " | in the command packet, the card returns a response by setting a number in the list to Status Flag1,\n" + " | indicating the location of the error."); + PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); +} + +static void print_status_flag2_interpration(void) { + PrintAndLogEx(INFO, _CYAN_("Status Flag 2")); + PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); + PrintAndLogEx(INFO, " 00 | Indicates the successful completion of a command."); + PrintAndLogEx(INFO, " 01 | The calculated result is either less than zero when the purse data is decremented, or exceeds 4\n" + " | Bytes when the purse data is incremented."); + PrintAndLogEx(INFO, " 02 | The specified data exceeds the value of cashback data at cashback of purse."); + PrintAndLogEx(INFO, " 70 | Memory error (fatal error)."); + PrintAndLogEx(INFO, " 71 | The number of memory rewrites exceeds the upper limit (this is only a warning; data writing is performed as normal).\n" + " | The maximum number of rewrites can differ, depending on the product being used.\n" + " | In addition, Status Flag1 is either 00h or FFh depending on the product being used."); + + PrintAndLogEx(INFO, " A1 | Illegal Number of Service| Number of Service or Number of Node specified by the command \n" + " | falls outside the range of the prescribed value."); + PrintAndLogEx(INFO, " A2 | Illegal command packet (specified Number of Block) : Number of Block specified by the \n" + " | command falls outside the range of the prescribed values for the product."); + PrintAndLogEx(INFO, " A3 | Illegal Block List (specified order of Service) : Service Code List Order specified by \n" + " | Block List Element falls outside the Number of Service specified by the command \n" + " | (or the Number of Service specified at the times of mutual authentication)."); + PrintAndLogEx(INFO, " A4 | Illegal Service type : Area Attribute specified by the command or Service Attribute of Service Code is incorrect."); + PrintAndLogEx(INFO, " A5 | Access is not allowed : Area or Service specified by the command cannot be accessed.\n" + " | The parameter specified by the command does not satisfy the conditions for success."); + PrintAndLogEx(INFO, " A6 | Illegal Service Code List : Target to be accessed, identified by Service Code List Order, specified by Block\n" + " | List Element does not exist. Or, Node specified by Node Code List does not exist."); + PrintAndLogEx(INFO, " A7 | Illegal Block List (Access Mode) : Access Mode specified by Block List Element is incorrect."); + PrintAndLogEx(INFO, " A8 | Illegal Block Number Block Number (access to the specified data is inhibited) :\n" + " | specified by Block List Element exceeds the number of Blocks assigned to Service."); + PrintAndLogEx(INFO, " A9 | Data write failure : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " AA | Key-change failure : Key change failed."); + PrintAndLogEx(INFO, " AB | Illegal Package Parity or illegal Package MAC : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " AC | Illegal parameter : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " AD | Service exists already : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " AE | Illegal System Code : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " AF | Too many simultaneous cyclic write operations : Number of simultaneous write Blocks\n" + " | specified by the command to Cyclic Service exceeds the number of Blocks assigned to Service."); + PrintAndLogEx(INFO, " C0 | Illegal Package Identifier : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " C1 | Discrepancy of parameters inside and outside Package : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, " C2 | Command is disabled already : This is the error that occurs in issuance commands."); + PrintAndLogEx(INFO, "----+--------------------------------------------------------------------------------------------------------------------"); + PrintAndLogEx(NORMAL, ""); +} + +static void print_block_list_element_constraints(void) { + PrintAndLogEx(INFO, " - Each Block List Element shall satisfy the following conditions:"); + PrintAndLogEx(INFO, " - The value of Service Code List Order shall not exceed Number of Service."); + PrintAndLogEx(INFO, " - Access Mode shall be 000b."); + PrintAndLogEx(INFO, " - The target specified by Service Code shall not be Area or System."); + PrintAndLogEx(INFO, " - Service specified in Service Code List shall exist in System."); + PrintAndLogEx(INFO, " - Service Attribute of Service specified in Service Code List shall be authentication-not-required Service."); + PrintAndLogEx(INFO, " - Block Number shall be in the range of the number of Blocks assigned to the specified Service."); +} + +static void print_number_of_service_constraints(void) { + PrintAndLogEx(INFO, " - Number of Service: shall be a positive integer in the range of 1 to 16, inclusive."); +} + +static void print_number_of_block_constraints(void) { + PrintAndLogEx(INFO, " - Number of Block: shall be less than or equal to the maximum number of Blocks that can be read simultaneously.\n" + " The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used.\n" + " Use as default 01"); +} + +static void print_service_code_list_constraints(void) { + PrintAndLogEx(INFO, " - Service Code List: For Service Code List, only Service Code existing in the product shall be specified:"); + PrintAndLogEx(INFO, " - Even when Service Code exists in the product, Service Code not referenced from Block List shall not \n" + " be specified to Service Code List."); + PrintAndLogEx(INFO, " - For existence or nonexistence of Service in a product, please check using the Request Service \n" + " (or Request Service v2) command."); +} + /* static int usage_hf_felica_sim(void) { - PrintAndLogEx(NORMAL, "\n Emulating ISO/18092 FeliCa tag \n"); - PrintAndLogEx(NORMAL, "Usage: hf felica sim [h] t [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " t : 1 = FeliCa"); - PrintAndLogEx(NORMAL, " : 2 = FeliCaLiteS"); - PrintAndLogEx(NORMAL, " v : (Optional) Verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf felica sim t 1 "); + PrintAndLogEx(INFO, "\n Emulating ISO/18092 FeliCa tag \n"); + PrintAndLogEx(INFO, "Usage: hf felica sim [h] t [v]"); + PrintAndLogEx(INFO, "Options:"); + PrintAndLogEx(INFO, " h : This help"); + PrintAndLogEx(INFO, " t : 1 = FeliCa"); + PrintAndLogEx(INFO, " : 2 = FeliCaLiteS"); + PrintAndLogEx(INFO, " v : (Optional) Verbose"); + PrintAndLogEx(INFO, "Examples:"); + PrintAndLogEx(INFO, " hf felica sim t 1 "); return PM3_SUCCESS; } */ -static int usage_hf_felica_sniff(void) { - PrintAndLogEx(NORMAL, "\nInfo: It get data from the field and saves it into command buffer. "); - PrintAndLogEx(NORMAL, " Buffer accessible from command 'hf felica list'"); - PrintAndLogEx(NORMAL, "\nUsage: hf felica sniff [-h] [-s] [-t]"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -s samples to skip (decimal) max 9999"); - PrintAndLogEx(NORMAL, " -t triggers to skip (decimal) max 9999"); - - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf felica sniff"); - PrintAndLogEx(NORMAL, " hf felica sniff -s 10 -t 10"); +static int print_authentication1(void) { + PrintAndLogEx(INFO, "Initiate mutual authentication. This command must always be executed before Auth2 command"); + PrintAndLogEx(INFO, "and mutual authentication is achieve only after Auth2 command has succeeded."); + PrintAndLogEx(INFO, " - Auth1 Parameters:"); + PrintAndLogEx(INFO, " - Number of Areas n: 1-byte (1 <= n <= 8)"); + PrintAndLogEx(INFO, " - Area Code List: 2n byte"); + PrintAndLogEx(INFO, " - Number of Services m: 1-byte (1 <= n <= 8)"); + PrintAndLogEx(INFO, " - Service Code List: 2n byte"); + PrintAndLogEx(INFO, " - 3DES-Key: 128-bit master secret used for the encryption"); + PrintAndLogEx(INFO, " - M1c: Encrypted random number - challenge for tag authentication (8-byte)"); + PrintAndLogEx(INFO, " - Response:"); + PrintAndLogEx(INFO, " - Response Code: 11h 1-byte"); + PrintAndLogEx(INFO, " - Manufacture ID(IDm): 8-byte"); + PrintAndLogEx(INFO, " - M2c: 8-byte"); + PrintAndLogEx(INFO, " - M3c: 8-byte"); + PrintAndLogEx(INFO, " - Success: Card Mode switches to Mode1. You can check this with the request response command."); + PrintAndLogEx(INFO, " - Unsuccessful: Card should not respond at all."); return PM3_SUCCESS; } -static int usage_hf_felica_simlite(void) { - PrintAndLogEx(NORMAL, "\n Emulating ISO/18092 FeliCa Lite tag \n"); - PrintAndLogEx(NORMAL, "Usage: hf felica litesim [h] u "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " uid : UID in hexsymbol"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf felica litesim 11223344556677"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_dumplite(void) { - PrintAndLogEx(NORMAL, "\n Dump ISO/18092 FeliCa Lite tag \n"); - PrintAndLogEx(NORMAL, "press button to abort run, otherwise it will loop for 200sec."); - PrintAndLogEx(NORMAL, "Usage: hf felica litedump [h]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf felica litedump"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_raw(void) { - PrintAndLogEx(NORMAL, "Usage: hf felica raw [-h] [-r] [-c] [-k] [-a] <0A 0B 0C ... hex>"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -r do not read response"); - PrintAndLogEx(NORMAL, " -c calculate and append CRC"); - PrintAndLogEx(NORMAL, " -k keep signal field ON after receive"); - PrintAndLogEx(NORMAL, " -a active signal field ON without select"); - PrintAndLogEx(NORMAL, " -s active signal field ON with select"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_request_service(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to verify the existence of Area and Service, and to acquire Key Version:"); - PrintAndLogEx(NORMAL, " - When the specified Area or Service exists, the card returns Key Version."); - PrintAndLogEx(NORMAL, " - When the specified Area or Service does not exist, the card returns FFFFh as Key Version."); - PrintAndLogEx(NORMAL, "For Node Code List of a command packet, Area Code or Service Code of the target " - "of acquisition of Key Version shall be enumerated in Little Endian format. " - "If Key Version of System is the target of acquisition, FFFFh shall be specified " - "in the command packet."); - PrintAndLogEx(NORMAL, "\nUsage: hf felica rqservice [-h] [-i] <01 Number of Node hex> <0A0B Node Code List hex (Little Endian)>"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, " -a auto node number mode - iterates through all possible nodes 1 < n < 32"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica rqservice 01 FFFF"); - PrintAndLogEx(NORMAL, " hf felica rqservice -a FFFF"); - PrintAndLogEx(NORMAL, " hf felica rqservice -i 01100910c11bc407 01 FFFF \n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_request_response(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to verify the existence of a card and its Mode."); - PrintAndLogEx(NORMAL, " - Current Mode of the card is returned."); - PrintAndLogEx(NORMAL, "\nUsage: hf felica rqresponse [-h]"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, " hf felica rqresponse -i 01100910c11bc407"); - return PM3_SUCCESS; -} - -static void print_status_flag1_interpretation(void) { - PrintAndLogEx(NORMAL, "\nStatus Flag1:"); - PrintAndLogEx(NORMAL, " - 00h : Indicates the successful completion of a command."); - PrintAndLogEx(NORMAL, " - FFh : If an error occurs during the processing of a command that includes no list in the command packet, or if " - "an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); - PrintAndLogEx(NORMAL, " - XXh : If an error occurs while processing a command that includes Service Code List or Block List " - "in the command packet, the card returns a response by setting a number in the list to Status Flag1, " - "indicating the location of the error."); -} - -static void print_status_flag2_interpration(void) { - PrintAndLogEx(NORMAL, "\nStatus Flag2:"); - PrintAndLogEx(NORMAL, " - 00h : Indicates the successful completion of a command."); - PrintAndLogEx(NORMAL, " - 01h : The calculated result is either less than zero when the purse data is decremented, or exceeds 4" - "Bytes when the purse data is incremented."); - PrintAndLogEx(NORMAL, " - 02h : The specified data exceeds the value of cashback data at cashback of purse."); - PrintAndLogEx(NORMAL, " - 70h : Memory error (fatal error)."); - PrintAndLogEx(NORMAL, " - 71h : The number of memory rewrites exceeds the upper limit (this is only a warning; data writing is " - "performed as normal). The maximum number of rewrites can differ, depending on the product being used."); - PrintAndLogEx(NORMAL, " In addition, Status Flag1 is either 00h or FFh depending on the product being used."); - PrintAndLogEx(NORMAL, " - A1h : Illegal Number of Service: Number of Service or Number of Node specified by the command falls outside the range of the prescribed value."); - PrintAndLogEx(NORMAL, " - A2h : Illegal command packet (specified Number of Block): Number of Block specified by the command falls outside the range of the prescribed values for the product."); - PrintAndLogEx(NORMAL, " - A3h : Illegal Block List (specified order of Service): Service Code List Order specified by Block List Element falls outside the Number of Service specified by the " - "command (or the Number of Service specified at the times of mutual authentication)."); - PrintAndLogEx(NORMAL, " - A4h : Illegal Service type: Area Attribute specified by the command or Service Attribute of Service Code is incorrect."); - PrintAndLogEx(NORMAL, " - A5h : Access is not allowed: Area or Service specified by the command cannot be accessed. " - "The parameter specified by the command does not satisfy the conditions for success."); - PrintAndLogEx(NORMAL, " - A6h : Illegal Service Code List: Target to be accessed, identified by Service Code List Order, specified by Block " - "List Element does not exist. Or, Node specified by Node Code List does not exist."); - PrintAndLogEx(NORMAL, " - A7h : Illegal Block List (Access Mode): Access Mode specified by Block List Element is incorrect."); - PrintAndLogEx(NORMAL, " - A8h : Illegal Block Number Block Number (access to the specified data is inhibited): specified by Block List Element exceeds the number of Blocks assigned to Service."); - PrintAndLogEx(NORMAL, " - A9h : Data write failure: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - AAh : Key-change failure: Key change failed."); - PrintAndLogEx(NORMAL, " - ABh : Illegal Package Parity or illegal Package MAC: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - ACh : Illegal parameter: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - ADh : Service exists already: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - AEh : Illegal System Code: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - AFh : Too many simultaneous cyclic write operations: Number of simultaneous write Blocks specified by the command to Cyclic Service " - "exceeds the number of Blocks assigned to Service."); - PrintAndLogEx(NORMAL, " - C0h : Illegal Package Identifier: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - C1h : Discrepancy of parameters inside and outside Package: This is the error that occurs in issuance commands."); - PrintAndLogEx(NORMAL, " - C2h : Command is disabled already: This is the error that occurs in issuance commands."); -} - -static void print_block_list_element_constraints(void) { - PrintAndLogEx(NORMAL, " - Each Block List Element shall satisfy the following conditions:"); - PrintAndLogEx(NORMAL, " - The value of Service Code List Order shall not exceed Number of Service."); - PrintAndLogEx(NORMAL, " - Access Mode shall be 000b."); - PrintAndLogEx(NORMAL, " - The target specified by Service Code shall not be Area or System."); - PrintAndLogEx(NORMAL, " - Service specified in Service Code List shall exist in System."); - PrintAndLogEx(NORMAL, " - Service Attribute of Service specified in Service Code List shall be authentication-not-required Service."); - PrintAndLogEx(NORMAL, " - Block Number shall be in the range of the number of Blocks assigned to the specified Service."); -} - -static void print_number_of_service_constraints(void) { - PrintAndLogEx(NORMAL, " - Number of Service: shall be a positive integer in the range of 1 to 16, inclusive."); -} - -static void print_number_of_block_constraints(void) { - PrintAndLogEx(NORMAL, " - Number of Block: shall be less than or equal to the maximum number of Blocks that can be read simultaneously. " - "The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used. Use as default 01"); -} - -static void print_service_code_list_constraints(void) { - PrintAndLogEx(NORMAL, " - Service Code List: For Service Code List, only Service Code existing in the product shall be specified:"); - PrintAndLogEx(NORMAL, " - Even when Service Code exists in the product, Service Code not referenced from Block List shall not be specified to Service Code List."); - PrintAndLogEx(NORMAL, " - For existence or nonexistence of Service in a product, please check using the Request Service (or Request Service v2) command."); -} - -static int usage_hf_felica_read_without_encryption(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to read Block Data from authentication-not-required Service."); - PrintAndLogEx(NORMAL, " - Mode shall be Mode0."); - print_number_of_service_constraints(); - print_number_of_block_constraints(); - print_service_code_list_constraints(); - print_block_list_element_constraints(); - PrintAndLogEx(NORMAL, " - Successful read: Card responses the block data"); - PrintAndLogEx(NORMAL, " - Unsuccessful read: Card responses with Status Flag1 and Flag2"); - print_status_flag1_interpretation(); - print_status_flag2_interpration(); - PrintAndLogEx(NORMAL, "\nUsage: hf felica rdunencrypted [-h] <01 Number of Service hex> <0A0B Service Code List (Little Endian) hex> <01 Number of Block hex> <0A0B Block List Element hex>"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, " -b get all Block List Elements starting from 00 to FF - stops when a block return an error status flags"); - PrintAndLogEx(NORMAL, " -l use 3-byte block list element block number"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica rdunencrypted 01 8B00 01 8000"); - PrintAndLogEx(NORMAL, " hf felica rdunencrypted -i 01100910c11bc407 01 8B00 01 8000"); - PrintAndLogEx(NORMAL, " hf felica rdunencrypted -b 01 4B18 01 8000\n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_write_without_encryption(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to write Block Data to authentication-not-required Service."); - PrintAndLogEx(NORMAL, " - Mode shall be Mode0."); - print_number_of_service_constraints(); - print_number_of_block_constraints(); - print_service_code_list_constraints(); - print_block_list_element_constraints(); - PrintAndLogEx(NORMAL, " - Un-/Successful read: Card responses with Status Flag1 and Flag2"); - print_status_flag1_interpretation(); - print_status_flag2_interpration(); - PrintAndLogEx(NORMAL, "\nUsage: hf felica wrunencrypted [-h][-i] <01 Number of Service hex> <0A0B Service Code List (Little Endian) hex> <01 Number of Block hex> <0A0B Block List Element hex> <0A0B0C0D0E0F... Data hex (16-Byte)>"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use\n"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica wrunencrypted 01 CB10 01 8001 0102030405060708090A0B0C0D0E0F10"); - PrintAndLogEx(NORMAL, " hf felica wrunencrypted -i 11100910C11BC407 01 CB10 01 8001 0102030405060708090A0B0C0D0E0F10\n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_request_system_code(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to acquire System Code registered to the card."); - PrintAndLogEx(NORMAL, " - If a card is divided into more than one System, this command acquires System Code of each System existing in the card."); - PrintAndLogEx(NORMAL, "\nUsage: hf felica rqsyscode [-h] [-i]"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica rqsyscode "); - PrintAndLogEx(NORMAL, " hf felica rqsyscode -i 11100910C11BC407\n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_reset_mode(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to reset Mode to Mode 0."); - print_status_flag1_interpretation(); - print_status_flag2_interpration(); - PrintAndLogEx(NORMAL, "\nUsage: hf felica resetmode [-h][-i][-r]"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, " -r <0A0B hex> set custom reserve to use"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica resetmode "); - PrintAndLogEx(NORMAL, " hf felica resetmode -r 0001"); - PrintAndLogEx(NORMAL, " hf felica resetmode -i 11100910C11BC407\n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_request_specification_version(void) { - PrintAndLogEx(NORMAL, "\nInfo: Use this command to acquire the version of card OS."); - PrintAndLogEx(NORMAL, " - Response:"); - PrintAndLogEx(NORMAL, " - Format Version: Fixed value 00h. Provided only if Status Flag1 = 00h."); - PrintAndLogEx(NORMAL, " - Basic Version: Each value of version is expressed in BCD notation . Provided only if Status Flag1 = 00h."); - PrintAndLogEx(NORMAL, " - Number of Option: value = 0: AES card, value = 1: AES/DES card. Provided only if Status Flag1 = 00h."); - PrintAndLogEx(NORMAL, " - Option Version List: Provided only if Status Flag1 = 00h."); - PrintAndLogEx(NORMAL, " - For AES card: not added."); - PrintAndLogEx(NORMAL, " - For AES/DES card: DES option version is added - BCD notation ."); - print_status_flag1_interpretation(); - print_status_flag2_interpration(); - PrintAndLogEx(NORMAL, "\nUsage: hf felica rqspecver [-h][-i][-r]"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, " -r <0A0B hex> set custom reserve to use"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica rqspecver "); - PrintAndLogEx(NORMAL, " hf felica rqspecver -r 0001"); - PrintAndLogEx(NORMAL, " hf felica rqspecver -i 11100910C11BC407\n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_authentication1(void) { - PrintAndLogEx(NORMAL, "\nInfo: Initiate mutual authentication. This command must always be executed before Authentication2 command" - ", and mutual authentication is achieve only after Authentication2 command has succeeded."); - PrintAndLogEx(NORMAL, " - Auth1 Parameters:"); - PrintAndLogEx(NORMAL, " - Number of Areas n: 1-byte (1 <= n <= 8)"); - PrintAndLogEx(NORMAL, " - Area Code List: 2n byte"); - PrintAndLogEx(NORMAL, " - Number of Services m: 1-byte (1 <= n <= 8)"); - PrintAndLogEx(NORMAL, " - Service Code List: 2n byte"); - PrintAndLogEx(NORMAL, " - 3DES-Key: 128-bit master secret used for the encryption"); - PrintAndLogEx(NORMAL, " - M1c: Encrypted random number - challenge for tag authentication (8-byte)"); - PrintAndLogEx(NORMAL, " - Response:"); - PrintAndLogEx(NORMAL, " - Response Code: 11h 1-byte"); - PrintAndLogEx(NORMAL, " - Manufacture ID(IDm): 8-byte"); - PrintAndLogEx(NORMAL, " - M2c: 8-byte"); - PrintAndLogEx(NORMAL, " - M3c: 8-byte"); - PrintAndLogEx(NORMAL, " - Success: Card Mode switches to Mode1. You can check this with the request response command."); - PrintAndLogEx(NORMAL, " - Unsuccessful: Card should not respond at all."); - - PrintAndLogEx(NORMAL, "\nUsage: hf felica auth1 [-h][-i] <01 Number of Areas hex> <0A0B... Area Code List hex> <01 Number of Services hex> <0A0B... Service Code List hex> <0x0102030405060809... 3DES-key hex (16-byte)>"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica auth1 01 0000 01 8B00 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB "); - PrintAndLogEx(NORMAL, " hf felica auth1 01 0000 01 8B00 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA "); - PrintAndLogEx(NORMAL, " hf felica auth1 -i 11100910C11BC407 01 0000 01 8B00 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n\n"); - return PM3_SUCCESS; -} - -static int usage_hf_felica_authentication2(void) { - PrintAndLogEx(NORMAL, "\nInfo: Complete mutual authentication. This command can only be executed subsquent to Authentication1" - " command."); - PrintAndLogEx(NORMAL, " - Auth2 Parameters:"); - PrintAndLogEx(NORMAL, " - Manufacturer IDm: (8-byte)"); - PrintAndLogEx(NORMAL, " - M3c: card challenge (8-byte)"); - PrintAndLogEx(NORMAL, " - 3DES Key: key used for decryption of M3c (16-byte)"); - PrintAndLogEx(NORMAL, " - Response (encrypted):"); - PrintAndLogEx(NORMAL, " - Response Code: 13h (1-byte)"); - PrintAndLogEx(NORMAL, " - IDtc: (8-byte)"); - PrintAndLogEx(NORMAL, " - IDi (encrypted): (8-byte)"); - PrintAndLogEx(NORMAL, " - PMi (encrypted): (8-byte)"); - PrintAndLogEx(NORMAL, " - Success: Card switches to mode2 and sends response frame."); - PrintAndLogEx(NORMAL, " - Unsuccessful: Card should not respond at all."); - PrintAndLogEx(NORMAL, "\nUsage: hf felica auth2 [-h][-i] <0102030405060708 M3c hex> <0x0102030405060809... 3DES-key hex (16-byte)>"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); - PrintAndLogEx(NORMAL, "\nExamples: "); - PrintAndLogEx(NORMAL, " hf felica auth2 0102030405060708 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB"); - PrintAndLogEx(NORMAL, " hf felica auth2 -i 11100910C11BC407 0102030405060708 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n\n"); - +static int print_authentication2(void) { + PrintAndLogEx(INFO, "Complete mutual authentication."); + PrintAndLogEx(INFO, "This command can only be executed subsquent to Auth1 command."); + PrintAndLogEx(INFO, " - Auth2 Parameters:"); + PrintAndLogEx(INFO, " - Manufacturer IDm: (8-byte)"); + PrintAndLogEx(INFO, " - M3c: card challenge (8-byte)"); + PrintAndLogEx(INFO, " - 3DES Key: key used for decryption of M3c (16-byte)"); + PrintAndLogEx(INFO, " - Response (encrypted):"); + PrintAndLogEx(INFO, " - Response Code: 13h (1-byte)"); + PrintAndLogEx(INFO, " - IDtc: (8-byte)"); + PrintAndLogEx(INFO, " - IDi (encrypted): (8-byte)"); + PrintAndLogEx(INFO, " - PMi (encrypted): (8-byte)"); + PrintAndLogEx(INFO, " - Success: Card switches to mode2 and sends response frame."); + PrintAndLogEx(INFO, " - Unsuccessful: Card should not respond at all."); return PM3_SUCCESS; } @@ -344,49 +177,27 @@ static bool waitCmdFelica(uint8_t iSelect, PacketResponseNG *resp, bool verbose) if (WaitForResponseTimeout(CMD_ACK, resp, 2000)) { uint16_t len = iSelect ? (resp->oldarg[1] & 0xffff) : (resp->oldarg[0] & 0xffff); if (verbose) { - PrintAndLogEx(SUCCESS, "Client Received %i octets", len); + PrintAndLogEx(SUCCESS, "client received %i octets", len); if (len == 0 || len == 1) { PrintAndLogEx(ERR, "Could not receive data correctly!"); return false; } PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp->data.asBytes, len)); if (!check_crc(CRC_FELICA, resp->data.asBytes + 2, len - 2)) { - PrintAndLogEx(WARNING, "Wrong or no CRC bytes"); + PrintAndLogEx(WARNING, "wrong or no CRC bytes"); } if (resp->data.asBytes[0] != 0xB2 && resp->data.asBytes[1] != 0x4D) { - PrintAndLogEx(ERR, "Received incorrect Frame Format!"); + PrintAndLogEx(ERR, "received incorrect frame format!"); return false; } } return true; } else { - PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply."); } return false; } -/* - * Counts and sets the number of parameters. - */ -static void strip_cmds(const char *Cmd) { - while (*Cmd == ' ' || *Cmd == '\t') { - Cmd++; - } -} - -/** - * Converts integer value to equivalent hex value. - * Examples: 1 = 1, 11 = B - * @param number number of hex bytes. - * @return number as hex value. - */ -static uint8_t int_to_hex(uint16_t *number) { - uint32_t hex; - char dataLengthChar[5]; - sprintf(dataLengthChar, "%x", *number); - sscanf(dataLengthChar, "%x", &hex); - return (uint8_t)(hex & 0xff); -} /** * Adds the last known IDm (8-Byte) to the data frame. @@ -396,29 +207,142 @@ static uint8_t int_to_hex(uint16_t *number) { */ static bool add_last_IDm(uint8_t position, uint8_t *data) { if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { - for (int i = 0; i < 8; i++) { - uint16_t number = (uint16_t)last_known_card.IDm[i]; - data[position + i] = int_to_hex(&number); - } + memcpy(data + position, last_known_card.IDm, sizeof(last_known_card.IDm)); return true; - } else { - return false; } + return false; } static int CmdHFFelicaList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t felica"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf felica", "felica"); +} + +int read_felica_uid(bool loop, bool verbose) { + + int res = PM3_SUCCESS; + + do { + clearCommandBuffer(); + SendCommandMIX(CMD_HF_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + uint8_t status = resp.oldarg[0] & 0xFF; + + if (loop) { + if (status != 0) { + continue; + } + } else { + // when not in continuous mode + if (status != 0) { + if (verbose) PrintAndLogEx(WARNING, "FeliCa card select failed"); + res = PM3_EOPABORTED; + break; + } + } + + felica_card_select_t card; + memcpy(&card, (felica_card_select_t *)resp.data.asBytes, sizeof(felica_card_select_t)); + if (loop == false) { + PrintAndLogEx(NORMAL, ""); + } + PrintAndLogEx(SUCCESS, "IDm: " _GREEN_("%s"), sprint_hex_inrow(card.IDm, sizeof(card.IDm))); + set_last_known_card(card); + } + } while (loop && kbd_enter_pressed() == false); + + DropField(); + return res; } static int CmdHFFelicaReader(const char *Cmd) { - bool verbose = !(tolower(Cmd[0]) == 's'); - return readFelicaUid(verbose); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica reader", + "Reader for FeliCa based tags", + "hf felica reader -@ -> Continuous mode"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("s", "silent", "silent (no messages)"), + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = (arg_get_lit(ctx, 1) == false); + bool cm = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + CLIParserFree(ctx); + return read_felica_uid(cm, verbose); +} + +static int info_felica(bool verbose) { + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + if (verbose) PrintAndLogEx(WARNING, "FeliCa card select failed"); + return PM3_ESOFT; + } + + felica_card_select_t card; + memcpy(&card, (felica_card_select_t *)resp.data.asBytes, sizeof(felica_card_select_t)); + uint64_t status = resp.oldarg[0]; + + switch (status) { + case 1: { + if (verbose) + PrintAndLogEx(WARNING, "card timeout"); + return PM3_ETIMEOUT; + } + case 2: { + if (verbose) + PrintAndLogEx(WARNING, "card answered wrong"); + return PM3_ESOFT; + } + case 3: { + if (verbose) + PrintAndLogEx(WARNING, "CRC check failed"); + return PM3_ESOFT; + } + case 0: { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + PrintAndLogEx(INFO, "IDm............ " _GREEN_("%s"), sprint_hex_inrow(card.IDm, sizeof(card.IDm))); + PrintAndLogEx(INFO, "Code........... %s ", sprint_hex_inrow(card.code, sizeof(card.code))); + PrintAndLogEx(INFO, "NFCID2......... %s", sprint_hex_inrow(card.uid, sizeof(card.uid))); + PrintAndLogEx(INFO, "Parameter"); + PrintAndLogEx(INFO, "PAD............ " _YELLOW_("%s"), sprint_hex_inrow(card.PMm, sizeof(card.PMm))); + PrintAndLogEx(INFO, "IC code........ %s", sprint_hex_inrow(card.iccode, sizeof(card.iccode))); + PrintAndLogEx(INFO, "MRT............ %s", sprint_hex_inrow(card.mrt, sizeof(card.mrt))); + PrintAndLogEx(INFO, "Service code... " _YELLOW_("%s"), sprint_hex(card.servicecode, sizeof(card.servicecode))); + PrintAndLogEx(NORMAL, ""); + set_last_known_card(card); + break; + } + } + return PM3_SUCCESS; +} + +static int CmdHFFelicaInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica info", + "Reader for FeliCa based tags", + "hf felica info"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + return info_felica(false); } /** @@ -433,35 +357,11 @@ static void clear_and_send_command(uint8_t flags, uint16_t datalen, uint8_t *dat SendCommandMIX(CMD_HF_FELICA_COMMAND, flags, (datalen & 0xFFFF) | (uint32_t)(numbits << 16), 0, data, datalen); } -/** - * Adds a parameter to the frame and checks if the parameter has the specific length. - * @param Cmd User input with the parameter. - * @param paramCount number of the parameter within the user input. - * @param data frame in which the data is stored. - * @param dataPosition position within frame where the data will be stored. - * @param length which the parameter should have and will be tested against. - * @return true if parameters was added. - */ -static bool add_param(const char *Cmd, uint8_t paramCount, uint8_t *data, uint8_t dataPosition, uint8_t length) { - if (param_getlength(Cmd, paramCount) == length) { - - if (param_gethex(Cmd, paramCount, data + dataPosition, length) == 1) - return false; - else - return true; - - } else { - PrintAndLogEx(ERR, "Param %s", Cmd); - PrintAndLogEx(ERR, "Incorrect Parameter length! Param %i should be %i", paramCount, length); - return false; - } -} - /** * Prints read-without-encryption response. * @param rd_noCry_resp Response frame. */ -static void print_rd_noEncrpytion_response(felica_read_without_encryption_response_t *rd_noCry_resp) { +static void print_rd_plain_response(felica_read_without_encryption_response_t *rd_noCry_resp) { if (rd_noCry_resp->status_flags.status_flag1[0] == 00 && rd_noCry_resp->status_flags.status_flag2[0] == 00) { @@ -475,11 +375,11 @@ static void print_rd_noEncrpytion_response(felica_read_without_encryption_respon temp = sprint_hex(rd_noCry_resp->block_element_number, sizeof(rd_noCry_resp->block_element_number)); strncpy(bl_element_number, temp, sizeof(bl_element_number) - 1); - PrintAndLogEx(INFO, "\t%s\t| %s ", bl_element_number, bl_data); + PrintAndLogEx(INFO, " %s | %s ", bl_element_number, bl_data); } else { - PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rd_noCry_resp->frame_response.IDm, sizeof(rd_noCry_resp->frame_response.IDm))); - PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); - PrintAndLogEx(SUCCESS, "Status Flag2: %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "IDm... %s", sprint_hex_inrow(rd_noCry_resp->frame_response.IDm, sizeof(rd_noCry_resp->frame_response.IDm))); + PrintAndLogEx(SUCCESS, " Status flag 1... %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, " Status flag 2... %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); } } @@ -494,14 +394,14 @@ int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool ve PrintAndLogEx(ERR, "\nGot no response from card"); return PM3_ERFTRANS; } - felica_request_service_response_t rqs_response; - memcpy(&rqs_response, (felica_request_service_response_t *)resp.data.asBytes, sizeof(felica_request_service_response_t)); + felica_request_service_response_t r; + memcpy(&r, (felica_request_service_response_t *)resp.data.asBytes, sizeof(felica_request_service_response_t)); - if (rqs_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "\nGot Service Response:"); - PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rqs_response.frame_response.IDm, sizeof(rqs_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, " -Node Number: %s", sprint_hex(rqs_response.node_number, sizeof(rqs_response.node_number))); - PrintAndLogEx(SUCCESS, " -Node Key Version List: %s\n", sprint_hex(rqs_response.node_key_versions, sizeof(rqs_response.node_key_versions))); + if (r.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "Service Response:"); + PrintAndLogEx(SUCCESS, "IDm... %s", sprint_hex_inrow(r.frame_response.IDm, sizeof(r.frame_response.IDm))); + PrintAndLogEx(SUCCESS, " Node number............. %s", sprint_hex(r.node_number, sizeof(r.node_number))); + PrintAndLogEx(SUCCESS, " Node key version list... %s\n", sprint_hex(r.node_key_versions, sizeof(r.node_key_versions))); } return PM3_SUCCESS; } @@ -517,11 +417,11 @@ int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool ve * @param rd_noCry_resp frame in which the response will be saved. * @return success if response was received. */ -int send_rd_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp) { +int send_rd_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp) { clear_and_send_command(flags, datalen, data, verbose); PacketResponseNG resp; if (!waitCmdFelica(0, &resp, verbose)) { - PrintAndLogEx(ERR, "\nGot no response from card"); + PrintAndLogEx(ERR, "No response from card"); return PM3_ERFTRANS; } else { memcpy(rd_noCry_resp, (felica_read_without_encryption_response_t *)resp.data.asBytes, sizeof(felica_read_without_encryption_response_t)); @@ -537,13 +437,13 @@ int send_rd_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool ver * @return */ static bool check_last_idm(uint8_t *data, uint16_t datalen) { - if (!add_last_IDm(2, data)) { - PrintAndLogEx(ERR, "No last known card! Use reader first or set a custom IDm!"); - return 0; - } else { - PrintAndLogEx(INFO, "Used last known IDm. %s", sprint_hex(data, datalen)); - return 1; + if (add_last_IDm(2, data) == false) { + PrintAndLogEx(WARNING, "No last known card! Use `" _YELLOW_("hf felica reader") "` first or set a custom IDm"); + return false; } + + PrintAndLogEx(INFO, "Using last known IDm... " _GREEN_("%s"), sprint_hex_inrow(data, datalen)); + return true; } /** @@ -555,16 +455,16 @@ static bool check_last_idm(uint8_t *data, uint16_t datalen) { * @param wr_noCry_resp frame in which the response will be saved. * @return success if response was received. */ -static int send_wr_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_status_response_t *wr_noCry_resp) { +static int send_wr_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_status_response_t *wr_noCry_resp) { clear_and_send_command(flags, datalen, data, verbose); PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, verbose)) { - PrintAndLogEx(ERR, "\nGot no response from card"); + if (waitCmdFelica(0, &resp, verbose) == false) { + PrintAndLogEx(ERR, "no response from card"); return PM3_ERFTRANS; - } else { - memcpy(wr_noCry_resp, (felica_status_response_t *)resp.data.asBytes, sizeof(felica_status_response_t)); - return PM3_SUCCESS; } + + memcpy(wr_noCry_resp, (felica_status_response_t *)resp.data.asBytes, sizeof(felica_status_response_t)); + return PM3_SUCCESS; } /** @@ -585,143 +485,212 @@ static void reverse_3des_key(uint8_t *master_key, int length, uint8_t *reverse_m * @return client result code. */ static int CmdHFFelicaAuthentication1(const char *Cmd) { - if (strlen(Cmd) < 4) { - return usage_hf_felica_authentication1(); - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica auth1", + "Initiate mutual authentication. This command must always be executed before Auth2 command\n" + "and mutual authentication is achieve only after Auth2 command has succeeded.\n" + _RED_("INCOMPLETE / EXPERIMENTAL COMMAND!!!"), + "hf felica auth1 --an 01 --acl 0000 --sn 01 --scl 8B00 --key AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n" + "hf felica auth1 --an 01 --acl 0000 --sn 01 --scl 8B00 --key AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA\n" + "hf felica auth1 -i 11100910C11BC407 --an 01 --acl 0000 --sn 01 ..scl 8B00 --key AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "an", "", "number of areas, 1 byte"), + arg_str0(NULL, "acl", "", "area code list, 2 bytes"), + arg_str0("i", NULL, "", "set custom IDm"), + 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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - PrintAndLogEx(INFO, "INCOMPLETE / EXPERIMENTAL COMMAND!!!"); - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - strip_cmds(Cmd); - uint16_t datalen = 24; // Length (1), Command ID (1), IDm (8), Number of Area (1), Area Code List (2), Number of Service (1), Service Code List (2), M1c (8) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_authentication1(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - default: - return usage_hf_felica_authentication1(); - } - } - i++; - } - data[0] = int_to_hex(&datalen); - data[1] = 0x10; // Command ID - if (!custom_IDm && !check_last_idm(data, datalen)) { + uint8_t an[1] = {0}; + int anlen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), an, sizeof(an), &anlen); + if (res) { + CLIParserFree(ctx); return PM3_EINVARG; } - // Number of Area (1), Area Code List (2), Number of Service (1), Service Code List (2), M1c (8) - uint8_t lengths[] = {2, 4, 2, 4}; - uint8_t dataPositions[] = {10, 11, 13, 14}; - for (i = 0; i < 4; i++) { - if (add_param(Cmd, paramCount, data, dataPositions[i], lengths[i])) { - paramCount++; - } else { - return PM3_EINVARG; - } + + uint8_t acl[2] = {0}; + int acllen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), acl, sizeof(acl), &acllen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t idm[8] = {0}; + int ilen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t sn[1] = {0}; + int snlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 4), sn, sizeof(sn), &snlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t scl[2] = {0}; + int scllen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 5), scl, sizeof(scl), &scllen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t key[24] = {0}; + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 6), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + if (verbose) { + print_authentication1(); + return PM3_SUCCESS; + } + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + data[0] = 0x0C; // Static length + data[1] = 0x3E; // Command ID + + bool custom_IDm = false; + + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, 8); + } + + // Length (1), + // Command ID (1), + // IDm (8), + // Number of Area (1), + // Area Code List (2), + // Number of Service (1), + // Service Code List (2), + // M1c (16) + uint16_t datalen = 32; + data[0] = (datalen & 0xFF); + data[1] = 0x10; // Command ID + + if (custom_IDm == false && check_last_idm(data, datalen) == false) { + return PM3_EINVARG; + } + + if (anlen) { + data[10] = an[0]; + } + if (acllen) { + data[11] = acl[0]; + data[12] = acl[1]; + } + if (snlen) { + data[13] = sn[0]; + } + if (scllen) { + data[14] = scl[0]; + data[15] = scl[1]; + } + if (keylen) { + memcpy(data + 16, key, keylen); } // READER CHALLENGE - (RANDOM To Encrypt = Rac) - unsigned char input[8]; - input[0] = 0x1; - input[1] = 0x2; - input[2] = 0x3; - input[3] = 0x4; - input[4] = 0x5; - input[5] = 0x6; - input[6] = 0x7; - input[7] = 0x8; - PrintAndLogEx(INFO, "Reader challenge (unencrypted): %s", sprint_hex(input, 8)); - unsigned char output[8]; + uint8_t nonce[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + 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[PM3_CMD_DATA_SIZE]; + uint8_t master_key[24]; mbedtls_des3_context des3_ctx; mbedtls_des3_init(&des3_ctx); - if (param_getlength(Cmd, paramCount) == 48) { - if (param_gethex(Cmd, paramCount, master_key, 48) == 1) { - PrintAndLogEx(ERR, "Failed param key"); - return PM3_EINVARG; - } - mbedtls_des3_set3key_enc(&des3_ctx, master_key); - PrintAndLogEx(INFO, "3DES Master Secret: %s", sprint_hex(master_key, 24)); - } else if (param_getlength(Cmd, paramCount) == 32) { - if (param_gethex(Cmd, paramCount, master_key, 32) == 1) { - PrintAndLogEx(ERR, "Failed param key"); - return PM3_EINVARG; - } + if (keylen == 24) { + + mbedtls_des3_set3key_enc(&des3_ctx, master_key); + PrintAndLogEx(INFO, "3DES Master Secret: %s", sprint_hex(master_key, keylen)); + + } else if (keylen == 16) { // Assumption: Master secret split in half for Kac, Kbc mbedtls_des3_set2key_enc(&des3_ctx, master_key); - PrintAndLogEx(INFO, "3DES Master Secret: %s", sprint_hex(master_key, 16)); + PrintAndLogEx(INFO, "3DES Master Secret: %s", sprint_hex(master_key, keylen)); } else { PrintAndLogEx(ERR, "Invalid key length"); + mbedtls_des3_free(&des3_ctx); return PM3_EINVARG; } - mbedtls_des3_crypt_ecb(&des3_ctx, input, output); - PrintAndLogEx(INFO, "3DES ENCRYPTED M1c: %s", sprint_hex(output, 8)); + uint8_t output[8]; + mbedtls_des3_crypt_ecb(&des3_ctx, nonce, output); + mbedtls_des3_free(&des3_ctx); + + PrintAndLogEx(INFO, "3DES ENCRYPTED M1c: %s", sprint_hex(output, sizeof(output))); + // Add M1c Challenge to frame - int frame_position = 16; - for (i = 0; i < 8; i++) { - data[frame_position++] = output[i]; - } + memcpy(data + 16, output, sizeof(output)); AddCrc(data, datalen); datalen += 2; - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); - PrintAndLogEx(INFO, "Client Send AUTH1 Frame: %s", sprint_hex(data, datalen)); + PrintAndLogEx(INFO, "Client send AUTH1 frame: %s", sprint_hex(data, datalen)); clear_and_send_command(flags, datalen, data, 0); PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, 1)) { - PrintAndLogEx(ERR, "\nGot no Response from card"); + if (waitCmdFelica(0, &resp, 1) == false) { + PrintAndLogEx(ERR, "no response from card"); return PM3_ERFTRANS; - } else { - felica_auth1_response_t auth1_response; - memcpy(&auth1_response, (felica_auth1_response_t *)resp.data.asBytes, sizeof(felica_auth1_response_t)); - if (auth1_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "\nGot auth1 response:"); - PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(auth1_response.frame_response.IDm, sizeof(auth1_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, "M2C: %s", sprint_hex(auth1_response.m2c, sizeof(auth1_response.m2c))); - PrintAndLogEx(SUCCESS, "M3C: %s", sprint_hex(auth1_response.m3c, sizeof(auth1_response.m3c))); - // Assumption: Key swap method used - uint8_t reverse_master_key[PM3_CMD_DATA_SIZE]; - reverse_3des_key(master_key, 16, reverse_master_key); - mbedtls_des3_set2key_dec(&des3_ctx, reverse_master_key); - bool isKeyCorrect = false; - unsigned char p2c[8]; - mbedtls_des3_crypt_ecb(&des3_ctx, auth1_response.m2c, p2c); - for (i = 0; i < 8; i++) { - if (p2c[i] != input[i]) { - isKeyCorrect = false; - break; - } else { - isKeyCorrect = true; - } - } - if (isKeyCorrect) { - PrintAndLogEx(SUCCESS, "\nAuth1 done with correct key material! Use Auth2 now with M3C and same key"); + } + + felica_auth1_response_t auth1_response; + memcpy(&auth1_response, (felica_auth1_response_t *)resp.data.asBytes, sizeof(felica_auth1_response_t)); + + if (auth1_response.frame_response.IDm[0]) { + PrintAndLogEx(SUCCESS, "Auth1 response:"); + PrintAndLogEx(SUCCESS, "IDm... %s", sprint_hex(auth1_response.frame_response.IDm, sizeof(auth1_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "M2C... %s", sprint_hex(auth1_response.m2c, sizeof(auth1_response.m2c))); + PrintAndLogEx(SUCCESS, "M3C... %s", sprint_hex(auth1_response.m3c, sizeof(auth1_response.m3c))); + + // Assumption: Key swap method used + uint8_t rev_master_key[PM3_CMD_DATA_SIZE]; + reverse_3des_key(master_key, 16, rev_master_key); + mbedtls_des3_set2key_dec(&des3_ctx, rev_master_key); + + bool is_key_correct = false; + unsigned char p2c[8]; + mbedtls_des3_crypt_ecb(&des3_ctx, auth1_response.m2c, p2c); + + for (uint8_t i = 0; i < 8; i++) { + if (p2c[i] != nonce[i]) { + is_key_correct = false; + break; } else { - PrintAndLogEx(INFO, "3DES secret (swapped decryption): %s", sprint_hex(reverse_master_key, 16)); - PrintAndLogEx(INFO, "P2c: %s", sprint_hex(p2c, 8)); - PrintAndLogEx(ERR, "Can't decrypt M2C with master secret (P1c != P2c)! Probably wrong keys or wrong decryption method"); + is_key_correct = true; } } + + if (is_key_correct) { + PrintAndLogEx(SUCCESS, "Auth1 done with correct key material!"); + PrintAndLogEx(SUCCESS, "Use Auth2 now with M3C and same key"); + } else { + PrintAndLogEx(INFO, "3DES secret (swapped decryption): %s", sprint_hex(rev_master_key, 16)); + PrintAndLogEx(INFO, "P2c: %s", sprint_hex(p2c, 8)); + PrintAndLogEx(ERR, "Can't decrypt M2C with master secret (P1c != P2c)!"); + PrintAndLogEx(ERR, "Probably wrong keys or wrong decryption method"); + } } return PM3_SUCCESS; } @@ -732,112 +701,158 @@ static int CmdHFFelicaAuthentication1(const char *Cmd) { * @return client result code. */ static int CmdHFFelicaAuthentication2(const char *Cmd) { - if (strlen(Cmd) < 2) { - return usage_hf_felica_authentication2(); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica auth2", + "Complete mutual authentication. This command can only be executed subsquent to Auth1\n" + _RED_("INCOMPLETE / EXPERIMENTAL COMMAND!!!\n") + _RED_("EXPERIMENTAL COMMAND - M2c/P2c will be not checked"), + "hf felica auth2 --cc 0102030405060708 --key AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n" + "hf felica auth2 -i 11100910C11BC407 --cc 0102030405060708 --key AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n" + ); + void *argtable[] = { + arg_param_begin, + 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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8] = {0}; + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } - PrintAndLogEx(INFO, "INCOMPLETE / EXPERIMENTAL COMMAND!!!"); - PrintAndLogEx(INFO, "EXPERIMENTAL COMMAND - M2c/P2c will be not checked"); + + uint8_t cc[1] = {0}; + int cclen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), cc, sizeof(cc), &cclen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t key[16] = {0}; + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + if (verbose) { + print_authentication2(); + return PM3_SUCCESS; + } + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + bool custom_IDm = false; - strip_cmds(Cmd); + + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, 8); + } + uint16_t datalen = 18; // Length (1), Command ID (1), IDm (8), M4c (8) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_authentication2(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - default: - return usage_hf_felica_authentication1(); - } - } - i++; - } - - data[0] = int_to_hex(&datalen); + data[0] = (datalen & 0xFF); data[1] = 0x12; // Command ID - if (!custom_IDm && !check_last_idm(data, datalen)) { + + if (custom_IDm == false && check_last_idm(data, datalen) == false) { return PM3_EINVARG; } - // M3c (8) - unsigned char m3c[8]; - if (add_param(Cmd, paramCount, m3c, 0, 16)) { - paramCount++; - } else { + if (cclen) { + memcpy(data + 16, cc, cclen); + } + + if (keylen) { + memcpy(data + 16, key, keylen); + } + + + if (custom_IDm == false && check_last_idm(data, datalen) == false) { return PM3_EINVARG; } - // Create M4c challenge response with 3DES - uint8_t master_key[PM3_CMD_DATA_SIZE]; - uint8_t reverse_master_key[PM3_CMD_DATA_SIZE]; - mbedtls_des3_context des3_ctx; - mbedtls_des3_init(&des3_ctx); - unsigned char p3c[8]; - if (param_getlength(Cmd, paramCount) == 32) { + // M3c (8) == cc +// unsigned char m3c[8]; == cc + + + mbedtls_des3_context des3_ctx_enc; + mbedtls_des3_context des3_ctx_dec; + + mbedtls_des3_init(&des3_ctx_enc); + mbedtls_des3_init(&des3_ctx_dec); + + if (keylen == 16) { + + // set encryption context + mbedtls_des3_set2key_enc(&des3_ctx_enc, key); + + // Create M4c challenge response with 3DES + uint8_t rev_key[16]; + reverse_3des_key(key, sizeof(key), rev_key); + + // set decryption context + mbedtls_des3_set2key_dec(&des3_ctx_dec, rev_key); - if (param_gethex(Cmd, paramCount, master_key, 32) == 1) { - PrintAndLogEx(ERR, "Failed param key"); - return PM3_EINVARG; - } - reverse_3des_key(master_key, 16, reverse_master_key); - mbedtls_des3_set2key_dec(&des3_ctx, reverse_master_key); - mbedtls_des3_set2key_enc(&des3_ctx, master_key); // Assumption: Key swap method used for E2 - PrintAndLogEx(INFO, "3DES Master Secret (encryption): %s", sprint_hex(master_key, 16)); - PrintAndLogEx(INFO, "3DES Master Secret (decryption): %s", sprint_hex(reverse_master_key, 16)); + PrintAndLogEx(INFO, "3DES Master Secret (encryption)... %s", sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "3DES Master Secret (decryption)... %s", sprint_hex_inrow(rev_key, sizeof(rev_key))); } else { PrintAndLogEx(ERR, "Invalid key length"); + mbedtls_des3_free(&des3_ctx_enc); + mbedtls_des3_free(&des3_ctx_dec); return PM3_EINVARG; } + // Decrypt m3c with reverse_master_key - mbedtls_des3_crypt_ecb(&des3_ctx, m3c, p3c); - PrintAndLogEx(INFO, "3DES decrypted M3c = P3c: %s", sprint_hex(p3c, 8)); + unsigned char p3c[8]; + mbedtls_des3_crypt_ecb(&des3_ctx_dec, cc, p3c); + PrintAndLogEx(INFO, "3DES decrypted M3c = P3c... %s", sprint_hex_inrow(p3c, sizeof(p3c))); + // Encrypt p3c with master_key unsigned char m4c[8]; - mbedtls_des3_crypt_ecb(&des3_ctx, p3c, m4c); - PrintAndLogEx(INFO, "3DES encrypted M4c: %s", sprint_hex(m4c, 8)); + mbedtls_des3_crypt_ecb(&des3_ctx_enc, p3c, m4c); + PrintAndLogEx(INFO, "3DES encrypted M4c......... %s", sprint_hex_inrow(m4c, sizeof(m4c))); + + // free contexts + mbedtls_des3_free(&des3_ctx_enc); + mbedtls_des3_free(&des3_ctx_dec); // Add M4c Challenge to frame - int frame_position = 10; - for (i = 0; i < 8; i++) { - data[frame_position++] = m4c[i]; - } + memcpy(data + 10, m4c, sizeof(m4c)); AddCrc(data, datalen); datalen += 2; - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); PrintAndLogEx(INFO, "Client Send AUTH2 Frame: %s", sprint_hex(data, datalen)); clear_and_send_command(flags, datalen, data, 0); PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, 1)) { - PrintAndLogEx(ERR, "\nGot no Response from card"); + if (waitCmdFelica(0, &resp, 1) == false) { + PrintAndLogEx(ERR, "no response from card"); return PM3_ERFTRANS; + } + + felica_auth2_response_t auth2_response; + memcpy(&auth2_response, (felica_auth2_response_t *)resp.data.asBytes, sizeof(felica_auth2_response_t)); + if (auth2_response.code[0] != 0x12) { + PrintAndLogEx(SUCCESS, "Auth2 response:"); + PrintAndLogEx(SUCCESS, "IDtc.............. %s", sprint_hex(auth2_response.IDtc, sizeof(auth2_response.IDtc))); + PrintAndLogEx(SUCCESS, "IDi (encrypted)... %s", sprint_hex(auth2_response.IDi, sizeof(auth2_response.IDi))); + PrintAndLogEx(SUCCESS, "PMi (encrypted)... %s", sprint_hex(auth2_response.PMi, sizeof(auth2_response.PMi))); } else { - felica_auth2_response_t auth2_response; - memcpy(&auth2_response, (felica_auth2_response_t *)resp.data.asBytes, sizeof(felica_auth2_response_t)); - if (auth2_response.code[0] != 0x12) { - PrintAndLogEx(SUCCESS, "\nGot auth2 response:"); - PrintAndLogEx(SUCCESS, "IDtc: %s", sprint_hex(auth2_response.IDtc, sizeof(auth2_response.IDtc))); - PrintAndLogEx(SUCCESS, "IDi (encrypted): %s", sprint_hex(auth2_response.IDi, sizeof(auth2_response.IDi))); - PrintAndLogEx(SUCCESS, "PMi (encrypted): %s", sprint_hex(auth2_response.PMi, sizeof(auth2_response.PMi))); - } else { - PrintAndLogEx(ERR, "\nGot wrong frame format."); - } + PrintAndLogEx(ERR, "Got wrong frame format"); } return PM3_SUCCESS; } @@ -848,66 +863,162 @@ static int CmdHFFelicaAuthentication2(const char *Cmd) { * @param Cmd input data of the user. * @return client result code. */ -static int CmdHFFelicaWriteWithoutEncryption(const char *Cmd) { - if (strlen(Cmd) < 5) - return usage_hf_felica_write_without_encryption(); - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - strip_cmds(Cmd); - uint16_t datalen = 32; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3), Block Data(16) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_write_without_encryption(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - default: - return usage_hf_felica_write_without_encryption(); - } - } - i++; - } - data[0] = 0x20; // Static length - data[1] = 0x08; // Command ID - if (!custom_IDm && !check_last_idm(data, datalen)) { +static int CmdHFFelicaWritePlain(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica wrbl", + "Use this command to write block data to authentication-not-required Service.\n\n" + " - Mode shall be Mode0.\n" + " - Un-/Ssuccessful == Status Flag1 and Flag2", + "hf felica wrbl --sn 01 --scl CB10 --bn 01 --ble 8001 -d 0102030405060708090A0B0C0D0E0F10\n" + "hf felica wrbl -i 01100910c11bc407 --sn 01 --scl CB10 --bn 01 --ble 8001 -d 0102030405060708090A0B0C0D0E0F10\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("d", "data", "", "data, 16 hex bytes"), + arg_str0("i", NULL, "", "set custom IDm"), + arg_str0(NULL, "sn", "", "number of service"), + 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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t userdata[16] = {0}; + int udlen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), userdata, sizeof(userdata), &udlen); + if (res) { + CLIParserFree(ctx); return PM3_EINVARG; } - // Number of Service 2, Service Code List 4, Number of Block 2, Block List Element 4, Data 16 - uint8_t lengths[] = {2, 4, 2, 4, 32}; - uint8_t dataPositions[] = {10, 11, 13, 14, 16}; - for (i = 0; i < 5; i++) { - if (add_param(Cmd, paramCount, data, dataPositions[i], lengths[i])) { - paramCount++; - } else { - return PM3_EINVARG; - } + + uint8_t idm[8] = {0}; + int ilen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; + + uint8_t sn[1] = {0}; + int snlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), sn, sizeof(sn), &snlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t scl[2] = {0}; + int scllen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 4), scl, sizeof(scl), &scllen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t bn[1] = {0}; + int bnlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 5), bn, sizeof(bn), &bnlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t ble[3] = {0}; + int blelen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 6), ble, sizeof(ble), &blelen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + if (verbose) { + print_number_of_service_constraints(); + print_number_of_block_constraints(); + print_service_code_list_constraints(); + print_block_list_element_constraints(); + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + return PM3_SUCCESS; + } + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + data[0] = 0x20; // Static length + data[1] = 0x08; // Command ID + + bool custom_IDm = false; + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, sizeof(idm)); + } + + // Length (1) + // Command ID (1) + // IDm (8) + // Number of Service (1) + // Service Code List(2) + // Number of Block(1) + // Block List(3) + // Block Data(16) + + uint16_t datalen = 32; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3), Block Data(16) + if (custom_IDm == false && check_last_idm(data, datalen) == false) { + return PM3_EINVARG; + } + + if (blelen == 3) { + datalen++; + } + + // Number of Service 1, Service Code List 2, Number of Block 1, Block List Element 2, Data 16 + + // Service Number 1 byte + if (snlen) { + data[10] = sn[0]; + } + // Service Code List 2 bytes + if (scllen) { + data[11] = scl[0]; + data[12] = scl[1]; + } + + // Block number 1 byte + if (bnlen) { + data[13] = bn[0]; + } + + // Block List Element 2|3 bytes + if (blelen) { + memcpy(data + 14, ble, blelen); + } + + // data to be written, 16 bytes + if (udlen) { + memcpy(data + 14 + blelen, userdata, sizeof(userdata)); + } + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); AddCrc(data, datalen); datalen += 2; + felica_status_response_t wr_noCry_resp; - if (send_wr_unencrypted(flags, datalen, data, 1, &wr_noCry_resp) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nIDm: %s", sprint_hex(wr_noCry_resp.frame_response.IDm, sizeof(wr_noCry_resp.frame_response.IDm))); - PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(wr_noCry_resp.status_flags.status_flag1, sizeof(wr_noCry_resp.status_flags.status_flag1))); - PrintAndLogEx(SUCCESS, "Status Flag2: %s\n", sprint_hex(wr_noCry_resp.status_flags.status_flag2, sizeof(wr_noCry_resp.status_flags.status_flag2))); + if (send_wr_plain(flags, datalen, data, 1, &wr_noCry_resp) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "IDm............ %s", sprint_hex(wr_noCry_resp.frame_response.IDm, sizeof(wr_noCry_resp.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1... %s", sprint_hex(wr_noCry_resp.status_flags.status_flag1, sizeof(wr_noCry_resp.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2... %s\n", sprint_hex(wr_noCry_resp.status_flags.status_flag2, sizeof(wr_noCry_resp.status_flags.status_flag2))); if (wr_noCry_resp.status_flags.status_flag1[0] == 0x00 && wr_noCry_resp.status_flags.status_flag2[0] == 0x00) { - PrintAndLogEx(SUCCESS, "Writing data successful!\n"); + PrintAndLogEx(SUCCESS, "Writing data successful!"); } else { - PrintAndLogEx(ERR, "Something went wrong! Check status flags.\n"); + PrintAndLogEx(FAILED, "Something went wrong! Check status flags."); } } + return PM3_SUCCESS; } @@ -916,82 +1027,142 @@ static int CmdHFFelicaWriteWithoutEncryption(const char *Cmd) { * @param Cmd input data of the user. * @return client result code. */ -static int CmdHFFelicaReadWithoutEncryption(const char *Cmd) { - if (strlen(Cmd) < 4) - return usage_hf_felica_read_without_encryption(); - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - strip_cmds(Cmd); - uint16_t datalen = 16; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3) - uint8_t paramCount = 0; - uint8_t flags = 0; - uint8_t all_block_list_elements = false; - uint8_t long_block_numbers = false; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_read_without_encryption(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - case 'b': - paramCount++; - all_block_list_elements = true; - break; - case 'l': - paramCount++; - long_block_numbers = true; - break; - default: - return usage_hf_felica_read_without_encryption(); - } - } - i++; - } - data[0] = 0x10; // Static length - data[1] = 0x06; // Command ID - if (!custom_IDm && !check_last_idm(data, datalen)) { +static int CmdHFFelicaReadPlain(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica rdbl", + "Use this command to read block data from authentication-not-required Service.\n\n" + " - Mode shall be Mode0.\n" + " - Successful == block data\n" + " - Unsuccessful == Status Flag1 and Flag2", + "hf felica rdbl --sn 01 --scl 8B00 --bn 01 --ble 8000\n" + "hf felica rdbl --sn 01 --scl 4B18 --bn 01 --ble 8000 -b\n" + "hf felica rdbl -i 01100910c11bc407 --sn 01 --scl 8B00 --bn 01 --ble 8000\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("b", NULL, "get all block list elements 00 -> FF"), + arg_str0("i", NULL, "", "set custom IDm"), + arg_lit0("l", "long", "use 3 byte block list element block number"), + arg_str0(NULL, "sn", "", "number of service"), + 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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool all_block_list_elements = arg_get_lit(ctx, 1); + + uint8_t idm[8] = {0}; + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); return PM3_EINVARG; } - // Number of Service 2, Service Code List 4, Number of Block 2, Block List Element 4 - uint8_t lengths[] = {2, 4, 2, 4}; - uint8_t dataPositions[] = {10, 11, 13, 14}; - if (long_block_numbers) { - datalen += 1; - lengths[3] = 6; - } - for (i = 0; i < 4; i++) { - if (add_param(Cmd, paramCount, data, dataPositions[i], lengths[i])) { - paramCount++; - } else { - return PM3_EINVARG; - } + + uint8_t long_block_numbers = arg_get_lit(ctx, 3); + + uint8_t sn[1] = {0}; + int snlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 4), sn, sizeof(sn), &snlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; + uint8_t scl[2] = {0}; + int scllen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 5), scl, sizeof(scl), &scllen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t bn[1] = {0}; + int bnlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 6), bn, sizeof(bn), &bnlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t ble[3] = {0}; + int blelen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 7), ble, sizeof(ble), &blelen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(ctx, 8); + CLIParserFree(ctx); + if (verbose) { + print_number_of_service_constraints(); + print_number_of_block_constraints(); + print_service_code_list_constraints(); + print_block_list_element_constraints(); + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + return PM3_SUCCESS; + } + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + data[0] = 0x10; // Static length + data[1] = 0x06; // Command ID + + bool custom_IDm = false; + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, sizeof(idm)); + } + + uint16_t datalen = 16; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3) + if (custom_IDm == false && check_last_idm(data, datalen) == false) { + return PM3_EINVARG; + } + + if (long_block_numbers) { + datalen++; + } + + // Number of Service 1, Service Code List 2, Number of Block 1, Block List Element 2|3 + if (snlen) { + data[10] = sn[0]; + } + if (scllen) { + data[11] = scl[0]; + data[12] = scl[1]; + } + if (bnlen) { + data[13] = bn[0]; + } + if (blelen) + memcpy(data + 14, ble, blelen); + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); + + PrintAndLogEx(INFO, "block | data "); + PrintAndLogEx(INFO, "------+----------------------------------------"); + + // main loop block reads if (all_block_list_elements) { - uint16_t last_block_number = 0xFF; + + uint16_t last_blockno = 0xFF; if (long_block_numbers) { - last_block_number = 0xFFFF; + last_blockno = 0xFFFF; } - PrintAndLogEx(INFO, "Block Nr.\t| Data "); - for (i = 0x00; i < last_block_number; i++) { + + for (uint16_t i = 0x00; i < last_blockno; i++) { data[15] = i; AddCrc(data, datalen); datalen += 2; felica_read_without_encryption_response_t rd_noCry_resp; - if ((send_rd_unencrypted(flags, datalen, data, 0, &rd_noCry_resp) == PM3_SUCCESS)) { - if (rd_noCry_resp.status_flags.status_flag1[0] == 00 && rd_noCry_resp.status_flags.status_flag2[0] == 00) { - print_rd_noEncrpytion_response(&rd_noCry_resp); + if ((send_rd_plain(flags, datalen, data, 0, &rd_noCry_resp) == PM3_SUCCESS)) { + if (rd_noCry_resp.status_flags.status_flag1[0] == 0 && rd_noCry_resp.status_flags.status_flag2[0] == 0) { + print_rd_plain_response(&rd_noCry_resp); } } else { break; @@ -1002,9 +1173,8 @@ static int CmdHFFelicaReadWithoutEncryption(const char *Cmd) { AddCrc(data, datalen); datalen += 2; felica_read_without_encryption_response_t rd_noCry_resp; - if (send_rd_unencrypted(flags, datalen, data, 1, &rd_noCry_resp) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Block Nr.\t| Data "); - print_rd_noEncrpytion_response(&rd_noCry_resp); + if (send_rd_plain(flags, datalen, data, 1, &rd_noCry_resp) == PM3_SUCCESS) { + print_rd_plain_response(&rd_noCry_resp); } } return PM3_SUCCESS; @@ -1016,139 +1186,178 @@ static int CmdHFFelicaReadWithoutEncryption(const char *Cmd) { * @return client result code. */ static int CmdHFFelicaRequestResponse(const char *Cmd) { - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - strip_cmds(Cmd); - uint16_t datalen = 10; // Length (1), Command ID (1), IDm (8) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_request_response(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - default: - return usage_hf_felica_request_response(); - } - } - i++; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica rqresponse", + "Use this command to verify the existence of a card and its Mode.\n" + " - current mode of the card is returned", + "hf felica rqresponse -i 11100910C11BC407\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("i", NULL, "", "set custom IDm"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8] = {0}; + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } + + CLIParserFree(ctx); + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); data[0] = 0x0A; // Static length data[1] = 0x04; // Command ID + + bool custom_IDm = false; + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, sizeof(idm)); + } + + uint8_t datalen = 10; // Length (1), Command ID (1), IDm (8) if (!custom_IDm && !check_last_idm(data, datalen)) { return PM3_EINVARG; } + AddCrc(data, datalen); datalen += 2; - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; + + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); clear_and_send_command(flags, datalen, data, 0); + PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, 1)) { - PrintAndLogEx(ERR, "\nGot no response from card"); + if (waitCmdFelica(0, &resp, 1) == false) { + PrintAndLogEx(ERR, "Got no response from card"); return PM3_ERFTRANS; - } else { - felica_request_request_response_t rq_response; - memcpy(&rq_response, (felica_request_request_response_t *)resp.data.asBytes, sizeof(felica_request_request_response_t)); - if (rq_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "\nGot Request Response:"); - PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rq_response.frame_response.IDm, sizeof(rq_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, " -Mode: %s\n\n", sprint_hex(rq_response.mode, sizeof(rq_response.mode))); - } } + + felica_request_request_response_t rq_response; + memcpy(&rq_response, (felica_request_request_response_t *)resp.data.asBytes, sizeof(felica_request_request_response_t)); + if (rq_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "Request Response"); + PrintAndLogEx(SUCCESS, "IDm...... %s", sprint_hex(rq_response.frame_response.IDm, sizeof(rq_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, " Mode... %s", sprint_hex(rq_response.mode, sizeof(rq_response.mode))); + } + return PM3_SUCCESS; } - /** * Command parser for rqspecver * @param Cmd input data of the user. * @return client result code. */ static int CmdHFFelicaRequestSpecificationVersion(const char *Cmd) { - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - bool custom_reserve = false; - strip_cmds(Cmd); - uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_request_specification_version(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - case 'r': - paramCount++; - custom_reserve = true; - if (!add_param(Cmd, paramCount, data, 10, 4)) { - return PM3_EINVARG; - } - paramCount++; - i += 4; - break; - default: - return usage_hf_felica_request_specification_version(); - } - } - i++; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica rqspecver", + "Use this command to acquire the version of card OS.\n" + "Response:\n" + " - Format version: Fixed value 00h. Provided only if Status Flag1 = 00h\n" + " - Basic version: Each value of version is expressed in BCD notation. Provided only if Status Flag1 = 00h\n" + " - Number of Option: value = 0: AES card, value = 1: AES/DES card. Provided only if Status Flag1 = 00h\n" + " - Option version list: Provided only if Status Flag1 = 00h\n" + " - AES card: not added\n" + " - AES/DES card: DES option version is added - BCD notation", + + "hf felica rqspecver\n" + "hf felica rqspecver -r 0001\n" + "hf felica rqspecver -i 11100910C11BC407 \n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("i", NULL, "", "set custom IDm"), + arg_str0("r", NULL, "", "set custom reserve"), + arg_lit0("v", "verbose", "verbose helptext"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8] = {0}; + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } + + uint8_t reserved[2] = {0, 0}; + int rlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), reserved, sizeof(reserved), &rlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(ctx, 3); + if (verbose) { + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + } + CLIParserFree(ctx); + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); data[0] = 0x0C; // Static length data[1] = 0x3C; // Command ID - if (!custom_reserve) { + + bool custom_IDm = false; + + // add custom idm + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, sizeof(idm)); + } + + // add custom reserved + if (rlen) { + memcpy(data + 10, reserved, sizeof(reserved)); + } else { data[10] = 0x00; // Reserved Value data[11] = 0x00; // Reserved Value } - if (!custom_IDm && !check_last_idm(data, datalen)) { + + uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) + if (custom_IDm == false && check_last_idm(data, datalen) == false) { return PM3_EINVARG; } + AddCrc(data, datalen); datalen += 2; - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; - clear_and_send_command(flags, datalen, data, 0); - PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, 1)) { - PrintAndLogEx(ERR, "\nGot no response from card"); - return PM3_ERFTRANS; - } else { - felica_request_spec_response_t spec_response; - memcpy(&spec_response, (felica_request_spec_response_t *)resp.data.asBytes, sizeof(felica_request_spec_response_t)); + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); - if (spec_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "\nGot Request Response:"); - PrintAndLogEx(SUCCESS, "\nIDm: %s", sprint_hex(spec_response.frame_response.IDm, sizeof(spec_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(spec_response.status_flags.status_flag1, sizeof(spec_response.status_flags.status_flag1))); - PrintAndLogEx(SUCCESS, "Status Flag2: %s", sprint_hex(spec_response.status_flags.status_flag2, sizeof(spec_response.status_flags.status_flag2))); - if (spec_response.status_flags.status_flag1[0] == 0x00) { - PrintAndLogEx(SUCCESS, "Format Version: %s", sprint_hex(spec_response.format_version, sizeof(spec_response.format_version))); - PrintAndLogEx(SUCCESS, "Basic Version: %s", sprint_hex(spec_response.basic_version, sizeof(spec_response.basic_version))); - PrintAndLogEx(SUCCESS, "Number of Option: %s", sprint_hex(spec_response.number_of_option, sizeof(spec_response.number_of_option))); - if (spec_response.number_of_option[0] == 0x01) { - PrintAndLogEx(SUCCESS, "Option Version List:"); - for (i = 0; i < spec_response.number_of_option[0]; i++) { - PrintAndLogEx(SUCCESS, " - %s", sprint_hex(spec_response.option_version_list + i * 2, sizeof(uint8_t) * 2)); - } + clear_and_send_command(flags, datalen, data, 0); + + PacketResponseNG resp; + if (waitCmdFelica(0, &resp, 1) == false) { + PrintAndLogEx(FAILED, "Got no response from card"); + return PM3_ERFTRANS; + } + + felica_request_spec_response_t spec_response; + memcpy(&spec_response, (felica_request_spec_response_t *)resp.data.asBytes, sizeof(felica_request_spec_response_t)); + + if (spec_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "Got Request Response"); + PrintAndLogEx(SUCCESS, "IDm............ %s", sprint_hex(spec_response.frame_response.IDm, sizeof(spec_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1... %s", sprint_hex(spec_response.status_flags.status_flag1, sizeof(spec_response.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2... %s", sprint_hex(spec_response.status_flags.status_flag2, sizeof(spec_response.status_flags.status_flag2))); + + if (spec_response.status_flags.status_flag1[0] == 0) { + PrintAndLogEx(SUCCESS, "Format Version..... %s", sprint_hex(spec_response.format_version, sizeof(spec_response.format_version))); + PrintAndLogEx(SUCCESS, "Basic Version...... %s", sprint_hex(spec_response.basic_version, sizeof(spec_response.basic_version))); + PrintAndLogEx(SUCCESS, "Number of Option... %s", sprint_hex(spec_response.number_of_option, sizeof(spec_response.number_of_option))); + if (spec_response.number_of_option[0] == 1) { + PrintAndLogEx(SUCCESS, "Option Version List..."); + for (int i = 0; i < spec_response.number_of_option[0]; i++) { + PrintAndLogEx(SUCCESS, " - %s", sprint_hex(spec_response.option_version_list + i * 2, 2)); } } } @@ -1156,77 +1365,93 @@ static int CmdHFFelicaRequestSpecificationVersion(const char *Cmd) { return PM3_SUCCESS; } - /** * Command parser for resetmode * @param Cmd input data of the user. * @return client result code. */ static int CmdHFFelicaResetMode(const char *Cmd) { - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - bool custom_reserve = false; - strip_cmds(Cmd); - uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_reset_mode(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - case 'r': - paramCount++; - custom_reserve = true; - if (!add_param(Cmd, paramCount, data, 10, 4)) { - return PM3_EINVARG; - } - paramCount++; - i += 4; - break; - default: - return usage_hf_felica_reset_mode(); - } - } - i++; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica resetmode", + "Use this command to reset Mode to Mode 0.", + "hf felica resetmode\n" + "hf felica resetmode -r 0001\n" + "hf felica resetmode -i 11100910C11BC407 \n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("i", NULL, "", "set custom IDm"), + arg_str0("r", NULL, "", "set custom reserve"), + arg_lit0("v", "verbose", "verbose helptext"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8] = {0}; + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } + + uint8_t reserved[2] = {0, 0}; + int rlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), reserved, sizeof(reserved), &rlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool verbose = arg_get_lit(ctx, 3); + if (verbose) { + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + } + CLIParserFree(ctx); + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); data[0] = 0x0C; // Static length data[1] = 0x3E; // Command ID - if (!custom_reserve) { + + bool custom_IDm = false; + + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, 8); + } + if (rlen) { + memcpy(data + 10, reserved, 2); + } else { data[10] = 0x00; // Reserved Value data[11] = 0x00; // Reserved Value } - if (!custom_IDm && !check_last_idm(data, datalen)) { + + uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) + if (custom_IDm == false && check_last_idm(data, datalen) == false) { return PM3_EINVARG; } + AddCrc(data, datalen); datalen += 2; - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); + clear_and_send_command(flags, datalen, data, 0); + PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, 1)) { - PrintAndLogEx(ERR, "\nGot no response from card"); + if (waitCmdFelica(0, &resp, 1) == false) { + PrintAndLogEx(ERR, "Got no response from card"); return PM3_ERFTRANS; - } else { - felica_status_response_t reset_mode_response; - memcpy(&reset_mode_response, (felica_status_response_t *)resp.data.asBytes, sizeof(felica_status_response_t)); - if (reset_mode_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "\nGot Request Response:"); - PrintAndLogEx(SUCCESS, "\nIDm: %s", sprint_hex(reset_mode_response.frame_response.IDm, sizeof(reset_mode_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(reset_mode_response.status_flags.status_flag1, sizeof(reset_mode_response.status_flags.status_flag1))); - PrintAndLogEx(SUCCESS, "Status Flag2: %s\n", sprint_hex(reset_mode_response.status_flags.status_flag2, sizeof(reset_mode_response.status_flags.status_flag2))); - } + } + + felica_status_response_t reset_mode_response; + memcpy(&reset_mode_response, (felica_status_response_t *)resp.data.asBytes, sizeof(felica_status_response_t)); + if (reset_mode_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "Request Response"); + PrintAndLogEx(SUCCESS, "IDm............ %s", sprint_hex(reset_mode_response.frame_response.IDm, sizeof(reset_mode_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1... %s", sprint_hex(reset_mode_response.status_flags.status_flag1, sizeof(reset_mode_response.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2... %s", sprint_hex(reset_mode_response.status_flags.status_flag2, sizeof(reset_mode_response.status_flags.status_flag2))); } return PM3_SUCCESS; } @@ -1237,61 +1462,74 @@ static int CmdHFFelicaResetMode(const char *Cmd) { * @return client result code. */ static int CmdHFFelicaRequestSystemCode(const char *Cmd) { - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - strip_cmds(Cmd); - uint16_t datalen = 10; // Length (1), Command ID (1), IDm (8) - uint8_t paramCount = 0; - uint8_t flags = 0; - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_request_system_code(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - default: - return usage_hf_felica_request_system_code(); - } - } - i++; - } - data[0] = 0x0A; // Static length - data[1] = 0x0C; // Command ID - if (!custom_IDm && !check_last_idm(data, datalen)) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica rqsyscode", + "Use this command to acquire System Code registered to the card." + " - if a card is divided into more than one System, \n" + " this command acquires System Code of each System existing in the card.", + "hf felica rqsyscode\n" + "hf felica rqsyscode -i 11100910C11BC407 \n" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("i", NULL, "", "set custom IDm"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t idm[8] = {0}; + int ilen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); return PM3_EINVARG; } + CLIParserFree(ctx); + + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + data[0] = 0x0A; // Static length + data[1] = 0x0C; // Command ID + + bool custom_IDm = false; + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, sizeof(idm)); + } + + uint16_t datalen = 10; // Length (1), Command ID (1), IDm (8) + if (custom_IDm == false && check_last_idm(data, datalen) == false) { + return PM3_EINVARG; + } + AddCrc(data, datalen); datalen += 2; - flags |= FELICA_APPEND_CRC; - flags |= FELICA_RAW; - clear_and_send_command(flags, datalen, data, 0); - PacketResponseNG resp; - if (!waitCmdFelica(0, &resp, 1)) { - PrintAndLogEx(ERR, "\nGot no response from card"); - return PM3_ERFTRANS; - } else { - felica_syscode_response_t rq_syscode_response; - memcpy(&rq_syscode_response, (felica_syscode_response_t *)resp.data.asBytes, sizeof(felica_syscode_response_t)); + uint8_t flags = (FELICA_APPEND_CRC | FELICA_RAW); - if (rq_syscode_response.frame_response.IDm[0] != 0) { - PrintAndLogEx(SUCCESS, "\nGot Request Response:"); - PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rq_syscode_response.frame_response.IDm, sizeof(rq_syscode_response.frame_response.IDm))); - PrintAndLogEx(SUCCESS, " - Number of Systems: %s", sprint_hex(rq_syscode_response.number_of_systems, sizeof(rq_syscode_response.number_of_systems))); - PrintAndLogEx(SUCCESS, " - System Codes: enumerated in ascending order starting from System 0."); - for (i = 0; i < rq_syscode_response.number_of_systems[0]; i++) { - PrintAndLogEx(SUCCESS, " - %s", sprint_hex(rq_syscode_response.system_code_list + i * 2, sizeof(uint8_t) * 2)); - } + clear_and_send_command(flags, datalen, data, 0); + + PacketResponseNG resp; + if (waitCmdFelica(0, &resp, true) == false) { + PrintAndLogEx(ERR, "Got no response from card"); + return PM3_ERFTRANS; + } + + felica_syscode_response_t rq_syscode_response; + memcpy(&rq_syscode_response, (felica_syscode_response_t *)resp.data.asBytes, sizeof(felica_syscode_response_t)); + + if (rq_syscode_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "Request Response"); + PrintAndLogEx(SUCCESS, "IDm... %s", sprint_hex(rq_syscode_response.frame_response.IDm, sizeof(rq_syscode_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, " - Number of Systems: %s", sprint_hex(rq_syscode_response.number_of_systems, sizeof(rq_syscode_response.number_of_systems))); + PrintAndLogEx(SUCCESS, " - System Codes: enumerated in ascending order starting from System 0."); + + for (int i = 0; i < rq_syscode_response.number_of_systems[0]; i++) { + PrintAndLogEx(SUCCESS, " - %s", sprint_hex(rq_syscode_response.system_code_list + i * 2, 2)); } } + return PM3_SUCCESS; } @@ -1301,68 +1539,81 @@ static int CmdHFFelicaRequestSystemCode(const char *Cmd) { * @return client result code. */ static int CmdHFFelicaRequestService(const char *Cmd) { - if (strlen(Cmd) < 2) return usage_hf_felica_request_service(); - int i = 0; - uint8_t data[PM3_CMD_DATA_SIZE]; - bool custom_IDm = false; - bool all_nodes = false; - uint16_t datalen = 13; // length (1) + CMD (1) + IDm(8) + Node Number (1) + Node Code List (2) - uint8_t flags = 0; - uint8_t paramCount = 0; - strip_cmds(Cmd); - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_request_service(); - case 'i': - paramCount++; - custom_IDm = true; - if (!add_param(Cmd, paramCount, data, 2, 16)) { - return PM3_EINVARG; - } - paramCount++; - i += 16; - break; - case 'a': - paramCount++; - all_nodes = true; - break; - default: - return usage_hf_felica_request_service(); - } - i += 2; - } - i++; - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica rqservice", + "Use this command to verify the existence of Area and Service, and to acquire Key Version:\n" + " - When the specified Area or Service exists, the card returns Key Version.\n" + " - When the specified Area or Service does not exist, the card returns FFFFh as Key Version.\n" + "For Node Code List of a command packet, Area Code or Service Code of the target\n" + "of acquisition of Key Version shall be enumerated in Little Endian format.\n" + "If Key Version of System is the target of acquisition, FFFFh shall be specified\n" + "in the command packet.", + "hf felcia rqservice --node 01 --code FFFF\n" + "hf felcia rqservice -a --code FFFF\n" + "hf felica rqservice -i 011204126417E405 --node 01 --code FFFF" + ); - if (!all_nodes) { - // Node Number - if (param_getlength(Cmd, paramCount) == 2) { + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "all", "auto node number mode, iterates through all nodes 1 < n < 32"), + arg_str0("n", "node", "", "Number of Node"), + arg_str0("c", "code", "", "Node Code List (little endian)"), + arg_str0("i", "idm", "", "use custom IDm"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool all_nodes = arg_get_lit(ctx, 1); - if (param_gethex(Cmd, paramCount++, data + 10, 2) == 1) { - PrintAndLogEx(ERR, "Failed param key"); - return PM3_EINVARG; - } - - } else { - PrintAndLogEx(ERR, "Incorrect Node number length!"); - return PM3_EINVARG; - } - } - - if (param_getlength(Cmd, paramCount) == 4) { - - if (param_gethex(Cmd, paramCount++, data + 11, 4) == 1) { - PrintAndLogEx(ERR, "Failed param key"); - return PM3_EINVARG; - } - } else { - PrintAndLogEx(ERR, "Incorrect parameter length!"); + uint8_t node[1] = {0}; + int nlen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), node, sizeof(node), &nlen); + if (res) { + CLIParserFree(ctx); return PM3_EINVARG; } - flags |= FELICA_APPEND_CRC; + uint8_t code[2] = {0, 0}; + int clen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), code, sizeof(code), &clen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t idm[8] = {0}; + int ilen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 4), idm, sizeof(idm), &ilen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + uint8_t data[PM3_CMD_DATA_SIZE]; + memset(data, 0, sizeof(data)); + + bool custom_IDm = false; + + if (ilen) { + custom_IDm = true; + memcpy(data + 2, idm, 8); + } + + if (all_nodes == false) { + // Node Number + if (nlen == 1) { + memcpy(data + 10, node, sizeof(node)); + } + + // code + if (clen == 2) { + memcpy(data + 11, code, sizeof(code)); + } + } + + uint8_t datalen = 13; // length (1) + CMD (1) + IDm(8) + Node Number (1) + Node Code List (2) + + uint8_t flags = FELICA_APPEND_CRC; if (custom_IDm) { flags |= FELICA_NO_SELECT; } @@ -1370,102 +1621,154 @@ static int CmdHFFelicaRequestService(const char *Cmd) { if (datalen > 0) { flags |= FELICA_RAW; } - -// Todo activate once datalen isn't hardcoded anymore... -// datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; - if (!custom_IDm && !check_last_idm(data, datalen)) { + // Todo activate once datalen isn't hardcoded anymore... + if (custom_IDm == false && check_last_idm(data, datalen) == false) { return PM3_EINVARG; } - data[0] = int_to_hex(&datalen); + data[0] = (datalen & 0xFF); data[1] = 0x02; // Service Request Command ID if (all_nodes) { - for (uint16_t y = 1; y < 32; y++) { - data[10] = int_to_hex(&y); + + // send 32 calls + for (uint8_t i = 1; i < 32; i++) { + data[10] = i; AddCrc(data, datalen); - datalen += 2; - send_request_service(flags, datalen, data, 1); - datalen -= 2; // Remove CRC bytes before adding new ones + send_request_service(flags, datalen + 2, data, 1); } + } else { AddCrc(data, datalen); - datalen += 2; - send_request_service(flags, datalen, data, 1); + send_request_service(flags, datalen + 2, data, 1); } return PM3_SUCCESS; } static int CmdHFFelicaNotImplementedYet(const char *Cmd) { - PrintAndLogEx(INFO, "Feature not implemented yet."); - PrintAndLogEx(INFO, "Feel free to contribute!"); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica scsvcode", + "Feature not implemented yet. Feel free to contribute!", + "hf felica scsvcode" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIParserFree(ctx); return PM3_SUCCESS; } static int CmdHFFelicaSniff(const char *Cmd) { - uint8_t paramCount = 0; - uint64_t samples2skip = 0; - uint64_t triggers2skip = 0; - strip_cmds(Cmd); - int i = 0; - while (Cmd[i] != '\0') { - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'H': - return usage_hf_felica_sniff(); - case 's': - paramCount++; - if (param_getlength(Cmd, paramCount) < 5) { - samples2skip = param_get32ex(Cmd, paramCount++, 0, 10); - } else { - PrintAndLogEx(ERR, "Invalid samples number!"); - return PM3_EINVARG; - } - break; - case 't': - paramCount++; - if (param_getlength(Cmd, paramCount) < 5) { - triggers2skip = param_get32ex(Cmd, paramCount++, 0, 10); - } else { - PrintAndLogEx(ERR, "Invalid triggers number!"); - return PM3_EINVARG; - } - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, paramCount)); - return usage_hf_felica_sniff(); - } - i += 2; - } - i++; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica sniff", + "Collect data from the field and save into command buffer.\n" + "Buffer accessible from `hf felica list`", + "hf felica sniff\n" + "hf felica sniff -s 10 -t 19" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_0("s", "samples", "", "samples to skip"), + arg_u64_0("t", "trig", "", "triggers to skip "), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + struct p { + uint32_t samples; + uint32_t triggers; + } PACKED payload; + + payload.samples = arg_get_u32_def(ctx, 1, 10); + payload.triggers = arg_get_u32_def(ctx, 2, 5000); + CLIParserFree(ctx); + + if (payload.samples > 9999) { + payload.samples = 9999; + PrintAndLogEx(INFO, "Too large samples to skip value, using max value 9999"); + return PM3_EINVARG; } - if (samples2skip == 0) { - samples2skip = 10; - PrintAndLogEx(INFO, "Set default samples2skip: %" PRIu64, samples2skip); + if (payload.triggers > 9999) { + payload.triggers = 9999; + PrintAndLogEx(INFO, "Too large trigger to skip value, using max value 9999"); + return PM3_EINVARG; } - if (triggers2skip == 0) { - triggers2skip = 5000; - PrintAndLogEx(INFO, "Set default triggers2skip: %" PRIu64, triggers2skip); - } - PrintAndLogEx(INFO, "Start Sniffing now. You can stop sniffing with clicking the PM3 Button"); - PrintAndLogEx(INFO, "During sniffing, other pm3 commands may not response."); + PrintAndLogEx(INFO, "Sniff Felica, getting first %" PRIu32 " frames, skipping after %" PRIu32 " triggers", payload.samples, payload.triggers); + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort sniffing"); clearCommandBuffer(); - SendCommandMIX(CMD_HF_FELICA_SNIFF, samples2skip, triggers2skip, 0, NULL, 0); + SendCommandNG(CMD_HF_FELICA_SNIFF, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + + for (;;) { + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + msleep(300); + break; + } + + if (WaitForResponseTimeout(CMD_HF_FELICA_SNIFF, &resp, 1000)) { + if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(DEBUG, "Button pressed, user aborted"); + break; + } + } + } + + PrintAndLogEx(HINT, "try `" _YELLOW_("hf felica list") "` to view"); + PrintAndLogEx(INFO, "Done"); return PM3_SUCCESS; } // uid hex static int CmdHFFelicaSimLite(const char *Cmd) { - uint64_t uid = param_get64ex(Cmd, 0, 0, 16); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica litesim", + "Emulating ISO/18092 FeliCa Lite tag", + "hf felica litesim -u 1122334455667788" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("u", "uid", "", "UID/NDEF2 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int uid_len = 0; + struct p { + uint8_t uid[8]; + } PACKED payload; + CLIGetHexWithReturn(ctx, 1, payload.uid, &uid_len); + CLIParserFree(ctx); - if (!uid) - return usage_hf_felica_simlite(); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); clearCommandBuffer(); - SendCommandMIX(CMD_HF_FELICALITE_SIMULATE, uid, 0, 0, NULL, 0); + SendCommandNG(CMD_HF_FELICALITE_SIMULATE, payload.uid, sizeof(payload)); + PacketResponseNG resp; + + for (;;) { + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + msleep(300); + break; + } + + if (WaitForResponseTimeout(CMD_HF_FELICALITE_SIMULATE, &resp, 1000)) { + if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(DEBUG, "Button pressed, user aborted"); + break; + } + } + } + + PrintAndLogEx(INFO, "Done"); return PM3_SUCCESS; } @@ -1622,19 +1925,46 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac static int CmdHFFelicaDumpLite(const char *Cmd) { - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_hf_felica_dumplite(); + /* + iceman 2021, + Why does this command say it dumps a FeliCa lite card + and then tries to print a trace?!? + Is this a trace list or a FeliCa dump cmd? + */ + + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica litedump", + "Dump ISO/18092 FeliCa Lite tag. It will timeout after 200sec", + "hf felica litedump" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "FeliCa lite - dump started"); - PrintAndLogEx(SUCCESS, "press pm3-button to cancel"); + clearCommandBuffer(); SendCommandNG(CMD_HF_FELICALITE_DUMP, NULL, 0); PacketResponseNG resp; + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort dumping"); + uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + return PM3_EOPABORTED; + } + timeout++; - PrintAndLogEx(NORMAL, "." NOLF); + PrintAndLogEx(INPLACE, "% 3i", timeout); + fflush(stdout); if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); @@ -1663,7 +1993,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { uint8_t *trace = calloc(tracelen, sizeof(uint8_t)); if (trace == NULL) { - PrintAndLogEx(WARNING, "Cannot allocate memory for trace"); + PrintAndLogEx(WARNING, "failed to allocate memory "); return PM3_EMALLOC; } @@ -1673,8 +2003,8 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { return PM3_ETIMEOUT; } - PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %"PRIu32" bytes)", tracelen); + PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %"PRIu32" bytes)", tracelen); print_hex_break(trace, tracelen, 32); printSep(); @@ -1689,79 +2019,40 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { } static int CmdHFFelicaCmdRaw(const char *Cmd) { - bool reply = 1; - bool crc = false; - bool keep_field_on = false; - bool active = false; - bool active_select = false; - uint16_t numbits = 0; - char buf[5] = ""; - int i = 0; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf felica raw ", + "Send raw hex data to tag", + "hf felica raw -cs 20\n" + "hf felica raw -cs 2008" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", NULL, "active signal field ON without select"), + arg_lit0("c", NULL, "calculate and append CRC"), + arg_lit0("k", NULL, "keep signal field ON after receive"), + arg_u64_0("n", NULL, "", "number of bits"), + arg_lit0("r", NULL, "do not read response"), + arg_lit0("s", NULL, "active signal field ON with select"), + arg_strx1(NULL, NULL, "", "raw bytes to send"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool active = arg_get_lit(ctx, 1); + bool crc = arg_get_lit(ctx, 2); + bool keep_field_on = arg_get_lit(ctx, 3); + uint16_t numbits = arg_get_u32_def(ctx, 4, 0) & 0xFFFF; + bool reply = (arg_get_lit(ctx, 5) == false); + bool active_select = arg_get_lit(ctx, 6); + + int datalen = 0; uint8_t data[PM3_CMD_DATA_SIZE]; - uint16_t datalen = 0; - uint32_t temp; + memset(data, 0, sizeof(data)); - if (strlen(Cmd) < 2) return usage_hf_felica_raw(); - - // strip - while (*Cmd == ' ' || *Cmd == '\t') Cmd++; - - while (Cmd[i] != '\0') { - if (Cmd[i] == ' ' || Cmd[i] == '\t') { i++; continue; } - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_felica_raw(); - case 'r': - reply = false; - break; - case 'c': - crc = true; - break; - case 'k': - keep_field_on = true; - break; - case 'a': - active = true; - break; - case 's': - active_select = true; - break; - case 'b': - sscanf(Cmd + i + 2, "%d", &temp); - numbits = temp & 0xFFFF; - i += 3; - while (Cmd[i] != ' ' && Cmd[i] != '\0') { i++; } - i -= 2; - break; - default: - return usage_hf_felica_raw(); - } - i += 2; - continue; - } - if ((Cmd[i] >= '0' && Cmd[i] <= '9') || - (Cmd[i] >= 'a' && Cmd[i] <= 'f') || - (Cmd[i] >= 'A' && Cmd[i] <= 'F')) { - buf[strlen(buf) + 1] = 0; - buf[strlen(buf)] = Cmd[i]; - i++; - - if (strlen(buf) >= 2) { - sscanf(buf, "%x", &temp); - data[datalen] = (uint8_t)(temp & 0xff); - *buf = 0; - if (++datalen >= (sizeof(data) - 2)) { - if (crc) - PrintAndLogEx(WARNING, "Buffer is full, we can't add CRC to your data"); - break; - } - } - continue; - } - PrintAndLogEx(WARNING, "Invalid char on input"); - return PM3_EINVARG; - } + CLIGetHexWithReturn(ctx, 7, data, &datalen); + CLIParserFree(ctx); if (crc) { AddCrc(data, datalen); @@ -1788,83 +2079,38 @@ static int CmdHFFelicaCmdRaw(const char *Cmd) { clearCommandBuffer(); PrintAndLogEx(SUCCESS, "Data: %s", sprint_hex(data, datalen)); + SendCommandMIX(CMD_HF_FELICA_COMMAND, flags, (datalen & 0xFFFF) | (uint32_t)(numbits << 16), 0, data, datalen); if (reply) { if (active_select) { PrintAndLogEx(SUCCESS, "Active select wait for FeliCa."); PacketResponseNG resp_IDm; - waitCmdFelica(1, &resp_IDm, 1); + if (waitCmdFelica(1, &resp_IDm, true) == false) { + return PM3_ERFTRANS; + } } if (datalen > 0) { PacketResponseNG resp_frame; - waitCmdFelica(0, &resp_frame, 1); - } - } - return PM3_SUCCESS; -} - -int readFelicaUid(bool verbose) { - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - if (verbose) PrintAndLogEx(WARNING, "FeliCa card select failed"); - return PM3_ESOFT; - } - - felica_card_select_t card; - memcpy(&card, (felica_card_select_t *)resp.data.asBytes, sizeof(felica_card_select_t)); - uint64_t status = resp.oldarg[0]; - - switch (status) { - case 1: { - if (verbose) - PrintAndLogEx(WARNING, "card timeout"); - return PM3_ETIMEOUT; - } - case 2: { - if (verbose) - PrintAndLogEx(WARNING, "card answered wrong"); - return PM3_ESOFT; - } - case 3: { - if (verbose) - PrintAndLogEx(WARNING, "CRC check failed"); - return PM3_ESOFT; - } - case 0: { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "FeliCa tag info"); - - PrintAndLogEx(SUCCESS, "IDm %s", sprint_hex(card.IDm, sizeof(card.IDm))); - PrintAndLogEx(SUCCESS, " - CODE %s", sprint_hex(card.code, sizeof(card.code))); - PrintAndLogEx(SUCCESS, " - NFCID2 %s", sprint_hex(card.uid, sizeof(card.uid))); - - PrintAndLogEx(SUCCESS, "Parameter (PAD) | %s", sprint_hex(card.PMm, sizeof(card.PMm))); - PrintAndLogEx(SUCCESS, " - IC CODE %s", sprint_hex(card.iccode, sizeof(card.iccode))); - PrintAndLogEx(SUCCESS, " - MRT %s", sprint_hex(card.mrt, sizeof(card.mrt))); - - PrintAndLogEx(SUCCESS, "SERVICE CODE %s", sprint_hex(card.servicecode, sizeof(card.servicecode))); - set_last_known_card(card); - break; + if (waitCmdFelica(0, &resp_frame, true) == false) { + return PM3_ERFTRANS; + } } } return PM3_SUCCESS; } static command_t CommandTable[] = { - - {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, + {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, {"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"}, + {"info", CmdHFFelicaInfo, IfPm3Felica, "Tag information"}, {"sniff", CmdHFFelicaSniff, IfPm3Felica, "Sniff ISO 18092/FeliCa traffic"}, {"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"}, - {"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from authentication-not-required Service."}, - {"wrunencrypted", CmdHFFelicaWriteWithoutEncryption, IfPm3Felica, "write Block Data to an authentication-not-required Service."}, - {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Standard") " -----------------------"}, + {"rdbl", CmdHFFelicaReadPlain, IfPm3Felica, "read block data from authentication-not-required Service."}, + {"wrbl", CmdHFFelicaWritePlain, IfPm3Felica, "write block data to an authentication-not-required Service."}, + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Standard") " -----------------------"}, //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, @@ -1876,17 +2122,17 @@ static command_t CommandTable[] = { //{"write", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to an authentication-required Service."}, //{"scsvcodev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "verify the existence of Area or Service, and to acquire Key Version."}, //{"getsysstatus", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire the setup information in System."}, - {"rqspecver", CmdHFFelicaRequestSpecificationVersion, IfPm3Felica, "acquire the version of card OS."}, + {"rqspecver", CmdHFFelicaRequestSpecificationVersion, IfPm3Felica, "acquire the version of card OS."}, {"resetmode", CmdHFFelicaResetMode, IfPm3Felica, "reset Mode to Mode 0."}, //{"auth1v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "authenticate a card."}, //{"auth2v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "allow a card to authenticate a Reader/Writer."}, //{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, //{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."}, //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, - {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Light") " -----------------------"}, - {"litesim", CmdHFFelicaSimLite, IfPm3Felica, " - only reply to poll request"}, - {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, - // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("FeliCa Light") " -----------------------"}, + {"litesim", CmdHFFelicaSimLite, IfPm3Felica, "Emulating ISO/18092 FeliCa Lite tag"}, + {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, + // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhffelica.h b/client/src/cmdhffelica.h index e7be7ede1..63f738a2d 100644 --- a/client/src/cmdhffelica.h +++ b/client/src/cmdhffelica.h @@ -15,8 +15,8 @@ #include "iso18.h" int CmdHFFelica(const char *Cmd); -int readFelicaUid(bool verbose); +int read_felica_uid(bool loop, bool verbose); int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose); -int send_rd_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp); +int send_rd_plain(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp); #endif diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index d3ea8e1cb..d6648371d 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -38,17 +38,11 @@ static int CmdHelp(const char *Cmd); -static int cmd_hf_fido_list(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 14a"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); +static int CmdHFFidoList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf fido", "14a"); } -static int cmd_hf_fido_info(const char *Cmd) { +static int CmdHFFidoInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido info", "Get info from Fido tags", @@ -167,7 +161,7 @@ static json_t *OpenJson(CLIParserContext *ctx, int paramnum, char *fname, void * return root; } -static int cmd_hf_fido_register(const char *cmd) { +static int CmdHFFidoRegister(const char *cmd) { uint8_t data[64] = {0}; int chlen = 0; uint8_t cdata[250] = {0}; @@ -176,10 +170,12 @@ static int cmd_hf_fido_register(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido reg", - "Initiate a U2F token registration. Needs two 32-byte hash numbers. \nchallenge parameter (32b) and application parameter (32b).", - "hf fido reg -> execute command with 2 parameters, filled 0x00\n" - "hf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters\n" - "hf fido reg -p s0 s1 -> execute command with plain parameters"); + "Initiate a U2F token registration. Needs two 32-byte hash numbers.\n" + "challenge parameter (32b) and application parameter (32b).", + "hf fido reg -> execute command with 2 parameters, filled 0x00\n" + "hf fido reg -p s0 s1 -> execute command with plain parameters\n" + "hf fido reg --cp 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f --ap 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n" + ); void *argtable[] = { arg_param_begin, @@ -188,8 +184,8 @@ static int cmd_hf_fido_register(const char *cmd) { arg_lit0("p", "plain", "send plain ASCII to challenge and application parameters instead of HEX"), arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"), arg_str0("j", "json", "fido.json", "JSON input / output file name for parameters."), - arg_str0(NULL, NULL, "", NULL), - arg_str0(NULL, NULL, "", NULL), + arg_str0(NULL, "cp", "", "challenge parameter (32 bytes hex / 1..16 chars)"), + arg_str0(NULL, "ap", "", "application parameter (32 bytes hex / 1..16 chars)"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); @@ -408,7 +404,7 @@ static int cmd_hf_fido_register(const char *cmd) { return PM3_SUCCESS; } -static int cmd_hf_fido_authenticate(const char *cmd) { +static int CmdHFFidoAuthenticate(const char *cmd) { uint8_t data[512] = {0}; uint8_t hdata[250] = {0}; bool public_key_loaded = false; @@ -418,10 +414,13 @@ static int cmd_hf_fido_authenticate(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido auth", - "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).", - "hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n" - "hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f " - "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"); + "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers.\n" + "key handle(var 0..255), challenge parameter (32b) and application parameter (32b)", + "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n" + "hf fido auth \n" + "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n" + "--cp 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n" + "--ap 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"); void *argtable[] = { arg_param_begin, @@ -432,10 +431,10 @@ static int cmd_hf_fido_authenticate(const char *cmd) { arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"), arg_lit0("c", "check", "mode: check-only"), arg_str0("j", "json", "fido.json", "JSON input / output file name for parameters."), - arg_str0("k", "key", "public key to verify signature", NULL), - arg_str0(NULL, NULL, "", NULL), - arg_str0(NULL, NULL, "", NULL), - arg_str0(NULL, NULL, "", NULL), + arg_str0("k", "key", "", "public key to verify signature"), + arg_str0(NULL, "kh", "", "key handle (var 0..255b)"), + arg_str0(NULL, "cp", "", "challenge parameter (32 bytes hex / 1..16 chars)"), + arg_str0(NULL, "ap", "", "application parameter (32 bytes hex / 1..16 chars)"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); @@ -678,23 +677,25 @@ static int GetExistsFileNameJson(const char *prefixDir, const char *reqestedFile return PM3_SUCCESS; } -static int cmd_hf_fido_2make_credential(const char *cmd) { +static int CmdHFFido2MakeCredential(const char *cmd) { json_error_t error; char fname[FILE_PATH_SIZE] = {0}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido make", - "Execute a FIDO2 Make Credential command. Needs json file with parameters. Sample file `fido2.json` in `resources/`.", - "hf fido make -> execute command with default parameters file `fido2.json`\n" - "hf fido make test.json -> execute command with parameters file `text.json`"); + "Execute a FIDO2 Make Credential command. Needs json file with parameters.\n" + "Sample file `fido2.json` in `client/resources/`.", + "hf fido make -> default parameters file `fido2.json`\n" + "hf fido make -f test.json -> use parameters file `text.json`" + ); void *argtable[] = { arg_param_begin, - arg_lit0("a", "apdu", "show APDU reqests and responses"), - arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), - arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"), - arg_lit0("c", "cbor", "show CBOR decoded data"), - arg_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), + arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"), + arg_lit0("c", "cbor", "show CBOR decoded data"), + arg_str0("f", "file", "", "parameter JSON file name. (def `fido2.json`)"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); @@ -804,23 +805,26 @@ static int cmd_hf_fido_2make_credential(const char *cmd) { return PM3_SUCCESS; } -static int cmd_hf_fido_2get_assertion(const char *cmd) { +static int CmdHFFido2GetAssertion(const char *cmd) { json_error_t error; char fname[FILE_PATH_SIZE] = {0}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido assert", - "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json` in `resources/`.", - "hf fido assert -> execute command with default parameters file `fido2.json`\n" - "hf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId"); + "Execute a FIDO2 Get Assertion command. Needs json file with parameters.\n" + "Sample file `fido2.json` in `client/resources/`.\n" + "- Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)" + , + "hf fido assert -> default parameters file `fido2.json`\n" + "hf fido assert -f test.json -l -> use parameters file `text.json` and add to request CredentialId"); void *argtable[] = { arg_param_begin, - arg_lit0("a", "apdu", "show APDU reqests and responses"), - arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), - arg_lit0("c", "cbor", "show CBOR decoded data"), - arg_lit0("l", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)"), - arg_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), + arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("c", "cbor", "show CBOR decoded data"), + arg_lit0("l", "list", "add CredentialId from json to allowList."), + arg_str0("f", "file", "", "parameter JSON file name. (def `fido2.json`)"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); @@ -931,12 +935,12 @@ static int cmd_hf_fido_2get_assertion(const char *cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, - {"list", cmd_hf_fido_list, IfPm3Iso14443a, "List ISO 14443A history"}, - {"info", cmd_hf_fido_info, IfPm3Iso14443a, "Info about FIDO tag."}, - {"reg", cmd_hf_fido_register, IfPm3Iso14443a, "FIDO U2F Registration Message."}, - {"auth", cmd_hf_fido_authenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, - {"make", cmd_hf_fido_2make_credential, IfPm3Iso14443a, "FIDO2 MakeCredential command."}, - {"assert", cmd_hf_fido_2get_assertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."}, + {"list", CmdHFFidoList, AlwaysAvailable, "List ISO 14443A history"}, + {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."}, + {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."}, + {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, + {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."}, + {"assert", CmdHFFido2GetAssertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."}, {NULL, NULL, 0, NULL} }; diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 7225a443b..2db1a8072 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -30,12 +30,20 @@ #include "cardhelper.h" #include "wiegand_formats.h" #include "wiegand_formatutils.h" +#include "cmdsmartcard.h" // smart select fct #define NUM_CSNS 9 #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 #define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" + +static picopass_hdr_t iclass_last_known_card; +static void iclass_set_last_known_card(picopass_hdr_t *card) { + memcpy(&iclass_last_known_card, card, sizeof(picopass_hdr_t)); +} + 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); @@ -105,6 +113,28 @@ static inline uint32_t leadingzeros(uint64_t a) { #endif } +static void iclass_upload_emul(uint8_t *d, uint32_t n, uint32_t *bytes_sent) { + // fast push mode + conn.block_after_ACK = true; + + //Send to device + *bytes_sent = 0; + uint32_t bytes_remaining = n; + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); + if (bytes_in_packet == bytes_remaining) { + // Disable fast mode on last packet + conn.block_after_ACK = false; + } + clearCommandBuffer(); + SendCommandOLD(CMD_HF_ICLASS_EML_MEMSET, *bytes_sent, bytes_in_packet, 0, d + *bytes_sent, bytes_in_packet); + bytes_remaining -= bytes_in_packet; + *bytes_sent += bytes_in_packet; + } +} + + const char *card_types[] = { "PicoPass 16K / 16", // 000 "PicoPass 32K with current book 16K / 16", // 001 @@ -127,6 +157,213 @@ uint8_t card_app2_limit[] = { 0xff, }; +iclass_config_card_item_t iclass_config_types[14] = { + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + // must be the last entry + {"no config card info available", ""} +}; + +static bool check_config_card(const iclass_config_card_item_t *o) { + if (o == NULL || strlen(o->desc) == 0) { + PrintAndLogEx(INFO, "No data available"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass config -l") "` to download from cardhelper"); + return false; + } + return true; +} + +static int load_config_cards(void) { + PrintAndLogEx(INFO, "detecting cardhelper..."); + if (IsCardHelperPresent(false) == false) { + PrintAndLogEx(FAILED, "failed to detect cardhelper"); + return PM3_ENODATA; + } + + for (int i = 0; i < ARRAYLEN(iclass_config_types); ++i) { + + PrintAndLogEx(INPLACE, "loading setting %i", i); + iclass_config_card_item_t *ret = &iclass_config_types[i]; + + uint8_t desc[70] = {0}; + if (GetConfigCardStrByIdx(i, desc) == PM3_SUCCESS) { + memcpy(ret->desc, desc, sizeof(desc)); + } + + uint8_t blocks[16] = {0}; + if (GetConfigCardByIdx(i, blocks) == PM3_SUCCESS) { + memcpy(ret->data, blocks, sizeof(blocks)); + } + } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass configcard -p") "` to list all"); + return PM3_SUCCESS; +} + +static const iclass_config_card_item_t *get_config_card_item(int idx) { + if (idx > -1 && idx < 14) { + return &iclass_config_types[idx]; + } + return &iclass_config_types[13]; +} + +static void print_config_cards(void) { + if (check_config_card(&iclass_config_types[0])) { + PrintAndLogEx(INFO, "---- " _CYAN_("Config cards available") " ------------"); + for (int i = 0; i < ARRAYLEN(iclass_config_types) - 1 ; ++i) { + PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_types[i].desc); + } + PrintAndLogEx(NORMAL, ""); + } +} + +static void print_config_card(const iclass_config_card_item_t *o) { + if (check_config_card(o)) { + PrintAndLogEx(INFO, "description... %s", o->desc); + PrintAndLogEx(INFO, "data....... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); + } +} + +static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *key, bool got_kr) { + if (check_config_card(o) == false) { + return PM3_EINVARG; + } + // get header from card + //bool have = memcmp(iclass_last_known_card.csn, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); + PrintAndLogEx(INFO, "trying to read a card.."); + int res = read_iclass_csn(false, false); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Put a card on antenna and try again..."); + return res; + } + + // generate dump file + uint8_t app1_limit = iclass_last_known_card.conf.app_limit; + uint8_t old_limit = app1_limit; + uint8_t tot_bytes = (app1_limit + 1) * 8; + + // normal size + uint8_t *data = calloc(1, tot_bytes); + if (data == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + + // calc diversified key for selected card + HFiClassCalcDivKey(iclass_last_known_card.csn, iClass_Key_Table[0], iclass_last_known_card.key_d, false); + + memset(data, 0x00, tot_bytes); + memcpy(data, (uint8_t *)&iclass_last_known_card, sizeof(picopass_hdr_t)); + + // Keyrolling configuration cards are special. + if (strstr(o->desc, "Keyroll") != NULL) { + + if (got_kr == false) { + PrintAndLogEx(ERR, "please specifiy KEYROLL key!"); + free(data); + return PM3_EINVARG; + } + + if (app1_limit < 0x16) { + // if card wasn't large enough before, adapt to new size + PrintAndLogEx(WARNING, "Adapting applimit1 for KEY rolling.."); + + app1_limit = 0x16; + iclass_last_known_card.conf.app_limit = 0x16; + tot_bytes = (app1_limit + 1) * 8; + + uint8_t *p = realloc(data, tot_bytes); + if (p == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + //free(data); + return PM3_EMALLOC; + } + data = p; + memset(data, 0xFF, tot_bytes); + } + + // need to encrypt + PrintAndLogEx(INFO, "Detecting cardhelper..."); + if (IsCardHelperPresent(false) == false) { + PrintAndLogEx(FAILED, "failed to detect cardhelper"); + free(data); + return PM3_ENODATA; + } + + uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + if (Encrypt(ffs, ffs) == false) { + PrintAndLogEx(WARNING, "failed to encrypt FF"); + } + + uint8_t enckey1[8]; + if (Encrypt(key, enckey1) == false) { + PrintAndLogEx(WARNING, "failed to encrypt key1"); + } + + memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t)); + memcpy(data + (6 * 8), o->data, sizeof(o->data)); + + // encrypted keyroll key 0D + memcpy(data + (0xD * 8), enckey1, sizeof(enckey1)); + // encrypted 0xFF + for (uint8_t i = 0xe; i < 0x14; i++) { + memcpy(data + (i * 8), ffs, sizeof(ffs)); + } + + // encrypted partial keyroll key 14 + uint8_t foo[8] = {0x15}; + memcpy(foo + 1, key, 7); + uint8_t enckey2[8]; + if (Encrypt(foo, enckey2) == false) { + PrintAndLogEx(WARNING, "failed to encrypt partial 1"); + } + memcpy(data + (0x14 * 8), enckey2, sizeof(enckey2)); + + // encrypted partial keyroll key 15 + memset(foo, 0xFF, sizeof(foo)); + foo[0] = key[7]; + if (Encrypt(foo, enckey2) == false) { + PrintAndLogEx(WARNING, "failed to encrypt partial 2"); + } + memcpy(data + (0x15 * 8), enckey2, sizeof(enckey2)); + + // encrypted 0xFF + for (uint8_t i = 0x16; i <= app1_limit; i++) { + memcpy(data + (i * 8), ffs, sizeof(ffs)); + } + + + // revert potential modified app1_limit + iclass_last_known_card.conf.app_limit = old_limit; + + } else { + memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t)); + memcpy(data + (6 * 8), o->data, sizeof(o->data)); + } + + //Send to device + uint32_t bytes_sent = 0; + iclass_upload_emul(data, tot_bytes, &bytes_sent); + free(data); + + PrintAndLogEx(SUCCESS, "sent %d bytes of data to device emulator memory", bytes_sent); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass eview") "` to view dump file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass sim -t 3") "` to start simulating config card"); + return PM3_SUCCESS; +} + static uint8_t isset(uint8_t val, uint8_t mask) { return (val & mask); } @@ -135,11 +372,11 @@ static uint8_t notset(uint8_t val, uint8_t mask) { return !(val & mask); } -uint8_t get_pagemap(const picopass_hdr *hdr) { +uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; } -static void fuse_config(const picopass_hdr *hdr) { +static void fuse_config(const picopass_hdr_t *hdr) { uint16_t otp = (hdr->conf.otp[1] << 8 | hdr->conf.otp[0]); @@ -224,7 +461,7 @@ static void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *app_areas, } } -static uint8_t get_mem_config(const picopass_hdr *hdr) { +static uint8_t get_mem_config(const picopass_hdr_t *hdr) { // three configuration bits that decides sizes uint8_t type = (hdr->conf.chip_config & 0x10) >> 2; // 16K bit 0 == 1== @@ -236,7 +473,7 @@ static uint8_t get_mem_config(const picopass_hdr *hdr) { return type; } -static void mem_app_config(const picopass_hdr *hdr) { +static void mem_app_config(const picopass_hdr_t *hdr) { uint8_t mem = hdr->conf.mem_config; uint8_t chip = hdr->conf.chip_config; uint8_t kb = 2; @@ -281,13 +518,13 @@ static void mem_app_config(const picopass_hdr *hdr) { } } -static void print_picopass_info(const picopass_hdr *hdr) { +static void print_picopass_info(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "-------------------- " _CYAN_("card configuration") " --------------------"); fuse_config(hdr); mem_app_config(hdr); } -static void print_picopass_header(const picopass_hdr *hdr) { +static void print_picopass_header(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "--------------------------- " _CYAN_("card") " ---------------------------"); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); PrintAndLogEx(SUCCESS, " Config: %s Card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); @@ -298,13 +535,7 @@ static void print_picopass_header(const picopass_hdr *hdr) { } static int CmdHFiClassList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t iclass"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf iclass", "iclass"); } static int CmdHFiClassSniff(const char *Cmd) { @@ -313,7 +544,7 @@ static int CmdHFiClassSniff(const char *Cmd) { CLIParserInit(&ctx, "hf iclass sniff", "Sniff the communication reader and tag", "hf iclass sniff\n" - "hf iclass sniff -j -> jam e-purse updates\n" + "hf iclass sniff -j --> jam e-purse updates\n" ); void *argtable[] = { @@ -353,12 +584,11 @@ static int CmdHFiClassSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass sim", "Simulate a iCLASS legacy/standard tag", - "hf iclass sim -t 0 --csn 031FEC8AF7FF12E0 -> simulate with specficied CSN\n" - "hf iclass sim -t 1 -> simulate with default CSN\n" - "hf iclass sim -t 2 -> execute loclass attack online part\n" - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -> simulate full iCLASS 2k tag\n" - "hf iclass sim -t 3 -> simulate full iCLASS 2k tag\n" - "hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key"); + "hf iclass sim -t 0 --csn 031FEC8AF7FF12E0 --> simulate with specficied CSN\n" + "hf iclass sim -t 1 --> simulate with default CSN\n" + "hf iclass sim -t 2 --> execute loclass attack online part\n" + "hf iclass sim -t 3 --> simulate full iCLASS 2k tag\n" + "hf iclass sim -t 4 --> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key"); void *argtable[] = { arg_param_begin, @@ -471,7 +701,7 @@ static int CmdHFiClassSim(const char *Cmd) { saveFile("iclass_mac_attack", ".bin", dump, datalen); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -h") "` to recover elite key"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack.bin") "` to recover elite key"); break; } case ICLASS_SIM_MODE_READER_ATTACK_KEYROLL: { @@ -537,7 +767,8 @@ static int CmdHFiClassSim(const char *Cmd) { saveFile("iclass_mac_attack_keyroll_B", ".bin", dump, datalen); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -h") "` to recover elite key"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack_keyroll_A.bin") "` to recover elite key"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass loclass -f iclass_mac_attack_keyroll_B.bin") "` to recover elite key"); break; } case ICLASS_SIM_MODE_CSN: @@ -599,9 +830,16 @@ int read_iclass_csn(bool loop, bool verbose) { } } - picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(hdr->csn, sizeof(hdr->csn))); + picopass_hdr_t *card = calloc(1, sizeof(picopass_hdr_t)); + if (card) { + memcpy(card, (picopass_hdr_t *)resp.data.asBytes, sizeof(picopass_hdr_t)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); + iclass_set_last_known_card(card); + free(card); + } else { + PrintAndLogEx(FAILED, "failed to allocate memory"); + } } } while (loop && kbd_enter_pressed() == false); @@ -636,14 +874,14 @@ static int CmdHFiClassReader(const char *Cmd) { static int CmdHFiClassELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass eload", - "Loads iCLASS tag dump into emulator memory on device", - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin\n"); + "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" + ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), - arg_lit0(NULL, "json", "load JSON type dump"), - arg_lit0(NULL, "eml", "load EML type dump"), + arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -658,32 +896,17 @@ static int CmdHFiClassELoad(const char *Cmd) { return PM3_EINVARG; } - DumpFileType_t dftype = BIN; - - bool use_json = arg_get_lit(ctx, 2); - bool use_eml = arg_get_lit(ctx, 3); CLIParserFree(ctx); - if (use_json && use_eml) { - PrintAndLogEx(ERR, "Error: can't specify both JSON & EML"); - return PM3_EINVARG; - } - - if (use_json) { - dftype = JSON; - } else if (use_eml) { - dftype = EML; - } - size_t bytes_read = 2048; uint8_t *dump = calloc(2048, sizeof(uint8_t)); - if (!dump) { + if (dump == NULL) { PrintAndLogEx(ERR, "error, cannot allocate memory "); return PM3_EMALLOC; } int res = 0; - + DumpFileType_t dftype = getfiletype(filename); switch (dftype) { case BIN: { res = loadFile_safe(filename, ".bin", (void **)&dump, &bytes_read); @@ -698,6 +921,7 @@ static int CmdHFiClassELoad(const char *Cmd) { break; } case DICTIONARY: { + free(dump); PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); return PM3_EINVARG; } @@ -716,29 +940,13 @@ static int CmdHFiClassELoad(const char *Cmd) { dump = newdump; } - print_picopass_header((picopass_hdr *) dump); - print_picopass_info((picopass_hdr *) dump); - - // fast push mode - conn.block_after_ACK = true; + print_picopass_header((picopass_hdr_t *) dump); + print_picopass_info((picopass_hdr_t *) dump); //Send to device uint32_t bytes_sent = 0; - uint32_t bytes_remaining = bytes_read; - - while (bytes_remaining > 0) { - uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); - if (bytes_in_packet == bytes_remaining) { - // Disable fast mode on last packet - conn.block_after_ACK = false; - } - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ICLASS_EML_MEMSET, bytes_sent, bytes_in_packet, 0, dump + bytes_sent, bytes_in_packet); - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - } + iclass_upload_emul(dump, bytes_read, &bytes_sent); free(dump); - PrintAndLogEx(SUCCESS, "sent %d bytes of data to device emulator memory", bytes_sent); return PM3_SUCCESS; } @@ -754,7 +962,7 @@ static int CmdHFiClassESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dumpfile"), + arg_str0("f", "file", "", "filename of dump file"), arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), arg_param_end }; @@ -763,8 +971,6 @@ static int CmdHFiClassESave(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); - char *fnameptr = filename; - uint16_t bytes = arg_get_int_def(ctx, 2, 256); if (bytes > 4096) { @@ -789,8 +995,9 @@ static int CmdHFiClassESave(const char *Cmd) { // user supplied filename? if (fnlen < 1) { - fnameptr += snprintf(fnameptr, sizeof(filename), "hf-iclass-"); - FillFileNameByUID(fnameptr, dump, "-dump", 8); + char *fptr = filename; + fptr += snprintf(fptr, sizeof(filename), "hf-iclass-"); + FillFileNameByUID(fptr, dump, "-dump", 8); } saveFile(filename, ".bin", dump, bytes); @@ -798,7 +1005,7 @@ static int CmdHFiClassESave(const char *Cmd) { saveFileJSON(filename, jsfIclass, dump, bytes, NULL); free(dump); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view") "` to view dump file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); return PM3_SUCCESS; } @@ -851,8 +1058,8 @@ static int CmdHFiClassEView(const char *Cmd) { } if (verbose) { - print_picopass_header((picopass_hdr *) dump); - print_picopass_info((picopass_hdr *) dump); + print_picopass_header((picopass_hdr_t *) dump); + print_picopass_info((picopass_hdr_t *) dump); } PrintAndLogEx(NORMAL, ""); @@ -868,15 +1075,18 @@ static int CmdHFiClassDecrypt(const char *Cmd) { "This is a naive implementation, it tries to decrypt every block after block 6.\n" "Correct behaviour would be to decrypt only the application areas where the key is valid,\n" "which is defined by the configuration block.\n" - "OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside\n" - "in the resources directory. The file should be 16 bytes binary data", + "\nOBS!\n" + "In order to use this function, the file `iclass_decryptionkey.bin` must reside\n" + "in the resources directory. The file should be 16 bytes binary data\n" + "or...\n" + "make sure your cardhelper is placed in the sim module", "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin -k 000102030405060708090a0b0c0d0e0f\n" "hf iclass decrypt -d 1122334455667788 -k 000102030405060708090a0b0c0d0e0f"); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dumpfile"), + arg_str0("f", "file", "", "filename of dump file (bin/eml/json)"), arg_str0("d", "data", "", "3DES encrypted data"), arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), @@ -888,33 +1098,12 @@ static int CmdHFiClassDecrypt(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(clictx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - size_t decryptedlen = 0; - uint8_t *decrypted = NULL; - bool have_file = false; - - if (fnlen > 0) { - if (loadFile_safe(filename, "", (void **)&decrypted, &decryptedlen) != PM3_SUCCESS) { - CLIParserFree(clictx); - return PM3_EINVARG; - } - have_file = true; - } - int enc_data_len = 0; uint8_t enc_data[8] = {0}; bool have_data = false; CLIGetHexWithReturn(clictx, 2, enc_data, &enc_data_len); - if (enc_data_len > 0) { - if (enc_data_len != 8) { - PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 HEX symbols)"); - CLIParserFree(clictx); - return PM3_EINVARG; - } - have_data = true; - } - int key_len = 0; uint8_t key[16] = {0}; uint8_t *keyptr = NULL; @@ -922,33 +1111,85 @@ static int CmdHFiClassDecrypt(const char *Cmd) { CLIGetHexWithReturn(clictx, 3, key, &key_len); + bool verbose = arg_get_lit(clictx, 4); + CLIParserFree(clictx); + + // sanity checks + if (enc_data_len > 0) { + if (enc_data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 HEX symbols)"); + return PM3_EINVARG; + } + have_data = true; + } + if (key_len > 0) { if (key_len != 16) { PrintAndLogEx(ERR, "Transport key must be 16 hex bytes (32 HEX characters)"); - CLIParserFree(clictx); return PM3_EINVARG; } have_key = true; } - bool verbose = arg_get_lit(clictx, 4); - CLIParserFree(clictx); + size_t decryptedlen = 2048; + uint8_t *decrypted = calloc(2048, sizeof(uint8_t)); + bool have_file = false; + if (decrypted == NULL) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } - uint8_t dec_data[8] = {0}; - bool use_sc = false; - if (have_key == false) { - use_sc = IsCryptoHelperPresent(verbose); - if (use_sc == false) { - size_t keylen = 0; - int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); - if (res != PM3_SUCCESS) { - PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + int res = PM3_SUCCESS; + + // if user supplied dump file, time to load it + if (fnlen > 0) { + + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&decrypted, &decryptedlen); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&decrypted, &decryptedlen); + break; + } + case JSON: { + res = loadFileJSON(filename, decrypted, 2048, &decryptedlen, NULL); + break; + } + case DICTIONARY: { + free(decrypted); + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); return PM3_EINVARG; } - + } + + if (res != PM3_SUCCESS) { + free(decrypted); + return PM3_EFILE; + } + + have_file = true; + } + + // load transport key + bool use_sc = false; + if (have_key == false) { + use_sc = IsCardHelperPresent(verbose); + if (use_sc == false) { + size_t keylen = 0; + res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + free(decrypted); + return PM3_EINVARG; + } + if (keylen != 16) { PrintAndLogEx(ERR, "Failed to load transport key from file"); free(keyptr); + free(decrypted); return PM3_EINVARG; } memcpy(key, keyptr, sizeof(key)); @@ -960,6 +1201,9 @@ static int CmdHFiClassDecrypt(const char *Cmd) { mbedtls_des3_context ctx; mbedtls_des3_set2key_dec(&ctx, key); + uint8_t dec_data[8] = {0}; + + // decrypt user supplied data if (have_data) { if (use_sc) { @@ -970,9 +1214,10 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(SUCCESS, "Data: %s", sprint_hex(dec_data, sizeof(dec_data))); } + // decrypt dump file data if (have_file) { - picopass_hdr *hdr = (picopass_hdr *)decrypted; + picopass_hdr_t *hdr = (picopass_hdr_t *)decrypted; uint8_t mem = hdr->conf.mem_config; uint8_t chip = hdr->conf.chip_config; @@ -1030,24 +1275,25 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(NORMAL, ""); // decode block 6 - if (memcmp(decrypted + (8 * 6), empty, 8) != 0) { + bool has_values = (memcmp(decrypted + (8 * 6), empty, 8) != 0) && (memcmp(decrypted + (8 * 6), zeros, 8) != 0); + if (has_values) { if (use_sc) { DecodeBlock6(decrypted + (8 * 6)); } } // decode block 7-8-9 - if (memcmp(decrypted + (8 * 7), empty, 8) != 0) { + has_values = (memcmp(decrypted + (8 * 7), empty, 8) != 0) && (memcmp(decrypted + (8 * 7), zeros, 8) != 0); + if (has_values) { //todo: remove preamble/sentinal - 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[8 + 1] = {0}; + 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}; @@ -1062,18 +1308,20 @@ static int CmdHFiClassDecrypt(const char *Cmd) { HIDTryUnpack(&packed, true); } else { - PrintAndLogEx(INFO, "No credential found."); + PrintAndLogEx(INFO, "No credential found"); } // decode block 9 - if (memcmp(decrypted + (8 * 9), empty, 8) != 0) { + has_values = (memcmp(decrypted + (8 * 9), empty, 8) != 0) && (memcmp(decrypted + (8 * 9), zeros, 8) != 0); + if (has_values) { uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (8 * 6)); if (usr_blk_len < 3) { - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Block 9 decoder"); if (use_sc) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Block 9 decoder"); + uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); if (pinsize > 0) { @@ -1087,7 +1335,6 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } PrintAndLogEx(INFO, "-----------------------------------------------------------------"); - free(decrypted); free(fptr); } @@ -1157,7 +1404,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { bool use_sc = false; if (have_key == false) { - use_sc = IsCryptoHelperPresent(verbose); + use_sc = IsCardHelperPresent(verbose); if (use_sc == false) { size_t keylen = 0; int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); @@ -1207,7 +1454,7 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) { return false; } - picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; + picopass_hdr_t *hdr = (picopass_hdr_t *)resp.data.asBytes; if (CSN != NULL) memcpy(CSN, hdr->csn, 8); @@ -1234,7 +1481,7 @@ static int CmdHFiClassDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename to save dump to"), + arg_str0("f", "file", "", "save filename"), arg_str0("k", "key", "", "debit key or NR/MAC for replay as 8 hex bytes"), arg_int0(NULL, "ki", "", "debit key index to select key from memory 'hf iclass managekeys'"), arg_str0(NULL, "credit", "", "credit key as 8 hex bytes"), @@ -1329,7 +1576,7 @@ static int CmdHFiClassDump(const char *Cmd) { CLIParserFree(ctx); if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(FAILED, "Can not use a combo of 'e', 'r', 'n'"); + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); return PM3_EINVARG; } @@ -1352,7 +1599,7 @@ static int CmdHFiClassDump(const char *Cmd) { DropField(); uint8_t readStatus = resp.oldarg[0] & 0xff; - picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; + picopass_hdr_t *hdr = (picopass_hdr_t *)resp.data.asBytes; if (readStatus == 0) { PrintAndLogEx(FAILED, "no tag found"); @@ -1561,8 +1808,8 @@ write_dump: saveFileEML(filename, tag_data, bytes_got, 8); saveFileJSON(filename, jsfIclass, tag_data, bytes_got, NULL); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt") "` to decrypt dump file"); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view") "` to view dump file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt -f") "` to decrypt dump file"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -1706,7 +1953,7 @@ static int CmdHFiClassRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "specify a filename to restore"), + arg_str1("f", "file", "", "specify a filename to restore (bin/eml/json)"), arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), arg_int1(NULL, "first", "", "The first block number to restore"), @@ -1784,10 +2031,37 @@ static int CmdHFiClassRestore(const char *Cmd) { return PM3_EINVARG; } - uint8_t *dump = NULL; - size_t bytes_read = 0; - if (loadFile_safe(filename, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + size_t bytes_read = 2048; + uint8_t *dump = calloc(2048, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + int res = PM3_SUCCESS; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, &bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&dump, &bytes_read); + break; + } + case JSON: { + res = loadFileJSON(filename, dump, 2048, &bytes_read, NULL); + break; + } + case DICTIONARY: { + free(dump); + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + return PM3_EINVARG; + } + } + + if (res != PM3_SUCCESS) { + free(dump); return PM3_EFILE; } @@ -1857,7 +2131,7 @@ static int CmdHFiClassRestore(const char *Cmd) { if (resp.status == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "iCLASS restore " _GREEN_("successful")); - PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass rdbl ") "` to verify data on card"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass rdbl") "` to verify data on card"); } else { PrintAndLogEx(WARNING, "iCLASS restore " _RED_("failed")); } @@ -1983,7 +2257,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { CLIParserFree(ctx); if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(ERR, "Can not use a combo of 'e', 'r', 'n'"); + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); return PM3_EINVARG; } @@ -2008,7 +2282,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { if (memcmp(data, empty, 8) == 0) return PM3_SUCCESS; - bool use_sc = IsCryptoHelperPresent(verbose); + bool use_sc = IsCardHelperPresent(verbose); if (use_sc == false) return PM3_SUCCESS; @@ -2084,9 +2358,9 @@ static int CmdHFiClass_loclass(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of Bruteforce iclass dumpfile"), - arg_lit0(NULL, "test", "Perform self-test"), - arg_lit0(NULL, "long", "Perform self-test, including long ones"), + arg_str0("f", "file", "", "filename with nr/mac data from `hf iclass sim -t 2` "), + arg_lit0(NULL, "test", "Perform self-test"), + arg_lit0(NULL, "long", "Perform self-test, including long ones"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2117,8 +2391,8 @@ static int CmdHFiClass_loclass(const char *Cmd) { void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize) { - picopass_hdr *hdr = (picopass_hdr *)iclass_dump; -// picopass_ns_hdr *ns_hdr = (picopass_ns_hdr *)iclass_dump; + picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump; +// picopass_ns_hdr_t *ns_hdr = (picopass_ns_hdr_t *)iclass_dump; // uint8_t pagemap = get_pagemap(hdr); // if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { } @@ -2235,13 +2509,13 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e static int CmdHFiClassView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass view", - "Print a iCLASS tag dump file", + "Print a iCLASS tag dump file (bin/eml/json)", "hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" "hf iclass view --first 1 -f hf-iclass-AA162D30F8FF12F1-dump.bin\n"); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), 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"), @@ -2259,10 +2533,37 @@ static int CmdHFiClassView(const char *Cmd) { CLIParserFree(ctx); - uint8_t *dump = NULL; - size_t bytes_read = 0; - if (loadFile_safe(filename, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + size_t bytes_read = 2048; + uint8_t *dump = calloc(2048, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + int res = 0; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, &bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&dump, &bytes_read); + break; + } + case JSON: { + res = loadFileJSON(filename, dump, 2048, &bytes_read, NULL); + break; + } + case DICTIONARY: { + free(dump); + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + return PM3_EINVARG; + } + } + + if (res != PM3_SUCCESS) { + free(dump); return PM3_EFILE; } @@ -2273,8 +2574,8 @@ static int CmdHFiClassView(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - print_picopass_header((picopass_hdr *) dump); - print_picopass_info((picopass_hdr *) dump); + print_picopass_header((picopass_hdr_t *) dump); + print_picopass_info((picopass_hdr_t *) dump); printIclassDumpContents(dump, startblock, endblock, bytes_read); free(dump); return PM3_SUCCESS; @@ -2314,9 +2615,9 @@ static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, u xor_div_key[i] = old_div_key[i] ^ new_div_key[i]; } if (verbose) { - PrintAndLogEx(SUCCESS, "Old div key : %s", sprint_hex(old_div_key, 8)); - PrintAndLogEx(SUCCESS, "New div key : %s", sprint_hex(new_div_key, 8)); - PrintAndLogEx(SUCCESS, "Xor div key : " _YELLOW_("%s") "\n", sprint_hex(xor_div_key, 8)); + PrintAndLogEx(SUCCESS, "Old div key......... %s", sprint_hex(old_div_key, 8)); + PrintAndLogEx(SUCCESS, "New div key......... %s", sprint_hex(new_div_key, 8)); + PrintAndLogEx(SUCCESS, "Xor div key......... " _YELLOW_("%s") "\n", sprint_hex(xor_div_key, 8)); } } @@ -2362,7 +2663,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { } else if (old_key_nr >= 0) { if (old_key_nr < ICLASS_KEYS_MAX) { memcpy(old_key, iClass_Key_Table[old_key_nr], 8); - PrintAndLogEx(SUCCESS, "Using old key[%d] " _GREEN_("%s"), old_key_nr, sprint_hex(iClass_Key_Table[old_key_nr], 8)); + PrintAndLogEx(SUCCESS, "Using old key[%d]... " _GREEN_("%s"), old_key_nr, sprint_hex(iClass_Key_Table[old_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); CLIParserFree(ctx); @@ -2395,7 +2696,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { } else if (new_key_nr >= 0) { if (new_key_nr < ICLASS_KEYS_MAX) { memcpy(new_key, iClass_Key_Table[new_key_nr], 8); - PrintAndLogEx(SUCCESS, "Using new key[%d] " _GREEN_("%s"), new_key_nr, sprint_hex(iClass_Key_Table[new_key_nr], 8)); + PrintAndLogEx(SUCCESS, "Using new key[%d]... " _GREEN_("%s"), new_key_nr, sprint_hex(iClass_Key_Table[new_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); CLIParserFree(ctx); @@ -2505,14 +2806,14 @@ static int CmdHFiClassManageKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass managekeys", "Manage iCLASS Keys in client memory", - "hf iclass managekeys --ki 0 -k 1122334455667788 -> set key 1122334455667788 at index 0\n" - "hf iclass managekeys -f mykeys.bin --save -> save key file\n" - "hf iclass managekeys -f mykeys.bin --load -> load key file\n" - "hf iclass managekeys -p -> print keys"); + "hf iclass managekeys --ki 0 -k 1122334455667788 --> set key 1122334455667788 at index 0\n" + "hf iclass managekeys -f mykeys.bin --save --> save key file\n" + "hf iclass managekeys -f mykeys.bin --load --> load key file\n" + "hf iclass managekeys -p --> print keys"); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "Specify a filename to use with load or save operations"), + arg_str0("f", "file", "", "Specify a filename for load / save operations"), arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Specify key index to set key in memory"), arg_lit0(NULL, "save", "Save keys in memory to file specified by filename"), @@ -2616,7 +2917,7 @@ static void add_key(uint8_t *key) { if (i == ICLASS_KEYS_MAX) { PrintAndLogEx(INFO, "Couldn't find an empty keyslot"); } else { - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf iclass managekeys -p`") " to view keys"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass managekeys -p") "` to view keys"); } } @@ -2726,7 +3027,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Dictionary file with default iclass keys"), + arg_str1("f", "file", "", "Dictionary file with default iclass keys"), 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)"), @@ -2912,7 +3213,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Dictionary file with default iclass keys"), + arg_str1("f", "file", "", "Dictionary file with default iclass keys"), arg_str1(NULL, "csn", "", "Specify CSN as 8 hex bytes"), arg_str1(NULL, "epurse", "", "Specify ePurse as 8 hex bytes"), arg_str1(NULL, "macs", "", "MACs"), @@ -3391,7 +3692,7 @@ static int CmdHFiClassEncode(const char *Cmd) { } if (have_enc_key == false) { - use_sc = IsCryptoHelperPresent(false); + use_sc = IsCardHelperPresent(false); if (use_sc == false) { size_t keylen = 0; int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&enckeyptr, &keylen); @@ -3489,6 +3790,78 @@ static int CmdHFiClassAutopwn(const char *Cmd) { } */ +static int CmdHFiClassConfigCard(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass configcard", + "Manage reader configuration card via Cardhelper,\n" + "The generated config card will be uploaded to device emulator memory.\n" + "You can start simulating `hf iclass sim -t 3` or use the emul commands", + "hf iclass configcard -l --> download config card settings\n" + "hf iclass configcard -p --> print all config cards\n" + "hf iclass configcard --ci 1 --> view config card setting in slot 1\n" + "hf iclass configcard -g --ci 0 --> generate config file from slot 0" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "ci", "", "use config slot at index"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_lit0("g", NULL, "generate card dump file"), + arg_lit0("l", NULL, "load available cards"), + arg_lit0("p", NULL, "print available cards"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int ccidx = arg_get_int_def(ctx, 1, -1); + int kidx = arg_get_int_def(ctx, 2, -1); + bool do_generate = arg_get_lit(ctx, 3); + bool do_load = arg_get_lit(ctx, 4); + bool do_print = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + bool got_kr = false; + uint8_t key[8] = {0}; + if (kidx >= 0) { + if (kidx < ICLASS_KEYS_MAX) { + got_kr = true; + memcpy(key, iClass_Key_Table[kidx], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), kidx, sprint_hex(iClass_Key_Table[kidx], 8)); + } else { + PrintAndLogEx(ERR, "--ki number is invalid"); + return PM3_EINVARG; + } + } + + if (do_load) { + if (load_config_cards() != PM3_SUCCESS) { + PrintAndLogEx(INFO, "failed to load, check your cardhelper"); + } + } + + if (do_print) { + print_config_cards(); + } + + if (ccidx > -1 && ccidx < 14) { + const iclass_config_card_item_t *item = get_config_card_item(ccidx); + print_config_card(item); + } + + if (do_generate) { + const iclass_config_card_item_t *item = get_config_card_item(ccidx); + if (strstr(item->desc, "Keyroll") != NULL) { + if (got_kr == false) { + PrintAndLogEx(ERR, "please specifiy KEYROLL key!"); + return PM3_EINVARG; + } + } + generate_config_card(item, key, got_kr); + } + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -3514,6 +3887,7 @@ static command_t CommandTable[] = { {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("utils") " ---------------------"}, + {"configcard", CmdHFiClassConfigCard, AlwaysAvailable, "Reader configuration card"}, {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "Calc diversified keys (blocks 3 & 4) to write new keys"}, {"encode", CmdHFiClassEncode, AlwaysAvailable, "Encode binary wiegand to block 7"}, {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "Encrypt given block data"}, @@ -3566,12 +3940,11 @@ int info_iclass(void) { return PM3_EOPABORTED; } - picopass_hdr *hdr = (picopass_hdr *)resp.data.asBytes; - picopass_ns_hdr *ns_hdr = (picopass_ns_hdr *)resp.data.asBytes; + picopass_hdr_t *hdr = (picopass_hdr_t *)resp.data.asBytes; + picopass_ns_hdr_t *ns_hdr = (picopass_ns_hdr_t *)resp.data.asBytes; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--------------------- " _CYAN_("Tag Information") " ----------------------"); - PrintAndLogEx(INFO, "------------------------------------------------------------"); if (readStatus & FLAG_ICLASS_CSN) { PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index d060d1275..f96c9b1b2 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -28,6 +28,11 @@ typedef struct iclass_prekey { uint8_t key[8]; } iclass_prekey_t; +typedef struct { + char desc[70]; + uint8_t data[16]; +} iclass_config_card_item_t; + int CmdHFiClass(const char *Cmd); int info_iclass(void); @@ -40,6 +45,6 @@ void GenerateMacKeyFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elit void PrintPreCalcMac(uint8_t *keys, uint32_t keycnt, iclass_premac_t *pre_list); void PrintPreCalc(iclass_prekey_t *list, uint32_t itemcnt); -uint8_t get_pagemap(const picopass_hdr *hdr); +uint8_t get_pagemap(const picopass_hdr_t *hdr); bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t *tmac, uint8_t *key); #endif diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 709cf9390..8bab7c120 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -390,9 +390,9 @@ static int CmdLegicRdbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic rdbl", "Read data from a LEGIC Prime tag", - "hf legic rdbl -o 0 -l 16 <- reads from byte[0] 16 bytes(system header)\n" - "hf legic rdbl -o 0 -l 4 --iv 55 <- reads from byte[0] 4 bytes with IV 0x55\n" - "hf legic rdbl -o 0 -l 256 --iv 55 <- reads from byte[0] 256 bytes with IV 0x55"); + "hf legic rdbl -o 0 -l 16 -> reads from byte[0] 16 bytes(system header)\n" + "hf legic rdbl -o 0 -l 4 --iv 55 -> reads from byte[0] 4 bytes with IV 0x55\n" + "hf legic rdbl -o 0 -l 256 --iv 55 -> reads from byte[0] 256 bytes with IV 0x55"); void *argtable[] = { arg_param_begin, @@ -444,9 +444,9 @@ static int CmdLegicSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic sim", "Simulates a LEGIC Prime tag. MIM22, MIM256, MIM1024 types can be emulated", - "hf legic sim -t 0 <- Simulate Type MIM22\n" - "hf legic sim -t 1 <- Simulate Type MIM256 (default)\n" - "hf legic sim -t 2 <- Simulate Type MIM1024"); + "hf legic sim -t 0 -> Simulate Type MIM22\n" + "hf legic sim -t 1 -> Simulate Type MIM256 (default)\n" + "hf legic sim -t 2 -> Simulate Type MIM1024"); void *argtable[] = { arg_param_begin, @@ -495,8 +495,8 @@ static int CmdLegicWrbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic wrbl", "Write data to a LEGIC Prime tag. It autodetects tagsize to ensure proper write", - "hf legic wrbl -o 0 -d 11223344 <- Write 0x11223344 starting from offset 0)\n" - "hf legic wrbl -o 10 -d DEADBEEF <- Write 0xdeadbeef starting from offset 10"); + "hf legic wrbl -o 0 -d 11223344 -> Write 0x11223344 starting from offset 0)\n" + "hf legic wrbl -o 10 -d DEADBEEF -> Write 0xdeadbeef starting from offset 10"); void *argtable[] = { arg_param_begin, @@ -599,7 +599,7 @@ static int CmdLegicCalcCrc(const char *Cmd) { CLIParserInit(&ctx, "hf legic crc", "Calculates the legic crc8/crc16 on the given data", "hf legic crc -d deadbeef1122\n" - "hf legic crc -d deadbeef1122 --mcc 9A -t 16 <- CRC Type 16"); + "hf legic crc -d deadbeef1122 --mcc 9A -t 16 -> CRC Type 16"); void *argtable[] = { arg_param_begin, @@ -758,9 +758,9 @@ static int CmdLegicDump(const char *Cmd) { CLIParserInit(&ctx, "hf legic dump", "Read all memory from LEGIC Prime MIM22, MIM256, MIM1024 and saves bin/eml/json dump file\n" "It autodetects card type.", - "hf legic dump <-- use UID as filename\n" - "hf legic dump -f myfile <-- use user specified filename\n" - "hf legic dump --deobfuscate <-- use UID as filename and deobfuscate data"); + "hf legic dump --> use UID as filename\n" + "hf legic dump -f myfile --> use user specified filename\n" + "hf legic dump --deobfuscate --> use UID as filename and deobfuscate data"); void *argtable[] = { arg_param_begin, @@ -852,8 +852,8 @@ static int CmdLegicRestore(const char *Cmd) { CLIParserInit(&ctx, "hf legic restore", "Reads binary file and it autodetects card type and verifies that the file has the same size\n" "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]", - "hf legic restore -f myfile <-- use user specified filename\n" - "hf legic restore -f myfile --obfuscate <-- use UID as filename and deobfuscate data"); + "hf legic restore -f myfile --> use user specified filename\n" + "hf legic restore -f myfile --obfuscate --> use UID as filename and deobfuscate data"); void *argtable[] = { arg_param_begin, @@ -951,9 +951,9 @@ static int CmdLegicELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic eload", "Loads a LEGIC binary dump into emulator memory", - "hf legic eload -f myfile -t 0 <- Simulate Type MIM22\n" - "hf legic eload -f myfile -t 1 <- Simulate Type MIM256 (default)\n" - "hf legic eload -f myfile -t 2 <- Simulate Type MIM1024"); + "hf legic eload -f myfile -t 0 -> Simulate Type MIM22\n" + "hf legic eload -f myfile -t 1 -> Simulate Type MIM256 (default)\n" + "hf legic eload -f myfile -t 2 -> Simulate Type MIM1024"); void *argtable[] = { arg_param_begin, @@ -1019,10 +1019,10 @@ static int CmdLegicESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic esave", "Saves bin/eml/json dump file of emulator memory", - "hf legic esave <- uses UID as filename\n" - "hf legic esave -f myfile -t 0 <- Type MIM22\n" - "hf legic esave -f myfile -t 1 <- Type MIM256 (default)\n" - "hf legic esave -f myfile -t 2 <- Type MIM1024"); + "hf legic esave --> uses UID as filename\n" + "hf legic esave -f myfile -t 0 --> Type MIM22\n" + "hf legic esave -f myfile -t 1 --> Type MIM256 (default)\n" + "hf legic esave -f myfile -t 2 --> Type MIM1024"); void *argtable[] = { arg_param_begin, @@ -1164,13 +1164,7 @@ static int CmdLegicWipe(const char *Cmd) { } static int CmdLegicList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t legic"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf legic", "legic"); } static command_t CommandTable[] = { diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 6e292ad6f..279d75ecb 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -59,8 +59,8 @@ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { if (n < 3) return 2; if (isResponse && (n < 6)) return 2; if (d[1] == 0x50 && - d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && - d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { + d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && + d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { return 2; } return check_crc(CRC_14443_A, d, n); @@ -228,7 +228,10 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "DEC(%d)", cmd[1]); break; case MIFARE_CMD_RESTORE: - snprintf(exp, size, "RESTORE(%d)", cmd[1]); + if (cmdsize == 4) + snprintf(exp, size, "RESTORE(%d)", cmd[1]); + else + return 0; break; case MIFARE_CMD_TRANSFER: snprintf(exp, size, "TRANSFER(%d)", cmd[1]); @@ -743,10 +746,10 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { // S-block 11xxx010 if ((cmd[0] & 0xC0) && (cmdsize == 3)) { switch ((cmd[0] & 0x30)) { - case 0x30: + case 0x00: snprintf(exp, size, "S-block DESELECT"); break; - case 0x00: + case 0x30: snprintf(exp, size, "S-block WTX"); break; default: @@ -773,6 +776,8 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { pos++; for (uint8_t i = 0; i < 2; i++, pos++) { + bool found_annotation = true; + switch (cmd[pos]) { case MFDES_CREATE_APPLICATION: snprintf(exp, size, "CREATE APPLICATION"); @@ -889,8 +894,13 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "READ SIGNATURE"); break; default: + found_annotation = false; break; } + + if (found_annotation) { + break; + } } } else { // anything else @@ -1290,9 +1300,10 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 MifareAuthState = masNrAr; if (AuthData.first_auth) { AuthData.nt = bytes_to_num(cmd, 4); + AuthData.nt_enc_par = 0; } else { AuthData.nt_enc = bytes_to_num(cmd, 4); - AuthData.nt_enc_par = parity[0]; + AuthData.nt_enc_par = parity[0] & 0xF0; } return; } else { @@ -1304,6 +1315,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 snprintf(exp, size, "AUTH: nr ar (enc)"); MifareAuthState = masAt; AuthData.nr_enc = bytes_to_num(cmd, 4); + AuthData.nr_enc_par = parity[0] & 0xF0; AuthData.ar_enc = bytes_to_num(&cmd[4], 4); AuthData.ar_enc_par = parity[0] << 4; return; @@ -1316,7 +1328,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 snprintf(exp, size, "AUTH: at (enc)"); MifareAuthState = masAuthComplete; AuthData.at_enc = bytes_to_num(cmd, 4); - AuthData.at_enc_par = parity[0]; + AuthData.at_enc_par = parity[0] & 0xF0; return; } else { MifareAuthState = masError; @@ -1335,6 +1347,17 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 } +static void mf_get_paritybinstr(char *s, uint32_t val, uint8_t par) { + uint8_t foo[4] = {0, 0, 0, 0}; + num_to_bytes(val, sizeof(uint32_t), foo); + for (uint8_t i = 0; i < 4; i++) { + if (oddparity8(foo[i]) != ((par >> (7 - (i & 0x0007))) & 0x01)) + sprintf(s++, "1"); + else + sprintf(s++, "0"); + } +} + bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount) { static struct Crypto1State *traceCrypto1; @@ -1428,7 +1451,29 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes //hardnested if (!traceCrypto1) { - PrintAndLogEx(NORMAL, "hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + + //PrintAndLogEx(NORMAL, "hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + + char snt[5] = {0, 0, 0, 0, 0}; + mf_get_paritybinstr(snt, AuthData.nt_enc, AuthData.nt_enc_par); + char sar[5] = {0, 0, 0, 0, 0}; + mf_get_paritybinstr(sar, AuthData.ar_enc, AuthData.ar_enc_par); + char sat[5] = {0, 0, 0, 0, 0}; + mf_get_paritybinstr(sat, AuthData.at_enc, AuthData.at_enc_par); + + PrintAndLogEx(NORMAL, "Nested authentication detected. "); + PrintAndLogEx(NORMAL, "tools/mf_nonce_brute/mf_nonce_brute %x %x %s %x %x %s %x %s %s\n" + , AuthData.uid + , AuthData.nt_enc + , snt + , AuthData.nr_enc + , AuthData.ar_enc + , sar + , AuthData.at_enc + , sat + , sprint_hex_inrow(cmd, cmdsize) + ); + MifareAuthState = masError; /* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index ff7f7f799..fe4a99208 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -18,6 +18,7 @@ typedef struct { uint32_t nt_enc; // encrypted tag challenge uint8_t nt_enc_par; // encrypted tag challenge parity uint32_t nr_enc; // encrypted reader challenge + uint8_t nr_enc_par; // encrypted reader challenge parity uint32_t ar_enc; // encrypted reader response uint8_t ar_enc_par; // encrypted reader response parity uint32_t at_enc; // encrypted tag response diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 7c8545629..15ddd253f 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -159,9 +159,10 @@ int infoLTO(bool verbose) { if (ret_val == PM3_SUCCESS) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "UID......... " _YELLOW_("%s"), sprint_hex_inrow(serial_number, sizeof(serial_number))); - PrintAndLogEx(SUCCESS, "Type info... " _YELLOW_("%s"), sprint_hex_inrow(type_info, sizeof(type_info))); - PrintAndLogEx(SUCCESS, "Memory...... " _YELLOW_("%s"), lto_print_size(type_info[1])); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + PrintAndLogEx(INFO, "UID......... " _YELLOW_("%s"), sprint_hex_inrow(serial_number, sizeof(serial_number))); + PrintAndLogEx(INFO, "Type info... " _YELLOW_("%s"), sprint_hex_inrow(type_info, sizeof(type_info))); + PrintAndLogEx(INFO, "Memory...... " _YELLOW_("%s"), lto_print_size(type_info[1])); if (type_info[1] > 3) { PrintAndLogEx(INFO, "Unknown LTO tag, report to @iceman!"); } @@ -171,13 +172,7 @@ int infoLTO(bool verbose) { } static int CmdHfLTOList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t lto"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf lto", "lto"); } static int lto_rdbl(uint8_t blk, uint8_t *block_response, uint8_t *block_cnt_response, bool verbose) { diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index cb338826b..b7e12d0af 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -43,101 +43,6 @@ static int CmdHelp(const char *Cmd); -/* - * static int usage_hf14_sniff(void) { - PrintAndLogEx(NORMAL, "It continuously gets data from the field and saves it to: log, emulator, emulator file."); - PrintAndLogEx(NORMAL, "Usage: hf mf sniff [h] [l] [d] [f]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " l save encrypted sequence to logfile `uid.log`"); - PrintAndLogEx(NORMAL, " d decrypt sequence and put it to log file `uid.log`"); -// PrintAndLogEx(NORMAL, " n/a e decrypt sequence, collect read and write commands and save the result of the sequence to emulator memory"); - PrintAndLogEx(NORMAL, " f decrypt sequence, collect read and write commands and save the result of the sequence to emulator dump file `uid.eml`"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf sniff l d f")); - return PM3_SUCCESS; -} -*/ -static int usage_hf14_hardnested(void) { - PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " hf mf hardnested "); - PrintAndLogEx(NORMAL, " [known target key (12 hex symbols)] [w] [s]"); - PrintAndLogEx(NORMAL, " or hf mf hardnested r [known target key]"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w acquire nonces and UID, and write them to binary file with default name hf-mf--nonces.bin"); - PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)"); - PrintAndLogEx(NORMAL, " r read hf-mf--nonces.bin if tag present, otherwise read nonces.bin, then start attack"); - PrintAndLogEx(NORMAL, " u read/write hf-mf--nonces.bin instead of default name"); - PrintAndLogEx(NORMAL, " f read/write instead of default name"); - PrintAndLogEx(NORMAL, " t tests?"); - PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); -#if defined(COMPILER_HAS_SIMD_AVX512) - PrintAndLogEx(NORMAL, " i 5 = AVX512"); -#endif -#if defined(COMPILER_HAS_SIMD) - PrintAndLogEx(NORMAL, " i 2 = AVX2"); - PrintAndLogEx(NORMAL, " i a = AVX"); - PrintAndLogEx(NORMAL, " i s = SSE2"); - PrintAndLogEx(NORMAL, " i m = MMX"); -#endif - PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf hardnested 0 A FFFFFFFFFFFF 4 A")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf hardnested 0 A FFFFFFFFFFFF 4 A w")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf hardnested 0 A FFFFFFFFFFFF 4 A f nonces.bin w s")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf hardnested r")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf hardnested r a0a1a2a3a4a5")); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Add the known target key to check if it is present in the remaining key space:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF")); - return PM3_SUCCESS; -} - -static int usage_hf14_autopwn(void) { - PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " hf mf autopwn [k] "); - PrintAndLogEx(NORMAL, " [* ] [f [.dic]] [s] [i ] [l] [v]"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Description:"); - PrintAndLogEx(NORMAL, " This command automates the key recovery process on MIFARE Classic cards."); - PrintAndLogEx(NORMAL, " It uses the darkside, nested, hardnested and staticnested to recover keys."); - PrintAndLogEx(NORMAL, " If all keys are found, try dumping card content."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " k known key is supplied"); - PrintAndLogEx(NORMAL, " f [.dic] key dictionary file"); - PrintAndLogEx(NORMAL, " s slower acquisition for hardnested (required by some non standard cards)"); - PrintAndLogEx(NORMAL, " v verbose output (statistics)"); - PrintAndLogEx(NORMAL, " l legacy mode (use the slow 'mf chk' for the key enumeration)"); - PrintAndLogEx(NORMAL, " * all sectors based on card memory"); - PrintAndLogEx(NORMAL, " * 0 = MINI(320 bytes)"); - PrintAndLogEx(NORMAL, " * 1 = 1k (default)"); - PrintAndLogEx(NORMAL, " * 2 = 2k"); - PrintAndLogEx(NORMAL, " * 4 = 4k"); - PrintAndLogEx(NORMAL, " i set type of SIMD instructions for hardnested. Default: autodetection."); -#if defined(COMPILER_HAS_SIMD_AVX512) - PrintAndLogEx(NORMAL, " i 5 = AVX512"); -#endif -#if defined(COMPILER_HAS_SIMD) - PrintAndLogEx(NORMAL, " i 2 = AVX2"); - PrintAndLogEx(NORMAL, " i a = AVX"); - PrintAndLogEx(NORMAL, " i s = SSE2"); -#endif - PrintAndLogEx(NORMAL, " i m = MMX"); - PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf autopwn")" -- target MIFARE Classic card with default keys"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf autopwn * 1 f mfc_default_keys")" -- target MIFARE Classic card (size 1k) with default dictionary"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf autopwn k 0 A FFFFFFFFFFFF")" -- target MIFARE Classic card with Sector0 typeA with known key 'FFFFFFFFFFFF'"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf autopwn k 0 A FFFFFFFFFFFF * 1 f mfc_default_keys")" -- this command combines the two above (reduce the need for nested / hardnested attacks, by using a dictionary)"); - return PM3_SUCCESS; -} - /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); @@ -145,286 +50,16 @@ static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "First 2 bytes of key will be bruteforced"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, " ---[ This attack is obsolete, try hardnested instead ]---"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf keybrute [h] "); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " target block number"); PrintAndLogEx(NORMAL, " target key type"); PrintAndLogEx(NORMAL, " candidate key from mf_nonce_brute tool"); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf keybrute 1 A 000011223344")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf keybrute --blk 1 -k 000011223344")); return 0; } */ -static int usage_hf14_restore(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf restore [card memory] u k f "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, " u : uid, try to restore from hf-mf--key.bin and hf-mf--dump.bin"); - PrintAndLogEx(NORMAL, " k : key filename, specific the full filename of key file"); - PrintAndLogEx(NORMAL, " f : data filename, specific the full filename of data file"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf restore") " -- read the UID from tag first, then restore from hf-mf--key.bin and and hf-mf--dump.bin"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf restore 1 u 12345678") " -- restore from hf-mf-12345678-key.bin and hf-mf-12345678-dump.bin"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf restore 1 u 12345678 k dumpkey.bin") " -- restore from dumpkey.bin and hf-mf-12345678-dump.bin"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf restore 4") " -- read the UID from tag with 4K memory first, then restore from hf-mf--key.bin and and hf-mf--dump.bin"); - return PM3_SUCCESS; -} -static int usage_hf14_decryptbytes(void) { - PrintAndLogEx(NORMAL, "Decrypt Crypto-1 encrypted bytes given some known state of crypto. See tracelog to gather needed values\n"); - PrintAndLogEx(NORMAL, "Usage: hf mf decrypt [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " tag nonce"); - PrintAndLogEx(NORMAL, " encrypted reader response"); - PrintAndLogEx(NORMAL, " encrypted tag response"); - PrintAndLogEx(NORMAL, " encrypted data, taken directly after at_enc and forward"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf decrypt b830049b 9248314a 9280e203 41e586f9")); - PrintAndLogEx(NORMAL, "\n this sample decrypts 41e586f9 -> 3003999a Annotated: 30 03 [99 9a] read block 3 [crc]"); - return PM3_SUCCESS; -} - -static int usage_hf14_egetblk(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf egetblk "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " block number"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf egetblk 0")); - return PM3_SUCCESS; -} -static int usage_hf14_egetsc(void) { - PrintAndLogEx(NORMAL, "Get sector data from emulator memory.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mf egetsc [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " sector number"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf egetsc 0")); - return PM3_SUCCESS; -} -static int usage_hf14_eclr(void) { - PrintAndLogEx(NORMAL, "It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF \n"); - PrintAndLogEx(NORMAL, "Usage: hf mf eclr"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf eclr")); - return PM3_SUCCESS; -} -static int usage_hf14_eset(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf eset "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf eset 1 000102030405060708090a0b0c0d0e0f")); - return PM3_SUCCESS; -} -static int usage_hf14_eload(void) { - PrintAndLogEx(NORMAL, "It loads emul dump from the file `filename.eml`"); - PrintAndLogEx(NORMAL, "Usage: hf mf eload [card memory] [numblocks]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K, u = UL"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf eload filename")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf eload 4 filename")); - return PM3_SUCCESS; -} -static int usage_hf14_esave(void) { - PrintAndLogEx(NORMAL, "It saves emul dump into the file `filename.eml` or `cardID.eml`"); - PrintAndLogEx(NORMAL, " Usage: hf mf esave [card memory] [file name w/o `.eml`]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf esave")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf esave 4")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf esave 4 filename")); - return PM3_SUCCESS; -} -static int usage_hf14_eview(void) { - PrintAndLogEx(NORMAL, "It displays emul memory"); - PrintAndLogEx(NORMAL, " Usage: hf mf eview [card memory]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf eview")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf eview 4")); - return PM3_SUCCESS; -} -static int usage_hf14_ecfill(void) { - PrintAndLogEx(NORMAL, "Read card and transfer its data to emulator memory."); - PrintAndLogEx(NORMAL, "Keys must be laid in the emulator memory. \n"); - PrintAndLogEx(NORMAL, "Usage: hf mf ecfill [card memory]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf ecfill A")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf ecfill A 4")); - return PM3_SUCCESS; -} -static int usage_hf14_ekeyprn(void) { - PrintAndLogEx(NORMAL, "Download and print the keys from emulator memory"); - PrintAndLogEx(NORMAL, "Usage: hf mf ekeyprn [card memory] [d]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf--key.bin`"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf ekeyprn 1")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf ekeyprn d")); - return PM3_SUCCESS; -} - -static int usage_hf14_csetuid(void) { - PrintAndLogEx(NORMAL, "Set UID, ATQA, and SAK for magic Chinese card. Only works with magic cards"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf csetuid [h] [ATQA 4 hex symbols] [SAK 2 hex symbols] [w]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w wipe card before writing"); - PrintAndLogEx(NORMAL, " UID 8 hex symbols"); - PrintAndLogEx(NORMAL, " ATQA 4 hex symbols"); - PrintAndLogEx(NORMAL, " SAK 2 hex symbols"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csetuid 01020304")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csetuid 01020304 0004 08 w")); - return PM3_SUCCESS; -} -static int usage_hf14_csetblk(void) { - PrintAndLogEx(NORMAL, "Set block data for magic Chinese card. Only works with magic cards"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf csetblk [h] [w]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w wipe card before writing"); - PrintAndLogEx(NORMAL, " block number"); - PrintAndLogEx(NORMAL, " block data to write (32 hex symbols)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csetblk 1 01020304050607080910111213141516")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csetblk 1 01020304050607080910111213141516 w")); - return PM3_SUCCESS; -} -static int usage_hf14_cload(void) { - PrintAndLogEx(NORMAL, "It loads magic Chinese card from the file `filename.eml`"); - PrintAndLogEx(NORMAL, "or from emulator memory"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cload [h] [e] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " e load card with data from emulator memory"); - PrintAndLogEx(NORMAL, " j load card with data from json file"); - PrintAndLogEx(NORMAL, " b load card with data from binary file"); - PrintAndLogEx(NORMAL, " load card with data from eml file"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf cload mydump")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf cload e")); - return PM3_SUCCESS; -} -static int usage_hf14_cgetblk(void) { - PrintAndLogEx(NORMAL, "Get block data from magic Chinese card. Only works with magic cards\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cgetblk [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " block number"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf cgetblk 1")); - return PM3_SUCCESS; -} -static int usage_hf14_cgetsc(void) { - PrintAndLogEx(NORMAL, "Get sector data from magic Chinese card. Only works with magic cards\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cgetsc [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " sector number"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf cgetsc 0")); - return PM3_SUCCESS; -} -static int usage_hf14_csave(void) { - PrintAndLogEx(NORMAL, "It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); - PrintAndLogEx(NORMAL, "or into emulator memory"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf csave [h] [e] [u] [card memory] o "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " e save data to emulator memory"); - PrintAndLogEx(NORMAL, " u save data to file, use carduid as filename"); - PrintAndLogEx(NORMAL, " card memory 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, " o save data to file"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csave u 1")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csave e 1")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf csave 4 o filename")); - return PM3_SUCCESS; -} -static int usage_hf14_cview(void) { - PrintAndLogEx(NORMAL, "View `magic Chinese` card "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cview [h] [card memory]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " card memory 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf cview 1")); - return PM3_SUCCESS; -} -static int usage_hf14_nack(void) { - PrintAndLogEx(NORMAL, "Test a MIFARE Classic based card for the NACK bug."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf nack [h] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " v verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf nack")); - return PM3_SUCCESS; -} -static int usage_hf14_gen3uid(void) { - PrintAndLogEx(NORMAL, "Set UID for magic GEN 3 card without manufacturer block changing"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf gen3uid [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " UID 8/14 hex symbols"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3uid 01020304")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3uid 01020304050607")); - return PM3_SUCCESS; -} -static int usage_hf14_gen3block(void) { - PrintAndLogEx(NORMAL, "Overwrite full manufacturer block for magic GEN 3 card"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf gen3blk [h] [block data (up to 32 hex symbols)]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " [block] manufacturer block data up to 32 hex symbols to write"); - PrintAndLogEx(NORMAL, " - If block data not specified, it prints current"); - PrintAndLogEx(NORMAL, " data without changes"); - PrintAndLogEx(NORMAL, " - You can specify part of manufacturer block as"); - PrintAndLogEx(NORMAL, " 4/7-bytes for UID change only for example"); - PrintAndLogEx(NORMAL, " NOTE: BCC, SAK, ATQA will be calculated automatically"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3blk 01020304FFFFFFFF0102030405060708")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3blk 01020304")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3blk 01020304050607")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3blk")); - return PM3_SUCCESS; -} -static int usage_hf14_gen3freeze(void) { - PrintAndLogEx(NORMAL, "Perma lock further UID changes. No more UID changes available after operation completed"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf gen3freeze [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " confirm UID locks operation"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf gen3freeze y")); - return PM3_SUCCESS; -} int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { @@ -523,35 +158,23 @@ static int32_t initSectorTable(sector_t **src, int32_t items) { static void decode_print_st(uint16_t blockno, uint8_t *data) { if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Sector trailer decoded:"); - PrintAndLogEx(NORMAL, "----------------------------------------------"); - PrintAndLogEx(NORMAL, "Key A " _GREEN_("%s"), sprint_hex_inrow(data, 6)); - PrintAndLogEx(NORMAL, "Key B " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); - PrintAndLogEx(NORMAL, "Access rights"); + PrintAndLogEx(INFO, "----------------------- " _CYAN_("Sector trailer decoder") " -----------------------"); + PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6)); + PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3)); + PrintAndLogEx(INFO, "user / gdb... " _GREEN_("%02x"), data[9]); + PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " # | Access rights"); + PrintAndLogEx(INFO, "----+-----------------------------------------------------------------"); int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; for (int i = 0; i < 4; i++) { - PrintAndLogEx(NORMAL, " block %d%s " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? "+" : ""), mfGetAccessConditionsDesc(i, &data[6])); + PrintAndLogEx(INFO, "%3d%c| " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? '+' : ' '), mfGetAccessConditionsDesc(i, &data[6])); bln += blinc; } - PrintAndLogEx(NORMAL, "UserData " _YELLOW_("0x%02x"), data[9]); - PrintAndLogEx(NORMAL, "----------------------------------------------"); - } -} - -static uint16_t NumOfBlocks(char card) { - switch (card) { - case '0' : - return MIFARE_MINI_MAXBLOCK; - case '1' : - return MIFARE_1K_MAXBLOCK; - case '2' : - return MIFARE_2K_MAXBLOCK; - case '4' : - return MIFARE_4K_MAXBLOCK; - default : - return 0; + PrintAndLogEx(INFO, "----------------------------------------------------------------------"); + PrintAndLogEx(NORMAL, ""); } } @@ -608,6 +231,34 @@ static char GetFormatFromSector(uint8_t sectorNo) { } } +static void mf_print_block(uint8_t blockno, uint8_t *d) { + 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 { + PrintAndLogEx(INFO, "%3d | %s ", blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + } +} + +static void mf_print_blocks(uint16_t n, uint8_t *d) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, "blk | data | ascii"); + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + for (uint16_t i = 0; i < n; i++) { + mf_print_block(i, d + (i * MFBLOCK_SIZE)); + } + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + PrintAndLogEx(NORMAL, ""); +} + +static void mf_print_sector_hdr(uint8_t sector) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " # | sector " _GREEN_("%02d") " / " _GREEN_("0x%02X") " | ascii", sector, sector); + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); +} + static int CmdHF14AMfDarkside(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf darkside", @@ -665,152 +316,198 @@ static int CmdHF14AMfDarkside(const char *Cmd) { } static int CmdHF14AMfWrBl(const char *Cmd) { - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char cmdp = 0x00; - if (strlen(Cmd) < 3) { - PrintAndLogEx(NORMAL, "Usage: hf mf wrbl "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf wrbl 1 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F"); - return PM3_SUCCESS; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf wrbl", + "Write MIFARE Classic block", + "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -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("k", "key", "", "key, 6 hex bytes"), + arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), + + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int b = arg_get_int_def(ctx, 1, 0); + + 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;; } - blockNo = param_get8(Cmd, 0); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 0x00) { - PrintAndLogEx(NORMAL, "Key type must be A or B"); + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + + uint8_t block[MFBLOCK_SIZE] = {0x00}; + int blen = 0; + CLIGetHexWithReturn(ctx, 5, block, &blen); + CLIParserFree(ctx); + + if (blen != MFBLOCK_SIZE) { + PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", blen); return PM3_EINVARG; } - if (cmdp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 2, key, 12)) { - PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); + if (b > 255) { return PM3_EINVARG; } + uint8_t blockno = (uint8_t)b; - if (param_gethex(Cmd, 3, bldata, 32)) { - PrintAndLogEx(NORMAL, "Block data must include 32 HEX symbols"); - return PM3_EINVARG; - } - - PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); - PrintAndLogEx(NORMAL, "--data: %s", sprint_hex(bldata, 16)); + PrintAndLogEx(INFO, "--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))); uint8_t data[26]; - memcpy(data, key, 6); - memcpy(data + 10, bldata, 16); + memcpy(data, key, sizeof(key)); + memcpy(data + 10, block, sizeof(block)); clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockNo, keyType, 0, data, sizeof(data)); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, keytype, 0, data, sizeof(data)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - PrintAndLogEx(NORMAL, "isOk:%02x", isOK); - } else { - PrintAndLogEx(NORMAL, "Command execute timeout"); + 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 mf rdbl") "` to verify"); + } else { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + // suggest the opposite keytype than what was used. + PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype " _YELLOW_("%c") " instead", (keytype == MF_KEY_A) ? 'B' : 'A'); + } return PM3_SUCCESS; } static int CmdHF14AMfRdBl(const char *Cmd) { - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - char cmdp = 0x00; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf rdbl", + "Read MIFARE Classic block", + "hf mf rdbl --blk 0 -k FFFFFFFFFFFF\n" + "hf mf rdbl -b 3 -v -> get block 3, decode sector trailer\n" + ); + 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("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); - if (strlen(Cmd) < 3) { - PrintAndLogEx(NORMAL, "Usage: hf mf rdbl "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf rdbl 0 A FFFFFFFFFFFF "); - return PM3_SUCCESS; + 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; } - blockNo = param_get8(Cmd, 0); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 0x00) { - PrintAndLogEx(NORMAL, "Key type must be A or B"); - return PM3_ESOFT; - } + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); - if (cmdp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 2, key, 12)) { - PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); - return PM3_ESOFT; + if (b > 255) { + return PM3_EINVARG; } - PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); + uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; - int res = mfReadBlock(blockNo, keyType, key, data); + int res = mfReadBlock(blockno, keytype, key, data); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data, 16)); - if ((data[6] || data[7] || data[8])) { - decode_print_st(blockNo, data); + + uint8_t sector = GetSectorFromBlockNo(blockno); + mf_print_sector_hdr(sector); + mf_print_block(blockno, data); + if (verbose) { + decode_print_st(blockno, data); } } - return PM3_SUCCESS; + PrintAndLogEx(NORMAL, ""); + return res; } static int CmdHF14AMfRdSc(const char *Cmd) { - uint8_t sectorNo = 0, keyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - char cmdp = 0x00; - if (strlen(Cmd) < 3) { - PrintAndLogEx(NORMAL, "Usage: hf mf rdsc "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf rdsc 0 A FFFFFFFFFFFF "); - return PM3_SUCCESS; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf rdsc", + "Read MIFARE Classic sector", + "hf mf rdsc -s 0 -k FFFFFFFFFFFF\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("a", NULL, "input key specified is A key (def)"), + arg_lit0("b", NULL, "input key specified is B key"), + arg_str0("k", "key", "", "key specified as 6 hex bytes"), + arg_int1("s", "sec", "", "sector number"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 1) && arg_get_lit(ctx, 2)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 2)) { + keytype = MF_KEY_B; } - sectorNo = param_get8(Cmd, 0); - if (sectorNo > MIFARE_4K_MAXSECTOR) { - PrintAndLogEx(NORMAL, "Sector number must be less than 40"); - return PM3_ESOFT; + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 3, key, &keylen); + + int s = arg_get_int_def(ctx, 4, 0); + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if (s > MIFARE_4K_MAXSECTOR) { + PrintAndLogEx(WARNING, "Sector number must be less then 40"); + return PM3_EINVARG; } - - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp != 'a' && cmdp != 'b') { - PrintAndLogEx(NORMAL, "Key type must be A or B"); - return PM3_ESOFT; - } - - if (cmdp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 2, key, 12)) { - PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); - return PM3_ESOFT; - } - PrintAndLogEx(NORMAL, "--sector no %d, key %c - %s ", sectorNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); - - uint8_t sc_size = mfNumBlocksPerSector(sectorNo) * 16; + uint8_t sector = (uint8_t)s; + uint8_t sc_size = mfNumBlocksPerSector(sector) * MFBLOCK_SIZE; uint8_t *data = calloc(sc_size, sizeof(uint8_t)); if (data == NULL) { PrintAndLogEx(ERR, "failed to allocate memory"); return PM3_EMALLOC; } - int res = mfReadSector(sectorNo, keyType, key, data); + int res = mfReadSector(sector, keytype, key, data); if (res == PM3_SUCCESS) { - uint8_t blocks = NumBlocksPerSector(sectorNo); - uint8_t start = FirstBlockOfSector(sectorNo); + uint8_t blocks = NumBlocksPerSector(sector); + uint8_t start = FirstBlockOfSector(sector); + mf_print_sector_hdr(sector); for (int i = 0; i < blocks; i++) { - PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16)); + mf_print_block(start + i, data + (i * MFBLOCK_SIZE)); } - decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16)); + if (verbose) { + decode_print_st(start + blocks - 1, data + ((blocks - 1) * MFBLOCK_SIZE)); + } } free(data); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -819,7 +516,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { mfc_eload_t payload; payload.sectorcnt = numsectors; - payload.keytype = 0; + payload.keytype = MF_KEY_A; // ecfill key A clearCommandBuffer(); @@ -835,7 +532,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { PrintAndLogEx(INFO, "fast dump reported back failure w KEY A, swapping to KEY B"); // ecfill key B - payload.keytype = 1; + payload.keytype = MF_KEY_B; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); @@ -858,18 +555,18 @@ static int CmdHF14AMfDump(const char *Cmd) { CLIParserInit(&ctx, "hf mf dump", "Dump MIFARE Classic tag to binary file\n" "If no given, UID will be used as filename", - "hf mf dump --mini --> MIFARE Mini\n" - "hf mf dump --1k --> MIFARE Classic 1k\n" - "hf mf dump --2k --> MIFARE 2k\n" - "hf mf dump --4k --> MIFARE 4k\n" - "hf mf dump -f hf-mf-066C8B78-key-5.bin --> MIFARE 1k with keys from specified file\n"); + "hf mf dump --mini --> MIFARE Mini\n" + "hf mf dump --1k --> MIFARE Classic 1k\n" + "hf mf dump --2k --> MIFARE 2k\n" + "hf mf dump --4k --> MIFARE 4k\n" + "hf mf dump --keys hf-mf-066C8B78-key.bin --> MIFARE 1k with keys from specified file\n"); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), - arg_str0("k", "keys", "", "filename of keys"), + arg_str0("f", "file", "", "filename of dump"), + arg_str0("k", "keys", "", "filename of keys"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), - arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), + 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 @@ -893,15 +590,6 @@ static int CmdHF14AMfDump(const char *Cmd) { uint64_t t1 = msclock(); - 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; - // validations if ((m0 + m1 + m2 + m4) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); @@ -910,7 +598,7 @@ static int CmdHF14AMfDump(const char *Cmd) { m1 = true; } - uint8_t numSectors = 1; + uint8_t numSectors = MIFARE_1K_MAXSECTOR; if (m0) { numSectors = MIFARE_MINI_MAXSECTOR; @@ -925,6 +613,15 @@ static int CmdHF14AMfDump(const char *Cmd) { 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; if (keyFilename[0] == 0x00) { @@ -977,7 +674,7 @@ static int CmdHF14AMfDump(const char *Cmd) { fflush(stdout); payload.blockno = FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1; - payload.keytype = 0; + payload.keytype = MF_KEY_A; memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); clearCommandBuffer(); @@ -1016,7 +713,7 @@ static int CmdHF14AMfDump(const char *Cmd) { if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A. payload.blockno = FirstBlockOfSector(sectorNo) + blockNo; - payload.keytype = 0; + payload.keytype = MF_KEY_A; memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); clearCommandBuffer(); @@ -1040,7 +737,7 @@ static int CmdHF14AMfDump(const char *Cmd) { } else { // key A would work payload.blockno = FirstBlockOfSector(sectorNo) + blockNo; - payload.keytype = 0; + payload.keytype = MF_KEY_A; memcpy(payload.key, keyA[sectorNo], sizeof(payload.key)); clearCommandBuffer(); @@ -1108,146 +805,246 @@ static int CmdHF14AMfDump(const char *Cmd) { } static int CmdHF14AMfRestore(const char *Cmd) { - uint8_t sectorNo, blockNo; - uint8_t keyType = 0; - uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - uint8_t bldata[16] = {0x00}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf restore", + "Restore MIFARE Classic binary file to tag.\n" + "\n" + "The key file and data file will program the card sector trailers.\n" + "By default we authenticate to card with key B 0xFFFFFFFFFFFF.\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" + " `-w` param you can indicate that the key file should be used for authentication instead.\n" + " if so we also try both B/A keys", + "hf mf restore\n" + "hf mf restore --1k --uid 04010203\n" + "hf mf restore --1k --uid 04010203 -k hf-mf-AABBCCDD-key.bin\n" + "hf mf restore --4k" + ); + + 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("u", "uid", "", "uid, 6 hex bytes"), + arg_str0("f", "file", "", "data filename"), + arg_str0("k", "kfn", "", "key filename"), + arg_lit0(NULL, "ka", "use specified keyfile to authenticate"), + 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 uidlen = 0; + char uid[14] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)uid, sizeof(uid), &uidlen); + + int datafnlen = 0; + char datafilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)datafilename, FILE_PATH_SIZE, &datafnlen); + + int keyfnlen = 0; + char keyfilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)keyfilename, FILE_PATH_SIZE, &keyfnlen); + + bool use_keyfile_for_auth = 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 sectors = MIFARE_1K_MAXSECTOR; + + if (m0) { + sectors = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + sectors = MIFARE_1K_MAXSECTOR; + } else if (m2) { + sectors = MIFARE_2K_MAXSECTOR; + } else if (m4) { + sectors = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + // if user specified UID, use it in file templates + if (uidlen) { + + if (keyfnlen == 0) { + snprintf(keyfilename, FILE_PATH_SIZE, "hf-mf-%s-key.bin", uid); + keyfnlen = strlen(keyfilename); + } + + if (datafnlen == 0) { + snprintf(datafilename, FILE_PATH_SIZE, "hf-mf-%s-dump.bin", uid); + datafnlen = strlen(datafilename); + } + } + + // try reading card uid and create filename + if (keyfnlen == 0) { + char *fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) + return PM3_ESOFT; + + strcpy(keyfilename, fptr); + free(fptr); + } + + FILE *f; + if ((f = fopen(keyfilename, "rb")) == NULL) { + PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyfilename); + return PM3_EFILE; + } + + // key arrays uint8_t keyA[40][6]; uint8_t keyB[40][6]; - uint8_t numSectors = 16; - uint8_t cmdp = 0; - char keyFilename[FILE_PATH_SIZE] = ""; - char dataFilename[FILE_PATH_SIZE] = ""; - char szTemp[FILE_PATH_SIZE - 20] = ""; - char *fptr; - FILE *fdump, *fkeys; - - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf14_restore(); - case 'u': - param_getstr(Cmd, cmdp + 1, szTemp, FILE_PATH_SIZE - 20); - if (keyFilename[0] == 0x00) - snprintf(keyFilename, FILE_PATH_SIZE, "hf-mf-%s-key.bin", szTemp); - if (dataFilename[0] == 0x00) - snprintf(dataFilename, FILE_PATH_SIZE, "hf-mf-%s-dump.bin", szTemp); - cmdp += 2; - break; - case 'k': - param_getstr(Cmd, cmdp + 1, keyFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - case 'f': - param_getstr(Cmd, cmdp + 1, dataFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - if (cmdp == 0) { - numSectors = NumOfSectors(param_getchar(Cmd, cmdp)); - if (numSectors == 0) return usage_hf14_restore(); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - return usage_hf14_restore(); - } - } - } - - if (keyFilename[0] == 0x00) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) - return 1; - - strcpy(keyFilename, fptr); - free(fptr); - } - - if ((fkeys = fopen(keyFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); - return 1; - } + // read key file size_t bytes_read; - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - bytes_read = fread(keyA[sectorNo], 1, 6, fkeys); + for (uint8_t s = 0; s < sectors; s++) { + bytes_read = fread(keyA[s], 1, 6, f); if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyFilename); - fclose(fkeys); - return 2; + PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); + fclose(f); + return PM3_EFILE; } } - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - bytes_read = fread(keyB[sectorNo], 1, 6, fkeys); + for (uint8_t s = 0; s < sectors; s++) { + bytes_read = fread(keyB[s], 1, 6, f); if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyFilename); - fclose(fkeys); - return 2; + PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); + fclose(f); + return PM3_EFILE; } } + fclose(f); + f = NULL; - fclose(fkeys); - - if (dataFilename[0] == 0x00) { - fptr = GenerateFilename("hf-mf-", "-dump.bin"); + // try reading card uid and create filename + if (datafnlen == 0) { + char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); if (fptr == NULL) - return 1; + return PM3_ESOFT; - strcpy(dataFilename, fptr); + strcpy(datafilename, fptr); free(fptr); } - if ((fdump = fopen(dataFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), dataFilename); - return 1; + // read dump file + if ((f = fopen(datafilename, "rb")) == NULL) { + PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), datafilename); + return PM3_EINVARG; } - PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", dataFilename); - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - for (blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + // default authentication key + uint8_t default_key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", datafilename); + + for (uint8_t s = 0; s < sectors; s++) { + for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { + uint8_t data[26]; - memcpy(data, key, 6); - bytes_read = fread(bldata, 1, 16, fdump); - if (bytes_read != 16) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), dataFilename); - fclose(fdump); - fdump = NULL; - return 2; + uint8_t bldata[MFBLOCK_SIZE] = {0x00}; + + bytes_read = fread(bldata, 1, MFBLOCK_SIZE, f); + if (bytes_read != sizeof(bldata)) { + PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), datafilename); + fclose(f); + f = NULL; + return PM3_EFILE; } - if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer - bldata[0] = (keyA[sectorNo][0]); - bldata[1] = (keyA[sectorNo][1]); - bldata[2] = (keyA[sectorNo][2]); - bldata[3] = (keyA[sectorNo][3]); - bldata[4] = (keyA[sectorNo][4]); - bldata[5] = (keyA[sectorNo][5]); - bldata[10] = (keyB[sectorNo][0]); - bldata[11] = (keyB[sectorNo][1]); - bldata[12] = (keyB[sectorNo][2]); - bldata[13] = (keyB[sectorNo][3]); - bldata[14] = (keyB[sectorNo][4]); - bldata[15] = (keyB[sectorNo][5]); + if (use_keyfile_for_auth == false) { + + // sector trailer + if (b == NumBlocksPerSector(s) - 1) { + bldata[0] = (keyA[s][0]); + bldata[1] = (keyA[s][1]); + bldata[2] = (keyA[s][2]); + bldata[3] = (keyA[s][3]); + bldata[4] = (keyA[s][4]); + bldata[5] = (keyA[s][5]); + bldata[10] = (keyB[s][0]); + bldata[11] = (keyB[s][1]); + bldata[12] = (keyB[s][2]); + bldata[13] = (keyB[s][3]); + bldata[14] = (keyB[s][4]); + bldata[15] = (keyB[s][5]); + } } - PrintAndLogEx(NORMAL, "Writing to block %3d: %s", FirstBlockOfSector(sectorNo) + blockNo, sprint_hex(bldata, 16)); + memcpy(data + 10, bldata, sizeof(bldata)); - memcpy(data + 10, bldata, 16); - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(sectorNo) + blockNo, keyType, 0, data, sizeof(data)); + if (use_keyfile_for_auth) { + for (int8_t kt = MF_KEY_B; kt > -1; kt--) { - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - PrintAndLogEx(SUCCESS, "isOk:%02x", isOK); + if (kt == MF_KEY_A) + memcpy(data, keyA[s], 6); + else + memcpy(data, keyB[s], 6); + + PrintAndLogEx(INFO, "block %3d: %s", FirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(s) + b, kt, 0, data, sizeof(data)); + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.oldarg[0] & 0xff; + if (isOK == 0) { + if (b == 0) { + PrintAndLogEx(INFO, "Writing to manufacture block w key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B'); + } else { + PrintAndLogEx(FAILED, "Write to block %u w key %c ( " _RED_("fail") " ) ", b, (kt == MF_KEY_A) ? 'A' : 'B'); + } + } else { + // if success, skip to next block + break; + } + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + } } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + memcpy(data, default_key, 6); + PrintAndLogEx(INFO, "block %3d: %s", FirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(s) + b, MF_KEY_B, 0, data, sizeof(data)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.oldarg[0] & 0xff; + if (isOK == 0) { + if (b == 0) { + PrintAndLogEx(INFO, "Writing to manufacture block w key B ( " _RED_("fail") " )"); + } else { + PrintAndLogEx(FAILED, "Write to block %u w key B ( " _RED_("fail") " )", b); + } + } + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } } } } - fclose(fdump); - PrintAndLogEx(INFO, "Finish restore"); + fclose(f); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1255,11 +1052,11 @@ static int CmdHF14AMfNested(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf nested", "Execute Nested attack against MIFARE Classic card for key recovery", - "hf mf nested --single --blk 0 -a FFFFFFFFFFFF --tblk 4 --tkeya --> Single sector key recovery. Use block 0 Key A to find block 4 Key A\n" - "hf mf nested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" - "hf mf nested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" - "hf mf nested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" - "hf mf nested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k"); + "hf mf nested --single --blk 0 -a FFFFFFFFFFFF --tblk 4 --ta --> Single sector key recovery. Use block 0 Key A to find block 4 Key A\n" + "hf mf nested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" + "hf mf nested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" + "hf mf nested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" + "hf mf nested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k"); void *argtable[] = { arg_param_begin, @@ -1272,8 +1069,8 @@ static int CmdHF14AMfNested(const char *Cmd) { arg_lit0("a", NULL, "Input key specified is A key (default)"), arg_lit0("b", NULL, "Input key specified is B key"), arg_int0(NULL, "tblk", "", "Target block number"), - arg_lit0(NULL, "tkeya", "Target A key (default)"), - arg_lit0(NULL, "tkeyb", "Target B key"), + arg_lit0(NULL, "ta", "Target A key (default)"), + arg_lit0(NULL, "tb", "Target B key"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to file"), arg_lit0(NULL, "single", "Single sector (defaults to All)"), @@ -1292,26 +1089,26 @@ static int CmdHF14AMfNested(const char *Cmd) { uint8_t blockNo = arg_get_u32_def(ctx, 6, 0); - uint8_t keyType = 0; + uint8_t keyType = MF_KEY_A; if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 8)) { - keyType = 1; + keyType = MF_KEY_B; } uint8_t trgBlockNo = arg_get_u32_def(ctx, 9, 0); - uint8_t trgKeyType = 0; + uint8_t trgKeyType = MF_KEY_A; if (arg_get_lit(ctx, 10) && arg_get_lit(ctx, 11)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Target key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 11)) { - trgKeyType = 1; + trgKeyType = MF_KEY_B; } bool transferToEml = arg_get_lit(ctx, 12); @@ -1395,7 +1192,7 @@ static int CmdHF14AMfNested(const char *Cmd) { } mfEmlGetMem(keyBlock, sectortrailer, 1); - if (!trgKeyType) + if (trgKeyType == MF_KEY_A) num_to_bytes(key64, 6, keyBlock); else num_to_bytes(key64, 6, &keyBlock[10]); @@ -1440,7 +1237,7 @@ static int CmdHF14AMfNested(const char *Cmd) { // nested sectors bool calibrate = true; - for (trgKeyType = 0; trgKeyType < 2; ++trgKeyType) { + for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) { @@ -1493,7 +1290,7 @@ static int CmdHF14AMfNested(const char *Cmd) { mf_readblock_t payload; payload.blockno = sectrail; - payload.keytype = 0; + payload.keytype = MF_KEY_A; num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A @@ -1597,14 +1394,14 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { uint8_t blockNo = arg_get_u32_def(ctx, 6, 0); - uint8_t keyType = 0; + uint8_t keyType = MF_KEY_A; if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; } else if (arg_get_lit(ctx, 8)) { - keyType = 1; + keyType = MF_KEY_B; } bool transferToEml = arg_get_lit(ctx, 9); @@ -1639,7 +1436,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { sector_t *e_sector = NULL; - uint8_t trgKeyType = 0; + uint8_t trgKeyType = MF_KEY_A; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; @@ -1691,7 +1488,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { PrintAndLogEx(SUCCESS, "enter static nested key recovery"); // nested sectors - for (trgKeyType = 0; trgKeyType < 2; ++trgKeyType) { + for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < 1; i++) { @@ -1739,7 +1536,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { mf_readblock_t payload; payload.blockno = sectrail; - payload.keytype = 0; + payload.keytype = MF_KEY_A; num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A @@ -1808,157 +1605,157 @@ jumptoend: } static int CmdHF14AMfNestedHard(const char *Cmd) { - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t trgBlockNo = 0; - uint8_t trgKeyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t trgkey[6] = {0, 0, 0, 0, 0, 0}; - uint8_t cmdp = 0; - char filename[FILE_PATH_SIZE] = {0}; - char szTemp[FILE_PATH_SIZE - 20]; - char ctmp; - bool know_target_key = false; - bool nonce_file_read = false; - bool nonce_file_write = false; - bool slow = false; - int tests = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf hardnested", + "Nested attack for hardened MIFARE Classic cards.\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 --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" + ); - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf14_hardnested(); - case 'r': { - char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); - if (fptr == NULL) - strncpy(filename, "nonces.bin", FILE_PATH_SIZE - 1); - else - strncpy(filename, fptr, FILE_PATH_SIZE - 1); + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Key, 12 hex bytes"), // 1 + arg_int0(NULL, "blk", "", "Input block number"), // 2 + arg_lit0("a", NULL, "Input key A (def)"), // 3 + arg_lit0("b", NULL, "Input key B"), + arg_int0(NULL, "tblk", "", "Target block number"), + arg_lit0(NULL, "ta", "Target key A"), + arg_lit0(NULL, "tb", "Target key B"), + arg_str0(NULL, "tk", "", "Target key, 12 hex bytes"), // 8 + arg_str0("u", "uid", "", "R/W `hf-mf--nonces.bin` instead of default name"), + arg_str0("f", "file", "", "R/W instead of default name"), + arg_lit0("r", "read", "Read `hf-mf--nonces.bin` if tag present, otherwise `nonces.bin`, and start attack"), + arg_lit0("s", "slow", "Slower acquisition (required by some non standard cards)"), + arg_lit0("t", "tests", "Run tests"), + arg_lit0("w", "wr", "Acquire nonces and UID, and write them to file `hf-mf--nonces.bin`"), - free(fptr); - nonce_file_read = true; - if (!param_gethex(Cmd, cmdp + 1, trgkey, 12)) { - know_target_key = true; - } - cmdp++; - break; - } - case 't': - tests = param_get32ex(Cmd, cmdp + 1, 100, 10); - if (!param_gethex(Cmd, cmdp + 2, trgkey, 12)) { - know_target_key = true; - } - cmdp += 2; - break; - default: - if (param_getchar(Cmd, cmdp) == 0x00) { - PrintAndLogEx(WARNING, "Block number is missing"); - return usage_hf14_hardnested(); - } - - blockNo = param_get8(Cmd, cmdp); - ctmp = tolower(param_getchar(Cmd, cmdp + 1)); - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "Key type must be A or B"); - return 1; - } - - if (ctmp != 'a') { - keyType = 1; - } - - if (param_gethex(Cmd, cmdp + 2, key, 12)) { - PrintAndLogEx(WARNING, "Key must include 12 HEX symbols"); - return 1; - } - - if (param_getchar(Cmd, cmdp + 3) == 0x00) { - PrintAndLogEx(WARNING, "Target block number is missing"); - return 1; - } - - trgBlockNo = param_get8(Cmd, cmdp + 3); - - ctmp = tolower(param_getchar(Cmd, cmdp + 4)); - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "Target key type must be A or B"); - return 1; - } - if (ctmp != 'a') { - trgKeyType = 1; - } - cmdp += 5; - } - if (!param_gethex(Cmd, cmdp, trgkey, 12)) { - know_target_key = true; - cmdp++; - } - - while ((ctmp = param_getchar(Cmd, cmdp))) { - switch (tolower(ctmp)) { - case 's': - slow = true; - break; - case 'w': { - nonce_file_write = true; - char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); - if (fptr == NULL) - return 1; - strncpy(filename, fptr, FILE_PATH_SIZE - 1); - free(fptr); - break; - } - case 'u': - param_getstr(Cmd, cmdp + 1, szTemp, FILE_PATH_SIZE - 20); - snprintf(filename, FILE_PATH_SIZE, "hf-mf-%s-nonces.bin", szTemp); - cmdp++; - break; - case 'f': - param_getstr(Cmd, cmdp + 1, szTemp, FILE_PATH_SIZE - 20); - strncpy(filename, szTemp, FILE_PATH_SIZE - 20); - cmdp++; - break; - case 'i': - SetSIMDInstr(SIMD_AUTO); - ctmp = tolower(param_getchar(Cmd, cmdp + 1)); - switch (ctmp) { -#if defined(COMPILER_HAS_SIMD_AVX512) - case '5': - SetSIMDInstr(SIMD_AVX512); - break; -#endif + arg_lit0(NULL, "in", "None (use CPU regular instruction set)"), #if defined(COMPILER_HAS_SIMD) - case '2': - SetSIMDInstr(SIMD_AVX2); - break; - case 'a': - SetSIMDInstr(SIMD_AVX); - break; - case 's': - SetSIMDInstr(SIMD_SSE2); - break; - case 'm': - SetSIMDInstr(SIMD_MMX); - break; + arg_lit0(NULL, "im", "MMX"), + arg_lit0(NULL, "is", "SSE2"), + arg_lit0(NULL, "ia", "AVX"), + arg_lit0(NULL, "i2", "AVX2"), #endif - case 'n': - SetSIMDInstr(SIMD_NONE); - break; - default: - PrintAndLogEx(WARNING, "Unknown SIMD type. %c", ctmp); - return 1; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", ctmp); - usage_hf14_hardnested(); - return 1; - } - cmdp++; +#if defined(COMPILER_HAS_SIMD_AVX512) + arg_lit0(NULL, "i5", "AVX512"), +#endif + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); + + uint8_t blockno = arg_get_u32_def(ctx, 2, 0); + + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 4)) { + keytype = MF_KEY_B; } - if (!know_target_key && nonce_file_read == false) { + uint8_t trg_blockno = arg_get_u32_def(ctx, 5, 0); + + uint8_t trg_keytype = MF_KEY_A; + if (arg_get_lit(ctx, 6) && arg_get_lit(ctx, 7)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 7)) { + trg_keytype = MF_KEY_B; + } + + int trg_keylen = 0; + uint8_t trg_key[6] = {0}; + CLIGetHexWithReturn(ctx, 8, trg_key, &trg_keylen); + + int uidlen = 0; + char uid[14] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)uid, sizeof(uid), &uidlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 10), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool nonce_file_read = arg_get_lit(ctx, 11); + bool slow = arg_get_lit(ctx, 12); + bool tests = arg_get_lit(ctx, 13); + bool nonce_file_write = arg_get_lit(ctx, 14); + + bool in = arg_get_lit(ctx, 15); +#if defined(COMPILER_HAS_SIMD) + bool im = arg_get_lit(ctx, 16); + bool is = arg_get_lit(ctx, 17); + bool ia = arg_get_lit(ctx, 18); + bool i2 = arg_get_lit(ctx, 19); +#endif +#if defined(COMPILER_HAS_SIMD_AVX512) + bool i5 = arg_get_lit(ctx, 20); +#endif + CLIParserFree(ctx); + + // set SIM instructions + SetSIMDInstr(SIMD_AUTO); + +#if defined(COMPILER_HAS_SIMD_AVX512) + if (i5) + SetSIMDInstr(SIMD_AVX512); +#endif + +#if defined(COMPILER_HAS_SIMD) + if (i2) + SetSIMDInstr(SIMD_AVX2); + if (ia) + SetSIMDInstr(SIMD_AVX); + if (is) + SetSIMDInstr(SIMD_SSE2); + if (im) + SetSIMDInstr(SIMD_MMX); +#endif + if (in) + SetSIMDInstr(SIMD_NONE); + + + bool know_target_key = (trg_keylen); + + if (nonce_file_read) { + char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); + if (fptr == NULL) + strncpy(filename, "nonces.bin", FILE_PATH_SIZE - 1); + else + strncpy(filename, fptr, FILE_PATH_SIZE - 1); + free(fptr); + } + + if (nonce_file_write) { + char *fptr = GenerateFilename("hf-mf-", "-nonces.bin"); + if (fptr == NULL) + return 1; + strncpy(filename, fptr, FILE_PATH_SIZE - 1); + free(fptr); + } + + if (uidlen) { + snprintf(filename, FILE_PATH_SIZE, "hf-mf-%s-nonces.bin", uid); + } + + if (know_target_key == false && nonce_file_read == false) { // check if tag doesn't have static nonce if (detect_classic_static_nonce() == NONCE_STATIC) { @@ -1969,25 +1766,25 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { uint64_t key64 = 0; // check if we can authenticate to sector - if (mfCheckKeys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to block: %3d key type: %c", blockNo, keyType ? 'B' : 'A'); - return 3; + if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to block: %3d key type: %c", blockno, (keytype == MF_KEY_B) ? 'B' : 'A'); + return PM3_EWRONGANSWER; } } - PrintAndLogEx(INFO, "Target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s", - trgBlockNo, - trgKeyType ? 'B' : 'A', - trgkey[0], trgkey[1], trgkey[2], trgkey[3], trgkey[4], trgkey[5], + PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), + 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)" ); - PrintAndLogEx(INFO, "File action: %s, Slow: %s, Tests: %d ", + PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", slow ? "Yes" : "No", tests); uint64_t foundkey = 0; - int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); + 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); if ((tests == 0) && IfPm3Iso14443a()) { DropField(); @@ -2010,149 +1807,173 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { } static int CmdHF14AMfAutoPWN(const char *Cmd) { - // Nested and Hardnested parameter - uint8_t blockNo = 0; - uint8_t keyType = 0; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf autopwn", + "This command automates the key recovery process on MIFARE Classic cards.\n" + "It uses the fchk, chk, darkside, nested, hardnested and staticnested to recover keys.\n" + "If all keys are found, it try dumping card content both to file and emulator memory.", + "hf mf autopwn\n" + "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF --> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'\n" + "hf mf autopwn --1k -f mfc_default_keys --> target MFC 1K card, default dictionary\n" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys --> combo of the two above samples" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Known key, 12 hex bytes"), + arg_int0("s", "sector", "", "Input sector number"), + 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("l", "legacy", "legacy mode (use the slow `hf mf chk`)"), + arg_lit0("v", "verbose", "verbose output (statistics)"), + + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + + arg_lit0(NULL, "in", "None (use CPU regular instruction set)"), +#if defined(COMPILER_HAS_SIMD) + arg_lit0(NULL, "im", "MMX"), + arg_lit0(NULL, "is", "SSE2"), + arg_lit0(NULL, "ia", "AVX"), + arg_lit0(NULL, "i2", "AVX2"), +#endif +#if defined(COMPILER_HAS_SIMD_AVX512) + arg_lit0(NULL, "i5", "AVX512"), +#endif + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int keylen = 0; uint8_t key[6] = {0}; + int32_t res = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing key bytes"); + return PM3_EINVARG; + } + + bool know_target_key = (keylen == 6); + + uint8_t sectorno = arg_get_u32_def(ctx, 2, 0); + + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 4)) { + keytype = MF_KEY_B; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool has_filename = (fnlen > 0); + + bool slow = arg_get_lit(ctx, 6); + bool legacy_mfchk = arg_get_lit(ctx, 7); + bool verbose = arg_get_lit(ctx, 8); + + bool m0 = arg_get_lit(ctx, 9); + bool m1 = arg_get_lit(ctx, 10); + bool m2 = arg_get_lit(ctx, 11); + bool m4 = arg_get_lit(ctx, 12); + + bool in = arg_get_lit(ctx, 13); +#if defined(COMPILER_HAS_SIMD) + bool im = arg_get_lit(ctx, 14); + bool is = arg_get_lit(ctx, 15); + bool ia = arg_get_lit(ctx, 16); + bool i2 = arg_get_lit(ctx, 17); +#endif +#if defined(COMPILER_HAS_SIMD_AVX512) + bool i5 = arg_get_lit(ctx, 18); +#endif + 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 sector_cnt = MIFARE_1K_MAXSECTOR; + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + + if (m0) { + sector_cnt = MIFARE_MINI_MAXSECTOR; + block_cnt = MIFARE_MINI_MAXBLOCK; + } else if (m1) { + sector_cnt = MIFARE_1K_MAXSECTOR; + block_cnt = MIFARE_1K_MAXBLOCK; + } else if (m2) { + sector_cnt = MIFARE_2K_MAXSECTOR; + block_cnt = MIFARE_2K_MAXBLOCK; + } else if (m4) { + sector_cnt = MIFARE_4K_MAXSECTOR; + block_cnt = MIFARE_4K_MAXBLOCK; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + + // set SIM instructions + SetSIMDInstr(SIMD_AUTO); + +#if defined(COMPILER_HAS_SIMD_AVX512) + if (i5) + SetSIMDInstr(SIMD_AVX512); +#endif + +#if defined(COMPILER_HAS_SIMD) + if (i2) + SetSIMDInstr(SIMD_AVX2); + if (ia) + SetSIMDInstr(SIMD_AVX); + if (is) + SetSIMDInstr(SIMD_SSE2); + if (im) + SetSIMDInstr(SIMD_MMX); +#endif + if (in) + SetSIMDInstr(SIMD_NONE); + + + // Nested and Hardnested parameter uint64_t key64 = 0; bool calibrate = true; // Attack key storage variables uint8_t *keyBlock = NULL; uint32_t key_cnt = 0; sector_t *e_sector; - uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR; - int block_cnt = MIFARE_1K_MAXBLOCK; uint8_t tmp_key[6] = {0}; - bool know_target_key = false; - // For the timer - uint64_t t1; - // Parameters and dictionary file - char filename[FILE_PATH_SIZE] = {0}; - uint8_t cmdp = 0; - char ctmp; + // Nested and Hardnested returned status uint64_t foundkey = 0; int isOK = 0; int current_sector_i = 0, current_key_type_i = 0; // Dumping and transfere to simulater memory uint8_t block[16] = {0x00}; - uint8_t *dump; int bytes; // Settings - bool slow = false; - bool legacy_mfchk = false; int prng_type = PM3_EUNDEF; int has_staticnonce; - bool verbose = false; - bool has_filename = false; - bool errors = false; uint8_t num_found_keys = 0; - // Parse the options given by the user - while ((ctmp = param_getchar(Cmd, cmdp)) && !errors) { - switch (tolower(ctmp)) { - case 'h': - return usage_hf14_autopwn(); - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - } else { - has_filename = true; - } - cmdp += 2; - break; - case 'l': - legacy_mfchk = true; - cmdp++; - break; - case 'v': - verbose = true; - cmdp++; - break; - case '*': - // Get the number of sectors - sectors_cnt = NumOfSectors(param_getchar(Cmd, cmdp + 1)); - block_cnt = NumOfBlocks(param_getchar(Cmd, cmdp + 1)); - cmdp += 2; - break; - case 'k': - // Get the known block number - if (param_getchar(Cmd, cmdp + 1) == 0x00) { - errors = true; - break; - } - - blockNo = param_get8(Cmd, cmdp + 1); - - // Get the knonwn block type - ctmp = tolower(param_getchar(Cmd, cmdp + 2)); - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "Key type must be A or B"); - errors = true; - break; - } - - if (ctmp != 'a') { - keyType = 1; - } - - // Get the known block key - if (param_gethex(Cmd, cmdp + 3, key, 12)) { - PrintAndLogEx(WARNING, "Key must include 12 HEX symbols"); - errors = true; - } - know_target_key = true; - cmdp += 3; - case 's': - slow = true; - cmdp++; - break; - case 'i': - SetSIMDInstr(SIMD_AUTO); - ctmp = tolower(param_getchar(Cmd, cmdp + 1)); - switch (ctmp) { -#if defined(COMPILER_HAS_SIMD_AVX512) - case '5': - SetSIMDInstr(SIMD_AVX512); - break; -#endif -#if defined(COMPILER_HAS_SIMD) - case '2': - SetSIMDInstr(SIMD_AVX2); - break; - case 'a': - SetSIMDInstr(SIMD_AVX); - break; - case 's': - SetSIMDInstr(SIMD_SSE2); - break; - case 'm': - SetSIMDInstr(SIMD_MMX); - break; -#endif - case 'n': - SetSIMDInstr(SIMD_NONE); - break; - default: - PrintAndLogEx(WARNING, "Unknown SIMD type. %c", ctmp); - return PM3_EINVARG; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", ctmp); - return usage_hf14_autopwn(); - } - } - - if (errors) { - return usage_hf14_autopwn(); - } +// ------------------------------ // create/initialize key storage structure - int32_t res = initSectorTable(&e_sector, sectors_cnt); - if (res != sectors_cnt) { + res = initSectorTable(&e_sector, sector_cnt); + if (res != sector_cnt) { free(e_sector); return PM3_EMALLOC; } @@ -2177,10 +1998,10 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // print parameters if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); - PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sectors_cnt); + PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), know_target_key ? "True" : "False"); - PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), blockNo); - PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), keyType ? 'B' : 'A'); + 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))); if (has_staticnonce == NONCE_STATIC) @@ -2197,7 +2018,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Start the timer - t1 = msclock(); + uint64_t t1 = msclock(); // check the user supplied key if (know_target_key == false) { @@ -2207,31 +2028,31 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START KNOWN KEY ATTACK") " ======================="); } - if (mfCheckKeys(FirstBlockOfSector(blockNo), keyType, true, 1, key, &key64) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "target sector:%3u key type: %c -- using valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)", - blockNo, - keyType ? 'B' : 'A', + if (mfCheckKeys(FirstBlockOfSector(sectorno), keytype, true, 1, key, &key64) == PM3_SUCCESS) { + PrintAndLogEx(INFO, "target sector %3u key type %c -- using valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)", + sectorno, + (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex(key, sizeof(key)) ); // Store the key for the nested / hardnested attack (if supplied by the user) - e_sector[blockNo].Key[keyType] = key64; - e_sector[blockNo].foundKey[keyType] = 'U'; + e_sector[sectorno].Key[keytype] = key64; + e_sector[sectorno].foundKey[keytype] = 'U'; ++num_found_keys; } else { know_target_key = false; - PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector:"_RED_("%3d") " key type: "_RED_("%c") " key: " _RED_("%s"), - blockNo, - keyType ? 'B' : 'A', + 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', sprint_hex(key, sizeof(key)) ); PrintAndLogEx(WARNING, "falling back to dictionary"); } // Check if the user supplied key is used by other sectors - for (int i = 0; i < sectors_cnt; i++) { - for (int j = 0; j < 2; j++) { + for (int i = 0; i < sector_cnt; i++) { + for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { if (e_sector[i].foundKey[j] == 0) { if (mfCheckKeys(FirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(key, 6); @@ -2241,18 +2062,18 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (know_target_key == false) { num_to_bytes(e_sector[i].Key[j], 6, key); know_target_key = true; - blockNo = i; - keyType = j; - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)", + sectorno = i; + keytype = j; + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, - j ? 'B' : 'A', - sprint_hex(key, sizeof(key)) + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(key, sizeof(key)) ); } else { - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, - j ? 'B' : 'A', - sprint_hex(key, sizeof(key)) + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(key, sizeof(key)) ); } ++num_found_keys; @@ -2261,7 +2082,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } } - if (num_found_keys == sectors_cnt * 2) { + if (num_found_keys == sector_cnt * 2) { goto all_found; } } @@ -2300,7 +2121,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (legacy_mfchk) { PrintAndLogEx(INFO, "." NOLF); // Check all the sectors - for (int i = 0; i < sectors_cnt; i++) { + for (int i = 0; i < sector_cnt; i++) { for (int j = 0; j < 2; j++) { // Check if the key is known if (e_sector[i].foundKey[j] == 0) { @@ -2340,7 +2161,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (size == key_cnt - i) lastChunk = true; - res = mfCheckKeys_fast(sectors_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); + res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); if (firstChunk) firstChunk = false; // all keys, aborted @@ -2356,8 +2177,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Analyse the dictionary attack - for (int i = 0; i < sectors_cnt; i++) { - for (int j = 0; j < 2; j++) { + for (int i = 0; i < sector_cnt; i++) { + for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { if (e_sector[i].foundKey[j] == 1) { e_sector[i].foundKey[j] = 'D'; num_to_bytes(e_sector[i].Key[j], 6, tmp_key); @@ -2366,18 +2187,18 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (know_target_key == false) { num_to_bytes(e_sector[i].Key[j], 6, key); know_target_key = true; - blockNo = i; - keyType = j; - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "] (used for nested / hardnested attack)", + sectorno = i; + keytype = j; + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, - j ? 'B' : 'A', - sprint_hex(tmp_key, sizeof(tmp_key)) + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, - j ? 'B' : 'A', - sprint_hex(tmp_key, sizeof(tmp_key)) + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } } @@ -2391,7 +2212,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); } - isOK = mfDarkside(FirstBlockOfSector(blockNo), keyType + 0x60, &key64); + isOK = mfDarkside(FirstBlockOfSector(sectorno), keytype + 0x60, &key64); switch (isOK) { case -1 : @@ -2411,17 +2232,17 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(WARNING, "\nAborted via keyboard."); goto noValidKeyFound; default : - PrintAndLogEx(SUCCESS, "\nFound valid key: [ " _GREEN_("%012" PRIx64) " ]\n", key64); + PrintAndLogEx(SUCCESS, "\nFound valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); break; } // Store the keys num_to_bytes(key64, 6, key); - e_sector[blockNo].Key[keyType] = key64; - e_sector[blockNo].foundKey[keyType] = 'S'; - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", - blockNo, - keyType ? 'B' : 'A', + e_sector[sectorno].Key[keytype] = key64; + e_sector[sectorno].foundKey[keytype] = 'S'; + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", + sectorno, + (keytype == MF_KEY_B) ? 'B' : 'A', key64 ); } else { @@ -2440,7 +2261,7 @@ noValidKeyFound: bool nested_failed = false; // Iterate over each sector and key(A/B) - for (current_sector_i = 0; current_sector_i < sectors_cnt; current_sector_i++) { + for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { for (current_key_type_i = 0; current_key_type_i < 2; current_key_type_i++) { // If the key is already known, just skip it @@ -2448,10 +2269,10 @@ noValidKeyFound: // Try the found keys are reused if (bytes_to_num(tmp_key, 6) != 0) { - // The fast check --> mfCheckKeys_fast(sectors_cnt, true, true, 2, 1, tmp_key, e_sector, false); + // The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false); // Returns false keys, so we just stick to the slower mfchk. - for (int i = 0; i < sectors_cnt; i++) { - for (int j = 0; j < 2; j++) { + for (int i = 0; i < sector_cnt; i++) { + for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the sector key is already broken if (e_sector[i].foundKey[j]) continue; @@ -2460,10 +2281,10 @@ noValidKeyFound: if (mfCheckKeys(FirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, 6); e_sector[i].foundKey[j] = 'R'; - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, - j ? 'B' : 'A', - sprint_hex(tmp_key, sizeof(tmp_key)) + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } } @@ -2472,19 +2293,19 @@ noValidKeyFound: // Clear the last found key num_to_bytes(0, 6, tmp_key); - if (current_key_type_i == 1) { + if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " ======================="); - PrintAndLogEx(INFO, "reading B key: sector: %3d key type: %c", + PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c", current_sector_i, - current_key_type_i ? 'B' : 'A'); + (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } uint8_t sectrail = (FirstBlockOfSector(current_sector_i) + NumBlocksPerSector(current_sector_i) - 1); mf_readblock_t payload; payload.blockno = sectrail; - payload.keytype = 0; + payload.keytype = MF_KEY_A; num_to_bytes(e_sector[current_sector_i].Key[0], 6, payload.key); // KEY A @@ -2502,16 +2323,16 @@ noValidKeyFound: e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; num_to_bytes(key64, 6, tmp_key); - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, - current_key_type_i ? 'B' : 'A', - sprint_hex(tmp_key, sizeof(tmp_key)) + (current_key_type_i == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { if (verbose) { PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c", current_sector_i, - current_key_type_i ? 'B' : 'A' + (current_key_type_i == MF_KEY_B) ? 'B' : 'A' ); PrintAndLogEx(INFO, " -- reading the B key was not possible, maybe due to access rights?"); @@ -2532,12 +2353,12 @@ skipReadBKey: uint8_t retries = 0; if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no: %3d, target key type: %c", + PrintAndLogEx(INFO, "sector no %3d, target key type %c", current_sector_i, - current_key_type_i ? 'B' : 'A'); + (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } tryNested: - isOK = mfnested(FirstBlockOfSector(blockNo), keyType, key, FirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); + isOK = mfnested(FirstBlockOfSector(sectorno), keytype, key, FirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key, calibrate); switch (isOK) { case PM3_ETIMEOUT: { @@ -2589,22 +2410,22 @@ tryNested: tryHardnested: // If the nested attack fails then we try the hardnested attack if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no: %3d, target key type: %c, Slow: %s", + PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s", current_sector_i, - current_key_type_i ? 'B' : 'A', + (current_key_type_i == MF_KEY_B) ? 'B' : 'A', slow ? "Yes" : "No"); } - isOK = mfnestedhard(FirstBlockOfSector(blockNo), keyType, key, FirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL); + isOK = mfnestedhard(FirstBlockOfSector(sectorno), keytype, key, FirstBlockOfSector(current_sector_i), current_key_type_i, NULL, false, false, slow, 0, &foundkey, NULL); DropField(); if (isOK) { switch (isOK) { case 1: { - PrintAndLogEx(ERR, "\nError: No response from Proxmark3."); + PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); break; } case 2: { - PrintAndLogEx(NORMAL, "\nButton pressed. Aborted."); + PrintAndLogEx(NORMAL, "\nButton pressed, user aborted"); break; } default: { @@ -2626,22 +2447,22 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack tryStaticnested: if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no: %3d, target key type: %c", + PrintAndLogEx(INFO, "sector no %3d, target key type %c", current_sector_i, - current_key_type_i ? 'B' : 'A'); + (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } - isOK = mfStaticNested(blockNo, keyType, key, FirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); + isOK = mfStaticNested(sectorno, keytype, key, FirstBlockOfSector(current_sector_i), current_key_type_i, tmp_key); DropField(); switch (isOK) { case PM3_ETIMEOUT: { - PrintAndLogEx(ERR, "\nError: No response from Proxmark3."); + PrintAndLogEx(ERR, "\nError: No response from Proxmark3"); free(e_sector); free(fptr); return PM3_ESOFT; } case PM3_EOPABORTED: { - PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); + PrintAndLogEx(WARNING, "\nButton pressed, user aborted"); free(e_sector); free(fptr); return PM3_EOPABORTED; @@ -2659,10 +2480,10 @@ tryStaticnested: // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i]) { - PrintAndLogEx(SUCCESS, "target sector:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", + PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, - current_key_type_i ? 'B' : 'A', - sprint_hex(tmp_key, sizeof(tmp_key)) + (current_key_type_i == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } } @@ -2676,12 +2497,12 @@ all_found: PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectors_cnt, e_sector); + printKeyTable(sector_cnt, e_sector); // Dump the keys PrintAndLogEx(NORMAL, ""); - if (createMfcKeyDump(fptr, sectors_cnt, e_sector) != PM3_SUCCESS) { + if (createMfcKeyDump(fptr, sector_cnt, e_sector) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to save keys to file"); } @@ -2691,7 +2512,7 @@ all_found: PrintAndLogEx(SUCCESS, "transferring keys to simulator memory (Cmd Error: 04 can occur)"); - for (current_sector_i = 0; current_sector_i < sectors_cnt; current_sector_i++) { + for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { mfEmlGetMem(block, current_sector_i, 1); if (e_sector[current_sector_i].foundKey[0]) num_to_bytes(e_sector[current_sector_i].Key[0], 6, block); @@ -2702,17 +2523,16 @@ all_found: } // use ecfill trick - FastDumpWithEcFill(sectors_cnt); + FastDumpWithEcFill(sector_cnt); bytes = block_cnt * MFBLOCK_SIZE; - dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { PrintAndLogEx(ERR, "Fail, cannot allocate memory"); free(e_sector); free(fptr); return PM3_EMALLOC; } - memset(dump, 0, bytes); PrintAndLogEx(INFO, "downloading the card content from emulator memory"); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { @@ -2723,15 +2543,16 @@ all_found: return PM3_ETIMEOUT; } - char *fnameptr = GenerateFilename("hf-mf-", "-dump"); - if (fnameptr == NULL) { + free(fptr); + fptr = GenerateFilename("hf-mf-", "-dump"); + if (fptr == NULL) { free(dump); free(e_sector); free(fptr); return PM3_ESOFT; } - strcpy(filename, fnameptr); - free(fnameptr); + strcpy(filename, fptr); + free(fptr); saveFile(filename, ".bin", dump, bytes); saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); @@ -2743,33 +2564,9 @@ all_found: free(dump); free(e_sector); - free(fptr); return PM3_SUCCESS; } -/* -static int randInRange(int min, int max) { - return min + (int)(rand() / (double)(RAND_MAX) * (max - min + 1)); -} -*/ - -//Fisher–Yates shuffle -/* -static void shuffle(uint8_t *array, uint16_t len) { - uint8_t tmp[6]; - uint16_t x; - time_t t; - srand((unsigned) time(&t)); - while (len) { - x = randInRange(0, (len -= 6)) | 0; // 0 = i < n - x %= 6; - memcpy(tmp, array + x, 6); - memcpy(array + x, array + len, 6); - memcpy(array + len, tmp, 6); - } -} -*/ - static int CmdHF14AMfChk_fast(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf fchk", @@ -2793,7 +2590,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to binary file"), arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), - arg_str0("f", "file", "", "filename of dictionary"), + arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3024,8 +2821,8 @@ out: conn.block_after_ACK = true; uint8_t block[16] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { - uint8_t blockno = FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1; - mfEmlGetMem(block, blockno, 1); + uint8_t b = FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1; + mfEmlGetMem(block, b, 1); if (e_sector[i].foundKey[0]) num_to_bytes(e_sector[i].Key[0], 6, block); @@ -3037,7 +2834,7 @@ out: // Disable fast mode on last packet conn.block_after_ACK = false; } - mfEmlSetMem(block, blockno, 1); + mfEmlSetMem(block, b, 1); } PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); @@ -3103,9 +2900,9 @@ static int CmdHF14AMfChk(const char *Cmd) { if ((arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) || arg_get_lit(ctx, 5)) { keyType = 2; } else if (arg_get_lit(ctx, 3)) { - keyType = 0; + keyType = MF_KEY_A; } else if (arg_get_lit(ctx, 4)) { - keyType = 1; + keyType = MF_KEY_B; } bool m0 = arg_get_lit(ctx, 6); @@ -3123,7 +2920,6 @@ static int CmdHF14AMfChk(const char *Cmd) { CLIParserFree(ctx); //validations - if ((m0 + m1 + m2 + m4) > 1) { PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; @@ -3265,7 +3061,7 @@ static int CmdHF14AMfChk(const char *Cmd) { return PM3_EMALLOC; } - uint8_t trgKeyType = 0; + uint8_t trgKeyType = MF_KEY_A; uint16_t max_keys = keycnt > KEYS_IN_BLOCK ? KEYS_IN_BLOCK : keycnt; PrintAndLogEx(INFO, "Start check for keys..."); @@ -3317,7 +3113,7 @@ static int CmdHF14AMfChk(const char *Cmd) { PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - if (keyType != 1) { + if (keyType != MF_KEY_B) { PrintAndLogEx(INFO, "testing to read key B..."); // loop sectors but block is used as to keep track of from which blocks to test @@ -3334,7 +3130,7 @@ static int CmdHF14AMfChk(const char *Cmd) { mf_readblock_t payload; payload.blockno = sectrail; - payload.keytype = 0; + payload.keytype = MF_KEY_A; // Use key A num_to_bytes(e_sector[i].Key[0], 6, payload.key); @@ -3441,7 +3237,7 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo uint8_t keytype = data.keytype; PrintAndLogEx(INFO, "Reader is trying authenticate with: Key %s, sector %02d: [%012" PRIx64 "]" - , keytype ? "B" : "A" + , (keytype == MF_KEY_B) ? "B" : "A" , sector , key ); @@ -3469,7 +3265,7 @@ 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 card", + "Simulate MIFARE Classic card", "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" @@ -3489,7 +3285,7 @@ static int CmdHF14AMfSim(const char *Cmd) { arg_str0(NULL, "sak", "", "Provide explicit SAK (1 bytes, overrides option t)"), arg_int0("n", "num", " ", "Automatically exit simulation after blocks have been read by reader. 0 = infinite"), arg_lit0("i", "interactive", "Console will not be returned until simulation finishes or is aborted"), - arg_lit0(NULL, "crack", "Performs the 'reader attack', nr/ar attack against a reader"), + arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0("e", "emukeys", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -3655,7 +3451,7 @@ static int CmdHF14AMfSim(const char *Cmd) { /* static int CmdHF14AMfKeyBrute(const char *Cmd) { - uint8_t blockNo = 0, keytype = 0; + uint8_t blockNo = 0, keytype = MF_KEY_A; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; uint64_t foundkey = 0; @@ -3667,7 +3463,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { // keytype cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 'b') keytype = 1; + if (cmdp == 'b') keytype = MF_KEY_B; // key if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute(); @@ -3691,6 +3487,7 @@ void printKeyTable(uint8_t sectorscnt, sector_t *e_sector) { void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { char strA[12 + 1] = {0}; char strB[12 + 1] = {0}; + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "|-----|----------------|---|----------------|---|"); PrintAndLogEx(SUCCESS, "| Sec | key A |res| key B |res|"); PrintAndLogEx(SUCCESS, "|-----|----------------|---|----------------|---|"); @@ -3742,154 +3539,260 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto } else { PrintAndLogEx(SUCCESS, "( " _YELLOW_("0") ":Failed / " _YELLOW_("1") ":Success)"); } + PrintAndLogEx(NORMAL, ""); } // EMULATOR COMMANDS static int CmdHF14AMfEGetBlk(const char *Cmd) { - char c = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || c == 'h') return usage_hf14_egetblk(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf egetblk", + "Get emulator memory block", + "hf mf egetblk --blk 0 -> get block 0 (manufacturer)\n" + "hf mf egetblk --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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int b = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (b > 255) { + return PM3_EINVARG; + } + uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0x00}; - uint8_t blockNo = param_get8(Cmd, 0); + if (mfEmlGetMem(data, blockno, 1) == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, ""); - if (mfEmlGetMem(data, blockNo, 1) == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, "data[%3d]:%s", blockNo, sprint_hex(data, sizeof(data))); + uint8_t sector = GetSectorFromBlockNo(blockno); + mf_print_sector_hdr(sector); + mf_print_block(blockno, data); + } + if (verbose) { + decode_print_st(blockno, data); + } else { + PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } + static int CmdHF14AMfEGetSc(const char *Cmd) { - uint8_t data[16] = {0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf egetsc", + "Get emulator memory sector", + "hf mf egetsc -s 0" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("s", "sec", "", "sector number"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int s = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_egetsc(); - - uint8_t sector = param_get8(Cmd, 0); - if (sector > 39) { + if (s > 39) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); - return PM3_ESOFT; + return PM3_EINVARG; } - PrintAndLogEx(NORMAL, "\n # | data - sector %02d / 0x%02X ", sector, sector); - PrintAndLogEx(NORMAL, "----+------------------------------------------------"); - uint8_t blocks = 4; - uint8_t start = sector * 4; - if (sector >= 32) { - blocks = 16; - start = 128 + (sector - 32) * 16; - } + uint8_t sector = (uint8_t)s; + mf_print_sector_hdr(sector); + uint8_t blocks = NumBlocksPerSector(sector); + uint8_t start = FirstBlockOfSector(sector); + + uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { - int res = mfEmlGetMem(data, start + i, 1); if (res == PM3_SUCCESS) { - if (start + i == 0) { - PrintAndLogEx(INFO, "%03d | " _RED_("%s"), start + i, sprint_hex_ascii(data, sizeof(data))); - } else if (mfIsSectorTrailer(i)) { - PrintAndLogEx(INFO, "%03d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data, sizeof(data))); - } else { - PrintAndLogEx(INFO, "%03d | %s ", start + i, sprint_hex_ascii(data, sizeof(data))); - } + mf_print_block(start + i, data); } } - decode_print_st(start + blocks - 1, data); + if (verbose) { + decode_print_st(start + blocks - 1, data); + } else { + PrintAndLogEx(NORMAL, ""); + } return PM3_SUCCESS; } static int CmdHF14AMfEClear(const char *Cmd) { - char c = tolower(param_getchar(Cmd, 0)); - if (c == 'h') return usage_hf14_eclr(); - + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf eclr", + "It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF", + "hf mf eclr" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); return PM3_SUCCESS; } static int CmdHF14AMfESet(const char *Cmd) { - char c = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || c == 'h') - return usage_hf14_eset(); - uint8_t memBlock[16]; - memset(memBlock, 0x00, sizeof(memBlock)); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf esetblk", + "Set emulator memory block", + "hf mf esetblk --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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t blockNo = param_get8(Cmd, 0); + int b = arg_get_int_def(ctx, 1, 0); - if (param_gethex(Cmd, 1, memBlock, 32)) { - PrintAndLogEx(WARNING, "block data must include 32 HEX symbols"); - return PM3_ESOFT; + uint8_t data[16] = {0x00}; + int datalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &datalen); + CLIParserFree(ctx); + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + if (b > 255) { + return PM3_EINVARG; + } + + if (datalen != sizeof(data)) { + PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", datalen); + return PM3_EINVARG; } // 1 - blocks count - return mfEmlSetMem(memBlock, blockNo, 1); + return mfEmlSetMem(data, b, 1); } int CmdHF14AMfELoad(const char *Cmd) { - size_t counter = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf eload", + "Load emulator memory with data from (bin/eml/json) dump file", + "hf mf eload -f hf-mf-01020304.bin\n" + "hf mf eload --4k -f hf-mf-01020304.eml\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump"), + 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(NULL, "ul", "MIFARE Ultralight family"), + arg_int0("q", "qty", "", "manually set number of blocks (overrides)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; char filename[FILE_PATH_SIZE]; - int blockNum, numBlocks, nameParamNo = 1; - uint8_t blockWidth = 16; - char c = tolower(param_getchar(Cmd, 0)); + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - if (strlen(Cmd) < 2 && c == 'h') - return usage_hf14_eload(); + 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); + bool mu = arg_get_lit(ctx, 6); - switch (c) { - case '0' : - numBlocks = MIFARE_MINI_MAXBLOCK; - break; - case '1' : - case '\0': - numBlocks = MIFARE_1K_MAXBLOCK; - break; - case '2' : - numBlocks = MIFARE_2K_MAXBLOCK; - break; - case '4' : - numBlocks = MIFARE_4K_MAXBLOCK; - break; - case 'u' : - numBlocks = 255; - blockWidth = 4; - break; - default: { - numBlocks = MIFARE_1K_MAXBLOCK; - nameParamNo = 0; - } + int numblks = arg_get_int_def(ctx, 7, -1); + + CLIParserFree(ctx); + + // validations + if ((m0 + m1 + m2 + m4 + mu) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4 + mu) == 0) { + m1 = true; } - uint32_t numblk2 = param_get32ex(Cmd, 2, 0, 10); - if (numblk2 > 0) - numBlocks = numblk2; - if (0 == param_getstr(Cmd, nameParamNo, filename, sizeof(filename))) - return usage_hf14_eload(); + uint8_t block_width = 16; + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; - uint8_t *data = calloc(4096, sizeof(uint8_t)); + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + } else if (mu) { + block_cnt = 255; + block_width = 4; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "%d blocks ( %u bytes ) to upload", block_cnt, block_cnt * block_width); + + if (numblks > 0) { + block_cnt = MIN(numblks, block_cnt); + PrintAndLogEx(INFO, "overriding number of blocks, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); + } + + uint8_t *data = calloc(MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK, sizeof(uint8_t)); if (data == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } size_t datalen = 0; - //int res = loadFile(filename, ".bin", data, maxdatalen, &datalen); - int res = loadFileEML(filename, data, &datalen); + int res = PM3_SUCCESS; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&data, &datalen); + break; + } + case JSON: { + res = loadFileJSON(filename, data, MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, &datalen, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + free(data); + return PM3_EINVARG; + } + } + if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } // 64 or 256 blocks. - if ((datalen % blockWidth) != 0) { + if ((datalen % block_width) != 0) { PrintAndLogEx(FAILED, "File content error. Size doesn't match blockwidth "); free(data); return PM3_ESOFT; } // convert plain or old mfu format to new format - if (blockWidth == 4) { - + if (block_width == 4) { res = convert_mfu_dump_format(&data, &datalen, true); if (res != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Failed convert on load to new Ultralight/NTAG format"); @@ -3901,7 +3804,8 @@ int CmdHF14AMfELoad(const char *Cmd) { printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0); // update expected blocks to match converted data. - numBlocks = datalen / 4; + block_cnt = datalen / 4; + PrintAndLogEx(INFO, "MIFARE Ultralight override, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); } PrintAndLogEx(INFO, "Uploading to emulator memory"); @@ -3909,91 +3813,122 @@ int CmdHF14AMfELoad(const char *Cmd) { // fast push mode conn.block_after_ACK = true; - blockNum = 0; - while (datalen) { - if (datalen == blockWidth) { + + size_t offset = 0; + int cnt = 0; + + while (datalen && cnt < block_cnt) { + if (datalen == block_width) { // Disable fast mode on last packet conn.block_after_ACK = false; } - if (mfEmlSetMem_xt(data + counter, blockNum, 1, blockWidth) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockNum); + if (mfEmlSetMem_xt(data + offset, cnt, 1, block_width) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(data); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - blockNum++; - counter += blockWidth; - datalen -= blockWidth; + cnt++; + offset += block_width; + datalen -= block_width; } free(data); PrintAndLogEx(NORMAL, "\n"); - if (blockWidth == 4) { - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mfu sim h`")); + if (block_width == 4) { + PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mfu sim -h`")); // MFU / NTAG - if ((blockNum != numBlocks)) { - PrintAndLogEx(WARNING, "Warning, Ultralight/Ntag file content, Loaded %d blocks of expected %d blocks into emulator memory", blockNum, numBlocks); + if ((cnt != block_cnt)) { + PrintAndLogEx(WARNING, "Warning, Ultralight/Ntag file content, Loaded %d blocks of expected %d blocks into emulator memory", cnt, block_cnt); return PM3_SUCCESS; } } else { PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mf sim -h`")); // MFC - if ((blockNum != numBlocks)) { - PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", blockNum, numBlocks); - + if ((cnt != block_cnt)) { + PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", cnt, block_cnt); return PM3_SUCCESS; } - } - PrintAndLogEx(SUCCESS, "Done"); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } static int CmdHF14AMfESave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf esave", + "Save emulator memory into three files (BIN/EML/JSON) ", + "hf mf esave\n" + "hf mf esave --4k\n" + "hf mf esave --4k -f hf-mf-01020304.eml" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dump"), + 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 fnlen = 0; char filename[FILE_PATH_SIZE]; - char *fnameptr = filename; - uint8_t *dump; - int len, bytes, nameParamNo = 1; - uint16_t blocks; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - memset(filename, 0, sizeof(filename)); + 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); - char c = tolower(param_getchar(Cmd, 0)); - if (c == 'h') return usage_hf14_esave(); - - if (c != 0) { - blocks = NumOfBlocks(c); - if (blocks == 0) return usage_hf14_esave(); - } else { - blocks = MIFARE_1K_MAXBLOCK; + // 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; } - bytes = blocks * MFBLOCK_SIZE; - dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + } + + int bytes = block_cnt * MFBLOCK_SIZE; + + // reserv memory + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } memset(dump, 0, bytes); - PrintAndLogEx(INFO, "downloading from emulator memory"); + 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; } - len = param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); - if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; - // user supplied filename? - if (len < 1) { - fnameptr += snprintf(fnameptr, sizeof(filename), "hf-mf-"); - FillFileNameByUID(fnameptr, dump, "-dump", 4); + if (fnlen < 1) { + char *fptr = filename; + fptr += snprintf(fptr, sizeof(filename), "hf-mf-"); + FillFileNameByUID(fptr, dump, "-dump", 4); } saveFile(filename, ".bin", dump, bytes); @@ -4005,121 +3940,189 @@ static int CmdHF14AMfESave(const char *Cmd) { static int CmdHF14AMfEView(const char *Cmd) { - uint8_t *dump; - int bytes; - uint16_t blocks; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf eview", + "It displays emulator memory", + "hf mf eview\n" + "hf mf eview --4k" + ); + 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_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); + CLIParserFree(ctx); - char c = tolower(param_getchar(Cmd, 0)); - if (c == 'h') return usage_hf14_eview(); - - if (c != 0) { - blocks = NumOfBlocks(c); - if (blocks == 0) return usage_hf14_eview(); - } else { - blocks = MIFARE_1K_MAXBLOCK; + // 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; } - bytes = blocks * MFBLOCK_SIZE; - dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + int 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; } memset(dump, 0, bytes); - PrintAndLogEx(INFO, "downloading from emulator memory"); + PrintAndLogEx(INFO, "downloading emulator memory"); 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; } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - PrintAndLogEx(INFO, "blk | data | ascii"); - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - for (uint16_t i = 0; i < blocks; i++) { - if (i == 0) { - PrintAndLogEx(INFO, "%03d | " _RED_("%s"), i, sprint_hex_ascii(dump + (i * 16), 16)); - } else if (mfIsSectorTrailer(i)) { - PrintAndLogEx(INFO, "%03d | " _YELLOW_("%s"), i, sprint_hex_ascii(dump + (i * 16), 16)); - } else { - PrintAndLogEx(INFO, "%03d | %s ", i, sprint_hex_ascii(dump + (i * 16), 16)); - } - } - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - PrintAndLogEx(NORMAL, ""); + mf_print_blocks(block_cnt, dump); free(dump); return PM3_SUCCESS; } static int CmdHF14AMfECFill(const char *Cmd) { - uint8_t keyType = 0; - uint8_t numSectors = 16; - char c = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || c == 'h') - return usage_hf14_ecfill(); - - if (c != 'a' && c != 'b') { - PrintAndLogEx(WARNING, "Key type must be A or B"); - return PM3_ESOFT; + 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", + "hf mf ecfill --> use key type A\n" + "hf mf ecfill --4k -b --> target 4K card with key type B" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("a", NULL, "input key type is key A(def)"), + arg_lit0("b", NULL, "input key type is key B"), + 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); + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 1) && arg_get_lit(ctx, 2)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 2)) { + keytype = MF_KEY_B; } - if (c != 'a') - keyType = 1; - c = tolower(param_getchar(Cmd, 1)); - if (c != 0) { - numSectors = NumOfSectors(c); - if (numSectors == 0) return usage_hf14_ecfill(); + bool m0 = arg_get_lit(ctx, 3); + bool m1 = arg_get_lit(ctx, 4); + bool m2 = arg_get_lit(ctx, 5); + bool m4 = arg_get_lit(ctx, 6); + 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 sectors_cnt = MIFARE_1K_MAXSECTOR; + + if (m0) { + sectors_cnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + sectors_cnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + sectors_cnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + sectors_cnt = MIFARE_4K_MAXSECTOR; } else { - numSectors = MIFARE_1K_MAXSECTOR; + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; } - PrintAndLogEx(NORMAL, "--params: numSectors: %d, keyType: %c\n", numSectors, (keyType == 0) ? 'A' : 'B'); - - mfc_eload_t payload; - payload.sectorcnt = numSectors; - payload.keytype = keyType; + mfc_eload_t payload = { + .sectorcnt = sectors_cnt, + .keytype = keytype + }; clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_LOAD, (uint8_t *)&payload, sizeof(payload)); + + // 2021, iceman: should get a response from device when its done. return PM3_SUCCESS; } static int CmdHF14AMfEKeyPrn(const char *Cmd) { - uint8_t sectors_cnt = MIFARE_1K_MAXSECTOR; - uint8_t data[16]; - uint8_t uid[4]; - uint8_t cmdp = 0; - bool errors = false, createDumpFile = false; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ekeyprn", + "Download and print the keys from emulator memory", + "hf mf ekeyprn --1k --> print MFC 1K keyset\n" + "hf mf ekeyprn -w --> write keys to binary file" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("w", "write", "write keys to binary file `hf-mf--key.bin`"), + 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); + + bool create_dumpfile = arg_get_lit(ctx, 1); + 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); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - char ctmp = tolower(param_getchar(Cmd, cmdp)); - switch (ctmp) { - case 'd': - createDumpFile = true; - cmdp++; - break; - case 'h': - return usage_hf14_ekeyprn(); - case '0': - case '1': - case '2': - case '4': - sectors_cnt = NumOfSectors(ctmp); - if (sectors_cnt == 0) return usage_hf14_ekeyprn(); - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } // validations - if (errors) return usage_hf14_ekeyprn(); + 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 sectors_cnt = MIFARE_1K_MAXSECTOR; + + if (m0) { + sectors_cnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + sectors_cnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + sectors_cnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + sectors_cnt = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } sector_t *e_sector = NULL; @@ -4131,12 +4134,15 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { } // read UID from EMUL + uint8_t data[16]; if (mfEmlGetMem(data, 0, 1) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "error get block %d", 0); + PrintAndLogEx(WARNING, "error get block 0"); free(e_sector); return PM3_ESOFT; } + // assuming 4byte UID. + uint8_t uid[4]; memcpy(uid, data, sizeof(uid)); // download keys from EMUL @@ -4158,7 +4164,7 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { printKeyTable(sectors_cnt, e_sector); // dump the keys - if (createDumpFile) { + if (create_dumpfile) { char filename[FILE_PATH_SIZE] = {0}; char *fptr = filename; @@ -4173,109 +4179,126 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { // CHINESE MAGIC COMMANDS static int CmdHF14AMfCSetUID(const char *Cmd) { - uint8_t wipeCard = 0; - uint8_t uid[8] = {0x00}; - uint8_t oldUid[8] = {0x00}; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf csetuid", + "Set UID, ATQA, and SAK for magic gen1a card", + "hf mf csetuid -u 01020304\n" + "hf mf csetuid -w -u 01020304 --atqa 0004 --sak 08" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("w", "wipe", "wipes card with backdoor cmd`"), + arg_str0("u", "uid", "", "UID, 4/7 hex bytes"), + arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), + arg_str0("s", "sak", "", "SAK, 1 hex byte"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t wipe_card = arg_get_lit(ctx, 1); + + int uidlen = 0; + uint8_t uid[7] = {0x00}; + CLIGetHexWithReturn(ctx, 2, uid, &uidlen); + + int alen = 0; uint8_t atqa[2] = {0x00}; + CLIGetHexWithReturn(ctx, 3, atqa, &alen); + + int slen = 0; uint8_t sak[1] = {0x00}; - uint8_t atqaPresent = 1; - int res, argi = 0; - char ctmp; + CLIGetHexWithReturn(ctx, 4, sak, &slen); + CLIParserFree(ctx); - if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') - return usage_hf14_csetuid(); - - if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) - return usage_hf14_csetuid(); - - argi++; - - ctmp = tolower(param_getchar(Cmd, argi)); - if (ctmp == 'w') { - wipeCard = 1; - atqaPresent = 0; + // sanity checks + if (uidlen != 4 && uidlen != 7) { + PrintAndLogEx(FAILED, "UID must be 4 or 7 hex bytes. Got %d", uidlen); + return PM3_EINVARG; + } + if (alen && alen != 2) { + PrintAndLogEx(FAILED, "ATQA must be 2 hex bytes. Got %d", alen); + return PM3_EINVARG; + } + if (slen && slen != 1) { + PrintAndLogEx(FAILED, "SAK must be 1 hex byte. Got %d", slen); + return PM3_EINVARG; } - if (atqaPresent) { - if (param_getchar(Cmd, argi)) { - if (param_gethex(Cmd, argi, atqa, 4)) { - PrintAndLogEx(WARNING, "ATQA must include 4 HEX symbols"); - return PM3_ESOFT; - } - argi++; - if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { - PrintAndLogEx(WARNING, "SAK must include 2 HEX symbols"); - return PM3_ESOFT; - } - argi++; - } else - atqaPresent = 0; - } + uint8_t old_uid[7] = {0}; + uint8_t verify_uid[7] = {0}; - if (!wipeCard) { - ctmp = tolower(param_getchar(Cmd, argi)); - if (ctmp == 'w') { - wipeCard = 1; - } - } + int res = mfCSetUID( + uid, + uidlen, + (alen) ? atqa : NULL, + (slen) ? sak : NULL, + old_uid, + verify_uid, + wipe_card + ); - PrintAndLogEx(NORMAL, "--wipe card:%s uid:%s", (wipeCard) ? "YES" : "NO", sprint_hex(uid, 4)); - - res = mfCSetUID(uid, (atqaPresent) ? atqa : NULL, (atqaPresent) ? sak : NULL, oldUid, wipeCard); if (res) { - PrintAndLogEx(ERR, "Can't set UID. error=%d", res); + PrintAndLogEx(ERR, "Can't set UID. error %d", res); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Old UID : %s", sprint_hex(oldUid, 4)); - PrintAndLogEx(SUCCESS, "New UID : %s", sprint_hex(uid, 4)); + res = memcmp(uid, verify_uid, uidlen); + + PrintAndLogEx(SUCCESS, "Old UID... %s", sprint_hex(old_uid, uidlen)); + PrintAndLogEx(SUCCESS, "New UID... %s ( %s )", + sprint_hex(verify_uid, uidlen), + (res == 0) ? _GREEN_("verified") : _RED_("fail") + ); return PM3_SUCCESS; } static int CmdHF14AMfCWipe(const char *cmd) { - uint8_t uid[8] = {0x00}; - int uidLen = 0; - uint8_t atqa[2] = {0x00}; - int atqaLen = 0; - uint8_t sak[1] = {0x00}; - int sakLen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cwipe", - "Wipe gen1 magic chinese card. Set UID/ATQA/SAK/Data/Keys/Access to default values.", - "hf mf cwipe --> wipe card\n" + "Wipe gen1 magic chinese card.\n" + "Set UID / ATQA / SAK / Data / Keys / Access to default values", + "hf mf cwipe\n" "hf mf cwipe -u 09080706 -a 0004 -s 18 --> set UID, ATQA and SAK and wipe card"); void *argtable[] = { arg_param_begin, - arg_str0("u", "uid", "", "UID for card"), - arg_str0("a", "atqa", "", "ATQA for card"), - arg_str0("s", "sak", "", "SAK for card"), + arg_str0("u", "uid", "", "UID, 4 hex bytes"), + arg_str0("a", "atqa", "", "ATQA, 2 hex bytes"), + arg_str0("s", "sak", "", "SAK, 1 hex byte"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); - CLIGetHexWithReturn(ctx, 1, uid, &uidLen); - CLIGetHexWithReturn(ctx, 2, atqa, &atqaLen); - CLIGetHexWithReturn(ctx, 3, sak, &sakLen); + int uidlen = 0; + uint8_t uid[8] = {0x00}; + CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + + int alen = 0; + uint8_t atqa[2] = {0x00}; + CLIGetHexWithReturn(ctx, 2, atqa, &alen); + + int slen = 0; + uint8_t sak[1] = {0x00}; + CLIGetHexWithReturn(ctx, 3, sak, &slen); CLIParserFree(ctx); - if (uidLen && uidLen != 4) { - PrintAndLogEx(ERR, "UID length must be 4 bytes instead of: %d", uidLen); + if (uidlen && uidlen != 4) { + PrintAndLogEx(ERR, "UID length must be 4 bytes, got %d", uidlen); return PM3_EINVARG; } - if (atqaLen && atqaLen != 2) { - PrintAndLogEx(ERR, "ATQA length must be 2 bytes instead of: %d", atqaLen); + if (alen && alen != 2) { + PrintAndLogEx(ERR, "ATQA length must be 2 bytes, got %d", alen); return PM3_EINVARG; } - if (sakLen && sakLen != 1) { - PrintAndLogEx(ERR, "SAK length must be 1 byte instead of: %d", sakLen); + if (slen && slen != 1) { + PrintAndLogEx(ERR, "SAK length must be 1 byte, got %d", slen); return PM3_EINVARG; } - int res = mfCWipe((uidLen) ? uid : NULL, (atqaLen) ? atqa : NULL, (sakLen) ? sak : NULL); + int res = mfCWipe((uidlen) ? uid : NULL, (alen) ? atqa : NULL, (slen) ? sak : NULL); if (res) { - PrintAndLogEx(ERR, "Can't wipe card. error=%d", res); + PrintAndLogEx(ERR, "Can't wipe card. error %d", res); return PM3_ESOFT; } @@ -4284,25 +4307,48 @@ static int CmdHF14AMfCWipe(const char *cmd) { } static int CmdHF14AMfCSetBlk(const char *Cmd) { - uint8_t block[16] = {0x00}; - uint8_t blockNo = 0; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf csetblk", + "Set block data on a magic gen1a card", + "hf mf csetblk --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_lit0("w", "wipe", "wipes card with backdoor cmd before writing"), + 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); + + uint8_t wipe_card = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (b < 0 || b >= MIFARE_1K_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; + } + uint8_t params = MAGIC_SINGLE; - int res; - char ctmp = tolower(param_getchar(Cmd, 0)); - - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_csetblk(); - - blockNo = param_get8(Cmd, 0); - - if (param_gethex(Cmd, 1, block, 32)) return usage_hf14_csetblk(); - - ctmp = tolower(param_getchar(Cmd, 2)); - if (ctmp == 'w') + if (wipe_card) { params |= MAGIC_WIPE; + } - PrintAndLogEx(NORMAL, "--block number:%2d data:%s", blockNo, sprint_hex(block, 16)); + PrintAndLogEx(INFO, "Writing block number:%2d data:%s", b, sprint_hex_inrow(data, sizeof(data))); - res = mfCSetBlock(blockNo, block, NULL, params); + int res = mfCSetBlock(b, data, NULL, params); if (res) { PrintAndLogEx(ERR, "Can't write block. error=%d", res); return PM3_ESOFT; @@ -4312,163 +4358,237 @@ static int CmdHF14AMfCSetBlk(const char *Cmd) { static int CmdHF14AMfCLoad(const char *Cmd) { - uint8_t fillFromEmulator = 0; - bool fillFromJson = false; - bool fillFromBin = false; - char fileName[50] = {0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf cload", + "Load magic gen1a card with data from (bin/eml/json) dump file\n" + "or from emulator memory.", + "hf mf cload --emu\n" + "hf mf cload -f hf-mf-01020304.eml\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump"), + arg_lit0(NULL, "emu", "from emulator memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (param_getlength(Cmd, 0) == 1) { - if (ctmp == 'h' || ctmp == 0x00) return usage_hf14_cload(); - if (ctmp == 'e') fillFromEmulator = 1; - if (ctmp == 'j') fillFromJson = true; - if (ctmp == 'b') fillFromBin = true; - } + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - if (fillFromJson || fillFromBin) - param_getstr(Cmd, 1, fileName, sizeof(fileName)); + bool fill_from_emulator = arg_get_lit(ctx, 2); + CLIParserFree(ctx); - - if (fillFromEmulator) { + if (fill_from_emulator) { PrintAndLogEx(INFO, "Start upload to emulator memory"); PrintAndLogEx(INFO, "." NOLF); - for (int blockNum = 0; blockNum < 16 * 4; blockNum += 1) { + for (int b = 0; b < MIFARE_1K_MAXBLOCK; b++) { int flags = 0; - uint8_t buf8[16] = {0x00}; - if (mfEmlGetMem(buf8, blockNum, 1)) { - PrintAndLogEx(WARNING, "Cant get block: %d", blockNum); - return 2; - } - if (blockNum == 0) flags = MAGIC_INIT + MAGIC_WUPC; // switch on field and send magic sequence - if (blockNum == 1) flags = 0; // just write - if (blockNum == 16 * 4 - 1) flags = MAGIC_HALT + MAGIC_OFF; // Done. Magic Halt and switch off field. + uint8_t buf8[MFBLOCK_SIZE] = {0x00}; - if (mfCSetBlock(blockNum, buf8, NULL, flags)) { - PrintAndLogEx(WARNING, "Cant set magic card block: %d", blockNum); + // read from emul memory + if (mfEmlGetMem(buf8, b, 1)) { + PrintAndLogEx(WARNING, "Can't read from emul block: %d", b); + return PM3_ESOFT; + } + + // switch on field and send magic sequence + if (b == 0) { + flags = MAGIC_INIT + MAGIC_WUPC; + } + + // just write + if (b == 1) { + flags = 0; + } + + // Done. Magic Halt and switch off field. + if (b == ((MFBLOCK_SIZE * 4) - 1)) { + flags = MAGIC_HALT + MAGIC_OFF; + } + + // write to card + if (mfCSetBlock(b, buf8, NULL, flags)) { + PrintAndLogEx(WARNING, "Can't set magic card block: %d", b); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } - size_t maxdatalen = 4096; - uint8_t *data = calloc(maxdatalen, sizeof(uint8_t)); - if (!data) { + uint8_t *data = calloc(MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK, sizeof(uint8_t)); + if (data == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } - size_t datalen = 0; + size_t bytes_read = 0; int res = 0; - if (fillFromBin) { - res = loadFile(fileName, ".bin", data, maxdatalen, &datalen); - } else { - if (fillFromJson) { - res = loadFileJSON(fileName, data, maxdatalen, &datalen, NULL); - } else { - res = loadFileEML(Cmd, data, &datalen); + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&data, &bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&data, &bytes_read); + break; + } + case JSON: { + res = loadFileJSON(filename, data, MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, &bytes_read, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + free(data); + return PM3_EINVARG; } } - if (res) { + if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } // 64 or 256blocks. - if (datalen != 1024 && datalen != 4096) { - PrintAndLogEx(ERR, "File content error. "); + if (bytes_read != (MIFARE_1K_MAXBLOCK * MFBLOCK_SIZE) && + bytes_read != (MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE)) { + PrintAndLogEx(ERR, "File content error. Read %zu bytes", bytes_read); free(data); return PM3_EFILE; } - PrintAndLogEx(INFO, "Copying to magic card"); + PrintAndLogEx(INFO, "Copying to magic gen1a card"); PrintAndLogEx(INFO, "." NOLF); - int blockNum = 0; + int blockno = 0; int flags = 0; - while (datalen) { + while (bytes_read) { // switch on field and send magic sequence - if (blockNum == 0) flags = MAGIC_INIT + MAGIC_WUPC; + if (blockno == 0) { + flags = MAGIC_INIT + MAGIC_WUPC; + } // write - if (blockNum == 1) flags = 0; + if (blockno == 1) { + flags = 0; + } - // Switch off field. - if (blockNum == 16 * 4 - 1) flags = MAGIC_HALT + MAGIC_OFF; + // switch off field + if (blockno == MFBLOCK_SIZE * 4 - 1) { + flags = MAGIC_HALT + MAGIC_OFF; + } - if (mfCSetBlock(blockNum, data + (16 * blockNum), NULL, flags)) { - PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockNum); + if (mfCSetBlock(blockno, data + (MFBLOCK_SIZE * blockno), NULL, flags)) { + PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockno); free(data); return PM3_ESOFT; } - datalen -= 16; + bytes_read -= MFBLOCK_SIZE; PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - blockNum++; + blockno++; // magic card type - mifare 1K - if (blockNum >= MIFARE_1K_MAXBLOCK) break; + if (blockno >= MIFARE_1K_MAXBLOCK) break; } PrintAndLogEx(NORMAL, "\n"); - // 64 or 256blocks. - if (blockNum != 16 * 4 && blockNum != 32 * 4 + 8 * 16) { - PrintAndLogEx(ERR, "File content error. There must be 64 blocks"); - free(data); + free(data); + + // confirm number written blocks. Must be 64 or 256 blocks + if (blockno != MIFARE_1K_MAXBLOCK) { + if (blockno != MIFARE_4K_MAXBLOCK) { + PrintAndLogEx(ERR, "File content error. There must be %u blocks", MIFARE_4K_MAXBLOCK); + return PM3_EFILE; + } + PrintAndLogEx(ERR, "File content error. There must be %d blocks", MIFARE_1K_MAXBLOCK); return PM3_EFILE; } - PrintAndLogEx(SUCCESS, "Card loaded %d blocks from file", blockNum); - free(data); + PrintAndLogEx(SUCCESS, "Card loaded %d blocks from file", blockno); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } static int CmdHF14AMfCGetBlk(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf cgetblk", + "Get block data from magic Chinese card.\n" + "Only works with magic gen1a cards", + "hf mf cgetblk --blk 0 --> get block 0 (manufacturer)\n" + "hf mf cgetblk --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_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int b = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (b > 255) { + return PM3_EINVARG; + } + + uint8_t blockno = (uint8_t)b; uint8_t data[16] = {0}; - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_cgetblk(); - - uint8_t blockNo = param_get8(Cmd, 0); - - PrintAndLogEx(NORMAL, "--block number:%2d ", blockNo); - - int res = mfCGetBlock(blockNo, data, MAGIC_SINGLE); + int res = mfCGetBlock(blockno, data, MAGIC_SINGLE); if (res) { PrintAndLogEx(ERR, "Can't read block. error=%d", res); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "data: %s", sprint_hex(data, sizeof(data))); + uint8_t sector = GetSectorFromBlockNo(blockno); + mf_print_sector_hdr(sector); + mf_print_block(blockno, data); - decode_print_st(blockNo, data); + if (verbose) { + decode_print_st(blockno, data); + } else { + PrintAndLogEx(NORMAL, ""); + } return PM3_SUCCESS; } static int CmdHF14AMfCGetSc(const char *Cmd) { - uint8_t data[16] = {0}; - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_cgetsc(); - - uint8_t sector = param_get8(Cmd, 0); - if (sector > 39) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf cgetsc", + "Get sector data from magic Chinese card.\n" + "Only works with magic gen1a cards", + "hf mf cgetsc -s 0" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("s", "sec", "", "sector number"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int s = arg_get_int_def(ctx, 1, 0); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + if (s > 39) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); - return PM3_ESOFT; + return PM3_EINVARG; } - PrintAndLogEx(NORMAL, "\n # | data - sector %02d / 0x%02X ", sector, sector); - PrintAndLogEx(NORMAL, "----+------------------------------------------------"); + uint8_t sector = (uint8_t)s; + mf_print_sector_hdr(sector); + uint8_t blocks = 4; uint8_t start = sector * 4; if (sector >= 32) { @@ -4477,7 +4597,7 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { } int flags = MAGIC_INIT + MAGIC_WUPC; - + uint8_t data[16] = {0}; for (int i = 0; i < blocks; i++) { if (i == 1) flags = 0; if (i == blocks - 1) flags = MAGIC_HALT + MAGIC_OFF; @@ -4487,83 +4607,85 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data, 16)); + mf_print_block(start + i, data); + } + if (verbose) { + decode_print_st(start + blocks - 1, data); + } else { + PrintAndLogEx(NORMAL, ""); } - decode_print_st(start + blocks - 1, data); return PM3_SUCCESS; } static int CmdHF14AMfCSave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf csave", + "Save magic gen1a card memory into three files (BIN/EML/JSON)" + "or into emulator memory", + "hf mf csave\n" + "hf mf csave --4k" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dump"), + 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(NULL, "emu", "from emulator memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int fnlen = 0; char filename[FILE_PATH_SIZE]; - char *fnameptr = filename; - uint8_t *dump; - bool fillEmulator = false; - bool errors = false, hasname = false, useuid = false; - int i, len, flags; - uint16_t numblocks = 0, cmdp = 0; - uint16_t bytes = 0; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - char ctmp = tolower(param_getchar(Cmd, cmdp)); - switch (ctmp) { - case 'e': - useuid = true; - fillEmulator = true; - cmdp++; - break; - case 'h': - return usage_hf14_csave(); - case '0': - case '1': - case '2': - case '4': - numblocks = NumOfBlocks(ctmp); - bytes = numblocks * MFBLOCK_SIZE; - PrintAndLogEx(SUCCESS, "Saving magic MIFARE %cK", ctmp); - cmdp++; - break; - case 'u': - useuid = true; - hasname = true; - cmdp++; - break; - case 'o': - len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (len < 1) { - errors = true; - break; - } + 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); + bool fill_emulator = arg_get_lit(ctx, 6); + CLIParserFree(ctx); - useuid = false; - hasname = true; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + // 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; } - if (!hasname && !fillEmulator) errors = true; - - if (errors || cmdp == 0) return usage_hf14_csave(); - - dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; + 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 Gen1a MIFARE Classic " _GREEN_("%s") " card memory", s); + PrintAndLogEx(INFO, "." NOLF); + // Select card to get UID/UIDLEN information clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); - free(dump); - return PM3_ESOFT; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; } /* @@ -4573,53 +4695,74 @@ static int CmdHF14AMfCSave(const char *Cmd) { 3: proprietary Anticollision */ uint64_t select_status = resp.oldarg[0]; - if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); - free(dump); 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)); - flags = MAGIC_INIT + MAGIC_WUPC; - for (i = 0; i < numblocks; i++) { - if (i == 1) flags = 0; - if (i == numblocks - 1) flags = MAGIC_HALT + MAGIC_OFF; + // 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; + } + + // switch on field and send magic sequence + uint8_t flags = MAGIC_INIT + MAGIC_WUPC; + for (uint16_t i = 0; i < block_cnt; i++) { + + // read + if (i == 1) { + flags = 0; + } + // switch off field + if (i == block_cnt - 1) { + flags = MAGIC_HALT + MAGIC_OFF; + } if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { - PrintAndLogEx(WARNING, "Cant get block: %d", i); + PrintAndLogEx(WARNING, "Can't get magic card block: %d", i); + 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, ""); - if (useuid) { - fnameptr += snprintf(fnameptr, sizeof(filename), "hf-mf-"); - FillFileNameByUID(fnameptr, card.uid, "-dump", card.uidlen); - } - - if (fillEmulator) { + if (fill_emulator) { PrintAndLogEx(INFO, "uploading to emulator memory"); PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; - for (i = 0; i < numblocks; i += 5) { - if (i == numblocks - 1) { + for (int i = 0; i < block_cnt; i += 5) { + if (i == block_cnt - 1) { // Disable fast mode on last packet conn.block_after_ACK = false; } if (mfEmlSetMem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Cant set emul block: %d", i); + PrintAndLogEx(WARNING, "Can't set emul block: %d", i); } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "uploaded %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); saveFileJSON(filename, jsfCardMemory, dump, bytes, NULL); @@ -4629,52 +4772,64 @@ static int CmdHF14AMfCSave(const char *Cmd) { static int CmdHF14AMfCView(const char *Cmd) { - bool errors = false; - int flags; - char ctmp = '1'; - uint8_t cmdp = 0; - uint16_t numblocks = NumOfBlocks(ctmp); - uint16_t bytes = numblocks * MFBLOCK_SIZE; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf cview", + "View `magic gen1a` card memory", + "hf mf cview\n" + "hf mf cview --4k" + ); + 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_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); + CLIParserFree(ctx); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - ctmp = tolower(param_getchar(Cmd, cmdp)); - switch (ctmp) { - case 'h': - return usage_hf14_cview(); - case '0': - case '1': - case '2': - case '4': - numblocks = NumOfBlocks(ctmp); - bytes = numblocks * MFBLOCK_SIZE; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + // 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; } - if (errors) return usage_hf14_cview(); - - PrintAndLogEx(SUCCESS, "View magic MIFARE " _GREEN_("%cK"), ctmp); + 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, "View magic Gen1a MIFARE Classic " _GREEN_("%s"), s); PrintAndLogEx(INFO, "." NOLF); - uint8_t *dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; - } - // Select card to get UID/UIDLEN information clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); - free(dump); - return PM3_ESOFT; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; } /* @@ -4687,127 +4842,171 @@ static int CmdHF14AMfCView(const char *Cmd) { if (select_status == 0) { PrintAndLogEx(WARNING, "iso14443a card select failed"); - free(dump); return select_status; } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - flags = MAGIC_INIT + MAGIC_WUPC; - for (uint16_t i = 0; i < numblocks; i++) { - if (i == 1) flags = 0; - if (i == numblocks - 1) flags = MAGIC_HALT + MAGIC_OFF; + // 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; + } + + // switch on field and send magic sequence + uint8_t flags = MAGIC_INIT + MAGIC_WUPC; + for (uint16_t i = 0; i < block_cnt; i++) { + // read + if (i == 1) { + flags = 0; + } + // switch off field + if (i == block_cnt - 1) { + flags = MAGIC_HALT + MAGIC_OFF; + } if (mfCGetBlock(i, dump + (i * MFBLOCK_SIZE), flags)) { - PrintAndLogEx(WARNING, "Cant get block: %d", i); + PrintAndLogEx(WARNING, "Can't get magic card block: %u", i); + 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, ""); - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - PrintAndLogEx(INFO, "blk | data | ascii"); - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - for (uint16_t i = 0; i < numblocks; i++) { - - if (i == 0) { - PrintAndLogEx(INFO, "%03d | " _RED_("%s"), i, sprint_hex_ascii(dump + (i * 16), 16)); - } else if (mfIsSectorTrailer(i)) { - PrintAndLogEx(INFO, "%03d | " _YELLOW_("%s"), i, sprint_hex_ascii(dump + (i * 16), 16)); - } else { - PrintAndLogEx(INFO, "%03d | %s ", i, sprint_hex_ascii(dump + (i * 16), 16)); - } - } - PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); - PrintAndLogEx(NORMAL, ""); + mf_print_blocks(block_cnt, dump); free(dump); return PM3_SUCCESS; } //needs nt, ar, at, Data to decrypt static int CmdHf14AMfDecryptBytes(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf decrypt", + "Decrypt Crypto-1 encrypted bytes given some known state of crypto. See tracelog to gather needed values", + "hf mf decrypt --nt b830049b --ar 9248314a --at 9280e203 -d 41e586f9\n" + " -> 41e586f9 becomes 3003999a\n" + " -> which annotates 30 03 [99 9a] read block 3 [crc]" + ); + void *argtable[] = { + arg_param_begin, + arg_str1(NULL, "nt", "", "tag nonce"), + arg_str1(NULL, "ar", "", "ar_enc, encrypted reader response"), + arg_str1(NULL, "at", "", "at_enc, encrypted tag response"), + arg_str1("d", "data", "", "encrypted data, taken directly after at_enc and forward"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_decryptbytes(); - - uint32_t nt = param_get32ex(Cmd, 0, 0, 16); - uint32_t ar_enc = param_get32ex(Cmd, 1, 0, 16); - uint32_t at_enc = param_get32ex(Cmd, 2, 0, 16); - - int len = param_getlength(Cmd, 3); - if (len & 1) { - PrintAndLogEx(WARNING, "Uneven hex string length. LEN=%d", len); - return PM3_ESOFT; + uint32_t nt = 0; + int res = arg_get_u32_hexstr_def(ctx, 1, 0, &nt); + if (res != 1) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "check `nt` parameter"); + return PM3_EINVARG; } - PrintAndLogEx(NORMAL, "nt\t%08X", nt); - PrintAndLogEx(NORMAL, "ar enc\t%08X", ar_enc); - PrintAndLogEx(NORMAL, "at enc\t%08X", at_enc); - - uint8_t *data = calloc(len, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; + uint32_t ar_enc = 0; + res = arg_get_u32_hexstr_def(ctx, 2, 0, &ar_enc); + if (res != 1) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "check `ar` parameter"); + return PM3_EINVARG; } - param_gethex_ex(Cmd, 3, data, &len); - len >>= 1; - tryDecryptWord(nt, ar_enc, at_enc, data, len); - free(data); - return PM3_SUCCESS; + uint32_t at_enc = 0; + res = arg_get_u32_hexstr_def(ctx, 3, 0, &at_enc); + if (res != 1) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "check `at` parameter"); + return PM3_EINVARG; + } + + int datalen = 0; + uint8_t data[512] = {0x00}; + CLIGetHexWithReturn(ctx, 4, data, &datalen); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "nt....... %08X", nt); + PrintAndLogEx(INFO, "ar enc... %08X", ar_enc); + PrintAndLogEx(INFO, "at enc... %08X", at_enc); + + return tryDecryptWord(nt, ar_enc, at_enc, data, datalen); } static int CmdHf14AMfSetMod(const char *Cmd) { - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t mod = 2; - char ctmp = param_getchar(Cmd, 0); - if (ctmp == '0') { - mod = 0; - } else if (ctmp == '1') { - mod = 1; - } - int gethexfail = param_gethex(Cmd, 1, key, 12); - if (mod == 2 || gethexfail) { - PrintAndLogEx(NORMAL, "Sets the load modulation strength of a MIFARE Classic EV1 card."); - PrintAndLogEx(NORMAL, "Usage: hf mf setmod <0|1> "); - PrintAndLogEx(NORMAL, " 0 = normal modulation"); - PrintAndLogEx(NORMAL, " 1 = strong modulation (default)"); - return PM3_ESOFT; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf setmod", + "Sets the load modulation strength of a MIFARE Classic EV1 card", + "hf mf setmod -k ffffffffffff -0" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("0", NULL, "normal modulation"), + arg_lit0("1", NULL, "strong modulation (def)"), + arg_str0("k", "key", "", "key A, Sector 0, 6 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool m0 = arg_get_lit(ctx, 1); + bool m1 = arg_get_lit(ctx, 2); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 3, key, &keylen); + CLIParserFree(ctx); + + if (m0 + m1 > 1) { + PrintAndLogEx(WARNING, "please select one modulation"); + return PM3_EINVARG; } - uint8_t data[7]; - data[0] = mod; + uint8_t data[7] = {0}; memcpy(data + 1, key, 6); + if (m1) { + data[0] = 1; + } else { + data[0] = 0; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_SETMOD, data, sizeof(data)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_SETMOD, &resp, 1500)) { - - if (resp.status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Success"); - else - PrintAndLogEx(FAILED, "Failed"); - - } else { + if (WaitForResponseTimeout(CMD_HF_MIFARE_SETMOD, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); + return PM3_ETIMEOUT; } - return PM3_SUCCESS; + + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Change ( " _GREEN_("ok") " )"); + else + PrintAndLogEx(FAILED, "Change (" _GREEN_("fail") " )"); + + return resp.status; } // MIFARE NACK bug detection static int CmdHf14AMfNack(const char *Cmd) { - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_hf14_nack(); - bool verbose = (ctmp == 'v'); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf nack", + "Test a MIFARE Classic based card for the NACK bug", + "hf mf nack" + ); + 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); if (verbose) PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort"); @@ -4816,6 +5015,7 @@ static int CmdHf14AMfNack(const char *Cmd) { return PM3_SUCCESS; } +/* static int CmdHF14AMfice(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf ice", @@ -4851,9 +5051,9 @@ static int CmdHF14AMfice(const char *Cmd) { } uint8_t blockNo = 0; - uint8_t keyType = 0; + uint8_t keyType = MF_KEY_A; uint8_t trgBlockNo = 0; - uint8_t trgKeyType = 1; + uint8_t trgKeyType = MF_KEY_B; bool slow = false; bool initialize = true; bool acquisition_completed = false; @@ -4917,6 +5117,7 @@ out: SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, 4, NULL, 0); return PM3_SUCCESS; } +*/ static int CmdHF14AMfAuth4(const char *Cmd) { uint8_t keyn[20] = {0}; @@ -5229,75 +5430,80 @@ static int CmdHFMFNDEF(const char *Cmd) { } 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 if it is a 7Byte UID card and if it is not already personalized.", - "hf mf personalize UIDF0 -> double size UID according to ISO/IEC14443-3\n" - "hf mf personalize UIDF1 -> double size UID according to ISO/IEC14443-3, optional usage of selection process shortcut\n" - "hf mf personalize UIDF2 -> single size random ID according to ISO/IEC14443-3\n" - "hf mf personalize UIDF3 -> single size NUID according to ISO/IEC14443-3\n" - "hf mf personalize -t B -k B0B1B2B3B4B5 UIDF3 -> use key B = 0xB0B1B2B3B4B5 instead of default key A"); + "Personalize the UID of a MIFARE Classic EV1 card. This is only possible \n" + "if it is a 7Byte UID card and if it is not already personalized.", + "hf mf personalize -f0 -> double size UID\n" + "hf mf personalize -f1 -> double size UID, optional usage of selection process shortcut\n" + "hf mf personalize -f2 -> single size random ID\n" + "hf mf personalize -f3 -> single size NUID\n" + "hf mf personalize -b -k B0B1B2B3B4B5 -f3 -> use key B = 0xB0B1B2B3B4B5" + ); void *argtable[] = { arg_param_begin, - arg_str0("t", "keytype", "", "key type (A or B) to authenticate sector 0 (default: A)"), - arg_str0("k", "key", "", "key to authenticate sector 0 (default: FFFFFFFFFFFF)"), - arg_str1(NULL, NULL, "", "Personalization Option"), + arg_lit0("a", NULL, "use key A to authenticate sector 0 (def)"), + arg_lit0("b", NULL, "use key B to authenticate sector 0"), + arg_str0("k", "key", "", "key (def FFFFFFFFFFFF)"), + arg_lit0(NULL, "f0", "UIDFO, double size UID"), + arg_lit0(NULL, "f1", "UIDF1, double size UID, optional usage of selection process shortcut"), + arg_lit0(NULL, "f2", "UIDF2, single size random ID"), + arg_lit0(NULL, "f3", "UIDF3, single size NUID"), arg_param_end }; CLIExecWithReturn(ctx, cmd, argtable, true); - char keytypestr[2] = "a"; - uint8_t keytype = 0x00; - int keytypestr_len; - int res = CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keytypestr, 1, &keytypestr_len); - str_lower(keytypestr); + bool use_a = arg_get_lit(ctx, 1); + bool use_b = arg_get_lit(ctx, 2); - if (res || (keytypestr[0] != 'a' && keytypestr[0] != 'b')) { - PrintAndLogEx(ERR, "ERROR: not a valid key type. Key type must be A or B"); + if (use_a + use_b > 1) { + PrintAndLogEx(ERR, "error, use only one key type"); CLIParserFree(ctx); return PM3_EINVARG; } - if (keytypestr[0] == 'b') { - keytype = 0x01; + + uint8_t keytype = 0; + if (use_b) { + keytype = 1; } uint8_t key[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; int key_len; - res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, 6, &key_len); - if (res || (!res && key_len > 0 && key_len != 6)) { + int res = CLIParamHexToBuf(arg_get_str(ctx, 3), key, 6, &key_len); + if (res || (!res && key_len && key_len != 6)) { PrintAndLogEx(ERR, "ERROR: not a valid key. Key must be 12 hex digits"); CLIParserFree(ctx); return PM3_EINVARG; } - char pers_optionstr[6]; - int opt_len; - uint8_t pers_option; - res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)pers_optionstr, 5, &opt_len); - str_lower(pers_optionstr); + bool f0 = arg_get_lit(ctx, 4); + bool f1 = arg_get_lit(ctx, 5); + bool f2 = arg_get_lit(ctx, 6); + bool f3 = arg_get_lit(ctx, 7); + CLIParserFree(ctx); - if (res || (!res && opt_len > 0 && opt_len != 5) - || (strncmp(pers_optionstr, "uidf0", 5) && strncmp(pers_optionstr, "uidf1", 5) && strncmp(pers_optionstr, "uidf2", 5) && strncmp(pers_optionstr, "uidf3", 5))) { - PrintAndLogEx(ERR, "ERROR: invalid personalization option. Must be one of UIDF0, UIDF1, UIDF2, or UIDF3"); - CLIParserFree(ctx); + uint8_t tmp = f0 + f1 + f2 + f3; + if (tmp > 1) { + PrintAndLogEx(WARNING, "select only one key type"); return PM3_EINVARG; } - if (!strncmp(pers_optionstr, "uidf0", 5)) { + if (tmp == 0) { + PrintAndLogEx(WARNING, "select one key type"); + return PM3_EINVARG; + } + + uint8_t pers_option = MIFARE_EV1_UIDF3; + if (f0) { pers_option = MIFARE_EV1_UIDF0; - } else if (!strncmp(pers_optionstr, "uidf1", 5)) { + } else if (f1) { pers_option = MIFARE_EV1_UIDF1; - } else if (!strncmp(pers_optionstr, "uidf2", 5)) { + } else if (f2) { pers_option = MIFARE_EV1_UIDF2; - } else { - pers_option = MIFARE_EV1_UIDF3; } CLIParserFree(ctx); - clearCommandBuffer(); - struct { uint8_t keytype; uint8_t pers_option; @@ -5305,92 +5511,134 @@ static int CmdHFMFPersonalize(const char *cmd) { } PACKED payload; payload.keytype = keytype; payload.pers_option = pers_option; + memcpy(payload.key, key, sizeof(payload.key)); - memcpy(payload.key, key, 6); - + clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_PERSONALIZE_UID, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_MIFARE_PERSONALIZE_UID, &resp, 2500)) return PM3_ETIMEOUT; - - PrintAndLogEx(SUCCESS, "Personalization %s", resp.status == PM3_SUCCESS ? "SUCCEEDED" : "FAILED"); + if (WaitForResponseTimeout(CMD_HF_MIFARE_PERSONALIZE_UID, &resp, 2500) == false) { + return PM3_ETIMEOUT; + } + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Personalization ( %s )", _GREEN_("ok")); + } else { + PrintAndLogEx(FAILED, "Personalization ( %s )", _RED_("fail")); + } return PM3_SUCCESS; } static int CmdHF14AMfList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t mf"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf mf", "mf"); } static int CmdHf14AGen3UID(const char *Cmd) { - uint8_t uid[7] = {0x00}; - uint8_t oldUid[10] = {0x00}; - uint8_t uidlen; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gen3uid", + "Set UID for magic Gen3 card _without_ changes to manufacturer block 0", + "hf mf gen3uid --uid 01020304 --> set 4 byte uid\n" + "hf mf gen3uid --uid 01020304050607 --> set 7 byte uid" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "UID 4/7 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_hf14_gen3uid(); + uint8_t uid[7] = {0}; + int uidlen = 0; + CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + CLIParserFree(ctx); - if (param_gethex(Cmd, 0, uid, 8)) - if (param_gethex(Cmd, 0, uid, 14)) - return usage_hf14_gen3uid(); - else - uidlen = 7; - else - uidlen = 4; + // sanity checks + if (uidlen != 4 && uidlen != 7) { + PrintAndLogEx(FAILED, "UID must be 4 or 7 hex bytes. Got %d", uidlen); + return PM3_EINVARG; + } - int res = mfGen3UID(uid, uidlen, oldUid); - if (res) { - PrintAndLogEx(ERR, "Can't set UID. Error=%d", res); + uint8_t old_uid[10] = {0}; + + int res = mfGen3UID(uid, uidlen, old_uid); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Can't set UID"); + PrintAndLogEx(HINT, "Are you sure your card is a Gen3 ?"); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Old UID : %s", sprint_hex(oldUid, uidlen)); - PrintAndLogEx(SUCCESS, "New UID : %s", sprint_hex(uid, uidlen)); + PrintAndLogEx(SUCCESS, "Old UID... %s", sprint_hex(old_uid, uidlen)); + PrintAndLogEx(SUCCESS, "New UID... %s", sprint_hex(uid, uidlen)); return PM3_SUCCESS; } static int CmdHf14AGen3Block(const char *Cmd) { - uint8_t block[16] = {0x00}; - int blocklen = 0; - uint8_t newBlock[16] = {0x00}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gen3blk", + "Overwrite full manufacturer block for magic Gen3 card\n" + " - You can specify part of manufacturer block as\n" + " 4/7-bytes for UID change only\n" + "\n" + "NOTE: BCC, SAK, ATQA will be calculated automatically" + , + "hf mf gen3blk --> print current data\n" + "hf mf gen3blk -d 01020304 --> set 4 byte uid\n" + "hf mf gen3blk -d 01020304050607 --> set 7 byte uid \n" + "hf mf gen3blk -d 01020304FFFFFFFF0102030405060708" - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_hf14_gen3block(); + ); + void *argtable[] = { + arg_param_begin, + arg_str0("d", "data", "", "manufacturer block data up to 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - if (ctmp != '\0' && param_gethex_to_eol(Cmd, 0, block, sizeof(block), &blocklen)) - return usage_hf14_gen3block(); + uint8_t data[MFBLOCK_SIZE] = {0x00}; + int datalen = 0; + CLIGetHexWithReturn(ctx, 1, data, &datalen); + CLIParserFree(ctx); - int res = mfGen3Block(block, blocklen, newBlock); + uint8_t new_block[MFBLOCK_SIZE] = {0x00}; + int res = mfGen3Block(data, datalen, new_block); if (res) { - PrintAndLogEx(ERR, "Can't change manufacturer block data. Error=%d", res); + PrintAndLogEx(ERR, "Can't change manufacturer block data. error %d", res); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "Current Block : %s", sprint_hex(newBlock, 16)); + PrintAndLogEx(SUCCESS, "Current block... %s", sprint_hex_inrow(new_block, sizeof(new_block))); return PM3_SUCCESS; } static int CmdHf14AGen3Freeze(const char *Cmd) { - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return usage_hf14_gen3freeze(); - if (ctmp != 'y') return usage_hf14_gen3freeze(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gen3freeze", + "Perma lock further UID changes. No more UID changes available after operation completed\n" + "\nNote: operation is " _RED_("! irreversible !"), + + "hf mf gen3freeze -y" + ); + void *argtable[] = { + arg_param_begin, + arg_lit1("y", "yes", "confirm UID lock operation"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + bool confirm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + if (confirm == false) { + PrintAndLogEx(INFO, "please confirm that you want to perma lock the card"); + return PM3_SUCCESS; + } int res = mfGen3Freeze(); - if (res) { - PrintAndLogEx(ERR, "Can't lock UID changes. Error=%d", res); - return PM3_ESOFT; + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Can't lock UID changes. error %d", res); + } else { + PrintAndLogEx(SUCCESS, "MFC Gen3 UID card is now perma-locked"); } - PrintAndLogEx(SUCCESS, "MFC Gen3 UID permalocked"); - return PM3_SUCCESS; + return res; } - static void des_decrypt(void *out, const void *in, const void *key) { mbedtls_des_context ctx; mbedtls_des_setkey_dec(&ctx, key); @@ -5531,6 +5779,201 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AMfWipe(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf wipe", + "Wipe card to zeros and default keys/acc. This command taks a key file to wipe card\n" + "New A/B keys FF FF FF FF FF FF\n" + "New acc FF 07 80\n" + "New GDB 69", + "hf mf wipe" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "key filename"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keyfnlen = 0; + char keyFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); + + CLIParserFree(ctx); + + char *fptr; + if (keyfnlen == 0) { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) + return PM3_ESOFT; + + strcpy(keyFilename, fptr); + free(fptr); + } + + uint8_t *keys; + size_t keyslen = 0; + if (loadFile_safeEx(keyFilename, ".bin", (void **)&keys, (size_t *)&keyslen, false) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to load key file"); + return PM3_ESOFT; + } + + uint8_t keyA[MIFARE_4K_MAXSECTOR * 6]; + uint8_t keyB[MIFARE_4K_MAXSECTOR * 6]; + uint8_t num_sectors = 0; + + switch (keyslen) { + case (MIFARE_MINI_MAXSECTOR * 2 * 6): { + PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic Mini 320b"); + memcpy(keyA, keys, (MIFARE_MINI_MAXSECTOR * 6)); + memcpy(keyB, keys + (MIFARE_MINI_MAXSECTOR * 6), (MIFARE_MINI_MAXSECTOR * 6)); + num_sectors = NumOfSectors('0'); + break; + } + case (MIFARE_1K_MAXSECTOR * 2 * 6): { + PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 1K"); + memcpy(keyA, keys, (MIFARE_1K_MAXSECTOR * 6)); + memcpy(keyB, keys + (MIFARE_1K_MAXSECTOR * 6), (MIFARE_1K_MAXSECTOR * 6)); + num_sectors = NumOfSectors('1'); + break; + } + case (MIFARE_4K_MAXSECTOR * 2 * 6): { + PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 4K"); + memcpy(keyA, keys, (MIFARE_4K_MAXSECTOR * 6)); + memcpy(keyB, keys + (MIFARE_4K_MAXSECTOR * 6), (MIFARE_4K_MAXSECTOR * 6)); + num_sectors = NumOfSectors('4'); + break; + } + default: { + PrintAndLogEx(INFO, "wrong key file size"); + goto out; + } + } + + uint8_t zeros[MFBLOCK_SIZE] = {0}; + memset(zeros, 0x00, sizeof(zeros)); + uint8_t st[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + // time to wipe card + for (uint8_t s = 0; s < num_sectors; s++) { + + for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { + + uint8_t data[26]; + memset(data, 0, sizeof(data)); + if (mfIsSectorTrailer(b)) { + memcpy(data + 10, st, sizeof(st)); + } else { + memcpy(data + 10, zeros, sizeof(zeros)); + } + + // try both A/B keys, start with B key first + for (int8_t kt = MF_KEY_B; kt > -1; kt--) { + + if (kt == MF_KEY_A) + memcpy(data, keyA + (s * 6), 6); + else + memcpy(data, keyB + (s * 6), 6); + + PrintAndLogEx(INFO, "block %3d: %s", FirstBlockOfSector(s) + b, sprint_hex(data + 10, MFBLOCK_SIZE)); + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, FirstBlockOfSector(s) + b, kt, 0, data, sizeof(data)); + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.oldarg[0] & 0xff; + if (isOK == 0) { + PrintAndLogEx(FAILED, "isOk: %02x", isOK); + } else { + break; + } + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + } + } + } + + PrintAndLogEx(INFO, "Done!"); +out: + free(keys); + return PM3_SUCCESS; +} + +static int CmdHF14AMfView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf view", + "Print a MIFARE Classic dump file (bin/eml/json)", + "hf mf view -f hf-mf-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); + + // reserve memory + uint8_t *dump = calloc(MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + size_t bytes_read = 0; + int res = 0; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, &bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&dump, &bytes_read); + break; + } + case JSON: { + res = loadFileJSON(filename, dump, MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, &bytes_read, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + free(dump); + return PM3_EINVARG; + } + } + + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + free(dump); + return PM3_EFILE; + } + + uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); + if (bytes_read == 320) + block_cnt = MIFARE_MINI_MAXBLOCK; + else if (bytes_read == 2048) + block_cnt = MIFARE_2K_MAXBLOCK; + else if (bytes_read == 4096) + block_cnt = MIFARE_4K_MAXBLOCK; + + 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); + } + + mf_print_blocks(block_cnt, dump); + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, @@ -5556,35 +5999,36 @@ static command_t CommandTable[] = { {"rdsc", CmdHF14AMfRdSc, IfPm3Iso14443a, "Read MIFARE Classic sector"}, {"restore", CmdHF14AMfRestore, IfPm3Iso14443a, "Restore MIFARE Classic binary file to BLANK tag"}, {"setmod", CmdHf14AMfSetMod, IfPm3Iso14443a, "Set MIFARE Classic EV1 load modulation strength"}, + {"view", CmdHF14AMfView, AlwaysAvailable, "Display content from tag dump file"}, + {"wipe", CmdHF14AMfWipe, IfPm3Iso14443a, "Wipe card to zeros and default keys/acc"}, {"wrbl", CmdHF14AMfWrBl, IfPm3Iso14443a, "Write MIFARE Classic block"}, -// {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("simulation") " -----------------------"}, {"sim", CmdHF14AMfSim, IfPm3Iso14443a, "Simulate MIFARE card"}, - {"ecfill", CmdHF14AMfECFill, IfPm3Iso14443a, "Fill simulator memory with help of keys from simulator"}, - {"eclr", CmdHF14AMfEClear, IfPm3Iso14443a, "Clear simulator memory"}, - {"egetblk", CmdHF14AMfEGetBlk, IfPm3Iso14443a, "Get simulator memory block"}, - {"egetsc", CmdHF14AMfEGetSc, IfPm3Iso14443a, "Get simulator memory sector"}, - {"ekeyprn", CmdHF14AMfEKeyPrn, IfPm3Iso14443a, "Print keys from simulator memory"}, + {"ecfill", CmdHF14AMfECFill, IfPm3Iso14443a, "Fill emulator memory with help of keys from emulator"}, + {"eclr", CmdHF14AMfEClear, IfPm3Iso14443a, "Clear emulator memory"}, + {"egetblk", CmdHF14AMfEGetBlk, IfPm3Iso14443a, "Get emulator memory block"}, + {"egetsc", CmdHF14AMfEGetSc, IfPm3Iso14443a, "Get emulator memory sector"}, + {"ekeyprn", CmdHF14AMfEKeyPrn, IfPm3Iso14443a, "Print keys from emulator memory"}, {"eload", CmdHF14AMfELoad, IfPm3Iso14443a, "Load from file emul dump"}, {"esave", CmdHF14AMfESave, IfPm3Iso14443a, "Save to file emul dump"}, - {"eset", CmdHF14AMfESet, IfPm3Iso14443a, "Set simulator memory block"}, - {"eview", CmdHF14AMfEView, IfPm3Iso14443a, "View emul memory"}, + {"esetblk", CmdHF14AMfESet, IfPm3Iso14443a, "Set emulator memory block"}, + {"eview", CmdHF14AMfEView, IfPm3Iso14443a, "View emulator memory"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen1") " -----------------------"}, - {"cgetblk", CmdHF14AMfCGetBlk, IfPm3Iso14443a, "Read block"}, - {"cgetsc", CmdHF14AMfCGetSc, IfPm3Iso14443a, "Read sector"}, - {"cload", CmdHF14AMfCLoad, IfPm3Iso14443a, "Load dump"}, + {"cgetblk", CmdHF14AMfCGetBlk, IfPm3Iso14443a, "Read block from card"}, + {"cgetsc", CmdHF14AMfCGetSc, IfPm3Iso14443a, "Read sector from card"}, + {"cload", CmdHF14AMfCLoad, IfPm3Iso14443a, "Load dump to card"}, {"csave", CmdHF14AMfCSave, IfPm3Iso14443a, "Save dump from card into file or emulator"}, - {"csetblk", CmdHF14AMfCSetBlk, IfPm3Iso14443a, "Write block"}, - {"csetuid", CmdHF14AMfCSetUID, IfPm3Iso14443a, "Set UID"}, - {"cview", CmdHF14AMfCView, IfPm3Iso14443a, "view card"}, + {"csetblk", CmdHF14AMfCSetBlk, IfPm3Iso14443a, "Write block to card"}, + {"csetuid", CmdHF14AMfCSetUID, IfPm3Iso14443a, "Set UID on card"}, + {"cview", CmdHF14AMfCView, IfPm3Iso14443a, "View card"}, {"cwipe", CmdHF14AMfCWipe, IfPm3Iso14443a, "Wipe card to default UID/Sectors/Keys"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen3") " -----------------------"}, - {"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without manufacturer block"}, - {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite full manufacturer block"}, - {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock further UID changes"}, + {"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without changing manufacturer block"}, + {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, + {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, - {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"}, - {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, +// {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"}, +// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index c208367bc..ed4947377 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1447,10 +1447,10 @@ static int desfire_print_amk_keysetting(uint8_t key_settings, uint8_t num_keys, break; } - PrintAndLogEx(SUCCESS, " [1000] AMK Configuration changeable : %s", (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); - PrintAndLogEx(SUCCESS, " [0100] AMK required for create/delete : %s", (key_settings & (1 << 2)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, " [0010] Directory list access with AMK : %s", (key_settings & (1 << 1)) ? "NO" : "YES"); - PrintAndLogEx(SUCCESS, " [0001] AMK is changeable : %s", (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, " [%c...] AMK Configuration changeable : %s", (key_settings & (1 << 3)) ? '1' : '0', (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, " [.%c..] AMK required for create/delete : %s", (key_settings & (1 << 2)) ? '1' : '0', (key_settings & (1 << 2)) ? "NO" : "YES"); + PrintAndLogEx(SUCCESS, " [..%c.] Directory list access with AMK : %s", (key_settings & (1 << 1)) ? '1' : '0', (key_settings & (1 << 1)) ? "NO" : "YES"); + PrintAndLogEx(SUCCESS, " [...%c] AMK is changeable : %s", (key_settings & (1 << 0)) ? '1' : '0', (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); return PM3_SUCCESS; } @@ -1497,11 +1497,10 @@ static int desfire_print_piccmk_keysetting(uint8_t key_settings, uint8_t num_key (key_settings & (1 << 0)) ? '1' : '0' ); - PrintAndLogEx(SUCCESS, " [1...] CMK Configuration changeable : %s", (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); - PrintAndLogEx(SUCCESS, " [.1..] CMK required for create/delete : %s", (key_settings & (1 << 2)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, " [..1.] Directory list access with CMK : %s", (key_settings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, " [...1] CMK is changeable : %s", (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); - + PrintAndLogEx(SUCCESS, " [%c...] CMK Configuration changeable : %s", (key_settings & (1 << 3)) ? '1' : '0', (key_settings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, " [.%c..] CMK required for create/delete : %s", (key_settings & (1 << 2)) ? '1' : '0', (key_settings & (1 << 2)) ? _GREEN_("NO") : "YES"); + PrintAndLogEx(SUCCESS, " [..%c.] Directory list access with CMK : %s", (key_settings & (1 << 1)) ? '1' : '0', (key_settings & (1 << 1)) ? _GREEN_("NO") : "YES"); + PrintAndLogEx(SUCCESS, " [...%c] CMK is changeable : %s", (key_settings & (1 << 0)) ? '1' : '0', (key_settings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); return PM3_SUCCESS; } @@ -2933,13 +2932,13 @@ static int CmdHF14ADesReadData(const char *Cmd) { res = handler_desfire_readdata(&ft, type, cs); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Successfully read data from file %d:", ft.fileno); - PrintAndLogEx(NORMAL, "\nOffset | Data | Ascii"); - PrintAndLogEx(NORMAL, "----------------------------------------------------------------------------"); + PrintAndLogEx(INFO, "\nOffset | Data | Ascii"); + PrintAndLogEx(INFO, "----------------------------------------------------------------------------"); uint32_t len = le24toh(ft.length); for (uint32_t i = 0; i < len; i += 16) { uint32_t l = len - i; - PrintAndLogEx(NORMAL, "%02d/0x%02X | %s| %s", i, i, sprint_hex(&ft.data[i], l > 16 ? 16 : l), sprint_ascii(&ft.data[i], l > 16 ? 16 : l)); + PrintAndLogEx(INFO, "%02d/0x%02X | %s| %s", i, i, sprint_hex(&ft.data[i], l > 16 ? 16 : l), sprint_ascii(&ft.data[i], l > 16 ? 16 : l)); } } else { PrintAndLogEx(ERR, "Couldn't read data. Error %d", res); @@ -4804,13 +4803,7 @@ static int CmdHF14aDesChk(const char *Cmd) { } static int CmdHF14ADesList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t des"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf mfdes", "des"); } /* @@ -4827,7 +4820,7 @@ static int CmdHF14aDesNDEF(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_litn("v", "verbose", 0, 2, "show technical data"), - arg_str0("", "aid", "", "replace default aid for NDEF"), + arg_str0(NULL, "aid", "", "replace default aid for NDEF"), arg_str0("k", "key", "", "replace default key for NDEF"), arg_param_end }; @@ -4931,7 +4924,7 @@ static int CmdHF14aDesMAD(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_litn("v", "verbose", 0, 2, "show technical data"), - arg_str0("", "aid", "", "replace default aid for MAD"), + arg_str0(NULL, "aid", "", "replace default aid for MAD"), arg_str0("k", "key", "", "replace default key for MAD"), arg_param_end }; diff --git a/client/src/cmdhfmfdesfire_disabled.c b/client/src/cmdhfmfdesfire_disabled.c deleted file mode 100644 index e2de33485..000000000 --- a/client/src/cmdhfmfdesfire_disabled.c +++ /dev/null @@ -1,246 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2014 Andy Davies -// -// This code is licensed to you under the terms of the GNU GPL, version 2 or, -// at your option, any later version. See the LICENSE.txt file for the text of -// the license. -//----------------------------------------------------------------------------- -// High frequency MIFARE commands -//----------------------------------------------------------------------------- - -#include "cmdhfmfdesfire_disabled.h" - -#include "cmdparser.h" // command_t - -#include "cmdhfmf.h" -#include "util.h" -#include "des.h" -#include "aes.h" - -static int CmdHelp(const char *Cmd); - -//DESFIRE -// Reader 2 Card : 020A, key (1 byte), CRC1 CRC2 ; auth (020a00) -// Card 2 Reader : 02AF, 8 Bytes(b0), CRC1 CRC2 -// Reader 2 Card : 03AF, 8 Bytes(b1),8 bytes(b2), CRC1 CRC2 -// Card 2 Reader : 0300, 8 bytes(b3), CRC1 CRC2 ; success - -//send 020A00, receive enc(nc) - -//02AE = error -//receive b3=enc(r4) -//r5=dec(b3) -//n'r=rol(r5) -//verify n'r=nr - -static int CmdHF14AMfDESAuth(const char *Cmd) { - - uint8_t blockNo = 0; - //keyNo=0; - uint32_t cuid = 0; - uint8_t reply[16] = {0x00}; - //DES_cblock r1_b1; - uint8_t b1[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t b2[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - DES_cblock nr, b0, r1, r0; - - - uint8_t key[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - //DES_cblock iv={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - DES_key_schedule ks1; - DES_cblock key1; - - if (strlen(Cmd) < 1) { - PrintAndLogEx(NORMAL, "Usage: hf desfire des-auth k "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf desfire des-auth k 0"); - return 0; - } - - //Change key to user defined one - - memcpy(key1, key, 8); - //memcpy(key2,key+8,8); - DES_set_key((DES_cblock *)key1, &ks1); - //DES_set_key((DES_cblock *)key2,&ks2); - - //Auth1 - SendCommandMIX(CMD_MIFARE_DES_AUTH1, blockNo, 0, 0, NULL, 0); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - cuid = resp.oldarg[1]; - uint8_t *data = resp.data.asBytes; - - if (isOK) { - PrintAndLogEx(NORMAL, "enc(nc)/b0:%s", sprint_hex(data + 2, 8)); - memcpy(b0, data + 2, 8); - } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - - //Do crypto magic - DES_random_key(&nr); - //b1=dec(nr) - //r0=dec(b0) - DES_ecb_encrypt(&nr, &b1, &ks1, 0); - DES_ecb_encrypt(&b0, &r0, &ks1, 0); - //PrintAndLogEx(NORMAL, "b1:%s",sprint_hex(b1, 8)); - PrintAndLogEx(NORMAL, "r0:%s", sprint_hex(r0, 8)); - //r1=rol(r0) - memcpy(r1, r0, 8); - rol(r1, 8); - PrintAndLogEx(NORMAL, "r1:%s", sprint_hex(r1, 8)); - for (int i = 0; i < 8; i++) { - b2[i] = (r1[i] ^ b1[i]); - } - DES_ecb_encrypt(&b2, &b2, &ks1, 0); - //PrintAndLogEx(NORMAL, "b1:%s",sprint_hex(b1, 8)); - PrintAndLogEx(NORMAL, "b2:%s", sprint_hex(b2, 8)); - - //Auth2 - memcpy(reply, b1, 8); - memcpy(reply + 8, b2, 8); - SendCommandOLD(CMD_MIFARE_DES_AUTH2, cuid, 0, 0, reply, sizeof(reply)); - - PacketResponseNG respb; - if (WaitForResponseTimeout(CMD_ACK, &respb, 1500)) { - uint8_t isOK = respb.arg[0] & 0xff; - uint8_t *data2 = respb.d.asBytes; - - if (isOK) - PrintAndLogEx(NORMAL, "b3:%s", sprint_hex(data2 + 2, 8)); - - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - return 1; -} - -//EV1 -// Reader 2 Card : 02AA, key (1 byte), CRC1 CRC2 ; auth -// Card 2 Reader : 02AF, 16 Bytes(b0), CRC1 CRC2 -// Reader 2 Card : 03AF, 16 Bytes(b1),16Bytes(b2) CRC1 CRC2 -// Card 2 Reader : 0300, 16 bytes(b3), CRC1 CRC2 ; success -static int CmdHF14AMfAESAuth(const char *Cmd) { - - uint8_t blockNo = 0; - //keyNo=0; - uint32_t cuid = 0; - uint8_t reply[32] = {0x00}; - //DES_cblock r1_b1; - //unsigned char * b1, b2, nr, b0, r0, r1; - - uint8_t b1[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t b2[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t nr[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t b0[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t r0[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t r1[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // - uint8_t key[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t iv[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - AES_KEY key_e; - AES_KEY key_d; - - if (strlen(Cmd) < 1) { - PrintAndLogEx(NORMAL, "Usage: hf desfire aes-auth k "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf desfire aes-auth k 0"); - return 0; - } - - //Change key to user defined one - // - // int private_AES_set_encrypt_key(const unsigned char *userKey, const int bits,AES_KEY *key); - //int private_AES_set_decrypt_key(const unsigned char *userKey, const int bits,AES_KEY *key); - // - //memcpy(key1,key,16); - //memcpy(key2,key+8,8); - AES_set_encrypt_key(key, 128, &key_e); - AES_set_decrypt_key(key, 128, &key_d); - - //Auth1 - SendCommandMIX(CMD_MIFARE_DES_AUTH1, blockNo, 0, 0, NULL, 0); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - cuid = resp.oldarg[1]; - uint8_t *data = resp.data.asBytes; - - if (isOK) { - PrintAndLogEx(NORMAL, "enc(nc)/b0:%s", sprint_hex(data + 2, 16)); - memcpy(b0, data + 2, 16); - } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - // - // void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, - //size_t length, const AES_KEY *key, - //unsigned char *ivec, const int enc); - - //Do crypto magic - //DES_random_key(&nr); - //b1=dec(nr) - //r0=dec(b0) - //AES_cbc_encrypt(&nr,&b1,16,&key,0); - AES_cbc_encrypt(&b0, &r0, 16, &key_d, iv, 0); - //PrintAndLogEx(NORMAL, "b1:%s",sprint_hex(b1, 8)); - PrintAndLogEx(NORMAL, "r0:%s", sprint_hex(r0, 16)); - //r1=rol(r0) - memcpy(r1, r0, 16); - rol(r1, 8); - PrintAndLogEx(NORMAL, "r1:%s", sprint_hex(r1, 16)); - for (int i = 0; i < 16; i++) { - b1[i] = (nr[i] ^ b0[i]); - b2[i] = (r1[i] ^ b1[i]); - } - PrintAndLogEx(NORMAL, "nr:%s", sprint_hex(nr, 16)); - AES_cbc_encrypt(&b1, &b1, 16, &key_e, iv, 1); - AES_cbc_encrypt(&b2, &b2, 16, &key_e, iv, 1); - PrintAndLogEx(NORMAL, "b1:%s", sprint_hex(b1, 16)); - PrintAndLogEx(NORMAL, "b2:%s", sprint_hex(b2, 16)); - - //Auth2 - memcpy(reply, b1, 16); - memcpy(reply + 16, b2, 16); - SendCommandOLD(CMD_MIFARE_DES_AUTH2, cuid, 0, 0, reply, sizeof(reply)); - - PacketResponseNG respb; - if (WaitForResponseTimeout(CMD_ACK, &respb, 1500)) { - uint8_t isOK = respb.arg[0] & 0xff; - uint8_t *data2 = respb.d.asBytes; - - if (isOK) - PrintAndLogEx(NORMAL, "b3:%s", sprint_hex(data2 + 2, 16)); - - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - return 1; -} - - -//------------------------------------ -// Menu Stuff -//------------------------------------ -static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"dbg", CmdHF14AMfDbg, IfPm3Iso14443a, "Set default debug mode"}, - {"des-auth", CmdHF14AMfDESAuth, IfPm3Iso14443a, "Desfire Authentication"}, - {"ev1-auth", CmdHF14AMfAESAuth, IfPm3Iso14443a, "EV1 Authentication"}, - {NULL, NULL, NULL, NULL} -}; - -static int CmdHelp(const char *Cmd) { - CmdsHelp(CommandTable); - return 0; -} - -int CmdHFMFDesfire(const char *Cmd) { - // flush - clearCommandBuffer(); - return CmdsParse(CommandTable, Cmd); -} diff --git a/client/src/cmdhfmfdesfire_disabled.h b/client/src/cmdhfmfdesfire_disabled.h deleted file mode 100644 index 1583bc395..000000000 --- a/client/src/cmdhfmfdesfire_disabled.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __MFDESFIRE_AD_H -#define __MFDESFIRE_AD_H - -#include "common.h" - -int CmdHFMFDesfire(const char *Cmd); - -#endif diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index fc9e00476..22b659110 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -2176,6 +2176,46 @@ static void set_test_state(uint8_t byte) { crypto1_destroy(pcs); } +static void init_it_all(void) { + memset(nonces, 0, sizeof(nonces)); + maximum_states = 0; + best_first_byte_smallest_bitarray = 0; + first_byte_Sum = 0; + first_byte_num = 0; + write_stats = false; + all_bitflips_bitarray[0] = NULL; + all_bitflips_bitarray[1] = NULL; + num_all_bitflips_bitarray[0] = 0; + num_all_bitflips_bitarray[1] = 0; + all_bitflips_bitarray_dirty[0] = false; + all_bitflips_bitarray_dirty[1] = false; + last_sample_clock = 0; + sample_period = 0; + num_keys_tested = 0; + candidates = NULL; + num_acquired_nonces = 0; + start_time = 0; + num_effective_bitflips[0] = 0; + num_effective_bitflips[1] = 0; + num_all_effective_bitflips = 0; + num_1st_byte_effective_bitflips = 0; + hardnested_stage = CHECK_1ST_BYTES; + known_target_key = 0; + test_state[0] = 0; + test_state[1] = 0; + brute_force_per_second = 0; + init_book_of_work(); + real_sum_a8 = 0; + + memset(effective_bitflip, 0, sizeof(effective_bitflip)); + memset(all_effective_bitflip, 0, sizeof(all_effective_bitflip)); + memset(bitflip_bitarrays, 0, sizeof(bitflip_bitarrays)); + memset(count_bitflip_bitarrays, 0, sizeof(count_bitflip_bitarrays)); + memset(part_sum_a0_bitarrays, 0, sizeof(part_sum_a0_bitarrays)); + memset(part_sum_a8_bitarrays, 0, sizeof(part_sum_a8_bitarrays)); + memset(sum_a0_bitarrays, 0, sizeof(sum_a0_bitarrays)); +} + int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests, uint64_t *foundkey, char *filename) { char progress_text[80]; char instr_set[12] = {0}; @@ -2183,8 +2223,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc get_SIMD_instruction_set(instr_set); PrintAndLogEx(SUCCESS, "Using %s SIMD core.", instr_set); + // initialize static arrays memset(part_sum_count, 0, sizeof(part_sum_count)); - real_sum_a8 = 0; + init_it_all(); srand((unsigned) time(NULL)); brute_force_per_second = brute_force_benchmark(); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 33991bb27..02e8dd910 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -446,28 +446,29 @@ static int CmdHFMFPInfo(const char *Cmd) { } static int CmdHFMFPWritePerso(const char *Cmd) { - uint8_t keyNum[64] = {0}; - int keyNumLen = 0; - uint8_t key[64] = {0}; - int keyLen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp wrp", "Executes Write Perso command. Can be used in SL0 mode only.", - "hf mfp wrp 4000 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" - "hf mfp wrp 4000 -> write default key(0xff..0xff) to key number 4000"); + "hf mfp wrp --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" + "hf mfp wrp --ki 4000 -> write default key(0xff..0xff) to key number 4000"); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "show internal data."), - arg_str1(NULL, NULL, "", NULL), - arg_strx0(NULL, NULL, "", NULL), + arg_lit0("v", "verbose", "show internal data."), + arg_str1("i", "ki", "", " key number, 2 hex bytes"), + arg_strx0(NULL, "key", "", " key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); + + uint8_t keyNum[64] = {0}; + int keyNumLen = 0; CLIGetHexWithReturn(ctx, 2, keyNum, &keyNumLen); + + uint8_t key[64] = {0}; + int keyLen = 0; CLIGetHexWithReturn(ctx, 3, key, &keyLen); CLIParserFree(ctx); @@ -480,11 +481,11 @@ static int CmdHFMFPWritePerso(const char *Cmd) { if (keyNumLen != 2) { PrintAndLogEx(ERR, "Key number length must be 2 bytes instead of: %d", keyNumLen); - return 1; + return PM3_EINVARG; } if (keyLen != 16) { PrintAndLogEx(ERR, "Key length must be 16 bytes instead of: %d", keyLen); - return 1; + return PM3_EINVARG; } uint8_t data[250] = {0}; @@ -498,60 +499,61 @@ static int CmdHFMFPWritePerso(const char *Cmd) { if (datalen != 3) { PrintAndLogEx(ERR, "Command must return 3 bytes instead of: %d", datalen); - return 1; + return PM3_ESOFT; } if (data[0] != 0x90) { PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0])); - return 1; + return PM3_ESOFT; } - PrintAndLogEx(INFO, "Write OK."); + PrintAndLogEx(INFO, "Write (" _GREEN_("ok") " )"); return PM3_SUCCESS; } static int CmdHFMFPInitPerso(const char *Cmd) { - int res; - uint8_t key[256] = {0}; - int keyLen = 0; - uint8_t keyNum[2] = {0}; - uint8_t data[250] = {0}; - int datalen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp initp", "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.", - "hf mfp initp 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n" + "hf mfp initp --key 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n" "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange"); void *argtable[] = { arg_param_begin, arg_litn("v", "verbose", 0, 2, "show internal data."), - arg_strx0(NULL, NULL, "", NULL), + arg_strx0("k", "key", "", "key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); bool verbose2 = arg_get_lit(ctx, 1) > 1; + + uint8_t key[256] = {0}; + int keyLen = 0; CLIGetHexWithReturn(ctx, 2, key, &keyLen); CLIParserFree(ctx); if (keyLen && keyLen != 16) { PrintAndLogEx(ERR, "Key length must be 16 bytes instead of: %d", keyLen); - return 1; + return PM3_EINVARG; } if (!keyLen) memmove(key, DefaultKey, 16); + uint8_t keyNum[2] = {0}; + uint8_t data[250] = {0}; + int datalen = 0; + int res; + mfpSetVerboseMode(verbose2); for (uint16_t sn = 0x4000; sn < 0x4050; sn++) { keyNum[0] = sn >> 8; keyNum[1] = sn & 0xff; res = MFPWritePerso(keyNum, key, (sn == 0x4000), true, data, sizeof(data), &datalen); if (!res && (datalen == 3) && data[0] == 0x09) { - PrintAndLogEx(INFO, "2k card detected."); + PrintAndLogEx(INFO, "2K card detected."); break; } if (res || (datalen != 3) || data[0] != 0x90) { @@ -574,14 +576,12 @@ static int CmdHFMFPInitPerso(const char *Cmd) { } } } - DropField(); if (res) return res; - PrintAndLogEx(INFO, "Done."); - + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -589,17 +589,19 @@ static int CmdHFMFPCommitPerso(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp commitp", "Executes Commit Perso command. Can be used in SL0 mode only.", - "hf mfp commitp"); + "hf mfp commitp\n" + // "hf mfp commitp --sl 1" + ); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "show internal data."), - arg_int0(NULL, NULL, "SL mode", NULL), +// arg_int0(NULL, "sl", "", "SL mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool verbose = arg_get_lit(ctx, 1); +// int slmode = arg_get_int(ctx, 2); CLIParserFree(ctx); mfpSetVerboseMode(verbose); @@ -615,15 +617,14 @@ static int CmdHFMFPCommitPerso(const char *Cmd) { if (datalen != 3) { PrintAndLogEx(ERR, "Command must return 3 bytes instead of: %d", datalen); - return 1; + return PM3_EINVARG; } if (data[0] != 0x90) { PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0])); - return 1; + return PM3_EINVARG; } - PrintAndLogEx(INFO, "Switch level OK."); - + PrintAndLogEx(INFO, "Switch level ( " _GREEN_("ok") " )"); return PM3_SUCCESS; } @@ -636,14 +637,14 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp auth", "Executes AES authentication command for Mifare Plus card", - "hf mfp auth 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" - "hf mfp auth 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data"); + "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data"); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "show internal data."), - arg_str1(NULL, NULL, "", NULL), - arg_str1(NULL, NULL, "", NULL), + arg_str1(NULL, "ki", "", "key number, 2 hex bytes"), + arg_str1(NULL, "key", "", "key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -654,37 +655,33 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserFree(ctx); if (keynlen != 2) { - PrintAndLogEx(ERR, "ERROR: must be 2 bytes long instead of: %d", keynlen); - return 1; + PrintAndLogEx(ERR, "ERROR: must be 2 bytes long instead of: %d", keynlen); + return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, "ERROR: must be 16 bytes long instead of: %d", keylen); - return 1; + PrintAndLogEx(ERR, "ERROR: must be 16 bytes long instead of: %d", keylen); + return PM3_EINVARG; } return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false); } static int CmdHFMFPRdbl(const char *Cmd) { - uint8_t keyn[2] = {0}; - uint8_t key[250] = {0}; - int keylen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp rdbl", - "Reads several blocks from Mifare Plus card.", - "hf mfp rdbl 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" - "hf mfp rdbl 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data"); + "Reads several blocks from Mifare Plus card", + "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" + "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF"); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "show internal data."), - arg_int0("n", "count", "blocks count (by default 1).", NULL), - arg_lit0("b", "keyb", "use key B (by default keyA)."), - arg_lit0("p", "plain", "plain communication mode between reader and card."), - arg_int1(NULL, NULL, "", NULL), - arg_str0(NULL, NULL, "", NULL), + arg_lit0("v", "verbose", "show internal data"), + arg_int0("n", "count", "", "blocks count (by default 1)"), + arg_lit0("b", "keyb", "use key B (by default keyA)"), + arg_lit0("p", "plain", "plain communication mode between reader and card"), + arg_int1(NULL, "blk", "", "block number (0..255)"), + arg_str0(NULL, "key", "", "Key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -694,6 +691,10 @@ static int CmdHFMFPRdbl(const char *Cmd) { bool keyB = arg_get_lit(ctx, 3); int plain = arg_get_lit(ctx, 4); uint32_t blockn = arg_get_int(ctx, 5); + + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; CLIGetHexWithReturn(ctx, 6, key, &keylen); CLIParserFree(ctx); @@ -705,19 +706,19 @@ static int CmdHFMFPRdbl(const char *Cmd) { } if (blockn > 255) { - PrintAndLogEx(ERR, " must be in range [0..255] instead of: %d", blockn); - return 1; + PrintAndLogEx(ERR, " must be in range [0..255] instead of: %d", blockn); + return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); - return 1; + PrintAndLogEx(ERR, " must be 16 bytes long instead of: %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); - return 1; + return PM3_EINVARG; } if (blocksCount > 1 && mfIsSectorTrailer(blockn)) { @@ -749,12 +750,12 @@ static int CmdHFMFPRdbl(const char *Cmd) { if (datalen && data[0] != 0x90) { PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0])); - return 6; + return PM3_ESOFT; } if (datalen != 1 + blocksCount * 16 + 8 + 2) { PrintAndLogEx(ERR, "Error return length:%d", datalen); - return 5; + return PM3_ESOFT; } int indx = blockn; @@ -776,27 +777,23 @@ static int CmdHFMFPRdbl(const char *Cmd) { PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); } - return 0; + return PM3_SUCCESS; } static int CmdHFMFPRdsc(const char *Cmd) { - uint8_t keyn[2] = {0}; - uint8_t key[250] = {0}; - int keylen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp rdsc", - "Reads one sector from Mifare Plus card.", - "hf mfp rdsc 0 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" - "hf mfp rdsc 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF and some additional data"); + "Reads one sector from Mifare Plus card", + "hf mfp rdsc --sn 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" + "hf mfp rdsc --sn 1 -v -> executes authentication and shows sector 1 data with default key"); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "show internal data."), - arg_lit0("b", "keyb", "use key B (by default keyA)."), - arg_lit0("p", "plain", "plain communication mode between reader and card."), - arg_int1(NULL, NULL, "", NULL), - arg_str0(NULL, NULL, "", NULL), + arg_lit0("v", "verbose", "show internal data."), + arg_lit0("b", "keyb", "use key B (by default keyA)."), + arg_lit0("p", "plain", "plain communication mode between reader and card."), + arg_int1(NULL, "sn", "", "sector number (0..255)"), + arg_str0("k", "key", "", "key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -805,6 +802,9 @@ static int CmdHFMFPRdsc(const char *Cmd) { bool keyB = arg_get_lit(ctx, 2); bool plain = arg_get_lit(ctx, 3); uint32_t sectorNum = arg_get_int(ctx, 4); + uint8_t keyn[2] = {0}; + uint8_t key[250] = {0}; + int keylen = 0; CLIGetHexWithReturn(ctx, 5, key, &keylen); CLIParserFree(ctx); @@ -816,13 +816,13 @@ static int CmdHFMFPRdsc(const char *Cmd) { } if (sectorNum > 39) { - PrintAndLogEx(ERR, " must be in range [0..39] instead of: %d", sectorNum); - return 1; + PrintAndLogEx(ERR, " must be in range [0..39] instead of: %d", sectorNum); + return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); - return 1; + PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + return PM3_EINVARG; } uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); @@ -852,12 +852,12 @@ static int CmdHFMFPRdsc(const char *Cmd) { if (datalen && data[0] != 0x90) { PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0])); DropField(); - return 6; + return PM3_ESOFT; } if (datalen != 1 + 16 + 8 + 2) { PrintAndLogEx(ERR, "Error return length:%d", datalen); DropField(); - return 5; + return PM3_ESOFT; } PrintAndLogEx(INFO, "data[%03d]: %s", n, sprint_hex(&data[1], 16)); @@ -877,25 +877,20 @@ static int CmdHFMFPRdsc(const char *Cmd) { } static int CmdHFMFPWrbl(const char *Cmd) { - uint8_t keyn[2] = {0}; - uint8_t key[250] = {0}; - int keylen = 0; - uint8_t datain[250] = {0}; - int datainlen = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp wrbl", - "Writes one block to Mifare Plus card.", - "hf mfp wrbl 1 ff0000000000000000000000000000ff 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n" - "hf mfp wrbl 2 ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF and some additional data"); + "Writes one block to Mifare Plus card", + "hf mfp wrbl --blk 1 -d ff0000000000000000000000000000ff --key 000102030405060708090a0b0c0d0e0f -> writes block 1 data\n" + "hf mfp wrbl --blk 2 -d ff0000000000000000000000000000ff -v -> writes block 2 data with default key 0xFF..0xFF" + ); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "show internal data."), - arg_lit0("b", "keyb", "use key B (by default keyA)."), - arg_int1(NULL, NULL, "", NULL), - arg_str1(NULL, NULL, "", NULL), - arg_str0(NULL, NULL, "", NULL), + arg_lit0("v", "verbose", "show internal data."), + arg_lit0("b", "keyb", "use key B (by default keyA)."), + arg_int1(NULL, "blk", "", "block number (0..255)"), + arg_str1("d", "data", "", "data, 16 hex bytes"), + arg_str0("k", "key", "", "key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -903,10 +898,18 @@ static int CmdHFMFPWrbl(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); uint32_t blockNum = arg_get_int(ctx, 3); + + uint8_t datain[250] = {0}; + int datainlen = 0; CLIGetHexWithReturn(ctx, 4, datain, &datainlen); + + uint8_t key[250] = {0}; + int keylen = 0; CLIGetHexWithReturn(ctx, 5, key, &keylen); CLIParserFree(ctx); + uint8_t keyn[2] = {0}; + mfpSetVerboseMode(verbose); if (!keylen) { @@ -915,18 +918,18 @@ static int CmdHFMFPWrbl(const char *Cmd) { } if (blockNum > 255) { - PrintAndLogEx(ERR, " must be in range [0..255] instead of: %d", blockNum); - return 1; + PrintAndLogEx(ERR, " must be in range [0..255] instead of: %d", blockNum); + return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); - return 1; + PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + return PM3_EINVARG; } if (datainlen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", datainlen); - return 1; + PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", datainlen); + return PM3_EINVARG; } uint8_t sectorNum = mfSectorNum(blockNum & 0xff); @@ -956,13 +959,13 @@ static int CmdHFMFPWrbl(const char *Cmd) { if (datalen != 3 && (datalen != 3 + 8)) { PrintAndLogEx(ERR, "Error return length:%d", datalen); DropField(); - return 5; + return PM3_ESOFT; } if (datalen && data[0] != 0x90) { PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0])); DropField(); - return 6; + return PM3_ESOFT; } if (memcmp(&data[1], mac, 8)) { @@ -975,7 +978,7 @@ static int CmdHFMFPWrbl(const char *Cmd) { } DropField(); - PrintAndLogEx(INFO, "Write OK."); + PrintAndLogEx(INFO, "Write ( " _GREEN_("ok") " )"); return PM3_SUCCESS; } diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 9488378fd..d78750fab 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -949,7 +949,7 @@ static int mfu_decrypt_amiibo(uint8_t *encrypted, uint16_t elen, uint8_t *decryp if (elen < NFC3D_AMIIBO_SIZE / 4) { PrintAndLogEx(ERR, "ERR, data wrong length, got %zu , expected %zu", elen, (NFC3D_AMIIBO_SIZE / 4)); - return PM3_ESOFT; + return PM3_ESOFT; } nfc3d_amiibo_keys amiibo_keys = {0}; @@ -961,7 +961,7 @@ static int mfu_decrypt_amiibo(uint8_t *encrypted, uint16_t elen, uint8_t *decryp PrintAndLogEx(ERR, "WARNING, Tag signature was NOT valid"); } - *dlen = NFC3D_AMIIBO_SIZE; + *dlen = NFC3D_AMIIBO_SIZE; return PM3_SUCCESS; } static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { @@ -1016,10 +1016,10 @@ out: } */ /* -Lego Dimensions, +Lego Dimensions, Version: 00 04 04 02 01 00 0F 03 - - matching bytes: + + matching bytes: index 12 ( 3 * 4 ) E1 10 12 00 01 03 A0 0C 34 03 13 D1 01 0F 54 02 65 6E */ @@ -1075,7 +1075,7 @@ static mfu_identify_t mfu_ident_table[] = { {NULL, NULL, 0, 0, NULL, NULL, NULL, NULL} }; -static mfu_identify_t* mfu_match_fingerprint(uint8_t *version, uint8_t *data) { +static mfu_identify_t *mfu_match_fingerprint(uint8_t *version, uint8_t *data) { uint8_t i = 0; do { @@ -1140,7 +1140,7 @@ static int mfu_fingerprint(void) { res = PM3_ESOFT; goto out; } - + maxbytes = ((maxbytes / 4) + 1) * 4; data = calloc(maxbytes, sizeof(uint8_t)); if (data == NULL) { @@ -1184,7 +1184,7 @@ static int mfu_fingerprint(void) { uint8_t version[8] = {0}; uint8_t uid[7] = {0}; if (mfu_get_version_uid(version, uid) == PM3_SUCCESS) { - mfu_identify_t* item = mfu_match_fingerprint(version, data); + mfu_identify_t *item = mfu_match_fingerprint(version, data); if (item) { PrintAndLogEx(SUCCESS, "Found " _GREEN_("%s"), item->desc); @@ -2536,22 +2536,21 @@ static int CmdHF14AMfUeLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu eload", - "It loads emul dump from the file `filename.eml`", - "hf mfu eload -u -f myfile\n" - "hf mfu eload -u -f myfile -q 57 -> load 57 blocks from myfile" + "Load emulator memory with data from `filename.eml` dump file\n" + "\nSee `script run data_mfu_bin2eml` to convert the .bin to .eml", + "hf mfu eload --ul -f hf-mfu-04010203040506.eml\n" + "hf mfu eload --ul -f hf-mfu-04010203040506.eml -q 57 -> load 57 blocks from myfile" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "specify a filename w/o `.eml` to load"), - arg_lit1("u", NULL, "Ultralight Family type"), + arg_str1("f", "file", "", "filename of dump"), + arg_lit1(NULL, "ul", "MIFARE Ultralight family"), arg_int0("q", "qty", "", "number of blocks to load from eml file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); CLIParserFree(ctx); - - PrintAndLogEx(HINT, "Hint: See " _YELLOW_("`script run data_mfu_bin2eml`") " to convert the .bin to .eml"); return CmdHF14AMfELoad(Cmd); } // @@ -2598,7 +2597,7 @@ static int CmdHF14AMfUCAuth(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), + arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_lit0("k", NULL, "keep field on (only if a password is provided too)"), arg_param_end @@ -2797,7 +2796,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("uk", "uid", "", "new uid (7 bytes)"), + arg_str0("u", "uid", "", "new uid (7 bytes)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3280,30 +3279,30 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { if (memcmp(pre, post, sizeof(pre)) == 0) { PrintAndLogEx(INFO, "Current : %02d (0x%02X) %s" - , blockno - , blockno - , poststr - ); + , blockno + , blockno + , poststr + ); } else { PrintAndLogEx(INFO, _CYAN_("Tear off occurred") " : %02d (0x%02X) %s => " _RED_("%s") - , blockno - , blockno - , prestr - , poststr - ); + , blockno + , blockno + , prestr + , poststr + ); lock_on = true; - if ((phase_begin_clear == -1) && (bitcount32(*(uint32_t*)pre) > bitcount32(*(uint32_t*)post))) + if ((phase_begin_clear == -1) && (bitcount32(*(uint32_t *)pre) > bitcount32(*(uint32_t *)post))) phase_begin_clear = current; - if ((phase_begin_clear > -1) && (phase_end_clear == -1) && (bitcount32(*(uint32_t*)post) == 0)) + if ((phase_begin_clear > -1) && (phase_end_clear == -1) && (bitcount32(*(uint32_t *)post) == 0)) phase_end_clear = current; - + if ((current == start) && (phase_end_clear > -1)) skip_phase1 = true; // new write phase must be atleast 100us later.. - if (((bitcount32(*(uint32_t*)pre) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(*(uint32_t*)post) != 0) && (skip_phase1 || (current > (phase_end_clear + 100)))) + if (((bitcount32(*(uint32_t *)pre) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(*(uint32_t *)post) != 0) && (skip_phase1 || (current > (phase_end_clear + 100)))) phase_begin_newwr = current; if ((phase_begin_newwr > -1) && (phase_end_newwr == -1) && (memcmp(post, teardata, sizeof(teardata)) == 0)) @@ -3346,7 +3345,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { if ((phase_begin_clear > - 1) && (phase_begin_clear != start)) { PrintAndLogEx(INFO, "Erase phase start boundary around " _YELLOW_("%5d") " us", phase_begin_clear); } - if ((phase_end_clear > - 1) && (phase_end_clear != start)){ + if ((phase_end_clear > - 1) && (phase_end_clear != start)) { PrintAndLogEx(INFO, "Erase phase end boundary around " _YELLOW_("%5d") " us", phase_end_clear); } if (phase_begin_newwr > - 1) { @@ -3754,8 +3753,8 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { */ -// -// name, identifying bytes, decode function, hints text +// +// name, identifying bytes, decode function, hints text // identifying bits // 1. getversion data must match. // 2. magic bytes in the readable payload @@ -3889,7 +3888,7 @@ static int CmdHF14MfuNDEF(const char *Cmd) { } } } - char *mattel = strstr((char*)records, ".pid.mattel/"); + char *mattel = strstr((char *)records, ".pid.mattel/"); if (mattel) { mattel += 12; while (mattel) { @@ -3906,7 +3905,7 @@ static int CmdHF14MfuNDEF(const char *Cmd) { PrintAndLogEx(INFO, "decoded... %s", sprint_hex(arr, arrlen)); break; } - } + } } free(records); diff --git a/client/src/cmdhfst.c b/client/src/cmdhfst.c index 5fc151cc0..b15c8e8b4 100644 --- a/client/src/cmdhfst.c +++ b/client/src/cmdhfst.c @@ -203,7 +203,7 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { } // ST rothult -int infoHF_ST(void) { +int infoHFST(void) { bool activate_field = true; bool keep_field_on = true; @@ -267,9 +267,9 @@ int infoHF_ST(void) { DropField(); return PM3_ESOFT; } - - print_st_cc_info(response, resplen - 2); - + // store st cc data for later + uint8_t st_cc_data[resplen - 2]; + memcpy(st_cc_data, response, sizeof(st_cc_data)); // --------------- System file reading ---------------- uint8_t aSELECT_FILE_SYS[30]; @@ -305,13 +305,19 @@ int infoHF_ST(void) { DropField(); return PM3_ESOFT; } + + + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + PrintAndLogEx(NORMAL, ""); + print_st_cc_info(st_cc_data, sizeof(st_cc_data)); print_st_system_info(response, resplen - 2); -// PrintAndLogEx(NORMAL, "<<<< %s", sprint_hex(response, resplen)); return PM3_SUCCESS; } // menu command to get and print all info known about any known ST25TA tag -static int cmd_hf_st_info(const char *Cmd) { +static int CmdHFSTInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf st info", "Get info about ST25TA tag", @@ -324,10 +330,10 @@ static int cmd_hf_st_info(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - return infoHF_ST(); + return infoHFST(); } -static int cmd_hf_st_sim(const char *Cmd) { +static int CmdHFSTSim(const char *Cmd) { int uidlen = 0; uint8_t uid[7] = {0}; @@ -355,7 +361,7 @@ static int cmd_hf_st_sim(const char *Cmd) { return CmdHF14ASim(param); } -static int cmd_hf_st_ndef(const char *Cmd) { +static int CmdHFSTNdef(const char *Cmd) { int pwdlen = 0; uint8_t pwd[16] = {0}; bool with_pwd = false; @@ -483,7 +489,7 @@ static int cmd_hf_st_ndef(const char *Cmd) { return PM3_SUCCESS; } -static int cmd_hf_st_protect(const char *Cmd) { +static int CmdHFSTProtect(const char *Cmd) { int pwdlen = 0; uint8_t pwd[16] = {0}; @@ -638,7 +644,7 @@ static int cmd_hf_st_protect(const char *Cmd) { return PM3_SUCCESS; } -static int cmd_hf_st_pwd(const char *Cmd) { +static int CmdHFSTPwd(const char *Cmd) { int pwdlen = 0; uint8_t pwd[16] = {0}; @@ -781,24 +787,18 @@ static int cmd_hf_st_pwd(const char *Cmd) { return PM3_SUCCESS; } -static int cmd_hf_st_list(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 7816"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); +static int CmdHFSTList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf st", "7816"); } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"info", cmd_hf_st_info, IfPm3Iso14443a, "Tag information"}, - {"list", cmd_hf_st_list, AlwaysAvailable, "List ISO 14443A/7816 history"}, - {"ndef", cmd_hf_st_ndef, AlwaysAvailable, "read NDEF file on tag"}, - {"protect", cmd_hf_st_protect, IfPm3Iso14443a, "change protection on tag"}, - {"pwd", cmd_hf_st_pwd, IfPm3Iso14443a, "change password on tag"}, - {"sim", cmd_hf_st_sim, IfPm3Iso14443a, "Fake ISO 14443A/ST tag"}, + {"info", CmdHFSTInfo, IfPm3Iso14443a, "Tag information"}, + {"list", CmdHFSTList, AlwaysAvailable, "List ISO 14443A/7816 history"}, + {"ndef", CmdHFSTNdef, AlwaysAvailable, "read NDEF file on tag"}, + {"protect", CmdHFSTProtect, IfPm3Iso14443a, "change protection on tag"}, + {"pwd", CmdHFSTPwd, IfPm3Iso14443a, "change password on tag"}, + {"sim", CmdHFSTSim, IfPm3Iso14443a, "Fake ISO 14443A/ST tag"}, {NULL, NULL, NULL, NULL} }; @@ -808,7 +808,7 @@ static int CmdHelp(const char *Cmd) { return PM3_SUCCESS; } -int CmdHF_ST(const char *Cmd) { +int CmdHFST(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } diff --git a/client/src/cmdhfst.h b/client/src/cmdhfst.h index c3772318d..cd73fffd0 100644 --- a/client/src/cmdhfst.h +++ b/client/src/cmdhfst.h @@ -13,7 +13,7 @@ #include "common.h" -int CmdHF_ST(const char *Cmd); +int CmdHFST(const char *Cmd); -int infoHF_ST(void); +int infoHFST(void); #endif diff --git a/client/src/cmdhfthinfilm.c b/client/src/cmdhfthinfilm.c index 48844b759..20fe64d84 100644 --- a/client/src/cmdhfthinfilm.c +++ b/client/src/cmdhfthinfilm.c @@ -124,6 +124,8 @@ int infoThinFilm(bool verbose) { if (resp.status == PM3_SUCCESS) { if (resp.length == 16 || resp.length == 32) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); print_barcode(resp.data.asBytes, resp.length, verbose); } else { if (verbose) @@ -189,13 +191,7 @@ static int CmdHfThinFilmSim(const char *Cmd) { } static int CmdHfThinFilmList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t thinfilm"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf thinfilm", "thinfilm"); } static command_t CommandTable[] = { diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index ba150cb71..69f840b19 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -494,7 +494,7 @@ static int CmdHFTopazSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz sim", "Simulate a Topaz tag", - "hf topaz sim <- Not yet implemented"); + "hf topaz sim -> Not yet implemented"); void *argtable[] = { arg_param_begin, @@ -510,7 +510,7 @@ static int CmdHFTopazCmdRaw(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 -> Not yet implemented"); void *argtable[] = { arg_param_begin, @@ -524,13 +524,7 @@ static int CmdHFTopazCmdRaw(const char *Cmd) { } static int CmdHFTopazList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t topaz"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "hf topaz", "topaz"); } static int CmdHFTopazSniff(const char *Cmd) { @@ -614,7 +608,6 @@ int readTopazUid(bool verbose) { // printing PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(SUCCESS, " UID: %02x %02x %02x %02x %02x %02x %02x", topaz_tag.uid[6], topaz_tag.uid[5], diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index fb560b2ee..eed42dff5 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -851,9 +851,30 @@ static int CmdConnect(const char *Cmd) { return PM3_SUCCESS; } +static int CmdBreak(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hw break", + "send break loop package", + "hw break\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_SUCCESS; +} + + static command_t CommandTable[] = { {"-------------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Hardware") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"break", CmdBreak, IfPm3Present, "Send break loop usb command"}, {"connect", CmdConnect, AlwaysAvailable, "Connect Proxmark3 to serial port"}, {"dbg", CmdDbg, IfPm3Present, "Set Proxmark3 debug level"}, {"detectreader", CmdDetectReader, IfPm3Present, "Detect external reader field"}, @@ -975,7 +996,7 @@ void pm3_version(bool verbose, bool oneliner) { } PrintAndLogEx(NORMAL, " device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch")); - PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("RDV4")); + PrintAndLogEx(NORMAL, " firmware.................. %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _YELLOW_("RDV4")); PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); @@ -1002,6 +1023,9 @@ void pm3_version(bool verbose, bool oneliner) { struct p *payload = (struct p *)&resp.data.asBytes; PrintAndLogEx(NORMAL, payload->versionstr); + if (strstr(payload->versionstr, "2s30vq100") == NULL) { + PrintAndLogEx(NORMAL, " FPGA firmware... %s", _RED_("chip mismatch")); + } lookupChipID(payload->id, payload->section_size); } diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 2096c6eb0..a20bbf758 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -562,7 +562,7 @@ int CmdLFConfig(const char *Cmd) { config.decimation = 8; } - if (divisor> -1) { + if (divisor > -1) { config.divisor = divisor; if (config.divisor < 19) { PrintAndLogEx(ERR, "divisor must be between 19 and 255"); @@ -768,6 +768,7 @@ int lfsim_upload_gb(void) { break; } PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); payload_up.flag = 0; } PrintAndLogEx(NORMAL, ""); @@ -1321,7 +1322,7 @@ static bool CheckChipType(bool getDeviceData) { goto out; } - PrintAndLogEx(NORMAL, "Couldn't identify a chipset"); + PrintAndLogEx(INFO, "Couldn't identify a chipset"); out: save_restoreGB(GRAPH_RESTORE); save_restoreDB(GRAPH_RESTORE); @@ -1342,14 +1343,16 @@ int CmdLFfind(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("1", NULL, "Use data from Graphbuffer to search"), - arg_lit0("u", NULL, "Search for unknown tags, if not set, reads only known tags"), + arg_lit0("c", NULL, "Continue searching even after a first hit"), + arg_lit0("u", NULL, "Search for unknown tags. If not set, reads only known tags"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool use_gb = arg_get_lit(ctx, 1); - bool search_unk = arg_get_lit(ctx, 2); + bool search_cont = arg_get_lit(ctx, 2); + bool search_unk = arg_get_lit(ctx, 3); CLIParserFree(ctx); - + int found = 0; bool is_online = (session.pm3_present && (use_gb == false)); if (is_online) lf_read(false, 30000); @@ -1366,6 +1369,10 @@ int CmdLFfind(const char *Cmd) { PrintAndLogEx(INFO, "False Positives " _YELLOW_("ARE") " possible"); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "Checking for known tags..."); + if (search_cont) + PrintAndLogEx(INFO, "We'll go over all possible demods"); + else + PrintAndLogEx(INFO, "We'll stop at first hit"); PrintAndLogEx(INFO, ""); // only run these tests if device is online @@ -1374,14 +1381,22 @@ int CmdLFfind(const char *Cmd) { if (IfPm3Hitag()) { if (readHitagUid()) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Hitag") " found!"); - return PM3_SUCCESS; + if (search_cont) { + found++; + } else { + return PM3_SUCCESS; + } } } if (IfPm3EM4x50()) { if (read_em4x50_uid() == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("EM4x50 ID") " found!"); - return PM3_SUCCESS; + if (search_cont) { + found++; + } else { + return PM3_SUCCESS; + } } } @@ -1392,61 +1407,241 @@ int CmdLFfind(const char *Cmd) { PrintAndLogEx(INPLACE, "Searching for MOTOROLA tag..."); if (readMotorolaUid()) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Motorola FlexPass ID") " found!"); - return PM3_SUCCESS; + if (search_cont) { + found++; + } else { + return PM3_SUCCESS; + } } PrintAndLogEx(INPLACE, "Searching for COTAG tag..."); if (readCOTAGUid()) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("COTAG ID") " found!"); - return PM3_SUCCESS; + if (search_cont) { + found++; + } else { + return PM3_SUCCESS; + } } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, _RED_("No data found!")); PrintAndLogEx(INFO, "Signal looks like noise. Maybe not an LF tag?"); PrintAndLogEx(NORMAL, ""); - return PM3_ESOFT; + if (! search_cont) { + return PM3_ESOFT; + } } } int retval = PM3_SUCCESS; // ask / man - if (demodEM410x(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("EM410x ID") " found!"); goto out;} - if (demodDestron(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-A FECAVA Destron ID") " found!"); goto out;} // to do before HID - if (demodGallagher(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("GALLAGHER ID") " found!"); goto out;} - if (demodNoralsy(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Noralsy ID") " found!"); goto out;} - if (demodPresco(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Presco ID") " found!"); goto out;} - if (demodSecurakey(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Securakey ID") " found!"); goto out;} - if (demodViking(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Viking ID") " found!"); goto out;} - if (demodVisa2k(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Visa2000 ID") " found!"); goto out;} + if (demodEM410x(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("EM410x ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodDestron(true) == PM3_SUCCESS) { // to do before HID + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-A FECAVA Destron ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodGallagher(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("GALLAGHER ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodNoralsy(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Noralsy ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodPresco(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Presco ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodSecurakey(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Securakey ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodViking(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Viking ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodVisa2k(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Visa2000 ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } // ask / bi - if (demodFDXB(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-B ID") " found!"); goto out;} - if (demodJablotron(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Jablotron ID") " found!"); goto out;} - if (demodGuard(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Guardall G-Prox II ID") " found!"); goto out; } - if (demodNedap(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("NEDAP ID") " found!"); goto out;} + if (demodFDXB(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("FDX-B ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodJablotron(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Jablotron ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodGuard(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Guardall G-Prox II ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodNedap(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("NEDAP ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } // nrz - if (demodPac(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("PAC/Stanley ID") " found!"); goto out;} + if (demodPac(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("PAC/Stanley ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } // fsk - if (demodHID(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("HID Prox ID") " found!"); goto out;} - if (demodAWID(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("AWID ID") " found!"); goto out;} - if (demodIOProx(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("IO Prox ID") " found!"); goto out;} - if (demodPyramid(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Pyramid ID") " found!"); goto out;} - if (demodParadox(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paradox ID") " found!"); goto out;} - + if (demodHID(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("HID Prox ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodAWID(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("AWID ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodIOProx(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("IO Prox ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodPyramid(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Pyramid ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodParadox(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paradox ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + // psk - if (demodIdteck(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Idteck ID") " found!"); goto out;} - if (demodKeri(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("KERI ID") " found!"); goto out;} - if (demodNexWatch(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("NexWatch ID") " found!"); goto out;} - if (demodIndala(true) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Indala ID") " found!"); goto out;} - -// if (demodTI() == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Texas Instrument ID") " found!"); goto out;} -// if (demodFermax() == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Fermax ID") " found!"); goto out;} - - PrintAndLogEx(FAILED, _RED_("No known 125/134 kHz tags found!")); + if (demodIdteck(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Idteck ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodKeri(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("KERI ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodNexWatch(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("NexWatch ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodIndala(true) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Indala ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + /* + if (demodTI() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Texas Instrument ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + if (demodFermax() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Fermax ID") " found!"); + if (search_cont) { + found++; + } else { + goto out; + } + } + */ + if (found == 0) { + PrintAndLogEx(FAILED, _RED_("No known 125/134 kHz tags found!")); + } if (search_unk) { //test unknown tag formats (raw mode) @@ -1464,35 +1659,50 @@ int CmdLFfind(const char *Cmd) { if (GetFskClock("", false)) { if (FSKrawDemod(0, 0, 0, 0, true) == PM3_SUCCESS) { PrintAndLogEx(INFO, "Unknown FSK Modulated Tag found!"); - goto out; + if (search_cont) { + found++; + } else { + goto out; + } } } bool st = true; if (ASKDemod_ext(0, 0, 0, 0, false, true, false, 1, &st) == PM3_SUCCESS) { PrintAndLogEx(INFO, "Unknown ASK Modulated and Manchester encoded Tag found!"); - PrintAndLogEx(INFO, "if it does not look right it could instead be ASK/Biphase - try " _YELLOW_("'data rawdemod ab'")); - goto out; + PrintAndLogEx(INFO, "if it does not look right it could instead be ASK/Biphase - try " _YELLOW_("'data rawdemod --ab'")); + if (search_cont) { + found++; + } else { + goto out; + } } if (CmdPSK1rawDemod("") == PM3_SUCCESS) { PrintAndLogEx(INFO, "Possible unknown PSK1 Modulated Tag found above!"); - PrintAndLogEx(INFO, " Could also be PSK2 - try " _YELLOW_("'data rawdemod p2'")); + PrintAndLogEx(INFO, " Could also be PSK2 - try " _YELLOW_("'data rawdemod --p2'")); PrintAndLogEx(INFO, " Could also be PSK3 - [currently not supported]"); - PrintAndLogEx(INFO, " Could also be NRZ - try " _YELLOW_("'data rawdemod nr")); - goto out; + PrintAndLogEx(INFO, " Could also be NRZ - try " _YELLOW_("'data rawdemod --nr")); + if (search_cont) { + found++; + } else { + goto out; + } } - PrintAndLogEx(FAILED, _RED_("No data found!")); + if (found == 0) { + PrintAndLogEx(FAILED, _RED_("No data found!")); + } } - retval = PM3_ESOFT; + if (found == 0) { + retval = PM3_ESOFT; + } out: // identify chipset if (CheckChipType(is_online) == false) { PrintAndLogEx(DEBUG, "Automatic chip type detection " _RED_("failed")); - retval = false; } return retval; } diff --git a/client/src/cmdlfem.c b/client/src/cmdlfem.c index 7ad0da3dc..378bf6729 100644 --- a/client/src/cmdlfem.c +++ b/client/src/cmdlfem.c @@ -24,10 +24,10 @@ static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"410x", CmdLFEM410X, AlwaysAvailable, "EM 4102 commands..."}, - {"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4205 / 4305 / 4369 / 4469 commands..."}, - {"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4350 / 4450 commands..."}, - {"4x70", CmdLFEM4X70, AlwaysAvailable, "EM 4070 / 4170 commands..."}, + {"410x", CmdLFEM410X, AlwaysAvailable, "{ EM 4102 commands... }"}, + {"4x05", CmdLFEM4X05, AlwaysAvailable, "{ EM 4205 / 4305 / 4369 / 4469 commands... }"}, + {"4x50", CmdLFEM4X50, AlwaysAvailable, "{ EM 4350 / 4450 commands... }"}, + {"4x70", CmdLFEM4X70, AlwaysAvailable, "{ EM 4070 / 4170 commands... }"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 0a65c1cb2..f43e163bb 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -504,7 +504,7 @@ int CmdEM4x05Dump(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("p", "pwd", "", "password (00000000)"), - arg_str0("f", "file", "", "override filename prefix (optional). Default is based on UID"), + arg_str0("f", "file", "", "override filename prefix (optional). Default is based on UID"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -557,12 +557,12 @@ int CmdEM4x05Dump(const char *Cmd) { // Test first if the password is correct status = em4x05_login_ext(pwd); if (status == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Password is " _GREEN_("correct")); + PrintAndLogEx(INFO, "password is " _GREEN_("correct")); } else if (status == PM3_EFAILED) { - PrintAndLogEx(WARNING, "Password is " _RED_("incorrect") ", will try without password"); + PrintAndLogEx(WARNING, "password is " _RED_("incorrect") ", will try without password"); usePwd = false; } else if (status != PM3_EFAILED) { - PrintAndLogEx(WARNING, "Login attempt: No answer from tag"); + PrintAndLogEx(WARNING, "Login attempt: no answer from tag"); return status; } } @@ -705,7 +705,6 @@ int CmdEM4x05Dump(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); saveFileJSON(filename, (card_type == EM_4369 || card_type == EM_4469) ? jsfEM4x69 : jsfEM4x05, (uint8_t *)data, 16 * sizeof(uint32_t), NULL); - saveFileEML(filename, (uint8_t *)data, 16 * sizeof(uint32_t), sizeof(uint32_t)); saveFile(filename, ".bin", data, sizeof(data)); } @@ -1376,7 +1375,7 @@ int CmdEM4x05Chk(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); + PrintAndLogEx(INFO, "press " _GREEN_("") " to exit"); for (uint32_t c = 0; c < keycount; ++c) { @@ -1429,7 +1428,7 @@ int CmdEM4x05Brute(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_u64_0("s", "start", "", "Start bruteforce enumeration from this password value"), - arg_int0("n", "", "", "Stop after having found n candidates. Default: 0 => infinite"), + arg_int0("n", NULL, "", "Stop after having found n candidates. Default: 0 => infinite"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1534,7 +1533,7 @@ int CmdEM4x05Unlock(const char *Cmd) { arg_int0("n", NULL, NULL, "steps to skip"), arg_int0("s", "start", "", "start scan from delay (us)"), arg_int0("e", "end", "", "end scan at delay (us)"), - arg_str0("p", "pwd", "", "password (00000000)"), + arg_str0("p", "pwd", "", "password (def 00000000)"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -1626,7 +1625,7 @@ int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "press " _YELLOW_("'enter'") " to cancel the command"); + PrintAndLogEx(INFO, "press " _GREEN_("'") " to exit"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------------\n"); @@ -1692,11 +1691,8 @@ int CmdEM4x05Unlock(const char *Cmd) { } // write - res = unlock_write_protect(use_pwd, pwd, write_value, verbose); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "failed unlock write"); - return PM3_ESOFT; - } + // don't check the return value. As a tear-off occurred, the write failed. + unlock_write_protect(use_pwd, pwd, write_value, verbose); // read after trigger res = em4x05_read_word_ext(14, pwd, use_pwd, &word14); @@ -1962,11 +1958,11 @@ int CmdEM4x05Sniff(const char *Cmd) { bool haveData, sampleData = true; CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_sniff", + CLIParserInit(&ctx, "lf em 4x05 sniff", "Sniff EM4x05 commands sent from a programmer", - "lf em 4x05_sniff -> sniff via lf sniff\n" - "lf em 4x05_sniff -1 -> sniff from data loaded into the buffer\n" - "lf em 4x05_sniff -r -> reverse the bit order when showing block data" + "lf em 4x05 sniff --> sniff via lf sniff\n" + "lf em 4x05 sniff -1 --> sniff from data loaded into the buffer\n" + "lf em 4x05 sniff -r --> reverse the bit order when showing block data" ); void *argtable[] = { diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index ea60c84b9..46230bafd 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -99,7 +99,6 @@ static void print_info_result(uint8_t *data, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); // data section PrintAndLogEx(NORMAL, ""); @@ -131,29 +130,52 @@ static void print_info_result(uint8_t *data, bool verbose) { static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len, size_t *bytes_read) { - // read data from dump file; file type is derived from file name extension + uint8_t *dump = calloc(DUMP_FILESIZE, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } - int res = 0; - uint32_t serial = 0x0, device_id = 0x0; + int res = PM3_SUCCESS; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&dump, bytes_read); + break; + } + case JSON: { + res = loadFileJSON(filename, dump, DUMP_FILESIZE, bytes_read, NULL); + break; + } + case DICTIONARY: { + free(dump); + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + return PM3_EINVARG; + } + } - if (str_endswith(filename, ".eml")) - res = loadFileEML(filename, data, bytes_read) != PM3_SUCCESS; - else if (str_endswith(filename, ".json")) - res = loadFileJSON(filename, data, data_len, bytes_read, NULL); - else - res = loadFile(filename, ".bin", data, data_len, bytes_read); - - if ((res != PM3_SUCCESS) && (*bytes_read != DUMP_FILESIZE)) + if ((res != PM3_SUCCESS) && (*bytes_read != DUMP_FILESIZE)) { + free(dump); return PM3_EFILE; + } + + uint32_t serial = 0, device_id = 0; // valid em4x50 data? serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4); device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4); if (serial == device_id) { - PrintAndLogEx(WARNING, "No valid em4x50 data in file %s.", filename); + PrintAndLogEx(WARNING, "No valid EM4x50 data in file %s", filename); + free(dump); return PM3_ENODATA; } + memcpy(data, dump, *bytes_read); + free(dump); return PM3_SUCCESS; } @@ -176,15 +198,13 @@ static void em4x50_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { int CmdEM4x50ELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 eload", - "Loads EM4x50 tag dump into emulator memory on device.", + "Loads EM4x50 tag dump (bin/eml/json) into emulator memory on device", "lf em 4x50 eload -f mydump.bin\n" - "lf em 4x50 eload -f mydump.eml\n" - "lf em 4x50 eload -f mydump.json" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "filename", "", "dump filename"), + arg_str1("f", "filename", "", "dump filename (bin/eml/json)"), arg_param_end }; @@ -204,10 +224,10 @@ int CmdEM4x50ELoad(const char *Cmd) { } // upload to emulator memory - PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to emulator memory", filename); + PrintAndLogEx(INFO, "Uploading to emulator memory contents of " _YELLOW_("%s"), filename); em4x50_seteml(data, 0, DUMP_FILESIZE); - PrintAndLogEx(INFO, "Done"); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -216,14 +236,12 @@ int CmdEM4x50ESave(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x50 esave", "Saves bin/eml/json dump file of emulator memory.", "lf em 4x50 esave -> use UID as filename\n" - "lf em 4x50 esave -f mydump.bin\n" - "lf em 4x50 esave -f mydump.eml\n" - "lf em 4x50 esave -f mydump.json\n" + "lf em 4x50 esave -f mydump\n" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "data filename"), + arg_str0("f", "filename", "", "data filename"), arg_param_end }; @@ -427,7 +445,7 @@ int CmdEM4x50Chk(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "dictionary filename"), + arg_str0("f", "filename", "", "dictionary filename"), arg_param_end }; @@ -471,7 +489,7 @@ int CmdEM4x50Chk(const char *Cmd) { // upload to flash. datalen = MIN(bytes_remaining, keyblock); - res = flashmem_spiffs_load(destfn, keys, datalen); + res = flashmem_spiffs_load((char *)destfn, keys, datalen); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "SPIFFS upload failed"); return res; @@ -568,7 +586,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { int CmdEM4x50Read(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50 read", + CLIParserInit(&ctx, "lf em 4x50 rdbl", "Reads single EM4x50 block/word.", "lf em 4x50 rdbl -b 3\n" "lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n" @@ -726,16 +744,16 @@ int CmdEM4x50Reader(const char *Cmd) { int CmdEM4x50Dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 dump", - "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format.", + "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format", "lf em 4x50 dump\n" - "lf em 4x50 dump -f mydump.eml\n" + "lf em 4x50 dump -f mydump\n" "lf em 4x50 dump -p 12345678\n" - "lf em 4x50 dump -f mydump.eml -p 12345678" + "lf em 4x50 dump -f mydump -p 12345678" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -1035,7 +1053,7 @@ int CmdEM4x50Wipe(const char *Cmd) { int CmdEM4x50Restore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 restore", - "Restores data from dumpfile onto a Em4x50 tag.\n" + "Restores data from dumpfile (bin/eml/json) onto a EM4x50 tag.\n" "if used with -u, the filetemplate `lf-4x50-UID-dump.bin` is used as filename", "lf em 4x50 restore -u 1b5aff5c -> uses lf-4x50-1B5AFF5C-dump.bin\n" "lf em 4x50 restore -f mydump.eml\n" @@ -1046,7 +1064,7 @@ int CmdEM4x50Restore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "uid, 4 hex bytes, msb"), - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -1129,14 +1147,10 @@ int CmdEM4x50Restore(const char *Cmd) { } int CmdEM4x50Sim(const char *Cmd) { - - int status = PM3_EFAILED; - uint32_t password = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 sim", - "Simulates a EM4x50 tag.\n" - "Upload using `lf em 4x50 eload`", + "Simulates a EM4x50 tag\n" + "First upload to device using `lf em 4x50 eload`", "lf em 4x50 sim" "lf em 4x50 sim -p 27182818 -> uses password for eload data" ); @@ -1153,25 +1167,26 @@ int CmdEM4x50Sim(const char *Cmd) { CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); CLIParserFree(ctx); + uint32_t password = 0; if (pwd_len) { if (pwd_len != 4) { - PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); + PrintAndLogEx(FAILED, "password length must be 4 bytes, got %d", pwd_len); return PM3_EINVARG; } else { password = BYTES2UINT32(pwd); } } - CLIParserFree(ctx); + int status = PM3_EFAILED; PrintAndLogEx(INFO, "Simulating data from emulator memory"); clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_SIM, (uint8_t *)&password, sizeof(password)); PacketResponseNG resp; - PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); - bool keypress = kbd_enter_pressed(); - while (keypress == false) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + bool keypress; + do { keypress = kbd_enter_pressed(); if (WaitForResponseTimeout(CMD_LF_EM4X50_SIM, &resp, 1500)) { @@ -1179,16 +1194,17 @@ int CmdEM4x50Sim(const char *Cmd) { break; } - } + } while (keypress == false); + if (keypress) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); status = PM3_EOPABORTED; } if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED)) - PrintAndLogEx(INFO, "Done"); + PrintAndLogEx(INFO, "Done!"); else - PrintAndLogEx(FAILED, "No valid em4x50 data in memory"); + PrintAndLogEx(FAILED, "No valid EM4x50 data in memory"); return resp.status; } diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 02ccd5801..882be2da5 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -388,7 +388,7 @@ static int CmdHIDClone(const char *Cmd) { packed.Mid = mid; packed.Bot = bot; } else if (bin_len) { - int res = binstring_to_u96(&top, &mid, &bot, (const char*)bin); + int res = binstring_to_u96(&top, &mid, &bot, (const char *)bin); if (res != bin_len) { PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); return PM3_EINVARG; @@ -545,8 +545,7 @@ static int CmdHIDBrute(const char *Cmd) { } } PrintAndLogEx(INFO, "Started brute-forcing HID Prox reader"); - PrintAndLogEx(INFO, "Press pm3-button to abort simulation or press " _GREEN_("``") " to exit"); - + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); // copy values to low. cn_low = cn_hi; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index cb711a65c..96daa6390 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -51,77 +51,8 @@ static size_t nbytes(size_t nbits) { } */ -static int usage_hitag_sim(void) { - PrintAndLogEx(NORMAL, "Simulate " _YELLOW_("Hitag2 / HitagS")" transponder"); - PrintAndLogEx(NORMAL, "Usage: lf hitag sim [h] [2|s] e|j|b "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " [2|s] 2 = hitag2, s = hitagS"); - PrintAndLogEx(NORMAL, " e Load data from EML file"); - PrintAndLogEx(NORMAL, " j Load data from JSON file"); - PrintAndLogEx(NORMAL, " b Load data from BIN file"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf hitag sim 2 b lf-hitag-dump"); - return PM3_SUCCESS; -} -/* -static int usage_hitag_dump(void) { - PrintAndLogEx(NORMAL, "Usage: lf hitag dump [h] p f "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); -// PrintAndLogEx(NORMAL, " p password"); -// PrintAndLogEx(NORMAL, " f data filename, if no given, UID will be used as filename"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf hitag dump f mydump"); - PrintAndLogEx(NORMAL, " lf hitag dump p 4D494B52 f mydump"); - return PM3_SUCCESS; -} -*/ -static int usage_hitag_reader(void) { - PrintAndLogEx(NORMAL, "Hitag reader functions"); - PrintAndLogEx(NORMAL, "Usage: lf hitag reader [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " HitagS (0*)"); - PrintAndLogEx(NORMAL, " 01 Read all pages, challenge mode"); - PrintAndLogEx(NORMAL, " 02 Read all pages, crypto mode. Set key=0 for no auth"); - PrintAndLogEx(NORMAL, " Hitag1 (1*)"); - PrintAndLogEx(NORMAL, " Not implemented"); - PrintAndLogEx(NORMAL, " Hitag2 (2*)"); - PrintAndLogEx(NORMAL, " 21 Read all pages, password mode. Default: " _YELLOW_("4D494B52") " (\"MIKR\")"); - PrintAndLogEx(NORMAL, " 22 Read all pages, challenge mode"); - PrintAndLogEx(NORMAL, " 23 Read all pages, crypto mode. Key format: ISK high + ISK low. Default: " _YELLOW_("4F4E4D494B52") " (\"ONMIKR\")"); - PrintAndLogEx(NORMAL, " 25 Test recorded authentications"); - PrintAndLogEx(NORMAL, " 26 Just read UID"); - return PM3_SUCCESS; -} -static int usage_hitag_writer(void) { - PrintAndLogEx(NORMAL, "Hitag writer functions"); - PrintAndLogEx(NORMAL, "Usage: lf hitag write [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " HitagS (0*)"); - PrintAndLogEx(NORMAL, " 03 Write page, challenge mode"); - PrintAndLogEx(NORMAL, " 04 Write page, crypto mode. Set key=0 for no auth"); - PrintAndLogEx(NORMAL, " Hitag1 (1*)"); - PrintAndLogEx(NORMAL, " Not implemented"); - PrintAndLogEx(NORMAL, " Hitag2 (2*)"); - PrintAndLogEx(NORMAL, " 24 Write page, crypto mode. Key format: ISK high + ISK low."); - PrintAndLogEx(NORMAL, " Default: 4F4E4D494B52 (\"ONMIKR\"). Set key=0 for no auth"); - PrintAndLogEx(NORMAL, " 27 Write page, password mode. Default: 4D494B52 (\"MIKR\")"); - return PM3_SUCCESS; -} - static int CmdLFHitagList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t hitag2"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "lf hitag", "hitag2"); /* @@ -273,84 +204,129 @@ static int CmdLFHitagSniff(const char *Cmd) { return PM3_SUCCESS; } -static int CmdLFHitagSim(const char *Cmd) { - bool errors = false; - bool tag_mem_supplied = false; - uint8_t cmdp = 0; - size_t maxdatalen = 48; - uint8_t *data = calloc(4 * 64, sizeof(uint8_t)); - size_t datalen = 0; +// eload , to be implemented +static int CmdLFHitagEload(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag eload", + "Loads hitag tag dump into emulator memory on device", + "lf hitag eload -f lf-hitag-11223344-dump.bin\n"); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "filename of dump"), + arg_lit0("1", NULL, "simulate Hitag1"), + arg_lit0("2", NULL, "simulate Hitag2"), + arg_lit0("s", NULL, "simulate HitagS"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool use_ht1 = arg_get_lit(ctx, 2); + bool use_ht2 = arg_get_lit(ctx, 3); + bool use_hts = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + uint8_t n = (use_ht1 + use_ht2 + use_hts); + if (n != 1) { + PrintAndLogEx(ERR, "error, only specify one Hitag type"); + return PM3_EINVARG; + } + + DumpFileType_t dftype = getfiletype(filename); + size_t dumplen = 0; + uint8_t *dump = NULL; int res = 0; - char filename[FILE_PATH_SIZE] = { 0x00 }; - - uint16_t cmd = CMD_LF_HITAG_SIMULATE; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - free(data); - return usage_hitag_sim(); - case '2': - maxdatalen = 48; - cmdp++; - break; - case 's': - cmd = CMD_LF_HITAGS_SIMULATE; - maxdatalen = 4 * 64; - cmdp++; - break; - case 'e': - param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - res = loadFileEML(filename, data, &datalen); - if (res > 0 || datalen != maxdatalen) { - PrintAndLogEx(FAILED, "error, bytes read mismatch file size"); - errors = true; - break; - } - tag_mem_supplied = true; - cmdp += 2; - break; - case 'j': - param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - res = loadFileJSON(filename, data, maxdatalen, &datalen, NULL); - if (res > 0) { - errors = true; - break; - } - tag_mem_supplied = true; - cmdp += 2; - break; - case 'b': - param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - res = loadFile(filename, ".bin", data, maxdatalen, &datalen); - if (res > 0) { - errors = true; - break; - } - tag_mem_supplied = true; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, &dumplen); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&dump, &dumplen); + break; + } + case JSON: { + dumplen = 4 * 64; + dump = calloc(dumplen, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(ERR, "error, cannot allocate memory"); + return PM3_EMALLOC; + } + res = loadFileJSON(filename, dump, dumplen, &dumplen, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "error, only BIN/JSON/EML formats allowed"); + return PM3_EINVARG; } } - //Validations - if (errors || cmdp == 0) { - free(data); - return usage_hitag_sim(); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "error, something went wrong when loading file"); + free(dump); + return PM3_EFILE; } + // check dump len.. + if (dumplen == 48 || dumplen == 4 * 64) { + struct { + uint16_t len; + uint8_t *data; + } PACKED payload; + payload.len = dumplen; + memcpy(payload.data, dump, dumplen); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAG_ELOAD, (uint8_t *)&payload, 2 + dumplen); + } else { + PrintAndLogEx(ERR, "error, wrong dump file size. got %zu", dumplen); + } + + free(dump); + return PM3_SUCCESS; +} + +static int CmdLFHitagSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag sim", + "Simulate Hitag2 / HitagS transponder\n" + "You need to `lf hitag eload` first", + "lf hitag sim -2" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("1", NULL, "simulate Hitag1"), + arg_lit0("2", NULL, "simulate Hitag2"), + arg_lit0("s", NULL, "simulate HitagS"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool use_ht1 = arg_get_lit(ctx, 1); + bool use_ht2 = arg_get_lit(ctx, 2); + bool use_hts = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if ((use_ht1 + use_ht2 + use_hts) > 1) { + PrintAndLogEx(ERR, "error, Only specify one Hitag type"); + return PM3_EINVARG; + } + + uint16_t cmd = CMD_LF_HITAG_SIMULATE; +// if (use_ht1) +// cmd = CMD_LF_HITAG1_SIMULATE; + + if (use_hts) + cmd = CMD_LF_HITAGS_SIMULATE; + clearCommandBuffer(); - if (tag_mem_supplied) { - SendCommandMIX(cmd, 1, 0, 0, data, datalen); - } else { - SendCommandMIX(cmd, 0, 0, 0, NULL, 0); - } - - free(data); + SendCommandMIX(cmd, 0, 0, 0, NULL, 0); return PM3_SUCCESS; } @@ -527,57 +503,117 @@ static int CmdLFHitagInfo(const char *Cmd) { // static int CmdLFHitagReader(const char *Cmd) { - uint16_t cmd = CMD_LF_HITAG_READER; - hitag_data htd; - hitag_function htf = param_get32ex(Cmd, 0, 0, 10); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag reader", + "Act like a Hitag Reader", + "Hitag S\n" + " lf hitag reader --01 --nrar 0102030411223344\n" + " lf hitag reader --02 -k 4F4E4D494B52\n" + "Hitag 2\n" + " lf hitag reader --21 -k 4D494B52\n" + " lf hitag reader --22 --nrar 0102030411223344\n" + " lf hitag reader --23 -k 4F4E4D494B52\n" + " lf hitag reader --26\n" + ); - switch (htf) { - case RHTSF_CHALLENGE: { - cmd = CMD_LF_HITAGS_READ; - num_to_bytes(param_get32ex(Cmd, 1, 0, 16), 4, htd.auth.NrAr); - num_to_bytes(param_get32ex(Cmd, 2, 0, 16), 4, htd.auth.NrAr + 4); - break; - } - case RHTSF_KEY: { - cmd = CMD_LF_HITAGS_READ; - num_to_bytes(param_get64ex(Cmd, 1, 0, 16), 6, htd.crypto.key); - break; - } - case RHT2F_PASSWORD: { - num_to_bytes(param_get32ex(Cmd, 1, 0, 16), 4, htd.pwd.password); - break; - } - case RHT2F_AUTHENTICATE: { - num_to_bytes(param_get32ex(Cmd, 1, 0, 16), 4, htd.auth.NrAr); - num_to_bytes(param_get32ex(Cmd, 2, 0, 16), 4, htd.auth.NrAr + 4); - break; - } - case RHT2F_CRYPTO: { - num_to_bytes(param_get64ex(Cmd, 1, 0, 16), 6, htd.crypto.key); - break; - } - case RHT2F_TEST_AUTH_ATTEMPTS: { - // No additional parameters needed - break; - } - case RHT2F_UID_ONLY: { - // No additional parameters needed - break; - } - default: - case RHT1F_PLAIN: - case RHT1F_AUTHENTICATE: - case WHTSF_CHALLENGE: - case WHTSF_KEY: - case WHT2F_PASSWORD: - case WHT2F_CRYPTO: - return usage_hitag_reader(); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "01", "HitagS, read all pages, challenge mode"), + arg_lit0(NULL, "02", "HitagS, read all pages, crypto mode. Set key=0 for no auth"), + arg_lit0(NULL, "21", "Hitag2, read all pages, password mode. def 4D494B52 (MIKR)"), + arg_lit0(NULL, "22", "Hitag2, read all pages, challenge mode"), + arg_lit0(NULL, "23", "Hitag2, read all pages, crypto mode. Key ISK high + ISK low. def 4F4E4D494B52 (ONMIKR)"), + arg_lit0(NULL, "25", "Hitag2, test recorded authentications (replay?)"), + arg_lit0(NULL, "26", "Hitag2, read UID"), + arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_str0(NULL, "nrar", "", "nonce / answer reader, 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + // Hitag S + bool s01 = arg_get_lit(ctx, 1); + bool s02 = arg_get_lit(ctx, 2); + + // Hitag 2 + bool h21 = arg_get_lit(ctx, 3); + bool h22 = arg_get_lit(ctx, 4); + bool h23 = arg_get_lit(ctx, 5); + bool h25 = arg_get_lit(ctx, 6); + bool h26 = arg_get_lit(ctx, 7); + + uint8_t key[6]; + int keylen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 8), key, sizeof(key), &keylen); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t nrar[8]; + int nalen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 9), nrar, sizeof(nrar), &nalen); + CLIParserFree(ctx); + if (res != 0) { + return PM3_EINVARG; + } + + // sanity checks + if (keylen != 0 && keylen != 6) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 6, got %d", keylen); + return PM3_EINVARG; + } + + if (nalen != 0 && nalen != 8) { + PrintAndLogEx(WARNING, "Wrong NR/AR len expected 0 or 8, got %d", nalen); + return PM3_EINVARG; + } + + uint8_t foo = (s01 + s02 + h21 + h22 + h23 + h25 + h26); + if (foo > 1) { + PrintAndLogEx(WARNING, "Only specify one HITAG reader cmd"); + return PM3_EINVARG; + } else if (foo == 0) { + PrintAndLogEx(WARNING, "Specify one HITAG reader cms"); + return PM3_EINVARG; + } + + hitag_function htf; + hitag_data htd; + uint16_t cmd = CMD_LF_HITAG_READER; + if (s01) { + cmd = CMD_LF_HITAGS_READ; + htf = RHTSF_CHALLENGE; + memcpy(htd.auth.NrAr, nrar, sizeof(nrar)); + } + if (s02) { + cmd = CMD_LF_HITAGS_READ; + htf = RHTSF_KEY; + memcpy(htd.crypto.key, key, sizeof(key)); + } + if (h21) { + htf = RHT2F_PASSWORD; + memcpy(htd.pwd.password, key, 4); + } + if (h22) { + htf = RHT2F_AUTHENTICATE; + memcpy(htd.auth.NrAr, nrar, sizeof(nrar)); + } + if (h23) { + htf = RHT2F_CRYPTO; + memcpy(htd.crypto.key, key, sizeof(key)); + } + if (h25) { + htf = RHT2F_TEST_AUTH_ATTEMPTS; + } + if (h26) { + htf = RHT2F_UID_ONLY; } clearCommandBuffer(); SendCommandMIX(cmd, htf, 0, 0, &htd, sizeof(htd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -628,11 +664,11 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) { size_t datalen = 0; int res = loadFile_safe(filename, ".cc", (void **)&data, &datalen); if (res == PM3_SUCCESS) { - if (datalen == (8 * 60) ) { + if (datalen == (8 * 60)) { SendCommandOLD(CMD_LF_HITAGS_TEST_TRACES, 1, 0, 0, data, datalen); } else { - PrintAndLogEx(ERR, "Error, file length mismatch. Expected %d, got %d", 8*60, datalen); - } + PrintAndLogEx(ERR, "Error, file length mismatch. Expected %d, got %zu", 8 * 60, datalen); + } } if (data) { free(data); @@ -645,47 +681,121 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) { } static int CmdLFHitagWriter(const char *Cmd) { - hitag_data htd; - hitag_function htf = param_get32ex(Cmd, 0, 0, 10); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag writer", + "Act like a Hitag writer" + "In password mode the default key is 4D494B52 (MIKR)\n" + "In crypto mode the default key is 4F4E4D494B52 (ONMIKR) format: ISK high + ISK low.", + "Hitag S\n" + " lf hitag writer --03 --nrar 0102030411223344 -p 3 -d 01020304\n" + " lf hitag writer --04 -k 4F4E4D494B52 -p 3 -d 01020304\n" + "Hitag 2\n" + " lf hitag writer --24 -k 4F4E4D494B52 -p 3 -d 01020304\n" + " lf hitag writer --27 -k 4D494B52 -p 3 -d 01020304\n" + ); - uint32_t arg2 = 0; - switch (htf) { - case WHTSF_CHALLENGE: { - num_to_bytes(param_get64ex(Cmd, 1, 0, 16), 8, htd.auth.NrAr); - arg2 = param_get32ex(Cmd, 2, 0, 10); - num_to_bytes(param_get32ex(Cmd, 3, 0, 16), 4, htd.auth.data); - break; - } - case WHTSF_KEY: - case WHT2F_CRYPTO: { - num_to_bytes(param_get64ex(Cmd, 1, 0, 16), 6, htd.crypto.key); - arg2 = param_get32ex(Cmd, 2, 0, 10); - num_to_bytes(param_get32ex(Cmd, 3, 0, 16), 4, htd.crypto.data); - break; - } - case WHT2F_PASSWORD: { - num_to_bytes(param_get64ex(Cmd, 1, 0, 16), 4, htd.pwd.password); - arg2 = param_get32ex(Cmd, 2, 0, 10); - num_to_bytes(param_get32ex(Cmd, 3, 0, 16), 4, htd.crypto.data); - break; - } - default: - case RHT1F_PLAIN: - case RHT1F_AUTHENTICATE: - case RHTSF_CHALLENGE: - case RHTSF_KEY: - case RHT2F_PASSWORD: - case RHT2F_AUTHENTICATE: - case RHT2F_CRYPTO: - case RHT2F_TEST_AUTH_ATTEMPTS: - case RHT2F_UID_ONLY: - return usage_hitag_writer(); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "03", "HitagS, write page, challenge mode"), + arg_lit0(NULL, "04", "HitagS, write page, crypto mode. Set key=0 for no auth"), + arg_lit0(NULL, "24", "Hitag2, write page, crypto mode."), + arg_lit0(NULL, "27", "Hitag2, write page, password mode"), + arg_int1("p", "page", "", "page address to write to"), + arg_str0("d", "data", "", "data, 4 hex bytes"), + arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + // Hitag S + bool s03 = arg_get_lit(ctx, 1); + bool s04 = arg_get_lit(ctx, 2); + + // Hitag 2 + bool h24 = arg_get_lit(ctx, 3); + bool h27 = arg_get_lit(ctx, 4); + + uint32_t page = arg_get_u32_def(ctx, 5, 0); + + uint8_t data[4]; + int dlen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 6), data, sizeof(data), &dlen); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t key[6]; + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 7), key, sizeof(key), &keylen); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t nrar[8]; + int nalen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 8), nrar, sizeof(nrar), &nalen); + + CLIParserFree(ctx); + + if (res != 0) { + return PM3_EINVARG; + } + + // sanity checks + if (dlen != sizeof(data)) { + PrintAndLogEx(WARNING, "Wrong DATA len expected 4, got %d", dlen); + return PM3_EINVARG; + } + + if (keylen != 0 && keylen != 6 && keylen != 4) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); + return PM3_EINVARG; + } + + if (nalen != 0 && nalen != 8) { + PrintAndLogEx(WARNING, "Wrong NR/AR len expected 0 or 8, got %d", nalen); + return PM3_EINVARG; + } + + uint8_t foo = (s03 + s04 + h24 + h27); + if (foo > 1) { + PrintAndLogEx(WARNING, "Only specify one HITAG write cmd"); + return PM3_EINVARG; + } else if (foo == 0) { + PrintAndLogEx(WARNING, "Specify one HITAG write cmd"); + return PM3_EINVARG; + } + + hitag_function htf; + hitag_data htd; + if (s03) { + htf = WHTSF_CHALLENGE; + memcpy(htd.auth.NrAr, nrar, sizeof(nrar)); + memcpy(htd.auth.data, data, sizeof(data)); + } + if (s04) { + htf = WHTSF_KEY; + memcpy(htd.crypto.key, key, sizeof(key)); + memcpy(htd.crypto.data, data, sizeof(data)); + } + if (h24) { + htf = WHT2F_CRYPTO; + memcpy(htd.pwd.password, key, 4); + memcpy(htd.crypto.data, data, sizeof(data)); + } + if (h27) { + htf = WHT2F_PASSWORD; + memcpy(htd.pwd.password, key, 4); + memcpy(htd.crypto.data, data, sizeof(data)); } clearCommandBuffer(); - SendCommandMIX(CMD_LF_HITAGS_WRITE, htf, 0, arg2, &htd, sizeof(htd)); + SendCommandMIX(CMD_LF_HITAGS_WRITE, htf, 0, page, &htd, sizeof(htd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 4000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 4000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -698,25 +808,67 @@ static int CmdLFHitagWriter(const char *Cmd) { } static int CmdLFHitag2Dump(const char *Cmd) { - PrintAndLogEx(INFO, "Dumping of tag memory"); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') return 0; // usage_hitag_dump(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag dump", + "Read all card memory and save to file" + "In password mode the default key is 4D494B52 (MIKR)\n" + "In crypto mode the default key is 4F4E4D494B52 (ONMIKR) format: ISK high + ISK low.", + "lf hitag dump -k 4F4E4D494B52\n" + "lf hitag dump -k 4D494B52\n" + ); - PacketResponseNG resp; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "file name"), + arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_str0(NULL, "nrar", "", "nonce / answer reader, 8 hex bytes"), + 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; + } - PrintAndLogEx(SUCCESS, "Dumping tag memory..."); - uint8_t *data = resp.data.asBytes; + uint8_t key[6]; + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, sizeof(key), &keylen); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } - char filename[FILE_PATH_SIZE]; - char *fnameptr = filename; - fnameptr += sprintf(fnameptr, "lf-hitag-"); - FillFileNameByUID(fnameptr, data, "-dump", 4); + uint8_t nrar[8]; + int nalen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 3), nrar, sizeof(nrar), &nalen); + CLIParserFree(ctx); + if (res != 0) { + return PM3_EINVARG; + } - saveFile(filename, ".bin", data, 48); - saveFileEML(filename, data, 48, 4); - saveFileJSON(filename, jsfHitag, data, 48, NULL); + PrintAndLogEx(WARNING, "to be implememted..."); + /* + PrintAndLogEx(SUCCESS, "Dumping tag memory..."); + + clearCommandBuffer(); + //SendCommandNG(CMD_LF_HITAG_DUMP, &htd, sizeof(htd)); + PacketResponseNG resp; + uint8_t *data = resp.data.asBytes; + if (fnlen < 1) { + char *fptr = filename; + fptr += sprintf(fptr, "lf-hitag-"); + FillFileNameByUID(fptr, data, "-dump", 4); + } + + saveFile(filename, ".bin", data, 48); + saveFileEML(filename, data, 48, 4); + saveFileJSON(filename, jsfHitag, data, 48, NULL); + */ return PM3_SUCCESS; } @@ -780,15 +932,16 @@ void annotateHitagS(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help" }, - {"list", CmdLFHitagList, IfPm3Hitag, "List Hitag trace history" }, - {"info", CmdLFHitagInfo, IfPm3Hitag, "Tag information" }, - {"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag Reader" }, - {"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder" }, - {"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication" }, - {"writer", CmdLFHitagWriter, IfPm3Hitag, "Act like a Hitag Writer" }, - {"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag2 tag" }, - {"cc", CmdLFHitagCheckChallenges, IfPm3Hitag, "Test all challenges" }, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"eload", CmdLFHitagEload, IfPm3Hitag, "Load Hitag dump file into emulator memory"}, + {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, + {"info", CmdLFHitagInfo, IfPm3Hitag, "Tag information"}, + {"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag reader"}, + {"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder"}, + {"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"}, + {"writer", CmdLFHitagWriter, IfPm3Hitag, "Act like a Hitag writer"}, + {"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag2 tag"}, + {"cc", CmdLFHitagCheckChallenges, IfPm3Hitag, "Test all challenges"}, { NULL, NULL, 0, NULL } }; @@ -804,5 +957,5 @@ int CmdLFHitag(const char *Cmd) { } int readHitagUid(void) { - return (CmdLFHitagReader("26") == PM3_SUCCESS); + return (CmdLFHitagReader("--26") == PM3_SUCCESS); } diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index b2719ba1e..8f811045c 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -238,7 +238,7 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { } int demodIndala(bool verbose) { - return demodIndalaEx(32, 0, 100, verbose); + return demodIndalaEx(0, 0, 100, verbose); } static int CmdIndalaDemod(const char *Cmd) { @@ -587,10 +587,10 @@ static int CmdIndalaSim(const char *Cmd) { // indala PSK // It has to send either 64bits (8bytes) or 224bits (28bytes). Zero padding needed if not. - // lf simpsk 1 c 32 r 2 d 0102030405060708 + // lf simpsk -1 -c 32 --fc 2 -d 0102030405060708 PrintAndLogEx(SUCCESS, "Simulating " _YELLOW_("%s") " Indala raw " _YELLOW_("%s") - , (is_long_uid) ? "224b" : "64b" + , (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"); diff --git a/client/src/cmdlfnedap.c b/client/src/cmdlfnedap.c index 829a5b106..e45d35c3d 100644 --- a/client/src/cmdlfnedap.c +++ b/client/src/cmdlfnedap.c @@ -242,25 +242,25 @@ static int CmdLFNedapDemod(const char *Cmd) { configuration -lf t55xx wr b 0 d 00170082 +lf t55xx wr -b 0 -d 00170082 1) uid 049033 -lf t55 wr b 1 d FF8B4168 -lf t55 wr b 2 d C90B5359 -lf t55 wr b 3 d 19A40087 -lf t55 wr b 4 d 120115CF +lf t55xx wr -b 1 -d FF8B4168 +lf t55xx wr -b 2 -d C90B5359 +lf t55xx wr -b 3 -d 19A40087 +lf t55xx wr -b 4 -d 120115CF 2) uid 001630 -lf t55 wr b 1 d FF8B6B20 -lf t55 wr b 2 d F19B84A3 -lf t55 wr b 3 d 18058007 -lf t55 wr b 4 d 1200857C +lf t55xx wr -b 1 -d FF8B6B20 +lf t55xx wr -b 2 -d F19B84A3 +lf t55xx wr -b 3 -d 18058007 +lf t55xx wr -b 4 -d 1200857C 3) uid 39feff -lf t55xx wr b 1 d ffbfa73e -lf t55xx wr b 2 d 4c0003ff -lf t55xx wr b 3 d ffbfa73e -lf t55xx wr b 4 d 4c0003ff +lf t55xx wr -b 1 -d ffbfa73e +lf t55xx wr -b 2 -d 4c0003ff +lf t55xx wr -b 3 -d ffbfa73e +lf t55xx wr -b 4 -d 4c0003ff */ @@ -459,7 +459,7 @@ static int CmdLFNedapClone(const char *Cmd) { if (res == PM3_SUCCESS) { PrintAndLogEx(INFO, "The block 0 was changed (eXtended) which can be hard to detect."); - PrintAndLogEx(INFO, "Configure it manually " _YELLOW_("`lf t55xx config b 64 d BI i 1 o 32`")); + PrintAndLogEx(INFO, "Configure it manually " _YELLOW_("`lf t55xx config -b 64 --BI -i -o 32`")); } else { PrintAndLogEx(NORMAL, ""); } diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index 5c2ccd98c..65a3c04a9 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -123,8 +123,9 @@ int demodPac(bool verbose) { PrintAndLogEx(DEBUG, "DEBUG: Error - PAC: NRZ Demod failed"); return PM3_ESOFT; } + bool invert = false; size_t size = DemodBufferLen; - int ans = detectPac(DemodBuffer, &size); + int ans = detectPac(DemodBuffer, &size, &invert); if (ans < 0) { if (ans == -1) PrintAndLogEx(DEBUG, "DEBUG: Error - PAC: too few bits found"); @@ -137,6 +138,12 @@ int demodPac(bool verbose) { return PM3_ESOFT; } + + if (invert) { + for (size_t i = ans; i < ans + 128; i++) { + DemodBuffer[i] ^= 1; + } + } setDemodBuff(DemodBuffer, 128, ans); setClockGrid(g_DemodClock, g_DemodStartIdx + (ans * g_DemodClock)); @@ -394,14 +401,29 @@ int CmdLFPac(const char *Cmd) { } // find PAC preamble in already demoded data -int detectPac(uint8_t *dest, size_t *size) { - if (*size < 128) return -1; //make sure buffer has data +int detectPac(uint8_t *dest, size_t *size, bool *invert) { + // make sure buffer has data + if (*size < 128) + return -1; + size_t startIdx = 0; uint8_t preamble[] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0}; - if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) - return -2; //preamble not found - if (*size != 128) return -3; //wrong demoded size - //return start position + if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) { + + // preamble not found + uint8_t pre_inv[] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1}; + if (!preambleSearch(dest, pre_inv, sizeof(pre_inv), size, &startIdx)) { + return -2; + } else { + *invert = true; + } + } + + // wrong demoded size + if (*size != 128) + return -3; + + // return start position return (int)startIdx; } diff --git a/client/src/cmdlfpac.h b/client/src/cmdlfpac.h index a2145051b..107280cdf 100644 --- a/client/src/cmdlfpac.h +++ b/client/src/cmdlfpac.h @@ -14,6 +14,6 @@ int CmdLFPac(const char *Cmd); int demodPac(bool verbose); -int detectPac(uint8_t *dest, size_t *size); +int detectPac(uint8_t *dest, size_t *size, bool *invert); #endif diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 5c298e85b..06c4e2e34 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -96,7 +96,17 @@ static void arg_add_t55xx_downloadlink(void *at[], uint8_t *idx, uint8_t show, u } static int CmdT55xxCloneHelp(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx clonehelp", + "Display a list of available commands for cloning specific techs on T5xx tags", + "lf t55xx clonehelp" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); PrintAndLogEx(NORMAL, "For cloning specific techs on T55xx tags, see commands available in corresponding LF sub-menus, e.g.:"); PrintAndLogEx(NORMAL, _GREEN_("lf awid clone")); PrintAndLogEx(NORMAL, _GREEN_("lf destron clone")); @@ -440,8 +450,8 @@ static int CmdT55xxSetConfig(const char *Cmd) { arg_lit0(NULL, "PSK2", "set demodulation PSK 2"), arg_lit0(NULL, "PSK3", "set demodulation PSK 3"), arg_lit0(NULL, "NRZ", "set demodulation NRZ"), - arg_lit0(NULL, "BI", "set demodulation Biphase"), - arg_lit0(NULL, "BIA", "set demodulation Diphase (inverted biphase)"), + arg_lit0(NULL, "BI", "set demodulation Biphase"), + arg_lit0(NULL, "BIA", "set demodulation Diphase (inverted biphase)"), arg_lit0("i", "inv", "set/reset data signal inversion"), arg_lit0(NULL, "q5", "set/reset as Q5/T5555 chip instead of T55x7"), arg_lit0(NULL, "st", "set/reset Sequence Terminator on"), @@ -458,11 +468,13 @@ static int CmdT55xxSetConfig(const char *Cmd) { bool mods[12]; int verify_mods = 0; while (idx - 1 < sizeof(mods)) { - mods[idx - 1] = arg_get_lit(ctx, idx); - verify_mods += mods[idx - 1]; - idx++; + mods[idx - 1] = arg_get_lit(ctx, idx); + verify_mods += mods[idx - 1]; + idx++; } + // Not these flags are used to Toggle the values. + // If not flag then dont set or reset, leave as is since the call may just be be setting a different value. bool invert = arg_get_lit(ctx, idx++); bool use_q5 = arg_get_lit(ctx, idx++); bool use_st = arg_get_lit(ctx, idx++); @@ -504,8 +516,9 @@ static int CmdT55xxSetConfig(const char *Cmd) { } // validate user specified bitrate - uint8_t rates[9] = {8, 16, 32, 40, 50, 64, 100, 128, 0}; + if (bitrate != -1) { + uint8_t rates[9] = {8, 16, 32, 40, 50, 64, 100, 128, 0}; uint8_t i = 0; for (; i < ARRAYLEN(rates); i++) { if (rates[i] == bitrate) { @@ -514,7 +527,7 @@ static int CmdT55xxSetConfig(const char *Cmd) { break; } } - if (i == 9){ + if (i == 9) { PrintAndLogEx(FAILED, "Error select a valid bitrate"); return PM3_EINVARG; } @@ -525,17 +538,21 @@ static int CmdT55xxSetConfig(const char *Cmd) { config.offset = offset; } - // validate user specific T5555 / Q5 - config.Q5 = use_q5; + // validate user specific T5555 / Q5 - use the flag to toggle between T5577 and Q5 + config.Q5 ^= use_q5; // validate user specific sequence terminator - config.ST = use_st; + // if use_st flag was supplied, then toggle and update the config block0; if not supplied skip the config block0 update. if (use_st) { + config.ST ^= use_st; config.block0 = ((config.block0 & ~(0x8)) | (config.ST << 3)); } // validate user specific invert - config.inverted = invert; + // In theory this should also be set in the config block 0; butit requries the extend mode config, which will change other things. + // as such, leave in user config for decoding the data until a full fix can be added. + // use the flag to toggle if invert is on or off. + config.inverted ^= invert; // validate user specific downlink mode uint8_t downlink_mode = config.downlink_mode; @@ -551,7 +568,7 @@ static int CmdT55xxSetConfig(const char *Cmd) { config.downlink_mode = downlink_mode; // validate user specific modulation - if (mods[0]){ + if (mods[0]) { config.modulation = DEMOD_FSK; } else if (mods[1]) { config.modulation = DEMOD_FSK1; @@ -581,7 +598,7 @@ static int CmdT55xxSetConfig(const char *Cmd) { } else if (mods[11]) { config.modulation = DEMOD_BIa; config.inverted = 1; - } + } config.block0 = ((config.block0 & ~(0x1f000)) | (config.modulation << 12)); @@ -637,10 +654,10 @@ static int CmdT55xxReadBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf t55xx read", "Read T55xx block data. This commands defaults to page 0.\n\n" - _RED_(" * * * WARNING * * *") "\n" + _RED_(" * * * WARNING * * *") "\n" _CYAN_("Use of read with password on a tag not configured") "\n" _CYAN_("for a password can damage the tag") "\n" - _RED_(" * * * * * * * * * *"), + _RED_(" * * * * * * * * * *"), "lf t55xx read -b 0 --> read data from block 0\n" "lf t55xx read -b 0 --pwd 01020304 --> read data from block 0, pwd 01020304\n" "lf t55xx read -b 0 --pwd 01020304 -o --> read data from block 0, pwd 01020304, override\n" @@ -651,7 +668,7 @@ static int CmdT55xxReadBlock(const char *Cmd) { arg_param_begin, arg_int1("b", "blk", "<0-7>", "block number to read"), arg_str0("p", "pwd", "", "password (4 hex bytes)"), - arg_lit0("o", "override", "override safety check"), + arg_lit0("o", "override", "override safety check"), arg_lit0(NULL, "pg1", "read page 1"), }; uint8_t idx = 5; @@ -915,10 +932,7 @@ static int CmdT55xxDetect(const char *Cmd) { else // This will set the default to user all d/l modes which will cover the ra flag as well. try_all_dl_modes = true; - - bool try_with_pwd = false; bool found = false; - bool usewake = false; // Setup the 90ms time value to sleep for after the wake, to allow delay init to complete (~70ms) struct timespec sleepperiod; @@ -937,6 +951,8 @@ static int CmdT55xxDetect(const char *Cmd) { char wakecmd[20] = { 0x00 }; sprintf(wakecmd, "-p %08" PRIx64, password); + bool usewake = false; + bool try_with_pwd = false; // do ... while not found and not yet tried with wake (for AOR or Init Delay) do { // do ... while to check without password then loop back if password supplied @@ -1121,7 +1137,7 @@ bool t55xxTryDetectModulationEx(uint8_t downlink_mode, bool print_config, uint32 // allow undo save_restoreGB(GRAPH_SAVE); // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - CmdLtrim("160"); + CmdLtrim("-i 160"); if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS) && test(DEMOD_PSK1, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { tests[hits].modulation = DEMOD_PSK1; tests[hits].bitrate = bitRate; @@ -1674,7 +1690,7 @@ static int CmdT55xxReadTrace(const char *Cmd) { // 1 (help) + 1 (one user specified params) + (5 T55XX_DLMODE_SINGLE) void *argtable[2 + 5] = { arg_param_begin, - arg_lit0("1", NULL, "extract using data from graphbuffer"), + arg_lit0("1", NULL, "extract using data from graphbuffer"), }; uint8_t idx = 2; arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); @@ -1725,7 +1741,7 @@ static int CmdT55xxReadTrace(const char *Cmd) { } } - if (DemodBufferLen == 0){ + if (DemodBufferLen == 0) { return PM3_ESOFT; } @@ -2296,13 +2312,13 @@ static int CmdT55xxDump(const char *Cmd) { static int CmdT55xxRestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf t55xx restore", - "This command restores T55xx card page 0/1 n blocks", + "Restore T55xx card page 0/1 n blocks from (bin/eml/json) dump file", "lf t55xx restore -f lf-t55xx-00148040-dump.bin" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "filename of the dump file (bin|eml)"), + arg_str0("f", "file", "", "filename of dump file"), arg_str0("p", "pwd", "", "password if target card has password set (4 hex bytes)"), arg_param_end }; @@ -2330,94 +2346,106 @@ static int CmdT55xxRestore(const char *Cmd) { return PM3_EINVARG; } - char ext[FILE_PATH_SIZE] = {0}; - uint32_t data[12] = {0}; - size_t datalen = 0; + size_t dlen = 0; + uint8_t *dump = calloc(T55x7_BLOCK_COUNT * 4, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } - int retval = PM3_ESOFT; - if (fnlen > 4) { // Holds extension [.bin|.eml] - memcpy(ext, &filename[fnlen - 4], 4); - ext[5] = 0x00; - - // check if valid file extension and attempt to load data - if (memcmp(ext, ".bin", 4) == 0) { - filename[fnlen - 4] = 0x00; - retval = loadFile(filename, ".bin", data, sizeof(data), &datalen); - - } else if (memcmp(ext, ".eml", 4) == 0) { - filename[fnlen - 4] = 0x00; - datalen = 12; - retval = loadFileEML(filename, (uint8_t *)data, &datalen); - - } else { - PrintAndLogEx(WARNING, "\nWarning: invalid dump filename "_YELLOW_("%s")" to restore!\n", filename); + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, &dlen); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&dump, &dlen); + break; + } + case JSON: { + res = loadFileJSON(filename, dump, T55x7_BLOCK_COUNT * 4, &dlen, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/EML/JSON formats allowed"); + free(dump); + return PM3_EINVARG; } } - if (retval != PM3_SUCCESS) { - return retval; + //sanity checks of file processing + if (res != PM3_SUCCESS) { + free(dump); + return res; } - if (datalen == T55x7_BLOCK_COUNT * 4) { - // 12 blocks * 4 bytes per block - - // this fct creats strings to call "lf t55 write" command. - // - // - uint8_t downlink_mode; - char wcmd[100]; - char pwdopt [14] = {0}; // p XXXXXXXX + if (dlen != T55x7_BLOCK_COUNT * 4) { + free(dump); + PrintAndLogEx(FAILED, "wrong length of dump file. Expected 48 bytes, got %zu", dlen); + return PM3_EFILE; + } - if (usepwd) - snprintf(pwdopt, sizeof(pwdopt), "-p %08X", password); + // 12 blocks * 4 bytes per block + // this part creates strings to call "lf t55 write" command. + PrintAndLogEx(INFO, "Starting to write..."); - uint8_t idx; - // Restore endien for writing to card - for (idx = 0; idx < 12; idx++) { - data[idx] = BSWAP_32(data[idx]); - } + uint8_t downlink_mode; + char wcmd[100]; + char pwdopt [14] = {0}; // p XXXXXXXX - // Have data ready, lets write - // Order - // write blocks 1..7 page 0 - // 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 (usepwd) + snprintf(pwdopt, sizeof(pwdopt), "-p %08X", password); - // 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); + 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]); + } - if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); - } - } + // Have data ready, lets write + // Order + // write blocks 1..7 page 0 + // 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 password was set on the "blank" update as we may have just changed it - if (usepwd) { - snprintf(pwdopt, sizeof(pwdopt), "-p %08X", data[7]); - } + // 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); - // 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); - - if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); - } - } - - // Update downlink mode for the page 0 config write. - config.downlink_mode = downlink_mode; - - // Write the page 0 config - snprintf(wcmd, sizeof(wcmd), "-b 0 -d %08X %s", data[0], pwdopt); if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Warning: error writing blk 0"); + PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); } } + + // 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]); + } + + // 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); + + if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); + } + } + + // Update downlink mode for the page 0 config write. + config.downlink_mode = downlink_mode; + + // Write the page 0 config + snprintf(wcmd, sizeof(wcmd), "-b 0 -d %08X %s", data[0], pwdopt); + if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Warning: error writing blk 0"); + } + free(dump); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } /* @@ -2540,7 +2568,7 @@ char *GetBitRateStr(uint32_t id, bool xmode) { char *retStr = buf; if (xmode) { //xmode bitrate calc is same as em4x05 calc - snprintf(retStr, sizeof(buf), "%u - RF/%d", id, EM4x05_GET_BITRATE(id)); + snprintf(retStr, sizeof(buf), "%u - RF/%u", id, EM4x05_GET_BITRATE(id)); } else { switch (id) { case 0: @@ -2811,11 +2839,11 @@ static int CmdResetRead(const char *Cmd) { ); // 1 (help) + 0(one user specified params) + (5 T55XX_DLMODE_SINGLE) - void *argtable[0 + 5] = { + void *argtable[2 + 5] = { arg_param_begin, arg_lit0("1", NULL, "extract using data from graphbuffer"), }; - uint8_t idx = 1; + uint8_t idx = 2; arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3006,7 +3034,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { void *argtable[4 + 6] = { arg_param_begin, arg_lit0("m", "fm", "use dictionary from flash memory (RDV4)"), - arg_str0("f", "file", "", "file name"), + arg_str0("f", "file", "", "file name"), arg_str0(NULL, "em", "", "EM4100 ID (5 hex bytes)"), }; uint8_t idx = 4; @@ -3051,23 +3079,24 @@ static int CmdT55xxChkPwds(const char *Cmd) { return PM3_EINVARG; } - uint8_t downlink_mode = config.downlink_mode; - if (r0) - downlink_mode = refFixedBit; - else if (r1) + uint8_t downlink_mode = refFixedBit; // Password checks should always start with default/fixed bit unluess requested by user for specific mode + // if (r0 || ra) // ra should start downlink mode ad fixed bit to loop through all modes correctly + // downlink_mode = refFixedBit; + // else + if (r1) downlink_mode = refLongLeading; else if (r2) downlink_mode = refLeading0; else if (r3) downlink_mode = ref1of4; - bool use_pwd_file = false; + bool use_pwd_file = true; // Assume we are going to use a file, unless turned off later. + if (strlen(filename) == 0) { snprintf(filename, sizeof(filename), "t55xx_default_pwds"); - use_pwd_file = true; } - PrintAndLogEx(INFO, "press " _GREEN_("'enter'") " to cancel the command"); + PrintAndLogEx(INFO, "press " _GREEN_("") " to exit"); PrintAndLogEx(NORMAL, ""); /* // block 7, page1 = false, usepwd = false, override = false, pwd = 00000000 @@ -3085,6 +3114,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { uint8_t flags = downlink_mode << 3; if (from_flash) { + use_pwd_file = false; // turn of local password file since we are checking from flash. clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_CHK_PWDS, &flags, sizeof(flags)); PacketResponseNG resp; @@ -3162,7 +3192,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(INFO, "press " _GREEN_("'enter'") " to cancel the command"); + PrintAndLogEx(INFO, "press " _GREEN_("") " to exit"); for (uint32_t c = 0; c < keycount && found == false; ++c) { @@ -3181,17 +3211,16 @@ static int CmdT55xxChkPwds(const char *Cmd) { PrintAndLogEx(INFO, "testing %08"PRIX32, curr_password); for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++) { - - if (!AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password, dl_mode)) { - continue; + // If aquire fails, then we still need to check if we are only trying a single downlink mode. + // If we continue on fail, it will skip that test and try the next downlink mode; thus slowing down the check + // when on a single downlink mode is wanted. + if (AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password, dl_mode)) { + found = t55xxTryDetectModulationEx(dl_mode, T55XX_PrintConfig, 0, curr_password); + if (found) { + PrintAndLogEx(SUCCESS, "found valid password: [ " _GREEN_("%08"PRIX32) " ]", curr_password); + break; + } } - - found = t55xxTryDetectModulationEx(dl_mode, T55XX_PrintConfig, 0, curr_password); - if (found) { - PrintAndLogEx(SUCCESS, "found valid password: [ " _GREEN_("%08"PRIX32) " ]", curr_password); - break; - } - if (ra == false) // Exit loop if not trying all downlink modes break; } @@ -3257,10 +3286,12 @@ static int CmdT55xxBruteForce(const char *Cmd) { return PM3_EINVARG; } - uint8_t downlink_mode = config.downlink_mode; - if (r0) - downlink_mode = refFixedBit; - else if (r1) + uint8_t downlink_mode = refFixedBit; // if no downlink mode suppliled use fixed bit/default as the is the most common + // Since we dont know the password the config.downlink mode is of little value. +// if (r0 || ra) // if try all (ra) then start at fixed bit for correct try all +// downlink_mode = refFixedBit; +// else + if (r1) downlink_mode = refLongLeading; else if (r2) downlink_mode = refLeading0; @@ -3275,7 +3306,7 @@ static int CmdT55xxBruteForce(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "press " _GREEN_("'enter'") " to cancel the command"); + PrintAndLogEx(INFO, "press " _GREEN_("") " to exit"); PrintAndLogEx(INFO, "Search password range [%08X -> %08X]", start_password, end_password); uint64_t t1 = msclock(); @@ -3383,7 +3414,7 @@ static int CmdT55xxRecoverPW(const char *Cmd) { else if (r3) downlink_mode = ref1of4; - PrintAndLogEx(NORMAL, "press " _GREEN_("'enter'") " to cancel the command"); + PrintAndLogEx(INFO, "press " _GREEN_("") " to exit"); int bit = 0; uint32_t curr_password = 0x0; @@ -3546,7 +3577,7 @@ bool tryDetectP1(bool getData) { // allow undo // save_restoreGB(GRAPH_SAVE); // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - //CmdLtrim("160"); + //CmdLtrim("-i 160"); if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS) && preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && (DemodBufferLen == 32 || DemodBufferLen == 64)) { @@ -3928,6 +3959,15 @@ static int CmdT55xxSniff(const char *Cmd) { int opt_width0 = arg_get_int_def(ctx, 4, -1); CLIParserFree(ctx); + if (opt_width0 == 0) { + PrintAndLogEx(ERR, "Must call with --zero larger than 0"); + return PM3_EINVARG; + } + if ((opt_width0 == 0) || (opt_width1 == 0)) { + PrintAndLogEx(ERR, "Must call with --one larger than 0"); + return PM3_EINVARG; + } + if (opt_width0 > 0 && opt_width1 == -1) { PrintAndLogEx(ERR, _RED_("Missing sample width for ONE")); return PM3_EINVARG; @@ -3938,15 +3978,6 @@ static int CmdT55xxSniff(const char *Cmd) { return PM3_EINVARG; } - if (opt_width0 == 0) { - PrintAndLogEx(ERR, "Must call with --zero larger than 0"); - return PM3_EINVARG; - } - if ((opt_width0 == 0) || (opt_width1 == 0)) { - PrintAndLogEx(ERR, "Must call with --one larger than 0"); - return PM3_EINVARG; - } - uint8_t width1 = 0; uint8_t width0 = 0; @@ -3984,18 +4015,13 @@ static int CmdT55xxSniff(const char *Cmd) { 00 01 10 11 */ - bool have_data = false; uint8_t page, blockAddr; - uint16_t dataLen = 0; size_t idx = 0; uint32_t usedPassword, blockData; - int pulseSamples = 0; - int pulseIdx = 0; - int minWidth = 1000; - int maxWidth = 0; - char modeText [100]; - char pwdText [100]; - char dataText [100]; + int pulseSamples = 0, pulseIdx = 0; + char modeText[100]; + char pwdText[100]; + char dataText[100]; int pulseBuffer[80] = { 0 }; // max should be 73 +/- - Holds Pulse widths char data[80]; // linked to pulseBuffer. - Holds 0/1 from pulse widths @@ -4015,11 +4041,11 @@ static int CmdT55xxSniff(const char *Cmd) { // loop though sample buffer while (idx < GraphTraceLen) { - minWidth = 1000; - maxWidth = 0; - dataLen = 0; + int minWidth = 1000; + int maxWidth = 0; + uint16_t dataLen = 0; data[0] = 0; - have_data = false; + bool have_data = false; sprintf(modeText, "Default"); sprintf(pwdText, " "); sprintf(dataText, " "); @@ -4101,7 +4127,6 @@ static int CmdT55xxSniff(const char *Cmd) { // printf ("Fixed | Data end of 80 samples | offset : %llu - datalen %-2d - data : %s --- - Bit 0 width : %d\n",idx,dataLen,data,pulseBuffer[0]); if (data[0] == '0') { // should never get here.. - dataLen = 0; data[0] = 0; } else { diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index fe4fa0b84..b31b14836 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -129,27 +129,31 @@ static int CmdAuto(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0("c", NULL, "Continue searching even after a first hit"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool exit_first = (arg_get_lit(ctx, 1) == false); CLIParserFree(ctx); PrintAndLogEx(INFO, "lf search"); int ret = CmdLFfind(""); - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS && exit_first) return ret; PrintAndLogEx(INFO, "hf search"); ret = CmdHFSearch(""); - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS && exit_first) return ret; PrintAndLogEx(INFO, "lf search - unknown"); ret = lf_search_plus(""); - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS && exit_first) return ret; - PrintAndLogEx(INFO, "Failed both LF / HF SEARCH,"); + if (ret != PM3_SUCCESS) + PrintAndLogEx(INFO, "Failed both LF / HF SEARCH,"); + PrintAndLogEx(INFO, "Trying " _YELLOW_("`lf read`") " and save a trace for you"); CmdPlot(""); @@ -259,7 +263,17 @@ static int CmdMsleep(const char *Cmd) { } static int CmdQuit(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "quit", + "Quit the Proxmark3 client terminal", + "quit" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); return PM3_EFATAL; } @@ -274,37 +288,47 @@ static int CmdPref(const char *Cmd) { } static int CmdClear(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "clear", + "Clear the Proxmark3 client terminal screen", + "clear" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); PrintAndLogEx(NORMAL, _CLEAR_ _TOP_ ""); return PM3_SUCCESS; } static command_t CommandTable[] = { - {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Technology") " -----------------------"}, - - {"analyse", CmdAnalyse, AlwaysAvailable, "{ Analyse utils... }"}, - {"data", CmdData, AlwaysAvailable, "{ Plot window / data buffer manipulation... }"}, - {"emv", CmdEMV, AlwaysAvailable, "{ EMV ISO-14443 / ISO-7816... }"}, - {"hf", CmdHF, AlwaysAvailable, "{ High frequency commands... }"}, - {"hw", CmdHW, AlwaysAvailable, "{ Hardware commands... }"}, - {"lf", CmdLF, AlwaysAvailable, "{ Low frequency commands... }"}, - {"mem", CmdFlashMem, IfPm3Flash, "{ Flash memory manipulation... }"}, - {"reveng", CmdRev, AlwaysAvailable, "{ CRC calculations from RevEng software... }"}, - {"smart", CmdSmartcard, AlwaysAvailable, "{ Smart card ISO-7816 commands... }"}, - {"script", CmdScript, AlwaysAvailable, "{ Scripting commands... }"}, - {"trace", CmdTrace, AlwaysAvailable, "{ Trace manipulation... }"}, - {"usart", CmdUsart, IfPm3FpcUsartFromUsb, "{ USART commands... }"}, - {"wiegand", CmdWiegand, AlwaysAvailable, "{ Wiegand format manipulation... }"}, - {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, - {"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"}, - {"clear", CmdClear, AlwaysAvailable, "Clear screen"}, - {"help", CmdHelp, AlwaysAvailable, "This help. Use " _YELLOW_("' help'") " for details of a particular command."}, - {"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"}, - {"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"}, - {"pref", CmdPref, AlwaysAvailable, "Edit preferences"}, - {"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"}, - {"quit", CmdQuit, AlwaysAvailable, ""}, - {"exit", CmdQuit, AlwaysAvailable, "Exit program"}, + {"help", CmdHelp, AlwaysAvailable, "Use `" _YELLOW_(" help") "` for details of a command"}, + {"prefs", CmdPref, AlwaysAvailable, "{ Edit client/device preferences... }"}, + {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Technology") " -----------------------"}, + {"analyse", CmdAnalyse, AlwaysAvailable, "{ Analyse utils... }"}, + {"data", CmdData, AlwaysAvailable, "{ Plot window / data buffer manipulation... }"}, + {"emv", CmdEMV, AlwaysAvailable, "{ EMV ISO-14443 / ISO-7816... }"}, + {"hf", CmdHF, AlwaysAvailable, "{ High frequency commands... }"}, + {"hw", CmdHW, AlwaysAvailable, "{ Hardware commands... }"}, + {"lf", CmdLF, AlwaysAvailable, "{ Low frequency commands... }"}, + {"mem", CmdFlashMem, IfPm3Flash, "{ Flash memory manipulation... }"}, + {"reveng", CmdRev, AlwaysAvailable, "{ CRC calculations from RevEng software... }"}, + {"smart", CmdSmartcard, AlwaysAvailable, "{ Smart card ISO-7816 commands... }"}, + {"script", CmdScript, AlwaysAvailable, "{ Scripting commands... }"}, + {"trace", CmdTrace, AlwaysAvailable, "{ Trace manipulation... }"}, + {"usart", CmdUsart, IfPm3FpcUsartFromUsb, "{ USART commands... }"}, + {"wiegand", CmdWiegand, AlwaysAvailable, "{ Wiegand format manipulation... }"}, + {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, + {"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"}, + {"clear", CmdClear, AlwaysAvailable, "Clear screen"}, + {"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"}, + {"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"}, + {"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"}, + {"quit", CmdQuit, AlwaysAvailable, ""}, + {"exit", CmdQuit, AlwaysAvailable, "Exit program"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 279f87664..1f5476b48 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -195,12 +195,22 @@ void CmdsHelp(const command_t Commands[]) { int CmdsParse(const command_t Commands[], const char *Cmd) { // Help dump children if (strcmp(Cmd, "XX_internal_command_dump_XX") == 0) { - dumpCommandsRecursive(Commands, 0); + dumpCommandsRecursive(Commands, 0, false); + return PM3_SUCCESS; + } + // Help dump children with help + if (strcmp(Cmd, "XX_internal_command_dump_full_XX") == 0) { + dumpCommandsRecursive(Commands, 0, true); return PM3_SUCCESS; } // Markdown help dump children if (strcmp(Cmd, "XX_internal_command_dump_markdown_XX") == 0) { - dumpCommandsRecursive(Commands, 1); + dumpCommandsRecursive(Commands, 1, false); + return PM3_SUCCESS; + } + // Markdown help dump children with help + if (strcmp(Cmd, "XX_internal_command_dump_markdown_help_XX") == 0) { + dumpCommandsRecursive(Commands, 1, true); return PM3_SUCCESS; } @@ -240,10 +250,18 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { if (cmd_name[0] == '#') return PM3_SUCCESS; + // find args, check for -h / --help + int tmplen = len; + while (Cmd[tmplen] == ' ') + ++tmplen; + bool request_help = (strcmp(Cmd + tmplen, "-h") == 0) || (strcmp(Cmd + tmplen, "--help") == 0); + int i = 0; while (Commands[i].Name) { if (0 == strcmp(Commands[i].Name, cmd_name)) { - if (Commands[i].IsAvailable()) { + if ((Commands[i].Help[0] == '{') || // always allow parsing categories + request_help || // always allow requesting help + Commands[i].IsAvailable()) { break; } else { PrintAndLogEx(WARNING, "This command is " _YELLOW_("not available") " in this mode"); @@ -282,7 +300,7 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { static char pparent[512] = {0}; static char *parent = pparent; -void dumpCommandsRecursive(const command_t cmds[], int markdown) { +void dumpCommandsRecursive(const command_t cmds[], int markdown, bool full_help) { if (cmds[0].Name == NULL) return; int i = 0; @@ -293,7 +311,7 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown) { if (markdown) { PrintAndLogEx(NORMAL, "|%-*s|%-*s|%s", w_cmd, "command", w_off, "offline", "description"); PrintAndLogEx(NORMAL, "|%-*s|%-*s|%s", w_cmd, "-------", w_off, "-------", "-----------"); - } else { + } else if (! full_help) { PrintAndLogEx(NORMAL, "%-*s|%-*s|%s", w_cmd, "command", w_off, "offline", "description"); PrintAndLogEx(NORMAL, "%-*s|%-*s|%s", w_cmd, "-------", w_off, "-------", "-----------"); } @@ -309,8 +327,13 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown) { cmd_offline = "Y"; 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 + 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"); + } else { PrintAndLogEx(NORMAL, "%s%-*s|%-*s|%s", parent, w_cmd - (int)strlen(parent), cmds[i].Name, w_off, cmd_offline, cmds[i].Help); + } ++i; } PrintAndLogEx(NORMAL, "\n"); @@ -322,8 +345,12 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown) { if ((cmds[i].Name[0] == '-' || strlen(cmds[i].Name) == 0) && ++i) continue; if (cmds[i].Help[0] != '{' && ++i) continue; - PrintAndLogEx(NORMAL, "### %s%s\n\n %s\n", parent, cmds[i].Name, cmds[i].Help); - + if (full_help) { + PrintAndLogEx(NORMAL, "======================================================================================="); + PrintAndLogEx(NORMAL, _RED_("%s%s\n\n ")_CYAN_("%s\n"), parent, cmds[i].Name, cmds[i].Help); + } else { + PrintAndLogEx(NORMAL, "### %s%s\n\n %s\n", parent, cmds[i].Name, cmds[i].Help); + } char currentparent[512] = {0}; snprintf(currentparent, sizeof currentparent, "%s%s ", parent, cmds[i].Name); char *old_parent = parent; @@ -332,8 +359,11 @@ void dumpCommandsRecursive(const command_t cmds[], int markdown) { // in turn calls the CmdsParse above. if (markdown) cmds[i].Parse("XX_internal_command_dump_markdown_XX"); - else + else if (full_help) { + cmds[i].Parse("XX_internal_command_dump_full_XX"); + } else { cmds[i].Parse("XX_internal_command_dump_XX"); + } parent = old_parent; ++i; diff --git a/client/src/cmdparser.h b/client/src/cmdparser.h index ff59df705..9b0933329 100644 --- a/client/src/cmdparser.h +++ b/client/src/cmdparser.h @@ -54,6 +54,6 @@ void CmdsHelp(const command_t Commands[]); void CmdsLS(const command_t Commands[]); // Parse a command line int CmdsParse(const command_t Commands[], const char *Cmd); -void dumpCommandsRecursive(const command_t cmds[], int markdown); +void dumpCommandsRecursive(const command_t cmds[], int markdown, bool full_help); #endif diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 4f3aad7d9..9b5f4e374 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -30,6 +30,7 @@ #include "proxmark3.h" #include "ui.h" #include "fileutils.h" +#include "cliparser.h" // cliparsing #ifdef HAVE_LUA_SWIG extern int luaopen_pm3(lua_State *L); @@ -198,7 +199,17 @@ static void set_python_paths(void) { * ending with .lua */ static int CmdScriptList(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "script list", + "List available Lua, Cmd and Python scripts", + "script list" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); PrintAndLogEx(NORMAL, "\n" _YELLOW_("[ Lua scripts ]")); int ret = searchAndList(LUA_SCRIPTS_SUBDIR, ".lua"); if (ret != PM3_SUCCESS) @@ -223,21 +234,46 @@ static int CmdScriptList(const char *Cmd) { * @return */ static int CmdScriptRun(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "script run", + "Run a Lua, Cmd or Python script. " + "If no extension it will search for lua/cmd/py extensions\n" + "Use `script list` to see available scripts", + "script run my_script -h\n" + ); - char preferredName[128] = {0}; + void *argtable[] = { + arg_param_begin, + arg_file1(NULL, NULL, "", "name of script to run"), + arg_strx0(NULL, NULL, "", "script parameters"), + arg_param_end + }; + + int fnlen = 0; + char filename[128] = {0}; + int arg_len = 0; char arguments[256] = {0}; - int name_len = 0; - int arg_len = 0; - static uint8_t luascriptfile_idx = 0; - sscanf(Cmd, "%127s%n %255[^\n\r]%n", preferredName, &name_len, arguments, &arg_len); - if (strlen(preferredName) == 0) { - PrintAndLogEx(FAILED, "no script name provided"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`script list`") " to see available scripts"); - return PM3_EINVARG; + sscanf(Cmd, "%127s%n %255[^\n\r]%n", filename, &fnlen, arguments, &arg_len); + + // hack + // since we don't want to use "-f" for script filename, + // and be able to send in parameters into script meanwhile + // being able to "-h" here too. + if ((strlen(filename) == 0) || + (strcmp(filename, "-h") == 0) || + (strcmp(filename, "--help") == 0)) { + ctx->argtable = argtable; + ctx->argtableLen = arg_getsize(argtable); + CLIParserPrintHelp(ctx); + CLIParserFree(ctx); + return PM3_ESOFT; } + CLIParserFree(ctx); + + // try to detect a valid script file extention, case-insensitive char *extension_chk; - extension_chk = str_dup(preferredName); + extension_chk = str_dup(filename); str_lower(extension_chk); pm3_scriptfile_t ext = PM3_UNSPECIFIED; @@ -253,8 +289,9 @@ static int CmdScriptRun(const char *Cmd) { #endif free(extension_chk); + static uint8_t luascriptfile_idx = 0; char *script_path = NULL; - if (((ext == PM3_LUA) || (ext == PM3_UNSPECIFIED)) && (searchFile(&script_path, LUA_SCRIPTS_SUBDIR, preferredName, ".lua", true) == PM3_SUCCESS)) { + if (((ext == PM3_LUA) || (ext == PM3_UNSPECIFIED)) && (searchFile(&script_path, LUA_SCRIPTS_SUBDIR, filename, ".lua", true) == PM3_SUCCESS)) { int error; if (luascriptfile_idx == MAX_NESTED_LUASCRIPT) { PrintAndLogEx(ERR, "too many nested scripts, skipping %s\n", script_path); @@ -308,11 +345,11 @@ static int CmdScriptRun(const char *Cmd) { // close the Lua state lua_close(lua_state); luascriptfile_idx--; - PrintAndLogEx(SUCCESS, "\nfinished " _YELLOW_("%s"), preferredName); + PrintAndLogEx(SUCCESS, "\nfinished " _YELLOW_("%s"), filename); return PM3_SUCCESS; } - if (((ext == PM3_CMD) || (ext == PM3_UNSPECIFIED)) && (searchFile(&script_path, CMD_SCRIPTS_SUBDIR, preferredName, ".cmd", true) == PM3_SUCCESS)) { + if (((ext == PM3_CMD) || (ext == PM3_UNSPECIFIED)) && (searchFile(&script_path, CMD_SCRIPTS_SUBDIR, filename, ".cmd", true) == PM3_SUCCESS)) { PrintAndLogEx(SUCCESS, "executing Cmd " _YELLOW_("%s"), script_path); PrintAndLogEx(SUCCESS, "args " _YELLOW_("'%s'"), arguments); @@ -350,14 +387,14 @@ static int CmdScriptRun(const char *Cmd) { #ifdef HAVE_PYTHON - if (((ext == PM3_PY) || (ext == PM3_UNSPECIFIED)) && (searchFile(&script_path, PYTHON_SCRIPTS_SUBDIR, preferredName, ".py", true) == PM3_SUCCESS)) { + if (((ext == PM3_PY) || (ext == PM3_UNSPECIFIED)) && (searchFile(&script_path, PYTHON_SCRIPTS_SUBDIR, filename, ".py", true) == PM3_SUCCESS)) { PrintAndLogEx(SUCCESS, "executing python " _YELLOW_("%s"), script_path); PrintAndLogEx(SUCCESS, "args " _YELLOW_("'%s'"), arguments); - wchar_t *program = Py_DecodeLocale(preferredName, NULL); + wchar_t *program = Py_DecodeLocale(filename, NULL); if (program == NULL) { - PrintAndLogEx(ERR, "could not decode " _YELLOW_("%s"), preferredName); + PrintAndLogEx(ERR, "could not decode " _YELLOW_("%s"), filename); free(script_path); return PM3_ESOFT; } @@ -374,7 +411,7 @@ static int CmdScriptRun(const char *Cmd) { char *argv[128]; int argc = split(arguments, argv); wchar_t *py_args[argc]; - py_args[0] = Py_DecodeLocale(preferredName, NULL); + py_args[0] = Py_DecodeLocale(filename, NULL); for (int i = 0; i < argc; i++) { py_args[i + 1] = Py_DecodeLocale(argv[i], NULL); } @@ -395,15 +432,15 @@ static int CmdScriptRun(const char *Cmd) { free(script_path); return PM3_ESOFT; } - int ret = Pm3PyRun_SimpleFileNoExit(f, preferredName); + int ret = Pm3PyRun_SimpleFileNoExit(f, filename); Py_Finalize(); PyMem_RawFree(program); free(script_path); if (ret) { - PrintAndLogEx(WARNING, "\nfinished " _YELLOW_("%s") " with exception", preferredName); + PrintAndLogEx(WARNING, "\nfinished " _YELLOW_("%s") " with exception", filename); return PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "\nfinished " _YELLOW_("%s"), preferredName); + PrintAndLogEx(SUCCESS, "\nfinished " _YELLOW_("%s"), filename); return PM3_SUCCESS; } } @@ -412,26 +449,26 @@ static int CmdScriptRun(const char *Cmd) { // file not found, let's search again to display the error messages int ret = PM3_EUNDEF; if (ext == PM3_LUA) - ret = searchFile(&script_path, LUA_SCRIPTS_SUBDIR, preferredName, ".lua", false); + ret = searchFile(&script_path, LUA_SCRIPTS_SUBDIR, filename, ".lua", false); else if (ext == PM3_CMD) - ret = searchFile(&script_path, CMD_SCRIPTS_SUBDIR, preferredName, ".cmd", false); + ret = searchFile(&script_path, CMD_SCRIPTS_SUBDIR, filename, ".cmd", false); #ifdef HAVE_PYTHON else if (ext == PM3_PY) - ret = searchFile(&script_path, PYTHON_SCRIPTS_SUBDIR, preferredName, ".py", false); + ret = searchFile(&script_path, PYTHON_SCRIPTS_SUBDIR, filename, ".py", false); else if (ext == PM3_UNSPECIFIED) - PrintAndLogEx(FAILED, "Error - can't find %s.[lua|cmd|py]", preferredName); + PrintAndLogEx(FAILED, "Error - can't find %s.[lua|cmd|py]", filename); #else else if (ext == PM3_UNSPECIFIED) - PrintAndLogEx(FAILED, "Error - can't find %s.[lua|cmd]", preferredName); + PrintAndLogEx(FAILED, "Error - can't find %s.[lua|cmd]", filename); #endif free(script_path); return ret; } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "Usage info"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdScriptList, AlwaysAvailable, "List available scripts"}, - {"run", CmdScriptRun, AlwaysAvailable, " -- execute a script"}, + {"run", CmdScriptRun, AlwaysAvailable, " - execute a script"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 247090dd0..fcd52abf8 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -255,51 +255,85 @@ static void PrintATR(uint8_t *atr, size_t atrlen) { } } -static int smart_wait(uint8_t *data, bool silent) { - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - if (!silent) PrintAndLogEx(WARNING, "smart card response timeout"); - return -1; - } +static int smart_wait(uint8_t *out, int maxoutlen, bool verbose) { + int i = 4; + uint32_t len = 0; + do { + clearCommandBuffer(); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SMART_RAW, &resp, 1000)) { - uint32_t len = resp.oldarg[0]; - if (!len) { - if (!silent) PrintAndLogEx(WARNING, "smart card response failed"); - return -2; - } - memcpy(data, resp.data.asBytes, len); - if (len >= 2) { - if (!silent) PrintAndLogEx(SUCCESS, "%02X%02X | %s", data[len - 2], data[len - 1], GetAPDUCodeDescription(data[len - 2], data[len - 1])); - } else { - if (!silent) PrintAndLogEx(SUCCESS, " %d | %s", len, sprint_hex_inrow_ex(data, len, 8)); - } + if (resp.status != PM3_SUCCESS) { + if (verbose) PrintAndLogEx(WARNING, "smart card response status failed"); + return -3; + } - return len; + len = resp.length; + if (len == 0) { + if (verbose) PrintAndLogEx(WARNING, "smart card response failed"); + return -2; + } + + if (len > maxoutlen) { + if (verbose) PrintAndLogEx(ERR, "Response too large. Got %u, expected %d", len, maxoutlen); + return -4; + } + + memcpy(out, resp.data.asBytes, len); + if (len >= 2) { + if (verbose) { + + + if (out[len - 2] == 0x90 && out[len - 1] == 0x00) { + PrintAndLogEx(SUCCESS, _GREEN_("%02X%02X") " | %s", out[len - 2], out[len - 1], GetAPDUCodeDescription(out[len - 2], out[len - 1])); + } else { + PrintAndLogEx(SUCCESS, "%02X%02X | %s", out[len - 2], out[len - 1], GetAPDUCodeDescription(out[len - 2], out[len - 1])); + } + } + } else { + if (verbose) { + PrintAndLogEx(SUCCESS, " %d | %s", len, sprint_hex_inrow_ex(out, len, 8)); + } + } + return len; + } + } while (i--); + + if (verbose) { + PrintAndLogEx(WARNING, "smart card response timeout"); + } + return -1; } -static int smart_responseEx(uint8_t *data, bool silent) { +static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { - int datalen = smart_wait(data, silent); + int datalen = smart_wait(out, maxoutlen, verbose); bool needGetData = false; if (datalen < 2) { goto out; } - if (data[datalen - 2] == 0x61 || data[datalen - 2] == 0x9F) { + if (out[datalen - 2] == 0x61 || out[datalen - 2] == 0x9F) { needGetData = true; } if (needGetData) { - int len = data[datalen - 1]; + int len = out[datalen - 1]; - if (!silent) PrintAndLogEx(INFO, "Requesting 0x%02X bytes response", len); + if (verbose) PrintAndLogEx(INFO, "Requesting " _YELLOW_("0x%02X") " bytes response", len); + + uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, len}; + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(cmd_getresp)); + payload->flags = SC_RAW | SC_LOG; + payload->len = sizeof(cmd_getresp); + memcpy(payload->data, cmd_getresp, sizeof(cmd_getresp)); - uint8_t getstatus[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, len}; clearCommandBuffer(); - SendCommandMIX(CMD_SMART_RAW, SC_RAW, sizeof(getstatus), 0, getstatus, sizeof(getstatus)); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + sizeof(cmd_getresp)); + free(payload); - datalen = smart_wait(data, silent); + datalen = smart_wait(out, maxoutlen, verbose); if (datalen < 2) { goto out; @@ -309,19 +343,19 @@ static int smart_responseEx(uint8_t *data, bool silent) { if (datalen != len + 2) { // data with ACK if (datalen == len + 2 + 1) { // 2 - response, 1 - ACK - if (data[0] != ISO7816_GET_RESPONSE) { - if (!silent) { - PrintAndLogEx(ERR, "GetResponse ACK error. len 0x%x | data[0] %02X", len, data[0]); + if (out[0] != ISO7816_GET_RESPONSE) { + if (verbose) { + PrintAndLogEx(ERR, "GetResponse ACK error. len 0x%x | data[0] %02X", len, out[0]); } datalen = 0; goto out; } datalen--; - memmove(data, &data[1], datalen); + memmove(out, &out[1], datalen); } else { // wrong length - if (!silent) { + if (verbose) { PrintAndLogEx(WARNING, "GetResponse wrong length. Must be 0x%02X got 0x%02X", len, datalen - 3); } } @@ -332,8 +366,8 @@ out: return datalen; } -static int smart_response(uint8_t *data) { - return smart_responseEx(data, false); +static int smart_response(uint8_t *out, int maxoutlen) { + return smart_responseEx(out, maxoutlen, true); } static int CmdSmartRaw(const char *Cmd) { @@ -358,7 +392,7 @@ static int CmdSmartRaw(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool reply = arg_get_lit(ctx, 1); + bool reply = (arg_get_lit(ctx, 1) == false); bool active = arg_get_lit(ctx, 2); bool active_select = arg_get_lit(ctx, 3); bool decode_tlv = arg_get_lit(ctx, 4); @@ -374,61 +408,78 @@ static int CmdSmartRaw(const char *Cmd) { return PM3_EINVARG; } - uint8_t flags = SC_LOG; + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + dlen); + if (payload == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + payload->len = dlen; + memcpy(payload->data, data, dlen); + + payload->flags = SC_LOG; if (active || active_select) { - flags |= (SC_CONNECT | SC_CLEARLOG); + payload->flags |= (SC_CONNECT | SC_CLEARLOG); if (active_select) - flags |= SC_SELECT; + payload->flags |= SC_SELECT; } if (dlen > 0) { if (use_t0) - flags |= SC_RAW_T0; + payload->flags |= SC_RAW_T0; else - flags |= SC_RAW; + payload->flags |= SC_RAW; + } + + uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); + if (buf == NULL) { + PrintAndLogEx(DEBUG, "failed to allocate memory"); + free(payload); + return PM3_EMALLOC; } clearCommandBuffer(); - SendCommandOLD(CMD_SMART_RAW, flags, dlen, 0, data, dlen); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + dlen); + + if (reply == false) { + goto out; + } // reading response from smart card - if (reply) { - - uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t)); - if (!buf) - return PM3_EMALLOC; - - int len = smart_response(buf); - if (len < 0) { - free(buf); - return PM3_ESOFT; - } - - if (buf[0] == 0x6C) { - data[4] = buf[1]; - - clearCommandBuffer(); - SendCommandMIX(CMD_SMART_RAW, 0, dlen, 0, data, dlen); - len = smart_response(buf); - - data[4] = 0; - } - - if (decode_tlv && len > 4) - TLVPrintFromBuffer(buf, len - 2); - else { - if (len > 16) { - for (int i = 0; i < len; i += 16) { - PrintAndLogEx(SUCCESS, "%s", sprint_hex_ascii(buf + i, 16)) ; - } - } else { - PrintAndLogEx(SUCCESS, "%s", sprint_hex_ascii(buf, len)) ; - } - } - + int len = smart_response(buf, PM3_CMD_DATA_SIZE); + if (len < 0) { + free(payload); free(buf); + return PM3_ESOFT; } + + if (buf[0] == 0x6C) { + + // request more bytes to download + data[4] = buf[1]; + memcpy(payload->data, data, dlen); + clearCommandBuffer(); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + dlen); + + len = smart_response(buf, PM3_CMD_DATA_SIZE); + + data[4] = 0; + } + + if (decode_tlv && len > 4) + TLVPrintFromBuffer(buf, len - 2); + else { + if (len > 2) { + PrintAndLogEx(INFO, "Response data:"); + PrintAndLogEx(INFO, " # | bytes | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(buf, len, 16); + } + } + PrintAndLogEx(NORMAL, ""); +out: + free(payload); + free(buf); return PM3_SUCCESS; } @@ -620,9 +671,9 @@ static int CmdSmartInfo(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SMART_ATR, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SMART_ATR, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_SMART_ATR, &resp, 2500) == false) { if (verbose) { - PrintAndLogEx(WARNING, "smart card select failed"); + PrintAndLogEx(WARNING, "smart card timeout"); } return PM3_ETIMEOUT; } @@ -639,7 +690,6 @@ static int CmdSmartInfo(const char *Cmd) { // print header PrintAndLogEx(INFO, "--- " _CYAN_("Smartcard Information") " ---------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(INFO, "ISO7618-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); PrintAndLogEx(INFO, "http://smartcard-atr.apdu.fr/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len)); @@ -672,7 +722,6 @@ static int CmdSmartInfo(const char *Cmd) { } static int CmdSmartReader(const char *Cmd) { - CLIParserContext *ctx; CLIParserInit(&ctx, "smart reader", "Act as a smart card reader.", @@ -691,7 +740,7 @@ static int CmdSmartReader(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SMART_ATR, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SMART_ATR, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_SMART_ATR, &resp, 2500) == false) { if (verbose) { PrintAndLogEx(WARNING, "smart card select failed"); } @@ -704,10 +753,8 @@ static int CmdSmartReader(const char *Cmd) { } return PM3_ESOFT; } - smart_card_atr_t card; - memcpy(&card, (smart_card_atr_t *)resp.data.asBytes, sizeof(smart_card_atr_t)); - - PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); + smart_card_atr_t *card = (smart_card_atr_t *)resp.data.asBytes; + PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card->atr, card->atr_len)); return PM3_SUCCESS; } @@ -779,13 +826,7 @@ static int CmdSmartSetClock(const char *Cmd) { } static int CmdSmartList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 7816"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "smart", "7816"); } static void smart_brute_prim(void) { @@ -805,17 +846,18 @@ static void smart_brute_prim(void) { for (int i = 0; i < ARRAYLEN(get_card_data); i += 5) { + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + 5); + payload->flags = SC_RAW_T0; + payload->len = 5; + memcpy(payload->data, get_card_data + i, 5); + clearCommandBuffer(); - SendCommandMIX(CMD_SMART_RAW, SC_RAW_T0, 5, 0, get_card_data + i, 5); - - int len = smart_responseEx(buf, true); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + 5); + free(payload); + int len = smart_responseEx(buf, PM3_CMD_DATA_SIZE, false); if (len > 2) { - // if ( decodeTLV ) { - // if (!TLVPrintFromBuffer(buf, len-2)) { PrintAndLogEx(SUCCESS, "\tHEX %d |: %s", len, sprint_hex(buf, len)); - // } - // } } } free(buf); @@ -847,21 +889,29 @@ static int smart_brute_sfi(bool decodeTLV) { READ_RECORD[2] = rec; READ_RECORD[3] = (sfi << 3) | 4; - clearCommandBuffer(); - SendCommandMIX(CMD_SMART_RAW, SC_RAW_T0, sizeof(READ_RECORD), 0, READ_RECORD, sizeof(READ_RECORD)); + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(READ_RECORD)); + payload->flags = SC_RAW_T0; + payload->len = sizeof(READ_RECORD); + memcpy(payload->data, READ_RECORD, sizeof(READ_RECORD)); - len = smart_responseEx(buf, true); + clearCommandBuffer(); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + sizeof(READ_RECORD)); + + len = smart_responseEx(buf, PM3_CMD_DATA_SIZE, false); if (buf[0] == 0x6C) { READ_RECORD[4] = buf[1]; + memcpy(payload->data, READ_RECORD, sizeof(READ_RECORD)); clearCommandBuffer(); - SendCommandMIX(CMD_SMART_RAW, SC_RAW_T0, sizeof(READ_RECORD), 0, READ_RECORD, sizeof(READ_RECORD)); - len = smart_responseEx(buf, true); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + sizeof(READ_RECORD)); + len = smart_responseEx(buf, PM3_CMD_DATA_SIZE, false); READ_RECORD[4] = 0; } + free(payload); + if (len > 4) { PrintAndLogEx(SUCCESS, "\n\t file %02d, record %02d found", sfi, rec); @@ -887,13 +937,19 @@ static void smart_brute_options(bool decodeTLV) { if (!buf) return; + // Get processing options command uint8_t GET_PROCESSING_OPTIONS[] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00}; - // Get processing options command - clearCommandBuffer(); - SendCommandMIX(CMD_SMART_RAW, SC_RAW_T0, sizeof(GET_PROCESSING_OPTIONS), 0, GET_PROCESSING_OPTIONS, sizeof(GET_PROCESSING_OPTIONS)); + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(GET_PROCESSING_OPTIONS)); + payload->flags = SC_RAW_T0; + payload->len = sizeof(GET_PROCESSING_OPTIONS); + memcpy(payload->data, GET_PROCESSING_OPTIONS, sizeof(GET_PROCESSING_OPTIONS)); - int len = smart_responseEx(buf, true); + clearCommandBuffer(); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + sizeof(GET_PROCESSING_OPTIONS)); + free(payload); + + int len = smart_responseEx(buf, PM3_CMD_DATA_SIZE, false); if (len > 4) { PrintAndLogEx(SUCCESS, "Got processing options"); if (decodeTLV) { @@ -986,10 +1042,16 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { if (res) continue; - clearCommandBuffer(); - SendCommandOLD(CMD_SMART_RAW, SC_RAW_T0, hexlen, 0, cmddata, hexlen); + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + hexlen); + payload->flags = SC_RAW_T0; + payload->len = hexlen; - int len = smart_responseEx(buf, true); + memcpy(payload->data, cmddata, hexlen); + clearCommandBuffer(); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + hexlen); + free(payload); + + int len = smart_responseEx(buf, PM3_CMD_DATA_SIZE, false); if (len < 3) continue; @@ -1036,7 +1098,7 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdSmartList, IfPm3Smartcard, "List ISO 7816 history"}, + {"list", CmdSmartList, AlwaysAvailable, "List ISO 7816 history"}, {"info", CmdSmartInfo, IfPm3Smartcard, "Tag information"}, {"reader", CmdSmartReader, IfPm3Smartcard, "Act like an IS07816 reader"}, {"raw", CmdSmartRaw, IfPm3Smartcard, "Send raw hex data to tag"}, @@ -1057,62 +1119,60 @@ int CmdSmartcard(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -int ExchangeAPDUSC(bool silent, uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { +int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { *dataoutlen = 0; - if (activateCard) - smart_select(true, NULL); - - PrintAndLogEx(DEBUG, "APDU SC"); - - uint8_t flags = SC_RAW_T0; + smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + datainlen); + payload->flags = (SC_RAW_T0 | SC_LOG); if (activateCard) { - flags |= SC_SELECT | SC_CONNECT; + payload->flags |= (SC_SELECT | SC_CONNECT); } + payload->len = datainlen; + memcpy(payload->data, datain, datainlen); clearCommandBuffer(); - SendCommandOLD(CMD_SMART_RAW, flags, datainlen, 0, datain, datainlen); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + datainlen); - int len = smart_responseEx(dataout, silent); + int len = smart_responseEx(dataout, maxdataoutlen, verbose); if (len < 0) { + free(payload); return 1; } // retry if (len > 1 && dataout[len - 2] == 0x6c && datainlen > 4) { - uint8_t data [5]; - memcpy(data, datain, 5); + payload->flags = SC_RAW_T0; + payload->len = 5; // transfer length via T=0 - data[4] = dataout[len - 1]; - + datain[4] = dataout[len - 1]; + memcpy(payload->data, datain, 5); clearCommandBuffer(); - // something fishy: we have only 5 bytes but we put datainlen in arg1? - SendCommandMIX(CMD_SMART_RAW, SC_RAW_T0, datainlen, 0, data, sizeof(data)); - - len = smart_responseEx(dataout, silent); + SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + 5); + datain[4] = 0; + len = smart_responseEx(dataout, maxdataoutlen, verbose); } + free(payload); *dataoutlen = len; return 0; } -bool smart_select(bool silent, smart_card_atr_t *atr) { +bool smart_select(bool verbose, smart_card_atr_t *atr) { if (atr) memset(atr, 0, sizeof(smart_card_atr_t)); clearCommandBuffer(); SendCommandNG(CMD_SMART_ATR, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SMART_ATR, &resp, 2500)) { - - if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); + if (WaitForResponseTimeout(CMD_SMART_ATR, &resp, 2500) == false) { + if (verbose) PrintAndLogEx(WARNING, "smart card select timeouted"); return false; } if (resp.status != PM3_SUCCESS) { - if (!silent) PrintAndLogEx(WARNING, "smart card select failed"); + if (verbose) PrintAndLogEx(WARNING, "smart card select failed"); return false; } @@ -1122,7 +1182,7 @@ bool smart_select(bool silent, smart_card_atr_t *atr) { if (atr) memcpy(atr, &card, sizeof(smart_card_atr_t)); - if (!silent) + if (verbose) PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); return true; diff --git a/client/src/cmdsmartcard.h b/client/src/cmdsmartcard.h index 798ea4678..8bd70b2bd 100644 --- a/client/src/cmdsmartcard.h +++ b/client/src/cmdsmartcard.h @@ -12,11 +12,11 @@ #define CMDSMARTCARD_H__ #include "common.h" -#include "mifare.h" // structs +#include "pm3_cmd.h" // structs int CmdSmartcard(const char *Cmd); -bool smart_select(bool silent, smart_card_atr_t *atr); -int ExchangeAPDUSC(bool silent, uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); +bool smart_select(bool verbose, smart_card_atr_t *atr); +int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCard, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); #endif diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 4369d3a71..d70b23080 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -242,7 +242,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } for (int j = 0; j < data_len && j / 18 < 18; j++) { - uint8_t parityBits = parityBytes[j >> 3]; if (protocol != LEGIC && protocol != ISO_14443B @@ -256,7 +255,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr && protocol != FELICA && protocol != LTO && protocol != PROTO_CRYPTORF - && (hdr->isResponse || protocol == ISO_14443A) + && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE) && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) { snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]); @@ -280,9 +279,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr if (markCRCBytes) { //CRC-command if (crcStatus == 0 || crcStatus == 1) { - char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4); + char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4) - 1; (*pos1) = '['; - char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4); + char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4) - 1; sprintf(pos2, "%c", ']'); } } @@ -309,9 +308,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } m--; } - line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 2] = '('; - line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 3] = m + 0x30; - line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 4] = ')'; + + if (data_len) { + line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 2] = '('; + line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 3] = m + 0x30; + line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 4] = ')'; + } } } @@ -401,32 +403,63 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr time2 = duration; } - if (use_us) { - PrintAndLogEx(NORMAL, " %10.1f | %10.1f | %s |%-72s | %s| %s", - (float)time1 / 13.56, - (float)time2 / 13.56, - (hdr->isResponse ? "Tag" : _YELLOW_("Rdr")), - line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" - ); + if (hdr->isResponse) { + // tag row + if (use_us) { + PrintAndLogEx(NORMAL, " %10.1f | %10.1f | Tag |%-72s | %s| %s", + (float)time1 / 13.56, + (float)time2 / 13.56, + 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), + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); + } } else { - PrintAndLogEx(NORMAL, " %10u | %10u | %s |%-72s | %s| %s", - (hdr->timestamp - first_hdr->timestamp), - (end_of_transmission_timestamp - first_hdr->timestamp), - (hdr->isResponse ? "Tag" : _YELLOW_("Rdr")), - line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" - ); + // reader row + if (use_us) { + PrintAndLogEx(NORMAL, + _YELLOW_(" %10.1f") " | " _YELLOW_("%10.1f") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + (float)time1 / 13.56, + (float)time2 / 13.56, + 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), + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); + } + } } else { - PrintAndLogEx(NORMAL, " | | |%-72s | %s| %s", - line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" - ); + if (hdr->isResponse) { + PrintAndLogEx(NORMAL, " | | |%-72s | %s| %s", + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); + } else { + PrintAndLogEx(NORMAL, " | | |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + line[j], + (j == num_lines - 1) ? crc : " ", + (j == num_lines - 1) ? explanation : "" + ); + } } } @@ -592,6 +625,44 @@ static int CmdTraceSave(const char *Cmd) { return PM3_SUCCESS; } +int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol) { + CLIParserContext *ctx; + char desc[500] = {0}; + snprintf(desc, sizeof(desc) - 1, + "Alias of `trace list -t %s` with selected protocol data to annotate trace buffer\n" + "You can load a trace from file (see `trace load -h`) or it be downloaded from device by default\n" + "It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific 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 ", + alias, alias); + char fullalias[100] = {0}; + snprintf(fullalias, sizeof(fullalias) - 1, "%s list", alias); + CLIParserInit(&ctx, fullalias, desc, example); + + void *argtable[] = { + arg_param_begin, + arg_lit0("1", "buffer", "use data from trace buffer"), + arg_lit0("f", NULL, "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_strx0(NULL, "dict", "", "use dictionary keys file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + char args[128] = {0}; + snprintf(args, sizeof(args), "-t %s ", protocol); + strncat(args, Cmd, sizeof(args) - strlen(args) - 1); + return CmdTraceList(args); +} + int CmdTraceList(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "trace list", @@ -679,8 +750,11 @@ int CmdTraceList(const char *Cmd) { else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF; else if (strcmp(type, "raw") == 0) protocol = -1; - if (use_buffer == false || (g_traceLen == 0)) { + if (use_buffer == false) { download_trace(); + } else if (g_traceLen == 0) { + PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace, consider using 'trace load' or removing parameter '1'"); + return PM3_EINVARG; } PrintAndLogEx(SUCCESS, "Recorded activity (trace len = " _YELLOW_("%lu") " bytes)", g_traceLen); diff --git a/client/src/cmdtrace.h b/client/src/cmdtrace.h index acef66884..7444fe62e 100644 --- a/client/src/cmdtrace.h +++ b/client/src/cmdtrace.h @@ -15,5 +15,6 @@ int CmdTrace(const char *Cmd); int CmdTraceList(const char *Cmd); +int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol); #endif diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index 3cdeb66b3..c0c32abd8 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -114,28 +114,47 @@ int CmdWiegandDecode(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("p", "parity", "ignore invalid parity"), - arg_strx1("r", "raw", "", "raw hex to be decoded"), + arg_strx0("r", "raw", "", "raw hex to be decoded"), + arg_str0("b", "bin", "", "binary string to be decoded"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); bool ignore_parity = arg_get_lit(ctx, 1); - int len = 0; + int hlen = 0; char hex[40] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &len); + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &hlen); + + int blen = 0; + uint8_t binarr[100] = {0x00}; + int res = CLIParamBinToBuf(arg_get_str(ctx, 3), binarr, sizeof(binarr), &blen); CLIParserFree(ctx); - if (len == 0) { - PrintAndLogEx(ERR, "empty input"); + if (res) { + PrintAndLogEx(FAILED, "Error parsing binary string"); return PM3_EINVARG; } uint32_t top = 0, mid = 0, bot = 0; - hexstring_to_u96(&top, &mid, &bot, hex); + if (hlen) { + res = hexstring_to_u96(&top, &mid, &bot, hex); + if (res != hlen) { + PrintAndLogEx(ERR, "hex string contains none hex chars"); + return PM3_EINVARG; + } + } else if (blen) { + uint16_t n = binarray_to_u96(&top, &mid, &bot, binarr, blen); + if (n != blen) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "empty input"); + return PM3_EINVARG; + } wiegand_message_t packed = initialize_message_object(top, mid, bot); HIDTryUnpack(&packed, ignore_parity); - return PM3_SUCCESS; } diff --git a/client/src/comms.c b/client/src/comms.c index 57a03afe7..4e624aa86 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -839,6 +839,9 @@ 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) + return true; // sample_buf is a array pointer, located in data.c // arg0 = offset in transfer. Startindex of this chunk diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 3e0fec872..989acaf6c 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -52,15 +52,17 @@ static void ParamLoadDefaults(struct tlvdb *tlvRoot) { //95:(Terminal Verification Results) len:5 // all OK TVR TLV_ADD(0x95, "\x00\x00\x00\x00\x00"); + // 9F4E Merchant Name and Location len:x + TLV_ADD(0x9F4E, "proxmrk3rdv\x00"); } static void PrintChannel(EMVCommandChannel channel) { switch (channel) { case ECC_CONTACTLESS: - PrintAndLogEx(INFO, "Selected channel : " _GREEN_("CONTACTLESS (T=CL)")); + PrintAndLogEx(INFO, "Selected channel... " _GREEN_("CONTACTLESS (T=CL)")); break; case ECC_CONTACT: - PrintAndLogEx(INFO, "Selected channel : " _GREEN_("CONTACT")); + PrintAndLogEx(INFO, "Selected channel... " _GREEN_("CONTACT")); break; } } @@ -72,7 +74,8 @@ static int CmdEMVSelect(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv select", "Executes select applet command", - "Usage:\n\temv select -s a00000000101 -> select card, select applet\n\temv select -st a00000000101 -> select card, select applet, show result in TLV\n"); + "emv select -s a00000000101 -> select card, select applet\n" + "emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); void *argtable[] = { arg_param_begin, @@ -121,8 +124,9 @@ static int CmdEMVSearch(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv search", - "Tries to select all applets from applet list:\n", - "Usage:\n\temv search -s -> select card and search\n\temv search -st -> select card, search and show result in TLV\n"); + "Tries to select all applets from applet list\n", + "emv search -s -> select card and search\n" + "emv search -st -> select card, search and show result in TLV\n"); void *argtable[] = { arg_param_begin, @@ -172,7 +176,8 @@ static int CmdEMVPPSE(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv pse", "Executes PSE/PPSE select command. It returns list of applet on the card:\n", - "Usage:\n\temv pse -s1 -> select, get pse\n\temv pse -st2 -> select, get ppse, show result in TLV\n"); + "emv pse -s1 -> select, get pse\n" + "emv pse -st2 -> select, get ppse, show result in TLV\n"); void *argtable[] = { arg_param_begin, @@ -228,10 +233,11 @@ static int CmdEMVGPO(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv gpo", - "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2) or plain format (0x80 - format1).\nNeeds a EMV applet to be selected.", - "Usage:\n\temv gpo -k -> execute GPO\n" - "\temv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" - "\temv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); + "Executes Get Processing Options command. It returns data in TLV format (0x77 - format2)\n" + "or plain format (0x80 - format1). Needs a EMV applet to be selected.", + "emv gpo -k -> execute GPO\n" + "emv gpo -t 01020304 -> execute GPO with 4-byte PDOL data, show result in TLV\n" + "emv gpo -pmt 9F 37 04 -> load params from file, make PDOL data from PDOL, execute GPO with PDOL, show result in TLV\n"); void *argtable[] = { arg_param_begin, @@ -337,8 +343,10 @@ static int CmdEMVReadRecord(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv readrec", - "Executes Read Record command. It returns data in TLV format.\nNeeds a bank applet to be selected and sometimes needs GPO to be executed.", - "Usage:\n\temv readrec -k 0101 -> read file SFI=01, SFIrec=01\n\temv readrec -kt 0201-> read file 0201 and show result in TLV\n"); + "Executes Read Record command. It returns data in TLV format.\n" + "Needs a bank applet to be selected and sometimes needs GPO to be executed.", + "emv readrec -k 0101 -> read file SFI=01, SFIrec=01\n" + "emv readrec -kt 0201 -> read file 0201 and show result in TLV\n"); void *argtable[] = { arg_param_begin, @@ -393,11 +401,12 @@ static int CmdEMVAC(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv genac", - "Generate Application Cryptogram command. It returns data in TLV format .\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\temv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" - "\temv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" - "\temv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" - "\temv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); + "Generate Application Cryptogram command. It returns data in TLV format.\n" + "Needs a EMV applet to be selected and GPO to be executed.", + "emv genac -k 0102 -> generate AC with 2-byte CDOLdata and keep field ON after command\n" + "emv genac -t 01020304 -> generate AC with 4-byte CDOL data, show result in TLV\n" + "emv genac -Daac 01020304 -> generate AC with 4-byte CDOL data and terminal decision 'declined'\n" + "emv genac -pmt 9F 37 04 -> load params from file, make CDOL data from CDOL, generate AC with CDOL, show result in TLV"); void *argtable[] = { arg_param_begin, @@ -516,8 +525,10 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv challenge", - "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\nNeeds a EMV applet to be selected and GPO to be executed.", - "Usage:\n\temv challenge -> get challenge\n\temv challenge -k -> get challenge, keep fileld ON\n"); + "Executes Generate Challenge command. It returns 4 or 8-byte random number from card.\n" + "Needs a EMV applet to be selected and GPO to be executed.", + "emv challenge -> get challenge\n" + "emv challenge -k -> get challenge, keep fileld ON\n"); void *argtable[] = { arg_param_begin, @@ -567,10 +578,9 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format .\n" "Needs a EMV applet to be selected and GPO to be executed.", - "Usage:\n" - "\temv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" - "\temv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" - "\temv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); + "emv intauth -k 01020304 -> execute Internal Authenticate with 4-byte DDOLdata and keep field ON after command\n" + "emv intauth -t 01020304 -> execute Internal Authenticate with 4-byte DDOL data, show result in TLV\n" + "emv intauth -pmt 9F 37 04 -> load params from file, make DDOL data from DDOL, Internal Authenticate with DDOL, show result in TLV"); void *argtable[] = { arg_param_begin, @@ -796,9 +806,8 @@ static int CmdEMVExec(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv exec", "Executes EMV contactless transaction", - "Usage:\n" - "\temv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" - "\temv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + "emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); void *argtable[] = { arg_param_begin, @@ -1075,12 +1084,29 @@ static int CmdEMVExec(const char *Cmd) { if (IAD) { PrintAndLogEx(NORMAL, "IAD: %s", sprint_hex(IAD->value, IAD->len)); - if (IAD->len >= IAD->value[0] + 1) { - PrintAndLogEx(NORMAL, "\tKey index: 0x%02x", IAD->value[1]); - PrintAndLogEx(NORMAL, "\tCrypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); - PrintAndLogEx(NORMAL, "\tCVR: %s", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + // https://mst-company.ru/blog/ekvajring-emv-tranzaktsiya-emv-transaction-flow-chast-4-pdol-i-beskontaktnye-karty-osobennosti-qvsdc-i-quics + if (IAD->value[0] == 0x1f) { + PrintAndLogEx(NORMAL, " Key index: 0x%02x", IAD->value[2]); + PrintAndLogEx(NORMAL, " Crypto ver: 0x%02x(%03d)", IAD->value[1], IAD->value[1]); + PrintAndLogEx(NORMAL, " CVR: %s", sprint_hex(&IAD->value[3], 5)); + struct tlvdb *cvr = tlvdb_fixed(0x20, 5, &IAD->value[3]); + TLVPrintFromTLVLev(cvr, 1); + PrintAndLogEx(NORMAL, " IDD option id: 0x%02x", IAD->value[8]); + PrintAndLogEx(NORMAL, " IDD: %s", sprint_hex(&IAD->value[9], 23)); + } else if (IAD->len >= IAD->value[0] + 1) { + PrintAndLogEx(NORMAL, " Key index: 0x%02x", IAD->value[1]); + PrintAndLogEx(NORMAL, " Crypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLogEx(NORMAL, " CVR: %s", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); struct tlvdb *cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); TLVPrintFromTLVLev(cvr, 1); + if (IAD->len >= 8) { + int iddLen = IAD->value[7]; + PrintAndLogEx(NORMAL, " IDD length: %d", iddLen); + if (iddLen >= 1) + PrintAndLogEx(NORMAL, " IDD option id: 0x%02x", IAD->value[8]); + if (iddLen >= 2) + PrintAndLogEx(NORMAL, " IDD: %s", sprint_hex(&IAD->value[9], iddLen - 1)); + } } } else { PrintAndLogEx(WARNING, "WARNING: IAD not found."); @@ -1141,9 +1167,13 @@ static int CmdEMVExec(const char *Cmd) { // CDA PrintAndLogEx(NORMAL, "\n* CDA:"); struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); - res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); - if (res) { - PrintAndLogEx(NORMAL, "CDA error (%d)", res); + if (tlvdb_get(ac_tlv, 0x9f4b, NULL)) { + res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); + if (res) { + PrintAndLogEx(NORMAL, "CDA error (%d)", res); + } + } else { + PrintAndLogEx(NORMAL, "\n* Signed Dynamic Application Data (0x9f4b) not present"); } free(ac_tlv); @@ -1387,10 +1417,11 @@ static int CmdEMVScan(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv scan", - "Scan EMV card and save it contents to a file.", - "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n" - "Usage:\n\temv scan -at -> scan MSD transaction mode and show APDU and TLV\n" - "\temv scan -c -> scan CDA transaction mode\n"); + "Scan EMV card and save it contents to a file.\n" + "It executes EMV contactless transaction and saves result to a file which can be used for emulation\n", + "emv scan -at -> scan MSD transaction mode and show APDU and TLV\n" + "emv scan -c -> scan CDA transaction mode\n" + ); void *argtable[] = { arg_param_begin, @@ -1772,25 +1803,21 @@ static int CmdEMVScan(const char *Cmd) { } static int CmdEMVList(const char *Cmd) { - char args[128] = {0}; - if (strlen(Cmd) == 0) { - snprintf(args, sizeof(args), "-t 7816"); - } else { - strncpy(args, Cmd, sizeof(args) - 1); - } - return CmdTraceList(args); + return CmdTraceListAlias(Cmd, "emv", "7816"); } static int CmdEMVTest(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv test", "Executes tests\n", - "Usage:\n\temv test [l]\n"); + "emv test -i\n" + "emv test --long" + ); void *argtable[] = { arg_param_begin, - arg_lit0("iI", "ignore", "ignore timing tests for VM"), - arg_lit0("lL", "long", "run long tests too"), + arg_lit0("i", "ignore", "ignore timing tests for VM"), + arg_lit0("l", "long", "run long tests too"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1815,9 +1842,8 @@ static int CmdEMVRoca(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv roca", "Tries to extract public keys and run the ROCA test against them.\n", - "Usage:\n" - "\temv roca -w -> select --CONTACT-- card and run test\n" - "\temv roca -> select --CONTACTLESS-- card and run test\n" + "emv roca -w -> select --CONTACT-- card and run test\n" + "emv roca -> select --CONTACTLESS-- card and run test\n" ); void *argtable[] = { diff --git a/client/src/emv/emv_pki.c b/client/src/emv/emv_pki.c index f06a5ea69..456e2960a 100644 --- a/client/src/emv/emv_pki.c +++ b/client/src/emv/emv_pki.c @@ -349,7 +349,7 @@ unsigned char *emv_pki_sdatl_fill(const struct tlvdb *db, size_t *sdatl_len) { } struct tlvdb *emv_pki_recover_dac_ex(const struct emv_pk *enc_pk, const struct tlvdb *db, const struct tlv *sda_tlv, bool showData) { - size_t data_len; + size_t data_len = 0; // Static Data Authentication Tag List size_t sdatl_len; diff --git a/client/src/emv/emv_tags.c b/client/src/emv/emv_tags.c index f86b88826..acafa18d3 100644 --- a/client/src/emv/emv_tags.c +++ b/client/src/emv/emv_tags.c @@ -261,6 +261,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f16, "Merchant Identifier", EMV_TAG_STRING, NULL }, { 0x9f17, "Personal Identification Number (PIN) Try Counter", EMV_TAG_GENERIC, NULL }, { 0x9f18, "Issuer Script Identifier", EMV_TAG_GENERIC, NULL }, + { 0x9f19, "Token Requestor ID", EMV_TAG_GENERIC, NULL }, { 0x9f1a, "Terminal Country Code", EMV_TAG_GENERIC, NULL }, { 0x9f1b, "Terminal Floor Limit", EMV_TAG_GENERIC, NULL }, { 0x9f1c, "Terminal Identification", EMV_TAG_STRING, NULL }, @@ -271,6 +272,8 @@ static const struct emv_tag emv_tags[] = { { 0x9f21, "Transaction Time", EMV_TAG_GENERIC, NULL }, { 0x9f22, "Certification Authority Public Key Index - Terminal", EMV_TAG_GENERIC, NULL }, { 0x9f23, "Upper Consecutive Offline Limit", EMV_TAG_GENERIC, NULL }, + { 0x9f24, "Payment Account Reference (PAR)", EMV_TAG_GENERIC, NULL }, + { 0x9f25, "Last 4 Digits of PAN", EMV_TAG_GENERIC, NULL }, { 0x9f26, "Application Cryptogram", EMV_TAG_GENERIC, NULL }, { 0x9f27, "Cryptogram Information Data", EMV_TAG_CID, NULL }, { 0x9f2a, "Kernel Identifier", EMV_TAG_GENERIC, NULL }, @@ -339,7 +342,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f66, "PUNATC (Track2) / Terminal Transaction Qualifiers (TTQ)", EMV_TAG_BITMASK, &EMV_TTQ }, { 0x9f67, "NATC (Track2) / MSD Offset", EMV_TAG_GENERIC, NULL }, { 0x9f68, "Cardholder verification method list (PayPass)", EMV_TAG_GENERIC, NULL }, - { 0x9f69, "Card Authentication Related Data (UDOL)", EMV_TAG_GENERIC, NULL }, + { 0x9f69, "Card Authentication Related Data (UDOL)", EMV_TAG_DOL, NULL }, { 0x9f6a, "Unpredictable Number", EMV_TAG_NUMERIC, NULL }, { 0x9f6b, "Track 2 Data", EMV_TAG_GENERIC, NULL }, { 0x9f6c, "Card Transaction Qualifiers (CTQ)", EMV_TAG_BITMASK, &EMV_CTQ }, @@ -359,6 +362,7 @@ static const struct emv_tag emv_tags[] = { { 0x9f79, "Unprotected Data Envelope 5", EMV_TAG_GENERIC, NULL }, { 0x9f7c, "Merchant Custom Data / Customer Exclusive Data (CED)", EMV_TAG_GENERIC, NULL }, { 0x9f7d, "DS Summary 1", EMV_TAG_GENERIC, NULL }, + { 0x9f7e, "Application Life Cycle Data", EMV_TAG_GENERIC, NULL }, { 0x9f7f, "DS Unpredictable Number", EMV_TAG_GENERIC, NULL }, { 0xa5, "File Control Information (FCI) Proprietary Template", EMV_TAG_GENERIC, NULL }, @@ -392,7 +396,7 @@ static const struct emv_tag emv_tags[] = { { 0xdf8117, "Card Data Input Capability", EMV_TAG_GENERIC, NULL }, { 0xdf8118, "CVM Capability - CVM Required", EMV_TAG_GENERIC, NULL }, { 0xdf8119, "CVM Capability - No CVM Required", EMV_TAG_GENERIC, NULL }, - { 0xdf811a, "Default UDOL", EMV_TAG_GENERIC, NULL }, + { 0xdf811a, "Default UDOL", EMV_TAG_DOL, NULL }, { 0xdf811b, "Kernel Configuration", EMV_TAG_GENERIC, NULL }, { 0xdf811c, "Max Lifetime of Torn Transaction Log Record", EMV_TAG_GENERIC, NULL }, { 0xdf811d, "Max Number of Torn Transaction Log Records", EMV_TAG_GENERIC, NULL }, @@ -553,7 +557,7 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, i return; } - if (tlv->len != tlv->value[0] + 1) { + if (tlv->len != 5 && tlv->len != tlv->value[0] + 1) { PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " INVALID length!"); return; @@ -577,6 +581,14 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, i PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " PIN try: %x", tlv->value[2] >> 4); } + if (tlv->len >= 3 && (tlv->value[2] & 0x40)) { + PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); + PrintAndLogEx(NORMAL, " PIN try exceeded"); + } + if (tlv->len >= 4 && (tlv->value[3] >> 4)) { + PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); + PrintAndLogEx(NORMAL, " Issuer script counter: %x", tlv->value[3] >> 4); + } if (tlv->len >= 4 && (tlv->value[3] & 0x0F)) { PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " Issuer discretionary bits: %x", tlv->value[3] & 0x0F); @@ -585,6 +597,10 @@ static void emv_tag_dump_cvr(const struct tlv *tlv, const struct emv_tag *tag, i PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); PrintAndLogEx(NORMAL, " Successfully processed issuer script commands: %x", tlv->value[4] >> 4); } + if (tlv->len >= 5 && (tlv->value[4] & 0x02)) { + PrintAndLogEx(INFO, "%*s" NOLF, (level * 4), " "); + PrintAndLogEx(NORMAL, " CDCVM OK"); + } // mask 0F 0F F0 0F uint8_t data[20] = {0}; diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index f9cbe4f82..6e3767546 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -307,7 +307,7 @@ static int EMVExchangeEx(EMVCommandChannel channel, bool ActivateField, bool Lea case ECC_CONTACT: res = 1; if (IfPm3Smartcard()) - res = ExchangeAPDUSC(true, data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + res = ExchangeAPDUSC(false, data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); if (res) { return res; @@ -647,7 +647,12 @@ int EMVInternalAuthenticate(EMVCommandChannel channel, bool LeaveFieldON, uint8_ } int MSCComputeCryptoChecksum(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *UDOL, uint8_t UDOLlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { - return EMVExchange(channel, LeaveFieldON, (sAPDU) {0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, Result, MaxResultLen, ResultLen, sw, tlv); + int res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU) {0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, true, Result, MaxResultLen, ResultLen, sw, tlv); + if (*sw == 0x6700 || *sw == 0x6f00) { + PrintAndLogEx(INFO, ">>> trying to reissue command without Le..."); + res = EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU) {0x80, 0x2a, 0x8e, 0x80, UDOLlen, UDOL}, false, Result, MaxResultLen, ResultLen, sw, tlv); + } + return res; } // Authentication @@ -965,17 +970,21 @@ int trCDA(struct tlvdb *tlv, struct tlvdb *ac_tlv, struct tlv *pdol_data_tlv, st sprint_hex(icc_pk->serial, 3) ); - 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); - PrintAndLogEx(SUCCESS, "SSAD verified (%s) (%02hhx:%02hhx)", _GREEN_("ok"), dac_tlv->value[0], dac_tlv->value[1]); - tlvdb_add(tlv, dac_db); - } else { - PrintAndLogEx(ERR, "Error: SSAD verify error"); - emv_pk_free(pk); - emv_pk_free(issuer_pk); - emv_pk_free(icc_pk); - return 4; + // Signed Static Application Data (SSAD) check + const struct tlv *ssad_tlv = tlvdb_get(tlv, 0x93, NULL); + if (ssad_tlv && ssad_tlv->len > 1) { + 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); + PrintAndLogEx(SUCCESS, "Signed Static Application Data (SSAD) verified (%s) (%02hhx:%02hhx)", _GREEN_("ok"), dac_tlv->value[0], dac_tlv->value[1]); + tlvdb_add(tlv, dac_db); + } else { + PrintAndLogEx(ERR, "Error: Signed Static Application Data (SSAD) verify error"); + emv_pk_free(pk); + emv_pk_free(issuer_pk); + emv_pk_free(icc_pk); + return 4; + } } PrintAndLogEx(INFO, "* * Check Signed Dynamic Application Data (SDAD)"); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index dc7ac530a..45185ea71 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -466,13 +466,13 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, case jsfIclass: { JsonSaveStr(root, "FileType", "iclass"); - picopass_hdr *hdr = (picopass_hdr *)data; + picopass_hdr_t *hdr = (picopass_hdr_t *)data; JsonSaveBufAsHexCompact(root, "$.Card.CSN", hdr->csn, sizeof(hdr->csn)); JsonSaveBufAsHexCompact(root, "$.Card.Configuration", (uint8_t *)&hdr->conf, sizeof(hdr->conf)); uint8_t pagemap = get_pagemap(hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { - picopass_ns_hdr *ns_hdr = (picopass_ns_hdr *)data; + picopass_ns_hdr_t *ns_hdr = (picopass_ns_hdr_t *)data; JsonSaveBufAsHexCompact(root, "$.Card.AIA", ns_hdr->app_issuer_area, sizeof(ns_hdr->app_issuer_area)); } else { JsonSaveBufAsHexCompact(root, "$.Card.Epurse", hdr->epurse, sizeof(hdr->epurse)); @@ -1210,6 +1210,10 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz *datalen = sptr; } + if (!strcmp(ctype, "15693")) { + JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); + } + out: if (callback != NULL) { @@ -1805,7 +1809,7 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con int res = searchFinalFile(foundpath, pm3dir, filename, silent); if (res != PM3_SUCCESS) { if ((res == PM3_EFILE) && (!silent)) - PrintAndLogEx(FAILED, "Error - can't find %s", filename); + PrintAndLogEx(FAILED, "Error - can't find `" _YELLOW_("%s") "`", filename); free(filename); return res; } diff --git a/client/src/loclass/hash1_brute.c b/client/src/loclass/hash1_brute.c index 49f2cd3de..2f2842b2f 100644 --- a/client/src/loclass/hash1_brute.c +++ b/client/src/loclass/hash1_brute.c @@ -29,7 +29,8 @@ static void calc_score(uint8_t *csn, uint8_t *k) { // goodvals[k[i]] = 1; // } - else if (k[i] >= 16) { +// else if (k[i] >= 16) { + else { badscore++; badval = k[i]; } diff --git a/client/src/mifare/desfire_crypto.c b/client/src/mifare/desfire_crypto.c index d8fb62aaa..d13563344 100644 --- a/client/src/mifare/desfire_crypto.c +++ b/client/src/mifare/desfire_crypto.c @@ -652,8 +652,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes free(edata); break; - case MDCM_ENCIPHERED: - (*nbytes)--; + case MDCM_ENCIPHERED: { bool verified = false; int crc_pos = 0x00; int end_crc_pos = 0x00; @@ -702,11 +701,13 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes if (res != NULL) { memcpy(res, data, *nbytes); - crc_pos = (*nbytes) - 16 - 3; - if (crc_pos < 0) { - /* Single block */ - crc_pos = 0; + size_t padding_start_pos = *nbytes - 1; + while (padding_start_pos > 0 && ((uint8_t *) res)[padding_start_pos] == 0x00) { + padding_start_pos--; } + //TODO: Add support for cases where there is no padding. Uncommon but possible. + crc_pos = padding_start_pos - 4; + memcpy((uint8_t *) res + crc_pos + 1, (uint8_t *) res + crc_pos, *nbytes - crc_pos); ((uint8_t *) res)[crc_pos] = 0x00; crc_pos++; @@ -747,6 +748,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes ((uint8_t *)data)[(*nbytes)++] = 0x00; break; case AS_NEW: + *nbytes = crc_pos - 1; /* The status byte was already before the CRC */ break; } @@ -775,6 +777,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes } break; + } default: PrintAndLogEx(ERR, "Unknown communication settings"); *nbytes = -1; @@ -862,7 +865,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, mbedtls_aes_context actx; mbedtls_aes_init(&actx); mbedtls_aes_setkey_enc(&actx, key->data, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(edata), ivect, data, edata); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, data, edata); mbedtls_aes_free(&actx); break; } @@ -870,7 +873,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, mbedtls_aes_context actx; mbedtls_aes_init(&actx); mbedtls_aes_setkey_dec(&actx, key->data, 128); - mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(edata), ivect, edata, data); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, data, edata); mbedtls_aes_free(&actx); break; } @@ -899,12 +902,10 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, * function with tag, key and ivect defined. */ void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation) { - size_t block_size; - if (tag) { - if (!key) + if (key == NULL) key = DESFIRE(tag)->session_key; - if (!ivect) + if (ivect == NULL) ivect = DESFIRE(tag)->ivect; switch (DESFIRE(tag)->authentication_scheme) { @@ -916,8 +917,7 @@ void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *i } } - block_size = key_block_size(key); - + size_t block_size = key_block_size(key); size_t offset = 0; while (offset < data_size) { mifare_cypher_single_block(key, data + offset, ivect, direction, operation, block_size); diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index 55961b15b..dcbc9ec6b 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -45,25 +45,25 @@ const char *mfpGetErrorDescription(uint8_t errorCode) { } AccessConditions_t MFAccessConditions[] = { - {0x00, "rdAB wrAB incAB dectrAB"}, - {0x01, "rdAB dectrAB"}, - {0x02, "rdAB"}, - {0x03, "rdB wrB"}, - {0x04, "rdAB wrB"}, - {0x05, "rdB"}, - {0x06, "rdAB wrB incB dectrAB"}, + {0x00, "read AB; write AB; increment AB; decrement transfer restore AB"}, + {0x01, "read AB; decrement transfer restore AB"}, + {0x02, "read AB"}, + {0x03, "read B; write B"}, + {0x04, "read AB; writeB"}, + {0x05, "read B"}, + {0x06, "read AB; write B; increment B; decrement transfer restore AB"}, {0x07, "none"} }; AccessConditions_t MFAccessConditionsTrailer[] = { - {0x00, "rdAbyA rdCbyA rdBbyA wrBbyA"}, - {0x01, "wrAbyA rdCbyA wrCbyA rdBbyA wrBbyA"}, - {0x02, "rdCbyA rdBbyA"}, - {0x03, "wrAbyB rdCbyAB wrCbyB wrBbyB"}, - {0x04, "wrAbyB rdCbyAB wrBbyB"}, - {0x05, "rdCbyAB wrCbyB"}, - {0x06, "rdCbyAB"}, - {0x07, "rdCbyAB"} + {0x00, "read A by A; read ACCESS by A; read B by A; write B by A"}, + {0x01, "write A by A; read ACCESS by A write ACCESS by A; read B by A; write B by A"}, + {0x02, "read ACCESS by A; read B by A"}, + {0x03, "write A by B; read ACCESS by AB; write ACCESS by B; write B by B"}, + {0x04, "write A by B; read ACCESS by AB; write B by B"}, + {0x05, "read ACCESS by AB; write ACCESS by B"}, + {0x06, "read ACCESS by AB"}, + {0x07, "read ACCESS by AB"} }; const char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data) { @@ -179,7 +179,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen, silentMode); if (res) { - if (!silentMode) PrintAndLogEx(ERR, "Exchande raw error: %d", res); + if (!silentMode) PrintAndLogEx(ERR, "Exchange raw error: %d", res); if (dropFieldIfError) DropField(); return 2; } @@ -194,7 +194,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti } if (data[0] != 0x90) { - if (!silentMode) PrintAndLogEx(ERR, "Card response error: %02x", data[2]); + if (!silentMode) PrintAndLogEx(ERR, "Card response error: %02x %s", data[0], mfpGetErrorDescription(data[0])); if (dropFieldIfError) DropField(); return 3; } @@ -223,7 +223,7 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode); if (res) { - if (!silentMode) PrintAndLogEx(ERR, "Exchande raw error: %d", res); + if (!silentMode) PrintAndLogEx(ERR, "Exchange raw error: %d", res); if (dropFieldIfError) DropField(); return 4; } diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index c6e02013e..d068aaac0 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -695,7 +695,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl mem[4] = (chunk & 0xFF); // upload to flash. - res = flashmem_spiffs_load(destfn, mem, 5 + (chunk * 6)); + res = flashmem_spiffs_load((char *)destfn, mem, 5 + (chunk * 6)); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "\nSPIFFS upload failed"); free(mem); @@ -769,7 +769,7 @@ int mfReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t *data) { mf_readblock_t payload = { .blockno = blockNo, .keytype = keyType - }; + }; memcpy(payload.key, key, sizeof(payload.key)); clearCommandBuffer(); @@ -853,37 +853,69 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt } // "MAGIC" CARD -int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard) { +int mfCSetUID(uint8_t *uid, uint8_t uidlen, uint8_t *atqa, uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard) { uint8_t params = MAGIC_SINGLE; uint8_t block0[16]; memset(block0, 0x00, sizeof(block0)); - int old = mfCGetBlock(0, block0, params); - if (old == 0) - PrintAndLogEx(SUCCESS, "old block 0: %s", sprint_hex(block0, sizeof(block0))); - else - PrintAndLogEx(FAILED, "couldn't get old data. Will write over the last bytes of Block 0."); + int res = mfCGetBlock(0, block0, params); + if (res == 0) { + PrintAndLogEx(SUCCESS, "old block 0... %s", sprint_hex_inrow(block0, sizeof(block0))); + if (old_uid) { + memcpy(old_uid, block0, uidlen); + } + } else { + PrintAndLogEx(INFO, "couldn't get old data. Will write over the last bytes of block 0"); + } // fill in the new values // UID - memcpy(block0, uid, 4); + memcpy(block0, uid, uidlen); // Mifare UID BCC - block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; - // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) - if (sak != NULL) - block0[5] = sak[0]; + if (uidlen == 4) { + block0[4] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3]; - if (atqa != NULL) { - block0[6] = atqa[1]; - block0[7] = atqa[0]; + // mifare classic SAK(byte 5) and ATQA(byte 6 and 7, reversed) + if (sak) + block0[5] = sak[0]; + + if (atqa) { + block0[6] = atqa[1]; + block0[7] = atqa[0]; + } + + } else if (uidlen == 7) { + block0[7] = block0[0] ^ block0[1] ^ block0[2] ^ block0[3] ^ block0[4] ^ block0[5] ^ block0[6]; + + // mifare classic SAK(byte 8) and ATQA(byte 9 and 10, reversed) + if (sak) + block0[8] = sak[0]; + + if (atqa) { + block0[9] = atqa[1]; + block0[10] = atqa[0]; + } } - PrintAndLogEx(SUCCESS, "new block 0: %s", sprint_hex(block0, 16)); - if (wipecard) params |= MAGIC_WIPE; - if (oldUID != NULL) params |= MAGIC_UID; + PrintAndLogEx(SUCCESS, "new block 0... %s", sprint_hex_inrow(block0, 16)); - return mfCSetBlock(0, block0, oldUID, params); + if (wipecard) { + params |= MAGIC_WIPE; + } + + res = mfCSetBlock(0, block0, NULL, params); + if (res == PM3_SUCCESS) { + params = MAGIC_SINGLE; + memset(block0, 0, sizeof(block0)); + res = mfCGetBlock(0, block0, params); + if (res == 0) { + if (verifed_uid) { + memcpy(verifed_uid, block0, uidlen); + } + } + } + return res; } int mfCWipe(uint8_t *uid, uint8_t *atqa, uint8_t *sak) { @@ -1028,13 +1060,14 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i } int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) { - PrintAndLogEx(SUCCESS, "\nencrypted data: [%s]", sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "encrypted data... %s", sprint_hex(data, len)); struct Crypto1State *s; uint32_t ks2 = ar_enc ^ prng_successor(nt, 64); uint32_t ks3 = at_enc ^ prng_successor(nt, 96); s = lfsr_recovery64(ks2, ks3); mf_crypto1_decrypt(s, data, len, false); - PrintAndLogEx(SUCCESS, "decrypted data: [%s]", sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "decrypted data... " _YELLOW_("%s"), sprint_hex(data, len)); + PrintAndLogEx(NORMAL, ""); crypto1_destroy(s); return PM3_SUCCESS; } @@ -1224,13 +1257,13 @@ int detect_mfc_ev1_signature(uint8_t *signature) { return PM3_EINVARG; } uint8_t sign[32] = {0}; - uint8_t key[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; + uint8_t key[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; int res = mfReadBlock(69, 1, key, sign); - if ( res == PM3_SUCCESS) { + if (res == PM3_SUCCESS) { res = mfReadBlock(70, 1, key, sign + 16); if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } } return res; -} \ No newline at end of file +} diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index c4c65737a..49adc8cbe 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -76,7 +76,7 @@ int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount); int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); -int mfCSetUID(uint8_t *uid, uint8_t *atqa, uint8_t *sak, uint8_t *oldUID, uint8_t wipecard); +int mfCSetUID(uint8_t *uid, uint8_t uidlen, uint8_t *atqa, uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard); int mfCWipe(uint8_t *uid, uint8_t *atqa, uint8_t *sak); int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params); int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params); diff --git a/client/src/preferences.c b/client/src/preferences.c index d961d2c6f..3228498f4 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -437,17 +437,19 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { strcpy(s, _RED_("unknown")" save path......"); } - if ((session.defaultPaths[path_index] == NULL) || (strcmp(session.defaultPaths[path_index], "") == 0)) { - PrintAndLogEx(INFO, " %s %s "_WHITE_("not set"), - prefShowMsg(opt), - s - ); - } else { - PrintAndLogEx(INFO, " %s %s "_GREEN_("%s"), - prefShowMsg(opt), - s, - session.defaultPaths[path_index] - ); + if (path_index < spItemCount) { + if ((session.defaultPaths[path_index] == NULL) || (strcmp(session.defaultPaths[path_index], "") == 0)) { + PrintAndLogEx(INFO, " %s %s "_WHITE_("not set"), + prefShowMsg(opt), + s + ); + } else { + PrintAndLogEx(INFO, " %s %s "_GREEN_("%s"), + prefShowMsg(opt), + s, + session.defaultPaths[path_index] + ); + } } } @@ -502,9 +504,9 @@ static void showBarModeState(prefShowOpt_t opt) { static int setCmdEmoji(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set emoji ", + CLIParserInit(&ctx, "prefs set emoji ", "Set presistent preference of using emojis in the client", - "pref set emoji --alias" + "prefs set emoji --alias" ); void *argtable[] = { @@ -556,9 +558,9 @@ static int setCmdEmoji(const char *Cmd) { static int setCmdColor(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set color ", + CLIParserInit(&ctx, "prefs set color ", "Set presistent preference of using colors in the client", - "pref set color --ansi" + "prefs set color --ansi" ); void *argtable[] = { @@ -600,9 +602,9 @@ static int setCmdColor(const char *Cmd) { static int setCmdDebug(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set clientdebug ", + CLIParserInit(&ctx, "prefs set clientdebug ", "Set presistent preference of using clientside debug level", - "pref set clientdebug --simple" + "prefs set clientdebug --simple" ); void *argtable[] = { @@ -651,9 +653,9 @@ static int setCmdDebug(const char *Cmd) { static int setCmdDeviceDebug (const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set devicedebug ", + CLIParserInit(&ctx, "prefs set devicedebug ", "Set presistent preference of device side debug level", - "pref set devicedebug --on" + "prefs set devicedebug --on" ); void *argtable[] = { @@ -718,9 +720,9 @@ static int setCmdDeviceDebug (const char *Cmd) static int setCmdHint(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set hints ", + CLIParserInit(&ctx, "prefs set hints ", "Set presistent preference of showing hint messages in the client", - "pref set hints --on" + "prefs set hints --on" ); void *argtable[] = { @@ -761,9 +763,9 @@ static int setCmdHint(const char *Cmd) { static int setCmdPlotSliders(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set plotsliders ", + CLIParserInit(&ctx, "prefs set plotsliders", "Set presistent preference of showing the plotslider control in the client", - "pref set plotsliders --on" + "prefs set plotsliders --on" ); void *argtable[] = { @@ -803,10 +805,10 @@ static int setCmdPlotSliders(const char *Cmd) { static int setCmdSavePaths(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set savepath", + CLIParserInit(&ctx, "prefs set savepaths", "Set presistent preference of file paths in the client", - "pref set savepaths --dump /home/mydumpfolder -> all dump files will be saved into this folder\n" - "pref set savepaths --def /home/myfolder -c -> create if needed, all files will be saved into this folder" + "prefs set savepaths --dump /home/mydumpfolder -> all dump files will be saved into this folder\n" + "prefs set savepaths --def /home/myfolder -c -> create if needed, all files will be saved into this folder" ); void *argtable[] = { @@ -884,9 +886,9 @@ static int setCmdSavePaths(const char *Cmd) { static int setCmdBarMode(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set barmode", + CLIParserInit(&ctx, "prefs set barmode", "Set presistent preference of HF/LF tune command styled output in the client", - "pref set barmode --mix" + "prefs set barmode --mix" ); void *argtable[] = { @@ -930,36 +932,113 @@ static int setCmdBarMode(const char *Cmd) { } static int getCmdEmoji(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get emoji", + "Get preference of using emojis in the client", + "prefs get emoji" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showEmojiState(prefShowNone); return PM3_SUCCESS; } static int getCmdHint(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get hints", + "Get preference of showing hint messages in the client", + "prefs get hints" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showHintsState(prefShowNone); return PM3_SUCCESS; } static int getCmdColor(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get color", + "Get preference of using colors in the client", + "prefs get color" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showColorState(prefShowNone); return PM3_SUCCESS; } static int getCmdDebug(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get clientdebug", + "Get preference of using clientside debug level", + "prefs get clientdebug" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showClientDebugState(prefShowNone); return PM3_SUCCESS; } static int getCmdPlotSlider(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get plotsliders", + "Get preference of showing the plotslider control in the client", + "prefs get plotsliders" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showPlotSliderState(prefShowNone); return PM3_SUCCESS; } static int getCmdBarMode(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get barmode", + "Get preference of HF/LF tune command styled output in the client", + "prefs get barmode" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showBarModeState(prefShowNone); return PM3_SUCCESS; } static int getCmdSavePaths(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get savepaths", + "Get preference of file paths in the client", + "prefs get savepaths" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showSavePathState(spDefault, prefShowNone); showSavePathState(spDump, prefShowNone); showSavePathState(spTrace, prefShowNone); @@ -1008,6 +1087,17 @@ static int CmdPrefSet(const char *Cmd) { } static int CmdPrefShow(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs show", + "Show all persistent preferences", + "prefs show" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); if (session.preferences_loaded) { char *fn = prefGetFilename(); @@ -1043,8 +1133,8 @@ static int CmdPrefSave (const char *Cmd) { */ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"get", CmdPrefGet, AlwaysAvailable, "Get a preference"}, - {"set", CmdPrefSet, AlwaysAvailable, "Set a preference"}, + {"get", CmdPrefGet, AlwaysAvailable, "{ Get a preference }"}, + {"set", CmdPrefSet, AlwaysAvailable, "{ Set a preference }"}, {"show", CmdPrefShow, AlwaysAvailable, "Show all preferences"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 3d92b8d00..b0d5c5188 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -455,15 +455,16 @@ check_script: } #ifndef LIBPM3 -static void dumpAllHelp(int markdown) { +static void dumpAllHelp(int markdown, bool full_help) { session.help_dump_mode = true; PrintAndLogEx(NORMAL, "\n%sProxmark3 command dump%s\n\n", markdown ? "# " : "", markdown ? "" : "\n======================"); PrintAndLogEx(NORMAL, "Some commands are available only if a Proxmark3 is actually connected.%s\n", markdown ? " " : ""); PrintAndLogEx(NORMAL, "Check column \"offline\" for their availability.\n"); PrintAndLogEx(NORMAL, "\n"); command_t *cmds = getTopLevelCommandTable(); - dumpCommandsRecursive(cmds, markdown); + dumpCommandsRecursive(cmds, markdown, full_help); session.help_dump_mode = false; + PrintAndLogEx(NORMAL, "Full help dump done."); } #endif //LIBPM3 @@ -518,10 +519,13 @@ static void set_my_user_directory(void) { if (my_user_directory == NULL) { uint16_t pathLen = FILENAME_MAX; // should be a good starting point - bool error = false; char *cwd_buffer = (char *)calloc(pathLen, sizeof(uint8_t)); + if (cwd_buffer == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); + return; + } - while (!error && (GetCurrentDir(cwd_buffer, pathLen) == NULL)) { + while (GetCurrentDir(cwd_buffer, pathLen) == NULL) { if (errno == ERANGE) { // Need bigger buffer pathLen += 10; // if buffer was too small add 10 characters and try again char *tmp = realloc(cwd_buffer, pathLen); @@ -537,23 +541,20 @@ static void set_my_user_directory(void) { } } - if (!error) { - - for (int i = 0; i < strlen(cwd_buffer); i++) { - if (cwd_buffer[i] == '\\') { - cwd_buffer[i] = '/'; - } + for (int i = 0; i < strlen(cwd_buffer); i++) { + if (cwd_buffer[i] == '\\') { + cwd_buffer[i] = '/'; } - - my_user_directory = cwd_buffer; } + + my_user_directory = cwd_buffer; } } #ifndef LIBPM3 static void show_help(bool showFullHelp, char *exec_name) { - PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m]", exec_name); + PrintAndLogEx(NORMAL, "\nsyntax: %s [-h|-t|-m|--fulltext]", exec_name); PrintAndLogEx(NORMAL, " %s [[-p] ] [-b] [-w] [-f] [-c ]|[-l ]|[-s ] [-i] [-d <0|1|2>]", exec_name); PrintAndLogEx(NORMAL, " %s [-p] --flash [--unlock-bootloader] [--image ]+ [-w] [-f] [-d <0|1|2>]", exec_name); @@ -567,8 +568,9 @@ static void show_help(bool showFullHelp, char *exec_name) { PrintAndLogEx(NORMAL, " -f/--flush output will be flushed after every print"); PrintAndLogEx(NORMAL, " -d/--debug <0|1|2> set debugmode"); PrintAndLogEx(NORMAL, "\nOptions in client mode:"); - PrintAndLogEx(NORMAL, " -t/--text dump all interactive command's help at once"); - PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive help at once in markdown syntax"); + PrintAndLogEx(NORMAL, " -t/--text dump all interactive command list at once"); + PrintAndLogEx(NORMAL, " --fulltext dump all interactive command's help at once"); + PrintAndLogEx(NORMAL, " -m/--markdown dump all interactive command list at once in markdown syntax"); PrintAndLogEx(NORMAL, " -b/--baud serial port speed (only needed for physical UART, not for USB-CDC or BT)"); PrintAndLogEx(NORMAL, " -c/--command execute one Proxmark3 command (or several separated by ';')."); PrintAndLogEx(NORMAL, " -l/--lua execute lua script."); @@ -823,14 +825,22 @@ int main(int argc, char *argv[]) { if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--text") == 0) { g_printAndLog = PRINTANDLOG_PRINT; show_help(false, exec_name); - dumpAllHelp(0); + dumpAllHelp(0, false); + return 0; + } + + // dump help + if (strcmp(argv[i], "--fulltext") == 0) { + g_printAndLog = PRINTANDLOG_PRINT; + show_help(false, exec_name); + dumpAllHelp(0, true); return 0; } // dump markup if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--markdown") == 0) { g_printAndLog = PRINTANDLOG_PRINT; - dumpAllHelp(1); + dumpAllHelp(1, false); return 0; } // print client version diff --git a/client/src/scripting.c b/client/src/scripting.c index 6ac012199..e64276767 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -1279,10 +1279,12 @@ static int l_ewd(lua_State *L) { static int l_cwd(lua_State *L) { uint16_t path_len = FILENAME_MAX; // should be a good starting point - bool error = false; char *cwd = (char *)calloc(path_len, sizeof(uint8_t)); + if (cwd == NULL) { + return returnToLuaWithError(L, "Failed to allocate memory"); + } - while (!error && (GetCurrentDir(cwd, path_len) == NULL)) { + while (GetCurrentDir(cwd, path_len) == NULL) { if (errno == ERANGE) { // Need bigger buffer path_len += 10; // if buffer was too small add 10 characters and try again cwd = realloc(cwd, path_len); diff --git a/client/src/ui.c b/client/src/ui.c index 3c4069f5b..a044d7312 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -182,7 +182,7 @@ static uint8_t PrintAndLogEx_spinidx = 0; void PrintAndLogEx(logLevel_t level, const char *fmt, ...) { - // skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG 0' + // skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0' if (g_debugMode == 0 && level == DEBUG) return; diff --git a/client/src/util.c b/client/src/util.c index 53825f28b..c18917f08 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -183,45 +183,54 @@ void print_hex(const uint8_t *data, const size_t len) { } void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { - if (data == NULL || len == 0) return; + if (data == NULL || len == 0 || breaks == 0) return; - int rownum = 0; - PrintAndLogEx(NORMAL, "[%02d] | " NOLF, rownum); - for (size_t i = 0; i < len; ++i) { - - PrintAndLogEx(NORMAL, "%02X " NOLF, data[i]); - - // check if a line break is needed - if (breaks > 0 && !((i + 1) % breaks) && (i + 1 < len)) { - ++rownum; - PrintAndLogEx(NORMAL, "\n[%02d] | " NOLF, rownum); + uint16_t rownum = 0; + int i; + for (i = 0; i < len; i += breaks, rownum++) { + if (len - i < breaks) { // incomplete block, will be treated out of the loop + break; } + PrintAndLogEx(INFO, "%02u | %s", rownum, sprint_hex_ascii(data + i, breaks)); + } + + // the last odd bytes + uint8_t mod = len % breaks; + + if (mod) { + char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; + memset(buf, 0, sizeof(buf)); + hex_to_buffer((uint8_t *)buf, data + i, mod, (sizeof(buf) - 1), 0, 1, true); + + // add the spaces... + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " "); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod)); + PrintAndLogEx(INFO, "%02u | %s", rownum, buf); } - PrintAndLogEx(NORMAL, ""); } -void print_buffer(const uint8_t *data, const size_t len, int level) { +static void print_buffer_ex(const uint8_t *data, const size_t len, int level, uint8_t breaks) { if (len < 1) return; char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; int i; - for (i = 0; i < len; i += 16) { - if (len - i < 16) { // incomplete block, will be treated out of the loop + for (i = 0; i < len; i += breaks) { + if (len - i < breaks) { // incomplete block, will be treated out of the loop break; } // (16 * 3) + (16) + + 1 memset(buf, 0, sizeof(buf)); sprintf(buf, "%*s%02x: ", (level * 4), " ", i); - hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, 16, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, 16)); + 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); } // the last odd bytes - uint8_t mod = len % 16; + uint8_t mod = len % breaks; if (mod) { memset(buf, 0, sizeof(buf)); @@ -229,13 +238,17 @@ void print_buffer(const uint8_t *data, const size_t len, int level) { hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, mod, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); // add the spaces... - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((16 - mod) * 3), " "); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " "); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod)); PrintAndLogEx(INFO, "%s", buf); } } +void print_buffer(const uint8_t *data, const size_t len, int level) { + print_buffer_ex(data, len, level, 16); +} + void print_blocks(uint32_t *data, size_t len) { PrintAndLogEx(SUCCESS, "Blk | Data "); PrintAndLogEx(SUCCESS, "----+------------"); @@ -272,36 +285,33 @@ char *sprint_hex_inrow_spaces(const uint8_t *data, const size_t len, size_t spac char *sprint_bin_break(const uint8_t *data, const size_t len, const uint8_t breaks) { // make sure we don't go beyond our char array memory - size_t in_index = 0, out_index = 0; - size_t rowlen = (len > MAX_BIN_BREAK_LENGTH) ? MAX_BIN_BREAK_LENGTH : len; - if (breaks > 0 && len % breaks != 0) - rowlen = (len + (len / breaks) > MAX_BIN_BREAK_LENGTH) ? MAX_BIN_BREAK_LENGTH : len + (len / breaks); - - //PrintAndLogEx(NORMAL, "(sprint_bin_break) rowlen %d", rowlen); - - static char buf[MAX_BIN_BREAK_LENGTH]; // 3072 + end of line characters if broken at 8 bits - //clear memory + // 3072 + end of line characters if broken at 8 bits + static char buf[MAX_BIN_BREAK_LENGTH]; memset(buf, 0x00, sizeof(buf)); char *tmp = buf; // loop through the out_index to make sure we don't go too far - for (out_index = 0; out_index < rowlen; out_index++) { - // set character - if (data[in_index] == 7) // Manchester wrong bit marker - sprintf(tmp++, "."); + for (int i = 0; i < rowlen; i++) { + + char c = data[i]; + // manchester wrong bit marker + if (c == 7) + c = '.'; else - sprintf(tmp++, "%u", data[in_index]); + c += '0'; + + *(tmp++) = c; // check if a line break is needed and we have room to print it in our array - if ((breaks > 0) && !((in_index + 1) % breaks) && (out_index + 1 != rowlen)) { - sprintf(tmp++, "%s", "\n"); + if (breaks) { + if (((i + 1) % breaks) == 0) { + + *(tmp++) = '\n'; + } } - - in_index++; } - return buf; } /* @@ -701,6 +711,47 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda return 0; } +int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen) { + int bg, en; + if (param_getptr(line, &bg, &en, paramnum)) { + return 1; + } + + *datalen = 0; + char buf[5] = {0}; + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; + continue; + } + + if (line[indx] == '0' || line[indx] == '1') { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and 0/1 + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) > 0) { + uint32_t temp = 0; + sscanf(buf, "%d", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + return 0; +} + int param_getstr(const char *line, int paramnum, char *str, size_t buffersize) { int bg, en; @@ -755,9 +806,8 @@ int hextobinarray(char *target, char *source) { // convert hex to human readable binary string int hextobinstring(char *target, char *source) { - int length; - - if (!(length = hextobinarray(target, source))) + int length = hextobinarray(target, source); + if (length == 0) return 0; binarraytobinstring(target, target, length); return length; @@ -976,7 +1026,7 @@ int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) { uint32_t n = 0, i = 0; - for(;;) { + for (;;) { int res = sscanf(&str[i], "%1u", &n); if ((res != 1) || (n > 1)) @@ -991,6 +1041,27 @@ int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str) return i; } + +/** + * Converts a binary array to component "hi2", "hi" and "lo" 32-bit integers, + * one bit at a time. + * + * Returns the number of bits entered. + */ +int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t *arr, int arrlen) { + int i = 0; + for (; i < arrlen; i++) { + uint8_t n = arr[i]; + if (n > 1) + break; + + *hi2 = (*hi2 << 1) | (*hi >> 31); + *hi = (*hi << 1) | (*lo >> 31); + *lo = (*lo << 1) | (n & 0x1); + } + return i; +} + inline uint32_t bitcount32(uint32_t a) { #if defined __GNUC__ return __builtin_popcountl(a); diff --git a/client/src/util.h b/client/src/util.h index 7577c216e..2730ed44f 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -77,6 +77,7 @@ uint8_t param_isdec(const char *line, int paramnum); int param_gethex(const char *line, int paramnum, uint8_t *data, int hexcnt); 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 param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen); int param_getstr(const char *line, int paramnum, char *str, size_t buffersize); int hextobinarray(char *target, char *source); @@ -106,6 +107,7 @@ char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); +int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t *arr, int arrlen); uint32_t bitcount32(uint32_t a); uint64_t bitcount64(uint64_t a); diff --git a/common/cardhelper.c b/common/cardhelper.c index ce57f6051..db62c49f6 100644 --- a/common/cardhelper.c +++ b/common/cardhelper.c @@ -17,19 +17,25 @@ #define CARD_INS_DECRYPT 0x01 #define CARD_INS_ENCRYPT 0x02 +#define CARD_INS_VEIRFY_RRG 0x05 #define CARD_INS_DECODE 0x06 #define CARD_INS_NUMBLOCKS 0x07 #define CARD_INS_PINSIZE 0x08 -static uint8_t cmd[] = {0x96, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +#define CARD_INS_CC 0x81 +#define CARD_INS_CC_DESC 0x82 -// look for CryptoHelper -bool IsCryptoHelperPresent(bool verbose) { +// look for CardHelper +bool IsCardHelperPresent(bool verbose) { if (IfPm3Smartcard()) { int resp_len = 0; uint8_t version[] = {0x96, 0x69, 0x00, 0x00, 0x00}; - uint8_t resp[20] = {0}; - ExchangeAPDUSC(true, version, sizeof(version), true, true, resp, sizeof(resp), &resp_len); + uint8_t resp[30] = {0}; + ExchangeAPDUSC(verbose, version, sizeof(version), true, true, resp, sizeof(resp), &resp_len); + + if (resp_len < 8) { + return false; + } if (strstr("CryptoHelper", (char *)resp) == 0) { if (verbose) { @@ -42,14 +48,12 @@ bool IsCryptoHelperPresent(bool verbose) { } static bool executeCrypto(uint8_t ins, uint8_t *src, uint8_t *dest) { - int resp_len = 0; - uint8_t dec[11] = {0}; - - cmd[1] = ins; + uint8_t cmd[] = {0x96, ins, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(cmd + 5, src, 8); - ExchangeAPDUSC(true, cmd, sizeof(cmd), false, true, dec, sizeof(dec), &resp_len); - + int resp_len = 0; + uint8_t dec[11] = {0}; + ExchangeAPDUSC(false, cmd, sizeof(cmd), false, true, dec, sizeof(dec), &resp_len); if (resp_len == 10) { memcpy(dest, dec, 8); return true; @@ -74,12 +78,23 @@ void DecodeBlock6(uint8_t *src) { memcpy(c + 6, src, 8); // first part - ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + ExchangeAPDUSC(false, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + + + if (resp_len < 11) { + return; + } + PrintAndLogEx(SUCCESS, "%.*s", resp_len - 11, resp + 9); // second part c[5] = 0x02; - ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + ExchangeAPDUSC(false, c, sizeof(c), false, false, resp, sizeof(resp), &resp_len); + + + if (resp_len < 11) { + return; + } PrintAndLogEx(SUCCESS, "%.*s", resp_len - 11, resp + 9); } @@ -89,7 +104,13 @@ uint8_t GetNumberBlocksForUserId(uint8_t *src) { uint8_t resp[254] = {0}; uint8_t c[] = {0x96, CARD_INS_NUMBLOCKS, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(c + 5, src, 8); - ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + ExchangeAPDUSC(false, c, sizeof(c), false, false, resp, sizeof(resp), &resp_len); + + + if (resp_len < 8) { + return 0; + } + return resp[8]; } @@ -99,10 +120,75 @@ uint8_t GetPinSize(uint8_t *src) { uint8_t resp[254] = {0}; uint8_t c[] = {0x96, CARD_INS_PINSIZE, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(c + 5, src, 8); - ExchangeAPDUSC(true, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + ExchangeAPDUSC(false, c, sizeof(c), false, false, resp, sizeof(resp), &resp_len); + if (resp_len < 2) { + return 0; + } if (resp[resp_len - 2] == 0x90 && resp[resp_len - 1] == 0x00) { return resp[8]; } return 0; } + +int GetConfigCardByIdx(uint8_t typ, uint8_t *blocks) { + if (blocks == NULL) + return PM3_EINVARG; + + int resp_len = 0; + uint8_t resp[254] = {0}; + uint8_t c[] = {0x96, CARD_INS_CC, 0x00, 0x00, 17, typ, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + ExchangeAPDUSC(false, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + + if (resp_len < 2) { + return PM3_ESOFT; + } + + if (resp[resp_len - 2] == 0x90 && resp[resp_len - 1] == 0x00) { + memcpy(blocks, resp + 1, 16); + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + +int GetConfigCardStrByIdx(uint8_t typ, uint8_t *out) { + if (out == NULL) + return PM3_EINVARG; + + int resp_len = 0; + uint8_t resp[254] = {0}; + uint8_t c[] = {0x96, CARD_INS_CC_DESC, 0x00, 0x00, 1, typ}; + ExchangeAPDUSC(false, c, sizeof(c), false, true, resp, sizeof(resp), &resp_len); + + if (resp_len < 2) { + return PM3_ESOFT; + } + + if (resp[resp_len - 2] == 0x90 && resp[resp_len - 1] == 0x00) { + memcpy(out, resp + 1, resp_len - 2 - 1); + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + +int VerifyRdv4Signature(uint8_t *memid, uint8_t *signature) { + if (memid == NULL || signature == NULL) + return PM3_EINVARG; + + int resp_len = 0; + uint8_t resp[254] = {0}; + uint8_t c[5 + 8 + 128] = {0x96, CARD_INS_VEIRFY_RRG, 0x00, 0x00, 8 + 128}; + + memcpy(c + 5, memid, 8); + memcpy(c + 5 + 8, signature, 128); + + ExchangeAPDUSC(false, c, sizeof(c), true, false, resp, sizeof(resp), &resp_len); + if (resp_len < 2) { + return PM3_ESOFT; + } + + if (memcmp(resp + resp_len - 4, "\x6f\x6b\x90\x00", 4) == 0) { + return PM3_SUCCESS; + } + return PM3_ESOFT; +} diff --git a/common/cardhelper.h b/common/cardhelper.h index d55ae6701..1ef5b646e 100644 --- a/common/cardhelper.h +++ b/common/cardhelper.h @@ -14,10 +14,14 @@ #include #include "common.h" -bool IsCryptoHelperPresent(bool verbose); +bool IsCardHelperPresent(bool verbose); bool Encrypt(uint8_t *src, uint8_t *dest); bool Decrypt(uint8_t *src, uint8_t *dest); void DecodeBlock6(uint8_t *src); uint8_t GetNumberBlocksForUserId(uint8_t *src); uint8_t GetPinSize(uint8_t *src); + +int GetConfigCardByIdx(uint8_t typ, uint8_t *blocks); +int GetConfigCardStrByIdx(uint8_t typ, uint8_t *out); +int VerifyRdv4Signature(uint8_t *memid, uint8_t *signature); #endif diff --git a/common/lfdemod.c b/common/lfdemod.c index 221dba8d6..7972e8788 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -1720,8 +1720,13 @@ int askdemod_ext(uint8_t *bits, size_t *size, int *clk, int *invert, int maxErr, } else if (bits[i] <= low) { bits[bitnum++] = *invert ^ 1; } else if (i - lastBit >= *clk / 2 + tol) { - bits[bitnum] = bits[bitnum - 1]; - bitnum++; + if (bitnum > 0) { + bits[bitnum] = bits[bitnum - 1]; + bitnum++; + } else { + bits[bitnum] = 0; + bitnum++; + } } else { //in tolerance - looking for peak continue; } diff --git a/common/parity.h b/common/parity.h index dad768524..3b829bdb4 100644 --- a/common/parity.h +++ b/common/parity.h @@ -29,7 +29,7 @@ static inline uint8_t evenparity32(uint32_t x) { x ^= x >> 8; return evenparity8(x); #else - return __builtin_parity(x); + return (__builtin_parity(x) & 0xFF); #endif } diff --git a/doc/T5577_Guide.md b/doc/T5577_Guide.md index 4bd8f30c6..61d926b22 100644 --- a/doc/T5577_Guide.md +++ b/doc/T5577_Guide.md @@ -24,17 +24,17 @@ ## Introduction -The T5577 is a generic LF (Low Frequency) RFID card the is used in the +The T5577 is a generic LF (Low Frequency) RFID card that is used in the 125 Khz frequency space. It is a good card to use to learn about RFID and learn how to use the proxmark3. -It is highly recommend that when learning about RFID that learning how +It is highly recommended that when learning about RFID that learning how to read the data sheets be near the top of the list. It can be very hard as the data sheet will hold the information you need, but you don’t yet know what it means. As such, I will attempt to point to sections of the data sheet and would highly advise that you look at the data sheet as you go. Overtime the data sheet may change, as a result things may not -always be reference correctly. +always be referenced correctly. As at writing this guide, the data sheet can be found at : @@ -64,9 +64,9 @@ the chip how to behave. ## What data is on my T5577 -Let’s have a look and see what a card might look in the proxmark3 +Let’s have a look and see what a card might look like in the proxmark3 software. Since we can change the configuration of how the T5577 will -output data, the proxmark3 software need to work out how to interpreted +output data, the proxmark3 software needs to work out how to interpret the data it receives, we do this with the following command. It should be noted that the T5577 has many clones. As such the default @@ -78,7 +78,7 @@ examples shown, it will be assumed you have run the detect command. ``` [usb] pm3 --> lf t55xx detect ``` -You should see a results simular to the following: +You should see a results similar to the following: ``` [=] Chip type......... T55x7 [=] Modulation........ ASK @@ -102,20 +102,20 @@ Your results should look similar to the following: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... -[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 01 | 00000000 | 00000000000000000000000000000000 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... +[+] 04 | 00000000 | 00000000000000000000000000000000 | .... +[+] 05 | 00000000 | 00000000000000000000000000000000 | .... +[+] 06 | 00000000 | 00000000000000000000000000000000 | .... +[+] 07 | 00000000 | 00000000000000000000000000000000 | .... [+] Reading Page 1: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... -[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H -[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 01 | 00000000 | 00000000000000000000000000000000 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... ``` I will cover the meaning of this data as we go, but for now, lets keep it simple. @@ -126,7 +126,7 @@ The basic function of using the proxmark3 with rfid cards is to read and write data. This reading and writing must be done in the correct way needed for the chip (and its configuration). Lucky for us, the developers have done a great job and gave us commands. What we need to -know is that with the T5577 data is read/written one complete block at a +know is that with the T5577, data is read/written one complete block at a time. Each block holds 32 bits of data (hence the binary output shown) Since we know that the card has data and configuration blocks, lets say @@ -150,14 +150,14 @@ can see the card) [+] Reading Page 0: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- - [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 01 | 00000000 | 00000000000000000000000000000000 | .... ``` - Note: Depending on the history of your card your data may vary, but + Note: Depending on the history of your card, your data may vary, but should match the dump data. 2) Write some new data into block 1 on the card. - We use the d option to supply the data ‘12345678’ + We use the -d option to supply the data ‘12345678’ ``` [usb] pm3 --> lf t55xx write -b 1 -d 12345678 ``` @@ -177,8 +177,8 @@ can see the card) [+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx ``` 4) The data is written in Hexadecimal. A single hex digit holds 4 bits - of data. So to store 32 bits in a block we need to supply 8 hex - digits (8 \* 4 = 32). If you are familiar with hex and binary do a + of data. So to store 32 bits in a block, we need to supply 8 hex + digits (8 \* 4 = 32). If you are not familiar with hex and binary do a little bit of home work to learn. The following is a quick start. | Hex | Binary | Decimal | @@ -238,31 +238,34 @@ result: [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... -[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... +[+] 04 | 00000000 | 00000000000000000000000000000000 | .... +[+] 05 | 00000000 | 00000000000000000000000000000000 | .... +[+] 06 | 00000000 | 00000000000000000000000000000000 | .... +[+] 07 | 00000000 | 00000000000000000000000000000000 | .... [+] Reading Page 1: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... -[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H -[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 01 | 00000000 | 00000000000000000000000000000000 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... ``` -Practice reading and writing to blocks 1 to 7 until you are happy you +Practice reading and writing to blocks 1 to 6 until you are happy you can do it and get the results you wanted (i.e. the data you want stored -is written to the block you want it stored in). +is written to the block you want it stored in). I recommend staying +away from block 7 as this is where the password is stored, if used. +If you forget this data/password, you wont be able to read or write +to the card. ## How do I use a password This can be a little tricky for beginners. ***If you forget your password you will lose access to your card***. -To tell the T5577 to use a password we have to change the data in the +To tell the T5577 to use a password, we have to change the data in the configuration block (0). To help learn this and make it as simple as I can, please read and follow exactly. If your results DON’T match 100% as required, please do not proceed. @@ -331,20 +334,20 @@ required, please do not proceed. [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... - [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 01 | 00000000 | 00000000000000000000000000000000 | .... + [+] 02 | 00000000 | 00000000000000000000000000000000 | .... + [+] 03 | 00000000 | 00000000000000000000000000000000 | .... + [+] 04 | 00000000 | 00000000000000000000000000000000 | .... + [+] 05 | 00000000 | 00000000000000000000000000000000 | .... + [+] 06 | 00000000 | 00000000000000000000000000000000 | .... [+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx [+] Reading Page 1: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... - [+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H - [+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. - [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 01 | 00000000 | 00000000000000000000000000000000 | .... + [+] 02 | 00000000 | 00000000000000000000000000000000 | .... + [+] 03 | 00000000 | 00000000000000000000000000000000 | .... ``` ***Important : If block 0 and block 7 don’t match exactly, do not continue.*** @@ -358,7 +361,7 @@ required, please do not proceed. ![](./t55xx_block0.png) We will cover other things in the configuration later. But the key - note here is we ONLY want to change bit 28 and nothing else. + note here, is we ONLY want to change bit 28 and nothing else. Current Block 0 : ***00088040*** New Block 0 : ***00088050*** @@ -366,7 +369,7 @@ required, please do not proceed. To understand what happened to get from 00088040 to 00088050 we need to look at the binary data. - While this can be confusing it is important to understand this as we + While this can be confusing, it is important to understand this as we do more advanced things. Bit Location (28) @@ -380,7 +383,7 @@ required, please do not proceed. - See how in the above we change the bit in location 28 from a 0 to 1 + See how in the above we changed the bit in location 28 from a 0 to 1 0 = No Password, 1 = Use Password Note how we did NOT change any other part of the configuration, only bit 28. @@ -415,22 +418,22 @@ required, please do not proceed. card. Lets try again, but this time supply the password. We use the option - p followed by the password. + -p followed by the password. ``` [usb] pm3 --> lf t55 detect -p 12345678 ``` result: ``` - [=] Chip type......... T55x7 - [=] Modulation........ ASK - [=] Bit rate.......... 2 - RF/32 - [=] Inverted.......... No - [=] Offset............ 33 - [=] Seq. terminator... Yes - [=] Block0............ 00088050 (auto detect) + [=] Chip type......... T55x7 + [=] Modulation........ ASK + [=] Bit rate.......... 2 - RF/32 + [=] Inverted.......... No + [=] Offset............ 33 + [=] Seq. terminator... Yes + [=] Block0............ 00088050 (auto detect) [=] Downlink mode..... default/fixed bit length [=] Password set...... Yes - [=] Password.......... 00000000 + [=] Password.......... 12345678 ``` 7) Write a block of data with a password @@ -460,13 +463,12 @@ required, please do not proceed. [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [!] Safety check: Could not detect if PWD bit is set in config block. Exits. - [?] Consider using the override parameter to force read. ``` - Note that the proxmark3 did not read the block, the safty kicked in - and wants us to confirm by supply the override option ‘o’. + Note that the proxmark3 did not read the block, the safety kicked in + and wants us to confirm by supply the override option ‘-o’. - Lets try again with the ‘o’ option as we know the password is set. + Lets try again with the ‘-o’ option as we know the password is set. ``` [usb] pm3 --> lf t55xx read -b 1 -p 12345678 -o ``` @@ -487,7 +489,7 @@ required, please do not proceed. this from above. Remember if we don’t know the config and write this config to the - card, it will over write all other settings. This can recoved the + card, it will overwrite all other settings. This can recover the card, but will lose any settings you may want. So it’s a good idea to read the config, and set bit 28 to 0, rather than just overwrite the config and change the way the card works. @@ -516,7 +518,7 @@ required, please do not proceed. [=] Downlink mode..... default/fixed bit length [=] Password set...... No ``` - Yes we can and we can see Block 0 is the correct config 00088040 + Yes we can! We can see Block 0 is the correct config 00088040 # Part 2 – Configuration Blocks @@ -532,7 +534,7 @@ from Block 0 in Page 0. It will use this in both default read mode (where is sends out the blocks from 1 to x on power up), as well as when it responds to commands. -In the Read To Card, the T5577 will encode the data using the settings +In the Reader To Card, the T5577 will encode the data using the settings from Block 3 Page 1. If the command is not encoded correctly it will ignore the command and revert back to default read mode. @@ -542,10 +544,10 @@ For this configuration the settings chosen will be for the purpose of the card when used in production. E.G. If you want the card to act like an EM4100, then we need to choose the settings that work like the EM4100; same goes for others like HID. I am not going to cover these -here, rather use an example. Others have collect these and posted on the -forum. +here, rather use an example. Others have collected these and posted on the +forum or can be found by searching the web. -To get started lets look back at the tech sheet. +To get started lets look back at the data sheet. ![](./t55xx_clock0_cfg.png) @@ -584,12 +586,11 @@ password set (if not, review and get you card back to this state). ``` result: ``` - [+] Preparing to clone EM4102 to T55x7 tag with ID 0F0368568B (RF/64) - [#] Clock rate: 64 - [#] Tag T55x7 written with 0xff83c03322a646e4 - - [+] Done - [?] Hint: try `lf em 410x reader` to verify + [+] Preparing to clone EM4102 to T55x7 tag with ID 1122334455 (RF/64) + [#] Clock rate: 64 + [#] Tag T55x7 written with 0xff8c65298c94a940 + + [+] Done ``` 2) Check this has work. @@ -632,7 +633,7 @@ password set (if not, review and get you card back to this state). ``` Looks good. -3) Now lest see what the T5577 detect and info shows +3) Now lets see what the T5577 detect and info shows ``` [usb] pm3 --> lf t55 detect ``` diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 1358c8f3d..ae41a4745 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -173,11 +173,11 @@ Options -t, --type Simulation type to use --csn Specify CSN as 8 bytes (16 hex symbols) to use with sim type 0 Types: -0 simulate the given CSN -1 simulate default CSN -2 Runs online part of LOCLASS attack -3 Full simulation using emulator memory (see 'hf iclass eload') -4 Runs online part of LOCLASS attack against reader in keyroll mode + 0 simulate the given CSN + 1 simulate default CSN + 2 runs online part of LOCLASS attack + 3 full simulation using emulator memory (see 'hf iclass eload') + 4 runs online part of LOCLASS attack against reader in keyroll mode pm3 --> hf iclass sim -t 3 ``` @@ -205,7 +205,7 @@ pm3 --> hf iclass dump --ki 7 --elite Verify custom iCLASS key ``` -Options +options --- -f, --file Dictionary file with default iclass keys --csn Specify CSN as 8 bytes (16 hex symbols) @@ -222,20 +222,20 @@ pm3 --> hf iclass lookup --csn 010a0ffff7ff12e0 --epurse feffffffffffffff --macs Check for default keys ``` -Options +options --- - -k, --key Key specified as 12 hex symbols - --blk Input block number - -a Target Key A, if found also check Key B for duplicate - -b Target Key B - -*, --all Target both key A & B (default) - --mini MIFARE Classic Mini / S20 - --1k MIFARE Classic 1k / S50 (default) - --2k MIFARE Classic/Plus 2k - --4k MIFARE Classic 4k / S70 - --emu Fill simulator keys from found keys - --dump Dump found keys to binary file - -f, --file filename of dictionary +-k, --key Key specified as 12 hex symbols + --blk Input block number +-a Target Key A, if found also check Key B for duplicate +-b Target Key B +-*, --all Target both key A & B (default) + --mini MIFARE Classic Mini / S20 + --1k MIFARE Classic 1k / S50 (default) + --2k MIFARE Classic/Plus 2k + --4k MIFARE Classic 4k / S70 + --emu Fill simulator keys from found keys + --dump Dump found keys to binary file +-f, --file filename of dictionary pm3 --> hf mf chk --1k -f mfc_default_keys ``` @@ -244,85 +244,107 @@ Check for default keys from local memory ``` Options --- - -k, --key Key specified as 12 hex symbols - --mini MIFARE Classic Mini / S20 - --1k MIFARE Classic 1k / S50 (default) - --2k MIFARE Classic/Plus 2k - --4k MIFARE Classic 4k / S70 - --emu Fill simulator keys from found keys - --dump Dump found keys to binary file - --mem Use dictionary from flashmemory - -f, --file filename of dictionary +-k, --key Key specified as 12 hex symbols + --mini MIFARE Classic Mini / S20 + --1k MIFARE Classic 1k / S50 (default) + --2k MIFARE Classic/Plus 2k + --4k MIFARE Classic 4k / S70 + --emu Fill simulator keys from found keys + --dump Dump found keys to binary file + --mem Use dictionary from flashmemory +-f, --file filename of dictionary pm3 --> hf mf fchk --1k --mem ``` -Dump MIFARE card contents +Dump MIFARE Classic card contents ``` -options: - -f, --file filename of dump - -k, --keys filename of keys - --mini MIFARE Classic Mini / S20 - --1k MIFARE Classic 1k / S50 (default) - --2k MIFARE Classic/Plus 2k - --4k MIFARE Classic 4k / S70 - -examples/notes: - hf mf dump --mini -> MIFARE Mini - hf mf dump --1k -> MIFARE Classic 1k - hf mf dump --2k -> MIFARE 2k - hf mf dump --4k -> MIFARE 4k - hf mf dump -f hf-mf-066C8B78-key-5.bin -> MIFARE 1k with keys from specified file +Options: +--- +-f, --file filename of dump +-k, --keys filename of keys + --mini MIFARE Classic Mini / S20 + --1k MIFARE Classic 1k / S50 (default) + --2k MIFARE Classic/Plus 2k + --4k MIFARE Classic 4k / S70 pm3 --> hf mf dump pm3 --> hf mf dump --1k -k hf-mf-A29558E4-key.bin -f hf-mf-A29558E4-dump.bin ``` -Convert .bin to .eml +Write to MIFARE Classic block ``` -Options +Options: --- -i : Specifies the dump-file (input). If omitted, 'dumpdata.bin' is used + --blk block number +-a input key type is key A (def) +-b input key type is key B +-k, --key key, 6 hex bytes +-d, --data bytes to write, 16 hex bytes -pm3 --> script run data_mf_bin2eml -i dumpdata.bin +pm3 --> hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d d3a2859f6b880400c801002000000016 ``` -Write to MIFARE block +Run autopwn, to extract all keys and backup a MIFARE Classic tag ``` -Options ---- - - -pm3 --> hf mf wrbl 0 A FFFFFFFFFFFF d3a2859f6b880400c801002000000016 -``` - -Run autopwn, to backup a MIFARE tag -``` -Options ---- +Options: +--- +-k, --key Known key, 12 hex bytes +-s, --sector Input sector number +-a Input key A (def) +-b Input key B +-f, --file filename of dictionary +-s, --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 + --1k MIFARE Classic 1k / S50 (default) + --2k MIFARE Classic/Plus 2k + --4k MIFARE Classic 4k / S70 pm3 --> hf mf autopwn + +// target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF' +pm3 --> hf mf autopwn -s 0 -a -k FFFFFFFFFFFF + +// target MFC 1K card, default dictionary +pm3 --> hf mf autopwn --1k -f mfc_default_keys ``` Run hardnested attack ``` Options --- - [known target key (12 hex symbols)] [w] [s] -w : Acquire nonces and write them to binary file nonces.bin +-k, --key Key, 12 hex bytes + --blk Input block number +-a Input key A (def) +-b Input key B + --tblk Target block number + --ta Target key A + --tb Target key B + --tk Target key, 12 hex bytes +-f, --file R/W instead of default name +-s, --slow Slower acquisition (required by some non standard cards) +-w, --wr Acquire nonces and UID, and write them to file `hf-mf--nonces.bin` -pm3 --> hf mf hardnested 0 A 8829da9daf76 0 A w +pm3 --> hf mf hardnested --blk 0 -a -k 8829da9daf76 --tblk 4 --ta -w ``` -Load MIFARE emul dump file into memory for simulation +Load MIFARE Classic dump file into emulator memory for simulation +Accepts (BIN/EML/JSON) ``` Options --- - -[card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K, u = UL +-f, --file filename of dump + --mini MIFARE Classic Mini / S20 + --1k MIFARE Classic 1k / S50 (def) + --2k MIFARE Classic/Plus 2k + --4k MIFARE Classic 4k / S70 + --ul MIFARE Ultralight family +-q, --qty manually set number of blocks (overrides) -pm3 --> hf mf eload hf-mf-353C2AA6 -pm3 --> hf mf eload 1 hf-mf-353C2AA6 +pm3 --> hf mf eload -f hf-mf-353C2AA6-dump.bin +pm3 --> hf mf eload --1k -f hf-mf-353C2AA6-dump.bin ``` Simulate MIFARE @@ -334,18 +356,17 @@ pm3 --> hf mf sim -u 353c2aa6 Simulate MIFARE Sequence ``` -pm3 --> hf mf chk -* --1k --all -f mfc_default_keys -pm3 --> hf mf dump 1 -pm3 --> script run data_mf_bin2eml -i dumpdata.bin -pm3 --> hf mf eload 353C2AA6 +pm3 --> hf mf fchk --1k -f mfc_default_keys.dic +pm3 --> hf mf dump +pm3 --> hf mf eload -f hf-mf--dump.bin pm3 --> hf mf sim -u 353c2aa6 ``` Clone MIFARE 1K Sequence ``` -pm3 --> hf mf chk -* --1k --all -f mfc_default_keys +pm3 --> hf mf fchk --1k -f mfc_default_keys.dic pm3 --> hf mf dump -pm3 --> hf mf restore 1 u 4A6CE843 k hf-mf-A29558E4-key.bin f hf-mf-A29558E4-dump.bin +pm3 --> hf mf restore --1k --uid 4A6CE843 -k hf-mf-A29558E4-key.bin -f hf-mf-A29558E4-dump.bin ``` Read MIFARE Ultralight EV1 @@ -356,8 +377,7 @@ pm3 --> hf mfu info Clone MIFARE Ultralight EV1 Sequence ``` pm3 --> hf mfu dump -k FFFFFFFF -pm3 --> script run data_mfu_bin2eml -i hf-mfu-XXXX-dump.bin -o hf-mfu-XXXX-dump.eml -pm3 --> hf mfu eload -u -f hf-mfu-XXXX-dump.eml +pm3 --> hf mfu eload -u -f hf-mfu-XXXX-dump.bin pm3 --> hf mfu sim -t 7 ``` @@ -383,23 +403,24 @@ Convert Site & Facility code to Wiegand raw hex ``` Options --- --w --oem --fc --cn --issue + --fc facility number + --cn card number + --issue issue level + --oem OEM code +-w, --wiegand see `wiegand list` for available formats + --pre add HID ProxII preamble to wiegand output --w wiegand format to use - --oem OEM number / site code - --fc facility code - --cn card number - --issue issue level - -pm3 --> wiegand encode -w H10301 --oem 0 --fc 56 --cn 150 +pm3 --> wiegand encode -w H10301 --oem 0 --fc 101 --cn 1337 +pm3 --> wiegand encode --fc 101 --cn 1337 ``` Convert Site & Facility code from Wiegand raw hex to numbers ``` Options --- --p ignore parity errors - --raw raw hex to be decoded +-p, --parity ignore invalid parity +-r, --raw raw hex to be decoded +-b, --bin binary string to be decoded pm3 --> wiegand decode --raw 2006f623ae ``` @@ -497,19 +518,20 @@ Act as Hitag reader ``` Options --- -HitagS: -01 : Read all pages, challenge mode -02 : Read all pages, crypto mode. Set key=0 for no auth + --01 HitagS, read all pages, challenge mode + --02 HitagS, read all pages, crypto mode. Set key=0 for no auth -Hitag2: -21 : Read all pages, password mode. Default: 4D494B52 ("MIKR") -22 : Read all pages, challenge mode -23 : Read all pages, crypto mode. Key format: ISK high + ISK low. Default: 4F4E4D494B52 ("ONMIKR") -25 : Test recorded authentications -26 : Just read UID - -pm3 --> lf hitag 26 -pm3 --> lf hitag 21 4D494B52 + --21 Hitag2, read all pages, password mode. def 4D494B52 (MIKR) + --22 Hitag2, read all pages, challenge mode + --23 Hitag2, read all pages, crypto mode. Key ISK high + ISK low. def 4F4E4D494B52 (ONMIKR) + --25 Hitag2, test recorded authentications (replay?) + --26 Hitag2, read UID +-k, --key key, 4 or 6 hex bytes + --nrar nonce / answer reader, 8 hex bytes + +pm3 --> lf hitag --26 +pm3 --> lf hitag --21 -k 4D494B52 +pm3 --> lf hitag reader --23 -k 4F4E4D494B52 ``` Sniff Hitag traffic @@ -518,30 +540,32 @@ pm3 --> lf hitag sniff pm3 --> lf hitag list ``` -Simulate Hitag +Simulate Hitag2 ``` -pm3 --> lf hitag sim c378181c_a8f7.ht2 +pm3 --> lf hitag sim -2 ``` Write to Hitag block ``` Options --- -HitagS: -03 : Write page, challenge mode -04 : Write page, crypto mode. Set key=0 for no auth + --03 HitagS, write page, challenge mode + --04 HitagS, write page, crypto mode. Set key=0 for no auth -Hitag2: -24 : Write page, crypto mode. Key format: ISK high + ISK low. -27 : Write page, password mode. Default: 4D494B52 ("MIKR") + --24 Hitag2, write page, crypto mode. + --27 Hitag2, write page, password mode +-p, --page page address to write to +-d, --data data, 4 hex bytes +-k, --key key, 4 or 6 hex bytes + --nrar nonce / answer writer, 8 hex bytes -pm3 --> lf hitag writer 24 499602D2 1 00000000 +pm3 --> lf hitag writer --24 -k 499602D2 -p 1 -d 00000000 ``` Simulate Hitag2 sequence ``` -pm3 --> lf hitag reader 21 56713368 -pm3 --> lf hitag sim c378181c_a8f7.ht2 +pm3 --> lf hitag reader --21 -k 56713368 +pm3 --> lf hitag sim -2 ``` ## T55XX @@ -556,30 +580,43 @@ Configure modulation ``` Options --- - : Set modulation + --FSK set demodulation FSK + --FSK1 set demodulation FSK 1 + --FSK1A set demodulation FSK 1a (inv) + --FSK2 set demodulation FSK 2 + --FSK2A set demodulation FSK 2a (inv) + --ASK set demodulation ASK + --PSK1 set demodulation PSK 1 + --PSK2 set demodulation PSK 2 + --PSK3 set demodulation PSK 3 + --NRZ set demodulation NRZ + --BI set demodulation Biphase + --BIA set demodulation Diphase (inverted biphase) + EM is ASK HID Prox is FSK Indala is PSK -pm3 --> lf t55xx config FSK +pm3 --> lf t55xx config --FSK ``` Set timings to default ``` Options --- --p : persist to flash memory (RDV4) --z : Set default t55x7 timings (use `-p` to save if required) -pm3 --> lf t55xx deviceconfig -z -p +-p, --persist persist to flash memory (RDV4) +-z Set default t55x7 timings (use `-p` to save if required) +pm3 --> lf t55xx deviceconfig -zp ``` Write to T55xx block ``` -b : block number to write. Between 0-7 -d : 4 bytes of data to write (8 hex characters) +-b, --blk <0-7> block number to write +-d, --data data to write (4 hex bytes) +-p, --pwd password (4 hex bytes) -pm3 --> lf t55xx wr b 0 d 00081040 +pm3 --> lf t55xx write -b 0 -d 00081040 ``` Wipe a T55xx tag and set defaults @@ -592,7 +629,7 @@ pm3 --> lf t55xx wipe Get raw samples [512-40000] ``` -pm3 --> data samples +pm3 --> data samples -n ``` Save samples to file @@ -625,8 +662,8 @@ Convert .bin to .eml ``` Options --- --i Specifies the dump-file (input). If omitted, 'dumpdata.bin' is used --o Specifies the output file. If omitted, .eml is used +-i Specifies the dump-file (input). If omitted, 'dumpdata.bin' is used +-o Specifies the output file. If omitted, .eml is used pm3 --> script run data_mf_bin2eml -i xxxxxxxxxxxxxx.bin ``` @@ -635,8 +672,8 @@ Convert .eml to .bin ``` Options --- --i Specifies the dump-file (input). If omitted, 'dumpdata.eml' is used --o Specifies the output file. If omitted, .bin is used +-i Specifies the dump-file (input). If omitted, 'dumpdata.eml' is used +-o Specifies the output file. If omitted, .bin is used pm3 --> script run data_mf_eml2bin -i myfile.eml -o myfile.bin ``` @@ -645,10 +682,10 @@ Format Mifare card ``` Options --- --k The current six byte key with write access --n The new key that will be written to the card --a The new access bytes that will be written to the card --x Execute the commands as well +-k The current six byte key with write access +-n The new key that will be written to the card +-a The new access bytes that will be written to the card +-x Execute the commands as well pm3 --> script run hf_mf_format -k FFFFFFFFFFFF -n FFFFFFFFFFFF -x ``` diff --git a/doc/cliparser_todo.txt b/doc/cliparser_todo.txt deleted file mode 100644 index f3cc3500c..000000000 --- a/doc/cliparser_todo.txt +++ /dev/null @@ -1,96 +0,0 @@ -clear -pref -analyse foo -data biphaserawdecode -data detectclock -data fsktonrz -data manrawdecode -data rawdemod -data askedgedetect -data autocorr -data dirthreshold -data hide -data hpf -data iir -data grid -data ltrim -data mtrim -data norm -data plot -data rtrim -data setgraphmarkers -data shiftgraphzero -data zerocrossings -data convertbitstream -data getbitstream -data bin2hex -data bitsamples -data clear -data hexsamples -data hex2bin -data print -data samples -data setdebugmode -data tune -hf 15 dump -hf 15 info -hf 15 raw -hf 15 rdbl -hf 15 readmulti -hf 15 restore -hf 15 wrbl -hf 15 writeafi -hf 15 writedsfid -hf felica reader -hf felica sniff -hf felica raw -hf felica rdunencrypted -hf felica wrunencrypted -hf felica rqservice -hf felica rqresponse -hf felica scsvcode -hf felica rqsyscode -hf felica auth1 -hf felica auth2 -hf felica rqspecver -hf felica resetmode -hf felica litesim -hf felica litedump -hf mf hardnested -hf mf autopwn -hf mf nack -hf mf decrypt -hf mf rdbl -hf mf rdsc -hf mf restore -hf mf setmod -hf mf wrbl -hf mf ecfill -hf mf eclr -hf mf egetblk -hf mf egetsc -hf mf ekeyprn -hf mf eload -hf mf esave -hf mf eset -hf mf eview -hf mf cgetblk -hf mf cgetsc -hf mf cload -hf mf csave -hf mf csetblk -hf mf csetuid -hf mf cview -hf mf gen3uid -hf mf gen3blk -hf mf gen3freeze -lf em 410x -lf em 4x05 -lf em 4x50 -lf em 4x70 -lf hitag reader -lf hitag sim -lf hitag writer -lf hitag dump -lf hitag cc -script run diff --git a/doc/cloner_notes.md b/doc/cloner_notes.md index 92de920ad..78f7b360e 100644 --- a/doc/cloner_notes.md +++ b/doc/cloner_notes.md @@ -69,14 +69,14 @@ The T55x7 protocol uses a pwm based protocol for writing to tags. In order to m ``` -- after threshold limit 20 is triggered, skip 10000 samples before collecting samples. -lf config s 10000 t 20 +lf config -s 10000 -t 20 lf t55xx sniff -- if you have a save trace from before, try data load -f xxxxxxx.pm3 -lf t55xx sniff 1 +lf t55xx sniff -1 ``` It uses the existing `lf sniff` command to collect the data, so setting that first as per normal sniffing is recommended. Once you have a sniff, you can "re-sniff" from the stored sniffed data and try different settings, if you think the data is not clean. -As normal, the cloner may write data past the end of the 40K sample buffer. So using the `lf config s ` then re-run the sniff to see if there is more data. \ No newline at end of file +As normal, the cloner may write data past the end of the 40K sample buffer. So using the `lf config -s ` then re-run the sniff to see if there is more data. \ No newline at end of file diff --git a/doc/colors_notes.md b/doc/colors_notes.md index 74d0cbb92..38a3e9ede 100644 --- a/doc/colors_notes.md +++ b/doc/colors_notes.md @@ -4,7 +4,7 @@ ## Table of Contents * [style/color](#style_color) * [Proxspace](#proxspace) - * [](#) + * [help texts](#help-texts) The client should autodetect color support when starting. @@ -48,3 +48,7 @@ Most commands doesn't use a header yet. We added it to make it standout (ie: yel ^[Top](#top) Proxspace has support for colors. + +## Help texts +^[Top](#top) +The help text uses a hard coded template deep inside the cliparser.c file. \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 1c9938d16..f4fb8c813 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -10,17 +10,57 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- +|`help `|Y |`Use ` help` for details of a command` |`auto `|N |`Automated detection process for unknown tags` |`clear `|Y |`Clear screen` -|`help `|Y |`This help. Use ' help' for details of a particular command.` |`hints `|Y |`Turn hints on / off` |`msleep `|Y |`Add a pause in milliseconds` -|`pref `|Y |`Edit preferences` |`rem `|Y |`Add a text line in log file` |`quit `|Y |`` |`exit `|Y |`Exit program` +### prefs + + { Edit client/device preferences... } + +|command |offline |description +|------- |------- |----------- +|`prefs help `|Y |`This help` +|`prefs show `|Y |`Show all preferences` + + +### prefs get + + { Get a preference } + +|command |offline |description +|------- |------- |----------- +|`prefs get barmode `|Y |`Get bar mode preference` +|`prefs get clientdebug `|Y |`Get client debug level preference` +|`prefs get color `|Y |`Get color support preference` +|`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 plotsliders `|Y |`Get plot slider display preference` + + +### prefs set + + { Set a preference } + +|command |offline |description +|------- |------- |----------- +|`prefs set help `|Y |`This help` +|`prefs set barmode `|Y |`Set bar mode` +|`prefs set clientdebug `|Y |`Set client debug level` +|`prefs set color `|Y |`Set color support` +|`prefs set emoji `|Y |`Set emoji display` +|`prefs set hints `|Y |`Set hint display` +|`prefs set savepaths `|Y |`... to be adjusted next ... ` +|`prefs set plotsliders `|Y |`Set plot slider display` + + ### analyse { Analyse utils... } @@ -54,38 +94,37 @@ Check column "offline" for their availability. |`data manrawdecode `|Y |`Manchester decode binary stream in DemodBuffer` |`data modulation `|Y |`Identify LF signal for clock and modulation` |`data rawdemod `|Y |`Demodulate the data in the GraphBuffer and output binary` -|`data askedgedetect `|Y |`[threshold] Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave (use 20-45, def:25)` +|`data askedgedetect `|Y |`Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave` |`data autocorr `|Y |`Autocorrelation over window` -|`data dirthreshold `|Y |` -- Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.` +|`data dirthreshold `|Y |`Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.` |`data decimate `|Y |`Decimate samples` |`data undecimate `|Y |`Un-decimate samples` |`data hide `|Y |`Hide graph window` |`data hpf `|Y |`Remove DC offset from trace` -|`data iir `|Y |`apply IIR buttersworth filter on plotdata` -|`data grid `|Y |` -- overlay grid on graph window, use zero value to turn off either` -|`data ltrim `|Y |` -- Trim samples from left of trace` -|`data mtrim `|Y |` -- Trim out samples from the specified start to the specified stop` +|`data iir `|Y |`Apply IIR buttersworth filter on plot data` +|`data grid `|Y |`overlay grid on graph window` +|`data ltrim `|Y |`Trim samples from left of trace` +|`data mtrim `|Y |`Trim out samples from the specified start to the specified stop` |`data norm `|Y |`Normalize max/min to +/-128` -|`data plot `|Y |`Show graph window (hit 'h' in window for keystroke help)` -|`data rtrim `|Y |` -- Trim samples from right of trace` -|`data setgraphmarkers `|Y |`[orange_marker] [blue_marker] (in graph window)` -|`data shiftgraphzero `|Y |` -- Shift 0 for Graphed wave + or - shift value` -|`data timescale `|Y |`Set a timescale to get a differential reading between the yellow and purple markers as time duration -` +|`data plot `|Y |`Show graph window` +|`data rtrim `|Y |`Trim samples from right of trace` +|`data setgraphmarkers `|Y |`Set blue and orange marker in graph window` +|`data shiftgraphzero `|Y |`Shift 0 for Graphed wave + or - shift value` +|`data timescale `|Y |`Set a timescale to get a differential reading between the yellow and purple markers as time duration` |`data zerocrossings `|Y |`Count time between zero-crossings` |`data convertbitstream `|Y |`Convert GraphBuffer's 0/1 values to 127 / -127` |`data getbitstream `|Y |`Convert GraphBuffer's >=1 values to 1 and <1 to 0` |`data bin2hex `|Y |`Converts binary to hexadecimal` |`data bitsamples `|N |`Get raw samples as bitstring` |`data clear `|Y |`Clears bigbuf on deviceside and graph window` -|`data hexsamples `|N |` [] -- Dump big buffer as hex bytes` +|`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 ndef `|Y |`Decode NDEF records` -|`data print `|Y |`print the data in the DemodBuffer` -|`data samples `|N |`[512 - 40000] -- Get raw samples for graph window (GraphBuffer)` +|`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)` -|`data setdebugmode `|Y |`<0|1|2> -- Set Debugging Level on client side` +|`data setdebugmode `|Y |`Set Debugging Level on client side` |`data tune `|N |`Measure tuning of device antenna. Results shown in graph window` @@ -163,7 +202,7 @@ Check column "offline" for their availability. |`hf 14b sim `|N |`Fake ISO 14443B tag` |`hf 14b sniff `|N |`Eavesdrop ISO 14443B` |`hf 14b rdbl `|N |`Read SRI512/SRIX4x block` -|`hf 14b sriwrite `|N |`Write data to a SRI512 | SRIX4K tag` +|`hf 14b sriwrite `|N |`Write data to a SRI512 or SRIX4K tag` ### hf 15 @@ -173,23 +212,23 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf 15 help `|Y |`This help` -|`hf 15 list `|Y |`List ISO15693 history` -|`hf 15 demod `|Y |`Demodulate ISO15693 from tag` -|`hf 15 dump `|N |`Read all memory pages of an ISO15693 tag, save to file` +|`hf 15 list `|Y |`List ISO-15693 history` +|`hf 15 demod `|Y |`Demodulate ISO-15693 from tag` +|`hf 15 dump `|N |`Read all memory pages of an ISO-15693 tag, save to file` |`hf 15 info `|N |`Tag information` -|`hf 15 sniff `|N |`Sniff ISO15693 traffic` +|`hf 15 sniff `|N |`Sniff ISO-15693 traffic` |`hf 15 raw `|N |`Send raw hex data to tag` |`hf 15 rdbl `|N |`Read a block` -|`hf 15 reader `|N |`Act like an ISO15693 reader` -|`hf 15 readmulti `|N |`Reads multiple Blocks` -|`hf 15 restore `|N |`Restore from file to all memory pages of an ISO15693 tag` -|`hf 15 samples `|N |`Acquire Samples as Reader (enables carrier, sends inquiry)` -|`hf 15 sim `|N |`Fake an ISO15693 tag` +|`hf 15 rdmulti `|N |`Reads multiple blocks` +|`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 sim `|N |`Fake an ISO-15693 tag` |`hf 15 wrbl `|N |`Write a block` -|`hf 15 findafi `|N |`Brute force AFI of an ISO15693 tag` -|`hf 15 writeafi `|N |`Writes the AFI on an ISO15693 tag` -|`hf 15 writedsfid `|N |`Writes the DSFID on an ISO15693 tag` -|`hf 15 csetuid `|N |`Set UID for magic Chinese card` +|`hf 15 findafi `|N |`Brute force AFI of an ISO-15693 tag` +|`hf 15 writeafi `|N |`Writes the AFI on an ISO-15693 tag` +|`hf 15 writedsfid `|N |`Writes the DSFID on an ISO-15693 tag` +|`hf 15 csetuid `|N |`Set UID for magic card` ### hf epa @@ -224,10 +263,11 @@ Check column "offline" for their availability. |`hf felica help `|Y |`This help` |`hf felica list `|Y |`List ISO 18092/FeliCa history` |`hf felica reader `|N |`Act like an ISO18092/FeliCa reader` +|`hf felica info `|N |`Tag information` |`hf felica sniff `|N |`Sniff ISO 18092/FeliCa traffic` |`hf felica raw `|N |`Send raw hex data to tag` -|`hf felica rdunencrypted`|N |`read Block Data from authentication-not-required Service.` -|`hf felica wrunencrypted`|N |`write Block Data to an authentication-not-required Service.` +|`hf felica rdbl `|N |`read block data from authentication-not-required Service.` +|`hf felica wrbl `|N |`write block data to an authentication-not-required Service.` |`hf felica rqservice `|N |`verify the existence of Area and Service, and to acquire Key Version.` |`hf felica rqresponse `|N |`verify the existence of a card and its Mode.` |`hf felica scsvcode `|N |`acquire Area Code and Service Code.` @@ -236,7 +276,7 @@ Check column "offline" for their availability. |`hf felica auth2 `|N |`allow a card to authenticate a Reader/Writer. Complete mutual authentication` |`hf felica rqspecver `|N |`acquire the version of card OS.` |`hf felica resetmode `|N |`reset Mode to Mode 0.` -|`hf felica litesim `|N |` - only reply to poll request` +|`hf felica litesim `|N |`Emulating ISO/18092 FeliCa Lite tag` |`hf felica litedump `|N |`Wait for and try dumping FelicaLite` @@ -290,6 +330,7 @@ Check column "offline" for their availability. |`hf iclass eload `|N |`Load Picopass / iCLASS dump file into emulator memory` |`hf iclass esave `|N |`Save emulator memory to file` |`hf iclass eview `|N |`View emulator memory` +|`hf iclass configcard `|Y |`Reader configuration card` |`hf iclass calcnewkey `|Y |`Calc diversified keys (blocks 3 & 4) to write new keys` |`hf iclass encode `|Y |`Encode binary wiegand to block 7` |`hf iclass encrypt `|Y |`Encrypt given block data` @@ -362,29 +403,30 @@ Check column "offline" for their availability. |`hf mf rdsc `|N |`Read MIFARE Classic sector` |`hf mf restore `|N |`Restore MIFARE Classic binary file to BLANK tag` |`hf mf setmod `|N |`Set MIFARE Classic EV1 load modulation strength` +|`hf mf view `|Y |`Display content from tag dump file` +|`hf mf wipe `|N |`Wipe card to zeros and default keys/acc` |`hf mf wrbl `|N |`Write MIFARE Classic block` |`hf mf sim `|N |`Simulate MIFARE card` -|`hf mf ecfill `|N |`Fill simulator memory with help of keys from simulator` -|`hf mf eclr `|N |`Clear simulator memory` -|`hf mf egetblk `|N |`Get simulator memory block` -|`hf mf egetsc `|N |`Get simulator memory sector` -|`hf mf ekeyprn `|N |`Print keys from simulator memory` +|`hf mf ecfill `|N |`Fill emulator memory with help of keys from emulator` +|`hf mf eclr `|N |`Clear emulator memory` +|`hf mf egetblk `|N |`Get emulator memory block` +|`hf mf egetsc `|N |`Get emulator memory sector` +|`hf mf ekeyprn `|N |`Print keys from emulator memory` |`hf mf eload `|N |`Load from file emul dump` |`hf mf esave `|N |`Save to file emul dump` -|`hf mf eset `|N |`Set simulator memory block` -|`hf mf eview `|N |`View emul memory` -|`hf mf cgetblk `|N |`Read block` -|`hf mf cgetsc `|N |`Read sector` -|`hf mf cload `|N |`Load dump` +|`hf mf esetblk `|N |`Set emulator memory block` +|`hf mf eview `|N |`View emulator memory` +|`hf mf cgetblk `|N |`Read block from card` +|`hf mf cgetsc `|N |`Read sector from card` +|`hf mf cload `|N |`Load dump to card` |`hf mf csave `|N |`Save dump from card into file or emulator` -|`hf mf csetblk `|N |`Write block` -|`hf mf csetuid `|N |`Set UID` -|`hf mf cview `|N |`view card` +|`hf mf csetblk `|N |`Write block to card` +|`hf mf csetuid `|N |`Set UID on card` +|`hf mf cview `|N |`View card` |`hf mf cwipe `|N |`Wipe card to default UID/Sectors/Keys` -|`hf mf gen3uid `|N |`Set UID without manufacturer block` -|`hf mf gen3blk `|N |`Overwrite full manufacturer block` -|`hf mf gen3freeze `|N |`Perma lock further UID changes` -|`hf mf ice `|N |`collect MIFARE Classic nonces to file` +|`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 mfp @@ -521,6 +563,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hw help `|Y |`This help` +|`hw break `|N |`Send break loop usb command` |`hw connect `|Y |`Connect Proxmark3 to serial port` |`hw dbg `|N |`Set Proxmark3 debug level` |`hw detectreader `|N |`Detect external reader field` @@ -606,10 +649,80 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em help `|Y |`This help` -|`lf em 410x `|Y |`EM 4102 commands...` -|`lf em 4x05 `|Y |`EM 4205 / 4305 / 4369 / 4469 commands...` -|`lf em 4x50 `|Y |`EM 4350 / 4450 commands...` -|`lf em 4x70 `|Y |`EM 4070 / 4170 commands...` + + +### lf em 410x + + { EM 4102 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 410x help `|Y |`This help` +|`lf em 410x demod `|Y |`demodulate a EM410x tag from the GraphBuffer` +|`lf em 410x reader `|N |`attempt to read and extract tag data` +|`lf em 410x sim `|N |`simulate EM410x tag` +|`lf em 410x brute `|N |`reader bruteforce attack by simulating EM410x tags` +|`lf em 410x watch `|N |`watches for EM410x 125/134 kHz tags (option 'h' for 134)` +|`lf em 410x spoof `|N |`watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)` +|`lf em 410x clone `|N |`write EM410x UID to T55x7 or Q5/T5555 tag` + + +### lf em 4x05 + + { EM 4205 / 4305 / 4369 / 4469 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 4x05 help `|Y |`This help` +|`lf em 4x05 brute `|N |`Bruteforce password` +|`lf em 4x05 chk `|N |`Check passwords from dictionary` +|`lf em 4x05 demod `|Y |`demodulate a EM4x05/EM4x69 tag from the GraphBuffer` +|`lf em 4x05 dump `|N |`dump EM4x05/EM4x69 tag` +|`lf em 4x05 info `|N |`tag information EM4x05/EM4x69` +|`lf em 4x05 read `|N |`read word data from EM4x05/EM4x69` +|`lf em 4x05 sniff `|Y |`Attempt to recover em4x05 commands from sample buffer` +|`lf em 4x05 unlock `|N |`execute tear off against EM4x05/EM4x69` +|`lf em 4x05 wipe `|N |`wipe EM4x05/EM4x69 tag` +|`lf em 4x05 write `|N |`write word data to EM4x05/EM4x69` + + +### lf em 4x50 + + { EM 4350 / 4450 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 4x50 help `|Y |`This help` +|`lf em 4x50 brute `|N |`guess password of EM4x50` +|`lf em 4x50 chk `|N |`check passwords from dictionary` +|`lf em 4x50 dump `|N |`dump EM4x50 tag` +|`lf em 4x50 info `|N |`tag information EM4x50` +|`lf em 4x50 login `|N |`login into EM4x50` +|`lf em 4x50 rdbl `|N |`read word data from EM4x50` +|`lf em 4x50 wrbl `|N |`write word data to EM4x50` +|`lf em 4x50 writepwd `|N |`change password of EM4x50` +|`lf em 4x50 wipe `|N |`wipe EM4x50 tag` +|`lf em 4x50 reader `|N |`show standard read mode data of EM4x50` +|`lf em 4x50 restore `|N |`restore EM4x50 dump to tag` +|`lf em 4x50 sim `|N |`simulate EM4x50 tag` +|`lf em 4x50 eload `|N |`upload dump of EM4x50 to emulator memory` +|`lf em 4x50 esave `|N |`save emulator memory to file` +|`lf em 4x50 eview `|N |`view EM4x50 content in emulator memory` + + +### lf em 4x70 + + { EM 4070 / 4170 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 4x70 help `|Y |`This help` +|`lf em 4x70 info `|N |`Tag information EM4x70` +|`lf em 4x70 write `|N |`Write EM4x70` +|`lf em 4x70 unlock `|N |`Unlock EM4x70 for writing` +|`lf em 4x70 auth `|N |`Authenticate EM4x70` +|`lf em 4x70 writepin `|N |`Write PIN` +|`lf em 4x70 writekey `|N |`Write Crypt Key` ### lf fdxb @@ -673,12 +786,13 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf hitag help `|Y |`This help` +|`lf hitag eload `|N |`Load Hitag dump file into emulator memory` |`lf hitag list `|N |`List Hitag trace history` |`lf hitag info `|N |`Tag information` -|`lf hitag reader `|N |`Act like a Hitag Reader` +|`lf hitag reader `|N |`Act like a Hitag reader` |`lf hitag sim `|N |`Simulate Hitag transponder` |`lf hitag sniff `|N |`Eavesdrop Hitag communication` -|`lf hitag writer `|N |`Act like a Hitag Writer` +|`lf hitag writer `|N |`Act like a Hitag writer` |`lf hitag dump `|N |`Dump Hitag2 tag` |`lf hitag cc `|N |`Test all challenges` @@ -905,7 +1019,7 @@ Check column "offline" for their availability. |`lf t55xx info `|Y |`Show T55x7 configuration data (page 0/ blk 0)` |`lf t55xx p1detect `|N |`Try detecting if this is a t55xx tag by reading page 1` |`lf t55xx read `|N |`Read T55xx block data` -|`lf t55xx resetread `|N |`Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)` +|`lf t55xx resetread `|N |`Send Reset Cmd then lf read the stream to attempt to identify the start of it` |`lf t55xx restore `|N |`Restore T55xx card Page 0 / Page 1 blocks` |`lf t55xx trace `|Y |`Show T55x7 traceability data (page 1/ blk 0-1)` |`lf t55xx wakeup `|N |`Send AOR wakeup command` @@ -953,13 +1067,34 @@ Check column "offline" for their availability. |------- |------- |----------- |`mem help `|Y |`This help` |`mem baudrate `|N |`Set Flash memory Spi baudrate` -|`mem spiffs `|N |`High level SPI FileSystem Flash manipulation` -|`mem info `|N |`Flash memory information` -|`mem load `|N |`Load data into flash memory` |`mem dump `|N |`Dump data from flash memory` +|`mem info `|N |`Flash memory information` +|`mem load `|N |`Load data to flash memory` |`mem wipe `|N |`Wipe data from flash memory` +### mem spiffs + + { SPI File system } + +|command |offline |description +|------- |------- |----------- +|`mem spiffs help `|Y |`This help` +|`mem spiffs copy `|N |`Copy a file to another (destructively) in SPIFFS file system` +|`mem spiffs check `|N |`Check/try to defrag faulty/fragmented file system` +|`mem spiffs dump `|N |`Dump a file from SPIFFS file system` +|`mem spiffs info `|N |`Print file system info and usage statistics` +|`mem spiffs mount `|N |`Mount the SPIFFS file system if not already mounted` +|`mem spiffs remove `|N |`Remove a file from SPIFFS file system` +|`mem spiffs rename `|N |`Rename/move a file in SPIFFS file system` +|`mem spiffs test `|N |`Test SPIFFS Operations` +|`mem spiffs tree `|N |`Print the Flash memory file system tree` +|`mem spiffs unmount `|N |`Un-mount the SPIFFS file system` +|`mem spiffs upload `|N |`Upload file into SPIFFS file system` +|`mem spiffs view `|N |`View file on SPIFFS file system` +|`mem spiffs wipe `|N |`Wipe all files from SPIFFS file system * dangerous *` + + ### reveng { CRC calculations from RevEng software... } @@ -988,9 +1123,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- -|`script help `|Y |`Usage info` +|`script help `|Y |`This help` |`script list `|Y |`List available scripts` -|`script run `|Y |` -- execute a script` +|`script run `|Y |` - execute a script` ### trace diff --git a/doc/jtag_notes.md b/doc/jtag_notes.md index 9029958e6..1e6369767 100644 --- a/doc/jtag_notes.md +++ b/doc/jtag_notes.md @@ -61,7 +61,7 @@ GND | 6 3.3 | 1 # Where to find more information? -There has been lots of articles and blogposts about recoving, debricking, JTAG your Proxmark3 and you find here below a sortiment of resources that will be of help. +There has been lots of articles and blogposts about recovering, debricking, JTAG your Proxmark3 and you find here below an assortiment of resources that will be of help. ## Third party notes on using a BusPirate diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index d24a0b7c5..8e1edbaac 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -79,7 +79,7 @@ UID 4b: (actually NUID as there are no more "unique" IDs on 4b) ``` -Computing BCC on UID 11223344: `hf analyse lcr 11223344` = `44` +Computing BCC on UID 11223344: `hf analyse lcr -d 11223344` = `44` UID 7b: @@ -189,13 +189,13 @@ hf mf cwipe -u 11223344 -a 0044 -s 18 or just fixing block0: ``` # MFC Gen1A 1k: -hf mf csetuid 11223344 0004 08 +hf mf csetuid -u 11223344 -a 0004 -s 08 # MFC Gen1A 4k: -hf mf csetuid 11223344 0044 18 +hf mf csetuid -u 11223344 -a 0044 -s 18 ``` ``` -script run run hf_mf_magicrevive +script run hf_mf_magicrevive ``` To execute commands manually: @@ -329,7 +329,7 @@ Android compatible ### Proxmark3 commands ``` -hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869 +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 11223344440804006263646566676869 ``` When "soft-bricked" (by writing invalid data in block0), these ones may help: @@ -342,8 +342,8 @@ e.g. for 4b UID: ``` hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip -hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869 # for 1k -hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869 # for 4k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -k 11223344440804006263646566676869 # for 1k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -k 11223344441802006263646566676869 # for 4k hf 14a config --std hf 14a reader ``` @@ -352,8 +352,8 @@ e.g. for 7b UID: ``` hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip -hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667 # for 1k -hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667 # for 4k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -k 04112233445566084400626364656667 # for 1k +hf mf wrbl --blk 0 -k FFFFFFFFFFFF -k 04112233445566184200626364656667 # for 4k hf 14a config --std hf 14a reader ``` @@ -499,9 +499,9 @@ BCC1 Int LCK0 LCK1 UID is made of SN0..SN6 bytes -Computing BCC0 on UID 04112233445566: `analyse lcr 88041122` = `bf` +Computing BCC0 on UID 04112233445566: `analyse lcr -d 88041122` = `bf` -Computing BCC1 on UID 04112233445566: `analyse lcr 33445566` = `44` +Computing BCC1 on UID 04112233445566: `analyse lcr -d 33445566` = `44` Int is internal, typically 0x48 @@ -538,7 +538,7 @@ When "soft-bricked" (by writing invalid data in block0), these ones may help: ``` hf 14a config -h -script run run hf_mf_magicrevive -u +script run hf_mf_magicrevive -u ``` ## MIFARE Ultralight DirectWrite @@ -585,7 +585,7 @@ Issue three regular MFU write commands in a row to write first three blocks. ### Proxmark3 commands ``` -hf mfu setuid +hf mfu setuid -h ``` Equivalent: don't use `hf mfu wrbl` as you need to write three blocks in a row, but do, with proper BCCx: @@ -605,7 +605,7 @@ hf 14a config -h E.g.: ``` hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip -hf mfu setuid 04112233445566 +hf mfu setuid --uid 04112233445566 hf 14a config --std hf 14a reader ``` @@ -825,7 +825,7 @@ It accepts longer UID but that doesn't affect BCC/ATQA/SAK The same effect (with better ATQA!) can be obtained with a MFC Gen1A that uses SAK defined in block0: ``` -hf mf csetblk 0 1122334444204403A1A2A3A4A5A6A7A8 +hf mf csetblk --blk 0 -d 1122334444204403A1A2A3A4A5A6A7A8 hf 14a info [+] UID: 11 22 33 44 [+] ATQA: 03 44 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 2ba6aeaba..211761b18 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -54,7 +54,7 @@ Known issues: * 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. Will most certainly mess up +* Proxmark Pro: it has different fpga and unknown pin assignments. Unsupported. ## PLATFORM_EXTRAS @@ -90,6 +90,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | HF_14ASNIFF | 14a sniff storing to flashmem - Micolous | 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_ICECLASS | iCLASS 4-1 mode sim/read & dump/loclass/glitch & config to flashmem - Iceman1001 | HF_LEGIC | HF Legic Prime standalone - uhei diff --git a/doc/new_frame_format.md b/doc/new_frame_format.md index 6075fb2c9..c5e7d542d 100644 --- a/doc/new_frame_format.md +++ b/doc/new_frame_format.md @@ -288,10 +288,10 @@ USART_BAUD_RATE defined there Receiving from USART need more than 30ms as we used on USB else we get errors about partial packet reception - FTDI 9600 hw status ⇒ we need 20ms - FTDI 115200 hw status ⇒ we need 50ms - FTDI 460800 hw status ⇒ we need 30ms - BT 115200 hf mf fchk 1 dic ⇒ we need 140ms + FTDI 9600 hw status ⇒ we need 20ms + FTDI 115200 hw status ⇒ we need 50ms + FTDI 460800 hw status ⇒ we need 30ms + BT 115200 hf mf fchk --1k -f file.dic ⇒ we need 140ms # define UART_FPC_CLIENT_RX_TIMEOUT_MS 170 # define UART_USB_CLIENT_RX_TIMEOUT_MS 20 @@ -307,7 +307,7 @@ it's reduced to UART_USB_CLIENT_RX_TIMEOUT_MS. Add automatically some communication delay in the `WaitForResponseTimeout` & `dl_it` timeouts. Only when using FPC, timeout = 2* empirically measured delay (FTDI cable). -Empirically measured delay (FTDI cable) with "hw ping 512" : +Empirically measured delay (FTDI cable) with "hw ping -l 512" : usb ⇒ 6.. 32ms 460800 ⇒ 40.. 70ms @@ -373,9 +373,9 @@ It was needed to tune pm3 RX usart `maxtry` : time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "lf read" 6.28s - time client/proxmark3 -p /dev/ttyACM0 -c "mem dump f foo_usb" + time client/proxmark3 -p /dev/ttyACM0 -c "mem dump -f foo_usb" 1.48s - time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "mem dump f foo_fpc" + time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "mem dump -f foo_fpc" 25.34s @@ -453,7 +453,7 @@ On linux UART (FTDI) CMD_PING reply_ng(CMD_PING, PM3_SUCCESS, packet->data.asBytes, packet->length); <-12=504d33620080000009016233 <- NG -`hw ping 512` (NG) +`hw ping -l 512` (NG) CmdPing SendCommandNG(CMD_PING, data, len); ->522=504d336100820901000102030405060708090a0b0c0d0e0f1011121314151617 -> NG diff --git a/doc/path_notes.md b/doc/path_notes.md index 76c3f7a1a..491f01cd0 100644 --- a/doc/path_notes.md +++ b/doc/path_notes.md @@ -65,7 +65,7 @@ Dictionaries used by the client will be copied to ``` Here you find the default dictionaries used for commands like `hf mf chk`, `hf mf fchk`, `lf t55xx chk` -A dictionary file is a text based file with one key per line in hexdecimal form. +A dictionary file is a text based file with one key per line in hexadecimal form. The length of the key is decided by the Proxmark3 client for the different commands. All chars afterwards on line is ignored. if key isn't a hex number, the key is ignored. diff --git a/doc/trace_notes.md b/doc/trace_notes.md index 515255053..6f89cffbc 100644 --- a/doc/trace_notes.md +++ b/doc/trace_notes.md @@ -26,10 +26,10 @@ The unit for this time information depends on the protocol in use: * Reader Mode: Timings are in ticks (1us == 1.5ticks) * Tag Mode: Timings are in sub carrier periods (1/212 kHz == 4.7us) * Hitag1 / Hitag2 / HitagS: Elementary Time Unit (ETU) is 8µs -* iClass, ISO15693, ISO18092 and FeliCa have no accurate timing information at the moment +* iCLASS, ISO15693, ISO18092 and FeliCa have no accurate timing information at the moment * For others timing is not available -By specifying the option ```f``` (e.g. ```trace list 14a f```) the frame delay times are shown. (So you don't have to do the math by your own). +By specifying the option ```f``` (e.g. ```trace list -t 14a -f```) the frame delay times are shown. (So you don't have to do the math by your own). ### Sources ^[Top](#top) @@ -88,14 +88,14 @@ To get a more detailed explanation of the transmitted data for ISO14443A traces To do so -* use `trace list 14a x` +* use `trace list -t 14a -x` * copy the output (starting with the timestamp) into a textfile * run `text2pcap -t "%S." -l 264 -n ` * now open your pcapng file in Wireshark or read it with the CLI version `tshark` An example frame -with `trace list 14a`: +with `trace list -t 14a`: ``` 19072 | 29536 | Rdr |93 70 88 04 cf ff bc 7f bb | ok | SELECT_UID diff --git a/doc/uart_notes.md b/doc/uart_notes.md index d34ed1a6d..9e71c3dbf 100644 --- a/doc/uart_notes.md +++ b/doc/uart_notes.md @@ -34,7 +34,7 @@ This USART can be reached from the host client (if connected via USB-CDC) throug * `usart config`, to configure the baudrate and the parity of the Proxmark3 USART * `usart txrx/tx/rx/txhex/rxhex` to transmit and receive bytes -So, `usart config` changes the Proxmark3 USART baudrate and parity, while e.g. `usart txrx d AT+Px` and `usart txrx d AT+BAUDx` changes the BT add-on parity and baudrate. +So, `usart config` changes the Proxmark3 USART baudrate and parity, while e.g. `usart txrx -d "AT+Px"` and `usart txrx -d "AT+BAUDx"` changes the BT add-on parity and baudrate. And for things to work fine, both sets have to match! Internally, the desired baudrate is converted to UART settings: a BRGR and a FP. The resulting baudrate will be close to but not always equal to the desired baudrate. Serial ports typically have some error tolerance in the actual baudrates. Theoretically < 2.5% on each side (so 5% in total), < 2% to be on the safe side. In the current firmware configuration, the Proxmark3 can provide any baudrate up to 2Mbauds with an error of max 2%, and selected baudrates up to 6Mbauds (tested with a FTDI C232HM DDHSL-0 cable). @@ -51,7 +51,7 @@ Some specific commands are available when you add `BTADDON` to `PLATFORM_EXTRAS` `usart btfactory` changes several times the Proxmark3 USART baudrate and parity till it matches the BT add-on settings, then changes the baudrate and parity of the add-on to a default value, then changes the Proxmark USART to the same default values, so everything should be back in order. (`btfactory` does more but we're only interested in baudrate in this discussion) -Manual configuration is also possible with `usart txrx d AT+Px` and `usart txrx d AT+BAUDx`. +Manual configuration is also possible with `usart txrx -d "AT+Px"` and `usart txrx -d "AT+BAUDx"`. ### BT add-on connected mode diff --git a/include/mifare.h b/include/mifare.h index 804d6bd78..e38c7eb45 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -101,7 +101,7 @@ typedef enum { } mifare_des_kdf_algo_t; //----------------------------------------------------------------------------- -// "hf 14a sim x", "hf mf sim x" attacks +// "hf 14a sim -x", "hf mf sim -x" attacks //----------------------------------------------------------------------------- typedef struct { uint32_t cuid; @@ -121,23 +121,5 @@ typedef struct { } state; } PACKED nonces_t; -//----------------------------------------------------------------------------- -// ISO 7618 Smart Card -//----------------------------------------------------------------------------- -typedef struct { - uint8_t atr_len; - uint8_t atr[30]; -} PACKED smart_card_atr_t; - -typedef enum SMARTCARD_COMMAND { - SC_CONNECT = (1 << 0), - SC_NO_DISCONNECT = (1 << 1), - SC_RAW = (1 << 2), - SC_SELECT = (1 << 3), - SC_RAW_T0 = (1 << 4), - SC_CLEARLOG = (1 << 5), - SC_LOG = (1 << 6), -} smartcard_command_t; - #endif // _MIFARE_H_ diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 6b7f5df07..5cee586d2 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -368,14 +368,14 @@ typedef struct { uint8_t key_d[8]; uint8_t key_c[8]; uint8_t app_issuer_area[8]; -} PACKED picopass_hdr; +} PACKED picopass_hdr_t; // iCLASS non-secure mode memory mapping typedef struct { uint8_t csn[8]; picopass_conf_block_t conf; uint8_t app_issuer_area[8]; -} PACKED picopass_ns_hdr; +} PACKED picopass_ns_hdr_t; typedef struct { @@ -384,6 +384,40 @@ typedef struct { bool off; } PACKED tearoff_params_t; +// when writing to SPIFFS +typedef struct { + bool append : 1; + uint16_t bytes_in_packet : 15; + uint8_t fnlen; + uint8_t fn[32]; + uint8_t data[]; +} PACKED flashmem_write_t; + +//----------------------------------------------------------------------------- +// ISO 7618 Smart Card +//----------------------------------------------------------------------------- +typedef struct { + uint8_t atr_len; + uint8_t atr[50]; +} PACKED smart_card_atr_t; + +typedef enum SMARTCARD_COMMAND { + SC_CONNECT = (1 << 0), + SC_NO_DISCONNECT = (1 << 1), + SC_RAW = (1 << 2), + SC_SELECT = (1 << 3), + SC_RAW_T0 = (1 << 4), + SC_CLEARLOG = (1 << 5), + SC_LOG = (1 << 6), +} smartcard_command_t; + +typedef struct { + uint8_t flags; + uint16_t len; + uint8_t data[]; +} PACKED smart_card_raw_t; + + // For the bootloader #define CMD_DEVICE_INFO 0x0000 //#define CMD_SETUP_WRITE 0x0001 @@ -565,6 +599,8 @@ typedef struct { #define CMD_LF_HITAGS_READ 0x0373 #define CMD_LF_HITAGS_WRITE 0x0375 +#define CMD_LF_HITAG_ELOAD 0x0376 + #define CMD_HF_ISO14443A_ANTIFUZZ 0x0380 #define CMD_HF_ISO14443B_SIMULATE 0x0381 #define CMD_HF_ISO14443B_SNIFF 0x0382 @@ -824,10 +860,10 @@ typedef struct { // Receiving from USART need more than 30ms as we used on USB // else we get errors about partial packet reception -// FTDI 9600 hw status -> we need 20ms -// FTDI 115200 hw status -> we need 50ms -// FTDI 460800 hw status -> we need 30ms -// BT 115200 hf mf fchk 1 dic -> we need 140ms +// FTDI 9600 hw status -> we need 20ms +// FTDI 115200 hw status -> we need 50ms +// FTDI 460800 hw status -> we need 30ms +// BT 115200 hf mf fchk --1k -f file.dic -> we need 140ms // all zero's configure: no timeout for read/write used. // took settings from libnfc/buses/uart.c diff --git a/pm3 b/pm3 index 6aadf2eb7..cd7b56b52 100755 --- a/pm3 +++ b/pm3 @@ -150,7 +150,15 @@ function get_pm3_list_Windows { # Normal SERIAL PORTS (COM) for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_9AC4&PID_4B8F%' Or PNPDeviceID LIKE '%VID_2D2D&PID_504D%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do DEV=${DEV/ */} - PM3LIST+=("$DEV") + #prevent soft bricking when using pm3-flash-all on an outdated bootloader + if [ $(basename -- "$0") = "pm3-flash-all" ]; then + if [ ! $(wmic /locale:ms_409 path Win32_SerialPort Where "DeviceID='$DEV'" Get PNPDeviceID 2>/dev/null | awk -b '/^USB/{print $1}') = "USB\VID_9AC4&PID_4B8F\ICEMAN" ]; then + echo -e "\033[0;31m[!] Using pm3-flash-all on an oudated bootloader, use pm3-flash-bootrom first!" + exit 1 + fi + fi + #Prioritise USB connections + PM3LIST=("$DEV" "${PM3LIST[@]}") if [ ${#PM3LIST[*]} -ge "$N" ]; then return fi @@ -192,10 +200,20 @@ function get_pm3_list_WSL { fi # 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 DeviceID" 2>/dev/null | sed -nr 's#^COM([0-9]+)\b#/dev/ttyS\1#p'); 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 | tr -dc '[:print:]'); do + _comport=$DEV + DEV=$(echo $DEV | sed -nr 's#^COM([0-9]+)\b#/dev/ttyS\1#p') # ttyS counterpart takes some more time to appear if [ -e "$DEV" ]; then - PM3LIST+=("$DEV") + #prevent soft bricking when using pm3-flash-all on an outdated bootloader + if [ $(basename -- "$0") = "pm3-flash-all" ]; then + if [ ! $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.DeviceID -eq '$_comport'} | Select -expandproperty PNPDeviceID" 2>/dev/null | tr -dc '[:print:]') = "USB\VID_9AC4&PID_4B8F\ICEMAN" ]; then + echo -e "\033[0;31m[!] Using pm3-flash-all on an oudated bootloader, use pm3-flash-bootrom first!" + exit 1 + fi + fi + #Prioritise USB connections + PM3LIST=("$DEV" "${PM3LIST[@]}") if [ ! -w "$DEV" ]; then echo "[!] Let's give users read/write access to $DEV" sudo chmod 666 "$DEV" diff --git a/tools/fpga_compress/Makefile b/tools/fpga_compress/Makefile index 4a97776b2..fb2f9d23d 100644 --- a/tools/fpga_compress/Makefile +++ b/tools/fpga_compress/Makefile @@ -12,8 +12,6 @@ MYCFLAGS += -DLZ4_MEMORY_USAGE=20 -Wno-redundant-decls -Wno-old-style-definitio BINS = fpga_compress -ROOT_DIR:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) - include ../../Makefile.host fpga_compress: $(OBJDIR)/fpga_compress.o $(MYOBJS) diff --git a/tools/hitag2crack/Makefile b/tools/hitag2crack/Makefile index c3e5cebc1..26f538110 100644 --- a/tools/hitag2crack/Makefile +++ b/tools/hitag2crack/Makefile @@ -1,6 +1,3 @@ -# Must be called before any Makefile include -ROOT_DIR:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) - include ../../Makefile.defs all clean install uninstall check: %: crack2/% crack3/% crack4/% crack5/% diff --git a/tools/hitag2crack/README.md b/tools/hitag2crack/README.md index cf6bffb8d..72fd6baa6 100644 --- a/tools/hitag2crack/README.md +++ b/tools/hitag2crack/README.md @@ -167,7 +167,7 @@ response pairs for the same tag UID. **TODO** will be ht2 sim or sniff with actual tag ? ``` -pm3 --> lf hitag sniff l +pm3 --> lf hitag sniff ``` It creates a file `Hitag2____collection.txt`. @@ -184,7 +184,7 @@ Attack 4 requires the same information as attack 3, but only 16-32 encrypted nonce and challenge response pairs are required. ``` -pm3 --> lf hitag sniff l +pm3 --> lf hitag sniff ``` It creates a file `Hitag2____collection.txt`. @@ -233,7 +233,7 @@ or $ ./ht2crack5opencl ``` -5opencl supports a number of additional parameters, see crack5opencl/README.md for details. +5opencl supports a number of additional parameters, see [crack5opencl/README.md](/tools/hitag2crack/crack5opencl/README.md) for details. Usage details: Next steps ------------------------- diff --git a/tools/hitag2crack/common/ht2crackutils.h b/tools/hitag2crack/common/ht2crackutils.h index 16f4fab5b..1ce71af1d 100644 --- a/tools/hitag2crack/common/ht2crackutils.h +++ b/tools/hitag2crack/common/ht2crackutils.h @@ -6,7 +6,9 @@ #include #include #include -#include +#ifndef __MINGW64__ +# include +#endif #include #include #include @@ -16,7 +18,7 @@ #define HEX_PER_ROW 16 -void writebuf(unsigned char *buf, uint64_t val, unsigned int len); +void writebuf(unsigned char *buf, uint64_t val, uint16_t len); void shexdump(unsigned char *data, int data_len); void printbin(unsigned char *c); void printbin2(uint64_t val, unsigned int size); diff --git a/tools/hitag2crack/crack5opencl/README.md b/tools/hitag2crack/crack5opencl/README.md index 1b7d05455..63c586679 100644 --- a/tools/hitag2crack/crack5opencl/README.md +++ b/tools/hitag2crack/crack5opencl/README.md @@ -44,12 +44,21 @@ Options: -V : enable debug messages -v : show the version -h : show this help +``` -Example, select devices 1, 2 and 3 using platform 1 and 2, with random queue engine: +Example, + +``` +simple + +./ht2crack5opencl 2ab12bf2 4B71E49D 6A606453 D79BD94B 16A2255B + +select devices 1, 2 and 3 using platform 1 and 2, with random queue engine: ./ht2crack5opencl -D 2 -Q 2 -p 1,2 -d 1,2,3 2ab12bf2 4B71E49D 6A606453 D79BD94B 16A2255B ``` + You can find the correct OpenCL Platform ID (-p) and Device ID (-d) with: ``` diff --git a/tools/hitag2crack/crack5opencl/ht2crack5opencl.c b/tools/hitag2crack/crack5opencl/ht2crack5opencl.c index 924932b2c..be70c9f9a 100644 --- a/tools/hitag2crack/crack5opencl/ht2crack5opencl.c +++ b/tools/hitag2crack/crack5opencl/ht2crack5opencl.c @@ -35,6 +35,18 @@ #include "hitag2.h" #include "dolphin_macro.h" +#if defined(__MINGW64__) +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000;\ + } \ + } while (0) +#endif + #define MAX_BITSLICES 32 #define VECTOR_SIZE (MAX_BITSLICES/8) @@ -1140,12 +1152,12 @@ int main(int argc, char **argv) { if (thread_count > 1) printf("[%zu] ", y); - printf("Key found @ slice %zu/%zu: ", t_arg[y].slice, t_arg[y].max_slices); + printf("\nKey found @ slice %zu/%zu: [", t_arg[y].slice, t_arg[y].max_slices); for (int i = 0; i < 6; i++) { printf("%02X", (uint8_t)(t_arg[y].key & 0xff)); t_arg[y].key = t_arg[y].key >> 8; } - printf("\n"); + printf(" ]\n"); fflush(stdout); break; } @@ -1166,7 +1178,7 @@ int main(int argc, char **argv) { printf("\nAttack 5 - opencl - end"); - if (show_overall_time) printf(" in %ld.%06ld second(s).\n\n", (long int)cpu_t_result.tv_sec, (long int)cpu_t_result.tv_usec); + if (show_overall_time) printf(" in %ld.%2ld second(s).\n\n", (long int)cpu_t_result.tv_sec, (long int)cpu_t_result.tv_usec); else printf("\n"); fflush(stdout); diff --git a/tools/hitag2crack/crack5opencl/threads.c b/tools/hitag2crack/crack5opencl/threads.c index 963425cff..f44861cc4 100644 --- a/tools/hitag2crack/crack5opencl/threads.c +++ b/tools/hitag2crack/crack5opencl/threads.c @@ -427,20 +427,6 @@ int thread_stop(thread_ctx_t *ctx) { return 0; } -__attribute__((format(printf, 1, 2))) -void tprintf(const char *restrict format, ...) { - flockfile(stdout); - - va_list va_args; - va_start(va_args, format); - vprintf(format, va_args); - va_end(va_args); - - funlockfile(stdout); - - fflush(stdout); -} - const char *thread_status_strdesc(thread_status_t s) { switch (s) { case TH_START: @@ -470,13 +456,13 @@ bool thread_setEnd(thread_ctx_t *ctx, thread_args_t *t_arg) { for (z = 0; z < ctx->thread_count; z++) { int m_ret = pthread_mutex_lock(&ctx->thread_mutexs[z]); if (m_ret != 0) { - tprintf("[%zu] [%s] Error: pthread_mutex_lock() failed (%d): %s\n", z, __func__, m_ret, strerror(m_ret)); + printf("[%zu] [%s] Error: pthread_mutex_lock() failed (%d): %s\n", z, __func__, m_ret, strerror(m_ret)); } thread_status_t tmp = t_arg[z].status; #if DEBUGME > 0 - tprintf("[%zu] [%s] Thread status: %s\n", z, __func__, thread_status_strdesc(t_arg[z].status)); + printf("[%zu] [%s] Thread status: %s\n", z, __func__, thread_status_strdesc(t_arg[z].status)); #endif if (tmp == TH_FOUND_KEY || tmp == TH_END || tmp == TH_ERROR) { @@ -486,19 +472,19 @@ bool thread_setEnd(thread_ctx_t *ctx, thread_args_t *t_arg) { } #if DEBUGME > 0 - tprintf("[%zu] [%s] Set thread status to TH_END\n", z, __func__); + printf("[%zu] [%s] Set thread status to TH_END\n", z, __func__); #endif t_arg[z].status = TH_END; if (tmp == TH_WAIT) { #if DEBUGME > 0 - tprintf("[%zu] [%s] Send cond_signal to thread\n", z, __func__); + printf("[%zu] [%s] Send cond_signal to thread\n", z, __func__); #endif c_ret = pthread_cond_signal(&ctx->thread_conds[z]); if (c_ret != 0) { - tprintf("[%zu] [%s] Error: pthread_cond_signal() failed (%d): %s\n", z, __func__, c_ret, strerror(c_ret)); + printf("[%zu] [%s] Error: pthread_cond_signal() failed (%d): %s\n", z, __func__, c_ret, strerror(c_ret)); } } @@ -529,17 +515,19 @@ void *computing_process(void *arg) { off = wu.off; a->slice = wu.id + 1; + float progress = (((wu.id + 1) * 100.0) / wu.max); if (ctx->queue_ctx.queue_type == QUEUE_TYPE_RANDOM) { + #if DEBUGME > 0 printf("[%zu] Slice %zu (off %zu), max %zu, remain %zu slice(s)\n", z, wu.id + 1, wu.off, wu.max, wu.rem); #else - printf("[%zu] Slice %zu/%zu (%zu remain)\n", z, wu.id + 1, wu.max, wu.rem); + printf("\r[%zu] Slice %zu/%zu (%zu remain) ( %2.1f%% )", z, wu.id + 1, wu.max, wu.rem, progress); #endif // DEBUGME } else { #if DEBUGME > 0 printf("[%zu] Slice %zu/%zu, off %zu\n", z, wu.id + 1, wu.max, wu.off); #else - printf("[%zu] Slice %zu/%zu\n", z, wu.id + 1, wu.max); + printf("\r[%zu] Slice %zu/%zu ( %2.1f%% )", z, wu.id + 1, wu.max, progress); #endif // DEBUGME } fflush(stdout); @@ -577,6 +565,7 @@ void *computing_process(void *arg) { } pthread_exit(NULL); + return NULL; } void *computing_process_async(void *arg) { @@ -684,17 +673,18 @@ void *computing_process_async(void *arg) { off = wu.off; a->slice = wu.id + 1; + float progress = (((wu.id + 1) * 100.0) / wu.max); if (ctx->queue_ctx.queue_type == QUEUE_TYPE_RANDOM) { #if DEBUGME > 0 printf("[%zu] Slice %zu (off %zu), max %zu, remain %zu slice(s)\n", z, wu.id + 1, wu.off, wu.max, wu.rem); #else - printf("[%zu] Slice %zu/%zu (%zu remain)\n", z, wu.id + 1, wu.max, wu.rem); + printf("[%zu] Slice %zu/%zu (%zu remain) ( %2.1f%% )", z, wu.id + 1, wu.max, wu.rem, progress); #endif // DEBUGME } else { #if DEBUGME > 0 printf("[%zu] Slice %zu/%zu, off %zu\n", z, wu.id + 1, wu.max, wu.off); #else - printf("[%zu] Slice %zu/%zu\n", z, wu.id + 1, wu.max); + printf("\r[%zu] Slice %zu/%zu ( %2.1f%% )", z, wu.id + 1, wu.max, progress); #endif // DEBUGME } @@ -874,4 +864,5 @@ void *computing_process_async(void *arg) { } while (status < TH_FOUND_KEY); pthread_exit(NULL); + return NULL; } diff --git a/tools/hitag2crack/crack5opencl/threads.h b/tools/hitag2crack/crack5opencl/threads.h index 0442a8f8c..6aa958fad 100644 --- a/tools/hitag2crack/crack5opencl/threads.h +++ b/tools/hitag2crack/crack5opencl/threads.h @@ -128,7 +128,6 @@ int thread_stop(thread_ctx_t *ctx); int thread_start_scheduler(thread_ctx_t *ctx, thread_args_t *t_arg, wu_queue_ctx_t *queue_ctx); bool thread_setEnd(thread_ctx_t *ctx, thread_args_t *t_arg); -void tprintf(const char *restrict format, ...); const char *thread_strerror(int error); const char *thread_status_strdesc(thread_status_t s); diff --git a/tools/mf_nonce_brute/Makefile b/tools/mf_nonce_brute/Makefile index cf565e72c..17bff27cc 100644 --- a/tools/mf_nonce_brute/Makefile +++ b/tools/mf_nonce_brute/Makefile @@ -8,7 +8,7 @@ ifneq ($(SKIPPTHREAD),1) MYLDLIBS += -lpthread endif -BINS = mf_nonce_brute +BINS = mf_nonce_brute mf_trace_brute INSTALLTOOLS = $(BINS) include ../../Makefile.host @@ -21,3 +21,5 @@ ifneq (,$(findstring MINGW,$(platform))) endif mf_nonce_brute : $(OBJDIR)/mf_nonce_brute.o $(MYOBJS) + +mf_trace_brute : $(OBJDIR)/mf_trace_brute.o $(MYOBJS) diff --git a/tools/mf_nonce_brute/README.md b/tools/mf_nonce_brute/README.md index c2e5f3e6f..6119412be 100644 --- a/tools/mf_nonce_brute/README.md +++ b/tools/mf_nonce_brute/README.md @@ -85,7 +85,7 @@ Example with parity (from this trace http://www.proxmark.org/forum/viewtopic.php ``` => ``` -./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 3e709c8a +./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 8e0e5db9 | | | | | | | | | +UID +nt_enc | +nr_enc +ar_enc | +at_enc | +encrypted next cmd +nt_par_err +at_par_err +at_par_err diff --git a/tools/mf_nonce_brute/mf_nonce_brute.c b/tools/mf_nonce_brute/mf_nonce_brute.c index 9891b99cc..e2bdff892 100644 --- a/tools/mf_nonce_brute/mf_nonce_brute.c +++ b/tools/mf_nonce_brute/mf_nonce_brute.c @@ -14,10 +14,17 @@ #include #include #include +#include #include "crapto1/crapto1.h" #include "protocol.h" #include "iso14443crc.h" +#define AEND "\x1b[0m" +#define _RED_(s) "\x1b[31m" s AEND +#define _GREEN_(s) "\x1b[32m" s AEND +#define _YELLOW_(s) "\x1b[33m" s AEND +#define _CYAN_(s) "\x1b[36m" s AEND + #define odd_parity(i) (( (i) ^ (i)>>1 ^ (i)>>2 ^ (i)>>3 ^ (i)>>4 ^ (i)>>5 ^ (i)>>6 ^ (i)>>7 ^ 1) & 0x01) // a global mutex to prevent interlaced printing from different threads @@ -42,24 +49,149 @@ typedef struct thread_args { bool ev1; } targs; +#define ENC_LEN (200) +typedef struct thread_key_args { + int thread; + int idx; + uint32_t uid; + uint32_t part_key; + uint32_t nt_enc; + uint32_t nr_enc; + uint16_t enc_len; + uint8_t enc[ENC_LEN]; // next encrypted command + a full read/write +} targs_key; + //------------------------------------------------------------------ -uint8_t cmds[] = { - ISO14443A_CMD_READBLOCK, - ISO14443A_CMD_WRITEBLOCK, - MIFARE_AUTH_KEYA, - MIFARE_AUTH_KEYB, - MIFARE_CMD_INC, - MIFARE_CMD_DEC, - MIFARE_CMD_RESTORE, - MIFARE_CMD_TRANSFER + +uint8_t cmds[8][2] = { + {ISO14443A_CMD_READBLOCK, 18}, + {ISO14443A_CMD_WRITEBLOCK, 18}, + {MIFARE_AUTH_KEYA, 0}, + {MIFARE_AUTH_KEYB, 0}, + {MIFARE_CMD_INC, 6}, + {MIFARE_CMD_DEC, 6}, + {MIFARE_CMD_RESTORE, 6}, + {MIFARE_CMD_TRANSFER, 0} }; int global_counter = 0; -int global_fin_flag = 0; int global_found = 0; int global_found_candidate = 0; +uint64_t global_candiate_key = 0; size_t thread_count = 2; +static int param_getptr(const char *line, int *bg, int *en, int paramnum) { + int i; + int len = strlen(line); + + *bg = 0; + *en = 0; + + // skip spaces + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + if (*bg >= len) { + return 1; + } + + for (i = 0; i < paramnum; i++) { + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + + if (line[*bg] == '\0') return 1; + } + + *en = *bg; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + + (*en)--; + + return 0; +} + +static 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; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; + continue; + } + + if (isxdigit(line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + +static void hex_to_buffer(const 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; + + char *tmp = (char *)buf; + size_t i; + memset(tmp, 0x00, hex_max_len); + + size_t max_len = (hex_len > hex_max_len) ? hex_max_len : hex_len; + + for (i = 0; i < max_len; ++i, tmp += 2 + spaces_between) { + sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + + for (size_t j = 0; j < spaces_between; j++) + sprintf(tmp + 2 + j, " "); + } + + i *= (2 + spaces_between); + + size_t mlen = min_str_len > i ? min_str_len : 0; + if (mlen > hex_max_len) + mlen = hex_max_len; + + for (; i < mlen; i++, tmp += 1) + sprintf(tmp, " "); + + // remove last space + *tmp = '\0'; + return; +} + +static char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + static char buf[100] = {0}; + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true); + return buf; +} + static uint16_t parity_from_err(uint32_t data, uint16_t par_err) { uint16_t par = 0; @@ -196,13 +328,33 @@ static bool candidate_nonce(uint32_t xored, uint32_t nt, bool ev1) { static bool checkValidCmd(uint32_t decrypted) { uint8_t cmd = (decrypted >> 24) & 0xFF; - for (int i = 0; i < sizeof(cmds); ++i) { - if (cmd == cmds[i]) + for (int i = 0; i < 8; ++i) { + if (cmd == cmds[i][0]) return true; } return false; } +static bool checkValidCmdByte(uint8_t *cmd, uint16_t n) { + + bool ok = false; + for (int i = 0; i < 8; ++i) { + if (cmd[0] == cmds[i][0]) { + + if (n >= 4) + ok = CheckCrc14443(CRC_14443_A, cmd, 4); + + if (cmds[i][1] > 0 && n >= cmds[i][1]) + ok = CheckCrc14443(CRC_14443_A, cmd + 4, cmds[i][1]); + + if (ok) { + return true; + } + } + } + return false; +} + static bool checkCRC(uint32_t decrypted) { uint8_t data[] = { (decrypted >> 24) & 0xFF, @@ -227,14 +379,12 @@ static void *brute_thread(void *arguments) { uint32_t p64 = 0; uint32_t count; - int found = 0; // TC == 4 ( // threads calls 0 ev1 == false // threads calls 0,1,2 ev1 == true for (count = args->idx; count < 0xFFFF; count += thread_count - 1) { - found = global_found; - if (found) { + if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { break; } @@ -265,8 +415,8 @@ static void *brute_thread(void *arguments) { #endif if (cmd_enc) { uint32_t decrypted = ks4 ^ cmd_enc; - printf("CMD enc(%08x)\n", cmd_enc); - printf(" dec(%08x)\t", decrypted); + printf("CMD enc( %08x )\n", cmd_enc); + printf(" dec( %08x ) ", decrypted); // check if cmd exists uint8_t isOK = checkValidCmd(decrypted); @@ -275,12 +425,12 @@ static void *brute_thread(void *arguments) { // Add a crc-check. isOK = checkCRC(decrypted); if (isOK == false) { - printf("<-- not a valid cmd\n"); + printf(_RED_("<-- not a valid cmd\n")); pthread_mutex_unlock(&print_lock); free(revstate); continue; } else { - printf("<-- Valid cmd\n"); + printf("<-- valid cmd\n"); } } @@ -293,32 +443,97 @@ static void *brute_thread(void *arguments) { free(revstate); if (args->ev1) { - printf("\nKey candidate: [%012" PRIx64 "]\n\n", key); + // if it was EV1, we know for sure xxxAAAAAAAA recovery + printf("\nKey candidate [ " _YELLOW_("....%08" PRIx64)" ]\n\n", key & 0xFFFFFFFF); __sync_fetch_and_add(&global_found_candidate, 1); } else { - printf("\nValid Key found: [%012" PRIx64 "]\n\n", key); + printf("\nKey candidate [ " _GREEN_("....%08" PRIx64) " ]\n\n", key & 0xFFFFFFFF); __sync_fetch_and_add(&global_found, 1); } + __sync_fetch_and_add(&global_candiate_key, key); //release lock pthread_mutex_unlock(&print_lock); + break; } } free(args); return NULL; } +static void *brute_key_thread(void *arguments) { + + struct thread_key_args *args = (struct thread_key_args *) arguments; + uint64_t key; + uint8_t local_enc[args->enc_len]; + memcpy(local_enc, args->enc, args->enc_len); + + for (uint64_t count = args->idx; count < 0xFFFF; count += thread_count) { + + if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { + break; + } + + key = (count << 32 | args->part_key); + + // Init cipher with key + struct Crypto1State *pcs = crypto1_create(key); + + // NESTED decrypt nt with help of new key + crypto1_word(pcs, args->nt_enc ^ args->uid, 1); + crypto1_word(pcs, args->nr_enc, 1); + crypto1_word(pcs, 0, 0); + crypto1_word(pcs, 0, 0); + + // decrypt 22 bytes + uint8_t dec[args->enc_len]; + for (int i = 0; i < args->enc_len; i++) + dec[i] = crypto1_byte(pcs, 0x00, 0) ^ local_enc[i]; + + crypto1_deinit(pcs); + + // check if cmd exists + uint8_t isOK = checkValidCmdByte(dec, args->enc_len); + if (isOK == false) { + continue; + } + + // lock this section to avoid interlacing prints from different threats + pthread_mutex_lock(&print_lock); + printf("\nenc: %s\n", sprint_hex_inrow_ex(local_enc, args->enc_len, 0)); + printf("dec: %s\n", sprint_hex_inrow_ex(dec, args->enc_len, 0)); + printf("\nValid Key found [ " _GREEN_("%012" PRIx64) " ]\n\n", key); + pthread_mutex_unlock(&print_lock); + __sync_fetch_and_add(&global_found, 1); + break; + } + free(args); + return NULL; +} + static int usage(void) { - printf(" syntax: mf_nonce_brute []\n\n"); - printf(" example: nt in trace = 8c! 42 e6! 4e!\n"); - printf(" nt = 8c42e64e\n"); - printf(" nt_par_err = 1011\n\n"); - printf("\n expected outcome:\n"); - printf(" KEY 0xFFFFFFFFFFFF == fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n"); + printf("\n"); + printf("syntax: mf_nonce_brute []\n\n"); + printf("how to convert trace data to needed input:\n"); + printf(" nt in trace = 8c! 42 e6! 4e!\n"); + printf(" nt = 8c42e64e\n"); + printf(" nt_par_err = 1011\n\n"); + printf("samples:\n"); + printf("\n"); + printf(" ./mf_nonce_brute fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n"); + printf("\n"); + printf("**** Possible key candidate ****\n"); + printf("Key candidate: [ffffffffffff]\n"); + printf("\n"); + printf(" ./mf_nonce_brute 96519578 d7e3c6ac 0011 cd311951 9da49e49 0010 2bb22e00 0100 a4f7f398ebdb4e484d1cb2b174b939d18b469f3fa5d9caab\n"); + printf("\n"); + printf("enc: A4F7F398EBDB4E484D1CB2B174B939D18B469F3FA5D9CAABBFA018EC7E0CC5721DE2E590F64BD0A5B4EFCE71\n"); + printf("dec: 30084A24302F8102F44CA5020500A60881010104763930084A24302F8102F44CA5020500A608810101047639\n"); + printf("Valid Key found: [3b7e4fd575ad]\n\n"); return 1; } int main(int argc, char *argv[]) { - printf("Mifare classic nested auth key recovery. Phase 1.\n"); + printf("\nMifare classic nested auth key recovery\n\n"); if (argc < 9) return usage(); @@ -331,21 +546,27 @@ int main(int argc, char *argv[]) { sscanf(argv[7], "%x", &at_enc); sscanf(argv[8], "%x", &at_par_err); - if (argc > 9) - sscanf(argv[9], "%x", &cmd_enc); + int enc_len = 0; + uint8_t enc[ENC_LEN] = {0}; // next encrypted command + a full read/write + if (argc > 9) { +// sscanf(argv[9], "%x", &cmd_enc); + param_gethex_to_eol(argv[9], 0, enc, sizeof(enc), &enc_len); + cmd_enc = (enc[0] << 24 | enc[1] << 16 | enc[2] << 8 | enc[3]); + } - printf("-------------------------------------------------\n"); - printf("uid:\t\t%08x\n", uid); - printf("nt encrypted:\t%08x\n", nt_enc); - printf("nt parity err:\t%04x\n", nt_par_err); - printf("nr encrypted:\t%08x\n", nr_enc); - printf("ar encrypted:\t%08x\n", ar_enc); - printf("ar parity err:\t%04x\n", ar_par_err); - printf("at encrypted:\t%08x\n", at_enc); - printf("at parity err:\t%04x\n", at_par_err); + printf("----------- " _CYAN_("Phase 1") " ------------------------\n"); + printf("uid.................. %08x\n", uid); + printf("nt encrypted......... %08x\n", nt_enc); + printf("nt parity err........ %04x\n", nt_par_err); + printf("nr encrypted......... %08x\n", nr_enc); + printf("ar encrypted......... %08x\n", ar_enc); + printf("ar parity err........ %04x\n", ar_par_err); + printf("at encrypted......... %08x\n", at_enc); + printf("at parity err........ %04x\n", at_par_err); - if (argc > 9) - printf("next cmd enc:\t%08x\n\n", cmd_enc); + if (argc > 9) { + printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc, enc_len, 0)); + } clock_t t1 = clock(); uint16_t nt_par = parity_from_err(nt_enc, nt_par_err); @@ -361,7 +582,8 @@ int main(int argc, char *argv[]) { thread_count = 2; #endif /* _WIN32 */ - printf("\nBruteforce using %zu threads to find encrypted tagnonce last bytes\n", thread_count); + printf("\nBruteforce using " _YELLOW_("%zu") " threads\n", thread_count); + printf("looking for the last bytes of the encrypted tagnonce\n"); pthread_t threads[thread_count]; @@ -390,14 +612,56 @@ int main(int argc, char *argv[]) { for (int i = 0; i < thread_count; ++i) pthread_join(threads[i], NULL); + t1 = clock() - t1; + printf("execution time %.2f sec\n", (float)t1 / 1000000.0); + + if (!global_found && !global_found_candidate) { printf("\nFailed to find a key\n\n"); + goto out; } - t1 = clock() - t1; - if (t1 > 0) - printf("Execution time: %.0f ticks\n", (float)t1); + if (enc_len < 4) { + printf("Too few next cmd bytes, skipping phase 2\n"); + goto out; + } + // reset thread signals + global_found = 0; + global_found_candidate = 0; + + printf("\n----------- " _CYAN_("Phase 2") " ------------------------\n"); + printf("uid.......... %08x\n", uid); + printf("partial key.. %08x\n", (uint32_t)(global_candiate_key & 0xFFFFFFFF)); + printf("nt enc....... %08x\n", nt_enc); + printf("nr enc....... %08x\n", nr_enc); + printf("next encrypted cmd: %s\n", sprint_hex_inrow_ex(enc, enc_len, 0)); + printf("\nlooking for the upper 16 bits of key\n"); + fflush(stdout); + + // threads + for (int i = 0; i < thread_count; ++i) { + struct thread_key_args *b = malloc(sizeof(struct thread_key_args)); + b->thread = i; + b->idx = i; + b->uid = uid; + b->part_key = (uint32_t)(global_candiate_key & 0xFFFFFFFF); + b->nt_enc = nt_enc; + b->nr_enc = nr_enc; + b->enc_len = enc_len; + memcpy(b->enc, enc, enc_len); + pthread_create(&threads[i], NULL, brute_key_thread, (void *)b); + } + + // wait for threads to terminate: + for (int i = 0; i < thread_count; ++i) + pthread_join(threads[i], NULL); + + if (!global_found && !global_found_candidate) { + printf("\nfailed to find a key\n\n"); + } + +out: // clean up mutex pthread_mutex_destroy(&print_lock); return 0; diff --git a/tools/mf_nonce_brute/mf_trace_brute.c b/tools/mf_nonce_brute/mf_trace_brute.c new file mode 100644 index 000000000..0217bcdee --- /dev/null +++ b/tools/mf_nonce_brute/mf_trace_brute.c @@ -0,0 +1,315 @@ +// +// bruteforce the upper 16bits of a partial key recovered from mf_nonce_brute. +// J-run's original idea was a two part recovery vector with first a offline trace and then online for 2 bytes. +// +// This idea is two use only offline, to recover a nested authentication key. +// Assumption, we get a read/write command after a nested auth, we need 22 bytes of data. +// Iceman, 2021, +// + +#define __STDC_FORMAT_MACROS + +#if !defined(_WIN64) +#if defined(_WIN32) || defined(__WIN32__) +# define _USE_32BIT_TIME_T 1 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "ctype.h" +#include +#include "crapto1/crapto1.h" +#include "protocol.h" +#include "iso14443crc.h" + +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; + +#define ENC_LEN (4 + 16 + 2) +//--------------------- define options here + +typedef struct thread_args { + int thread; + int idx; + uint32_t uid; + uint32_t part_key; + uint32_t nt_enc; + uint32_t nr_enc; + uint8_t enc[ENC_LEN]; // next encrypted command + a full read/write +} targs; + +//------------------------------------------------------------------ +uint8_t cmds[] = { + ISO14443A_CMD_READBLOCK, + ISO14443A_CMD_WRITEBLOCK, + MIFARE_AUTH_KEYA, + MIFARE_AUTH_KEYB, + MIFARE_CMD_INC, + MIFARE_CMD_DEC, + MIFARE_CMD_RESTORE, + MIFARE_CMD_TRANSFER +}; + +int global_counter = 0; +int global_fin_flag = 0; +int global_found = 0; +int global_found_candidate = 0; +size_t thread_count = 2; + +static int param_getptr(const char *line, int *bg, int *en, int paramnum) { + int i; + int len = strlen(line); + + *bg = 0; + *en = 0; + + // skip spaces + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + if (*bg >= len) { + return 1; + } + + for (i = 0; i < paramnum; i++) { + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + + if (line[*bg] == '\0') return 1; + } + + *en = *bg; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + + (*en)--; + + return 0; +} + +static 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; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; + continue; + } + + if (isxdigit(line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + +static void hex_to_buffer(const 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; + + char *tmp = (char *)buf; + size_t i; + memset(tmp, 0x00, hex_max_len); + + size_t max_len = (hex_len > hex_max_len) ? hex_max_len : hex_len; + + for (i = 0; i < max_len; ++i, tmp += 2 + spaces_between) { + sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + + for (size_t j = 0; j < spaces_between; j++) + sprintf(tmp + 2 + j, " "); + } + + i *= (2 + spaces_between); + + size_t mlen = min_str_len > i ? min_str_len : 0; + if (mlen > hex_max_len) + mlen = hex_max_len; + + for (; i < mlen; i++, tmp += 1) + sprintf(tmp, " "); + + // remove last space + *tmp = '\0'; + return; +} + +static char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + static char buf[100] = {0}; + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true); + return buf; +} + +static void *brute_thread(void *arguments) { + + //int shift = (int)arg; + struct thread_args *args = (struct thread_args *) arguments; + + uint64_t key; // recovered key candidate + int found = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs = &mpcs; + + uint8_t local_enc[ENC_LEN] = {0}; + memcpy(local_enc, args->enc, sizeof(local_enc)); + + for (uint64_t count = args->idx; count < 0xFFFF; count += thread_count) { + + found = global_found; + if (found) { + break; + } + + key = (count << 32 | args->part_key); + + // Init cipher with key + pcs = crypto1_create(key); + + // NESTED decrypt nt with help of new key +// if (args->use_nested) +// crypto1_word(pcs, args->nt_enc ^ args->uid, 1) ^ args->nt_enc; +// else + crypto1_word(pcs, args->nt_enc ^ args->uid, 1); + + crypto1_word(pcs, args->nr_enc, 1); + crypto1_word(pcs, 0, 0); + crypto1_word(pcs, 0, 0); + + // decrypt 22 bytes + uint8_t dec[ENC_LEN] = {0}; + for (int i = 0; i < ENC_LEN; i++) + dec[i] = crypto1_byte(pcs, 0x00, 0) ^ local_enc[i]; + + crypto1_deinit(pcs); + + if (CheckCrc14443(CRC_14443_A, dec, 4)) { + + // check crc-16 in the end + + if (CheckCrc14443(CRC_14443_A, dec + 4, 18)) { + + // lock this section to avoid interlacing prints from different threats + pthread_mutex_lock(&print_lock); + printf("\nValid Key found: [%012" PRIx64 "]\n", key); + + printf("enc: %s\n", sprint_hex_inrow_ex(local_enc, ENC_LEN, 0)); + printf(" xx crcA crcA\n"); + printf("dec: %s\n", sprint_hex_inrow_ex(dec, ENC_LEN, 0)); + pthread_mutex_unlock(&print_lock); + + __sync_fetch_and_add(&global_found, 1); + } + } + } + free(args); + return NULL; +} + +static int usage(void) { + printf(" syntax: mf_trace_brute []\n\n"); + return 1; +} + +int main(int argc, char *argv[]) { + printf("Mifare classic nested auth key recovery. Phase 2.\n"); + if (argc < 3) return usage(); + + uint32_t uid = 0; // serial number + uint32_t part_key = 0; // last 4 keys of key + uint32_t nt_enc = 0; // noncce tag + uint32_t nr_enc = 0; // nonce reader encrypted + + sscanf(argv[1], "%x", &uid); + sscanf(argv[2], "%x", &part_key); + sscanf(argv[3], "%x", &nt_enc); + sscanf(argv[4], "%x", &nr_enc); + + int enc_len = 0; + uint8_t enc[ENC_LEN] = {0}; // next encrypted command + a full read/write + param_gethex_to_eol(argv[5], 0, enc, sizeof(enc), &enc_len); + + printf("-------------------------------------------------\n"); + printf("uid.......... %08x\n", uid); + printf("partial key.. %08x\n", part_key); + printf("nt enc....... %08x\n", nt_enc); + printf("nr enc....... %08x\n", nr_enc); + printf("next encrypted cmd: %s\n", sprint_hex_inrow_ex(enc, ENC_LEN, 0)); + + clock_t t1 = clock(); + +#if !defined(_WIN32) || !defined(__WIN32__) + thread_count = sysconf(_SC_NPROCESSORS_CONF); + if (thread_count < 2) + thread_count = 2; +#endif /* _WIN32 */ + + printf("\nBruteforce using %zu threads to find upper 16bits of key\n", thread_count); + + pthread_t threads[thread_count]; + + // create a mutex to avoid interlacing print commands from our different threads + pthread_mutex_init(&print_lock, NULL); + + // threads + for (int i = 0; i < thread_count; ++i) { + struct thread_args *a = malloc(sizeof(struct thread_args)); + a->thread = i; + a->idx = i; + a->uid = uid; + a->part_key = part_key; + a->nt_enc = nt_enc; + a->nr_enc = nr_enc; + memcpy(a->enc, enc, sizeof(a->enc)); + pthread_create(&threads[i], NULL, brute_thread, (void *)a); + } + + // wait for threads to terminate: + for (int i = 0; i < thread_count; ++i) + pthread_join(threads[i], NULL); + + if (!global_found && !global_found_candidate) { + printf("\nFailed to find a key\n\n"); + } + + t1 = clock() - t1; + if (t1 > 0) + printf("Execution time: %.0f ticks\n", (float)t1); + + // clean up mutex + pthread_mutex_destroy(&print_lock); + return 0; +} diff --git a/tools/pm3_amii_bin2eml.pl b/tools/pm3_amii_bin2eml.pl index f15b0de75..83e9f195f 100755 --- a/tools/pm3_amii_bin2eml.pl +++ b/tools/pm3_amii_bin2eml.pl @@ -6,7 +6,7 @@ # # -samy kamkar 05/28/2017 # -# hf mf eload u FILENAME_MINUS_EML +# hf mf eload --ul FILENAME_MINUS_EML # hf 14a sim -t 7 -u UID # perl -lne 'chomp; s/\s+(\S+)$//;$f=$1;if($f=~s/-(\S+)//){$g=hex($1);}else{$g=hex($f)}$f=hex($f); for$m($f..$g){print "0x" . substr(unpack("H4",pack("n",$m)),1) ." => \"$_\","}' /tmp/game >> game2 @@ -684,7 +684,7 @@ print STDERR "\n"; $uid = uc $uid; #print STDERR "amiitool -d -k ../client/amiitool/key_retail.bin -i $input -o $input.decrypted\n"; $input =~ s/\....$//; -print STDERR "hf mf eload u $input\n"; +print STDERR "hf mfu eload --u $input\n"; print STDERR "hf 14a sim -t 7 -u $uid\n"; diff --git a/tools/pm3_mf7b_wipe.py b/tools/pm3_mf7b_wipe.py index 1bc0db4b9..d4ba6f197 100755 --- a/tools/pm3_mf7b_wipe.py +++ b/tools/pm3_mf7b_wipe.py @@ -27,11 +27,11 @@ # # Get first line from EML file (block0) and write it down using command # -# Place it here -# | -# | -# v -# hf mf wrbl 0 B FFFFFFFFFFFF 046E46AAA53480084400120111003113 +# Place it here +# | +# | +# v +# hf mf wrbl --blk 0 -b -k FFFFFFFFFFFF -d 046E46AAA53480084400120111003113 # # Now restore all the data using built-in restore command # @@ -73,15 +73,12 @@ # # - - - import subprocess # EML data var te get keys of EML_FILE_DATA = """PLACE RAW hf-mf-CARD_UID-dump.eml FILE CONTENT OF CURRENTLY LOADED CARD HERE""" # Change your device name here if it differs from the default Proxmark3 RDV4.0 -PROXMARK_BIN_EXEC_STRING = 'proxmark3 -c "%s" /dev/tty.usbmodemiceman1' +PROXMARK_BIN_EXEC_STRING = './pm3 -c "%s"' # Constants DEFAULT_ACCESS_BLOCK = "FFFFFFFFFFFFFF078000FFFFFFFFFFFF" F12_KEY = "FFFFFFFFFFFF" @@ -95,7 +92,7 @@ def exec_proxmark_cmd(command, retry = 2, input=""): proxmark_reply = rst.stdout.decode("utf-8") proxmark_status = proxmark_reply.splitlines()[-1:][0].strip() - if proxmark_status == "isOk:01": + if proxmark_status == "ok": return True, "Success: " + proxmark_status retry_c += 1 return False, "Error: %s , status %s" % (proxmark_reply.splitlines()[-2:][0], proxmark_status) @@ -128,19 +125,19 @@ for sector in sector_array: key_B = sector[3][-12:] for _block in range(0,4): if sector_array.index(sector) == 0 and block == 0: - write_status, verbose = exec_proxmark_cmd("hf mf wrbl %s B %s %s" % (block, key_B, sector[0])) + write_status, verbose = exec_proxmark_cmd("hf mf wrbl --blk %s -b -k %s -d %s" % (block, key_B, sector[0])) if not write_status: - write_status, verbose = exec_proxmark_cmd("hf mf wrbl %s A %s %s" % (block, key_A, sector[0])) + write_status, verbose = exec_proxmark_cmd("hf mf wrbl --blk %s -a -k %s -d %s" % (block, key_A, sector[0])) if not write_status: - write_status, verbose = exec_proxmark_cmd("hf mf wrbl %s A %s %s" % (block, F12_KEY, sector[0])) + write_status, verbose = exec_proxmark_cmd("hf mf wrbl --blk %s -a -k %s -d %s" % (block, F12_KEY, sector[0])) block_success[block] = verbose elif _block == 3: - write_status, verbose = exec_proxmark_cmd("hf mf wrbl %s B %s %s" % (block, key_B, DEFAULT_ACCESS_BLOCK)) + write_status, verbose = exec_proxmark_cmd("hf mf wrbl --blk %s -b -k %s -d %s" % (block, key_B, DEFAULT_ACCESS_BLOCK)) if not write_status: - write_status, verbose = exec_proxmark_cmd("hf mf wrbl %s A %s %s" % (block, key_A, DEFAULT_ACCESS_BLOCK)) + write_status, verbose = exec_proxmark_cmd("hf mf wrbl --blk %s -a -k %s -d %s" % (block, key_A, DEFAULT_ACCESS_BLOCK)) if not write_status: - write_status, verbose = exec_proxmark_cmd("hf mf wrbl %s A %s %s" % (block, F12_KEY, DEFAULT_ACCESS_BLOCK)) + write_status, verbose = exec_proxmark_cmd("hf mf wrbl --blk %s -a -k %s -d %s" % (block, F12_KEY, DEFAULT_ACCESS_BLOCK)) block_success[block] = verbose _block += 1 diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index da5ea56a5..c3e4afcd1 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -263,7 +263,8 @@ while true; do if $TESTALL || $TESTMFNONCEBRUTE; then echo -e "\n${C_BLUE}Testing mf_nonce_brute:${C_NC} ${MFNONCEBRUTEBIN:=./tools/mf_nonce_brute/mf_nonce_brute}" if ! CheckFileExist "mf_nonce_brute exists" "$MFNONCEBRUTEBIN"; then break; fi - if ! CheckExecute slow "mf_nonce_brute test" "$MFNONCEBRUTEBIN 9c599b32 5a920d85 1011 98d76b77 d6c6e870 0000 ca7e0b63 0111 3e709c8a" "Key.*: \[ffffffffffff\]"; then break; fi +# if ! CheckExecute slow "mf_nonce_brute test" "$MFNONCEBRUTEBIN 9c599b32 5a920d85 1011 98d76b77 d6c6e870 0000 ca7e0b63 0111 3e709c8a" "Key found \[.*ffffffffffff.*\]"; then break; fi + if ! CheckExecute slow "mf_nonce_brute test" "$MFNONCEBRUTEBIN 96519578 d7e3c6ac 0011 cd311951 9da49e49 0010 2bb22e00 0100 a4f7f398" "Key found \[.*3b7e4fd575ad.*\]"; then break; fi fi # hitag2crack not yet part of "all" # if $TESTALL || $TESTHITAG2CRACK; then @@ -337,6 +338,7 @@ while true; do if ! CheckExecute "proxmark help" "$CLIENTBIN -h" "wait"; then break; fi if ! CheckExecute "proxmark help text ISO7816" "$CLIENTBIN -t 2>&1" "ISO7816"; then break; fi if ! CheckExecute "proxmark help text hardnested" "$CLIENTBIN -t 2>&1" "hardnested"; then break; fi + if ! CheckExecute "proxmark full help dump" "$CLIENTBIN --fulltext 2>&1" "Full help dump done"; then break; fi echo -e "\n${C_BLUE}Testing data manipulation:${C_NC}" if ! CheckExecute "reveng readline test" "$CLIENTBIN -c 'reveng -h;reveng -D'" "CRC-64/GO-ISO"; then break; fi @@ -460,7 +462,7 @@ 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 1 000000000000'" "found:"; 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 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 ! $SLOWTESTS; then diff --git a/tools/recover_pk.py b/tools/recover_pk.py index 0f01337a7..470f406ae 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -102,7 +102,8 @@ def selftests(): 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, {'name': "DESFire EV2", 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", - "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"], + "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33", + "040D259A965B80","B158073A7100C88C3726F4299FA58311FC3CB18744686DE3F234928AD74578F5CAD7FCEC1DCB962ECC7CC000B8557B37F45B76DC6573A58F"], 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, {'name': "DESFire EV3", 'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6", @@ -146,6 +147,7 @@ def selftests(): for c in curvenames: for h in [None, "md5", "sha1", "sha256", "sha512"]: recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h) + if (len(recovered) == 1): pk = recovered.pop() pk = binascii.hexlify(pk).decode('utf8')