diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e035578b4..07d1832ce 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -70,7 +70,7 @@ jobs: env: V: 1 PLATFORM_EXTRAS: BTADDON - run: make + run: make -j$((`nproc` + 1)) - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a632a5e37..37d628655 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -50,14 +50,12 @@ jobs: - name: Build env: V: 1 - run: make + run: make -j$((`sysctl -n hw.ncpu` + 1)) - name: Test run: make check macos-make-btaddon: - if: always() - needs: [macos-make] runs-on: macos-latest steps: @@ -93,14 +91,12 @@ jobs: env: V: 1 PLATFORM_EXTRAS: BTADDON - run: make + run: make -j$((`sysctl -n hw.ncpu` + 1)) - name: Test run: make check macos-cmake: - if: always() - needs: [macos-make, macos-make-btaddon] runs-on: macos-latest steps: @@ -144,7 +140,7 @@ jobs: - name: Build env: VERBOSE: 1 - run: make + run: make -j$((`sysctl -n hw.ncpu` + 1)) working-directory: client/build/ - name: Test diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 9887be49f..d81ea5db2 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -41,14 +41,12 @@ jobs: - name: Build env: V: 1 - run: make + run: make -j$((`nproc` + 1)) - name: Test run: make check ubuntu-make-btaddon: - if: always() - needs: [ubuntu-make] runs-on: ubuntu-latest steps: @@ -74,14 +72,12 @@ jobs: env: V: 1 PLATFORM_EXTRAS: BTADDON - run: make + run: make -j$((`nproc` + 1)) - name: Test run: make check ubuntu-cmake: - if: always() - needs: [ubuntu-make, ubuntu-make-btaddon] runs-on: ubuntu-latest steps: @@ -115,7 +111,7 @@ jobs: - name: Build env: VERBOSE: 1 - run: make + run: make -j$((`nproc` + 1)) working-directory: client/build/ - name: Test diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3c093f96b..d913a35fc 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,7 +54,7 @@ jobs: run: make clean - name: Build - run: make V=1 + run: make -j $([System.Environment]::ProcessorCount + 1) V=1 - name: Test run: make check @@ -63,7 +63,7 @@ jobs: run: make clean - name: Build btaddon - run: make V=1 PLATFORM_EXTRAS=BTADDON + run: make -j $([System.Environment]::ProcessorCount + 1) V=1 PLATFORM_EXTRAS=BTADDON - name: Test btaddon run: make check @@ -84,7 +84,7 @@ jobs: working-directory: client/build/ - name: Build cmake - run: make VERBOSE=1 + run: make -j $([System.Environment]::ProcessorCount + 1) VERBOSE=1 working-directory: client/build/ - name: Test cmake @@ -141,7 +141,7 @@ jobs: run: make clean - name: Build - run: make V=1 + run: make -j$((`nproc` + 1)) V=1 - name: Test run: make check @@ -150,7 +150,7 @@ jobs: run: make clean - name: Build btaddon - run: make V=1 PLATFORM_EXTRAS=BTADDON + run: make -j$((`nproc` + 1)) V=1 PLATFORM_EXTRAS=BTADDON - name: Test btaddon run: make check @@ -171,7 +171,7 @@ jobs: working-directory: client/build/ - name: Build cmake - run: make VERBOSE=1 + run: make -j$((`nproc` + 1)) VERBOSE=1 working-directory: client/build/ - name: Test cmake diff --git a/CHANGELOG.md b/CHANGELOG.md index d82b128c5..46dfc8d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,61 @@ -# Change Log + Change Log 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] - Use proxmark3 as a generic smartcard reader with other software with `smart relay` (@gm3197) + - Added `tools\mfkeys\staticnested` - program to recover static nested keys (@iceman1001) + - Added `pm3_gen_dictionary.py` - python script to extract and save all keys from MFC dump files. (@iceman1001) + - Changed `hf mfu info` - now detect MIFARE Ultralight AES (@iceman1001) + - Changed `hf mf autopwn` - now supports multiple user supplied keys (@iceman1001) + - Added `hf mf gchpwd` command for change Gen4 GTU card access password (@merlokk) + - Added `--ms` option in `hw status` to specify the timeout of connection speed test (@wh201906) + - Added `hf mf ginfo` command for get info about Gen4 GTU configuration (@merlokk) + - Added support for loading Flipper PICOPASS dump files (@iceman1001) + - Fixed unknown chip identification (@jmichelp) + - Fixed `nfc decode` - now properly handles MFU dump files (@iceman1001) + - Added support for loading Flipper MCT/MFU dump files (@iceman1001) + - Changed `data bmap` - now default `-m` is 8 (@iceman1001) + - Added support for NTAG424 cards. (@dankar) + - Additional fixes to configcard code for keyroll mode based on nfc-iclass output (@Antiklesys) + - Changed lf sampling - improved the performance (@yah01) + - Added `bind` option for network connections to specify the outbound address and port (@wh201906) + - Changed `lf em 4x05 dump` - now supports the `--ns` nosave parameter (@iceman1001) + - Fixed some wrong synchronization waits in usb_write() to increase the communication speed (@wh201906) + - Added new command `data bmap` - breaks down a hexvalue to a binary template (@iceman1001) + - Changed aid_desfire.json - added entreis from the Metrodroid project (@iceman1001) + - Changed mad.json - added entries from the Metrodroid project (@iceman1001) + - Changed `hf iclass dump` - now allow no save of dumped data (@iceman1001) + - Changed `hf iclass calcnewkey` - Added calculations for old key elite and new key non elite (@Antiklesys) + - Changed the CLI prompt to show tcp/udp if used (@iceman1001) + - Changed `hw ping` - now shows transfer time (@doegox) + - Added `hf mf encodehid` - writes HID legacy credential to a empty MFC (@iceman1001) + - Added `hf iclass sam` - Added support for HID SAM Picopass communications (@iceman1001) + - Add support for quoted arguments in the CLI, allowing spaces in them which + are removed automatically (@jmichelp) + - Added UDP support on Windows (@wh201906) + - Added client communication timeout to preferences (@iceman1001) + - Added IPv6 support (@wh201906) + - Fixed `lf hid clone --bin` - now correctly handles sentinel bits (@iceman1001) + - Experimental UDP support in linux (@iceman1001, @wh201906) + - Changed CI scripts to speed up the builds (@wh201906) + - Changed the timeout of local TCP connections (@wh201906) + - Finalized implementation of configcard generation for keyroll when cardhelper is not present (@Antiklesys) + - Added documentation for compiling on iOS (@The-SamminAter) + - Fixed `hf iclass wrbl` - pagemap bit map for secured is now handled better (@iceman1001) + - Changed `hf iclass view/decrypt` to detect SIO lengths better and show if legacy credentials are encrypted (@nvx) + - Changed the json file formats for mfc, 14b, 15, legic, cryptorf, ndef (@iceman1001) + - Deprecated the EML file format when saving dump files. (@iceman1001) + - Added `sim014.bin` - new sim module firmware v4.42 with improved ISO7816 Protocol T0 support (@gentilkiwi) + - Added datasheet for sim module (@iceman1001) + - Changed `smart raw --timeout` - allows for a custom timeout (@iceman1001) + - Changed `lf t55 detectp1` - now also accepts 0xE039 Silicon Craft Tech as valid card (@iceman1001) - Fixed `utils.lua` library function "convertdectohex" wasn't working (@iceman1001) - Added `hf iclass creditepurse` command to allow crediting the epurse debit value (@nvx) + - Modified `hf iclass configcard` to only support online mode (@Antiklesys) + - Modified `hf iclass configcard` command to generate config cards without a cardhelper module by porting the contents of blocks 6 & 7 from nfc-iclass (@Antiklesys) + - Fixed `hf iclass info` command showing incorrectly in offline mode (@Antiklesys) + - The "doc/magic_cards_notes.md" file has been rebuilt, filled up, and so on. (@team-orangeBlue) ## [Raccoon.4.17140][2023-09-09] - Changed text and adjust pm3_test case for mf_aes_brute (@doegox) @@ -59,7 +109,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Fixed compiling liblua on iOS (@The-SamminAter) - Changed `hf_mf_luxeo_dump.lua` - now have list of keys to iterate (@iceman1001) - Fixed the timeout of TCP connections (@wh201906) - - Changed the connection timeout configurable (@wh201906) + - Added `hw timeout` - make the connection timeout configurable (@wh201906) ## [Seven.4.16717][2023-06-25] - Change `hf 14a info` - now identifes QL88 tags (@iceman1001) @@ -81,7 +131,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mfu pwdgen -r` - now generates pwd/pack for Philips Sonicare, thanks @ckuenzi, @atc1441 (@iceman1001) - Changed `hf mfu info` - now detects Philips Sonicare devices (@iceman1001) - Fixed truncated FPGA upload due to incorrect integer size variable (@d18c7db) - - Changed `usart btfactory` - handles the new BT board with version "BT SPP V3.0" (@iceman1001) + - Changed `usart btfactory` - handles the new BT board with version "BT SPP V3.0" (@iceman1001) - Changed `hf mf eview --sk` - now can extract keys and save to file (@iceman1001) - Changed `hf mf view --sk` - now can extract keys and save to file (@iceman1001) - Changed `hf mf sim` - reduce 6ms threshold to 4ms for reset to idle #1974 (@net147) @@ -108,7 +158,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf mf gdmcfg` - Support Gen4 GDM read configuration block (@iceman1001) - Changed magic note to include a section about GDM tags (@iceman1001) - Added `hf mf gdmsetblk` - Support Gen4 GDM write block (@iceman1001) - - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) + - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) - Changed CLI max string argument length limit from 512 to 4096 (@iceman1001) - Fixed `data asn1` - now handles bad input better (@iceman1001) - Added new public key for signature MIFARE Plus Troika (@iceman100) @@ -178,14 +228,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Fixed length check in sim module communications (@jmichelp) - Changed timings in i2c.c when communicating with sim module (@iceman1001) - Moved to non-deprecated API to initialize Python interpreter (@jmichelp) - - Changed `sc upgrade` updated firmware v4.13 (RDV40) - frame buffer is now 384 bytes (@sentiprox) + - Changed `sc upgrade` updated firmware v4.13 (RDV40) - frame buffer is now 384 bytes (@sentiprox) - Fixed contact interface / smartcard APDU chaining logic and allow 256 bytes ADPU payload. Need SIM firmware 4.13 to work (@jmichelp) - Fixed `lf hitag dump` - Should now work as described in the command help (@natmchugh) - Fixed SPI flash overflow when loading dictionnaries into flash. Breaking change: added 1 more sector for Mifare - dictionnaries should be loaded again (@jmichelp) - Added `hf mf gload, gsave, ggetblk, gsetblk` for Gen4 GTU in mifare classic mode (@DidierA) - Fixed `trace list -r` (relative times) not working unless `-u` (microseconds) was specified, and made `--frame` respect `-u` and `-r` options (@nvx) - Added detection of magic Gen4 GTU (@DidierA) - - Added luascript `hf_i2c_plus_2k_utils` - Script for dumping/modifying user memory of sectors 0 and 1 (@flamebarke) + - Added luascript `hf_i2c_plus_2k_utils` - Script for dumping/modifying user memory of sectors 0 and 1 (@flamebarke) - Added `hf mfu esave` - saves emulator memory to mfu dump file (@DidierA) - Added luascript `hf_mfu_ntag` - Script for configuring NTAG216 configuration pages (@flamebarke) - Changed `hf mf hardnested` - a detection for static encrypted nonces (@iceman1001) @@ -299,6 +349,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added new standalone mode `lf_em4100rsww` (@zabszk) - Fixed `hf 15 slixdisable` wrong pass id (@r1ddl3rz) - Added `script run hf_mf_hid_sim.lua` (@micsen) + - Added flashmem support in `HF_14BSNIFF` standalone mode (@wh201906) + - Changed `HF_14ASNIFF` standalone mode - now supports Proxmark3 without flashmem (@wh201906) ## [Frostbit.4.14831][2022-01-11] - Changed Wiegand format lookup - now case-insensitive (@iceman1001) diff --git a/Makefile b/Makefile index 64b0aa6ab..c86e765a6 100644 --- a/Makefile +++ b/Makefile @@ -34,7 +34,7 @@ all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfk #all clean install uninstall check: %: hitag2crack/% INSTALLTOOLS=pm3_eml2lower.sh pm3_eml2upper.sh pm3_mfdread.py pm3_mfd2eml.py pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py -INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt +INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt sim014.bin sim014.sha512.txt INSTALLSCRIPTS=pm3 pm3-flash pm3-flash-all pm3-flash-bootrom pm3-flash-fullimage INSTALLSHARES=tools/jtag_openocd traces INSTALLDOCS=doc/*.md doc/md diff --git a/README.md b/README.md index d65262674..89bacebf1 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,7 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the | Actions OSX CI | Actions Ubuntu CI | Actions Windows CI | |:--------------:|:------------------:|:------------------:| -| ![MacOS Build and Test](https://github.com/RfidResearchGroup/proxmark3/workflows/MacOS%20Build%20and%20Test/badge.svg?branch=master) | ![Ubuntu Build and Test](https://github.com/RfidResearchGroup/proxmark3/workflows/Ubuntu%20Build%20and%20Test/badge.svg?branch=master) | [![Windows Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml) | - +| [![MacOS Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/macos.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/macos.yml) | [![Ubuntu Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/ubuntu.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/ubuntu.yml) | [![Windows Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml) | # Table of Contents 1. [PROXMARK3 INSTALLATION AND OVERVIEW](#proxmark3-installation-and-overview) @@ -40,6 +39,7 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the | [macOS - Setup and Build](/doc/md/Installation_Instructions/macOS-Compile-From-Source-Instructions.md) || | [Windows - Setup and Build](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md) || | [Termux / Android - Setup and Build](/doc/termux_notes.md) || +| [iOS - Setup and Build](/doc/md/Installation_Instructions/iOS-Installation-Instructions.md) | [Blue Shark Manual](/doc/bt_manual_v10.md) | [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) | @@ -77,6 +77,7 @@ We define generic Proxmark3 platforms as following devices. - Ryscorp green PCB version - Radiowar black PCB version - numerous Chinese adapted versions of the RDV3 easy (kkmoon, PiSwords etc) + - Proxmark3 SE (Second edition) (BLE enabled) **Not supported** - ⚠ Proxmark Evolution (EVO) @@ -96,8 +97,6 @@ We define generic Proxmark3 platforms as following devices. - **Note**: unknown device hw - ⚠ Proxmark3 Ultimate - **Note**: unknown device hw -- ⚠ Proxmark3 SE - - **Note**: unknown device hw When it comes to these new unknown models we are depending on the community to report in if this repo works and what they did to make it work. @@ -185,6 +184,7 @@ This repo compiles nicely on - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian - Android / Termux - macOS / Homebrew (or MacPorts, experimental) / Apple Silicon M1 + - iOS (Jailbroken, rootful) - Docker container - [ 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/) diff --git a/armsrc/Makefile b/armsrc/Makefile index d6ab2bfc4..5f641884a 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -37,13 +37,13 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_HF = hfops.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c +SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_mfc.c sam_seos.c #UNUSED: mifaresniff.c SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c SRC_CRAPTO1 = crypto1.c des.c desfire_crypto.c mifaredesfire.c aes.c platform_util.c SRC_CRC = crc.c crc16.c crc32.c -SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c +SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c sam_picopass.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c SRC_NFCBARCODE = thinfilm.c diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 481475bbc..a47cf6517 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -69,7 +69,6 @@ #define HF_ICALSSS_READSIM_TEMP_MOD_BIN "iceclass-temp-mod.bin" #define HF_ICLASS_FULLSIM_MOD "iceclass-modified" #define HF_ICLASS_FULLSIM_MOD_BIN HF_ICLASS_FULLSIM_MOD".bin" -#define HF_ICLASS_FULLSIM_MOD_EML HF_ICLASS_FULLSIM_MOD".eml" #define HF_ICLASS_ATTACK_BIN "iclass_mac_attack" #define HF_ICLASS_CC_A "iceclass_cc_a.bin" @@ -117,10 +116,6 @@ 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_t *hdr) { - return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; -} - static uint8_t csns[8 * NUM_CSNS] = { 0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, 0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0, diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index f97a2e57f..b58c36856 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -28,9 +28,10 @@ #include "iso14443a.h" #include "protocols.h" #include "cmd.h" +#include "commonutil.h" void ModInfo(void) { - DbpString(" HF - Reading Visa cards & Emulating a Visa MSD Transaction(ISO14443) - (Salvador Mendoza)"); + DbpString(" HF - Reading VISA cards & Emulating a VISA MSD Transaction(ISO14443) - (Salvador Mendoza)"); } /* This standalone implements two different modes: reading and emulating. @@ -132,9 +133,15 @@ static uint8_t treatPDOL(const uint8_t *apdu) { void RunMod(void) { StandAloneMode(); - DbpString(_YELLOW_(">>") "Reading Visa cards & Emulating a Visa MSD Transaction a.k.a. MSDSal Started " _YELLOW_("<<")); + DbpString(""); + DbpString(_YELLOW_(">>>") " Reading VISA cards & Emulating a VISA MSD Transaction a.k.a. MSDSal Started " _YELLOW_("<<<")); + DbpString(""); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // free eventually allocated BigBuf memory but keep Emulator Memory + // also sets HIGH pointer of BigBuf enabling us to malloc w/o fiddling w the reserved emulator memory + BigBuf_free_keep_EM(); + //For reading process iso14a_card_select_t card_a_info; @@ -146,10 +153,32 @@ void RunMod(void) { 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00 }; - uint8_t visa[13] = { - 0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, - 0x00, 0x03, 0x10, 0x10, 0x00 + + uint8_t visa[] = { 0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00 }; + + + /* + uint8_t select_aid_hdr[5] = { 0x00, 0xA4, 0x04, 0x00, 0x00 }; + static const char* aid_list [] = { + "A00000000305076010", // VISA ELO Credit + "A0000000031010", // VISA Debit/Credit (Classic) + "A000000003101001", // VISA Credit + "A000000003101002", // VISA Debit + "A0000000032010", // VISA Electron + "A0000000032020", // VISA + "A0000000033010", // VISA Interlink + "A0000000034010", // VISA Specific + "A0000000035010", // VISA Specific + "A0000000036010", // Domestic Visa Cash Stored Value + "A0000000036020", // International Visa Cash Stored Value + "A0000000038002", // VISA Auth, VisaRemAuthen EMV-CAP (DPA) + "A0000000038010", // VISA Plus + "A0000000039010", // VISA Loyalty + "A000000003999910", // VISA Proprietary ATM + "A000000098", // Visa USA Debit Card + "A0000000980848", // Visa USA Debit Cardv }; + */ uint8_t processing [8] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00}; uint8_t sfi[5] = {0x00, 0xb2, 0x01, 0x0c, 0x00}; @@ -161,48 +190,49 @@ void RunMod(void) { bool existpdol; - // - MSD token card format - // - //Card number: 4412 3456 0578 1234 - //Expiration date: 17/11 - //Service code: 201 - //Discretionary data: 0000030000991 - //char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x12,0x34,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; + // Card number.............. 4412 3456 0578 1234 + // Expiration date.......... 17/11 + // Service code............. 201 + // Discretionary data....... 0000030000991 + // Pin verification value... 0000 + // CVV / iCvv............... 030 + // Trailing................. 000991 + + // 44 12 34 56 05 78 12 34 D 1711 2 01 00 00 03 00 00 99 1 + // char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x12,0x34,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; // // It is possible to initialize directly the emulation mode, having "token" with data and set "chktoken" = true ;) // char token[19] = {0x00}; bool chktoken = false; - // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it - // Such a response is less time critical, so we can prepare them on the fly -#define DYNAMIC_RESPONSE_BUFFER_SIZE 64 -#define DYNAMIC_MODULATION_BUFFER_SIZE 512 - // UID 4 bytes(could be 7 bytes if needed it) uint8_t flags = FLAG_4B_UID_IN_DATA; // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; + uint8_t visauid[7] = {0xE9, 0x66, 0x5D, 0x20}; memcpy(data, visauid, 4); // to initialize the emulation - uint8_t tagType = 11; // 11 = ISO/IEC 14443-4 - javacard (JCOP) tag_response_info_t *responses; + uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; - uint8_t pages = 0; // command buffers uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + + // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it + // Such a response is less time critical, so we can prepare them on the fly +#define DYNAMIC_RESPONSE_BUFFER_SIZE 64 +#define DYNAMIC_MODULATION_BUFFER_SIZE 512 + uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; - // to know the transaction status uint8_t prevCmd = 0; @@ -223,11 +253,11 @@ void RunMod(void) { // Checking if the user wants to go directly to emulation mode using a hardcoded track 2 if (chktoken == true && token[0] != 0x00) { state = STATE_EMU; - DbpString(_YELLOW_("[ ") "Initialized emulation mode" _YELLOW_(" ]")); - DbpString("\n"_YELLOW_("!!") "Waiting for a card reader..."); + DbpString("Initialized [ " _BLUE_("emulation mode") " ]"); + DbpString("Waiting for a card reader..."); } else { - DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); - DbpString("\n"_YELLOW_("!!") "Waiting for a Visa card..."); + DbpString("Initialized [ " _YELLOW_("reading mode") " ]"); + DbpString("Waiting for a VISA card..."); } for (;;) { @@ -240,20 +270,20 @@ void RunMod(void) { int button_pressed = BUTTON_HELD(1000); - if (button_pressed == BUTTON_HOLD) + if (button_pressed == BUTTON_HOLD) { break; - else if (button_pressed == BUTTON_SINGLE_CLICK) { + } else if (button_pressed == BUTTON_SINGLE_CLICK) { // pressing one time change between reading & emulation if (state == STATE_READ) { if (chktoken == true && token[0] != 0x00) { // only change to emulation if it saved a track 2 in memory state = STATE_EMU; - DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + DbpString("[ " _BLUE_("Emulation mode") " ]"); } else - DbpString(_YELLOW_("!!") "Nothing in memory to emulate"); + DbpString(_YELLOW_("Nothing in memory to emulate")); } else { state = STATE_READ; - DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + DbpString("[ " _YELLOW_("Reading mode") " ]"); } } @@ -261,35 +291,40 @@ void RunMod(void) { if (state == STATE_READ) { LED_A_ON(); - if (chktoken) + if (chktoken) { LED_C_ON(); + } iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { - DbpString(_YELLOW_("+") "Found ISO 14443 Type A!"); - for (uint8_t i = 0; i < 4; i++) { chktoken = false; LED_C_OFF(); LED_B_ON(); + + // add loop visa + // for (int i = 0; i < ARRAYLEN(AIDlist); i ++) { +// hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len); uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apduslen[i], false, apdubuffer, NULL); if (apdulen > 0) { - DbpString(_YELLOW_("[ ") "Proxmark command" _YELLOW_(" ]")); + DbpString("[ " _YELLOW_("Proxmark command") " ]"); Dbhexdump(apduslen[i], apdus[i], false); - DbpString(_GREEN_("[ ") "Card answer" _GREEN_(" ]")); + DbpString("[ " _GREEN_("Card answer") " ]"); Dbhexdump(apdulen - 2, apdubuffer, false); - DbpString("----"); + DbpString("-------------------------------"); for (uint8_t u = 0; u < apdulen; u++) { if (i == 1) { // check for PDOL if (apdubuffer[u] == 0x9F && apdubuffer[u + 1] == 0x38) { - for (uint8_t e = 0; e <= apdubuffer[u + 2]; e++) + + for (uint8_t e = 0; e <= apdubuffer[u + 2]; e++) { pdol[e] = apdubuffer[u + e + 2]; + } // generate a challenge plen = treatPDOL(pdol); @@ -309,25 +344,27 @@ void RunMod(void) { } if (i == 1) { - DbpString(_GREEN_("[ ") "Challenge generated" _GREEN_(" ]")); + DbpString("[ "_GREEN_("Challenge generated") " ]"); Dbhexdump(plen, existpdol ? ppdol : processing, false); } } else { - DbpString(_YELLOW_("!!") "Error reading the card"); + DbpString(_RED_("Error reading the card")); } LED_B_OFF(); } if (chktoken) { - DbpString(_RED_("[ ") "Track 2" _RED_(" ]")); + DbpString("[ " _GREEN_("Track 2") " ]"); Dbhexdump(19, (uint8_t *)token, false); - DbpString(_YELLOW_("!!") "Card number"); + DbpString("[ " _GREEN_("Card Number") " ]"); Dbhexdump(8, (uint8_t *)token, false); - DbpString("---"); + DbpString("-------------------------------"); + DbpString(""); + DbpString(""); LED_C_ON(); state = STATE_EMU; - DbpString(_YELLOW_("[ ") "Initialized emulation mode" _YELLOW_(" ]")); - DbpString("\n"_YELLOW_("!!") "Waiting for a card reader..."); + DbpString("Initialized [ " _BLUE_("emulation mode") " ]"); + DbpString("Waiting for a card reader..."); } } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -340,14 +377,15 @@ void RunMod(void) { // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + // tag type: 11 = ISO/IEC 14443-4 - javacard (JCOP) + if (SimulateIso14443aInit(11, flags, data, &responses, &cuid, NULL, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); - DbpString(_YELLOW_("!!") "Error initializing the emulation process!"); + DbpString(_RED_("Error initializing the emulation process!")); SpinDelay(500); state = STATE_READ; - DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); - DbpString("\n" _YELLOW_("!!") "Waiting for a Visa card..."); + DbpString("Initialized [ "_YELLOW_("reading mode") " ]"); + DbpString("Waiting for a VISA card..."); continue; } @@ -366,11 +404,12 @@ void RunMod(void) { for (;;) { LED_B_OFF(); // clean receive command buffer - if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { - DbpString(_YELLOW_("!!") "Emulator stopped"); + if (GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len) == false) { + DbpString("Emulator stopped"); retval = PM3_EOPABORTED; break; } + tag_response_info_t *p_response = NULL; LED_B_ON(); @@ -387,53 +426,49 @@ void RunMod(void) { // received a HALT } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { - //DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; // received a WAKEUP } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { - //DbpString(_YELLOW_("+") "WAKEUP Received"); prevCmd = 0; p_response = &responses[RESP_INDEX_ATQA]; // received request for UID (cascade 1) } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { - //DbpString(_YELLOW_("+") "Request for UID C1"); p_response = &responses[RESP_INDEX_UIDC1]; // received a SELECT (cascade 1) } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { - //DbpString(_YELLOW_("+") "Request for SELECT S1"); p_response = &responses[RESP_INDEX_SAKC1]; // received a RATS request } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { - DbpString(_YELLOW_("+") "Request for RATS"); prevCmd = 0; - //p_response = &responses[RESP_INDEX_RATS]; - - static uint8_t rRATS[] = { 0x13, 0x78, 0x80, 0x72, 0x02, 0x80, 0x31, 0x80, 0x66, 0xb1, 0x84, 0x0c, 0x01, 0x6e, 0x01, 0x83, 0x00, 0x90, 0x00 }; - - memcpy(&dynamic_response_info.response[0], rRATS, sizeof(rRATS)); - dynamic_response_info.response_n = sizeof(rRATS); + p_response = &responses[RESP_INDEX_RATS]; } else { - DbpString(_YELLOW_("[ ") "Card reader command" _YELLOW_(" ]")); - Dbhexdump(len, receivedCmd, false); + if (g_dbglevel == DBG_DEBUG) { + DbpString("[ "_YELLOW_("Card reader command") " ]"); + Dbhexdump(len, receivedCmd, false); + } - // emulate a Visa MSD(Magnetic stripe data) card + // emulate a Visa MSD (Magnetic stripe data) card if (receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) { dynamic_response_info.response[0] = receivedCmd[0]; // depending on card reader commands, the Proxmark will answer to fool the reader // respond with PPSE if (receivedCmd[2] == 0xA4 && receivedCmd[6] == 0x32 && prevCmd == 0) { + // need to adapt lengths.. uint8_t ppsea[39] = { + // 0x23 = 35, skip two first bytes then the message - SW 2 is 35 = 0x23 0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61, - 0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, - 0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00 + 0x0C, 0x4F, + // len aid0 aid1 aid2... + 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, + 0x87, 0x01, 0x01, 0x90, 0x00 }; memcpy(&dynamic_response_info.response[1], ppsea, sizeof(ppsea)); dynamic_response_info.response_n = sizeof(ppsea) + 1; @@ -442,10 +477,14 @@ void RunMod(void) { // respond Visa AID } else if (receivedCmd[2] == 0xA4 && receivedCmd[10] == 0x03 && receivedCmd[11] == 0x10 && prevCmd == 1) { uint8_t visauid_long[34] = { - 0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, - 0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56, - 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, - 0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, + // 0x1E = 30, skip two first bytes then the message - SW 2 is 30 = 0x1E + 0x6F, 0x1E, 0x84, + // len aid0 aid1 aid2.... + 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, + 0xA5, 0x13, 0x50, + // len V I S A C R E D I T + 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, + 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, 0x90, 0x00 }; memcpy(&dynamic_response_info.response[1], visauid_long, sizeof(visauid_long)); @@ -461,18 +500,21 @@ void RunMod(void) { // SFI } else if (receivedCmd[1] == 0x00 && receivedCmd[2] == 0xB2 && prevCmd == 3) { - uint8_t last[4] = {0x70, 0x15, 0x57, 0x13}; - uint8_t statusapdu[2] = {0x90, 0x00}; - uint8_t card[25]; - memcpy(&card[0], last, sizeof(last)); + uint8_t card[25] = { + 0x70, 0x15, 0x57, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x00 + }; + // add token array == Track 2 found before memcpy(&card[4], token, sizeof(token)); - memcpy(&card[23], statusapdu, sizeof(statusapdu)); + memcpy(&dynamic_response_info.response[1], card, sizeof(card)); dynamic_response_info.response_n = sizeof(card) + 1; prevCmd++; } else { - uint8_t finished[2] = {0x6f, 0x00}; + uint8_t finished[2] = {0x6F, 0x00}; memcpy(&dynamic_response_info.response[1], finished, sizeof(finished)); dynamic_response_info.response_n = sizeof(finished) + 1; if (prevCmd == 5) { @@ -480,7 +522,7 @@ void RunMod(void) { } } } else { - DbpString(_YELLOW_("!!") "Received unknown command!"); + DbpString(_RED_("Received unknown command!")); if (prevCmd < 4) { memcpy(dynamic_response_info.response, receivedCmd, len); dynamic_response_info.response_n = len; @@ -491,7 +533,7 @@ void RunMod(void) { } if (dynamic_response_info.response_n > 0) { - DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]")); + DbpString("[ " _GREEN_("Proxmark3 answer") " ]"); Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); DbpString("----"); @@ -501,7 +543,7 @@ void RunMod(void) { if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { SpinDelay(500); - DbpString(_YELLOW_("!!") "Error preparing Proxmark to answer!"); + DbpString(_RED_("Error preparing Proxmark to answer!")); continue; } p_response = &dynamic_response_info; @@ -511,13 +553,15 @@ void RunMod(void) { EmSendPrecompiledCmd(p_response); } } - switch_off(); + switch_off(); set_tracing(false); BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); } } - DbpString(_YELLOW_("[=]") "exiting"); + DbpString("Exit standalone mode!"); + DbpString(""); + SpinErr(15, 200, 3); LEDsoff(); } diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index cf290fa31..62fea749e 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -73,29 +73,25 @@ void ModInfo(void) { void RunMod() { StandAloneMode(); - Dbprintf(_YELLOW_(">>") "Relaying ISO/14443A data over Bluetooth a.k.a. reblay Started<<"); + DbpString(""); + Dbprintf(_YELLOW_(">>> ") " Relaying ISO/14443A data over Bluetooth a.k.a. reblay Started " _YELLOW_("<<<")); + DbpString(""); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); -// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it -// Such a response is less time critical, so we can prepare them on the fly -#define DYNAMIC_RESPONSE_BUFFER_SIZE 512 -#define DYNAMIC_MODULATION_BUFFER_SIZE 1024 - uint8_t flags = FLAG_4B_UID_IN_DATA; //UID 4 bytes(could be 7 bytes if needed it) - uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break - uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; + // UID 4 bytes(could be 7 bytes if needed it) + uint8_t flags = FLAG_4B_UID_IN_DATA; + // in case there is a read command received we shouldn't break + uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; + + uint8_t visauid[7] = {0xE9, 0x66, 0x5D, 0x20}; memcpy(data, visauid, 4); // to initialize the emulation - uint8_t tagType = 4; // 4 = ISO/IEC 14443-4 - javacard (JCOP) tag_response_info_t *responses; uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; - uint8_t pages = 0; - // For received Bluetooth package uint8_t rpacket[MAX_FRAME_SIZE] = { 0x00 }; @@ -126,6 +122,12 @@ void RunMod() { uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + +// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it +// Such a response is less time critical, so we can prepare them on the fly +#define DYNAMIC_RESPONSE_BUFFER_SIZE 512 +#define DYNAMIC_MODULATION_BUFFER_SIZE 1024 + uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; @@ -143,9 +145,9 @@ void RunMod() { uint8_t state = STATE_READ; if (state == STATE_READ) { - DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + DbpString("Initialized [ " _YELLOW_("reading mode") " ]"); } else { - DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + DbpString("Initialized [ " _BLUE_("emulation mode") " ]"); } for (;;) { @@ -162,10 +164,10 @@ void RunMod() { else if (button_pressed == BUTTON_SINGLE_CLICK) { // Pressing one time change between reading & emulation if (state == STATE_READ) { state = STATE_EMU; - DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + DbpString("[ " _BLUE_("Emulation mode") " ]"); } else { state = STATE_READ; - DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + DbpString("[ " _YELLOW_("Reading mode") " ]"); } } @@ -178,6 +180,7 @@ void RunMod() { iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { + LED_B_ON(); // Get data to send a ping with UID + ATQA + SAK @@ -208,7 +211,9 @@ void RunMod() { Dbhexdump(uidlen + 4, rdata, false); DbpString(_YELLOW_("[ ") "Sending ping" _YELLOW_(" ]")); + if (usart_writebuffer_sync(rdata, uidlen + 4) == PM3_SUCCESS) { + DbpString(_YELLOW_("[ ") "Sent!" _YELLOW_(" ]")); for (;;) { @@ -261,26 +266,31 @@ void RunMod() { // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + // 4 = ISO/IEC 14443-4 - javacard (JCOP) + if (SimulateIso14443aInit(4, flags, data, &responses, &cuid, NULL, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); - DbpString(_YELLOW_("!!") "Error initializing the emulation process!"); + DbpString(_RED_("Error initializing the emulation process!")); SpinDelay(500); state = STATE_READ; - DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); + DbpString("Initialized [ "_YELLOW_("reading mode") " ]"); continue; } // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); - int len = 0; // Command length - int retval = PM3_SUCCESS; // Check emulation status + // Command length + int len = 0; + // Check emulation status + int retval = PM3_SUCCESS; - uint8_t resp = 0; // Bluetooth response + // Bluetooth response + uint8_t resp = 0; lenpacket = 0; - uint8_t prevcmd = 0x00; // Keep track of last terminal type command + // Keep track of last terminal type command + uint8_t prevcmd = 0x00; clear_trace(); set_tracing(true); @@ -288,11 +298,12 @@ void RunMod() { for (;;) { LED_B_OFF(); // Clean receive command buffer - if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { - DbpString(_YELLOW_("!!") "Emulator stopped"); + if (GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len) == false) { + DbpString("Emulator stopped"); retval = PM3_EOPABORTED; break; } + tag_response_info_t *p_response = NULL; LED_B_ON(); @@ -314,46 +325,42 @@ void RunMod() { } } if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST -// DbpString(_YELLOW_("+") "REQUEST Received"); p_response = &responses[RESP_INDEX_ATQA]; } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT -// DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; resp = 0; } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP -// DbpString(_YELLOW_("+") "WAKEUP Received"); p_response = &responses[RESP_INDEX_ATQA]; resp = 0; } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) -// DbpString(_YELLOW_("+") "Request for UID C1"); p_response = &responses[RESP_INDEX_UIDC1]; } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) -// DbpString(_YELLOW_("+") "Request for SELECT S1"); p_response = &responses[RESP_INDEX_SAKC1]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request -// DbpString(_YELLOW_("+") "Request for RATS"); p_response = &responses[RESP_INDEX_RATS]; resp = 1; } else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension - DbpString(_YELLOW_("!!") "Reader accepted time extension!"); + DbpString(_YELLOW_("!!") " Reader accepted time extension!"); p_response = NULL; } else if ((receivedCmd[0] == 0xb2 || receivedCmd[0] == 0xb3) && len == 3) { //NACK - Request more time WTX - DbpString(_YELLOW_("!!") "NACK - time extension request?"); + DbpString(_YELLOW_("!!") " NACK - time extension request?"); if (resp == 2 && lenpacket == 0) { - DbpString(_YELLOW_("!!") "Requesting more time - WTX"); + DbpString(_YELLOW_("!!") " Requesting more time - WTX"); dynamic_response_info.response_n = 2; dynamic_response_info.response[0] = 0xf2; dynamic_response_info.response[1] = 0x0b; // Requesting the maximum amount of time } else if (lenpacket == 0) { - DbpString(_YELLOW_("!!") "NACK - ACK - Resend last command!"); // To burn some time as well + DbpString(_YELLOW_("!!") " NACK - ACK - Resend last command!"); // To burn some time as well dynamic_response_info.response[0] = 0xa3; dynamic_response_info.response_n = 1; } else { - DbpString(_YELLOW_("!!") "Avoiding request - Bluetooth data already in memory!!"); + DbpString(_YELLOW_("!!") " Avoiding request - Bluetooth data already in memory!!"); } } else { - DbpString(_GREEN_("[ ") "Card reader command" _GREEN_(" ]")); - Dbhexdump(len - 2, &receivedCmd[1], false); + if (g_dbglevel == DBG_DEBUG) { + DbpString("[ "_YELLOW_("Card reader command") " ]"); + Dbhexdump(len - 2, &receivedCmd[1], false); + } if ((receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) && len > 3) { // Process reader commands @@ -379,16 +386,17 @@ void RunMod() { } else { if (lenpacket == 0) { - DbpString(_YELLOW_("!!") "Received unknown command!"); + DbpString(_RED_("Received unknown command!")); memcpy(dynamic_response_info.response, receivedCmd, len); dynamic_response_info.response_n = len; } else { - DbpString(_YELLOW_("!!") "Avoiding unknown command - Bluetooth data already in memory!!"); + DbpString(_YELLOW_("!!") " Avoiding unknown command - Bluetooth data already in memory !!"); } } } + if (dynamic_response_info.response_n > 0) { - DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]")); + DbpString("[ " _GREEN_("Proxmark3 answer") " ]"); Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); DbpString("----"); if (lenpacket > 0) { @@ -402,7 +410,7 @@ void RunMod() { if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { Dbprintf(_YELLOW_("[ ") "Buffer size: %d "_YELLOW_(" ]"), dynamic_response_info.response_n); SpinDelay(500); - DbpString(_YELLOW_("!!") "Error preparing Proxmark to answer!"); + DbpString(_RED_("Error preparing Proxmark to answer!")); continue; } p_response = &dynamic_response_info; @@ -412,13 +420,15 @@ void RunMod() { EmSendPrecompiledCmd(p_response); } } - switch_off(); + switch_off(); set_tracing(false); BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); } } - DbpString(_YELLOW_("[=]") "exiting"); + DbpString("Exit standalone mode!"); + DbpString(""); + SpinErr(15, 200, 3); LEDsoff(); } diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 42d551d9b..b8823af3b 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -62,7 +62,9 @@ #include "crc16.h" #include "protocols.h" #include "mifareutil.h" - +#include "sam_picopass.h" +#include "sam_seos.h" +#include "sam_mfc.h" #ifdef WITH_LCD #include "LCD_disabled.h" @@ -367,11 +369,10 @@ static void print_debug_level(void) { // measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. // Note: this mimics GetFromBigbuf(), i.e. we have the overhead of the PacketCommandNG structure included. -static void printConnSpeed(void) { +static void printConnSpeed(uint32_t wait) { DbpString(_CYAN_("Transfer Speed")); Dbprintf(" Sending packets to client..."); -#define CONN_SPEED_TEST_MIN_TIME 500 // in milliseconds uint8_t *test_data = BigBuf_get_addr(); uint32_t start_time = GetTickCount(); uint32_t delta_time = 0; @@ -379,7 +380,7 @@ static void printConnSpeed(void) { LED_B_ON(); - while (delta_time < CONN_SPEED_TEST_MIN_TIME) { + while (delta_time < wait) { reply_ng(CMD_DOWNLOADED_BIGBUF, PM3_SUCCESS, test_data, PM3_CMD_DATA_SIZE); bytes_transferred += PM3_CMD_DATA_SIZE; delta_time = GetTickCountDelta(start_time); @@ -388,13 +389,15 @@ static void printConnSpeed(void) { Dbprintf(" Time elapsed................... %dms", delta_time); Dbprintf(" Bytes transferred.............. %d", bytes_transferred); - Dbprintf(" Transfer Speed PM3 -> Client... " _YELLOW_("%d") " bytes/s", 1000 * bytes_transferred / delta_time); + if (delta_time) { + Dbprintf(" Transfer Speed PM3 -> Client... " _YELLOW_("%llu") " bytes/s", 1000 * (uint64_t)bytes_transferred / delta_time); + } } /** * Prints runtime information about the PM3. **/ -static void SendStatus(void) { +static void SendStatus(uint32_t wait) { BigBuf_print_status(); Fpga_print_status(); #ifdef WITH_FLASH @@ -410,7 +413,7 @@ static void SendStatus(void) { #ifdef WITH_ISO14443a printHf14aConfig(); // HF 14a config #endif - printConnSpeed(); + printConnSpeed(wait); DbpString(_CYAN_("Various")); print_stack_usage(); @@ -1684,6 +1687,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_HF_MIFARE_EML_MEMCLR: { MifareEMemClr(); reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); break; } case CMD_HF_MIFARE_EML_MEMSET: { @@ -2038,6 +2042,21 @@ static void PacketReceived(PacketCommandNG *packet) { fwdata = NULL; break; } + + case CMD_HF_SAM_PICOPASS: { + sam_picopass_get_pacs(); + break; + } + case CMD_HF_SAM_SEOS: { +// sam_seos_get_pacs(); + break; + } + + case CMD_HF_SAM_MFC: { +// sam_mfc_get_pacs(); + break; + } + #endif #ifdef WITH_FPC_USART @@ -2645,7 +2664,10 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_STATUS: { - SendStatus(); + if (packet->length == 4) + SendStatus(packet->data.asDwords[0]); + else + SendStatus(CONN_SPEED_TEST_MIN_TIME_DEFAULT); break; } case CMD_TIA: { diff --git a/armsrc/appmain.h b/armsrc/appmain.h index 815245b04..fdc520f5f 100644 --- a/armsrc/appmain.h +++ b/armsrc/appmain.h @@ -37,6 +37,9 @@ int tearoff_hook(void); // ADC Vref = 3300mV, (240k-10M):240k voltage divider, 140800 mV #define MAX_ADC_LF_VOLTAGE 140800 +// Default connection speed test timeout, used in hw status +#define CONN_SPEED_TEST_MIN_TIME_DEFAULT 500 // in milliseconds + extern int ToSendMax; extern uint8_t ToSend[]; diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c index 218223231..5caaafb0f 100644 --- a/armsrc/dbprint.c +++ b/armsrc/dbprint.c @@ -76,11 +76,11 @@ void Dbprintf(const char *fmt, ...) { // prints HEX & ASCII void Dbhexdump(int len, const uint8_t *d, bool bAsci) { #if DEBUG - char ascii[9]; + char ascii[17]; while (len > 0) { - int l = (len > 8) ? 8 : len; + int l = (len > 16) ? 16 : len; memcpy(ascii, d, l); ascii[l] = 0; @@ -97,33 +97,41 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) { else Dbprintf("%*D", l, d, " "); - len -= 8; - d += 8; + len -= 16; + d += 16; } #endif } -void print_result(const char *name, const uint8_t *buf, size_t len) { +void print_result(const char *name, const uint8_t *d, size_t n) { - const uint8_t *p = buf; - uint16_t tmp = len & 0xFFF0; + const uint8_t *p = d; + uint16_t tmp = n & 0xFFF0; - for (; p - buf < tmp; p += 16) { + for (; p - d < tmp; p += 16) { Dbprintf("[%s: %02d/%02d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", name, - p - buf, - len, + p - d, + n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15] ); } - if (len % 16 != 0) { + + if (n % 16 != 0) { char s[46] = {0}; char *sp = s; - for (; p - buf < len; p++) { + for (; p - d < n; p++) { sprintf(sp, "%02x ", p[0]); sp += 3; } - Dbprintf("[%s: %02d/%02d] %s", name, p - buf, len, s); + Dbprintf("[%s: %02d/%02d] %s", name, p - d, n, s); + } +} + +// Prints message and hexdump +void print_dbg(char *msg, uint8_t *d, uint16_t n) { + if (g_dbglevel == DBG_DEBUG) { + print_result(msg, d, n); } } diff --git a/armsrc/dbprint.h b/armsrc/dbprint.h index 0795db274..4bfa6c89f 100644 --- a/armsrc/dbprint.h +++ b/armsrc/dbprint.h @@ -28,6 +28,7 @@ void Dbprintf(const char *fmt, ...); void DbprintfEx(uint32_t flags, const char *fmt, ...); void Dbhexdump(int len, const uint8_t *d, bool bAsci); void print_result(const char *name, const uint8_t *buf, size_t len); +void print_dbg(char *msg, uint8_t *d, uint16_t n); //void PrintToSendBuffer(void); #endif diff --git a/armsrc/hitag2crack.c b/armsrc/hitag2crack.c index ed7daecd5..bd5fc2a94 100644 --- a/armsrc/hitag2crack.c +++ b/armsrc/hitag2crack.c @@ -569,7 +569,7 @@ bool hitag2_keystream(uint8_t *response, uint8_t *nrarhex) { uint8_t *spaceptr = NULL; /* - keybits = malloc(2080); + keybits = calloc(2080, sizeof(uint8_t)); if (!keybits) { UserMessage("cannot malloc keybits\r\n"); return false; diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 016ae2f13..2848509a1 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +// //----------------------------------------------------------------------------- // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify @@ -53,9 +53,6 @@ static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { #define I2C_DELAY_2CLK I2CSpinDelayClk(2) #define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) -// The SIM module v4 supports up to 384 bytes for the length. -#define ISO7816_MAX_FRAME 270 - // try i2c bus recovery at 100kHz = 5us high, 5us low void I2C_recovery(void) { @@ -168,15 +165,15 @@ static bool WaitSCL_H_delay(uint32_t delay) { return false; } -// 5000 * 3.07us = 15350us. 15.35ms -// 15000 * 3.07us = 46050us. 46.05ms +// 5000 * 3.07us = 15350 us = 15.35 ms +// 15000 * 3.07us = 46050 us = 46.05 ms static bool WaitSCL_H(void) { return WaitSCL_H_delay(5000); } static bool WaitSCL_L_delay(uint32_t delay) { while (delay--) { - if (!SCL_read) { + if (SCL_read == false) { return true; } I2C_DELAY_1CLK; @@ -194,7 +191,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 = 200; + volatile uint32_t delay = 1200; while (delay--) { // exit on SCL LOW if (SCL_read == false) @@ -212,32 +209,41 @@ static bool I2C_Start(void) { SDA_H; I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) + if (WaitSCL_H() == false) { return false; + } I2C_DELAY_2CLK; - if (!SCL_read) + if (SCL_read == false) { return false; - if (!SDA_read) + } + + if (SDA_read == false) { return false; + } SDA_L; I2C_DELAY_2CLK; return true; } -static bool I2C_WaitForSim(void) { +static bool I2C_WaitForSim(uint32_t wait) { // wait for data from card - if (!WaitSCL_L_timeout()) + if (WaitSCL_L_timeout() == false) { return false; + } // 8051 speaks with smart card. - // 1000*50*3.07 = 153.5ms - // 1000*110*3.07 = 337.7ms + // 1000*50*3.07 = 153.5ms + // 1000*110*3.07 = 337.7ms (337700) + // 4 560 000 * 3.07 = 13999,2ms (13999200) // 1byte transfer == 1ms with max frame being 256bytes - return WaitSCL_H_delay(1000 * 110); + + // fct WaitSCL_H_delay uses a I2C_DELAY_1CLK in the loop with "wait" as number of iterations. + // I2C_DELAY_1CLK == I2CSpinDelayClk(1) = 3.07us + return WaitSCL_H_delay(wait); } // send i2c STOP @@ -248,7 +254,11 @@ static void I2C_Stop(void) { I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; - if (!WaitSCL_H()) return; + + if (WaitSCL_H() == false) { + return; + } + SDA_H; I2C_DELAY_2CLK; I2C_DELAY_2CLK; @@ -264,7 +274,11 @@ static void I2C_Ack(void) { I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; - if (!WaitSCL_H()) return; + + if (WaitSCL_H() == false) { + return; + } + SCL_L; I2C_DELAY_2CLK; } @@ -277,7 +291,11 @@ static void I2C_NoAck(void) { I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; - if (!WaitSCL_H()) return; + + if (WaitSCL_H() == false) { + return; + } + SCL_L; I2C_DELAY_2CLK; } @@ -288,8 +306,10 @@ static bool I2C_WaitAck(void) { SDA_H; I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) + + if (WaitSCL_H() == false) { return false; + } I2C_DELAY_2CLK; I2C_DELAY_2CLK; @@ -302,6 +322,7 @@ static bool I2C_WaitAck(void) { } static void I2C_SendByte(uint8_t data) { + uint8_t bits = 8; while (bits--) { @@ -319,8 +340,9 @@ static void I2C_SendByte(uint8_t data) { I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) + if (WaitSCL_H() == false) { return; + } I2C_DELAY_2CLK; } @@ -332,111 +354,131 @@ static int16_t I2C_ReadByte(void) { SDA_H; while (bits--) { + b <<= 1; SCL_L; - if (!WaitSCL_L()) return -2; + if (WaitSCL_L() == false) { + return -2; + } I2C_DELAY_1CLK; - SCL_H; - if (!WaitSCL_H()) return -1; + if (WaitSCL_H() == false) { + return -1; + } I2C_DELAY_1CLK; - if (SDA_read) + if (SDA_read) { b |= 0x01; + } } SCL_L; return b; } -// Sends one byte ( command to be written, SlaveDevice address) +// Sends one byte (command to be written, SlaveDevice address) bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); I2C_Stop(); - if (bBreak) { + + if (_break) { + if (g_dbglevel > 3) DbpString(I2C_ERROR); + return false; } return true; } -// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). +// Sends 1 byte data (data to be written, command to be written , SlaveDevice address) bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(data); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); I2C_Stop(); - if (bBreak) { + if (_break) { if (g_dbglevel > 3) DbpString(I2C_ERROR); return false; } return true; } -//Sends array of data (Array, length, command to be written , SlaveDevice address ). +// Sends array of data (array, length, command to be written , SlaveDevice address) // len = uint16 because we need to write up to 256 bytes bool I2C_BufferWrite(const uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } while (len) { I2C_SendByte(*data); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) break; len--; data++; } - if (len == 0) - bBreak = false; + if (len == 0) { + _break = false; + } + } while (false); I2C_Stop(); - if (bBreak) { + if (_break) { if (g_dbglevel > 3) DbpString(I2C_ERROR); return false; } @@ -447,132 +489,157 @@ bool I2C_BufferWrite(const uint8_t *data, uint16_t len, uint8_t device_cmd, uint // len = uint16 because we need to read up to 256bytes int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { - if (!data || len == 0) + // sanity check + if (data == NULL || len == 0) { return 0; + } + +// uint8_t *pd = data; // extra wait 500us (514us measured) // 200us (xx measured) WaitUS(600); - bool bBreak = true; - uint16_t readcount = 0; - uint16_t recv_len = 0; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return 0; + } // 0xB0 / 0xC0 == i2c write I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } // 0xB1 / 0xC1 == i2c read I2C_Start(); I2C_SendByte(device_address | 1); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); - if (bBreak) { + if (_break) { I2C_Stop(); if (g_dbglevel > 3) DbpString(I2C_ERROR); return 0; } + uint16_t readcount = 0; + uint16_t recv_len = 0; + while (len) { int16_t tmp = I2C_ReadByte(); - if (tmp < 0) + if (tmp < 0) { return tmp; + } *data = (uint8_t)tmp & 0xFF; len--; // Starting firmware v4 the length is encoded on the first two bytes. - // This only applies if command is I2C_DEVICE_CMD_READ. - if (device_cmd == I2C_DEVICE_CMD_READ) { - switch (readcount) { - case 0: - // Length (MSB) - recv_len = (*data) << 8; - break; - case 1: - // Length (LSB) - recv_len += *data; - // Adjust len if needed - if (len > recv_len) { - len = recv_len; - } - break; - default: - // Data byte received - data++; - break; + switch (readcount) { + case 0: { + // Length (MSB) + recv_len = (*data) << 8; + break; } - } else { - // Length is encoded on 1 byte - if ((readcount == 0) && (len > *data)) { - len = *data; - } else { + case 1: { + // Length (LSB) + recv_len += *data; + + // old packages.. + if (recv_len > 0x0200) { + // [0] = len + // [1] = data + recv_len >>= 8; + data++; + } + + // Adjust len if needed + if (len > recv_len) { + len = recv_len; + } + break; + } + default: { + // Data byte received data++; + break; } } + readcount++; // acknowledgements. After last byte send NACK. - if (len == 0) + if (len == 0) { I2C_NoAck(); - else + } else { I2C_Ack(); + } } I2C_Stop(); +// Dbprintf("rec len... %u readcount... %u", recv_len, readcount); +// Dbhexdump(readcount, pd, false); + + if (readcount < 2) { + return 0; + } + // return bytecount - bytes encoding length - return readcount - (device_cmd == I2C_DEVICE_CMD_READ ? 2 : 1); + return readcount - 2; } int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP - bool bBreak = true; + bool _break = true; uint8_t readcount = 0; // sending do { - if (!I2C_Start()) + if (I2C_Start() == false) { return 0; + } // 0xB0 / 0xC0 i2c write I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) break; I2C_SendByte(msb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(lsb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } // 0xB1 / 0xC1 i2c read I2C_Start(); I2C_SendByte(device_address | 1); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); - if (bBreak) { + if (_break) { I2C_Stop(); if (g_dbglevel > 3) DbpString(I2C_ERROR); return 0; @@ -582,8 +649,9 @@ int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t while (len) { int16_t tmp = I2C_ReadByte(); - if (tmp < 0) + if (tmp < 0) { return tmp; + } *data = (uint8_t)tmp & 0xFF; @@ -604,40 +672,47 @@ int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t bool I2C_WriteFW(const uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } // 0xB0 == i2c write I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(msb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(lsb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } while (len) { I2C_SendByte(*data); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; - + } len--; data++; } - if (len == 0) - bBreak = false; + if (len == 0) { + _break = false; + } + } while (false); I2C_Stop(); - if (bBreak) { + + if (_break) { if (g_dbglevel > 3) DbpString(I2C_ERROR); return false; } @@ -646,37 +721,40 @@ bool I2C_WriteFW(const uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uin void I2C_print_status(void) { DbpString(_CYAN_("Smart card module (ISO 7816)")); - uint8_t maj, min; - if (I2C_get_version(&maj, &min) == PM3_SUCCESS) { - Dbprintf(" version................. " _YELLOW_("v%x.%02d"), maj, min); - if (maj < 4) { - DbpString(" " _RED_("Outdated firmware.") " Please upgrade to v4.x or above."); - } + + uint8_t major, minor; + if (I2C_get_version(&major, &minor) == PM3_SUCCESS) { + + Dbprintf(" version................. v%d.%02d ( %s )" + , major + , minor + , ((major == 4) && (minor == 42)) ? _GREEN_("ok") : _RED_("Outdated") + ); } else { - DbpString(" version................. " _RED_("FAILED")); + DbpString(" version................. ( " _RED_("fail") " )"); } } -int I2C_get_version(uint8_t *maj, uint8_t *min) { +int I2C_get_version(uint8_t *major, uint8_t *minor) { uint8_t resp[] = {0, 0, 0, 0}; I2C_Reset_EnterMainProgram(); uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN); - if (len > 0) { - *maj = resp[0]; - *min = resp[1]; + if (len > 1) { + *major = resp[0]; + *minor = resp[1]; return PM3_SUCCESS; } return PM3_EDEVNOTSUPP; } // Will read response from smart card module, retries 3 times to get the data. -bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { +bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait) { - uint8_t i = 5; + uint8_t i = 10; int16_t len = 0; while (i--) { - I2C_WaitForSim(); + I2C_WaitForSim(wait); len = I2C_BufferRead(dest, *destlen, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); @@ -691,9 +769,9 @@ bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { } } - // after three - if (len <= 1) + if (len < 1) { return false; + } *destlen = len; return true; @@ -701,8 +779,10 @@ bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { - if (card_ptr == NULL) + if (card_ptr == NULL) { return false; + } + card_ptr->atr_len = 0; memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); @@ -711,15 +791,17 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { // start [C0 01] stop start C1 len aa bb cc stop] I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN); - //wait for sim card to answer. + // wait for sim card to answer. // 1byte = 1ms , max frame 256bytes. Should wait 256ms atleast just in case. - if (I2C_WaitForSim() == false) + if (I2C_WaitForSim(SIM_WAIT_DELAY) == false) { return false; + } // read bytes from module uint16_t len = sizeof(card_ptr->atr); - if (sc_rx_bytes(card_ptr->atr, &len) == false) + if (sc_rx_bytes(card_ptr->atr, &len, SIM_WAIT_DELAY) == false) { return false; + } if (len > sizeof(card_ptr->atr)) { len = sizeof(card_ptr->atr); @@ -803,16 +885,22 @@ void SmartCardRaw(const smart_card_raw_t *p) { } } - if ((flags & SC_RAW) || (flags & SC_RAW_T0)) { + if (((flags & SC_RAW) == SC_RAW) || ((flags & SC_RAW_T0) == SC_RAW_T0)) { + + uint32_t wait = SIM_WAIT_DELAY; + if ((flags & SC_WAIT) == SC_WAIT) { + wait = (uint32_t)((p->wait_delay * 1000) / 3.07); + } LogTrace(p->data, p->len, 0, 0, NULL, true); bool res = I2C_BufferWrite( p->data, p->len, - ((flags & SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), + (((flags & SC_RAW_T0) == SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), I2C_DEVICE_ADDRESS_MAIN ); + if (res == false && g_dbglevel > 3) { DbpString(I2C_ERROR); reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0); @@ -821,7 +909,7 @@ void SmartCardRaw(const smart_card_raw_t *p) { // read bytes from module len = ISO7816_MAX_FRAME; - res = sc_rx_bytes(resp, &len); + res = sc_rx_bytes(resp, &len, wait); if (res) { LogTrace(resp, len, 0, 0, NULL, false); } else { diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 6fa8b8809..52c96862e 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -30,6 +30,15 @@ #define I2C_DEVICE_CMD_GETVERSION 0x06 #define I2C_DEVICE_CMD_SEND_T0 0x07 +// The SIM module v4 supports up to 384 bytes for the length. +#define ISO7816_MAX_FRAME 270 + +// 8051 speaks with smart card. +// 1000*50*3.07 = 153.5ms +// 1 byte transfer == 1ms with max frame being 256 bytes +#define SIM_WAIT_DELAY 88000 // about 270ms delay // 109773 -- about 337.7ms delay + + void I2C_recovery(void); void I2C_init(bool has_ticks); void I2C_Reset(void); @@ -48,7 +57,7 @@ int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); bool I2C_WriteFW(const uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); -bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen); +bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait); // bool GetATR(smart_card_atr_t *card_ptr, bool verbose); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 953cfaa6f..405ad610f 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -36,9 +36,10 @@ #include "protocols.h" #include "ticks.h" #include "iso15693.h" -#include "iclass_cmd.h" /* iclass_card_select_t struct */ +#include "iclass_cmd.h" // iclass_card_select_t struct +#include "i2c.h" // i2c defines (SIM module access) -static uint8_t get_pagemap(const picopass_hdr_t *hdr) { +uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; } @@ -52,23 +53,6 @@ static uint8_t get_pagemap(const picopass_hdr_t *hdr) { #define ICLASS_16KS_SIZE 0x100 * 8 #endif -// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after -// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period. -// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating. -// 56,64us = 24 ssp_clk_cycles -#define DELAY_ICLASS_VCD_TO_VICC_SIM (140 - 26) // (140 - 24) - -// times in ssp_clk_cycles @ 3,3625MHz when acting as reader -#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER - -// times in samples @ 212kHz when acting as reader -#define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us -#define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms -#define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us - -#define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) - - /* * CARD TO READER * in ISO15693-2 mode - Manchester @@ -1245,7 +1229,7 @@ send: } // THE READER CODE -static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod) { +void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod) { CodeIso15693AsReader(frame, len); tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, start_time, shallow_mod); @@ -1784,7 +1768,6 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, return false; } - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (blockno == 2) { // check response. e-purse update swaps first and second half if (memcmp(data + 4, resp, 4) || memcmp(data, resp + 4, 4)) { @@ -1792,6 +1775,7 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, } } else if (blockno == 3 || blockno == 4) { // check response. Key updates always return 0xffffffffffffffff + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (memcmp(all_ff, resp, 8)) { return false; } @@ -1821,7 +1805,7 @@ void iClass_WriteBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr_t hdr = {0}; - uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod); + bool res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod); if (res == false) { goto out; } @@ -1881,8 +1865,9 @@ void iClass_WriteBlock(uint8_t *msg) { if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred res = false; switch_off(); - if (payload->req.send_reply) - reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t)); + if (payload->req.send_reply) { + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_ETEAROFF, (uint8_t *)&res, sizeof(bool)); + } return; } else { @@ -1901,16 +1886,16 @@ void iClass_WriteBlock(uint8_t *msg) { } // verify write - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if (pagemap == PICOPASS_SECURE_PAGEMODE && payload->req.blockno == 2) { + if ((pagemap != PICOPASS_NON_SECURE_PAGEMODE) && (payload->req.blockno == 2)) { // check response. e-purse update swaps first and second half if (memcmp(payload->data + 4, resp, 4) || memcmp(payload->data, resp + 4, 4)) { res = false; goto out; } - } else if (pagemap == PICOPASS_SECURE_PAGEMODE && (payload->req.blockno == 3 || payload->req.blockno == 4)) { + } else if ((pagemap != PICOPASS_NON_SECURE_PAGEMODE) && (payload->req.blockno == 3 || payload->req.blockno == 4)) { // check response. Key updates always return 0xffffffffffffffff - if (memcmp(all_ff, resp, 8)) { + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (memcmp(all_ff, resp, sizeof(all_ff))) { res = false; goto out; } @@ -1925,8 +1910,9 @@ void iClass_WriteBlock(uint8_t *msg) { out: switch_off(); - if (payload->req.send_reply) - reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); + if (payload->req.send_reply) { + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(bool)); + } } void iclass_credit_epurse(iclass_credit_epurse_t *payload) { @@ -1967,8 +1953,9 @@ void iclass_credit_epurse(iclass_credit_epurse_t *payload) { res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), epurse, sizeof(epurse), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); if (!res) { switch_off(); - if (payload->req.send_reply) + if (payload->req.send_reply) { reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETIMEOUT, (uint8_t *)&res, sizeof(uint8_t)); + } return; } @@ -1977,7 +1964,7 @@ void iclass_credit_epurse(iclass_credit_epurse_t *payload) { uint8_t epurse_offset = 0; const uint8_t empty_epurse[] = {0xff, 0xff, 0xff, 0xff}; - if (!memcmp(epurse, empty_epurse, 4)) { + if (memcmp(epurse, empty_epurse, 4) == 0) { // epurse data in stage 2 epurse_offset = 4; } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 363c042ab..30846ff36 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -21,6 +21,23 @@ #include "common.h" #include "iclass_cmd.h" +// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after +// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period. +// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating. +// 56,64us = 24 ssp_clk_cycles +#define DELAY_ICLASS_VCD_TO_VICC_SIM (140 - 26) // (140 - 24) + +// times in ssp_clk_cycles @ 3,3625MHz when acting as reader +#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER + + +// times in samples @ 212kHz when acting as reader +#define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us +#define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms +#define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us + +#define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) + void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void ReaderIClass(uint8_t flags); @@ -43,4 +60,7 @@ bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, ui bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time, bool shallow_mod); bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); + +uint8_t get_pagemap(const picopass_hdr_t *hdr); +void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 346669398..9de1e9a31 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1021,7 +1021,8 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } -bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { +bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_response_info_t **responses, + uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). static uint8_t rATQA[2] = { 0x00 }; @@ -1038,8 +1039,13 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // Prepare the optional third SAK (for 10 byte UID), drop the cascade bit static uint8_t rSAKc3[3] = { 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS + // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, + // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 + // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) + // TC(1) = 0x02: CID supported, NAD not supported // static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; - static uint8_t rRATS[] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; + static uint8_t rRATS[40] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; + uint8_t rRATS_len = 8; // GET_VERSION response for EV1/NTAG static uint8_t rVERSION[10] = { 0x00 }; @@ -1092,6 +1098,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r rATQA[1] = 0x03; sak = 0x20; memcpy(rRATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8); + rRATS_len = 8; break; } case 4: { // ISO/IEC 14443-4 - javacard (JCOP) @@ -1158,7 +1165,10 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r sak = 0x20; break; } - case 11: { // ISO/IEC 14443-4 - javacard (JCOP) + case 11: { // ISO/IEC 14443-4 - javacard (JCOP) / EMV + + memcpy(rRATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00", 19); + rRATS_len = 19; rATQA[0] = 0x04; sak = 0x20; break; @@ -1266,11 +1276,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r return false; } - // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, - // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 - // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) - // TC(1) = 0x02: CID supported, NAD not supported - AddCrc14A(rRATS, sizeof(rRATS) - 2); + AddCrc14A(rRATS, rRATS_len - 2); AddCrc14A(rPPS, sizeof(rPPS) - 2); @@ -1305,14 +1311,23 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r { .response = rPACK, .response_n = sizeof(rPACK) } // PACK response }; - // "precompile" responses. There are 12 predefined responses with a total of 84 bytes data to transmit. + // since rats len is variable now. + responses_init[RESP_INDEX_RATS].response_n = rRATS_len; + // "precompiled" responses. + // These exist for speed reasons. There are no time in the anti collision phase to calculate responses. + // There are 12 predefined responses with a total of 84 bytes data to transmit. + // // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) // 85 * 8 data bits, 85 * 1 parity bits, 12 start bits, 12 stop bits, 12 correction bits // 85 * 8 + 85 + 12 + 12 + 12 == 801 -#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 801 + // CHG: + // 85 bytes normally (rats = 8 bytes) + // 77 bytes + ratslen, - uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rRATS_len) * 8) + 77 + rRATS_len + 12 + 12 + 12) + + uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size uint8_t *free_buffer_pointer = free_buffer; size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; @@ -1328,7 +1343,6 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r } *responses = responses_init; - return true; } @@ -1362,12 +1376,16 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it // Such a response is less time critical, so we can prepare them on the fly #define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 512 - uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; - uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; + + uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); + uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE); tag_response_info_t dynamic_response_info = { .response = dynamic_response_buffer, .response_n = 0, @@ -1375,9 +1393,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ .modulation_n = 0 }; - // free eventually allocated BigBuf memory but keep Emulator Memory - BigBuf_free_keep_EM(); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 54aaa16bd..da2f9255e 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1060,7 +1060,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo if (dtf->len > dtf->max_len) { ret = PM3_EOVFLOW; - Dbprintf("overflow (%d > %d", dtf->len, dtf->max_len); + Dbprintf("overflow (%d > %d)", dtf->len, dtf->max_len); } break; } @@ -1083,7 +1083,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo if (dt->len > dt->max_len) { ret = PM3_EOVFLOW; - Dbprintf("overflow (%d > %d", dt->len, dt->max_len); + Dbprintf("overflow (%d > %d)", dt->len, dt->max_len); } break; } diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 6c131e7f3..c763bd0c6 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -128,20 +128,6 @@ sample_config *getSamplingConfig(void) { return &config; } -/** - * @brief Pushes bit onto the stream - * @param stream - * @param bit - */ -static void pushBit(BitstreamOut_t *stream, uint8_t bit) { - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = stream->position & 7; - *(stream->buffer + bytepos) &= ~(1 << (7 - bitpos)); - *(stream->buffer + bytepos) |= (bit > 0) << (7 - bitpos); - stream->position++; - stream->numbits++; -} - void initSampleBuffer(uint32_t *sample_size) { initSampleBufferEx(sample_size, false); } @@ -233,13 +219,20 @@ void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool data.numbits = samples.total_saved << 3; } else { - pushBit(&data, sample & 0x80); - if (bits_per_sample > 1) pushBit(&data, sample & 0x40); - if (bits_per_sample > 2) pushBit(&data, sample & 0x20); - if (bits_per_sample > 3) pushBit(&data, sample & 0x10); - if (bits_per_sample > 4) pushBit(&data, sample & 0x08); - if (bits_per_sample > 5) pushBit(&data, sample & 0x04); - if (bits_per_sample > 6) pushBit(&data, sample & 0x02); + // truncate trailing data + sample >>= 8 - bits_per_sample; + sample <<= 8 - bits_per_sample; + + uint8_t bits_offset = data.numbits & 0x7; + uint8_t bits_cap = 8 - bits_offset; + + // write the current byte + data.buffer[data.numbits >> 3] |= sample >> bits_offset; + int numbits = data.numbits + bits_cap; + + // write the remaining bits to the next byte + data.buffer[numbits >> 3] |= sample << (bits_cap); + data.numbits += bits_per_sample; } } diff --git a/armsrc/sam_mfc.c b/armsrc/sam_mfc.c new file mode 100644 index 000000000..090f4a781 --- /dev/null +++ b/armsrc/sam_mfc.c @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support MFC <-> SAM communication +//----------------------------------------------------------------------------- +#include "sam_mfc.h" +#include "sam_seos.h" +#include "iclass.h" + +#include "proxmark3_arm.h" +#include "cmd.h" diff --git a/armsrc/sam_mfc.h b/armsrc/sam_mfc.h new file mode 100644 index 000000000..5cf55d711 --- /dev/null +++ b/armsrc/sam_mfc.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_MFC_H +#define __SAM_MFC_H + +#include "common.h" + +#endif diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c new file mode 100644 index 000000000..fd465c992 --- /dev/null +++ b/armsrc/sam_picopass.c @@ -0,0 +1,447 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support Picopass <-> SAM communication +//----------------------------------------------------------------------------- +#include "sam_picopass.h" +#include "iclass.h" +#include "crc16.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "cmd.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "iso15693.h" +#include "protocols.h" +#include "optimized_cipher.h" +#include "fpgaloader.h" + +static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) { + + StartTicks(); + + bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD"); + goto out; + } + + *resplen = ISO7816_MAX_FRAME; + + res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD"); + goto out; + } + + if (*resplen < 2) { + DbpString("received too few bytes from SIM CARD"); + res = false; + goto out; + } + + uint16_t more_len = 0; + + if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) { + more_len = resp[*resplen - 1]; + } else { + // we done, return + goto out; + } + + // Don't discard data we already received except the SW code. + // If we only received 1 byte, this is the echo of INS, we discard it. + *resplen -= 2; + if (*resplen == 1) { + *resplen = 0; + } + + uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; + + res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD 2"); + goto out; + } + + more_len = 255 - *resplen; + + res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD 2"); + goto out; + } + + *resplen += more_len; + +out: + StopTicks(); + return res; +} + +// using HID SAM to authenticate w PICOPASS +int sam_picopass_get_pacs(void) { + + static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; + static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; + static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; + uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; + + picopass_hdr_t hdr = {0}; + // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used + // bit 7: parity. + // if (use_credit_key) + // read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; + + BigBuf_free_keep_EM(); + + clear_trace(); + + I2C_Reset_EnterMainProgram(); + StopTicks(); + + uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); + + bool shallow_mod = false; + uint16_t resp_len = 0; + int res; + uint32_t eof_time = 0; + + // wakeup + Iso15693InitReader(); + + uint32_t start_time = GetCountSspClk(); + iclass_send_as_reader(act_all, 1, &start_time, &eof_time, shallow_mod); + + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_ACTALL, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // send Identify + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(identify, 1, &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // copy the Anti-collision CSN to our select-packet + memcpy(&select[1], resp, 8); + + // select the card + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(select, sizeof(select), &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here, 8 byte CSN and 2 byte CRC + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store CSN + memcpy(hdr.csn, resp, sizeof(hdr.csn)); + + // card selected, now read config (block1) (only 8 bytes no CRC) + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, &eof_time, shallow_mod); + + // expect a 8-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store CONFIG + memcpy((uint8_t *)&hdr.conf, resp, sizeof(hdr.conf)); + + uint8_t pagemap = get_pagemap(&hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + res = PM3_EWRONGANSWER; + goto out; + } + + // read App Issuer Area block 5 + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store AIA + memcpy(hdr.app_issuer_area, resp, sizeof(hdr.app_issuer_area)); + + // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + + // expect a 8-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 8) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store EPURSE + memcpy(hdr.epurse, resp, sizeof(hdr.epurse)); + + // ----------------------------------------------------------------------------- + // SAM comms + // ----------------------------------------------------------------------------- + size_t sam_len = 0; + uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME); + + // ----------------------------------------------------------------------------- + // first + // a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0 + hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len); + memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn)); + sam_len += sizeof(hdr.csn); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 1", resp, resp_len); + + // ----------------------------------------------------------------------------- + // second + // a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04 + hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len); + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 2", resp, resp_len); + + // TAG response + // -- 0c 05 de64 // read block 5 + // Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4 + + // ----------------------------------------------------------------------------- + // third AIA block 5 + // a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000 + // picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5 + // picpoasss SE ff ff ff 00 06 ff ff ff f8 8e + hexstr_to_byte_array("a0da02631c140a00000000bd14a012a010800affffff0006fffffff88e81020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, hdr.app_issuer_area, sizeof(hdr.app_issuer_area)); + AddCrc(sam_apdu + 19, 8); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 3", resp, resp_len); + + // 88 02 -- readcheck (block2 epurse, start of auth) + // Tag|c00a140a000000a10ea10c8002 8802 8102 0004 820201f4 9000 + // 61 16 f5 0a140a000000a10ea10c 8002 8802 8102 0004 820201f4 9000 + + // ----------------------------------------------------------------------------- + // forth EPURSE + // a0da02631a140a00000000bd12a010a00e8008 ffffffffedffffff 81020000 + hexstr_to_byte_array("a0da02631a140a00000000bd12a010a00e8008ffffffffedffffff81020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, hdr.epurse, sizeof(hdr.epurse)); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 4", resp, resp_len); + + uint8_t nr_mac[9] = {0}; + memcpy(nr_mac, resp + 11, sizeof(nr_mac)); + // resp here hold the whole NR/MAC + // 05 9bcd475e965ee20e // CHECK (w key) + print_dbg("NR/MAC", nr_mac, sizeof(nr_mac)); + + // c00a140a000000a115a1138009 059bcd475e965ee20e 8102 0004 820201f4 9000 + + // pre calc ourself? + // uint8_t cc_nr[] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; + uint8_t div_key[8] = {0}; + static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; + iclass_calc_div_key(hdr.csn, legacy_aa1_key, div_key, false); + + uint8_t mac[4] = {0}; + if (g_dbglevel == DBG_DEBUG) { + uint8_t wb[16] = {0}; + memcpy(wb, hdr.epurse, sizeof(hdr.epurse)); + memcpy(wb + sizeof(hdr.epurse), nr_mac + 1, 4); + print_dbg("cc_nr...", wb, sizeof(wb)); + doMAC_N(wb, sizeof(wb), div_key, mac); + print_dbg("Calc MAC...", mac, sizeof(mac)); + } + + // start ssp clock again... + StartCountSspClk(); + + // NOW we auth against tag + uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; + memcpy(cmd_check + 1, nr_mac + 1, 8); + + start_time = GetCountSspClk(); + iclass_send_as_reader(cmd_check, sizeof(cmd_check), &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 4) { + res = PM3_ECARDEXCHANGE; + goto out; + } + // store MAC + memcpy(mac, resp, sizeof(mac)); + print_dbg("Got MAC", mac, sizeof(mac)); + + // ----------------------------------------------------------------------------- + // fifth send received MAC + // A0DA026316140A00000000BD0EA00CA00A8004 311E32E9 81020000 + hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, mac, sizeof(mac)); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 5", resp, resp_len); + + uint8_t tmp_p1[4] = {0}; + uint8_t tmp_p2[4] = {0}; + + // c161c10000a11aa118800e8702 ffffffff88ffffff 0a914eb981020004820236b09000 + + memcpy(tmp_p1, resp + 13, sizeof(tmp_p1)); + memcpy(tmp_p2, resp + 13 + 4, sizeof(tmp_p2)); + // ----------------------------------------------------------------------------- + // sixth send fake epurse update + // A0DA02631C140A00000000BD14A012A010800A 88FFFFFFFFFFFFFF9DE1 81020000 + hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A88FFFFFFFFFFFFFF9DE181020000", sam_apdu, &sam_len); + + memcpy(sam_apdu + 19, tmp_p2, sizeof(tmp_p1)); + memcpy(sam_apdu + 19 + 4, tmp_p1, sizeof(tmp_p1)); + AddCrc(sam_apdu + 19, 8); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 6", resp, resp_len); + // c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00 + + // read block 6 + StartCountSspClk(); + start_time = GetCountSspClk(); + iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("Block 6 from Picopass", resp, resp_len); + + // ----------------------------------------------------------------------------- + // eight send block 6 config to SAM + // A0DA02631C140A00000000BD14A012A010800A 030303030003E0174323 81020000 + hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, resp, resp_len); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 7", resp, resp_len); + + // c161c10000a110a10e8004 0606455681020004820201f49000 + + // read the credential blocks + StartCountSspClk(); + start_time = GetCountSspClk(); + iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("Block 6-9 from Picopass", resp, resp_len); + + // ----------------------------------------------------------------------------- + // nine send credential blocks to SAM + // A0DA026334140A00000000BD2CA02AA0288022 030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C 81020000 + hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, resp, resp_len); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 8", resp, resp_len); + + + // ----------------------------------------------------------------------------- + // TEN ask for PACS data + // A0DA02630C440A00000000BD04A0028200 + hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, resp, resp_len); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + print_dbg("-- 9 response", resp, resp_len); + if (memcmp(resp, "\xc1\x64\x00\x00\x00\xbd\x17\x8a\x15", 9) == 0) { + res = PM3_ENOPACS; + goto out; + } + + // c164000000bd098a07 030506951f9a00 9000 + uint8_t *pacs = BigBuf_calloc(resp[8]); + memcpy(pacs, resp + 9, resp[8]); + + print_dbg("-- 10 PACS data", pacs, resp[8]); + + reply_ng(CMD_HF_SAM_PICOPASS, PM3_SUCCESS, pacs, resp[8]); + res = PM3_SUCCESS; + goto off; + +out: + reply_ng(CMD_HF_SAM_PICOPASS, res, NULL, 0); + +off: + switch_off(); + BigBuf_free(); + return res; +} + +// HID SAM <-> MFC +// HID SAM <-> SEOS diff --git a/armsrc/sam_picopass.h b/armsrc/sam_picopass.h new file mode 100644 index 000000000..7feef0bde --- /dev/null +++ b/armsrc/sam_picopass.h @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_PICOPASS_H +#define __SAM_PICOPASS_H + +#include "common.h" + +int sam_picopass_get_pacs(void); + +#endif diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c new file mode 100644 index 000000000..00e4da45b --- /dev/null +++ b/armsrc/sam_seos.c @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support SEOS <-> SAM communication +//----------------------------------------------------------------------------- +#include "sam_seos.h" +#include "iclass.h" + +#include "proxmark3_arm.h" +#include "cmd.h" diff --git a/armsrc/sam_seos.h b/armsrc/sam_seos.h new file mode 100644 index 000000000..c2100f07e --- /dev/null +++ b/armsrc/sam_seos.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_SEOS_H +#define __SAM_SEOS_H + +#include "common.h" + +#endif diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index eaee2f0f0..f14b2d50f 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -279,6 +279,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifare4.c ${PM3_ROOT}/client/src/mifare/mifaredefault.c ${PM3_ROOT}/client/src/mifare/mifarehost.c + ${PM3_ROOT}/client/src/mifare/gen4.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/lrpcrypto.c ${PM3_ROOT}/client/src/mifare/desfirecrypto.c @@ -286,6 +287,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/desfirecore.c ${PM3_ROOT}/client/src/mifare/desfiretest.c ${PM3_ROOT}/client/src/mifare/gallaghercore.c + ${PM3_ROOT}/client/src/uart/ringbuffer.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui diff --git a/client/Makefile b/client/Makefile index b63fdf329..0d4f73928 100644 --- a/client/Makefile +++ b/client/Makefile @@ -371,12 +371,16 @@ ifeq ($(PYTHON_FOUND),1) endif ####################################################################################################### +# macOS doesn't like this params +#MYCFLAGS += --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 +MYCFLAGS += -O3 + CFLAGS ?= $(DEFCFLAGS) CFLAGS += $(MYDEFS) $(MYCFLAGS) $(MYINCLUDES) # We cannot just use CFLAGS+=... because it has impact on sub-makes if CFLAGS is defined in env: PM3CFLAGS = $(CFLAGS) -PM3CFLAGS += -g -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) +PM3CFLAGS += -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) # WIP Testing #PM3CFLAGS += -std=c11 -pedantic @@ -415,7 +419,7 @@ endif PM3CFLAGS += -DHAVE_SNPRINTF -CXXFLAGS ?= -Wall -Werror -O3 +CXXFLAGS ?= -Wall -Werror CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) PM3CXXFLAGS = $(CXXFLAGS) @@ -699,6 +703,7 @@ SRCS = mifare/aiddesfire.c \ mifare/mifare4.c \ mifare/mifaredefault.c \ mifare/mifarehost.c \ + mifare/gen4.c \ nfc/ndef.c \ pm3.c \ pm3_binlib.c \ @@ -707,6 +712,7 @@ SRCS = mifare/aiddesfire.c \ pm3line.c \ proxmark3.c \ scandir.c \ + uart/ringbuffer.c \ uart/uart_posix.c \ uart/uart_win32.c \ scripting.c \ diff --git a/client/atr_scrap_pcsctools.py b/client/atr_scrap_pcsctools.py index 9c4d1b4be..1f0ab3948 100755 --- a/client/atr_scrap_pcsctools.py +++ b/client/atr_scrap_pcsctools.py @@ -59,6 +59,7 @@ const char *getAtrInfo(const char *atr_str); // atr_t array is expected to be NULL terminated const static atr_t AtrTable[] = { { "3BDF18FFC080B1FE751F033078464646462026204963656D616E1D", "Cardhelper by 0xFFFF and Iceman" }, + { "3B90969181B1FE551FC7D4", "IClass SE Processor (Other) https://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor"}, """ C_FOOTER=""" {NULL, "N/A"} diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index e5e8f946c..0ea0bd33b 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -147,6 +147,7 @@ enum ParserState { PS_FIRST, PS_ARGUMENT, PS_OPTION, + PS_QUOTE, }; #define isSpace(c)(c == ' ' || c == '\t') @@ -195,6 +196,10 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab case PS_ARGUMENT: if (state == PS_FIRST) state = PS_ARGUMENT; + if (str[i] == '"') { + state = PS_QUOTE; + break; + } if (isSpace(str[i])) { spaceptr = bufptr; state = PS_FIRST; @@ -215,6 +220,16 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab *bufptr = str[i]; bufptr++; break; + case PS_QUOTE: + if (str[i] == '"') { + *bufptr++ = 0x00; + state = PS_FIRST; + } else { + if (isSpace(str[i]) == false) { + *bufptr++ = str[i]; + } + } + break; } if (bufptr > bufptrend) { PrintAndLogEx(ERR, "ERROR: Line too long\n"); diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 241581b69..100cfb4f0 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -141,6 +141,10 @@ F1D83F964314 FFF011223358 FF9F11223358 # +# Elevator system Kherson, Ukraine +AC37E76385F5 +576DCFFF2F25 +# # more Keys from mfc_default_keys.lua 000000000001 000000000002 @@ -966,7 +970,7 @@ D58660D1ACDE C01FC822C6E5 0854BF31111E # -# More keys +# More keys - Found 8A at Sebel Hotel in Canberra, Australia 8A19D40CF2B5 AE8587108640 # @@ -1029,6 +1033,10 @@ F8493407799D 6B8BD9860763 D3A297DC2698 # +# Data from reddit +34635A313344 +593367486137 +# # Keys from Mifare Classic Tool project 044CE1872BC3 045CECA15535 @@ -1829,11 +1837,17 @@ E19504C39461 FA1FBB3F0F1F FF16014FEFC7 # -# # Food GEM 6686FADE5566 # +# Samsung Data Systems (SDS) — Electronic Locks +# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks +# +# SDS Gen 2 S10 KB +C22E04247D9A +# # Data from Discord, French pool +# SDS Gen 2 S10 KA 9B7C25052FC3 494446555455 # @@ -2093,6 +2107,45 @@ D144BD193063 # Unknown hotel system Sec 0 / A 353038383134 # +# Brazil transport Sec 8 / A +50d4c54fcdf5 +# +# Bandai Namco Passport [fka Banapassport] / Sega Aime Card +# Dumped on the Flipper Devices Discord Server +6090D00632F5 +019761AA8082 +574343467632 +A99164400748 +62742819AD7C +CC5075E42BA1 +B9DF35A0814C +8AF9C718F23D +58CD5C3673CB +FC80E88EB88C +7A3CDAD7C023 +30424C029001 +024E4E44001F +ECBBFA57C6AD +4757698143BD +1D30972E6485 +F8526D1A8D6D +1300EC8C7E80 +F80A65A87FFA +DEB06ED4AF8E +4AD96BF28190 +000390014D41 +0800F9917CB0 +730050555253 +4146D4A956C4 +131157FBB126 +E69DD9015A43 +337237F254D5 +9A8389F32FBF +7B8FB4A7100B +C8382A233993 +7B304F2A12A6 +FC9418BF788B +# # Data from "the more the marriott" mifare project (colonelborkmundus) # aka The Horde # @@ -2225,3 +2278,13 @@ EDC317193709 A1670589B2AF # SF Hotel (SoMa area) 2E0F00700000 +# +# Unknown PACS from Western Australia +CA80E51FA52B +A71E80EA35E1 +05597810D63D +# +# Hotel Key from Las Vegas +EA0CA627FD06 +80BB8436024C +5044068C5183 \ No newline at end of file diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index a5caeb014..9dae9d64b 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -166,11 +166,12 @@ endif (NOT SKIPJANSSONSYSTEM EQUAL 1) if(EMBED_BZIP2) cmake_policy(SET CMP0114 NEW) set(BZIP2_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/bzip2/src/bzip2) + # Specify SOURCE_DIR will cause some errors ExternalProject_Add(bzip2 GIT_REPOSITORY https://android.googlesource.com/platform/external/bzip2 GIT_TAG platform-tools-30.0.2 PREFIX deps/bzip2 - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/bzip2 + # SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/bzip2 CONFIGURE_COMMAND mkdir -p ${BZIP2_BUILD_DIR} && git archive --format tar HEAD | tar -C ${BZIP2_BUILD_DIR} -x BUILD_IN_SOURCE ON BUILD_COMMAND make -C ${BZIP2_BUILD_DIR} -j4 CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} libbz2.a @@ -279,6 +280,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifare4.c ${PM3_ROOT}/client/src/mifare/mifaredefault.c ${PM3_ROOT}/client/src/mifare/mifarehost.c + ${PM3_ROOT}/client/src/mifare/gen4.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/lrpcrypto.c ${PM3_ROOT}/client/src/mifare/desfirecrypto.c @@ -286,6 +288,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/desfirecore.c ${PM3_ROOT}/client/src/mifare/desfiretest.c ${PM3_ROOT}/client/src/mifare/gallaghercore.c + ${PM3_ROOT}/client/src/uart/ringbuffer.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui @@ -656,6 +659,8 @@ if (MINGW) set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}") + # link Winsock2 + set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK}) endif (MINGW) # GCC 10 has issues with false positives on stringop-overflow, diff --git a/client/experimental_lib/example_c/01make_test.sh b/client/experimental_lib/example_c/01make_test.sh index 1ce6357ff..7bda5aa37 100755 --- a/client/experimental_lib/example_c/01make_test.sh +++ b/client/experimental_lib/example_c/01make_test.sh @@ -1,3 +1,4 @@ #!/bin/bash gcc -o test test.c -I../../include -lpm3rrg_rdv4 -L../build -lpthread +gcc -o test_grab test_grab.c -I../../include -lpm3rrg_rdv4 -L../build -lpthread diff --git a/client/experimental_lib/example_c/02run_test.sh b/client/experimental_lib/example_c/02run_test.sh index a3e4ec057..7da0e3700 100755 --- a/client/experimental_lib/example_c/02run_test.sh +++ b/client/experimental_lib/example_c/02run_test.sh @@ -1,3 +1,3 @@ #!/bin/bash -LD_LIBRARY_PATH=../build ./test +LD_LIBRARY_PATH=../build ./test /dev/ttyACM0 diff --git a/client/experimental_lib/example_c/02run_test_grab.sh b/client/experimental_lib/example_c/02run_test_grab.sh new file mode 100755 index 000000000..93545c422 --- /dev/null +++ b/client/experimental_lib/example_c/02run_test_grab.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +LD_LIBRARY_PATH=../build ./test_grab /dev/ttyACM0 diff --git a/client/experimental_lib/example_c/test.c b/client/experimental_lib/example_c/test.c index a3cf212dc..319b6b08e 100644 --- a/client/experimental_lib/example_c/test.c +++ b/client/experimental_lib/example_c/test.c @@ -1,8 +1,14 @@ +#include +#include #include "pm3.h" int main(int argc, char *argv[]) { + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(-1); + } pm3 *p; - p = pm3_open("/dev/ttyACM0"); + p = pm3_open(argv[1]); pm3_console(p, "hw status"); pm3_close(p); } diff --git a/client/experimental_lib/example_c/test_grab.c b/client/experimental_lib/example_c/test_grab.c new file mode 100644 index 000000000..c785361ba --- /dev/null +++ b/client/experimental_lib/example_c/test_grab.c @@ -0,0 +1,74 @@ +#include "pm3.h" +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + + int pipefd[2]; + char buf[8196 + 1]; + size_t n; + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(-1); + } + + if (pipe(pipefd) == -1) { + exit(-1); + } + + int pid = fork(); + if (pid == -1) { + perror("fork"); + exit(-1); + } + + // child + if (pid == 0) { + printf("[INFO] inside child\n"); + + // Redirect stdout to the write end of the pipe + dup2(pipefd[1], STDOUT_FILENO); + + close(pipefd[0]); // Child: close read end of the pipe + close(pipefd[1]); // Close original write end + + pm3 *p; + p = pm3_open(argv[1]); + + // Execute the command + pm3_console(p, "hw status"); + pm3_close(p); + _exit(-1); + } else { + + printf("[INFO] inside parent\n"); + // Parent: close write end of the pipe + close(pipefd[1]); + + // Read from the pipe + while (1) { + n = read(pipefd[0], buf, sizeof(buf)); + if (n == -1) { + continue; + } + if (n == 0) { + break; + } else { + // null termination + buf[n] = 0; + if (strstr(buf, "ERROR") != NULL) { + printf("%s", buf); + } + if (strstr(buf, "Unique ID") != NULL) { + printf("%s", buf); + } + } + } + + // Close read end + close(pipefd[0]); + } +} diff --git a/client/luascripts/hf_mf_sim_hid.lua b/client/luascripts/hf_mf_sim_hid.lua index 4091a10b5..3f3933872 100644 --- a/client/luascripts/hf_mf_sim_hid.lua +++ b/client/luascripts/hf_mf_sim_hid.lua @@ -7,7 +7,7 @@ local ansicolors = require('ansicolors') copyright = '' author = "Michael Micsen" -version = 'v0.0.1' +version = 'v0.0.2' desc = [[ Perform simulation of Mifare credentials with HID encoding This script only supports: H10301 @@ -17,12 +17,12 @@ example = [[ script run hf_mf_sim_hid.lua -f 1 -c 10000 ]] usage = [[ -script run hf_mf_sim_hid.lua -f facility -c card_number +script run hf_mf_sim_hid.lua -f -c ]] arguments = [[ -h : this help - -f : facility id - -c : starting card id + -f : facility code + -c : card number ]] local DEBUG = true --local bxor = bit32.bxor @@ -126,7 +126,6 @@ local function cardHex(i, f) sentinel = lshift(1, 26) bits = bor(bits, sentinel) - return ('%08x'):format(bits) end --- @@ -146,15 +145,14 @@ local function main(args) if o == 'h' then return help() end if o == 'f' then if isempty(a) then - print('You did not supply a facility code, using 0') + print('Defaulting to facility code 0') facility = 0 else facility = a end end if o == 'c' then - print(a) - if isempty(a) then return oops('You must supply the flag -c (card number)1') end + if isempty(a) then return oops('You must supply a card number') end cardnum = a end end @@ -162,23 +160,27 @@ local function main(args) --Due to my earlier complaints about how this specific getopt library --works, specifying ':' does not enforce supplying a value, thus we --need to do these checks all over again. - if isempty(cardnum) then return oops('You must supply the flag -c (card number)2') end + if isempty(cardnum) then return oops('You must supply a card number') end + --If the facility ID is non specified, ensure we code it as zero if isempty(facility) then - print('Using 0 for the facility code as -f was not supplied') + print('Defaulting to facility code 0') facility = 0 end -- Write the MAD to read for a Mifare HID credential - core.console('hf mf esetblk -b 1 -d 1B014D48000000000000000000000000') - core.console('hf mf esetblk -b 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A') + core.console('hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000') + core.console('hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A') --Write the sector trailer for the credential sector - core.console('hf mf esetblk -b 7 -d 484944204953787788AA204752454154') + core.console('hf mf esetblk --blk 7 -d 484944204953787788AA204752454154') local cardh = cardHex(cardnum, facility) - print('Hex') - print(cardh) - core.console( ('hf mf esetblk -b 5 -d 020000000000000000000000%s'):format(cardh) ) + print('Facility Code... ' .. facility) + print('Card number..... ' .. cardnum) + print('Hex............. ' .. cardh) + print('') + + core.console( ('hf mf esetblk --blk 5 -d 020000000000000000000000%s'):format(cardh) ) core.console('hf mf sim --1k -i') end diff --git a/client/luascripts/hf_mf_tnp3_sim.lua b/client/luascripts/hf_mf_tnp3_sim.lua index d44a883fd..f04c8d7fa 100644 --- a/client/luascripts/hf_mf_tnp3_sim.lua +++ b/client/luascripts/hf_mf_tnp3_sim.lua @@ -87,12 +87,6 @@ local function ExitMsg(msg) print() end -local function writedumpfile(infile) - t = infile:read('*all') - len = string.len(t) - local len,hex = bin.unpack(('H%d'):format(len),t) - return hex -end -- blocks with data -- there are two dataareas, in block 8 or block 36, ( 1==8 , -- checksum type = 0, 1, 2, 3 diff --git a/client/luascripts/lf_em_tearoff.lua b/client/luascripts/lf_em_tearoff.lua index 2aa0dd5ee..c26979d9a 100644 --- a/client/luascripts/lf_em_tearoff.lua +++ b/client/luascripts/lf_em_tearoff.lua @@ -82,16 +82,16 @@ local function main(args) sd = sd or 2000 ed = ed or 2100 - if #password ~= 8 then - password = '' + if password ~= '' and #password ~= 8 then + return oops('password must be 4 hex bytes') end if #wr_value ~= 8 then - wr_value = 'FFFFFFFF' + return oops('write value must be 4 hex bytes') end if #rd_value ~= 8 then - rd_value = 'FFFFFFFF' + return oops('read value must be 4 hex bytes') end if sd > ed then @@ -114,7 +114,7 @@ local function main(args) local set_tearoff_delay = 'hw tearoff --delay %d' local enable_tearoff = 'hw tearoff --on' - local wr_template = 'lf em 4x05_write %s %s %s' + local wr_template = 'lf em 4x05 write --addr %s --data %s --pwd %s' -- init addr to value core.console(wr_template:format(addr, wr_value, password)) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index f5387ff45..f1a0d946a 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -49,7 +49,7 @@ }, { "AID": "F21190", - "Vendor": "Metropolitan Transportation Commission", + "Vendor": "Metropolitan Transportation Commission / Cubic", "Country": "US", "Name": "Clipper Card", "Description": "", @@ -81,7 +81,7 @@ }, { "AID": "784000", - "Vendor": "NO1", + "Vendor": "NOL", "Country": "UAE", "Name": "Nol Card/Dubai", "Description": "Nol Card/Dubai", @@ -121,7 +121,7 @@ }, { "AID": "F21030", - "Vendor": "ORCA Card", + "Vendor": "ORCA (VUX/ERG)", "Country": "", "Name": "ORCA Card", "Description": "(FIDs 02: Trip History; 04: current balance)", @@ -383,5 +383,157 @@ "Name": "Disney MagicBand", "Description": "AID found on MagicBand desfire cards", "Type": "payment system" + }, + { + "AID": "F21100", + "Vendor": "MyKI", + "Country": "AUS", + "Name": "Myki", + "Description": "AID found on Myki ticket cards", + "Type": "transport" + }, + { + "AID": "F210F0", + "Vendor": "MyKI", + "Country": "AUS", + "Name": "Myki", + "Description": "AID found on Myki ticket cards", + "Type": "transport" + }, + { + "AID": "F206B0", + "Vendor": "ACS", + "Country": "AUS", + "Name": "Metrocard / ACS", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21050", + "Vendor": "INIT", + "Country": "NZ", + "Name": "Metrocard / Christchurch", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21150", + "Vendor": "HAGUESS", + "Country": "CZ", + "Name": "Lítačka / Prague", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21360", + "Vendor": "INIT", + "Country": "CZ", + "Name": "HOLO", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21381", + "Vendor": "Cubic", + "Country": "US", + "Name": "Ventra", + "Description": "", + "Type": "transport" + }, + { + "AID": "F213A0", + "Vendor": "INIT", + "Country": "US", + "Name": "WAVE / Rhode Island", + "Description": "", + "Type": "transport" + }, + { + "AID": "F210E0", + "Vendor": "Hop Fastpass", + "Country": "", + "Name": "Hop Fastpass", + "Description": "", + "Type": "transport" + }, + { + "AID": "EF2011", + "Vendor": "HSL", + "Country": "FI", + "Name": "HSL / Helsinki", + "Description": "", + "Type": "transport" + }, + { + "AID": "A00216", + "Vendor": "ITSO", + "Country": "", + "Name": "ITSO", + "Description": "", + "Type": "transport" + }, + { + "AID": "554000", + "Vendor": "AT HOP", + "Country": "", + "Name": "AT HOP", + "Description": "", + "Type": "transport" + }, + { + "AID": "534531", + "Vendor": "OPAL", + "Country": "AUS", + "Name": "OPAL", + "Description": "", + "Type": "transport" + }, + { + "AID": "2211AF", + "Vendor": "Leap", + "Country": "", + "Name": "Leap", + "Description": "", + "Type": "transport" + }, + { + "AID": "015342", + "Vendor": "BEM", + "Country": "TH", + "Name": "BEM / Bangkok", + "Description": "", + "Type": "transport" + }, + { + "AID": "012242", + "Vendor": "Istanbulkart", + "Country": "TR", + "Name": "Istanbulkart / Istanbul", + "Description": "", + "Type": "transport" + }, + { + "AID": "010000", + "Vendor": "Madrid Public Transit Card", + "Country": "ES", + "Name": "Madrid Public Transit Card", + "Description": "", + "Type": "transport" + }, + { + "AID": "000001", + "Vendor": "Invalid / reserved", + "Country": "", + "Name": "Invalid / reserved", + "Description": "used by Compass DESFire and Breeze DESFire", + "Type": "transport" + }, + { + "AID": "FFFFFF", + "Vendor": "Reserved for future use", + "Country": "", + "Name": "Reserved for future use", + "Description": "used by AT HOP, Nol, ORCA", + "Type": "transport" } ] diff --git a/client/resources/mad.json b/client/resources/mad.json index 38cdf2636..a11236189 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -2940,7 +2940,7 @@ "system_integrator": "A.I.I." }, { - "application": "Multi-Modal Transit", + "application": "Multi-Modal Transit (Vix/ERG)", "company": "ERG Transit Systems", "mad": "0x2103", "service_provider": "ERG Transit Systems", @@ -2953,6 +2953,13 @@ "service_provider": "ERG Transit Systems", "system_integrator": "ERG Transit Systems" }, + { + "application": "Transit (Metrocard / Christchurch)", + "company": "INIT Transit Systems", + "mad": "0x2105", + "service_provider": "INIT Transit Systems", + "system_integrator": "INIT Transit Systems" + }, { "application": "Mass Transportation Ticketing", "company": "Omnifare", @@ -2995,6 +3002,13 @@ "service_provider": "Emcard", "system_integrator": "Emtest" }, + { + "application": "Integrated multi-modal transport ticketing system", + "company": "HOP Fastpass", + "mad": "0x210E", + "service_provider": "HOP Fastpass", + "system_integrator": "HOP Fastpass" + }, { "application": "Integrated multi-modal transport ticketing system", "company": "Keane Australia PTY Limited", @@ -3038,7 +3052,7 @@ "system_integrator": "T-Systems GEI" }, { - "application": "Transportation ticket", + "application": "Transportation ticket (Lítačka)", "company": "Haguess s.r.o.", "mad": "0x2115", "service_provider": "HAGUESS, a.s.", @@ -3135,6 +3149,35 @@ "service_provider": "RTD", "system_integrator": "ACS" }, + { + "application": "Bus and rail fare collection (HOLO)", + "company": "INIT", + "mad": "0x2136", + "service_provider": "INIT", + "system_integrator": "INIT" + }, + { + "application": "Bus and rail fare collection (Ventra)", + "company": "Cubic", + "mad": "0x2138", + "service_provider": "Cubic", + "system_integrator": "Cubic" + }, + { + "application": "Bus and rail fare collection (Clipper)", + "company": "Cubic", + "mad": "0x2139", + "service_provider": "Cubic", + "system_integrator": "Cubic" + }, + { + "application": "Bus and rail fare collection (WAVE)", + "company": "INIT Rhode Island", + "mad": "0x213A", + "service_provider": "INIT Rhode Island", + "system_integrator": "INIT Rhode Island" + }, + { "application": "Bus services with extension to rail and taxi", "company": "Questek Marketing", @@ -13621,5 +13664,12 @@ "mad": "0xF001", "service_provider": "Tech ID", "system_integrator": "Tech ID" + }, + { + "application": "Miscellaneous applications", + "company": "Reserved For future Use", + "mad": "0xFFFF", + "service_provider": "RFU", + "system_integrator": "RFU" } ] diff --git a/client/resources/sim014.bin b/client/resources/sim014.bin new file mode 100644 index 000000000..d4f91b0c3 Binary files /dev/null and b/client/resources/sim014.bin differ diff --git a/client/resources/sim014.sha512.txt b/client/resources/sim014.sha512.txt new file mode 100644 index 000000000..2daf74b65 --- /dev/null +++ b/client/resources/sim014.sha512.txt @@ -0,0 +1 @@ +8b754191cec19a8172ff77443eee67490f61c53161af2e831ff4e8c7909b788802f5c0b68cecdc5decabe7852479740a0122832803af26b68beab814ced543b8 *client/resources/sim014.bin diff --git a/client/src/atrs.h b/client/src/atrs.h index 2a6bc7521..134e74d77 100644 --- a/client/src/atrs.h +++ b/client/src/atrs.h @@ -34,6 +34,7 @@ const char *getAtrInfo(const char *atr_str); // atr_t array is expected to be NULL terminated const static atr_t AtrTable[] = { { "3BDF18FFC080B1FE751F033078464646462026204963656D616E1D", "Cardhelper by 0xFFFF and Iceman" }, + { "3B90969181B1FE551FC7D4", "IClass SE Processor (Other) https://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor"}, { "3B..............0031B8640000000073......829000", "MultiApp ID IAS ECC 72K CC (with IAS XL / IAS ECC Applet) IAS ECC Type 3" }, { "3B..............0031B8640000000073......829000..", "MultiApp ID IAS ECC 72K CC (with IAS XL / IAS ECC Applet) IAS ECC Type 4" }, { "3B........0031B864........73......829000", "IDClassic IAS (old name: IAS TPC) IAS ECC Type 1\nMultiApp ID IAS ECC 72K CC (with IAS XL / IAS ECC Applet) IAS ECC Type 1" }, @@ -362,6 +363,7 @@ const static atr_t AtrTable[] = { { "3B3C110042AF20A32007002283829000", "Orange Mobicarte (SIM card old generation)" }, { "3B3C110044AF11F7200504FB83819000", "itineris (Old French Mobile Operator SIM card) (Telecommunication)" }, { "3B3C9400423111A21202095183809000", "Omnitel IT 16K GSM SIM card" }, + { "3B3C9400443111F000002CAE83839000", "Movistar Spain (Telecommunication)" }, { "3B3C94004B3125A21013144783839000", "GSM SFR" }, { "3B3C94004C3125A7201B001583839000", "GSM-SIM (900MHz) card of the carrier vodafone for their cellular\nnetwork (phase 2+ with 3V)" }, { "3B3C9400633112F00000464083839000", "Old russian 'beeline' sim" }, @@ -425,6 +427,7 @@ const static atr_t AtrTable[] = { { "3B5F9600805A2C1100101000FFFFFFFF829000", "Calypso (Transport)" }, { "3B5F9600805A3F0608140101C546DEDC829000", "Navegante(r) Personalizado (Lisbon public transportation card) (Transport)\nhttps://www.navegante.pt/viajar/cartoes" }, { "3B5F9600805A3F0608140101C546EBDC829000", "Multi-Transport Pass (Navegante) (Metro, Bus, Electric, Boat) Carris Metropolitano, PT (Transport)\nhttps://www.navegante.pt/" }, + { "3B5F9600805A3F0608201223C4325FDE829000", "Montreal metropolitan area and Quebec city area OPUS card (Transport)\nhttps://www.carteopus.info/" }, { "3B600000", "Meano (Bank)" }, { "3B61000080", "blank A40CR card (JavaCard)" }, { "3B630000364180", "Schlumberger Payflex 4k User" }, @@ -1189,6 +1192,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A28130210122B75021B8A829000", "KorriGo, smart transport card in France region Brittany (Transport)\nhttps://www.ter.sncf.com/bretagne/offres/carte-korrigo" }, { "3B6F0000805A28130210122B7503FB01829000", "origo (France) (Transport)\nhttps://www.breizhgo.bzh/se-deplacer-en-bretagne/KorriGo" }, { "3B6F0000805A28130210122B750C7E79829000", "Transportation card delivered by STAR (Transportation service from the city of Rennes, France) (Transport)\nhttps://www.star.fr/titres-et-tarifs/carte-korrigo/" }, + { "3B6F0000805A28130210122B750DD382829000", "ZOU! for Region Sud (Transport)\nhttps://zou.maregionsud.fr/ma-carte-zou/" }, { "3B6F0000805A28130210122B9292E642829000", "Transport card in cote d'or France (mobigo)" }, { "3B6F0000805A28130210122B9292E829829000", "French transport card of the city of Dijon and Cote d'or department. (Transport)\nhttps://www.viamobigo.fr/fr/acheter-mes-titres-de-transport-mobigo-en-cote-dor/176" }, { "3B6F0000805A28130210122B92D663FC829000", "Card 'Korrigo' region Bretagne, France, Bibus Brest Metropole public transport (Transport)\nhttps://fr.wikipedia.org/wiki/KorriGo" }, @@ -1227,6 +1231,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2D06081010027835EDCE829000", "Lisbon Transportation SmartCard (Transport)" }, { "3B6F0000805A2D06081010027848BBCC829000", "Lisbon Metro Monthly Student Pass (Transport)\nhttps://www.metrolisboa.pt/" }, { "3B6F0000805A2D0608101005935C42FB829000", "Comboios de Portugal Transit Card (Transport)\nhttps://www.cp.pt/passageiros/pt/consultar-horarios/precos/cartao-cp" }, + { "3B6F0000805A2E130200010104EF8342829000", "Oura Auvergne-Rhone-Alpes (Transport)\nhttps://www.oura.com" }, { "3B6F0000805A3407061500017917A7E2829000", "Rav-Kav multi-line travel ticket used in the public transportation system in Israel (Transport)\nhttps://www.gov.il/en/departments/guides/multi_line_card" }, { "3B6F0000805A340706150001792A4B5C829000", "Rav Kav Transit Payment Card (Israel) (Transport)\nhttps://ravkavonline.co.il/" }, { "3B6F0000805A3B0706150101793E797B829000", "Rav Kav (Transport)\nhttp://alhakav.mot.gov.il/he/rav-kav" }, @@ -1468,7 +1473,7 @@ const static atr_t AtrTable[] = { { "3B7F..0000006A4345524553022C3402..039000", "Ceres ST v2" }, { "3B7F..0000006A4345524553022C3403..039000", "Ceres ST v3" }, { "3B7F..00008031..65B000000000......8290..", "MultiApp V2.1 (with IAS XL / IAS ECC and IAS Classic Applet V3) [MultiApp V2.1 Type 2]" }, - { "3B7F..000080318065B0........120FFE829000", "IDPrime MD 8840, 3840, 3810, 840 and 830 Cards T=0" }, + { "3B7F..000080318065B0........120FFE829000", "IDPrime MD 8840, 3840, 3810, 840 and 830 Cards T=0\nIDPrime 930 (JavaCard)" }, { "3B7F0100FE58434F53763235312863295046424D", "XCOS is an Experimental Card Operating System for Atmel based smartcards (Funcard, etc..) (Other)\nhttp://runningserver.com/?page=runningserver.content.download.xcos" }, { "3B7F04000080318071906754454D44412E309000", "public key (PKI)" }, { "3B7F1100000031C053CAC4016452D90400829000", "DoD CAC, Oberthur CosmopolIC 32K V4" }, @@ -1604,6 +1609,7 @@ const static atr_t AtrTable[] = { { "3B7F96000080318065B085050011120FFF829000", "LuxTrust card (Luxembourg qualified electronic signature / authentication system) (Other)\nhttps://www.luxtrust.com/en/professionals/smartcard" }, { "3B7F96000080318065B0855956FB120268829000", "qualified certificate (eID)\nhttps://www.elektronicznypodpis.pl/en/offer/qualified-certificates/" }, { "3B7F96000080318065B0855956FB1202C1829000", "Gemalto USB (eID)" }, + { "3B7F96000080318065B0855956FB120FFE829000", "Thales (Gemalto) IDPrime 941 (PKI)\nhttps://cpl.thalesgroup.com/de/access-management/idprime-md-pki-smart-cards\nThales SafeNet IDPrime 940B (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/idprime-940-product-brief" }, { "3B7F96000080318065B0855956FB12FFFE829000", "IDCORE 3140 (JavaCard)" }, { "3B7F9600008031B865B0850300EF1200F6829000", "Finnish identity card (eID)\nhttp://vrk.fi/en/citizen-certificate" }, { "3B7F9600008031B865B08504021B1200F6829000", "Finnish ID-card v5.0(?) (eID)\nhttps://dvv.fi/en/fineid-specifications" }, @@ -1705,6 +1711,7 @@ const static atr_t AtrTable[] = { { "3B8680014B4F4E41141109", "Mastercard paypass enabled credit card" }, { "3B8680015741524930310B", "Gusto Karta (Bank)\nhttps://www.gustokarta.cz" }, { "3B86800157575061737336", "WWPass Passkey (eID)\nhttps://www.wwpass.com/passkey" }, + { "3B868001801434373000A0", "Malta eID Identity Card (eID)\nhttps://www.identitymalta.com/" }, { "3B8680018031C15211182C", "IDEMIA Cosmo V8.0 with a PIV applet (contactless) (PKI)" }, { "3B8680018031C152411A7E", "IDEMIA Cosmo V8.1 with a PIV applet (contactless) (PKI)" }, { "3B86800180540410010FC9", "Nickel.eu prepaid account (Bank)\nhttps://nickel.eu" }, @@ -2006,6 +2013,7 @@ const static atr_t AtrTable[] = { { "3B8C8001505CF5A94530AAAA017781D708", "Indonesia ektp (eID)" }, { "3B8C800150605F2EFA00000000778191D1", "Residence Permit (Switzerland) (eID)" }, { "3B8C80015064E65B000000000000818085", "Chicago CTA Ventra Transit card\nhttps://www.ventrachicago.com/" }, + { "3B8C80015068DDA5B11C00001177818582", "EZ-Link Card (issued by Land Transport Authority Singapore) (Transport)\nhttps://www.ezlink.com.sg/ez-link-faqs/ez-link-card/" }, { "3B8C800150710CF3C800000000B37171A8", "MOBIB CARD BELGIUM (Transport)" }, { "3B8C800150773B2DBD0000001100818594", "Texas Instruments Dynamic NFC Interface Transponder (RF430CL330H)" }, { "3B8C800150784B2CCB00000000B371713A", "... (Transport)\nhttps://www.portalviva.pt/" }, @@ -2135,6 +2143,7 @@ const static atr_t AtrTable[] = { { "3B8F80010031B96409377213738401E0000000AB", "National Identity Card of Slovakia (NFC interface) (eID)\nhttps://en.wikipedia.org/wiki/Slovak_identity_card" }, { "3B8F80010031C173C800106457494943009000B5", "ICC Solutions Card for Certification (Other)\nhttps://www.iccsolutions.com/" }, { "3B8F80010031C173C8211064414D31300790008A", "master card (Bank)" }, + { "3B8F80010031C173C8211064414D313307900089", "CheBanca! nexi debit card (Bank)\nhttps://www.chebanca.it/" }, { "3B8F80010031C173C8211064414D31370790008D", "AirPlus International Mastercard (Bank)\nhttps://www.airplus.com/" }, { "3B8F80010031C173C8211064414D333007900088", "ING-VISA-Card (Bank)\nhttps://www.ing.de/girokonto/karten-bargeld/" }, { "3B8F80010031C173C8211064414D333107900089", "NAB VISA Debit (Bank)\nhttps://www.nab.com.au/" }, @@ -2276,7 +2285,7 @@ const static atr_t AtrTable[] = { { "3B8F8001804F0CA0000003060E....00000000..", "Contact (7816-10) Extended I2C (as per PCSC std part3)" }, { "3B8F8001804F0CA0000003060F....00000000..", "Contact (7816-10) 2WBP (as per PCSC std part3)" }, { "3B8F8001804F0CA00000030610....00000000..", "RFID - FeliCa compatible (as per PCSC std part3)\nContact (7816-10) 3WBP (as per PCSC std part3)" }, - { "3B8F8001804F0CA00000030611003B0000000042", "RFID - FeliCa (generic) (as per PCSC std part3)\nSuica public transit card (Japan IC system)\n(also: Hayakaken, ICOCA, Kitaca, manaca, nimoca, PASMO, PiTaPa, SUGOCA, TOICA)\nhttps://en.wikipedia.org/wiki/Suica\nOctopus, MTR network from Hong Kong, 2014" }, + { "3B8F8001804F0CA00000030611003B0000000042", "RFID - FeliCa (generic) (as per PCSC std part3)\nSuica public transit card (Japan IC system)\n(also: Hayakaken, ICOCA, Kitaca, manaca, nimoca, PASMO, PiTaPa, SUGOCA, TOICA)\nhttps://en.wikipedia.org/wiki/Suica\nOctopus, MTR network from Hong Kong, 2014\ne-Amusement card (Other)\nhttps://en.wikipedia.org/wiki/E-Amusement" }, { "3B8F8001804F0CA00000030640....00000000..", "RFID - Low Frequency < 135 kHz (as per PCSC std part3)" }, { "3B8F8001804F0CA0000003064000000000000028", "HID Proximity. Used to access buildings. Reference on the card 'HID0008P'.\nhttp://www.hidglobal.com/product-display/cards-and-credentials/hid-proximity" }, { "3B8F8001804F0CA0001A0000000078", "iclass 16k cl (eID)" }, @@ -2328,7 +2337,6 @@ const static atr_t AtrTable[] = { { "3B959640F001130A0A1D", "xcrypt (Pay TV)" }, { "3B959640F00F100A096A", "Zain Usim Card (Telecommunication)\nwww.qariya.com" }, { "3B959680B1FE551FC7477261636513", "IClass SE Processor (Other)\nhttps://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor" }, - { "3B90969181B1FE551FC7D4", "IClass SE Processor (Other)\nhttps://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor"}, { "3B9596C0F01FC20F100A0A16", "viettel (Telecommunication)" }, { "3B959740F01A160A1941", "SG50 (Samsung Chip) (Telecommunication)" }, { "3B96004121920000622433339000", "Ukrainian Telecommunications Operator Kyivstar (old simcard) (Telecommunication)\nhttps://kyivstar.ua/uk/mm" }, @@ -2378,6 +2386,7 @@ const static atr_t AtrTable[] = { { "3B9A940091010017000126050096", "GSM-SIM T-D1 prepaid (Xtra) (Telecommunication)" }, { "3B9A940091010017000126060096", "T D1 GSM card" }, { "3B9A9400920275931100010202..", "SuperSIM (X-sim)" }, + { "3B9A940092029093110001340200", "Orange Spain (Telecommunication)" }, { "3B9A960092013693170002040300", "GSM-SIM EMT 'Simpel' (prepaid, Estonia)\nhttps://www.emt.ee/web/simpel" }, { "3B9A960092014893170002120400", "SIM Card C*******r Mobile (Belgium)" }, { "3B9A960092016693170002120400", "GSM SIM Bite.lv prepaid 'Toxic'; 2008" }, @@ -2396,6 +2405,7 @@ const static atr_t AtrTable[] = { { "3B9C131181647265616D6372797074000408", "XPlusTV & INXCT Access Card-9 (FIRECrypt)" }, { "3B9C131181647265616D6372797074900599", "FireCrypt, access card 9 (Pay TV)" }, { "3B9C188121455A43332E333420524556204467", "MS-Protect safecard for access system (eID)" }, + { "3B9C188121755A43332E333420524556204457", "ZC3.34 (Other)\nhttps://www.zeitcontrol.de/Smart-card-BasicCard-Enhanced-ZC334-SIM-cut" }, { "3B9C940068868D0A86980256C2000500", "G3 & GSM & Blank SIM card: to be programmed for OpenBTS with pySim-prog (Telecommunication)" }, { "3B9C940068868D0C86980245A1000500", "China Mobile (Telecommunication)" }, { "3B9C940068868D0C86980256408B0500", "mobiledit (Telecommunication)" }, @@ -2490,6 +2500,7 @@ const static atr_t AtrTable[] = { { "3B9E95801FC78031E073FE211B66D00217C71100C0", "3 UK SIM card (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D00217F61100F1", "Ziggo SmartCard Television Subscription (Pay TV)" }, { "3B9E95801FC78031E073FE211B66D00219151300", "ABS-CBN (Telecommunication)" }, + { "3B9E95801FC78031E073FE211B66D0022A861300BE", "KPN, Netherlands (Telecommunication)" }, { "3B9E95C00A1FC68031E073FE211B66D001830D58811E", "Mobile Paypass G199 NFC" }, { "3B9E96008031C0654D4700000072F7418107", "Russian Magistra (UTF8: Magistra) smart card (eID)\nhttp://www.smart-park.ru/index.php/products/smartcards.html" }, { "3B9E96008031C0654D5300000072F7418107", "Smart-key for bank-client of MDM Bank (RU) with faktura.ru service (Bank)" }, @@ -2506,6 +2517,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC68031E073FE211B66D0019F8F120002", "Russian 'Megafon' gsm sim card (Telecommunication)\nhttp://www.megafon.ru/" }, { "3B9E96801FC68031E073FE211B66D0019FBC100033", "sim beeline russia krasnodar (Telecommunication)" }, { "3B9E96801FC68031E073FE211B66D0019FEF100060", "Infineon SLU14MCO480K2 (JavaCard)" }, + { "3B9E96801FC68031E073FE211B66D0024010150046", "Kyivstar USIM-card (Telecommunication)" }, { "3B9E96801FC78031E073FE21136200498381900007", "Claro Brazil SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D000282401000D", "United Mobile SIM" }, { "3B9E96801FC78031E073FE211B66D0002B2D020004", "KTF SHOW GE-B1400 WCDMA USIM (Telecommunication)" }, @@ -2540,6 +2552,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00199FE0E0068", "Finnish Sonera SIM-card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0041100B4", "Aldi Talk SIM card, Germany (Telecommunication)\nhttps://www.alditalk.de/talk" }, { "3B9E96801FC78031E073FE211B66D001A01E1100AE", "Zevvle SIM Card (Telecommunication)\nhttps://zevvle.com" }, + { "3B9E96801FC78031E073FE211B66D001A0741000C5", "USIM of Vodafone Germany (MCC 262, MNC 2) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0DD12006E", "USIM (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0EC11005C", "Boost Mobile Prepaid Micro SIM Card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1121000A2", "OpenAirInerface (Telecommunication)" }, @@ -2565,6 +2578,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00217F41200F3", "SIM card for Swedish operator Vimla! (Telecommunication)\nhttps://www.vimla.se" }, { "3B9E96801FC78031E073FE211B66D002194B120042", "halebop (swedish mobile provider) SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022436140004", "SIM (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022A6D140051", "Mobitel SIM from Sri Lanka (Telecommunication)\nhttps://mobitel.lk/" }, { "3B9E96801FC78031E073FE211B66D0022A72130049", "SIM Card OI (Brazil) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022A861300BD", "Mobile Vikings SIM Card (Telecommunication)\nhttps://mobilevikings.com" }, { "3B9E96801FC78031E073FE211B66D0022A8F1400B3", "Telefonica USIM (Telecommunication)" }, @@ -2575,6 +2589,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00233AD140088", "4G-LTE (Telecommunication)" }, { "3B9E96803FC3A08031E073FE211B630801140F9000D3", "KT Olleh LTE Warp SA-L 1670 (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0019F7A1200F6", "Spectrun USA Sim Card (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0024011150046", "SIM Card for French mobile provider Unyc (Telecommunication)\nhttps://www.unyc.io/" }, { "3B9E97801FC68031E073FE211B66D002401D15004A", "Telia Sim card for IoT (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0024027150070", "verymobile wind 3 (Telecommunication)\nhttps://verymobile.it/" }, { "3B9E97801FC68031E073FE211B66D0025E7315003A", "Twilio Super SIM (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, @@ -2643,6 +2658,7 @@ const static atr_t AtrTable[] = { { "3B9F94801FC78031E073FE2113578681098698621880", "Oyeitimes 5G ISIM R16 (Telecommunication)\nhttps://www.oyeitimes.com/detail.php?id=535&ids=565&idt=&ide=&dd=1410" }, { "3B9F94801FC78031E073FE21135786850686984218AB", "4G LTE blank USIM Green Card (Telecommunication)\nhttp://grcard.en.alibaba.com/product/60076835567-209365843/4G_LTE_blank_usim_card_for_4G_network.html" }, { "3B9F94801FC78031E073FE21136321150683079000F8", "Maroc Telecom 4G+ (Telecommunication)" }, + { "3B9F94801FC78031E073FE21136700000000000070AA", "OMANTEL (Telecommunication)\nhttps://portal.omantel.om/Personal/mobile/hayyak" }, { "3B9F94801FC78031E073FE21136761210D10000070F7", "Celcom XPAX (Telecommunication)\nhttps://www.celcom.com.my/personal/prepaid" }, { "3B9F94801FC78031E073FE2119573C8660CFB902A0EE", "Gotanet SE USIM (Telecommunication)" }, { "3B9F94801FC78031E073FE2119573C8660CFBA02A0ED", "Gotanet DK USIM (Telecommunication)" }, @@ -2774,6 +2790,7 @@ const static atr_t AtrTable[] = { { "3B9F96801F878031E073FE2119674A555473300948DB", "nano sim/usim card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A555475300662F8", "SIM Card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A557330310746BE", "Hologram Developer Global IoT SIM Card (Telecommunication)\nhttps://hologram.io/store/global-iot-sim-card/17" }, + { "3B9F96801F878031E073FE2119674A55753031136288", "Zimbabwe sim card provider: ECONET (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C5275310451D5", "Test card provided with 4G/5G network from Amarisoft (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C7530300248A9", "Cardcentrics (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C753034054BA9", "sysmoISIM-SJA2 (Telecommunication)\nhttps://osmocom.org/projects/cellular-infrastructure/wiki/SysmoISIM-SJA2" }, @@ -2944,6 +2961,7 @@ const static atr_t AtrTable[] = { { "3B9F96803FC7828031E073F62157574A330581053000CE", "COMPRION M2M eUICC (Telecommunication)" }, { "3B9F96803FC7828031E073F62157574A4D020B60010069", "eSIM GSMA Card (Telecommunication)\nhttps://www.gsma.com/newsroom/wp-content/uploads/SGP.22_v2.2.pdf" }, { "3B9F96803FC7828031E073F62157574A4D020B60610009", "ting (Telecommunication)" }, + { "3B9F96803FC7828031E073FE211B57AA8660F0010004FB", "The eSIM.me Card (Telecommunication)\nhttps://esim.me/" }, { "3B9F96803FC7828031E073FE211B57AA8660F0010011EE", "eSIM.me pluggable eSIM (Telecommunication)\nhttps://esim.me/" }, { "3B9F96803FC7828031E073FE211B633A204E8300900031", "eSIM (Telecommunication)" }, { "3B9F96803FC7828031E075F62157210355020B60500019", "st33g1m2 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33g1m2.html" }, @@ -3047,6 +3065,7 @@ const static atr_t AtrTable[] = { { "3BB813008131205D0057696E4361726402", "SmartCard for Windows 1.1" }, { "3BB813008131FA524348544D4F494341A5", "citizen digital certificate (PKI)\nhttp://moica.nat.gov.tw/" }, { "3BB897008131FE45FFFF148230502300F1", "UAE Emirates ID (eID)\nhttps://www.icp.gov.ae" }, + { "3BB89700813FE45FFFF148230502300F", "UAE Emirates ID (eID)" }, { "3BB89700C00831FE45FFFF148230502300B8", "Infineon SECORA ID X (JavaCard)" }, { "3BB918008131FE9E8073FF614083000000DF", "Serbian Identity Card\nThis is the new Serbian biometric identity card (every adult cityzen\nmust have). The chip contains owners picture, name, date and place\nof birth, current address, unique ID number and fingerprint." }, { "3BB9940040144747334D4838353330", "T D1 GSM card (Telecommunication)" }, @@ -3239,6 +3258,7 @@ const static atr_t AtrTable[] = { { "3BDB960080B1FE451F830031C064B0FC100007900005", "Oberthur Cosmo V7 debug card (SDK)" }, { "3BDB960080B1FE451F830031C064B0FC10000F90000D", "ID-One PIV (that's the only non-numeric identifying mark) (PKI)" }, { "3BDB960080B1FE451F830031C064BAFC10000790000F", "Oberthur ID-One Cosmo v7.0 80K (eID)\nhttps://www.ssi.gouv.fr/uploads/IMG/certificat/ANSSI-CC-cible_2011-64en.pdf" }, + { "3BDB960080B1FE451F830031C064BAFC10000F900007", "Oberthur ID-One Cosmo v7.0 (PKI)\nhttps://csrc.nist.rip/groups/STM/cmvp/documents/140-1/140sp/140sp1236.pdf" }, { "3BDB960080B1FE451F830031C064BE1B0100019000FB", "Bank card" }, { "3BDB960080B1FE451F830031C064C30801000F90009B", "SIM Aruba (Italian provider)" }, { "3BDB960080B1FE451F830031C064C7FC100001900074", "Oberthur Cosmo (eID)\nhttp://www.stampit.org" }, @@ -3247,6 +3267,7 @@ const static atr_t AtrTable[] = { { "3BDB960080B1FE451F830031E85427E6040007900084", "Polish encard (eID)" }, { "3BDB960080B1FE451F830031E85427E604000F90008C", "Token card from iBRE CompanyNet (mbank) (Bank)" }, { "3BDB960080B1FE451F834553544F4E49412D65494455", "Estonian Identity Card (ID-One Cosmo v8.1) (eID)" }, + { "3BDB960080B1FE451F870031C1640958223607900019", "Idemia Solvo Fly 40 (JavaCard)" }, { "3BDB960081B1FE451F0380F9A0000003080000100018", "Oberthur CS PIV End Point v1.08 FIPS201 Certified" }, { "3BDB960081B1FE451F0380F9A0000003480000000149", "Fly Clear card" }, { "3BDB960081B1FE451F8380F9A0000003080000100098", "Oberthur Cosmo v7 128K with PIV applet\nhttp://www.smartcardfocus.com/shop/ilp/id~410/p/index.shtml" }, @@ -3344,6 +3365,7 @@ const static atr_t AtrTable[] = { { "3BDF96FF910131FE4680319052410264050200AC73D622C017", "Acos-ID (AUSTRIACARD's Operating System) (Other)\nhttps://www.austriacard.com/digital-security/solutions/card-solutions/acos-id/" }, { "3BDF97008131FE588031B05202056405A100AC73D622C021", "Austrian healthcare insurance identification card (HealthCare)\nhttps://www.chipkarte.at" }, { "3BDF970081B1FE451F838073CC91CBF9A0000003080000100078", "NASA PIV Card (Other)" }, + { "3BE000008131104505", "Emoney indonesia (Bank)" }, { "3BE000008131204030", "SmarTEC" }, { "3BE000FF8131FE4514", "'JUKICARD', digitally sign tax documents in Japan" }, { "3BE20000402049..", "Schlumberger Cryptoflex 4k" }, @@ -3432,6 +3454,7 @@ const static atr_t AtrTable[] = { { "3BEE00008131804280318066B0840C016E01830090008E", "MultiApp Cards (Easy 72K Type B and Combi 72K Type B)\nE.SUN Commercial bank debit master card (Bank)\nTaiwan EasyCard (Transport)\nhttps://www.easycard.com.tw/english/index.asp" }, { "3BEE00008131804380318066B1A1110100F683009000", "Optelio/Desineo Cards (D72 FXR1)" }, { "3BEE00008131804380318066B1A11101A0F683009000", "Optelio D72 FXR1 (MD) T=1" }, + { "3BEE00008131804380318066B1A30401110B83009000D4", "Japan Post Bank cash card (Bank)\nhttps://www.jp-bank.japanpost.jp/kojin/chokin/sogou/kj_cho_sg_iccard.html" }, { "3BEE00008131FE45003180718665016702A00A8390001B", "IBM JCOP 'Java Card 2.1.1' et 'Open Platform 2.0.1'" }, { "3BEE00008131FE4580318066409093060F1783019000FD", "Health insurance (HealthCare)" }, { "3BEE00008131FE4580318066409093060F17830F9000F3", "IC card for the National Health Insurance, Taiwan" }, @@ -3579,6 +3602,7 @@ const static atr_t AtrTable[] = { { "3BF711000140967070370E6CB6D6", "Carte pour decodeur cable numerique (fourni par www.voo.be et\nwww.ledecodeur.be)" }, { "3BF711000140967070670E6CB6D6", "UK TopUp TV" }, { "3BF711000140967071090E6CB6D6", "Carte pour decodeur tele de Neuf Telecom TV" }, + { "3BF71300008131FE4500C08AC80C658185", "NXP JCop (JavaCard)" }, { "3BF71300008131FE45464F4D534F4D53A9", "Health card Russian Federation" }, { "3BF71300008131FE454A434F503234....", "NXP JCOP v2.4.x (see hist bytes for more info)" }, { "3BF71300008131FE4580654A5030310415", "Nichizeiren Denshi-shomei (eID)\nhttps://www.nichizeiren.or.jp/taxaccount/auth/fifth/" }, @@ -3818,6 +3842,7 @@ const static atr_t AtrTable[] = { { "3BFF1300008131FE45434433690940000020202020202000F3", "VISA credit card (LBBW/Payback VISA) (Bank)" }, { "3BFF1300008131FE454F574F4B31302D4A................", "OWOK (One Web, One Key) login card\nhttp://www.reiner-sct.com/owok/\nReiner SCT loginCard\nhttps://cardlogin.reiner-sct.com/" }, { "3BFF1300008131FE4D8025A00000005657444B3333300600D2", "Datakey DCOS model 330 (DKCCOS 6.0 token)" }, + { "3BFF1300918131FE4141434F532046696F6E6131204C6336F4", "TURKEY A101 HADI APP CARD (Bank)\nhttps://a101hadi.a101.com.tr/" }, { "3BFF1300FF10000031C1738211064414D33470779000", "Visa Debit (Bank)\nhttps://www.chase.com/" }, { "3BFF1300FF10000031C173C821106441443533079000", "BRADESCO-CONTA SALARIO (Bank)" }, { "3BFF1300FF10000031C173C8211064414D3130079000", "Tangerine MasterCard (Bank)\nhttps://www.tangerine.ca/en/products/spending/creditcard/money-back/" }, @@ -3883,6 +3908,7 @@ const static atr_t AtrTable[] = { { "3BFF1800FF813150456563............................", "GeldKarte v3 (Germany)" }, { "3BFF1800FF8131FE4165630608710156000FB85073204712D8", "Commerzbank maestro (Bank)\nhttps://www.commerzbank.de/konten-zahlungsverkehr/produkte/girokonten/kostenloses-girokonto/" }, { "3BFF1800FF8131FE4165630608710156000FB8602AA0471231", "Debit card (Germany): Postbank - GeldKarte (EUR), girocard, V-PAY (Bank)\nhttps://www.postbank.de/" }, + { "3BFF1800FF8131FE41656306087102500023B8907360471271", "Debit card (Germany): Deutsche Kreditbank (DKB), ec-cash, (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, { "3BFF1800FF8131FE4165631116710156000F0902904E5711AC", "German Bank Card IDEMIA 9 Maestro/Girocard (Sparkasse S-Payment TPY 1974693D) (Bank)" }, { "3BFF1800FF8131FE450031C573C00180547615020105900074", "SIGILANCE NFC OpenPGP Smart Card (JavaCard)\nhttps://www.sigilance.com/" }, { "3BFF1800FF8131FE455448434331305445434F4744484E3224", "National Health Insurance Card, Taiwan" }, @@ -3993,6 +4019,7 @@ const static atr_t AtrTable[] = { { "3BFF9600008131FE4380318065B0845651101201788290006A", "SafeNet eToken 5300 (PKI)" }, { "3BFF9600008131FE4380318065B08456511012021082900001", "Nedap NexS N:Secure (eID)\nhttps://www.nsecure.nl/nl/" }, { "3BFF9600008131FE4380318065B0846160FB120FFD8290000C", "IDPrime 930 FIPS Level 2 (T=1 CT=96) (BAI3.1) (PKI)" }, + { "3BFF9600008131FE4380318065B0846566FB12017882900085", "eToken 5110+ FIPS 140-2 Level 2 (JavaCard)" }, { "3BFF9600008131FE4380318065B0846566FB120FFC8290000F", "SmartID 3930 FIDO Contact and Contactless card (PKI)\nhttps://www.smartcardfocus.com/shop/ilp/id~962/safenet-idprime-3930-fido-dual-interface-fips-l2/p/index.shtml" }, { "3BFF9600008131FE4380318065B0846669FB12FFFE829000F1", "IDCore3230 build 6.8, test APDU applet (JavaCard)" }, { "3BFF9600008131FE4380318065B085040011120FFF829000E0", "Pakistan National identity card (eID)" }, @@ -4024,6 +4051,7 @@ const static atr_t AtrTable[] = { { "3F2F008059AF0201013000000A0E83069F12", "Gemplus GemXplore" }, { "3F2F008059AF02010230000C0A0E831E9F16", "GSM-SIM (900MHz) card of the carrier 'Mannesmann Mobilfunk' for\ntheir network 'D2-Privat' - now known as Vodafone Mobilfunk\nhttp://www.vodafone.de/" }, { "3F2F008069AE0202013600000A0E833E9F16", "GSM-SIM e-plus (1800MHz)" }, + { "3F2F008069AF0204013600000A0E833E9F16", "Telia Mobitel GSM (Telecommunication)" }, { "3F2F008069AF0204013600020A0E833E9F16", "GSM-SIM D2 CallYa (900MHz)" }, { "3F2F008069AF0307015900000A0E833E9F16", "Nokia SIM Ph2 16K Ver2.0" }, { "3F2F008069AF0307015900130A0E833E9F16", "Old Spanish Telefonica Movistar GSM SIM card manufactured by Gemplus" }, diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 563fda14f..ff75cc20e 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -104,9 +104,7 @@ static int CIPURSEExchangeEx(bool activate_field, bool leave_field_on, sAPDU_t a memcpy(result, securedata, rlen); } - if (result_len != NULL) { - *result_len = rlen; - } + *result_len = rlen; if (sw != NULL) { *sw = isw; @@ -628,7 +626,7 @@ void CIPURSEPrintFileAttrEx(uint8_t *attr, size_t len, bool isDGI) { } void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) { - return CIPURSEPrintFileAttrEx(attr, len, false); + CIPURSEPrintFileAttrEx(attr, len, false); } void CIPURSEPrintFileUpdateAttr(uint8_t *attr, size_t len) { diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 6eb3fcb85..cca476c20 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -3279,9 +3279,9 @@ static int CmdNumCon(const char *Cmd) { } radix_t; radix_t radix[] = { - {"dec..... ", 10}, - {"hex..... 0x", 16}, - {"bin..... 0b", 2} + {"dec... ", 10}, + {"hex... ", 16}, + {"bin... ", 2} }; char s[600] = {0}; @@ -3290,7 +3290,7 @@ static int CmdNumCon(const char *Cmd) { for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); if (slen > 0) { - PrintAndLogEx(INFO, "%s%s", radix[i].desc, s); + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); } } @@ -3451,6 +3451,72 @@ static int CmdAtrLookup(const char *Cmd) { return PM3_SUCCESS; } +static int CmdBinaryMap(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data bmap", + "Breaks down a hex value to binary according a template\n" + " data bmap -d 16 -m 4,4\n" + "This will give two rows each with four bits", + "data bmap -d 3B\n" + "data bmap -d 3B -m 2,5,1\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", NULL, "", "hex string"), + arg_str0("m", NULL, "", "binary template"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int hlen = 5; + uint8_t hex[5 + 1]; + CLIGetStrWithReturn(ctx, 1, hex, &hlen); + + int tlen = 40; + uint8_t template[40 + 1]; + CLIGetStrWithReturn(ctx, 2, template, &tlen); + CLIParserFree(ctx); + + char bits[(8 * 4) + 1] = {0}; + hextobinstring_n(bits, (char *)hex, hlen); + + if (tlen == 0) { + template[0] = '8'; + template[1] = 0; + } + + char *token = strtok((char *)template, ","); + + // header + PrintAndLogEx(INFO, "---+---------------------------"); + PrintAndLogEx(INFO, " | b0 b1 b2 b3 b4 b5 b6 b7"); + PrintAndLogEx(INFO, "---+---------------------------"); + + uint8_t i = 0; + uint8_t cnt = 1; + int x = 0; + while (token != NULL) { + sscanf(token, "%d", &x); + + PrintAndLogEx(INFO, " %d | %*.s" NOLF, cnt, i * 3, " "); + + // incease with previous offset + x += i; + + for (; i < (uint8_t)x; i++) { + PrintAndLogEx(NORMAL, "%c " NOLF, bits[7 - i]); + } + + PrintAndLogEx(NORMAL, ""); + token = strtok(NULL, ","); + cnt++; + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -3493,6 +3559,7 @@ static command_t CommandTable[] = { {"atr", CmdAtrLookup, AlwaysAvailable, "ATR lookup"}, {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"}, {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, + {"bmap", CmdBinaryMap, AlwaysAvailable, "Convert hex value according a binary template"}, {"clear", CmdBuffClear, AlwaysAvailable, "Clears bigbuf on deviceside and graph window"}, {"diff", CmdDiff, AlwaysAvailable, "Diff of input files"}, {"hexsamples", CmdHexsamples, IfPm3Present, "Dump big buffer as hex bytes"}, diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 2cfe3849c..9d1f5605f 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -379,8 +379,7 @@ static int CmdFlashMemDump(const char *Cmd) { } if (filename[0] != '\0') { - saveFile(filename, ".bin", dump, len); - saveFileEML(filename, dump, len, 16); + pm3_save_dump(filename, dump, len, jsfRaw); } free(dump); diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index e84b3e597..218109755 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -370,14 +370,13 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { "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" + "mem spiffs dump -s tag.bin -d a001 -e --> download tag.bin, save as `a001.bin`" ); 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); @@ -390,7 +389,6 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { 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 ! @@ -433,15 +431,6 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { else saveFile(fn, ".bin", dump, len); // default - 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; } diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index ecd57e181..a39092fe8 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -994,7 +994,7 @@ int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card // check result if (resp.oldarg[0] == 0) { if (verbose) { - PrintAndLogEx(FAILED, "No card in field"); + PrintAndLogEx(WARNING, "No ISO1443-A Card in field"); } return PM3_ECARDEXCHANGE; } @@ -1595,6 +1595,7 @@ typedef enum { MTEMV = 128, MTFUDAN = 256, MTISO18092 = 512, + MT424 = 1024, } nxp_mifare_type_t; // Based on NXP AN10833 Rev 3.6 and NXP AN10834 Rev 4.1 @@ -1723,7 +1724,7 @@ static int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { } printTag("NTAG 4xx"); - type |= MTDESFIRE; + type |= (MTDESFIRE | MT424); } } else if ((sak & 0x04) == 0x04) { printTag("Any MIFARE CL1"); @@ -1920,6 +1921,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bool isEMV = false; bool isFUDAN = false; bool isISO18092 = false; + bool isNTAG424 = false; int nxptype = MTNONE; if (card.uidlen <= 4) { @@ -1929,6 +1931,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { isMifareDESFire = ((nxptype & MTDESFIRE) == MTDESFIRE); isMifarePlus = ((nxptype & MTPLUS) == MTPLUS); isMifareUltralight = ((nxptype & MTULTRALIGHT) == MTULTRALIGHT); + isNTAG424 = ((nxptype & MT424) == MT424); if ((nxptype & MTOTHER) == MTOTHER) isMifareClassic = true; @@ -1958,6 +1961,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { isMifareDESFire = ((nxptype & MTDESFIRE) == MTDESFIRE); isMifarePlus = ((nxptype & MTPLUS) == MTPLUS); isMifareUltralight = ((nxptype & MTULTRALIGHT) == MTULTRALIGHT); + isNTAG424 = ((nxptype & MT424) == MT424); if ((nxptype & MTOTHER) == MTOTHER) isMifareClassic = true; @@ -2010,8 +2014,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // ******** is card of the MFU type (UL/ULC/NTAG/ etc etc) DropField(); - uint32_t tagT = GetHF14AMfU_Type(); - if (tagT != UL_ERROR) { + uint64_t tagT = GetHF14AMfU_Type(); + if (tagT != MFU_TT_UL_ERROR) { ul_print_type(tagT, 0); isMifareUltralight = true; printTag("MIFARE Ultralight/C/NTAG Compatible"); @@ -2461,6 +2465,11 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { */ } + if (isNTAG424) { + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf ntag424 info") "`"); + } + + PrintAndLogEx(NORMAL, ""); DropField(); return select_status; @@ -2748,6 +2757,7 @@ int CmdHF14ANdefRead(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool verbose = arg_get_lit(ctx, 2); + bool verbose2 = arg_get_lit(ctx, 2) > 1; CLIParserFree(ctx); bool activate_field = true; @@ -2934,11 +2944,24 @@ int CmdHF14ANdefRead(const char *Cmd) { memcpy(ndef_file + (i - offset), response, segment_size); } - if (fnlen != 0) { - saveFile(filename, ".bin", ndef_file, ndef_size); + if (verbose2) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("NDEF raw") " ----------------"); + print_buffer(ndef_file, ndef_size, 1); } NDEFRecordsDecodeAndPrint(ndef_file, ndef_size, verbose); + + pm3_save_dump(filename, ndef_file, ndef_size, jsfNDEF); + + if (verbose == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf 14a ndefread -v`") " for more details"); + } else { + if (verbose2 == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf 14a ndefread -vv`") " for more details"); + } + } + free(ndef_file); return PM3_SUCCESS; } diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 1ba73e797..5ddd150cb 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1537,17 +1537,21 @@ static int CmdHF14BDump(const char *Cmd) { print_sr_blocks(data, cardsize, card.uid); - if (nosave == false) { - // save to file - if (fnlen < 1) { - PrintAndLogEx(INFO, "using UID as filename"); - char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); - FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); - } - - size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; - pm3_save_dump(filename, data, datalen, jsf14b, ST25TB_SR_BLOCK_SIZE); + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } + + // save to file + if (fnlen < 1) { + PrintAndLogEx(INFO, "using UID as filename"); + char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); + } + + size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; + pm3_save_dump(filename, data, datalen, jsf14b_v2); } return switch_off_field_14b(); @@ -1697,7 +1701,7 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) { // check result int status = resp.oldarg[0]; if (status < 0) { - PrintAndLogEx(ERR, "No card in field."); + PrintAndLogEx(WARNING, "No ISO14443-B Card in field"); switch_off_field_14b(); return PM3_ESOFT; } @@ -2121,9 +2125,13 @@ int CmdHF14BNdefRead(const char *Cmd) { goto out; } - if (fnlen != 0) { - saveFile(filename, ".bin", response + 2, resplen - 4); - } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(response + 2, resplen - 4, &n) != PM3_SUCCESS) + n = resplen - 4; + + pm3_save_dump(filename, response + 2, n, jsfNDEF); + res = NDEFRecordsDecodeAndPrint(response + 2, resplen - 4, verbose); out: @@ -2164,7 +2172,7 @@ static int CmdHF14BView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 75b972871..e9468943a 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -62,7 +62,7 @@ typedef struct { uint8_t lock; - uint8_t block[4]; + uint8_t block[8]; } t15memory_t; // structure and database for uid -> tagtype lookups @@ -849,7 +849,7 @@ static int CmdHF15Info(const char *Cmd) { "hf 15 info -u E011223344556677" ); - void *argtable[6 + 1] = {}; + void *argtable[6 + 1] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_param_end; @@ -1143,14 +1143,14 @@ static int CmdHF15ELoad(const char *Cmd) { static int CmdHF15ESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 esave", - "Save emulator memory into three files (BIN/EML/JSON) ", + "Save emulator memory into two files (bin/json) ", "hf 15 esave -f hf-15-01020304" "hf 15 esave -b 8 -c 42 -f hf-15-01020304" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), - arg_int0("b", "blocksize", "", "block size, defaults to 4"), + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_int0(NULL, "bsize", "", "block size, defaults to 4"), arg_int0("c", "count", "", "number of blocks to export, defaults to all"), arg_param_end }; @@ -1176,13 +1176,18 @@ static int CmdHF15ESave(const char *Cmd) { } PrintAndLogEx(INFO, "Downloading %u bytes from emulator memory", bytes); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } - pm3_save_dump(filename, dump, bytes, jsf15, blocksize); + if (blocksize == 8) { + pm3_save_dump(filename, dump, bytes, jsf15_v3); + } else { + pm3_save_dump(filename, dump, bytes, jsf15_v2); + } + free(dump); return PM3_SUCCESS; } @@ -1244,7 +1249,7 @@ static int CmdHF15EView(const char *Cmd) { } PrintAndLogEx(INFO, "Downloading %u bytes from emulator memory", bytes); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; @@ -1360,12 +1365,13 @@ static int CmdHF15WriteAfi(const char *Cmd) { "hf 15 writeafi -u E011223344556677 --afi 12 -p 0F0F0F0F" ); - void *argtable[5] = {}; - argtable[0] = arg_param_begin; - argtable[1] = arg_str0("u", "uid", "", "full UID, 8 bytes"); - argtable[2] = arg_int1(NULL, "afi", "", "AFI number (0-255)"); - argtable[3] = arg_str0("p", "pwd", "", "optional AFI/EAS password"); - argtable[4] = arg_param_end; + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "full UID, 8 bytes"), + arg_int1(NULL, "afi", "", "AFI number (0-255)"), + arg_str0("p", "pwd", "", "optional AFI/EAS password"), + arg_param_end + }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1443,7 +1449,7 @@ static int CmdHF15WriteDsfid(const char *Cmd) { "hf 15 writedsfid -u E011223344556677 --dsfid 12" ); - void *argtable[6 + 2] = {}; + void *argtable[6 + 2] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_int1(NULL, "dsfid", "", "DSFID number (0-255)"); argtable[arglen++] = arg_param_end; @@ -1537,15 +1543,15 @@ static int CmdHF15WriteDsfid(const char *Cmd) { 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", + "This command dumps the contents of a ISO-15693 tag and save to file (bin/json)", "hf 15 dump\n" "hf 15 dump -*\n" "hf 15 dump -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 2] = {}; + void *argtable[6 + 2] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_str0("f", "file", "", "filename of dump"), + argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"), argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1604,9 +1610,12 @@ static int CmdHF15Dump(const char *Cmd) { // memory. t15memory_t mem[256]; - uint8_t data[256 * 4] = {0}; + uint8_t data[256 * 8] = {0}; memset(data, 0, sizeof(data)); + // keep track of which block length tag returned? + uint8_t blklen = 4; + for (int retry = 0; (retry < 5 && blocknum < 0x100); retry++) { req[10] = blocknum; @@ -1646,9 +1655,15 @@ static int CmdHF15Dump(const char *Cmd) { break; } + // lock byte value mem[blocknum].lock = resp.data.asBytes[0]; - memcpy(mem[blocknum].block, resp.data.asBytes + 1, 4); - memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + + // is tag responding with 4 or 8 bytes? + if (resp.length == 11) { + blklen = 8; + } + memcpy(mem[blocknum].block, resp.data.asBytes + 1, blklen); + memcpy(data + (blocknum * 4), resp.data.asBytes + 1, blklen); retry = 0; blocknum++; @@ -1659,6 +1674,10 @@ static int CmdHF15Dump(const char *Cmd) { DropField(); + if (blklen == 8) { + PrintAndLogEx(INFO, "8 byte block length detected"); + } + PrintAndLogEx(NORMAL, "\n"); PrintAndLogEx(INFO, "block# | data |lck| ascii"); PrintAndLogEx(INFO, "---------+--------------+---+----------"); @@ -1672,9 +1691,9 @@ static int CmdHF15Dump(const char *Cmd) { PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s" , i , i - , sprint_hex(mem[i].block, 4) + , sprint_hex(mem[i].block, blklen) , lck - , sprint_ascii(mem[i].block, 4) + , sprint_ascii(mem[i].block, blklen) ); } PrintAndLogEx(NORMAL, ""); @@ -1687,8 +1706,11 @@ static int CmdHF15Dump(const char *Cmd) { FillFileNameByUID(fptr, SwapEndian64(uid, sizeof(uid), 8), "-dump", sizeof(uid)); } - size_t datalen = blocknum * 4; - pm3_save_dump(filename, data, datalen, jsf15, 4); + if (blklen == 8) { + pm3_save_dump(filename, data, (size_t)(blocknum * blklen), jsf15_v3); + } else { + pm3_save_dump(filename, data, (size_t)(blocknum * blklen), jsf15_v2); + } return PM3_SUCCESS; } @@ -1771,7 +1793,7 @@ static int CmdHF15Readmulti(const char *Cmd) { "hf 15 rdmulti -u E011223344556677 -b 12 --cnt 3 -> read three blocks" ); - void *argtable[6 + 3] = {}; + void *argtable[6 + 3] = {0}; 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)"); @@ -1907,7 +1929,7 @@ static int CmdHF15Readblock(const char *Cmd) { "hf 15 rdbl -u E011223344556677 -b 12" ); - void *argtable[6 + 2] = {}; + void *argtable[6 + 2] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_int1("b", "blk", "", "page number (0-255)"); argtable[arglen++] = arg_param_end; @@ -2000,15 +2022,16 @@ static int CmdHF15Readblock(const char *Cmd) { return PM3_EWRONGANSWER; } + // print response char lck[16] = {0}; if (data[1]) { - snprintf(lck, sizeof(lck), _RED_("%d"), data[1]); + snprintf(lck, sizeof(lck), _RED_("%02X"), data[1]); } else { - snprintf(lck, sizeof(lck), "%d", data[1]); + snprintf(lck, sizeof(lck), "%02X", data[1]); } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, " #%3d |lck| ascii", block); + PrintAndLogEx(INFO, "#%3d |lck| ascii", block); PrintAndLogEx(INFO, "------------+---+------"); PrintAndLogEx(INFO, "%s| %s | %s", sprint_hex(data + 2, resp.length - 4), lck, sprint_ascii(data + 2, resp.length - 4)); PrintAndLogEx(NORMAL, ""); @@ -2068,7 +2091,7 @@ static int CmdHF15Write(const char *Cmd) { "hf 15 wrbl -u E011223344556677 -b 12 -d AABBCCDD" ); - void *argtable[6 + 4] = {}; + void *argtable[6 + 4] = {0}; 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"); @@ -2153,15 +2176,15 @@ static int CmdHF15Write(const char *Cmd) { 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", + "This command restore the contents of a dump file (bin/eml/json) onto a ISO-15693 tag", "hf 15 restore\n" "hf 15 restore -*\n" "hf 15 restore -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 5] = {}; + void *argtable[6 + 5] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_str0("f", "file", "", "filename of dump"), + argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"), 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"); @@ -2834,7 +2857,7 @@ static int CmdHF15View(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), // arg_lit0("z", "dense", "dense dump output style"), arg_param_end }; diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index 552c25590..ef0979182 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -414,9 +414,8 @@ static int CmdHFCryptoRFDump(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } - saveFileEML(filename, data, datalen, 4); - saveFile(filename, ".bin", data, datalen); - // json? + pm3_save_dump(filename, data, datalen, jsfCryptorf); + return switch_off_field_cryptorf(); } @@ -424,13 +423,13 @@ static int CmdHFCryptoRFELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cryptorf eload", - "Loads CryptoRF tag dump into emulator memory on device", + "Loads CryptoRF tag dump (bin/eml/json) 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_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -486,14 +485,14 @@ static int CmdHFCryptoRFESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cryptorf esave", - "Save emulator memory to bin/eml/json file\n" + "Save emulator memory to file (bin/json)\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_str0("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -527,11 +526,7 @@ static int CmdHFCryptoRFESave(const char *Cmd) { 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); + pm3_save_dump(filename, data, numofbytes, jsfCryptorf); free(data); return PM3_SUCCESS; } diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index c8edd2054..2e81ec3a7 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -42,13 +42,9 @@ #define EMRTD_MAX_FILE_SIZE 35000 // ISO7816 commands -#define EMRTD_SELECT 0xA4 -#define EMRTD_EXTERNAL_AUTHENTICATE 0x82 -#define EMRTD_GET_CHALLENGE 0x84 -#define EMRTD_READ_BINARY 0xB0 -#define EMRTD_P1_SELECT_BY_EF 0x02 -#define EMRTD_P1_SELECT_BY_NAME 0x04 -#define EMRTD_P2_PROPRIETARY 0x0C +#define EMRTD_P1_SELECT_BY_EF 0x02 +#define EMRTD_P1_SELECT_BY_NAME 0x04 +#define EMRTD_P2_PROPRIETARY 0x0C // App IDs #define EMRTD_AID_MRTD {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01} @@ -124,7 +120,7 @@ static emrtd_hashalg_t hashalg_table[] = { {"SHA-1", sha1hash, 20, 7, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}}, {"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}}, {"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}}, - {NULL, NULL, 0, 0, {}} + {NULL, NULL, 0, 0, {0}} }; static emrtd_pacealg_t pacealg_table[] = { @@ -145,7 +141,7 @@ static emrtd_pacealg_t pacealg_table[] = { {"ECDH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x02}}, {"ECDH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x03}}, {"ECDH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x04}}, - {NULL, NULL, {}} + {NULL, NULL, {0}} }; static emrtd_pacesdp_t pacesdp_table[] = { @@ -383,25 +379,25 @@ static void _emrtd_convert_fileid(uint16_t file, uint8_t *dataout) { } static int emrtd_select_file_by_name(uint8_t namelen, uint8_t *name) { - return emrtd_exchange_commands_noout((sAPDU_t) {0, EMRTD_SELECT, EMRTD_P1_SELECT_BY_NAME, 0x0C, namelen, name}, false, true); + return emrtd_exchange_commands_noout((sAPDU_t) {0, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_NAME, 0x0C, namelen, name}, false, true); } static int emrtd_select_file_by_ef(uint16_t file_id) { uint8_t data[2]; _emrtd_convert_fileid(file_id, data); - return emrtd_exchange_commands_noout((sAPDU_t) {0, EMRTD_SELECT, EMRTD_P1_SELECT_BY_EF, 0x0C, sizeof(data), data}, false, true); + return emrtd_exchange_commands_noout((sAPDU_t) {0, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_EF, 0x0C, sizeof(data), data}, false, true); } static int emrtd_get_challenge(int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - return emrtd_exchange_commands((sAPDU_t) {0, EMRTD_GET_CHALLENGE, 0, 0, 0, NULL}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); + return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_GET_CHALLENGE, 0, 0, 0, NULL}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); } static int emrtd_external_authenticate(uint8_t *data, int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - return emrtd_exchange_commands((sAPDU_t) {0, EMRTD_EXTERNAL_AUTHENTICATE, 0, 0, length, data}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); + return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_EXTERNAL_AUTHENTICATION, 0, 0, length, data}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); } static int _emrtd_read_binary(int offset, int bytes_to_read, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - return emrtd_exchange_commands((sAPDU_t) {0, EMRTD_READ_BINARY, offset >> 8, offset & 0xFF, 0, NULL}, true, bytes_to_read, dataout, maxdataoutlen, dataoutlen, false, true); + return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_READ_BINARY, offset >> 8, offset & 0xFF, 0, NULL}, true, bytes_to_read, dataout, maxdataoutlen, dataoutlen, false, true); } static void emrtd_bump_ssc(uint8_t *ssc) { @@ -503,7 +499,7 @@ static bool emrtd_secure_select_file_by_ef(uint8_t *kenc, uint8_t *kmac, uint8_t memcpy(data + (datalen + 3), do8e, 10); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); - if (emrtd_exchange_commands((sAPDU_t) {0x0C, EMRTD_SELECT, EMRTD_P1_SELECT_BY_EF, 0x0C, lc, data}, true, 0, response, sizeof(response), &resplen, false, true) == false) { + if (emrtd_exchange_commands((sAPDU_t) {0x0C, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_EF, 0x0C, lc, data}, true, 0, response, sizeof(response), &resplen, false, true) == false) { return false; } @@ -552,7 +548,7 @@ static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, i memcpy(data + 3, do8e, 10); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); - if (emrtd_exchange_commands((sAPDU_t) {0x0C, EMRTD_READ_BINARY, offset >> 8, offset & 0xFF, lc, data}, true, 0, dataout, maxdataoutlen, dataoutlen, false, true) == false) { + if (emrtd_exchange_commands((sAPDU_t) {0x0C, ISO7816_READ_BINARY, offset >> 8, offset & 0xFF, lc, data}, true, 0, dataout, maxdataoutlen, dataoutlen, false, true) == false) { return false; } diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 191a481de..230628c51 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -566,7 +566,7 @@ static void reverse_3des_key(const uint8_t *master_key, int length, uint8_t *rev for (int i = 0; i < length; i++) { reverse_master_key[i] = master_key[(length - 1) - i]; } -}; +} /** * Command parser for auth1 diff --git a/client/src/cmdhffudan.c b/client/src/cmdhffudan.c index 53e580e1b..85e857e6e 100644 --- a/client/src/cmdhffudan.c +++ b/client/src/cmdhffudan.c @@ -235,14 +235,14 @@ static int CmdHFFudanReader(const char *Cmd) { static int CmdHFFudanDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fudan dump", - "Dump FUDAN tag to binary file\n" + "Dump FUDAN tag to file (bin/json)\n" "If no given, UID will be used as filename", "hf fudan dump -f mydump --> dump using filename\n" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -339,14 +339,7 @@ static int CmdHFFudanDump(const char *Cmd) { free(fptr); } - saveFile(dataFilename, ".bin", (uint8_t *)carddata, sizeof(carddata)); - saveFileEML(dataFilename, (uint8_t *)carddata, sizeof(carddata), MAX_FUDAN_BLOCK_SIZE); - - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = (uint8_t *)carddata; - xdump.dumplen = sizeof(carddata); - saveFileJSON(dataFilename, jsfFudan, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_dump(dataFilename, (uint8_t *)carddata, sizeof(carddata), jsfFudan); return PM3_SUCCESS; } @@ -460,7 +453,7 @@ static int CmdHFFudanView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 86dda89cd..313ecf2ce 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -19,8 +19,8 @@ #include "cmdhficlass.h" #include #include "cliparser.h" -#include "cmdparser.h" // command_t -#include "commonutil.h" // ARRAYLEN +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN #include "cmdtrace.h" #include "util_posix.h" #include "comms.h" @@ -34,13 +34,13 @@ #include "cardhelper.h" #include "wiegand_formats.h" #include "wiegand_formatutils.h" -#include "cmdsmartcard.h" // smart select fct +#include "cmdsmartcard.h" // smart select fct #include "proxendian.h" #include "iclass_cmd.h" -#include "crypto/asn1utils.h" // ASN1 decoder +#include "crypto/asn1utils.h" // ASN1 decoder #include "preferences.h" -#define PICOPASS_BLOCK_SIZE 8 + #define NUM_CSNS 9 #define MAC_ITEM_SIZE 24 // csn(8) + epurse(8) + nr(4) + mac(4) = 24 bytes #define ICLASS_KEYS_MAX 8 @@ -60,7 +60,7 @@ static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static int CmdHelp(const char *Cmd); -static void printIclassSIO(uint8_t *iclass_dump); +static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len); static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][PICOPASS_BLOCK_SIZE] = { { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }, @@ -196,22 +196,20 @@ static uint8_t card_app2_limit[] = { 0xff, }; -static iclass_config_card_item_t iclass_config_types[14] = { - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - // must be the last entry - {"no config card info available", ""} +static iclass_config_card_item_t iclass_config_types[13] = { + {"Audio/Visual #1 - Beep ON, LED Off, Flash GREEN on read", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x8F, 0xA7, 0x80, 0xA9, 0x01}}, + {"Audio/Visual #2 - Beep ON, LED RED, Host must flash GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x18, 0xAC, 0x00, 0xA8, 0x1F, 0xA7, 0x80, 0xA9, 0x01}}, + {"Audio/Visual #3 - Beep ON, LED Off, Host must flash RED and/or GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x0F, 0xA9, 0x03, 0xA7, 0x80}}, + {"Keypad Output #1 - Buffer ONE key (8 bit Dorado)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"Keypad Output #2 - Buffer ONE to FIVE keys (standard 26 bit)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x0B, 0xAF, 0xFF, 0xAD, 0x15, 0xB3, 0x03}}, + {"Keypad Output #3 - Local PIN verify", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAD, 0x6D, 0xB3, 0x03, 0x00, 0x00, 0x00, 0x00}}, + {"Mifare CSN #1 - 32 bit reverse output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x01, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, + {"Mifare CSN #2 - 16 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x02, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, + {"Mifare CSN #3 - 34 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x03, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, + {"Keyroll DISABLE - Set ELITE Key and DISABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"Keyroll ENABLE - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"Reset READER - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"Reset ENROLLER - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}} }; static bool check_config_card(const iclass_config_card_item_t *o) { @@ -260,7 +258,7 @@ static const iclass_config_card_item_t *get_config_card_item(int idx) { 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) { + for (int i = 0; i < ARRAYLEN(iclass_config_types) ; ++i) { PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_types[i].desc); } PrintAndLogEx(NORMAL, ""); @@ -274,6 +272,16 @@ static void print_config_card(const iclass_config_card_item_t *o) { } } +static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { + uint8_t encrypted_data[16]; + uint8_t *encrypted = encrypted_data; + mbedtls_des3_context ctx; + mbedtls_des3_set2key_enc(&ctx, key); + mbedtls_des3_crypt_ecb(&ctx, blk_data, encrypted); + memcpy(blk_data, encrypted, 8); + mbedtls_des3_free(&ctx); +} + 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; @@ -353,11 +361,37 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke SetFlushAfterWrite(true); // KEYROLL need to encrypt + uint8_t key_en[16] = {0}; + uint8_t *keyptr_en = NULL; + if (IsCardHelperPresent(false) == false) { + size_t keylen = 0; + int res_key = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr_en, &keylen); + if (res_key != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to find iclass_decryptionkey.bin"); + free(data); + return PM3_EINVARG; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr_en); + free(data); + return PM3_EINVARG; + } + memcpy(key_en, keyptr_en, sizeof(key_en)); + free(keyptr_en); + } + PrintAndLogEx(INFO, "Setting up encryption... " NOLF); uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - if (Encrypt(ffs, ffs) == false) { - PrintAndLogEx(WARNING, "failed to encrypt FF"); + if (IsCardHelperPresent(false) != false) { + if (Encrypt(ffs, ffs) == false) { + PrintAndLogEx(WARNING, "failed to encrypt FF"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + } } else { + iclass_encrypt_block_data(ffs, key_en); PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); } @@ -366,9 +400,14 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke uint8_t lkey[8]; memcpy(lkey, key, sizeof(lkey)); uint8_t enckey1[8]; - if (Encrypt(lkey, enckey1) == false) { - PrintAndLogEx(WARNING, "failed to encrypt key1"); + if (IsCardHelperPresent(false) != false) { + if (Encrypt(lkey, enckey1) == false) { + PrintAndLogEx(WARNING, "failed to encrypt key1"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + } } else { + iclass_encrypt_block_data(lkey, key_en); PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); } @@ -377,9 +416,13 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data + (6 * 8), o->data, sizeof(o->data)); // encrypted keyroll key 0D - memcpy(data + (0xD * 8), enckey1, sizeof(enckey1)); + if (IsCardHelperPresent(false) != false) { + memcpy(data + (0x0D * 8), enckey1, sizeof(enckey1)); + } else { + memcpy(data + (0x0D * 8), lkey, sizeof(enckey1)); + } // encrypted 0xFF - for (uint8_t i = 0xD; i < 0x14; i++) { + for (uint8_t i = 0x0E; i < 0x14; i++) { memcpy(data + (i * 8), ffs, sizeof(ffs)); } PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); @@ -387,24 +430,37 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // encrypted partial keyroll key 14 PrintAndLogEx(INFO, "Setting encrypted partial key14... " NOLF); uint8_t foo[8] = {0x15}; - memcpy(foo + 1, lkey, 7); + memcpy(foo + 1, key, 7); uint8_t enckey2[8]; - if (Encrypt(foo, enckey2) == false) { - PrintAndLogEx(WARNING, "failed to encrypt partial 1"); + if (IsCardHelperPresent(false) != false) { + if (Encrypt(foo, enckey2) == false) { + PrintAndLogEx(WARNING, "failed to encrypt partial 1"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x14 * 8), enckey2, sizeof(enckey2)); + } + } else { + iclass_encrypt_block_data(foo, key_en); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x14 * 8), foo, sizeof(enckey2)); } - memcpy(data + (0x14 * 8), enckey2, sizeof(enckey2)); - PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); - // encrypted partial keyroll key 15 PrintAndLogEx(INFO, "Setting encrypted partial key15... " NOLF); memset(foo, 0xFF, sizeof(foo)); - foo[0] = lkey[7]; - if (Encrypt(foo, enckey2) == false) { - PrintAndLogEx(WARNING, "failed to encrypt partial 2"); + foo[0] = key[7]; + if (IsCardHelperPresent(false) != false) { + if (Encrypt(foo, enckey2) == false) { + PrintAndLogEx(WARNING, "failed to encrypt partial 2"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x15 * 8), enckey2, sizeof(enckey2)); + } + } else { + iclass_encrypt_block_data(foo, key_en); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x15 * 8), foo, sizeof(enckey2)); } - memcpy(data + (0x15 * 8), enckey2, sizeof(enckey2)); - PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); // encrypted 0xFF PrintAndLogEx(INFO, "Setting 0xFF's... " NOLF); @@ -1009,14 +1065,14 @@ static int CmdHFiClassReader(const char *Cmd) { static int CmdHFiClassELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass eload", - "Load emulator memory with data from (bin/eml/json) iCLASS dump file", - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml\n" + "Load emulator memory with data from (bin/json) iCLASS dump file", + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.json\n" "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -m\n" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("m", "mem", "use RDV4 spiffs"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -1103,7 +1159,7 @@ static int CmdHFiClassELoad(const char *Cmd) { static int CmdHFiClassESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass esave", - "Save emulator memory to file.\n" + "Save emulator memory to file (bin/json)\n" "if filename is not supplied, CSN will be used.", "hf iclass esave\n" "hf iclass esave -f hf-iclass-dump\n" @@ -1111,7 +1167,7 @@ static int CmdHFiClassESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump file"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), arg_param_end }; @@ -1149,7 +1205,7 @@ static int CmdHFiClassESave(const char *Cmd) { FillFileNameByUID(fptr, dump, "-dump", 8); } - pm3_save_dump(filename, dump, bytes, jsfIclass, PICOPASS_BLOCK_SIZE); + pm3_save_dump(filename, dump, bytes, jsfIclass); free(dump); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); @@ -1215,7 +1271,7 @@ static int CmdHFiClassEView(const char *Cmd) { printIclassDumpContents(dump, 1, blocks, bytes, dense_output); if (verbose) { - printIclassSIO(dump); + print_iclass_sio(dump, bytes); } free(dump); @@ -1226,11 +1282,11 @@ static int CmdHFiClassESetBlk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass esetblk", "Sets an individual block in emulator memory.", - "hf iclass esetblk -b 7 -d 0000000000000000"); + "hf iclass esetblk --blk 7 -d 0000000000000000"); void *argtable[] = { arg_param_begin, - arg_int1("b", "blk", "", "block number"), + arg_int1(NULL, "blk", "", "block number"), arg_str0("d", "data", "", "bytes to write, 8 hex bytes"), arg_param_end }; @@ -1265,35 +1321,104 @@ static int CmdHFiClassESetBlk(const char *Cmd) { return PM3_SUCCESS; } +static bool iclass_detect_new_pacs(uint8_t *d) { + uint8_t n = 0; + while (n++ < (PICOPASS_BLOCK_SIZE / 2)) { + if (d[n] && d[n + 1] == 0xA6) { + return true; + } + } + return false; +} + +// block 7 decoder for PACS +static int iclass_decode_credentials_new_pacs(uint8_t *d) { + + uint8_t offset = 0; + while (d[offset] == 0 && (offset < PICOPASS_BLOCK_SIZE / 2)) { + offset++; + } + + uint8_t pad = d[offset]; + + PrintAndLogEx(INFO, "%u , %u", offset, pad); + + char *binstr = (char *)calloc((PICOPASS_BLOCK_SIZE * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return PM3_EMALLOC; + } + + uint8_t n = PICOPASS_BLOCK_SIZE - offset - 2; + byte_2_binstr(binstr, d + offset + 2, n); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); + PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + free(binstr); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + + return PM3_SUCCESS; +} + static void iclass_decode_credentials(uint8_t *data) { - if (memcmp(data + (5 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + picopass_hdr_t *hdr = (picopass_hdr_t *)data; + if (memcmp(hdr->app_issuer_area, empty, PICOPASS_BLOCK_SIZE)) { // Not a Legacy or SR card, nothing to do here. return; } - BLOCK79ENCRYPTION encryption = (data[(6 * 8) + 7] & 0x03); - bool has_values = (memcmp(data + (8 * 7), empty, 8) != 0) && (memcmp(data + (8 * 7), zeros, 8) != 0); + BLOCK79ENCRYPTION encryption = (data[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03); + + uint8_t *b7 = data + (PICOPASS_BLOCK_SIZE * 7); + + bool has_new_pacs = iclass_detect_new_pacs(b7); + bool has_values = (memcmp(b7, empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(b7, zeros, PICOPASS_BLOCK_SIZE) != 0); if (has_values && encryption == None) { - //todo: remove preamble/sentinel - uint32_t top = 0, mid = 0, bot = 0; - + // todo: remove preamble/sentinel PrintAndLogEx(INFO, "Block 7 decoder"); - char hexstr[16 + 1] = {0}; - hex_to_buffer((uint8_t *)hexstr, data + (8 * 7), 8, sizeof(hexstr) - 1, 0, 0, true); - hexstring_to_u96(&top, &mid, &bot, hexstr); + if (has_new_pacs) { + iclass_decode_credentials_new_pacs(b7); + } else { + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, b7, PICOPASS_BLOCK_SIZE, sizeof(hexstr) - 1, 0, 0, true); - char binstr[64 + 1]; - hextobinstring(binstr, hexstr); - char *pbin = binstr; - while (strlen(pbin) && *(++pbin) == '0'); + uint32_t top = 0, mid = 0, bot = 0; + hexstring_to_u96(&top, &mid, &bot, hexstr); - PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); + + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + } - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); } else { PrintAndLogEx(INFO, "No unencrypted legacy credential found"); } @@ -1308,7 +1433,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { "which is defined by the configuration block.\n" "\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" + "in the resources directory. The file must 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" @@ -1317,7 +1442,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump file (bin/eml/json)"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_str0("d", "data", "", "3DES encrypted data"), arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), @@ -1497,12 +1622,12 @@ static int CmdHFiClassDecrypt(const char *Cmd) { strcat(fptr, "hf-iclass-"); FillFileNameByUID(fptr, hdr->csn, "-dump-decrypted", sizeof(hdr->csn)); - pm3_save_dump(fptr, decrypted, decryptedlen, jsfIclass, PICOPASS_BLOCK_SIZE); + pm3_save_dump(fptr, decrypted, decryptedlen, jsfIclass); printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen, dense_output); if (verbose) { - printIclassSIO(decrypted); + print_iclass_sio(decrypted, decryptedlen); } PrintAndLogEx(NORMAL, ""); @@ -1546,16 +1671,6 @@ static int CmdHFiClassDecrypt(const char *Cmd) { return PM3_SUCCESS; } -static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { - uint8_t encrypted_data[16]; - uint8_t *encrypted = encrypted_data; - mbedtls_des3_context ctx; - mbedtls_des3_set2key_enc(&ctx, key); - mbedtls_des3_crypt_ecb(&ctx, blk_data, encrypted); - memcpy(blk_data, encrypted, 8); - mbedtls_des3_free(&ctx); -} - static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass encrypt", @@ -1705,6 +1820,7 @@ static int CmdHFiClassDump(const char *Cmd) { arg_lit0("z", "dense", "dense dump output style"), arg_lit0(NULL, "force", "force unsecure card read"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1791,6 +1907,7 @@ static int CmdHFiClassDump(const char *Cmd) { bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); bool force = arg_get_lit(ctx, 10); bool shallow_mod = arg_get_lit(ctx, 11); + bool nosave = arg_get_lit(ctx, 12); CLIParserFree(ctx); @@ -2036,6 +2153,12 @@ write_dump: // print the dump printIclassDumpContents(tag_data, 1, (bytes_got / 8), bytes_got, dense_output); + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + // use CSN as filename if (filename[0] == 0) { strcat(filename, "hf-iclass-"); @@ -2045,7 +2168,7 @@ write_dump: // save the dump to .bin file PrintAndLogEx(SUCCESS, "saving dump file - %u blocks read", bytes_got / 8); - pm3_save_dump(filename, tag_data, bytes_got, jsfIclass, PICOPASS_BLOCK_SIZE); + pm3_save_dump(filename, tag_data, bytes_got, jsfIclass); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt -f") "` to decrypt dump file"); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); @@ -2085,6 +2208,7 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); return resp.status; } + return (resp.data.asBytes[0] == 1) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2092,15 +2216,15 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass wrbl", "Write data to an iCLASS tag", - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA --ki 0"); + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0"); void *argtable[] = { arg_param_begin, 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("b", "block", "", "The block number to read"), + arg_int1(NULL, "blk", "", "block number"), arg_str1("d", "data", "", "data to write as 8 hex bytes"), arg_str0("m", "mac", "", "replay mac data (4 hex bytes)"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), @@ -2189,7 +2313,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { int isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); switch (isok) { case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Wrote block %3d/0x%02X successful", blockno, blockno); + PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno); break; case PM3_ETEAROFF: if (verbose) @@ -2199,6 +2323,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { PrintAndLogEx(FAILED, "Writing failed"); break; } + PrintAndLogEx(NORMAL, ""); return isok; } @@ -2328,7 +2453,7 @@ static int CmdHFiClassCreditEpurse(const char *Cmd) { static int CmdHFiClassRestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass restore", - "Restore data from dumpfile onto a iCLASS tag", + "Restore data from dumpfile (bin/eml/json) onto a iCLASS tag", "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0\n" "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite\n" "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 -k 1122334455667788 --elite\n" @@ -2336,7 +2461,7 @@ static int CmdHFiClassRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "specify a filename to restore (bin/eml/json)"), + arg_str1("f", "file", "", "specify a filename to restore"), 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"), @@ -2550,15 +2675,15 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass rdbl", "Read a iCLASS block from tag", - "hf iclass rdbl -b 6 -k 0011223344556677\n" - "hf iclass rdbl -b 27 -k 0011223344556677 --credit\n" - "hf iclass rdbl -b 10 --ki 0"); + "hf iclass rdbl --blk 6 -k 0011223344556677\n" + "hf iclass rdbl --blk 27 -k 0011223344556677 --credit\n" + "hf iclass rdbl --blk 10 --ki 0"); void *argtable[] = { arg_param_begin, 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("b", "block", "", "The block number to read"), + arg_int1(NULL, "blk", "", "Block number"), 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"), @@ -2659,9 +2784,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } case 7: { - uint8_t dec_data[8]; + uint8_t dec_data[PICOPASS_BLOCK_SIZE]; - uint64_t a = bytes_to_num(data, 8); + uint64_t a = bytes_to_num(data, PICOPASS_BLOCK_SIZE); bool starts = (leadingzeros(a) < 12); bool ones = (bitcount64(a) > 16 && bitcount64(a) < 48); @@ -2674,25 +2799,32 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { PrintAndLogEx(INFO, "data looks unencrypted, trying to decode"); } - if (memcmp(dec_data, empty, 8) != 0) { + bool has_new_pacs = iclass_detect_new_pacs(dec_data); + bool has_values = (memcmp(dec_data, empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(dec_data, zeros, PICOPASS_BLOCK_SIZE) != 0); - //todo: remove preamble/sentinel - uint32_t top = 0, mid = 0, bot = 0; + if (has_values) { - char hexstr[16 + 1] = {0}; - hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); - hexstring_to_u96(&top, &mid, &bot, hexstr); + if (has_new_pacs) { + iclass_decode_credentials_new_pacs(dec_data); + } else { + //todo: remove preamble/sentinel + uint32_t top = 0, mid = 0, bot = 0; - char binstr[64 + 1]; - hextobinstring(binstr, hexstr); - char *pbin = binstr; - while (strlen(pbin) && *(++pbin) == '0'); + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, dec_data, PICOPASS_BLOCK_SIZE, sizeof(hexstr) - 1, 0, 0, true); + hexstring_to_u96(&top, &mid, &bot, hexstr); - PrintAndLogEx(SUCCESS, " bin : %s", pbin); - PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "------------------------------ " _CYAN_("Wiegand") " -------------------------------"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, " bin : %s", pbin); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "------------------------------ " _CYAN_("Wiegand") " -------------------------------"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + } } else { PrintAndLogEx(INFO, "no credential found"); } @@ -2750,67 +2882,99 @@ static int CmdHFiClass_loclass(const char *Cmd) { return bruteforceFileNoKeys(filename); } -static void detect_credential(uint8_t *data, bool *legacy, bool *se, bool *sr) { - *legacy = false; - *sr = false; - *se = false; +static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_legacy, bool *is_se, bool *is_sr, uint8_t **sio_start_ptr, size_t *sio_length) { + *is_legacy = false; + *is_sr = false; + *is_se = false; + if (sio_start_ptr != NULL) { + *sio_start_ptr = NULL; + } + if (sio_length != NULL) { + *sio_length = 0; + } - // Legacy AIA - if (!memcmp(data + (5 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { - *legacy = true; + if (dump_len < sizeof(picopass_hdr_t)) { + // Can't really do anything with a dump that doesn't include the header + return; + } - // SR bit set in legacy config block - if ((data[6 * PICOPASS_BLOCK_SIZE] & ICLASS_CFG_BLK_SR_BIT) == ICLASS_CFG_BLK_SR_BIT) { - // If the card is blank (all FF's) then we'll reach here too, so check for an empty block 10 - // to avoid false positivies - if (memcmp(data + (10 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { - *sr = true; - } + picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump; + + if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + // Legacy AIA + *is_legacy = true; + + if (dump_len < 11 * PICOPASS_BLOCK_SIZE) { + // Can't reliably detect if the card is SR without checking + // blocks 6 and 10 + return; } + // SR bit set in legacy config block + if ((iclass_dump[6 * PICOPASS_BLOCK_SIZE] & ICLASS_CFG_BLK_SR_BIT) == ICLASS_CFG_BLK_SR_BIT) { + // If the card is blank (all FF's) then we'll reach here too, so check for an empty block 10 + // to avoid false positivies + if (memcmp(iclass_dump + (10 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + *is_sr = true; + if (sio_start_ptr != NULL) { + // SR SIO starts at block 10 + *sio_start_ptr = iclass_dump + (10 * PICOPASS_BLOCK_SIZE); + } + } + } + } else if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + // SE AIA + *is_se = true; + + if (sio_start_ptr != NULL) { + // SE SIO starts at block 6 + *sio_start_ptr = iclass_dump + (6 * PICOPASS_BLOCK_SIZE); + } + } + + if (sio_length == NULL || sio_start_ptr == NULL || *sio_start_ptr == NULL) { + // No need to calculate length return; } - // SE AIA - if (!memcmp(data + (5 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { - *se = true; + uint8_t *sio_start = *sio_start_ptr; + + if (sio_start[0] != 0x30) { + // SIOs always start with a SEQUENCE(P), if this is missing then bail return; } + + if (sio_start[1] >= 0x80 || sio_start[1] == 0x00) { + // We only support definite short form lengths + return; + } + + // Length of bytes within the SEQUENCE, plus tag and length bytes for the SEQUENCE tag + *sio_length = sio_start[1] + 2; } // print ASN1 decoded array in TLV view -static void printIclassSIO(uint8_t *iclass_dump) { - bool isLegacy, isSE, isSR; - detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); - +static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len) { + bool is_legacy, is_se, is_sr; uint8_t *sio_start; - if (isSE) { - // SE SIO starts at block 6 - sio_start = iclass_dump + (6 * PICOPASS_BLOCK_SIZE); - } else if (isSR) { - // SR SIO starts at block 10 - sio_start = iclass_dump + (10 * PICOPASS_BLOCK_SIZE); - } else { - // No SIO on Legacy credentials + size_t sio_length; + detect_credential(iclass_dump, dump_len, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); + + if (sio_start == NULL) { return; } - // Readers assume the SIO always fits within 7 blocks (they don't read any further blocks) - // Search backwards to find the last 0x05 0x00 seen at the end of the SIO - const uint8_t pattern_sio_end[] = {0x05, 0x00}; - int dlen = byte_strrstr(sio_start, 7 * PICOPASS_BLOCK_SIZE, pattern_sio_end, 2); - if (dlen == -1) { + if (dump_len < sio_length + (sio_start - iclass_dump)) { + // SIO length exceeds the size of the dump we have, bail return; } - dlen += sizeof(pattern_sio_end); - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------"); - print_hex_noascii_break(sio_start, dlen, 32); + print_hex_noascii_break(sio_start, sio_length, 32); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - ASN1 TLV") " --------------------------"); - asn1_print(sio_start, dlen, " "); + asn1_print(sio_start, sio_length, " "); PrintAndLogEx(NORMAL, ""); } @@ -2862,9 +3026,17 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e ); */ - bool isLegacy = false, isSE = false, isSR = false; - if (filemaxblock >= 17) { - detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); + bool is_legacy, is_se, is_sr; + uint8_t *sio_start; + size_t sio_length; + detect_credential(iclass_dump, endblock * 8, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); + + bool is_legacy_decrypted = is_legacy && (iclass_dump[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03) == 0x00; + + int sio_start_block = 0, sio_end_block = 0; + if (sio_start && sio_length > 0) { + sio_start_block = (sio_start - iclass_dump) / PICOPASS_BLOCK_SIZE; + sio_end_block = sio_start_block + (sio_length + PICOPASS_BLOCK_SIZE - 1) / PICOPASS_BLOCK_SIZE - 1; } int i = startblock; @@ -2937,32 +3109,25 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e } else { const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; - if (i >= 6 && i <= 9 && isLegacy && isSE == false) { + if (i >= 6 && i <= 9 && is_legacy) { // legacy credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _YELLOW_("%s") "| " _YELLOW_("%s") " | %s | User / Cred " + PrintAndLogEx(INFO, "%3d/0x%02X | " _YELLOW_("%s") "| " _YELLOW_("%s") " | %s | User / %s " , i , i , sprint_hex(blk, 8) , sprint_ascii(blk, 8) , lockstr + , i == 6 ? "HID CFG" : (is_legacy_decrypted ? "Cred" : "Enc Cred") ); - } else if (i >= 6 && i <= 12 && isSE) { + } else if (sio_start_block != 0 && i >= sio_start_block && i <= sio_end_block) { // SIO credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / SE" - , i - , i - , sprint_hex(blk, 8) - , sprint_ascii(blk, 8) - , lockstr - ); - } else if (i >= 10 && i <= 16 && isSR) { - // SIO credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / SR" + PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / %s" , i , i , sprint_hex(blk, 8) , sprint_ascii(blk, 8) , lockstr + , is_se ? "SE" : "SR" ); } else { if (i < 6) { @@ -2977,7 +3142,7 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e if (regular_print_block) { // suppress repeating blocks, truncate as such that the first and last block with the same data is shown - // but the blocks in between are replaced with a single line of "*" if dense_output is enabled + // but the blocks in between are replaced with a single line of "......" if dense_output is enabled if (dense_output && i > 6 && i < (endblock - 1) && !in_repeated_block && !memcmp(blk, blk - 8, 8) && !memcmp(blk, blk + 8, 8) && !memcmp(blk, blk + 16, 8)) { // we're in a user block that isn't the first user block nor last two user blocks, @@ -3003,13 +3168,13 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e i++; } PrintAndLogEx(INFO, "---------+-------------------------+----------+---+----------------"); - if (isLegacy) + if (is_legacy) PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential"); - if (isSE) + if (is_se) PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SE credential"); - if (isSR) + if (is_sr) PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SR credential"); PrintAndLogEx(NORMAL, ""); @@ -3026,7 +3191,7 @@ static int CmdHFiClassView(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_int0(NULL, "first", "", "Begin printing from this block (default first user block)"), arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), @@ -3067,7 +3232,7 @@ static int CmdHFiClassView(const char *Cmd) { iclass_decode_credentials(dump); if (verbose) { - printIclassSIO(dump); + print_iclass_sio(dump, bytes_read); } free(dump); @@ -3131,6 +3296,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { arg_str0(NULL, "csn", "", "Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)"), arg_lit0(NULL, "elite", "Elite computations applied to new key"), arg_lit0(NULL, "elite2", "Elite computations applied to both old and new key"), + arg_lit0(NULL, "oldelite", "Elite computations applied only to old key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3223,6 +3389,11 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { old_elite = true; } + if (arg_get_lit(ctx, 8)) { + elite = false; + old_elite = true; + } + CLIParserFree(ctx); uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -3240,7 +3411,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { return PM3_SUCCESS; } -static int loadKeys(char *filename) { +static int iclass_load_keys(char *filename) { uint8_t *dump = NULL; size_t bytes_read = 0; @@ -3249,46 +3420,30 @@ static int loadKeys(char *filename) { return PM3_EFILE; } - if (bytes_read > ICLASS_KEYS_MAX * 8) { + if (bytes_read > ICLASS_KEYS_MAX * PICOPASS_BLOCK_SIZE) { PrintAndLogEx(WARNING, "File is too long to load - bytes: %zu", bytes_read); free(dump); return PM3_EFILE; } size_t i = 0; - for (; i < bytes_read / 8; i++) - memcpy(iClass_Key_Table[i], dump + (i * 8), 8); + for (; i < bytes_read / PICOPASS_BLOCK_SIZE; i++) { + memcpy(iClass_Key_Table[i], dump + (i * PICOPASS_BLOCK_SIZE), PICOPASS_BLOCK_SIZE); + } free(dump); PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2zd") " keys from %s", i, filename); return PM3_SUCCESS; } -static int saveKeys(char *filename) { - FILE *f; - f = fopen(filename, "wb"); - if (!f) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); - return PM3_EFILE; - } - for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++) { - if (fwrite(iClass_Key_Table[i], 8, 1, f) != 1) { - PrintAndLogEx(WARNING, "save key failed to write to file:" _YELLOW_("%s"), filename); - break; - } - } - fclose(f); - return PM3_SUCCESS; -} - -static int printKeys(void) { +static int iclass_print_keys(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "idx| key"); PrintAndLogEx(INFO, "---+------------------------"); for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++) { - if (memcmp(iClass_Key_Table[i], "\x00\x00\x00\x00\x00\x00\x00\x00", 8) == 0) + if (memcmp(iClass_Key_Table[i], zeros, sizeof(zeros)) == 0) PrintAndLogEx(INFO, " %u |", i); else - PrintAndLogEx(INFO, " %u | " _YELLOW_("%s"), i, sprint_hex(iClass_Key_Table[i], 8)); + PrintAndLogEx(INFO, " %u | " _YELLOW_("%s"), i, sprint_hex(iClass_Key_Table[i], PICOPASS_BLOCK_SIZE)); } PrintAndLogEx(INFO, "---+------------------------"); PrintAndLogEx(NORMAL, ""); @@ -3381,11 +3536,15 @@ static int CmdHFiClassManageKeys(const char *Cmd) { PrintAndLogEx(SUCCESS, " New key[%d] " _GREEN_("%s"), key_nr, sprint_hex_inrow(iClass_Key_Table[key_nr], 8)); return PM3_SUCCESS; case 4: - return printKeys(); + return iclass_print_keys(); case 5: - return loadKeys(filename); - case 6: - return saveKeys(filename); + return iclass_load_keys(filename); + case 6: { + bool isOK = saveFile(filename, ".bin", iClass_Key_Table, sizeof(iClass_Key_Table)); + if (isOK == false) { + return PM3_EFILE; + } + } } return PM3_SUCCESS; } @@ -4277,11 +4436,11 @@ 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" + "Manage reader configuration card via Cardhelper or internal database,\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 -l --> download config card settings from cardhelper\n" + "hf iclass configcard -p --> print all config cards in the database\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" ); @@ -4327,12 +4486,14 @@ static int CmdHFiClassConfigCard(const char *Cmd) { print_config_cards(); } - if (ccidx > -1 && ccidx < 14) { + if (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types)) { const iclass_config_card_item_t *item = get_config_card_item(ccidx); print_config_card(item); + } else { + PrintAndLogEx(ERR, "Please specify a valid configuration number!"); } - if (do_generate) { + if (do_generate && (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types))) { const iclass_config_card_item_t *item = get_config_card_item(ccidx); if (strstr(item->desc, "Keyroll") != NULL) { if (got_kr == false) { @@ -4349,42 +4510,118 @@ static int CmdHFiClassConfigCard(const char *Cmd) { static int CmdHFiClassSAM(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass sam", - "Manage via SAM\n", + "Extract PACS via a HID SAM\n", "hf iclass sam\n" ); void *argtable[] = { arg_param_begin, - arg_str0("d", "data", "", "data"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - int dlen = 0; - uint8_t data[128] = {0}; - CLIGetHexWithReturn(ctx, 1, data, &dlen); - - bool verbose = arg_get_lit(ctx, 2); + bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); - Iso7816CommandChannel channel = CC_CONTACT; - if (IfPm3Smartcard() == false) { - if (channel == CC_CONTACT) { - PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support, exiting"); - return PM3_EDEVNOTSUPP; - } + if (IsHIDSamPresent(verbose) == false) { + return PM3_ESOFT; } - int res = IsHIDSamPresent(verbose); - if (res != PM3_SUCCESS) { - return res; + clearCommandBuffer(); + SendCommandNG(CMD_HF_SAM_PICOPASS, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_SAM_PICOPASS, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "SAM timeout"); + return PM3_ETIMEOUT; } - SetAPDULogging(verbose); + switch (resp.status) { + case PM3_SUCCESS: + break; + case PM3_ENOPACS: + PrintAndLogEx(SUCCESS, "No PACS data found. Card empty?"); + return resp.status; + default: + PrintAndLogEx(WARNING, "SAM select failed"); + return resp.status; + } -// do things with sending apdus.. + // CSN, config, epurse, NR/MAC, AIA + // PACS + // first byte skip + // second byte length + // third padded + // fourth .. + uint8_t *d = resp.data.asBytes; + uint8_t n = d[1] - 1; // skip length byte + uint8_t pad = d[2]; + char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return PM3_EMALLOC; + } + + byte_2_binstr(binstr, d + 3, n); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2)); + PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + + PrintAndLogEx(NORMAL, ""); + + if (strlen(binstr) >= 26 && verbose) { + + // iCLASS Legacy + PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); + PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // HID Prox II + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); + PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // MIFARE Classic + char mfcbin[28] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, binstr, strlen(binstr)); + binstr_2_bytes(hex, &hexlen, mfcbin); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); + PrintAndLogEx(SUCCESS, " hf mf eclr;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); + PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); + PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + } + free(binstr); - SetAPDULogging(false); return PM3_SUCCESS; } @@ -4394,14 +4631,14 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("general") " ---------------------"}, // {"clone", CmdHFiClassClone, IfPm3Iclass, "Create a HID credential to Picopass / iCLASS tag"}, {"dump", CmdHFiClassDump, IfPm3Iclass, "Dump Picopass / iCLASS tag to file"}, - {"info", CmdHFiClassInfo, AlwaysAvailable, "Tag information"}, + {"info", CmdHFiClassInfo, IfPm3Iclass, "Tag information"}, {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "Read Picopass / iCLASS block"}, {"reader", CmdHFiClassReader, IfPm3Iclass, "Act like a Picopass / iCLASS reader"}, {"restore", CmdHFiClassRestore, IfPm3Iclass, "Restore a dump file onto a Picopass / iCLASS tag"}, {"sniff", CmdHFiClassSniff, IfPm3Iclass, "Eavesdrop Picopass / iCLASS communication"}, {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"}, - {"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"}, + {"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " --------------------"}, // {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"}, {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"}, @@ -4414,7 +4651,7 @@ static command_t CommandTable[] = { {"esetblk", CmdHFiClassESetBlk, IfPm3Iclass, "Set emulator memory block data"}, {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "---------------------- " _CYAN_("utils") " ----------------------"}, - {"configcard", CmdHFiClassConfigCard, AlwaysAvailable, "Reader configuration card"}, + {"configcard", CmdHFiClassConfigCard, IfPm3Iclass, "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"}, @@ -4437,7 +4674,7 @@ int CmdHFiClass(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -//static void test_credential_type(void) { +// static void test_credential_type(void) { // need AA1 key // Block 5 -> tells if its a legacy or SIO, also tells which key to use. @@ -4535,13 +4772,13 @@ int info_iclass(bool shallow_mod) { memcpy(aia, hdr->app_issuer_area, sizeof(aia)); } - // if CSN ends with FF12E0, it's inside HID CSN range. - bool isHidRange = (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); + // if CSN starts with E012FFF (big endian), it's inside HID CSN range. + bool is_hid_range = (hdr->csn[4] & 0xF0) == 0xF0 && (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); - bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); - bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + if (is_hid_range) { + bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - if (isHidRange) { PrintAndLogEx(SUCCESS, " CSN.......... " _YELLOW_("HID range")); if (legacy) PrintAndLogEx(SUCCESS, " Credential... " _GREEN_("iCLASS legacy")); diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 488f8dcb2..2b9101516 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -112,27 +112,25 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff int fl = 0; - if (data[6] == 0xec) { + if (data[6] == 0xEC) { strncpy(token_type, "XAM", sizeof(token_type) - 1); fl = 1; stamp_len = 0x0c - (data[5] >> 4); } else { - switch (data[5] & 0x7f) { - case 0x00 ... 0x2f: - strncpy(token_type, "IAM", sizeof(token_type) - 1); - fl = (0x2f - (data[5] & 0x7f)) + 1; - break; - case 0x30 ... 0x6f: - strncpy(token_type, "SAM", sizeof(token_type) - 1); - fl = (0x6f - (data[5] & 0x7f)) + 1; - break; - case 0x70 ... 0x7f: - strncpy(token_type, "GAM", sizeof(token_type) - 1); - fl = (0x7f - (data[5] & 0x7f)) + 1; - break; + + uint8_t tmp = data[5] & 0x7F; + if (tmp <= 0x2F) { + strncpy(token_type, "IAM", sizeof(token_type) - 1); + fl = (0x2F - tmp) + 1; + } else if (tmp >= 0x30 && tmp <= 0x6F) { + strncpy(token_type, "SAM", sizeof(token_type) - 1); + fl = (0x6F - tmp) + 1; + } else if (tmp >= 0x70 && tmp <= 0x7F) { + strncpy(token_type, "GAM", sizeof(token_type) - 1); + fl = (0x7F - tmp) + 1; } - stamp_len = 0xfc - data[6]; + stamp_len = 0xFC - data[6]; } PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x) Token Type=" _YELLOW_("%s") " (OLE=%01u) OL=%02u FL=%02u", @@ -858,7 +856,7 @@ static int CmdLegicReader(const char *Cmd) { static int CmdLegicDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic dump", - "Read all memory from LEGIC Prime tags and saves to (bin/eml/json) dump file\n" + "Read all memory from LEGIC Prime tags and saves to (bin/json) dump file\n" "It autodetects card type (MIM22, MIM256, MIM1024)", "hf legic dump --> use UID as filename\n" "hf legic dump -f myfile \n" @@ -951,7 +949,7 @@ static int CmdLegicDump(const char *Cmd) { FillFileNameByUID(filename, data, "-dump", 4); } - pm3_save_dump(filename, data, readlen, jsfLegic, LEGIC_BLOCK_SIZE); + pm3_save_dump(filename, data, readlen, jsfLegic_v2); free(data); return PM3_SUCCESS; } @@ -966,7 +964,7 @@ static int CmdLegicRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename to restore"), + arg_str1("f", "file", "", "Specify a filename to restore"), arg_lit0(NULL, "ob", "obfuscate dump data (xor with MCC)"), arg_param_end }; @@ -1119,7 +1117,7 @@ static int CmdLegicELoad(const char *Cmd) { static int CmdLegicESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic esave", - "Saves a (bin/eml/json) dump file of emulator memory", + "Saves a (bin/json) dump file of emulator memory", "hf legic esave --> uses UID as filename\n" "hf legic esave -f myfile --22\n" "hf legic esave -f myfile --22 --de\n" @@ -1188,7 +1186,7 @@ static int CmdLegicESave(const char *Cmd) { legic_xor(data, numofbytes); } - pm3_save_dump(filename, data, numofbytes, jsfLegic, LEGIC_BLOCK_SIZE); + pm3_save_dump(filename, data, numofbytes, jsfLegic_v2); return PM3_SUCCESS; } @@ -1406,7 +1404,7 @@ static int CmdLegicView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index d11f0c6d1..6ffe115c0 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -655,12 +655,12 @@ static int CmdHfLTOWriteBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf lto wrbl", "Write data to block on LTO tag", - "hf lto wrbl --block 128 -d 0001020304050607080910111213141516171819202122232425262728293031"); + "hf lto wrbl --blk 128 -d 0001020304050607080910111213141516171819202122232425262728293031"); void *argtable[] = { arg_param_begin, arg_str1("d", "data", "", "32 bytes of data to write (64 hex symbols, no spaces)"), - arg_int1(NULL, "block", "", "The block number to write to as an integer"), + arg_int1(NULL, "blk", "", "The block number to write to as an integer"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -771,8 +771,7 @@ static int CmdHfLTODump(const char *Cmd) { char *fptr = filename + snprintf(filename, sizeof(filename), "hf-lto-"); FillFileNameByUID(fptr, dump, "-dump", 5); } - saveFile(filename, ".bin", dump, dump_len); - saveFileEML(filename, dump, dump_len, 32); + pm3_save_dump(filename, dump, dump_len, jsfLto); free(dump); return PM3_SUCCESS; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 7f8be4fbf..334933a5c 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -37,6 +37,9 @@ #include "wiegand_formats.h" #include "wiegand_formatutils.h" #include "cmdhw.h" // set_fpga_mode +#include "loclass/cipherutils.h" // BitstreamOut_t +#include "proxendian.h" +#include "mifare/gen4.h" static int CmdHelp(const char *Cmd); @@ -472,7 +475,7 @@ void mf_print_sector_hdr(uint8_t sector) { static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, uint8_t *block) { uint8_t data[26]; - memcpy(data, key, MFKEY_SIZE); + memcpy(data, key, MIFARE_KEY_SIZE); memcpy(data + 10, block, MFBLOCK_SIZE); clearCommandBuffer(); @@ -483,7 +486,7 @@ static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, return false; } - return (resp.oldarg[0] & 0xff); + return ((resp.oldarg[0] & 0xff) == 1); } // assumes n is in number of blocks 0..255 @@ -747,6 +750,77 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n return PM3_SUCCESS ; } +static int mfLoadKeys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen) { + // Handle Keys + *pkeycnt = 0; + *pkeyBlock = NULL; + uint8_t *p; + // Handle user supplied key + // (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered) + if (userkeylen >= MIFARE_KEY_SIZE) { + int numKeys = userkeylen / MIFARE_KEY_SIZE; + p = realloc(*pkeyBlock, numKeys * MIFARE_KEY_SIZE); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + memcpy(*pkeyBlock, userkey, numKeys * MIFARE_KEY_SIZE); + + for (int i = 0; i < numKeys; i++) { + PrintAndLogEx(INFO, "[" _YELLOW_("%d") "] key %s", i, sprint_hex(*pkeyBlock + i * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); + } + *pkeycnt += numKeys; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " keys supplied by user ", numKeys); + } + + // Handle default keys + p = realloc(*pkeyBlock, (*pkeycnt + ARRAYLEN(g_mifare_default_keys)) * MIFARE_KEY_SIZE); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + // Copy default keys to list + for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { + num_to_bytes(g_mifare_default_keys[i], MIFARE_KEY_SIZE, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE)); + PrintAndLogEx(DEBUG, "[" _YELLOW_("%d") "] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); + } + *pkeycnt += ARRAYLEN(g_mifare_default_keys); + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " keys from hardcoded default array", ARRAYLEN(g_mifare_default_keys)); + + + // Handle user supplied dictionary file + if (fnlen > 0) { + uint32_t loaded_numKeys = 0; + uint8_t *keyBlock_tmp = NULL; + int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock_tmp, MIFARE_KEY_SIZE, &loaded_numKeys); + if (res != PM3_SUCCESS || loaded_numKeys == 0 || *pkeyBlock == NULL) { + PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); + free(keyBlock_tmp); + free(*pkeyBlock); + return PM3_EFILE; + } else { + p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * MIFARE_KEY_SIZE); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(keyBlock_tmp); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + memcpy(*pkeyBlock + *pkeycnt * MIFARE_KEY_SIZE, keyBlock_tmp, loaded_numKeys * MIFARE_KEY_SIZE); + *pkeycnt += loaded_numKeys; + free(keyBlock_tmp); + } + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%u") " keys from dictionary", loaded_numKeys); + } + return PM3_SUCCESS; +} + static int CmdHF14AMfAcl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf acl", @@ -1130,7 +1204,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { static int CmdHF14AMfDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf dump", - "Dump MIFARE Classic tag to binary file\n" + "Dump MIFARE Classic tag to file (bin/json)\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" @@ -1140,7 +1214,7 @@ static int CmdHF14AMfDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_str0("k", "keys", "", "filename of keys"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), @@ -1230,14 +1304,7 @@ static int CmdHF14AMfDump(const char *Cmd) { free(fptr); } - saveFile(dataFilename, ".bin", mem, bytes); - saveFileEML(dataFilename, mem, bytes, MFBLOCK_SIZE); - - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = mem; - xdump.dumplen = bytes; - saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(dataFilename, mem, bytes, jsfCardMemory); free(mem); return PM3_SUCCESS; } @@ -1271,7 +1338,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0("u", "uid", "", "uid, (4|7|10 hex bytes)"), - arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "specify a filename for dump file"), arg_str0("k", "kfn", "", "key filename"), arg_lit0(NULL, "ka", "use specified keyfile to authenticate"), arg_lit0(NULL, "force", "override warnings"), @@ -2324,12 +2391,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { "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" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys --> combo of the two above samples\n" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -k a0a1a2a3a4a5 --> multiple user supplied keys" ); void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Known key, 12 hex bytes"), + arg_strx0("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"), @@ -2360,16 +2428,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { }; 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 known_key = (keylen == 6); + int in_keys_len = 0; + uint8_t in_keys[100 * MIFARE_KEY_SIZE] = {0}; + CLIGetHexWithReturn(ctx, 1, in_keys, &in_keys_len); uint8_t sectorno = arg_get_u32_def(ctx, 2, 0); @@ -2385,7 +2446,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { 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); @@ -2468,30 +2528,30 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { 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; - uint8_t tmp_key[6] = {0}; + uint8_t tmp_key[MIFARE_KEY_SIZE] = {0}; // 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 block[MFBLOCK_SIZE] = {0x00}; int bytes; + // Settings int prng_type = PM3_EUNDEF; - uint8_t num_found_keys = 0; - + int isOK = 0; // ------------------------------ - uint32_t tagT = GetHF14AMfU_Type(); - if (tagT != UL_ERROR) { + uint64_t tagT = GetHF14AMfU_Type(); + if (tagT != MFU_TT_UL_ERROR) { PrintAndLogEx(ERR, "Detected a MIFARE Ultralight/C/NTAG Compatible card."); PrintAndLogEx(ERR, "This command targets " _YELLOW_("MIFARE Classic")); return PM3_ESOFT; @@ -2518,6 +2578,12 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + bool known_key = (in_keys_len > 5); + uint8_t key[MIFARE_KEY_SIZE] = {0}; + if (known_key) { + memcpy(key, in_keys, sizeof(key)); + } + // detect MFC EV1 Signature bool is_ev1 = detect_mfc_ev1_signature(); if (is_ev1) { @@ -2583,106 +2649,21 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, "========================================================================"); } - // Start the timer - uint64_t t1 = msclock(); - // check the user supplied key if (known_key == false) { PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); - } else { - if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START KNOWN KEY ATTACK") " ======================="); - } - - if (mfCheckKeys(mfFirstBlockOfSector(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_inrow(key, sizeof(key)) - ); - - // Store the key for the nested / hardnested attack (if supplied by the user) - e_sector[sectorno].Key[keytype] = key64; - e_sector[sectorno].foundKey[keytype] = 'U'; - - ++num_found_keys; - } else { - known_key = false; - PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector"_RED_("%3d") " key type "_RED_("%c") " key " _RED_("%s"), - sectorno, - (keytype == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(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 < sector_cnt; i++) { - for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { - - if (e_sector[i].foundKey[j]) { - continue; - } - - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num(key, 6); - e_sector[i].foundKey[j] = 'U'; - - // If the user supplied secctor / keytype was wrong --> just be nice and correct it ;) - if (known_key == false) { - num_to_bytes(e_sector[i].Key[j], 6, key); - known_key = true; - sectorno = i; - keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", - i, - (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") " ]", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - } - ++num_found_keys; - } - } - } - - if (num_found_keys == sector_cnt * 2) { - goto all_found; - } } - bool load_success = true; - // Load the dictionary - if (has_filename) { - res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 6, &key_cnt); - if (res != PM3_SUCCESS || key_cnt == 0 || keyBlock == NULL) { - PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); - if (keyBlock != NULL) { - free(keyBlock); - } - load_success = false; - } + // Start the timer + uint64_t t1 = msclock(); + + int ret = mfLoadKeys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen); + if (ret != PM3_SUCCESS) { + free(e_sector); + return ret; } - if (has_filename == false || load_success == false) { - keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); - if (keyBlock == NULL) { - free(e_sector); - free(fptr); - return PM3_EMALLOC; - } - - for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) { - num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6); - } - key_cnt = ARRAYLEN(g_mifare_default_keys); - PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " keys from hardcoded default array", key_cnt); - } + int32_t res = PM3_SUCCESS; // Use the dictionary to find sector keys on the card if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); @@ -2698,10 +2679,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (6 * k)), &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num((keyBlock + (6 * k)), 6); + if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (MIFARE_KEY_SIZE * k)), &key64) == PM3_SUCCESS) { + e_sector[i].Key[j] = bytes_to_num((keyBlock + (MIFARE_KEY_SIZE * k)), MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'D'; - ++num_found_keys; break; } } @@ -2711,7 +2691,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, ""); } else { - uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : key_cnt; + uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : key_cnt; bool firstChunk = true, lastChunk = false; for (uint8_t strategy = 1; strategy < 3; strategy++) { @@ -2727,12 +2707,14 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; // last chunk? - if (size == key_cnt - i) + if (size == key_cnt - i) { lastChunk = true; + } - res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); - if (firstChunk) + res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false); + if (firstChunk) { firstChunk = false; + } // all keys, aborted if (res == PM3_SUCCESS) { i = key_cnt; @@ -2746,18 +2728,21 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Analyse the dictionary attack + uint8_t num_found_keys = 0; 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) { continue; } + ++num_found_keys; + e_sector[i].foundKey[j] = 'D'; - num_to_bytes(e_sector[i].Key[j], 6, tmp_key); + num_to_bytes(e_sector[i].Key[j], MIFARE_KEY_SIZE, tmp_key); // Store valid credentials for the nested / hardnested attack if none exist if (known_key == false) { - num_to_bytes(e_sector[i].Key[j], 6, key); + num_to_bytes(e_sector[i].Key[j], MIFARE_KEY_SIZE, key); known_key = true; sectorno = i; keytype = j; @@ -2776,6 +2761,10 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } } + if (num_found_keys == sector_cnt * 2) { + goto all_found; + } + // Check if at least one sector key was found if (known_key == false) { @@ -2784,7 +2773,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); } - isOK = mfDarkside(mfFirstBlockOfSector(sectorno), keytype + 0x60, &key64); + isOK = mfDarkside(mfFirstBlockOfSector(sectorno), MIFARE_AUTH_KEYA + keytype, &key64); switch (isOK) { case PM3_EOPABORTED : @@ -2806,7 +2795,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Store the keys - num_to_bytes(key64, 6, key); + num_to_bytes(key64, MIFARE_KEY_SIZE, key); 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)", @@ -2827,7 +2816,7 @@ noValidKeyFound: free(keyBlock); // Clear the needed variables - num_to_bytes(0, 6, tmp_key); + num_to_bytes(0, MIFARE_KEY_SIZE, tmp_key); bool nested_failed = false; // Iterate over each sector and key(A/B) @@ -2842,7 +2831,7 @@ noValidKeyFound: goto tryStaticnested; // Try the found keys are reused - if (bytes_to_num(tmp_key, 6) != 0) { + if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { // 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 < sector_cnt; i++) { @@ -2853,7 +2842,7 @@ noValidKeyFound: // Check if the key works if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num(tmp_key, 6); + e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, @@ -2865,7 +2854,7 @@ noValidKeyFound: } } // Clear the last found key - num_to_bytes(0, 6, tmp_key); + num_to_bytes(0, MIFARE_KEY_SIZE, tmp_key); if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { @@ -2881,7 +2870,7 @@ noValidKeyFound: payload.blockno = sectrail; payload.keytype = MF_KEY_A; - num_to_bytes(e_sector[current_sector_i].Key[0], 6, payload.key); // KEY A + num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); @@ -2891,11 +2880,11 @@ noValidKeyFound: if (resp.status != PM3_SUCCESS) goto skipReadBKey; uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { 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); + num_to_bytes(key64, MIFARE_KEY_SIZE, tmp_key); PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', @@ -2981,7 +2970,7 @@ tryNested: } case PM3_SUCCESS: { calibrate = false; - e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); + e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[current_sector_i].foundKey[current_key_type_i] = 'N'; break; } @@ -3043,7 +3032,7 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack } // Copy the found key to the tmp_key variale (for the following print statement, and the mfCheckKeys above) - num_to_bytes(foundkey, 6, tmp_key); + num_to_bytes(foundkey, MIFARE_KEY_SIZE, tmp_key); e_sector[current_sector_i].Key[current_key_type_i] = foundkey; e_sector[current_sector_i].foundKey[current_key_type_i] = 'H'; } @@ -3073,7 +3062,7 @@ tryStaticnested: return isOK; } case PM3_SUCCESS: { - e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); + e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[current_sector_i].foundKey[current_key_type_i] = 'C'; break; } @@ -3121,9 +3110,9 @@ all_found: 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); + num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[current_sector_i].foundKey[1]) - num_to_bytes(e_sector[current_sector_i].Key[1], 6, block + 10); + num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); transfer_status |= mfEmlSetMem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } @@ -3164,13 +3153,7 @@ all_found: strncpy(filename, fptr, sizeof(filename) - 1); free(fptr); - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); // Generate and show statistics t1 = msclock() - t1; @@ -3181,73 +3164,6 @@ all_found: return PM3_SUCCESS; } -static int mfLoadKeys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen) { - // Handle Keys - *pkeycnt = 0; - *pkeyBlock = NULL; - uint8_t *p; - // Handle user supplied key - // (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered) - if (userkeylen >= 6) { - int numKeys = userkeylen / 6; - p = realloc(*pkeyBlock, (*pkeycnt + numKeys) * 6); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(*pkeyBlock); - return PM3_EMALLOC; - } - *pkeyBlock = p; - - memcpy(*pkeyBlock + *pkeycnt * 6, userkey, numKeys * 6); - - for (int i = 0; i < numKeys; i++) { - PrintAndLogEx(INFO, "[%2d] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * 6, 6)); - } - *pkeycnt += numKeys; - } - - // Handle default keys - p = realloc(*pkeyBlock, (*pkeycnt + ARRAYLEN(g_mifare_default_keys)) * 6); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(*pkeyBlock); - return PM3_EMALLOC; - } - *pkeyBlock = p; - // Copy default keys to list - for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { - num_to_bytes(g_mifare_default_keys[i], 6, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * 6)); - PrintAndLogEx(DEBUG, "[%2d] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * 6, 6)); - } - *pkeycnt += ARRAYLEN(g_mifare_default_keys); - - // Handle user supplied dictionary file - if (fnlen > 0) { - uint32_t loaded_numKeys = 0; - uint8_t *keyBlock_tmp = NULL; - int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock_tmp, 6, &loaded_numKeys); - if (res != PM3_SUCCESS || loaded_numKeys == 0 || *pkeyBlock == NULL) { - PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); - free(keyBlock_tmp); - free(*pkeyBlock); - return PM3_EFILE; - } else { - p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * 6); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(keyBlock_tmp); - free(*pkeyBlock); - return PM3_EMALLOC; - } - *pkeyBlock = p; - memcpy(*pkeyBlock + *pkeycnt * 6, keyBlock_tmp, loaded_numKeys * 6); - *pkeycnt += loaded_numKeys; - free(keyBlock_tmp); - } - } - return PM3_SUCCESS; -} - static int CmdHF14AMfChk_fast(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf fchk", @@ -3277,7 +3193,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; - uint8_t key[255 * 6] = {0}; + uint8_t key[100 * MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); @@ -3332,7 +3248,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { return PM3_EMALLOC; } - uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; + uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; @@ -3362,7 +3278,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { if (size == keycnt - i) lastChunk = true; - int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); + int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false); if (firstChunk) firstChunk = false; @@ -3409,16 +3325,16 @@ out: if (transferToEml) { // fast push mode g_conn.block_after_ACK = true; - uint8_t block[16] = {0x00}; + uint8_t block[MFBLOCK_SIZE] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mfEmlGetMem(block, b, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, block); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, block + 10); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); if (i == sectorsCnt - 1) { // Disable fast mode on last packet @@ -3480,7 +3396,7 @@ static int CmdHF14AMfChk(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; - uint8_t key[255 * 6] = {0}; + uint8_t key[100 * MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); int blockNo = arg_get_int_def(ctx, 2, -1); @@ -3616,7 +3532,7 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[6 * c], &key64) == PM3_SUCCESS) { + if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -3653,7 +3569,7 @@ static int CmdHF14AMfChk(const char *Cmd) { payload.keytype = MF_KEY_A; // Use key A - num_to_bytes(e_sector[i].Key[0], 6, payload.key); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, payload.key); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); @@ -3664,9 +3580,9 @@ static int CmdHF14AMfChk(const char *Cmd) { if (resp.status != PM3_SUCCESS) continue; uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { - PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data + 10, 6)); + PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data + 10, MIFARE_KEY_SIZE)); e_sector[i].foundKey[1] = 1; e_sector[i].Key[1] = key64; } @@ -3690,16 +3606,16 @@ out: if (transferToEml) { // fast push mode g_conn.block_after_ACK = true; - uint8_t block[16] = {0x00}; + uint8_t block[MFBLOCK_SIZE] = {0x00}; for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mfEmlGetMem(block, blockno, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, block); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, block + 10); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); if (i == sectors_cnt - 1) { // Disable fast mode on last packet @@ -4001,7 +3917,10 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { if (cmdp == 'b') keytype = MF_KEY_B; // key - if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute(); + int keylen = 0; + if (param_gethex_ex(Cmd, 2, key, &keylen) && (keylen != 12)) { + return usage_hf14_keybrute(); + } uint64_t t1 = msclock(); @@ -4017,7 +3936,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { */ void printKeyTable(size_t sectorscnt, sector_t *e_sector) { - return printKeyTableEx(sectorscnt, e_sector, 0); + printKeyTableEx(sectorscnt, e_sector, 0); } void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { @@ -4263,7 +4182,7 @@ int CmdHF14AMfELoad(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), 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"), @@ -4452,14 +4371,14 @@ static int CmdHF14AMfESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf esave", - "Save emulator memory into three files (BIN/EML/JSON) ", + "Save emulator memory to file (bin/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_str0("f", "file", "", "Specify a filename for dump file"), 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"), @@ -4522,30 +4441,7 @@ static int CmdHF14AMfESave(const char *Cmd) { FillFileNameByUID(fptr, dump, "-dump", 4); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - - iso14a_mf_extdump_t xdump = {0}; - xdump.card_info.ats_len = 0; - // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA - if ((dump[0] ^ dump[1] ^ dump[2] ^ dump[3]) == dump[4] && (dump[6] & 0xc0) == 0) { - xdump.card_info.uidlen = 4; - memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen); - xdump.card_info.sak = dump[5]; - memcpy(xdump.card_info.atqa, &dump[6], sizeof(xdump.card_info.atqa)); - } - // Check for 7 bytes UID: double size uid bits in ATQA - else if ((dump[8] & 0xc0) == 0x40) { - xdump.card_info.uidlen = 7; - memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen); - xdump.card_info.sak = dump[7]; - memcpy(xdump.card_info.atqa, &dump[8], sizeof(xdump.card_info.atqa)); - } else { - PrintAndLogEx(WARNING, "Invalid dump. UID/SAK/ATQA not found"); - } - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); free(dump); return PM3_SUCCESS; } @@ -4988,7 +4884,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), arg_param_end }; @@ -5217,14 +5113,14 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { static int CmdHF14AMfCSave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf csave", - "Save magic gen1a card memory into three files (BIN/EML/JSON)" + "Save magic gen1a card memory to file (bin/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_str0("f", "file", "", "Specify a filename for dump file"), 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"), @@ -5364,13 +5260,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); free(dump); return PM3_SUCCESS; } @@ -6176,16 +6066,19 @@ int CmdHFMFNDEFRead(const char *Cmd) { print_buffer(data, datalen, 1); } - if (fnlen != 0) { - saveFile(filename, ".bin", data, datalen); - } - res = NDEFDecodeAndPrint(data, datalen, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); res = NDEFRecordsDecodeAndPrint(data, datalen, verbose); } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(data, datalen, &n) != PM3_SUCCESS) + n = datalen; + + pm3_save_dump(filename, data, n, jsfNDEF); + if (verbose == false) { PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -v`") " for more details"); } else { @@ -6282,8 +6175,8 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // init keys to default key - uint8_t keyA[MIFARE_4K_MAXSECTOR][MFKEY_SIZE]; - uint8_t keyB[MIFARE_4K_MAXSECTOR][MFKEY_SIZE]; + uint8_t keyA[MIFARE_4K_MAXSECTOR][MIFARE_KEY_SIZE]; + uint8_t keyB[MIFARE_4K_MAXSECTOR][MIFARE_KEY_SIZE]; for (uint8_t i = 0; i < MIFARE_4K_MAXSECTOR; i++) { memcpy(keyA[i], g_mifare_default_key, sizeof(g_mifare_default_key)); @@ -6314,7 +6207,6 @@ int CmdHFMFNDEFFormat(const char *Cmd) { DropField(); } - // load key file if exist if (strlen(keyFilename)) { // @@ -7372,7 +7264,7 @@ static int CmdHF14AMfView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "sk", "Save extracted keys to file"), arg_param_end @@ -7388,7 +7280,7 @@ static int CmdHF14AMfView(const char *Cmd) { // read dump file uint8_t *dump = NULL; size_t bytes_read = 0; - int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, MIFARE_4K_MAX_BYTES); if (res != PM3_SUCCESS) { return res; } @@ -7402,7 +7294,6 @@ static int CmdHF14AMfView(const char *Cmd) { 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); } @@ -7470,6 +7361,188 @@ static int CmdHF14AMfView(const char *Cmd) { return PM3_SUCCESS; } +// info about Gen4 GTU card +static int CmdHF14AGen4Info(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ginfo", + "Read info about magic gen4 GTU card.", + "hf mf ginfo --> get info with default password 00000000\n" + "hf mf ginfo --pwd 01020304 --> get info with password\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_str0("p", "pwd", "", "password 4bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 2, pwd, &pwd_len); + CLIParserFree(ctx); + + if (pwd_len != 0 && pwd_len != 4) { + PrintAndLogEx(FAILED, "Password must be 4 bytes length, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + uint8_t resp[40] = {0}; + size_t resplen = 0; + int res = mfG4GetConfig(pwd, resp, &resplen, verbose); + if (res != PM3_SUCCESS || resplen == 0) { + if (res == PM3_ETIMEOUT) + PrintAndLogEx(ERR, "No card in the field or card command timeout."); + else + PrintAndLogEx(ERR, "Error get config. Maybe not a Gen4 card?. error=%d rlen=%zu", res, resplen); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "---------- Gen4 configuration ----------"); + if (resplen != 30 && resplen != 32) { + PrintAndLogEx(INFO, "Raw config [%02zu] %s", resplen, sprint_hex_inrow(resp, resplen)); + PrintAndLogEx(WARNING, "Unknown config format"); + return PM3_SUCCESS; + } + if (verbose) + PrintAndLogEx(INFO, "Raw config [%02zu]..... %s", resplen, sprint_hex_inrow(resp, resplen)); + + PrintAndLogEx(INFO, "UL protocol......... %02x" NOLF, resp[0]); + switch (resp[0]) { + case 0x00: + PrintAndLogEx(NORMAL, " (MIFARE Classic mode)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (MIFARE Ultralight/NTAG mode)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[0]); + break; + } + + uint8_t uid_len = resp[1]; + PrintAndLogEx(INFO, "UID length.......... %02x" NOLF, resp[1]); + switch (resp[1]) { + case 0x00: + PrintAndLogEx(NORMAL, " (4 byte)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (7 byte)"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (10 byte)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[1]); + break; + } + + PrintAndLogEx(INFO, "Password............ %s", sprint_hex_inrow(&resp[2], 4)); + + PrintAndLogEx(INFO, "GTU mode............ %02x" NOLF, resp[6]); + switch (resp[6]) { + case 0x00: + PrintAndLogEx(NORMAL, " (pre-write, shadow data can be written)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (restore mode)"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (disabled)"); + break; + case 0x03: + PrintAndLogEx(NORMAL, " (disabled, high speed R/W mode for Ultralight?)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[6]); + break; + } + + PrintAndLogEx(INFO, "ATS [%02d]............ %s", resp[7], sprint_hex_inrow(&resp[8], resp[7])); + PrintAndLogEx(INFO, "ATQA................ %02x%02x", resp[25], resp[24]); + PrintAndLogEx(INFO, "SAK................. %02x", resp[26]); + + PrintAndLogEx(INFO, "UL mode............. %02x" NOLF, resp[27]); + switch (resp[27]) { + case 0x00: + PrintAndLogEx(NORMAL, " (UL EV1)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (NTAG)"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (UL-C)"); + break; + case 0x03: + PrintAndLogEx(NORMAL, " (UL)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[27]); + break; + } + + PrintAndLogEx(INFO, "max rd/wr sectors... %02x", resp[28]); + PrintAndLogEx(INFO, "block0 direct wr.... %02x" NOLF, resp[29]); + switch (resp[29]) { + case 0x00: + PrintAndLogEx(NORMAL, " (Activate direct write to block 0 (Same behaviour of Gen2 cards. Some readers may identify the card as magic))"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (Deactivate direct write to block 0 (Same behaviour of vanilla cards))"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (Default value. Same behaviour as 00?"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[29]); + break; + } + + res = mfG4GetFactoryTest(pwd, resp, &resplen, false); + if (res == PM3_SUCCESS && resplen > 2) { + if (verbose) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Raw test [%02zu]....... %s", resplen, sprint_hex_inrow(resp, resplen)); + } + + if (resp[resplen - 2] == 0x66 && resp[resplen - 1] == 0x66) + PrintAndLogEx(INFO, "Card type........... generic"); + else if (resp[resplen - 2] == 0x02 && resp[resplen - 1] == 0xaa) + PrintAndLogEx(INFO, "Card type........... limited functionality"); + else if (resp[resplen - 2] == 0x03 && resp[resplen - 1] == 0xa0) + PrintAndLogEx(INFO, "Card type........... old card version"); + else if (resp[resplen - 2] == 0x06 && resp[resplen - 1] == 0xa0) + PrintAndLogEx(INFO, "Card type........... new card version"); + else + PrintAndLogEx(INFO, "Card type........... unknown %02x%02x", resp[resplen - 2], resp[resplen - 1]); + } + + // read block 0 + res = mfG4GetBlock(pwd, 0, resp, MAGIC_INIT | MAGIC_OFF); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Block 0............. %s", sprint_hex_inrow(resp, 16)); + + switch (uid_len) { + case 0x00: + PrintAndLogEx(INFO, "UID [4]............. %s", sprint_hex(resp, 4)); + break; + case 0x01: + PrintAndLogEx(INFO, "UID [7]............. %s", sprint_hex(resp, 7)); + break; + case 0x02: + PrintAndLogEx(INFO, "UID [10]............ %s", sprint_hex(resp, 10)); + break; + default: + break; + } + } + + + return PM3_SUCCESS; +} + // Read block from Gen4 GTU card static int CmdHF14AGen4GetBlk(const char *cmd) { CLIParserContext *ctx; @@ -7550,7 +7623,7 @@ static int CmdHF14AGen4Load(const char *cmd) { arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0("p", "pwd", "", "password 4bytes"), arg_lit0("v", "verbose", "verbose output"), - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), arg_int0(NULL, "start", "", "index of block to start writing (default 0)"), arg_int0(NULL, "end", "", "index of block to end writing (default last block)"), @@ -7896,7 +7969,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gsave", - "Save `magic gen4 gtu` card memory into three files (BIN/EML/JSON)" + "Save `magic gen4 gtu` card memory to file (bin/json)" "or into emulator memory", "hf mf gsave\n" "hf mf gsave --4k\n" @@ -7908,8 +7981,8 @@ static int CmdHF14AGen4Save(const char *Cmd) { arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), - arg_str0("p", "pwd", "", "password 4bytes"), - arg_str0("f", "file", "", "filename of dump"), + arg_str0("p", "pwd", "", "password 4 bytes"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "to emulator memory"), arg_param_end }; @@ -8079,18 +8152,60 @@ static int CmdHF14AGen4Save(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); - + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); free(dump); return PM3_SUCCESS; } +// change Gent4 GTU card access password +static int CmdHF14AGen4ChangePwd(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gchpwd", + "Change access password for Gen4 GTU card. WARNING! If you dont KNOW the password - you CAN'T access it!!!", + "hf mf gchpwd --pwd 00000000 --newpwd 01020304" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("p", "pwd", "", "password 4 bytes"), + arg_str0("n", "newpwd", "", "new password 4 bytes"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); + + int new_pwd_len = 0; + uint8_t new_pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 2, new_pwd, &new_pwd_len); + + bool verbose = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); + + if (pwd_len != 4) { + PrintAndLogEx(FAILED, "Old password must be 4 bytes long, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + if (new_pwd_len != 4) { + PrintAndLogEx(FAILED, "New password must be 4 bytes long, got " _YELLOW_("%u"), new_pwd_len); + return PM3_EINVARG; + } + + int res = mfG4ChangePassword(pwd, new_pwd, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Change password error"); + return res; + } + + PrintAndLogEx(SUCCESS, "Change password ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gdmcfg", @@ -8556,6 +8671,128 @@ static int CmdHF14AMfValue(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFMFHidEncode(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf encodehid", + "Encode binary wiegand to card\n" + "Use either --bin or --wiegand/--fc/--cn", + "hf mf encodehid --bin 10001111100000001010100011 -> FC 31 CN 337 (H10301)\n" + "hf mf encodehid -w H10301 --fc 31 --cn 337\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "bin", "", "Binary string i.e 0001001001"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number"), + arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int bin_len = 120; + uint8_t bin[121] = {0}; + CLIGetStrWithReturn(ctx, 1, bin, &bin_len); + + wiegand_card_t card; + memset(&card, 0, sizeof(wiegand_card_t)); + card.FacilityCode = arg_get_u32_def(ctx, 2, 0); + card.CardNumber = arg_get_u32_def(ctx, 3, 0); + + char format[16] = {0}; + int format_len = 0; + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)format, sizeof(format), &format_len); + + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + // santity checks + if (bin_len > 120) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 120 bits"); + return PM3_EINVARG; + } + + if (bin_len == 0 && card.FacilityCode == 0 && card.CardNumber == 0) { + PrintAndLogEx(ERR, "Must provide either --cn/--fc or --bin"); + return PM3_EINVARG; + } + + uint8_t blocks[] = { + 0x1B, 0x01, 0x4D, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x49, 0x44, 0x20, 0x49, 0x53, 0x78, 0x77, 0x88, 0xAA, 0x20, 0x47, 0x52, 0x45, 0x41, 0x54, + }; + + if (bin_len) { + char mfcbin[121] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, bin, bin_len); + + size_t hexlen = 0; + uint8_t hex[15] = {0}; + binstr_2_bytes(hex, &hexlen, mfcbin); + + memcpy(blocks + (MFBLOCK_SIZE * 4) + 1 + (15 - hexlen), hex, hexlen); + } else { + wiegand_message_t packed; + memset(&packed, 0, sizeof(wiegand_message_t)); + + int format_idx = HIDFindCardFormat(format); + if (format_idx == -1) { + PrintAndLogEx(WARNING, "Unknown format: " _YELLOW_("%s"), format); + return PM3_EINVARG; + } + + if (HIDPack(format_idx, &card, &packed, false) == false) { + PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); + return PM3_ESOFT; + } + + // iceman: only for formats w length smaller than 37. + // Needs a check. + + // increase length to allow setting bit just above real data + packed.Length++; + // Set sentinel bit + set_bit_by_position(&packed, true, 0); + +#ifdef HOST_LITTLE_ENDIAN + packed.Mid = BSWAP_32(packed.Mid); + packed.Bot = BSWAP_32(packed.Bot); +#endif + + memcpy(blocks + (MFBLOCK_SIZE * 4) + 8, &packed.Mid, sizeof(packed.Mid)); + memcpy(blocks + (MFBLOCK_SIZE * 4) + 12, &packed.Bot, sizeof(packed.Bot)); + } + + uint8_t empty[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + bool res = true; + for (uint8_t i = 0; i < (sizeof(blocks) / MFBLOCK_SIZE); i++) { + + if (verbose) { + PrintAndLogEx(INFO, "Writing %u - %s", (i + 1), sprint_hex_inrow(blocks + (i * MFBLOCK_SIZE), MFBLOCK_SIZE)); + } + + if (mf_write_block(empty, MF_KEY_A, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { + if (mf_write_block(empty, MF_KEY_B, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { + PrintAndLogEx(WARNING, "failed writing block %d using default empty key", (i + 1)); + res = false; + break; + } + } + } + if (res == false) { + PrintAndLogEx(WARNING, "Make sure card is wiped before running this command"); + } + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, @@ -8610,11 +8847,13 @@ static command_t CommandTable[] = { {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GTU") " --------------------------"}, + {"ginfo", CmdHF14AGen4Info, IfPm3Iso14443a, "Info about configuration of the card"}, {"ggetblk", CmdHF14AGen4GetBlk, IfPm3Iso14443a, "Read block from card"}, {"gload", CmdHF14AGen4Load, IfPm3Iso14443a, "Load dump to card"}, {"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"}, {"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"}, + {"gchpwd", CmdHF14AGen4ChangePwd, IfPm3Iso14443a, "Change card access password. Warning!"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, {"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"}, {"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"}, @@ -8624,6 +8863,7 @@ static command_t CommandTable[] = { {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, {"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, + {"encodehid", CmdHFMFHidEncode, IfPm3Iso14443a, "Encode a HID Credential / NDEF record to card"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 03dcea842..88166a8f2 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -372,8 +372,7 @@ static int mfdes_get_info(mfdes_info_res_t *info) { } // --- GET SIGNATURE -static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len, nxp_cardtype_t card_type) { - (void)card_type; +int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len) { if (uid == NULL) { PrintAndLogEx(DEBUG, "UID=NULL"); @@ -445,7 +444,7 @@ static void swap24(uint8_t *data) { uint8_t tmp = data[0]; data[0] = data[2]; data[2] = tmp; -}; +} // default parameters static uint8_t defaultKeyNum = 0; @@ -742,7 +741,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { res = DesfireReadSignature(&dctx, 0x00, signature, &signature_len); if (res == PM3_SUCCESS) { if (signature_len == 56) - desfire_print_signature(info.uid, info.uidlen, signature, signature_len, cardtype); + desfire_print_signature(info.uid, info.uidlen, signature, signature_len); else PrintAndLogEx(WARNING, "--- GetSignature returned wrong signature length: %zu", signature_len); } else { @@ -808,9 +807,9 @@ static int CmdHF14ADesInfo(const char *Cmd) { iso14a_card_select_t card; res = SelectCard14443A_4(true, false, &card); if (res == PM3_SUCCESS) { - static const char STANDALONE_DESFIRE[] = { 0x75, 0x77, 0x81, 0x02 }; - static const char JCOP_DESFIRE[] = { 0x75, 0xf7, 0xb1, 0x02 }; - static const char JCOP3_DESFIRE[] = { 0x78, 0x77, 0x71, 0x02 }; + static const uint8_t STANDALONE_DESFIRE[] = { 0x75, 0x77, 0x81, 0x02 }; + static const uint8_t JCOP_DESFIRE[] = { 0x75, 0xf7, 0xb1, 0x02 }; + static const uint8_t JCOP3_DESFIRE[] = { 0x78, 0x77, 0x71, 0x02 }; if (card.sak == 0x20) { @@ -1126,9 +1125,9 @@ static int CmdHF14aDesChk(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes chk", "Checks keys with MIFARE DESFire card.", "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456\n" - "hf mfdes chk -d mfdes_default_keys -> check keys from dictionary against all existing aid on card\n" - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys from dictionary against aid 0x123456\n" - "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to json\n" + "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card\n" + "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456\n" + "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`\n" "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00"); void *argtable[] = { diff --git a/client/src/cmdhfmfdes.h b/client/src/cmdhfmfdes.h index 88d2cdb32..80b3e8c93 100644 --- a/client/src/cmdhfmfdes.h +++ b/client/src/cmdhfmfdes.h @@ -28,6 +28,8 @@ char *getVersionStr(uint8_t major, uint8_t minor); int getKeySettings(uint8_t *aid); */ +int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len); + // Ev1 card limits #define MAX_NUM_KEYS 0x0F #define MAX_APPLICATION_COUNT 28 diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index c0078819b..9c02e8aea 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -1384,15 +1384,15 @@ static int CmdHFMFPChk(const char *Cmd) { static int CmdHFMFPDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp dump", - "Dump MIFARE Plus tag to binary file\n" + "Dump MIFARE Plus tag to file (bin/json)\n" "If no given, UID will be used as filename", "hf mfp dump\n" "hf mfp dump --keys hf-mf-066C8B78-key.bin --> MIFARE Plus 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", "", "Specify a filename for dump file"), + arg_str0("k", "keys", "", "Specify a filename for keys file"), arg_lit0(NULL, "ns", "no save to file"), arg_lit0("v", "verbose", "Verbose mode"), arg_param_end @@ -1448,14 +1448,7 @@ static int CmdHFMFPDump(const char *Cmd) { free(fptr); } - saveFile(data_fn, ".bin", mem, MIFARE_4K_MAX_BYTES); - saveFileEML(data_fn, mem, MIFARE_4K_MAX_BYTES, MFBLOCK_SIZE); - - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = mem; - xdump.dumplen = MIFARE_4K_MAX_BYTES; - saveFileJSON(data_fn, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, MIFARE_4K_MAX_BYTES, jsfCardMemory); */ free(mem); return PM3_SUCCESS; @@ -1746,16 +1739,19 @@ int CmdHFMFPNDEFRead(const char *Cmd) { print_buffer(data, datalen, 1); } - if (fnlen != 0) { - saveFile(filename, ".bin", data, datalen); - } - res = NDEFDecodeAndPrint(data, datalen, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); res = NDEFRecordsDecodeAndPrint(data, datalen, verbose); } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(data, datalen, &n) != PM3_SUCCESS) + n = datalen; + + pm3_save_dump(filename, data, n, jsfNDEF); + if (verbose == false) { PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -v`") " for more details"); } else { diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 13b8c9cb4..f19f36f99 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -51,6 +51,7 @@ #define MAX_MY_D_MOVE 0x25 #define MAX_MY_D_MOVE_LEAN 0x0F #define MAX_UL_NANO_40 0x0A +#define MAX_UL_AES 0x37 static int CmdHelp(const char *Cmd); @@ -69,16 +70,25 @@ static uint8_t default_pwd_pack[][4] = { {0x4E, 0x45, 0x78, 0x54}, // NExT }; -static uint32_t UL_TYPES_ARRAY[] = { - UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, - NTAG, NTAG_203, NTAG_210, NTAG_212, - NTAG_213, NTAG_215, NTAG_216, - MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN, - NTAG_I2C_1K, NTAG_I2C_2K, NTAG_I2C_1K_PLUS, NTAG_I2C_2K_PLUS, - FUDAN_UL, NTAG_213_F, NTAG_216_F, UL_EV1, UL_NANO_40, - NTAG_213_TT, NTAG_213_C, - MAGIC_1A, MAGIC_1B, MAGIC_NTAG, - NTAG_210u, UL_MAGIC, UL_C_MAGIC +static uint64_t UL_TYPES_ARRAY[] = { + MFU_TT_UNKNOWN, MFU_TT_UL, + MFU_TT_UL_C, MFU_TT_UL_EV1_48, + MFU_TT_UL_EV1_128, MFU_TT_NTAG, + MFU_TT_NTAG_203, MFU_TT_NTAG_210, + MFU_TT_NTAG_212, MFU_TT_NTAG_213, + MFU_TT_NTAG_215, MFU_TT_NTAG_216, + MFU_TT_MY_D, MFU_TT_MY_D_NFC, + MFU_TT_MY_D_MOVE, MFU_TT_MY_D_MOVE_NFC, + MFU_TT_MY_D_MOVE_LEAN, MFU_TT_NTAG_I2C_1K, + MFU_TT_NTAG_I2C_2K, MFU_TT_NTAG_I2C_1K_PLUS, + MFU_TT_NTAG_I2C_2K_PLUS, MFU_TT_FUDAN_UL, + MFU_TT_NTAG_213_F, MFU_TT_NTAG_216_F, + MFU_TT_UL_EV1, MFU_TT_UL_NANO_40, + MFU_TT_NTAG_213_TT, MFU_TT_NTAG_213_C, + MFU_TT_MAGIC_1A, MFU_TT_MAGIC_1B, + MFU_TT_MAGIC_NTAG, MFU_TT_NTAG_210u, + MFU_TT_UL_MAGIC, MFU_TT_UL_C_MAGIC, + MFU_TT_UL_AES }; static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { @@ -99,7 +109,7 @@ static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { // MAGIC_1A, MAGIC_1B, MAGIC_NTAG, MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_NTAG_216, // NTAG_210u, UL_MAGIC, UL_C_MAGIC - MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS + MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES }; //------------------------------------ @@ -372,8 +382,8 @@ static int ulev1_requestAuthentication(uint8_t *pwd, uint8_t *pack, uint16_t pac return len; } -static int ul_auth_select(iso14a_card_select_t *card, TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { - if (hasAuthKey && (tagtype & UL_C)) { +static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { + if (hasAuthKey && (tagtype & MFU_TT_UL_C)) { //will select card automatically and close connection on error if (!ulc_authentication(authkey, false)) { PrintAndLogEx(WARNING, "Authentication Failed UL-C"); @@ -420,7 +430,6 @@ static int ulev1_readTearing(uint8_t counter, uint8_t *response, uint16_t respon } static int ulev1_readSignature(uint8_t *response, uint16_t responseLength) { - uint8_t cmd[] = {MIFARE_ULEV1_READSIG, 0x00}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; @@ -442,16 +451,16 @@ static int ulev1_readSignature(uint8_t *response, uint16_t responseLength) { static int ul_fudan_check(void) { iso14a_card_select_t card; if (!ul_select(&card)) - return UL_ERROR; + return MFU_TT_UL_ERROR; uint8_t cmd[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa7}; //wrong crc on purpose should be 0xa8 clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 4, 0, cmd, sizeof(cmd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return UL_ERROR; - if (resp.oldarg[0] != 1) return UL_ERROR; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return MFU_TT_UL_ERROR; + if (resp.oldarg[0] != 1) return MFU_TT_UL_ERROR; - return (!resp.data.asBytes[0]) ? FUDAN_UL : UL; //if response == 0x00 then Fudan, else Genuine NXP + return (!resp.data.asBytes[0]) ? MFU_TT_FUDAN_UL : MFU_TT_UL; //if response == 0x00 then Fudan, else Genuine NXP } static int ul_print_default(uint8_t *data, uint8_t *real_uid) { @@ -623,7 +632,7 @@ static int ndef_print_CC(uint8_t *data) { return PM3_SUCCESS; } -int ul_print_type(uint32_t tagtype, uint8_t spaces) { +int ul_print_type(uint64_t tagtype, uint8_t spaces) { if (spaces > 10) spaces = 10; @@ -631,73 +640,73 @@ int ul_print_type(uint32_t tagtype, uint8_t spaces) { char typestr[100]; memset(typestr, 0x00, sizeof(typestr)); - if (tagtype & UL) + if (tagtype & MFU_TT_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight (MF0ICU1)"), spaces, ""); - else if (tagtype & UL_C) + else if (tagtype & MFU_TT_UL_C) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight C (MF0ULC)"), spaces, ""); - else if (tagtype & UL_NANO_40) + else if (tagtype & MFU_TT_UL_NANO_40) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight Nano 40bytes (MF0UNH00)"), spaces, ""); - else if (tagtype & UL_EV1_48) + else if (tagtype & MFU_TT_UL_EV1_48) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 48bytes (MF0UL1101)"), spaces, ""); - else if (tagtype & UL_EV1_128) + else if (tagtype & MFU_TT_UL_EV1_128) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 128bytes (MF0UL2101)"), spaces, ""); - else if (tagtype & UL_EV1) + else if (tagtype & MFU_TT_UL_EV1) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 UNKNOWN"), spaces, ""); - else if (tagtype & NTAG) + else if (tagtype & MFU_TT_NTAG) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG UNKNOWN"), spaces, ""); - else if (tagtype & NTAG_203) + else if (tagtype & MFU_TT_NTAG_203) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 203 144bytes (NT2H0301F0DT)"), spaces, ""); - else if (tagtype & NTAG_210u) + else if (tagtype & MFU_TT_NTAG_210u) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 210u (micro) 48bytes (NT2L1001G0DU)"), spaces, ""); - else if (tagtype & NTAG_210) + else if (tagtype & MFU_TT_NTAG_210) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 210 48bytes (NT2L1011G0DU)"), spaces, ""); - else if (tagtype & NTAG_212) + else if (tagtype & MFU_TT_NTAG_212) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 212 128bytes (NT2L1211G0DU)"), spaces, ""); - else if (tagtype & NTAG_213) + else if (tagtype & MFU_TT_NTAG_213) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213 144bytes (NT2H1311G0DU)"), spaces, ""); - else if (tagtype & NTAG_213_F) + else if (tagtype & MFU_TT_NTAG_213_F) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213F 144bytes (NT2H1311F0DTL)"), spaces, ""); - else if (tagtype & NTAG_213_C) + else if (tagtype & MFU_TT_NTAG_213_C) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213C 144bytes (NT2H1311C1DTL)"), spaces, ""); - else if (tagtype & NTAG_213_TT) + else if (tagtype & MFU_TT_NTAG_213_TT) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213TT 144bytes (NT2H1311TTDU)"), spaces, ""); - else if (tagtype & NTAG_215) + else if (tagtype & MFU_TT_NTAG_215) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 215 504bytes (NT2H1511G0DU)"), spaces, ""); - else if (tagtype & NTAG_216) + else if (tagtype & MFU_TT_NTAG_216) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 216 888bytes (NT2H1611G0DU)"), spaces, ""); - else if (tagtype & NTAG_216_F) + else if (tagtype & MFU_TT_NTAG_216_F) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 216F 888bytes (NT2H1611F0DTL)"), spaces, ""); - else if (tagtype & NTAG_I2C_1K) + else if (tagtype & MFU_TT_NTAG_I2C_1K) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C 888bytes (NT3H1101FHK)"), spaces, ""); - else if (tagtype & NTAG_I2C_2K) + else if (tagtype & MFU_TT_NTAG_I2C_2K) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C 1904bytes (NT3H1201FHK)"), spaces, ""); - else if (tagtype & NTAG_I2C_1K_PLUS) + else if (tagtype & MFU_TT_NTAG_I2C_1K_PLUS) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C plus 888bytes (NT3H2111FHK)"), spaces, ""); - else if (tagtype & NTAG_I2C_2K_PLUS) + else if (tagtype & MFU_TT_NTAG_I2C_2K_PLUS) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C plus 1912bytes (NT3H2211FHK)"), spaces, ""); - else if (tagtype & MY_D) + else if (tagtype & MFU_TT_MY_D) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 (SLE 66RxxS)"), spaces, ""); - else if (tagtype & MY_D_NFC) + else if (tagtype & MFU_TT_MY_D_NFC) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 NFC (SLE 66RxxP)"), spaces, ""); - else if (tagtype & MY_D_MOVE) + else if (tagtype & MFU_TT_MY_D_MOVE) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move (SLE 66R01P)"), spaces, ""); - else if (tagtype & MY_D_MOVE_NFC) + else if (tagtype & MFU_TT_MY_D_MOVE_NFC) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move NFC (SLE 66R01P)"), spaces, ""); - else if (tagtype & MY_D_MOVE_LEAN) + else if (tagtype & MFU_TT_MY_D_MOVE_LEAN) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move lean (SLE 66R01L)"), spaces, ""); - else if (tagtype & FUDAN_UL) + else if (tagtype & MFU_TT_FUDAN_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("FUDAN Ultralight Compatible (or other compatible)"), spaces, ""); else - snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06x"), spaces, "", tagtype); + snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06" PRIx64), spaces, "", tagtype); - bool ismagic = ((tagtype & MAGIC) == MAGIC); + bool ismagic = ((tagtype & MFU_TT_MAGIC) == MFU_TT_MAGIC); if (ismagic) snprintf(typestr + strlen(typestr), 4, " ("); - snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), " %s ", (tagtype & MAGIC) ? _GREEN_("magic") : ""); - tagtype &= ~(MAGIC); - snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MAGIC_1A) ? _GREEN_("Gen 1a") : ""); - snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MAGIC_1B) ? _GREEN_("Gen 1b") : ""); + snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), " %s ", (tagtype & MFU_TT_MAGIC) ? _GREEN_("magic") : ""); + tagtype &= ~(MFU_TT_MAGIC); + snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MFU_TT_MAGIC_1A) ? _GREEN_("Gen 1a") : ""); + snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MFU_TT_MAGIC_1B) ? _GREEN_("Gen 1b") : ""); if (ismagic) snprintf(typestr + strlen(typestr), 4, " )"); @@ -738,7 +747,7 @@ static int ulc_print_configuration(uint8_t *data) { return PM3_SUCCESS; } -static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t startPage) { +static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t startPage) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Configuration")); @@ -756,7 +765,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: %s", startPage, startPage, sprint_hex(data, 4)); //NTAG213TT has different ASCII mirroring options and config bytes interpretation from other ulev1 class tags - if (tagtype & NTAG_213_TT) { + if (tagtype & MFU_TT_NTAG_213_TT) { uint8_t mirror_conf = ((data[0] & 0xE0) >> 5); uint8_t mirror_byte = ((data[0] & 0x18) >> 3); uint8_t mirror_page = data[2]; @@ -822,7 +831,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " mirror start page %02X | byte pos %02X - %s", mirror_page, mirror_byte, (mirror_page >= 0x4 && ((mirror_user_mem_start_byte + bytes_required_for_mirror_data) <= 144)) ? _GREEN_("OK") : _YELLOW_("Invalid value")); } - } else if (tagtype & (NTAG_213_F | NTAG_216_F)) { + } else if (tagtype & (MFU_TT_NTAG_213_F | MFU_TT_NTAG_216_F)) { uint8_t mirror_conf = ((data[0] & 0xC0) >> 6); uint8_t mirror_byte = (data[0] & 0x30); bool sleep_en = (data[0] & 0x08); @@ -865,7 +874,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st break; } // valid mirror start page and byte position within start page. - if (tagtype & NTAG_213_F) { + if (tagtype & MFU_TT_NTAG_213_F) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0x24) ? "OK" : "Invalid value"); break;} @@ -876,7 +885,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st default: break; } - } else if (tagtype & NTAG_216_F) { + } else if (tagtype & MFU_TT_NTAG_216_F) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0xDE) ? "OK" : "Invalid value"); break;} @@ -901,7 +910,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st uint8_t tt_msg_resp_len = 0; uint8_t tt_status_resp[5] = {0x00}; - if (tagtype & NTAG_213_TT) { + if (tagtype & MFU_TT_NTAG_213_TT) { tt_enabled = (data[1] & 0x02); tt_msg_resp_len = ul_read(45, tt_message, 4); @@ -941,7 +950,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " PACK [%u/0x%02X]: %s - (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 12, 2)); PrintAndLogEx(INFO, " RFU [%u/0x%02X]: %s- (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 14, 2)); - if (tagtype & NTAG_213_TT) { + if (tagtype & MFU_TT_NTAG_213_TT) { if (data[1] & 0x06) { PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s- (cannot be read)", sprint_hex(tt_message, tt_msg_resp_len)); PrintAndLogEx(INFO, " - tamper message is masked in memory"); @@ -952,7 +961,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st } //The NTAG213TT only returns meaningful information for the fields below if the tamper feature is enabled - if ((tagtype & NTAG_213_TT) && tt_enabled) { + if ((tagtype & MFU_TT_NTAG_213_TT) && tt_enabled) { uint8_t tt_status_len = ntagtt_getTamperStatus(tt_status_resp, 5); @@ -1012,9 +1021,10 @@ static int ulev1_print_counters(void) { return len; } -static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) { +static int ulev1_print_signature(uint64_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) { #define PUBLIC_ECDA_KEYLEN 33 +#define PUBLIC_ECDA_192_KEYLEN 49 // known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier) // ref: AN11350 NTAG 21x Originality Signature Validation // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation @@ -1028,6 +1038,11 @@ static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *sig {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, }; + // https://www.nxp.com/docs/en/application-note/AN13452.pdf + const ecdsa_publickey_t nxp_mfu_192_public_keys[] = { + {"NXP Ultralight AES", "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, + }; + /* uint8_t nxp_mfu_public_keys[6][PUBLIC_ECDA_KEYLEN] = { // UL, NTAG21x and NDEF @@ -1076,34 +1091,60 @@ static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *sig */ uint8_t i; bool is_valid = false; - for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { + if (signature_len == 32) { + for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { - int dl = 0; - uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; - param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); + int dl = 0; + uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; + param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); + int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); - is_valid = (res == 0); - if (is_valid) - break; + is_valid = (res == 0); + if (is_valid) + break; + } + } + + bool is_192_valid = false; + if (signature_len == 48) { + for (i = 0; i < ARRAYLEN(nxp_mfu_192_public_keys); i++) { + int dl = 0; + uint8_t key[PUBLIC_ECDA_192_KEYLEN] = {0}; + param_gethex_to_eol(nxp_mfu_192_public_keys[i].value, 0, key, PUBLIC_ECDA_192_KEYLEN, &dl); + + int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP192R1, key, uid, 7, signature, signature_len, false); + + is_192_valid = (res == 0); + if (is_192_valid) + break; + } } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (is_valid == false || i == ARRAYLEN(nxp_mfu_public_keys)) { - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + if (is_192_valid) { + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_192_public_keys[i].desc); + PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_192_public_keys[i].value); + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp192r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )"); - return PM3_ESOFT; + PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); + return PM3_SUCCESS; } - PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + if (is_valid) { + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc); + PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); + PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); + return PM3_SUCCESS; + } + + PrintAndLogEx(INFO, " Elliptic curve parameters: %s", (signature_len == 48) ? "NID_secp192r1" : "NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); - return PM3_SUCCESS; + PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )"); + return PM3_ESOFT; } static int ulev1_print_version(uint8_t *data) { @@ -1152,14 +1193,14 @@ static int ulc_magic_test(){ uint8_t nonce1[11] = {0x00}; uint8_t nonce2[11] = {0x00}; if ( !ul_select(&card) ){ - return UL_ERROR; + return MFU_TT_UL_ERROR; } int status = ulc_requestAuthentication(nonce1, sizeof(nonce1)); if ( status > 0 ) { status = ulc_requestAuthentication(nonce2, sizeof(nonce2)); - returnValue = ( !memcmp(nonce1, nonce2, 11) ) ? UL_C_MAGIC : UL_C; + returnValue = ( !memcmp(nonce1, nonce2, 11) ) ? MFU_TT_UL_C_MAGIC : MFU_TT_UL_C; } else { - returnValue = UL; + returnValue = MFU_TT_UL; } DropField(); return returnValue; @@ -1172,12 +1213,12 @@ static int ul_magic_test(void) { iso14a_card_select_t card; if (ul_select(&card) == false) - return UL_ERROR; + return MFU_TT_UL_ERROR; int status = ul_comp_write(0, NULL, 0); DropField(); if (status == 0) - return MAGIC; + return MFU_TT_MAGIC; // check for GEN1A, GEN1B and NTAG21x uint8_t is_generation = 0; @@ -1191,11 +1232,11 @@ static int ul_magic_test(void) { } switch (is_generation) { case MAGIC_GEN_1A: - return MAGIC_1A; + return MFU_TT_MAGIC_1A; case MAGIC_GEN_1B: - return MAGIC_1B; + return MFU_TT_MAGIC_1B; case MAGIC_NTAG21X: - return MAGIC_NTAG; + return MFU_TT_MAGIC_NTAG; default: break; } @@ -1461,7 +1502,7 @@ static int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { return PM3_SUCCESS; } -static int mfu_fingerprint(TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authkey, int ak_len) { +static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, int ak_len) { uint8_t *data = NULL; int res = PM3_SUCCESS; @@ -1486,7 +1527,7 @@ static int mfu_fingerprint(TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authke uint8_t keytype = 0; if (hasAuthKey) { - if (tagtype & UL_C) + if (tagtype & MFU_TT_UL_C) keytype = 1; //UL_C auth else keytype = 2; //UL_EV1/NTAG auth @@ -1567,17 +1608,17 @@ out: uint32_t GetHF14AMfU_Type(void) { - TagTypeUL_t tagtype = UNKNOWN; + uint64_t tagtype = MFU_TT_UNKNOWN; iso14a_card_select_t card; if (ul_select(&card) == false) - return UL_ERROR; + return MFU_TT_UL_ERROR; // Ultralight - ATQA / SAK if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) { //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); DropField(); - return UL_ERROR; + return MFU_TT_UL_ERROR; } if (card.uid[0] != 0x05) { @@ -1628,79 +1669,80 @@ uint32_t GetHF14AMfU_Type(void) { Feiju NTAG 0053040201000F03 */ - if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } - else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { tagtype = UL_NANO_40; break; } - else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } - else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } - else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } - else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag - else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = NTAG_210; break; } - else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } - else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = NTAG_212; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } - else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } //Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) - else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = NTAG_213_C; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = NTAG_215; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; } - else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213_F; break; } - else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x13", 7) == 0) { tagtype = NTAG_216_F; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x03\x00\x0F", 7) == 0) { tagtype = NTAG_213_TT; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x13", 7) == 0) { tagtype = NTAG_I2C_1K; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x15", 7) == 0) { tagtype = NTAG_I2C_2K; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x13", 7) == 0) { tagtype = NTAG_I2C_1K_PLUS; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x15", 7) == 0) { tagtype = NTAG_I2C_2K_PLUS; break; } - else if (version[2] == 0x04) { tagtype = NTAG; break; } - else if (version[2] == 0x03) { tagtype = UL_EV1; } + if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_EV1_48; break; } + else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_NANO_40; break; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_EV1_48; break; } + else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } + else if (memcmp(version, "\x00\x04\x03\x01\x04\x00\x0F\x03", 8) == 0) { tagtype = MFU_TT_UL_AES; break; } + else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag + else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210; break; } + else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210u; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210u; break; } + else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_NTAG_212; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213; break; } + else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213; break; } // Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) + else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_C; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = MFU_TT_NTAG_215; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = MFU_TT_NTAG_216; break; } + else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_F; break; } + else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x13", 7) == 0) { tagtype = MFU_TT_NTAG_216_F; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x03\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_TT; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x13", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_1K; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x15", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_2K; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x13", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_1K_PLUS; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x15", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_2K_PLUS; break; } + else if (version[2] == 0x04) { tagtype = MFU_TT_NTAG; break; } + else if (version[2] == 0x03) { tagtype = MFU_TT_UL_EV1; } break; } case 0x01: - tagtype = UL_C; + tagtype = MFU_TT_UL_C; break; case 0x00: - tagtype = UL; + tagtype = MFU_TT_UL; break; case -1 : - tagtype = (UL | UL_C | NTAG_203); + tagtype = (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203); break; // could be UL | UL_C magic tags default : - tagtype = UNKNOWN; + tagtype = MFU_TT_UNKNOWN; break; } // UL vs UL-C vs ntag203 test - if (tagtype & (UL | UL_C | NTAG_203)) { - if (!ul_select(&card)) return UL_ERROR; + if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) { + if (!ul_select(&card)) return MFU_TT_UL_ERROR; // do UL_C check first... uint8_t nonce[11] = {0x00}; int status = ulc_requestAuthentication(nonce, sizeof(nonce)); DropField(); if (status > 1) { - tagtype = UL_C; + tagtype = MFU_TT_UL_C; } else { // need to re-select after authentication error if (ul_select(&card) == false) - return UL_ERROR; + return MFU_TT_UL_ERROR; uint8_t data[16] = {0x00}; // read page 0x26-0x29 (last valid ntag203 page) status = ul_read(0x26, data, sizeof(data)); if (status <= 1) { - tagtype = UL; + tagtype = MFU_TT_UL; } else { // read page 0x30 (should error if it is a ntag203) status = ul_read(0x30, data, sizeof(data)); if (status <= 1) { - tagtype = NTAG_203; + tagtype = MFU_TT_NTAG_203; } else { - tagtype = UNKNOWN; + tagtype = MFU_TT_UNKNOWN; } } DropField(); } } - if (tagtype & UL) { + if (tagtype & MFU_TT_UL) { tagtype = ul_fudan_check(); DropField(); } @@ -1711,23 +1753,23 @@ uint32_t GetHF14AMfU_Type(void) { switch (nib) { // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k case 1: - tagtype = MY_D; + tagtype = MFU_TT_MY_D; break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... case 2: - tagtype = (MY_D_NFC); + tagtype = MFU_TT_MY_D_NFC; break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) case 3: - tagtype = (MY_D_MOVE | MY_D_MOVE_NFC); + tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC); break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two case 7: - tagtype = MY_D_MOVE_LEAN; + tagtype = MFU_TT_MY_D_MOVE_LEAN; break; // or SLE 66R01L // 16 pages of 4 bytes } } tagtype |= ul_magic_test(); - if (tagtype == (UNKNOWN | MAGIC)) { - tagtype = (UL_MAGIC); + if (tagtype == (MFU_TT_UNKNOWN | MFU_TT_MAGIC)) { + tagtype = (MFU_TT_UL_MAGIC); } return tagtype; } @@ -1780,8 +1822,8 @@ static int CmdHF14AMfUInfo(const char *Cmd) { uint8_t pack[4] = {0, 0, 0, 0}; int len; - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; PrintAndLogEx(NORMAL, ""); @@ -1812,7 +1854,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // UL_C Specific - if ((tagtype & UL_C)) { + if ((tagtype & MFU_TT_UL_C)) { // read pages 0x28, 0x29, 0x2A, 0x2B uint8_t ulc_conf[16] = {0x00}; @@ -1829,7 +1871,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { mfu_fingerprint(tagtype, has_auth_key, authkeyptr, ak_len); - if ((tagtype & MAGIC)) { + if ((tagtype & MFU_TT_MAGIC)) { //just read key uint8_t ulc_deskey[16] = {0x00}; status = ul_read(0x2C, ulc_deskey, sizeof(ulc_deskey)); @@ -1863,7 +1905,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { // do counters and signature first (don't neet auth) // ul counters are different than ntag counters - if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1))) { + if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1))) { if (ulev1_print_counters() != 3) { // failed - re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { @@ -1873,7 +1915,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // NTAG counters? - if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { + if ((tagtype & (MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216))) { if (ntag_print_counter()) { // failed - re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { @@ -1883,8 +1925,12 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // Read signature - if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1 | UL_NANO_40 | NTAG_210u | NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216 | NTAG_216_F | NTAG_I2C_1K | NTAG_I2C_2K | NTAG_I2C_1K_PLUS | NTAG_I2C_2K_PLUS))) { - uint8_t ulev1_signature[32] = {0x00}; + if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1 | MFU_TT_UL_NANO_40 | + MFU_TT_NTAG_210u | MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | + MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216 | MFU_TT_NTAG_216_F | + MFU_TT_NTAG_I2C_1K | MFU_TT_NTAG_I2C_2K | MFU_TT_NTAG_I2C_1K_PLUS | MFU_TT_NTAG_I2C_2K_PLUS | + MFU_TT_UL_AES))) { + uint8_t ulev1_signature[49] = {0x00}; status = ulev1_readSignature(ulev1_signature, sizeof(ulev1_signature)); if (status == -1) { PrintAndLogEx(ERR, "Error: tag didn't answer to READ SIGNATURE"); @@ -1892,7 +1938,9 @@ static int CmdHF14AMfUInfo(const char *Cmd) { return PM3_ESOFT; } if (status == 32) { - ulev1_print_signature(tagtype, card.uid, ulev1_signature, sizeof(ulev1_signature)); + ulev1_print_signature(tagtype, card.uid, ulev1_signature, 32); + } else if (status == 48) { + ulev1_print_signature(tagtype, card.uid, ulev1_signature, 48); } else { // re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { @@ -2096,8 +2144,8 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { uint8_t *authKeyPtr = authenticationkey; // starting with getting tagtype - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; uint8_t maxblockno = 0; @@ -2217,8 +2265,8 @@ static int CmdHF14AMfURdBl(const char *Cmd) { uint8_t *authKeyPtr = authenticationkey; // start with getting tagtype - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; uint8_t maxblockno = 0; @@ -2328,7 +2376,12 @@ void printMFUdumpEx(mfu_dump_t *card, uint16_t pages, uint8_t startpage) { for (uint16_t i = 0; i < pages; ++i) { if (i < 3) { - PrintAndLogEx(INFO, "%3d/0x%02X | %s| | %s", i + startpage, i + startpage, sprint_hex(data + i * 4, 4), sprint_ascii(data + i * 4, 4)); + PrintAndLogEx(INFO, "%3d/0x%02X | " _RED_("%s")"| | %s", + i + startpage, + i + startpage, + sprint_hex(data + i * 4, 4), + sprint_ascii(data + i * 4, 4) + ); continue; } switch (i) { @@ -2434,7 +2487,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu dump", - "Dump MIFARE Ultralight/NTAG tag to binary/eml/json files.\n" + "Dump MIFARE Ultralight/NTAG tag to files (bin/json)\n" "It autodetects card type." "Supports:\n" "Ultralight, Ultralight-C, Ultralight EV1\n" @@ -2501,8 +2554,8 @@ static int CmdHF14AMfUDump(const char *Cmd) { authKeyPtr = SwapEndian64(authenticationkey, ak_len, 4); } - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; //get number of pages to read @@ -2519,7 +2572,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Reading tag memory..."); uint8_t keytype = 0; if (has_auth_key || has_pwd) { - if (tagtype & UL_C) + if (tagtype & MFU_TT_UL_C) keytype = 1; //UL_C auth else keytype = 2; //UL_EV1/NTAG auth @@ -2569,7 +2622,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { // not ul_c and not std ul then attempt to collect info like // VERSION, SIGNATURE, COUNTERS, TEARING, PACK, - if (!(tagtype & UL_C || tagtype & UL || tagtype & MY_D_MOVE || tagtype & MY_D_MOVE_LEAN)) { + if (!(tagtype & MFU_TT_UL_C || tagtype & MFU_TT_UL || tagtype & MFU_TT_MY_D_MOVE || tagtype & MFU_TT_MY_D_MOVE_LEAN)) { //attempt to read pack uint8_t get_pack[] = {0, 0}; if (ul_auth_select(&card, tagtype, true, authKeyPtr, get_pack, sizeof(get_pack)) != PM3_SUCCESS) { @@ -2599,7 +2652,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { uint8_t n = 0; // NTAG has 1 counter, at 0x02 - if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { + if ((tagtype & (MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216))) { n = 2; } @@ -2647,7 +2700,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { authKeyPtr = authenticationkey; } - if (tagtype & UL_C) { //add 4 pages + if (tagtype & MFU_TT_UL_C) { //add 4 pages memcpy(data + pages * 4, authKeyPtr, ak_len); pages += ak_len / 4; } else { // 2nd page from end @@ -2665,23 +2718,27 @@ static int CmdHF14AMfUDump(const char *Cmd) { printMFUdumpEx(&dump_file_data, pages, start_page); - if (nosave == false) { - // user supplied filename? - if (fnlen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - uint8_t uid[7] = {0}; - memcpy(uid, (uint8_t *)&dump_file_data.data, 3); - memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); - strcat(filename, "hf-mfu-"); - FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); - } + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } - uint16_t datalen = pages * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; - pm3_save_dump(filename, (uint8_t *)&dump_file_data, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); + // user supplied filename? + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + uint8_t uid[7] = {0}; + memcpy(uid, (uint8_t *)&dump_file_data.data, 3); + memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); + strcat(filename, "hf-mfu-"); + FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); + } - if (is_partial) { - PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); - } + uint16_t datalen = pages * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; + pm3_save_dump(filename, (uint8_t *)&dump_file_data, datalen, jsfMfuMemory); + + if (is_partial) { + PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); } return PM3_SUCCESS; @@ -2742,13 +2799,13 @@ int CmdHF14MfUTamper(const char *Cmd) { bool disable = arg_get_lit(ctx, 2); CLIParserFree(ctx); - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) { + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) { PrintAndLogEx(WARNING, "Tag type not detected"); DropField(); return PM3_ESOFT; } - if (tagtype != NTAG_213_TT) { + if (tagtype != MFU_TT_NTAG_213_TT) { PrintAndLogEx(WARNING, "Tag type not NTAG 213TT"); DropField(); return PM3_ESOFT; @@ -2766,7 +2823,7 @@ int CmdHF14MfUTamper(const char *Cmd) { if (use_msg) { if (ul_select(&card) == false) { DropField(); - return UL_ERROR; + return MFU_TT_UL_ERROR; } PrintAndLogEx(INFO, "Trying to write tamper message\n"); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, tt_msg_page, 0, 0, msg_data, 4); @@ -2789,7 +2846,7 @@ int CmdHF14MfUTamper(const char *Cmd) { if (ul_select(&card) == false) { PrintAndLogEx(ERR, "Unable to select tag"); DropField(); - return UL_ERROR; + return MFU_TT_UL_ERROR; } uint8_t cfg_page[4] = {0x00}; @@ -2840,7 +2897,7 @@ int CmdHF14MfUTamper(const char *Cmd) { static int CmdHF14AMfURestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu restore", - "Restore MIFARE Ultralight/NTAG dump file to tag.\n", + "Restore MIFARE Ultralight/NTAG dump file (bin/eml/json) to tag.\n", "hf mfu restore -f myfile -s -> special write\n" "hf mfu restore -f myfile -k AABBCCDD -s -> special write, use key\n" "hf mfu restore -f myfile -k AABBCCDD -ser -> special write, use key, write dump pwd, ..." @@ -2848,7 +2905,7 @@ static int CmdHF14AMfURestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_lit0("s", NULL, "enable special write UID -MAGIC TAG ONLY-"), @@ -3065,7 +3122,7 @@ static int CmdHF14AMfUeLoad(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_int0("q", "qty", "", "Number of blocks to load from eml file"), arg_lit0("v", "verbose", "verbose output"), arg_param_end @@ -4375,8 +4432,8 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { } // Get tag type - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) { + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) { PrintAndLogEx(WARNING, "No Ultralight / NTAG based tag found"); return PM3_ESOFT; } @@ -4443,14 +4500,20 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { } DropField(); - if (fnlen != 0) { - saveFile(filename, ".bin", records, (size_t)maxsize); - } + status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize, verbose); if (status != PM3_SUCCESS) { status = NDEFDecodeAndPrint(records, (size_t)maxsize, verbose); } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(records, maxsize, &n) != PM3_SUCCESS) + n = maxsize; + + pm3_save_dump(filename, records, n, jsfNDEF); + + char *jooki = strstr((char *)records, "s.jooki.rocks/s/?s="); if (jooki) { jooki += 17; @@ -4557,18 +4620,18 @@ static int CmdHF14AMfuEView(const char *Cmd) { static int CmdHF14AMfuESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu esave", - "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/eml/json)\n" + "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/json)\n" "By default number of pages saved depends on defined tag type.\n" "You can override this with option --end.", "hf mfu esave\n" "hf mfu esave --end 255 -> saves whole memory\n" - "hf mfu esave -f hf-mfu-04010203040506-dump.json" + "hf mfu esave -f hf-mfu-04010203040506-dump" ); void *argtable[] = { arg_param_begin, arg_int0("e", "end", "", "index of last block"), - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_param_end }; @@ -4613,7 +4676,7 @@ static int CmdHF14AMfuESave(const char *Cmd) { // save dump. Last block contains PACK + RFU uint16_t datalen = (end + 1) * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; - res = pm3_save_dump(filename, (uint8_t *)dump, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); + res = pm3_save_dump(filename, (uint8_t *)dump, datalen, jsfMfuMemory); free(dump); return res; @@ -4628,7 +4691,7 @@ static int CmdHF14AMfuView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index 1ed652e79..7b1acf27a 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -43,7 +43,7 @@ typedef struct { } PACKED old_mfu_dump_t; uint32_t GetHF14AMfU_Type(void); -int ul_print_type(uint32_t tagtype, uint8_t spaces); +int ul_print_type(uint64_t tagtype, uint8_t spaces); void printMFUdumpEx(mfu_dump_t *card, uint16_t pages, uint8_t startpage); int ul_read_uid(uint8_t *uid); int trace_mfuc_try_default_3des_keys(uint8_t **correct_key, int state, uint8_t (*authdata)[16]); @@ -55,44 +55,44 @@ int CmdHF14MfUTamper(const char *Cmd); uint16_t ul_ev1_packgen_VCNEW(uint8_t *uid, uint32_t pwd); uint32_t ul_ev1_otpgenA(uint8_t *uid); -typedef enum TAGTYPE_UL { - UNKNOWN = 0x000000, - UL = 0x1, - UL_C = 0x2, - UL_EV1_48 = 0x4, - UL_EV1_128 = 0x8, - NTAG = 0x10, - NTAG_203 = 0x20, - NTAG_210 = 0x40, - NTAG_212 = 0x80, - NTAG_213 = 0x100, - NTAG_215 = 0x200, - NTAG_216 = 0x400, - MY_D = 0x800, - MY_D_NFC = 0x1000, - MY_D_MOVE = 0x2000, - MY_D_MOVE_NFC = 0x4000, - MY_D_MOVE_LEAN = 0x8000, - NTAG_I2C_1K = 0x10000, - NTAG_I2C_2K = 0x20000, - NTAG_I2C_1K_PLUS = 0x40000, - NTAG_I2C_2K_PLUS = 0x80000, - FUDAN_UL = 0x100000, - MAGIC = 0x200000, - NTAG_213_F = 0x400000, - NTAG_216_F = 0x800000, - UL_EV1 = 0x1000000, - UL_NANO_40 = 0x2000000, - NTAG_213_TT = 0x4000000, - NTAG_213_C = 0x8000000, - MAGIC_1A = 0x10000000 | MAGIC, - MAGIC_1B = 0x20000000 | MAGIC, - MAGIC_NTAG = 0x40000000 | MAGIC, - NTAG_210u = 0x80000000, - UL_MAGIC = UL | MAGIC, - UL_C_MAGIC = UL_C | MAGIC, - // Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added - UL_ERROR = 0xFFFFFF, -} TagTypeUL_t; +#define MFU_TT_UNKNOWN 0x0ULL +#define MFU_TT_UL 0x1ULL +#define MFU_TT_UL_C 0x2ULL +#define MFU_TT_UL_EV1_48 0x4ULL +#define MFU_TT_UL_EV1_128 0x8ULL +#define MFU_TT_NTAG 0x10ULL +#define MFU_TT_NTAG_203 0x20ULL +#define MFU_TT_NTAG_210 0x40ULL +#define MFU_TT_NTAG_212 0x80ULL +#define MFU_TT_NTAG_213 0x100ULL +#define MFU_TT_NTAG_215 0x200ULL +#define MFU_TT_NTAG_216 0x400ULL +#define MFU_TT_MY_D 0x800ULL +#define MFU_TT_MY_D_NFC 0x1000ULL +#define MFU_TT_MY_D_MOVE 0x2000ULL +#define MFU_TT_MY_D_MOVE_NFC 0x4000ULL +#define MFU_TT_MY_D_MOVE_LEAN 0x8000ULL +#define MFU_TT_NTAG_I2C_1K 0x10000ULL +#define MFU_TT_NTAG_I2C_2K 0x20000ULL +#define MFU_TT_NTAG_I2C_1K_PLUS 0x40000ULL +#define MFU_TT_NTAG_I2C_2K_PLUS 0x80000ULL +#define MFU_TT_FUDAN_UL 0x100000ULL +#define MFU_TT_MAGIC 0x200000ULL +#define MFU_TT_NTAG_213_F 0x400000ULL +#define MFU_TT_NTAG_216_F 0x800000ULL +#define MFU_TT_UL_EV1 0x1000000ULL +#define MFU_TT_UL_NANO_40 0x2000000ULL +#define MFU_TT_NTAG_213_TT 0x4000000ULL +#define MFU_TT_NTAG_213_C 0x8000000ULL +#define MFU_TT_MAGIC_1A (0x10000000ULL | MFU_TT_MAGIC) +#define MFU_TT_MAGIC_1B (0x20000000ULL | MFU_TT_MAGIC) +#define MFU_TT_MAGIC_NTAG (0x40000000ULL | MFU_TT_MAGIC) +#define MFU_TT_NTAG_210u 0x80000000ULL +#define MFU_TT_UL_AES 0x100000000ULL +#define MFU_TT_UL_MAGIC (MFU_TT_UL | MFU_TT_MAGIC) +#define MFU_TT_UL_C_MAGIC (MFU_TT_UL_C | MFU_TT_MAGIC) +// Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added +#define MFU_TT_UL_ERROR 0x7FFFFFFFULL + #endif diff --git a/client/src/cmdhfntag424.c b/client/src/cmdhfntag424.c index fa6170c94..277b2ec18 100644 --- a/client/src/cmdhfntag424.c +++ b/client/src/cmdhfntag424.c @@ -20,15 +20,832 @@ #include #include "cmdparser.h" #include "commonutil.h" +#include "comms.h" +#include "iso7816/apduinfo.h" #include "protocols.h" #include "cliparser.h" #include "cmdmain.h" #include "fileutils.h" // saveFile #include "crypto/libpcrypto.h" // aes_decode #include "cmac.h" +#include "cmdhf14a.h" +#include "ui.h" +#include "util.h" +#include "crc32.h" +#include "cmdhfmfdes.h" + +#define NTAG424_MAX_BYTES 412 -#define NTAG424_MAX_BYTES 412 +// NTAG424 commands currently implemented +#define NTAG424_CMD_GET_FILE_SETTINGS 0xF5 +#define NTAG424_CMD_CHANGE_FILE_SETTINGS 0x5F +#define NTAG424_CMD_CHANGE_KEY 0xC4 +#define NTAG424_CMD_READ_DATA 0xAD +#define NTAG424_CMD_WRITE_DATA 0x8D +#define NTAG424_CMD_AUTHENTICATE_EV2_FIRST 0x71 +#define NTAG424_CMD_MORE_DATA 0xAF +#define NTAG424_CMD_GET_VERSION 0x60 +#define NTAG424_CMD_GET_SIGNATURE 0x3C + +// +// Original from https://github.com/rfidhacking/node-sdm/ +// +typedef struct sdm_picc_s { + uint8_t tag; + uint8_t uid[7]; + uint8_t cnt[3]; + uint32_t cnt_int; +} sdm_picc_t; + +// -------------- Encryption structs --------------------------- +typedef struct { + uint8_t ti[4]; + uint8_t rnd_a[16]; + uint8_t pd_cap2[6]; + uint8_t pcd_cap2[6]; +} ntag424_ev2_response_t; + +typedef struct { + uint16_t command_counter; + uint8_t ti[4]; + uint8_t encryption[16]; + uint8_t mac[16]; +} ntag424_session_keys_t; + +typedef enum { + COMM_PLAIN, + COMM_MAC, + COMM_FULL +} ntag424_communication_mode_t; + +const CLIParserOption ntag424_communication_mode_options[] = { + {COMM_PLAIN, "plain"}, + {COMM_MAC, "mac"}, + {COMM_FULL, "encrypt"}, + {0, NULL}, +}; + +// -------------- File settings structs ------------------------- +// Enabling this bit in the settings will also reset the read counter to 0 +#define FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING (1 << 6) + +#define FILE_SETTINGS_SDM_OPTIONS_UID (1 << 7) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER (1 << 6) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT (1 << 5) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA (1 << 4) +#define FILE_SETTINGS_SDM_OPTIONS_ENCODING_MODE_ASCII (1 << 0) + +typedef struct { + uint8_t sdm_options; + uint8_t sdm_access[2]; + uint8_t sdm_data[8][3]; +} ntag424_file_sdm_settings_t; + +typedef struct { + uint8_t type; + uint8_t options; + uint8_t access[2]; + uint8_t size[3]; + ntag424_file_sdm_settings_t optional_sdm_settings; +} ntag424_file_settings_t; + +#define SETTINGS_WITHOUT_SDM_DATA_SIZE (1+1+2+3+1+2) + +// A different struct is used when actually writing the settings back, +// since we obviously can't change the size or type of a static file. +typedef struct { + uint8_t options; + uint8_t access[2]; + ntag424_file_sdm_settings_t optional_sdm_settings; +} file_settings_write_t; + +// -------------- Version information structs ------------------------- +typedef struct { + uint8_t vendor_id; + uint8_t type; + uint8_t sub_type; + uint8_t major_version; + uint8_t minor_version; + uint8_t storage_size; + uint8_t protocol; +} PACKED ntag424_version_information_t; + +typedef struct { + uint8_t uid[7]; + uint8_t batch[4]; + uint8_t fab_key_high : 4; + uint8_t batchno : 4; + uint8_t week_prod : 7; + uint8_t fab_key_low : 1; + uint8_t year_prod; +} PACKED ntag424_production_information_t; + +typedef struct { + ntag424_version_information_t hardware; + ntag424_version_information_t software; + ntag424_production_information_t production; +} ntag424_full_version_information_t; + + +static void ntag424_print_version_information(ntag424_version_information_t *version) { + PrintAndLogEx(INFO, " vendor id: " _GREEN_("%02X"), version->vendor_id); + PrintAndLogEx(INFO, " type: " _GREEN_("%02X"), version->type); + PrintAndLogEx(INFO, " sub type: " _GREEN_("%02X"), version->sub_type); + PrintAndLogEx(INFO, " version: " _GREEN_("%d.%d"), version->major_version, version->minor_version); + PrintAndLogEx(INFO, "storage size: " _GREEN_("%02X"), version->storage_size); + PrintAndLogEx(INFO, " protocol: " _GREEN_("%02X"), version->protocol); +} + +static void ntag424_print_production_information(ntag424_production_information_t *version) { + PrintAndLogEx(INFO, " uid: " _GREEN_("%s"), sprint_hex(version->uid, sizeof(version->uid))); + PrintAndLogEx(INFO, " batch: " _GREEN_("%s"), sprint_hex(version->batch, sizeof(version->batch))); + PrintAndLogEx(INFO, " batchno: " _GREEN_("%02X"), version->batchno); + PrintAndLogEx(INFO, " fab key: " _GREEN_("%02X"), (version->fab_key_high << 1) | version->fab_key_low); + PrintAndLogEx(INFO, " date: week " _GREEN_("%02X") " / " _GREEN_("20%02X"), version->week_prod, version->year_prod); +} + +static void ntag424_print_full_version_information(ntag424_full_version_information_t *version) { + PrintAndLogEx(INFO, "--- " _CYAN_("Hardware version information:")); + ntag424_print_version_information(&version->hardware); + + PrintAndLogEx(INFO, "--- " _CYAN_("Software version information:")); + ntag424_print_version_information(&version->software); + + PrintAndLogEx(INFO, "--- " _CYAN_("Production information:")); + ntag424_print_production_information(&version->production); +} + +// Currently unused functions, commented out due to -Wunused-function +/*static void ntag424_file_settings_set_access_rights(ntag424_file_settings_t *settings, + uint8_t read_write_key, uint8_t change_key, + uint8_t read_key, uint8_t write_key) + +{ + settings->access[0] = read_write_key << 4 | change_key; + settings->access[1] = read_key << 4 | write_key; +}*/ + +// Currently unused functions, commented out due to -Wunused-function +/*static void ntag424_file_settings_set_sdm_access_rights(ntag424_file_settings_t *settings, + uint8_t sdm_meta_read, uint8_t sdm_file_read, uint8_t sdm_ctr_ret) +{ + settings->optional_sdm_settings.sdm_access[1] = sdm_meta_read << 4 | sdm_file_read; + settings->optional_sdm_settings.sdm_access[0] = 0xf << 4 | sdm_ctr_ret; // (0xf is due to reserved for future use) +}*/ + + +static uint8_t ntag424_file_settings_get_sdm_meta_read(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[1] >> 4; +} + +static uint8_t ntag424_file_settings_get_sdm_file_read(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[1] & 0xf; +} + +// Currently unused functions, commented out due to -Wunused-function +/*static uint8_t ntag424_file_settings_get_sdm_ctr_ret(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[0] & 4; +}*/ + +// Calculate the actual size of a file settings struct. A variable number of data is attached +// at the end depending on settings. +static int ntag424_calc_file_settings_size(const ntag424_file_settings_t *settings) { + int size = 7; + + if (settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) { + size += 3; // sdm_options and sdm_access must be present + + if (settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_UID && + ntag424_file_settings_get_sdm_meta_read(settings) == 0xe) { + size += 3; // UIDOffset + } + + if (settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER && + ntag424_file_settings_get_sdm_meta_read(settings) == 0xe) { + size += 3; // SDMReadCtrOffset + } + + if (ntag424_file_settings_get_sdm_meta_read(settings) <= 0x04) { + size += 3; // PICCDataOffset + } + + if (ntag424_file_settings_get_sdm_file_read(settings) != 0x0f) { + size += 3; // SDMMacInputOffset + } + + if (ntag424_file_settings_get_sdm_file_read(settings) != 0x0f && + settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA) { + size += 3; // SDMEncOffset + size += 3; // SDMEncLength + } + + if (ntag424_file_settings_get_sdm_file_read(settings) != 0x0f) { + // Warning, this value has different offsets depending on + // FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA + size += 3; // SDMMacOffset + } + + if (settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT) { + size += 3; // SDMReadCtrLimit + } + } + + return size; +} + +static int ntag424_calc_file_write_settings_size(const ntag424_file_settings_t *settings) { + return ntag424_calc_file_settings_size(settings) - 4; +} + +static void ntag424_calc_send_iv(ntag424_session_keys_t *session_keys, uint8_t *out_ivc) { + uint8_t iv_clear[] = { 0xa5, 0x5a, + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + (uint8_t)(session_keys->command_counter), (uint8_t)(session_keys->command_counter >> 8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint8_t zero_iv[16] = {0}; + aes_encode(zero_iv, session_keys->encryption, iv_clear, out_ivc, 16); +} + +static void ntag424_calc_receive_iv(ntag424_session_keys_t *session_keys, uint8_t *out_ivc) { + uint8_t iv_clear[] = { 0x5a, 0xa5, + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + (uint8_t)(session_keys->command_counter), (uint8_t)(session_keys->command_counter >> 8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint8_t zero_iv[16] = {0}; + aes_encode(zero_iv, session_keys->encryption, iv_clear, out_ivc, 16); +} + +static void ntag424_calc_mac(ntag424_session_keys_t *session_keys, uint8_t command, uint8_t *data, uint8_t datalen, uint8_t *out_mac) { + uint8_t mac_input_header[] = { command, + (uint8_t)session_keys->command_counter, (uint8_t)(session_keys->command_counter >> 8), + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3] + }; + + int mac_input_len = sizeof(mac_input_header) + datalen; + + uint8_t *mac_input = (uint8_t *)malloc(mac_input_len); + memcpy(mac_input, mac_input_header, sizeof(mac_input_header)); + memcpy(&mac_input[sizeof(mac_input_header)], data, datalen); + uint8_t mac[16] = {0}; + mbedtls_aes_cmac_prf_128(session_keys->mac, 16, mac_input, sizeof(mac_input_header) + datalen, mac); + + for (int i = 0; i < 8; i++) { + out_mac[i] = mac[i * 2 + 1]; + } + + free(mac_input); +} + +static int ntag424_comm_mac_apdu(APDU_t *apdu, int command_header_length, int apdu_max_data_size, ntag424_session_keys_t *session_keys) { + + int size = apdu->lc; + + if (size + 8 > apdu_max_data_size) { + return PM3_EOVFLOW; + } + + ntag424_calc_mac(session_keys, apdu->ins, apdu->data, size, &apdu->data[size]); + session_keys->command_counter++; // CmdCtr should be incremented each time a MAC is calculated + apdu->lc = size + 8; + + return PM3_SUCCESS; +} + +static int ntag424_comm_encrypt_apdu(APDU_t *apdu, int command_header_length, int apdu_max_data_size, ntag424_session_keys_t *session_keys) { + // ------- Calculate IV + uint8_t ivc[16]; + ntag424_calc_send_iv(session_keys, ivc); + + int size = apdu->lc; + + size_t encrypt_data_size = size - command_header_length; + size_t padded_data_size = encrypt_data_size + 16 - (encrypt_data_size % 16); // pad up to 16 byte blocks + uint8_t temp_buffer[256] = {0}; + + if (!encrypt_data_size) { + return PM3_SUCCESS; + } + + if (padded_data_size + command_header_length > apdu_max_data_size) { + return PM3_EOVFLOW; + } + + // ------ Pad data + memcpy(temp_buffer, &apdu->data[command_header_length], encrypt_data_size); // We encrypt everything except the CmdHdr (first byte in data) + temp_buffer[encrypt_data_size] = 0x80; + + // ------ Encrypt it + aes_encode(ivc, session_keys->encryption, temp_buffer, &apdu->data[command_header_length], padded_data_size); + + apdu->lc = (uint8_t)(command_header_length + padded_data_size); // Set size to CmdHdr + padded data + + return PM3_SUCCESS; +} + +static int ntag424_exchange_apdu(APDU_t apdu, int command_header_length, uint8_t *response, int *response_length, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys, uint8_t sw1_expected, uint8_t sw2_expected) { + + int res; + + // New buffer since we might need to expand the data in the apdu + int buffer_length = 256; + uint8_t tmp_apdu_buffer[256] = {0}; + + if (comm_mode != COMM_PLAIN) { + if (session_keys == NULL) { + PrintAndLogEx(ERR, "Non-plain communications mode requested but no session keys supplied"); + return PM3_EINVARG; + } + memcpy(tmp_apdu_buffer, apdu.data, apdu.lc); + apdu.data = tmp_apdu_buffer; + } + + if (comm_mode == COMM_FULL) { + res = ntag424_comm_encrypt_apdu(&apdu, command_header_length, buffer_length, session_keys); + if (res != PM3_SUCCESS) { + return res; + } + } + + if (comm_mode == COMM_MAC || comm_mode == COMM_FULL) { + res = ntag424_comm_mac_apdu(&apdu, command_header_length, buffer_length, session_keys); + if (res != PM3_SUCCESS) { + return res; + } + } + + uint8_t cmd[256] = {0}; + int apdu_length = 256; + + if (APDUEncode(&apdu, cmd, &apdu_length) != 0) { + return PM3_EINVARG; + } + + res = ExchangeAPDU14a(cmd, apdu_length + 1, false, true, response, *response_length, response_length); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to exchange APDU: %d", res); + return res; + } + + if (*response_length < 2) { + PrintAndLogEx(ERR, "No response"); + return PM3_ESOFT; + } + + uint8_t sw1 = response[*response_length - 2]; + uint8_t sw2 = response[*response_length - 1]; + + if (sw1 != sw1_expected || sw2 != sw2_expected) { + PrintAndLogEx(ERR, "Error from card: %02X %02X (%s)", sw1, sw2, GetAPDUCodeDescription(sw1, sw2)); + return PM3_ESOFT; + } + + // Decrypt data if we are in full communications mode. If we want to verify MAC, this + // should also be done here + if (comm_mode == COMM_FULL) { + uint8_t iv[16] = {0}; + ntag424_calc_receive_iv(session_keys, iv); + + uint8_t tmp[256]; + memcpy(tmp, response, *response_length); + aes_decode(iv, session_keys->encryption, response, tmp, *response_length - 10); + + memcpy(response, tmp, *response_length); + } + + return PM3_SUCCESS; +} + + +static int ntag424_get_file_settings(uint8_t fileno, ntag424_file_settings_t *settings_out) { + int response_length = sizeof(ntag424_file_settings_t) + 2; + uint8_t response[response_length]; + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_GET_FILE_SETTINGS, + .lc = 1, + .data = &fileno, + .extended_apdu = false + }; + + int res = ntag424_exchange_apdu(apdu, 1, response, &response_length, COMM_PLAIN, NULL, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + if (settings_out) { + memcpy(settings_out, response, response_length); + } + + return PM3_SUCCESS; +} + +static int ntag424_write_file_settings(uint8_t fileno, ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) { + + // ------- Convert file settings to the format for writing + file_settings_write_t write_settings = { + .options = settings->options, + .access[0] = settings->access[0], + .access[1] = settings->access[1], + .optional_sdm_settings = settings->optional_sdm_settings, + }; + + size_t settings_size = ntag424_calc_file_write_settings_size(settings); + + uint8_t cmd_buffer[256]; + cmd_buffer[0] = fileno; + memcpy(&cmd_buffer[1], &write_settings, settings_size); + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_CHANGE_FILE_SETTINGS, + .lc = 1 + settings_size, + .data = cmd_buffer + }; + + + // ------- Actually send the APDU + int response_length = 8 + 2; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 1, response, &response_length, COMM_FULL, session_keys, 0x91, 0x00); + return res; +} + +static void ntag424_print_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings) { + + int num_sdm_data = (ntag424_calc_file_settings_size(settings) - SETTINGS_WITHOUT_SDM_DATA_SIZE) / 3; + + PrintAndLogEx(SUCCESS, "--- " _CYAN_("File %d settings:"), fileno); + + PrintAndLogEx(SUCCESS, " type: " _GREEN_("%02X"), settings->type); + PrintAndLogEx(SUCCESS, " options: " _GREEN_("%02X"), settings->options); + PrintAndLogEx(SUCCESS, " access: " _GREEN_("%02X%02X (RW, C, R, W)"), settings->access[0], settings->access[1]); + PrintAndLogEx(SUCCESS, " size: " _GREEN_("%02X%02X%02X"), settings->size[2], settings->size[1], settings->size[0]); + + if (settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) { + PrintAndLogEx(SUCCESS, "--- " _CYAN_("SDM settings: ")); + PrintAndLogEx(SUCCESS, " options: " _GREEN_("%02X"), settings->optional_sdm_settings.sdm_options); + PrintAndLogEx(SUCCESS, " sdm access: " _GREEN_("%02X%02X"), settings->optional_sdm_settings.sdm_access[0], settings->optional_sdm_settings.sdm_access[1]); + + if (num_sdm_data > 0) { + PrintAndLogEx(SUCCESS, "--- " _CYAN_("SDM data: ")); + for (int i = 0; i < num_sdm_data; i++) { + PrintAndLogEx(SUCCESS, " %d: %02X%02X%02X", i, + settings->optional_sdm_settings.sdm_data[i][2], + settings->optional_sdm_settings.sdm_data[i][1], + settings->optional_sdm_settings.sdm_data[i][0]); + } + } + } +} + +// NTAG424 only have one static application, so we select it here +static int ntag424_select_application(void) { + const size_t RESPONSE_LENGTH = 2; + uint8_t cmd[] = {0x00, 0xA4, 0x04, 0x0C, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00 }; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if (outlen != RESPONSE_LENGTH || resp[RESPONSE_LENGTH - 2] != 0x90 || resp[RESPONSE_LENGTH - 1] != 0x00) { + PrintAndLogEx(ERR, "Failed to select application"); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int ntag424_auth_first_step(uint8_t keyno, uint8_t *key, uint8_t *out) { + uint8_t key_number[2] = { keyno, 0x00 }; + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_AUTHENTICATE_EV2_FIRST, + .lc = 0x02, + .data = key_number + }; + + int response_length = 16 + 2; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 2, response, &response_length, COMM_PLAIN, NULL, 0x91, 0xAF); + if (res != PM3_SUCCESS) { + return res; + } + + if (response_length != 16 + 2) { + PrintAndLogEx(ERR, "Failed to get RndB (invalid key number?)"); + return PM3_ESOFT; + } + + uint8_t iv[16] = {0}; + aes_decode(iv, key, response, out, 16); + + return PM3_SUCCESS; +} + +static int ntag424_auth_second_step(uint8_t *challenge, uint8_t *response_out) { + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_MORE_DATA, + .lc = 0x20, + .data = challenge, + }; + int response_length = 256; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 0x20, response, &response_length, COMM_PLAIN, NULL, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + memcpy(response_out, response, response_length - 2); + + return PM3_SUCCESS; +} + +// Authenticate against a key number and optionally get session keys out +static int ntag424_authenticate_ev2_first(uint8_t keyno, uint8_t *key, ntag424_session_keys_t *session_keys_out) { + // -------- Get first challenge from card + uint8_t rnd_b_clear[16] = {0}; + + int res = ntag424_auth_first_step(keyno, key, rnd_b_clear); + if (res != PM3_SUCCESS) { + return res; + } + + // -------- Concatenate RndA and RndB and encrypt it with the key + uint8_t concat_clear[32] = {0}; + uint8_t concat_enc[32] = {0}; + // This should of course be completely random, if we cared + // about security + uint8_t rnd_a_clear[16] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf + }; + + uint8_t iv[16] = {0}; + memcpy(&concat_clear[16], &rnd_b_clear[1], 15); + concat_clear[31] = rnd_b_clear[0]; + memcpy(concat_clear, rnd_a_clear, 16); + + aes_encode(iv, key, concat_clear, concat_enc, 32); + + // -------- Do second step with our concatenated encrypted RndA || RndB + uint8_t resp[4 + 16 + 6 + 6]; + res = ntag424_auth_second_step(concat_enc, resp); + if (res != PM3_SUCCESS) { + return res; + } + + ntag424_ev2_response_t response; + aes_decode(iv, key, resp, (uint8_t *)&response, sizeof(ntag424_ev2_response_t)); + + // -------- Verify that the response we got contains the RndA that we supplied (rotated one byte) + if (memcmp(response.rnd_a, &rnd_a_clear[1], 15) != 0 || + response.rnd_a[15] != rnd_a_clear[0]) { + PrintAndLogEx(ERR, "Incorrect response from card\n" + "expected: %s\n" + "got: %s" + , sprint_hex(rnd_a_clear, 16), + sprint_hex(response.rnd_a, 16)); + return PM3_ESOFT; + } + + // -------- Optionally calculate session keys + if (session_keys_out) { + memset(session_keys_out, 0, sizeof(ntag424_session_keys_t)); + memcpy(session_keys_out->ti, response.ti, sizeof(response.ti)); + + // SV 1 = [0xA5][0x5A][0x00][0x01] + // [0x00][0x80][RndA[15:14] || + // [ (RndA[13:8] ⊕ RndB[15:10]) ] || + // [RndB[9:0] || RndA[7:0] + + uint8_t sv1[] = { 0xa5, 0x5a, 0x00, 0x01, 0x00, 0x80, rnd_a_clear[0], rnd_a_clear[1], + rnd_a_clear[2] ^rnd_b_clear[0], + rnd_a_clear[3] ^rnd_b_clear[1], + rnd_a_clear[4] ^rnd_b_clear[2], + rnd_a_clear[5] ^rnd_b_clear[3], + rnd_a_clear[6] ^rnd_b_clear[4], + rnd_a_clear[7] ^rnd_b_clear[5], + rnd_b_clear[6], rnd_b_clear[7], rnd_b_clear[8], rnd_b_clear[9], rnd_b_clear[10], + rnd_b_clear[11], rnd_b_clear[12], rnd_b_clear[13], rnd_b_clear[14], rnd_b_clear[15], + rnd_a_clear[8], rnd_a_clear[9], rnd_a_clear[10], + rnd_a_clear[11], rnd_a_clear[12], rnd_a_clear[13], rnd_a_clear[14], rnd_a_clear[15] + }; + + // SV 2 = [0x5A][0xA5][0x00][0x01] + // [0x00][0x80][RndA[15:14] || + // [ (RndA[13:8] ⊕ RndB[15:10]) ] || + // [RndB[9:0] || RndA[7:0] + + uint8_t sv2[] = { 0x5a, 0xa5, 0x00, 0x01, 0x00, 0x80, rnd_a_clear[0], rnd_a_clear[1], + rnd_a_clear[2] ^rnd_b_clear[0], + rnd_a_clear[3] ^rnd_b_clear[1], + rnd_a_clear[4] ^rnd_b_clear[2], + rnd_a_clear[5] ^rnd_b_clear[3], + rnd_a_clear[6] ^rnd_b_clear[4], + rnd_a_clear[7] ^rnd_b_clear[5], + rnd_b_clear[6], rnd_b_clear[7], rnd_b_clear[8], rnd_b_clear[9], rnd_b_clear[10], + rnd_b_clear[11], rnd_b_clear[12], rnd_b_clear[13], rnd_b_clear[14], rnd_b_clear[15], + rnd_a_clear[8], rnd_a_clear[9], rnd_a_clear[10], + rnd_a_clear[11], rnd_a_clear[12], rnd_a_clear[13], rnd_a_clear[14], rnd_a_clear[15] + }; + + mbedtls_aes_cmac_prf_128(key, 16, sv1, sizeof(sv1), session_keys_out->encryption); + mbedtls_aes_cmac_prf_128(key, 16, sv2, sizeof(sv2), session_keys_out->mac); + } + + return PM3_SUCCESS; +} + +#define MAX_WRITE_APDU (200) + +// Write file to card. Only supports plain communications mode. Authentication must be done +// first unless file has free write access. +static int ntag424_write_data(uint8_t fileno, uint32_t offset, uint32_t num_bytes, uint8_t *in, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys) { + size_t remainder = 0; + + // Split writes that are too large for one APDU + if (num_bytes > MAX_WRITE_APDU) { + remainder = num_bytes - MAX_WRITE_APDU; + num_bytes = MAX_WRITE_APDU; + } + + uint8_t cmd_header[] = { + fileno, + (uint8_t)offset, + (uint8_t)(offset << 8), + (uint8_t)(offset << 16), // offset + (uint8_t)num_bytes, + (uint8_t)(num_bytes >> 8), + (uint8_t)(num_bytes >> 16) // size + }; + + uint8_t cmd[256] = {0}; + + memcpy(cmd, cmd_header, sizeof(cmd_header)); + memcpy(&cmd[sizeof(cmd_header)], in, num_bytes); + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_WRITE_DATA, + .lc = sizeof(cmd_header) + num_bytes, + .data = cmd, + }; + + int response_length = 8 + 2; // potential MAC and result + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, sizeof(cmd_header), response, &response_length, comm_mode, session_keys, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + if (remainder > 0) { + return ntag424_write_data(fileno, offset + num_bytes, remainder, &in[num_bytes], comm_mode, session_keys); + } + + return PM3_SUCCESS; +} + +// Read file from card. Only supports plain communications mode. Authentication must be done +// first unless file has free read access. +static int ntag424_read_data(uint8_t fileno, uint16_t offset, uint16_t num_bytes, uint8_t *out, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys) { + uint8_t cmd_header[] = { + fileno, + (uint8_t)offset, (uint8_t)(offset << 8), (uint8_t)(offset << 16), // offset + (uint8_t)num_bytes, (uint8_t)(num_bytes >> 8), 0x00 + }; + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_READ_DATA, + .lc = sizeof(cmd_header), + .data = cmd_header, + }; + + int response_length = num_bytes + 4 + 2 + 20; // number of bytes to read + mac + result + potential padding + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, sizeof(cmd_header), response, &response_length, comm_mode, session_keys, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + memcpy(out, response, num_bytes); + return PM3_SUCCESS; +} + +static int ntag424_get_version(ntag424_full_version_information_t *version) { + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_GET_VERSION, + }; + + + uint8_t response[256]; + + int response_length = sizeof(ntag424_version_information_t) + 2; + if (ntag424_exchange_apdu(apdu, 0, response, &response_length, COMM_PLAIN, NULL, 0x91, 0xAF) != PM3_SUCCESS) { + return PM3_ESOFT; + } + memcpy(&version->hardware, response, sizeof(ntag424_version_information_t)); + + APDU_t continue_apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_MORE_DATA, + }; + + response_length = sizeof(ntag424_version_information_t) + 2; + if (ntag424_exchange_apdu(continue_apdu, 0, response, &response_length, COMM_PLAIN, NULL, 0x91, 0xAF) != PM3_SUCCESS) { + return PM3_ESOFT; + } + memcpy(&version->software, response, sizeof(ntag424_version_information_t)); + + response_length = sizeof(ntag424_production_information_t) + 2; + if (ntag424_exchange_apdu(continue_apdu, 0, response, &response_length, COMM_PLAIN, NULL, 0x91, 0x00) != PM3_SUCCESS) { + return PM3_ESOFT; + } + memcpy(&version->production, response, sizeof(ntag424_production_information_t)); + + return PM3_SUCCESS; +} + +#define NXP_SIGNATURE_LENGTH 56 +#define NXP_SIGNATURE_ID 0x00 + +static int ntag424_get_signature(uint8_t *signature_out) { + uint8_t signature_id = NXP_SIGNATURE_ID; + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_GET_SIGNATURE, + .lc = 1, + .data = &signature_id, + }; + + int response_length = NXP_SIGNATURE_LENGTH + 2; + // This is a weird one. Datasheet claims this command should result in 91 00, but cards, and the AN12196 + // document shows 91 90 on success. + if (ntag424_exchange_apdu(apdu, 1, signature_out, &response_length, COMM_PLAIN, NULL, 0x91, 0x90) != PM3_SUCCESS) { + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int ntag424_change_key(uint8_t keyno, uint8_t *new_key, uint8_t *old_key, uint8_t version, ntag424_session_keys_t *session_keys) { + // -------- Calculate xor and crc + uint8_t key[16] = {0}; + uint8_t crc[4] = {0}; + if (keyno != 0) { + for (int i = 0; i < 16; i++) { + key[i] = old_key[i] ^ new_key[i]; + } + crc32_ex(new_key, 16, crc); + } else { + memcpy(key, new_key, 16); + } + + // ------- Assemble KeyData command + uint8_t key_cmd_data[32] = {0}; + key_cmd_data[0] = keyno; + memcpy(&key_cmd_data[1], key, 16); + key_cmd_data[17] = version; + int key_data_len; + if (keyno != 0) { + memcpy(&key_cmd_data[18], crc, sizeof(crc)); + key_data_len = sizeof(keyno) + sizeof(key) + sizeof(version) + sizeof(crc); + } else { + key_data_len = sizeof(keyno) + sizeof(key) + sizeof(version); + } + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_CHANGE_KEY, + .lc = key_data_len, + .data = key_cmd_data + }; + + int response_length = 8 + 2; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 1, response, &response_length, COMM_FULL, session_keys, 0x91, 0x00); + return res; +} static int CmdHelp(const char *Cmd); @@ -41,123 +858,38 @@ static int CmdHF_ntag424_view(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), 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); + char fn[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)fn, FILE_PATH_SIZE, &fnlen); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); // read dump file uint8_t *dump = NULL; size_t bytes_read = NTAG424_MAX_BYTES; - int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, NTAG424_MAX_BYTES); + int res = pm3_load_dump(fn, (void **)&dump, &bytes_read, NTAG424_MAX_BYTES); if (res != PM3_SUCCESS) { return res; } if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); + PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), fn); PrintAndLogEx(INFO, "File size %zu bytes", bytes_read); } + // to be implemented... + PrintAndLogEx(INFO, "not implemented yet"); + PrintAndLogEx(INFO, "Feel free to contribute!"); + free(dump); return PM3_SUCCESS; } -// -// Original from https://github.com/rfidhacking/node-sdm/ -// -typedef struct sdm_picc_s { - uint8_t tag; - uint8_t uid[7]; - uint8_t cnt[3]; - uint32_t cnt_int; -} sdm_picc_t; - -static int sdm_generator(void) { - - // NXP Secure Dynamic Messaging (SDM) with Secure Unique NFC message (SUN) - // Where do they come up with these names? - // - // ref: - // https://www.nxp.com/docs/en/application-note/AN12196.pdf - - // SMD / CMAC - uint8_t iv[16] = {0}; - uint8_t aeskey[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // uint8_t enc_txt[16] = {0xEF, 0x96, 0x3F, 0xF7, 0x82, 0x86, 0x58, 0xA5, 0x99, 0xF3, 0x04, 0x15, 0x10, 0x67, 0x1E, 0x88}; - uint8_t enc_txt[16] = {0xe6, 0x45, 0xb6, 0x15, 0x4e, 0x8f, 0x32, 0x7d, 0xfb, 0xab, 0x93, 0x4d, 0x4c, 0x66, 0x46, 0x14}; - uint8_t dec_txt[16] = {0}; - - aes_decode(iv, aeskey, enc_txt, dec_txt, sizeof(enc_txt)); - - PrintAndLogEx(INFO, "Ntag424 SUN message validation and encryption"); - PrintAndLogEx(INFO, "Enc text... %s", sprint_hex(enc_txt, sizeof(enc_txt))); - PrintAndLogEx(INFO, "Dec text... %s", sprint_hex(dec_txt, sizeof(dec_txt))); - - sdm_picc_t o = {0}; - o.tag = dec_txt[0]; - memcpy(o.uid, dec_txt + 1, sizeof(o.uid)); - memcpy(o.cnt, dec_txt + 8, sizeof(o.cnt)); - o.cnt_int = MemLeToUint3byte(o.cnt); - - PrintAndLogEx(INFO, "Decypted text"); - PrintAndLogEx(INFO, " Tag........... 0x%02X", o.tag); - PrintAndLogEx(INFO, " UID........... %s", sprint_hex(o.uid, sizeof(o.uid))); - PrintAndLogEx(INFO, " Count bytes... %s", sprint_hex(o.cnt, sizeof(o.cnt))); - PrintAndLogEx(INFO, " Count value... 0x%X ( %u )", o.cnt_int, o.cnt_int); - - // SV2 as per NXP DS465430 (NT4H2421Gx Data sheet) - uint8_t sv2data[16] = {0x3C, 0xC3, 0x00, 0x01, 0x00, 0x80}; - - memcpy(sv2data + 6, o.uid, sizeof(o.uid)); - memcpy(sv2data + 6 + sizeof(o.uid), o.cnt, sizeof(o.cnt)); - - uint8_t cmackey[16] = {0}; - mbedtls_aes_cmac_prf_128(aeskey, 16, sv2data, sizeof(sv2data), cmackey); - - uint8_t zero[16] = {0}; - uint8_t full_cmac[16] = {0}; - mbedtls_aes_cmac_prf_128(cmackey, 16, zero, 0, full_cmac); - - uint8_t cmac[8] = {0}; - for (int i = 0, j = 1; i < 8; ++i, j += 2) { - cmac[i] = full_cmac[j]; - } - - //uint8_t veri[] = {0x94, 0xee, 0xd9, 0xee, 0x65, 0x33, 0x70, 0x86}; - uint8_t veri[] = {0x8b, 0xa1, 0xfb, 0x47, 0x0d, 0x63, 0x39, 0xe8 }; - uint8_t is_ok = (memcmp(cmac, veri, 8) == 0); - - PrintAndLogEx(INFO, "SDM cmac... %s ( %s )", - sprint_hex(cmac, sizeof(cmac)), - (is_ok) ? _GREEN_("ok") : _RED_("fail") - ); - - return PM3_SUCCESS; -} -static int CmdHF_ntag424_sdm(const char *Cmd) { - - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf ntag424 sdm", - "Validate a SDM message", - "hf ntag424 sdm" - ); - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - CLIParserFree(ctx); - - return sdm_generator(); -} - static int CmdHF_ntag424_info(const char *Cmd) { CLIParserContext *ctx; @@ -169,40 +901,583 @@ static int CmdHF_ntag424_info(const char *Cmd) { arg_param_begin, arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - PrintAndLogEx(INFO, "not implemented yet"); - PrintAndLogEx(INFO, "Feel free to contribute!"); + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to select card"); + DropField(); + return PM3_ERFTRANS; + } + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } - // has hardcoded application and three files. + ntag424_full_version_information_t version = {0}; + if (ntag424_get_version(&version) != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + ntag424_print_full_version_information(&version); + uint8_t signature[NXP_SIGNATURE_LENGTH]; + int res = ntag424_get_signature(signature); + DropField(); - /* - // Check if the tag reponds to APDUs. - PrintAndLogEx(INFO, "Sending a test APDU (select file command) to check if the tag is responding to APDU"); - param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, true, false, response, sizeof(response), &response_n); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Tag did not respond to a test APDU (select file command). Aborting..."); - return res; - } - */ + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "--- " _CYAN_("NXP originality signature:")); + desfire_print_signature(version.production.uid, 7, signature, NXP_SIGNATURE_LENGTH); + } + return res; +} + +static int ntag424_cli_get_auth_information(CLIParserContext *ctx, int keyno_index, int key_index, int *keyno, uint8_t *key_out) { + + if (keyno) { + *keyno = arg_get_int(ctx, keyno_index); + } + + int keylen = 16; + uint8_t key[16] = {0}; + + if (CLIParamHexToBuf(arg_get_str(ctx, key_index), key, sizeof(key), &keylen) || (keylen != 16)) { + return PM3_ESOFT; + } + + memcpy(key_out, key, 16); return PM3_SUCCESS; } -//------------------------------------ -// Menu Stuff -//------------------------------------ +static int CmdHF_ntag424_auth(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 auth", + "Authenticate with selected key against NTAG424.", + "hf ntag424 auth --keyno 0 -k 00000000000000000000000000000000"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "keyno", "", "Key number"), + arg_str1("k", "key", "", "Key for authenticate (HEX 16 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keyno; + uint8_t key[16] = {0}; + if (ntag424_cli_get_auth_information(ctx, 1, 2, &keyno, key) != PM3_SUCCESS) { + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to select card"); + DropField(); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = ntag424_authenticate_ev2_first(keyno, key, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + + DropField(); + return PM3_SUCCESS; +} + +// Read can only read files with plain communication mode! +static int CmdHF_ntag424_read(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 read", + "Read and print data from file on NTAG424 tag. Will authenticate if key information is provided.", + "hf ntag424 read --fileno 1 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 32\n" + "hf ntag424 read --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 256\n" + "hf ntag424 read --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -l 128 -m encrypt"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "fileno", "<1|2|3>", "File number"), + arg_int0(NULL, "keyno", "", "Key number"), + arg_str0("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_int0("o", "offset", "", "Offset to read in file (def 0)"), + arg_int1("l", "length", "", "Number of bytes to read"), + arg_str0("m", "cmode", "", "Communication mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fileno = arg_get_int(ctx, 1); + + int keyno; + uint8_t key[16] = {0}; + bool auth = (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) == PM3_SUCCESS); + + int offset = arg_get_int_def(ctx, 4, 0); + int read_length = arg_get_int(ctx, 5); + + ntag424_communication_mode_t comm_mode; + int comm_out = 0; + if (CLIGetOptionList(arg_get_str(ctx, 6), ntag424_communication_mode_options, &comm_out)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + comm_mode = comm_out; + + if (comm_mode != COMM_PLAIN && auth == false) { + PrintAndLogEx(ERR, "Only plain communication mode can be used without a key specified"); + return PM3_EINVARG; + } + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session_keys; + if (auth) { + res = ntag424_authenticate_ev2_first(keyno, key, &session_keys); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + DropField(); + return res; + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + } + + uint8_t data[512] = {0}; + res = ntag424_read_data(fileno, offset, read_length, data, comm_mode, &session_keys); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, " -------- Read file " _YELLOW_("%d") " contents ------------ ", fileno); + print_hex_break(data, read_length, 16); + } + return res; +} + +static int CmdHF_ntag424_write(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 write", + "Write data to file on NTAG424 tag. Will authenticate if key information is provided.", + "hf ntag424 write --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788\n" + "hf ntag424 write --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788 -m encrypt"); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "fileno", "<1|2|3>", "File number (def 2)"), + arg_int0(NULL, "keyno", "", "Key number"), + arg_str0("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_int0("o", "offset", "", "Offset to write in file (def 0)"), + arg_str1("d", "data", "", "Data to write"), + arg_str0("m", "cmode", "", "Communication mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fileno = arg_get_int(ctx, 1); + + int keyno = 0; + uint8_t key[16] = {0}; + bool auth = (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) == PM3_SUCCESS); + + uint32_t offset = arg_get_u32_def(ctx, 4, 0); + + uint8_t data[512] = {0}; + int datalen = 512; + CLIGetHexWithReturn(ctx, 5, data, &datalen); + + ntag424_communication_mode_t comm_mode; + int comm_out = 0; + if (CLIGetOptionList(arg_get_str(ctx, 6), ntag424_communication_mode_options, &comm_out)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + comm_mode = comm_out; + + if (comm_mode != COMM_PLAIN && auth == false) { + PrintAndLogEx(ERR, "Only plain communication mode can be used without a key specified"); + return PM3_EINVARG; + } + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session_keys = {0}; + if (auth) { + res = ntag424_authenticate_ev2_first(keyno, key, &session_keys); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + DropField(); + return res; + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + } + + res = ntag424_write_data(fileno, offset, (uint32_t)datalen, data, comm_mode, &session_keys); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Wrote " _YELLOW_("%d") " bytes ( " _GREEN_("ok") " )", datalen); + } else { + PrintAndLogEx(ERR, "Wrote " _YELLOW_("%d") " bytes ( " _RED_("fail") " )", datalen); + } + return res; +} + +static int CmdHF_ntag424_getfilesettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 getfs", + "Read and print file settings for file", + "hf ntag424 getfs --fileno 2"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "fileno", "", "File number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fileno = arg_get_int(ctx, 1); + + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + ntag424_file_settings_t settings = {0}; + int res = ntag424_get_file_settings(fileno, &settings); + DropField(); + if (res == PM3_SUCCESS) { + ntag424_print_file_settings(fileno, &settings); + } + return res; +} + +static int CmdHF_ntag424_changefilesettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 changefs", + "Updates file settings for file, must be authenticated.\n" + "This is a short explanation of the settings. See AN12196 for more information:\n" + "options: byte with bit flags\n" + " Bit: Setting:\n" + " 6 Enable SDM and mirroring\n\n" + + "access: two byte access rights.\n" + "Each nibble is a key number, or E for free access.\n" + "Order is key for readwrite, change, read and write\n\n" + + "sdmoptions: byte with bit flags\n" + " Bit: Setting:\n" + " 0 ASCII encoding\n" + " 4 SDMEncFileData\n" + " 5 SDMReadCtrLimit\n" + " 6 SDMReadCtr\n" + " 7 SDMOptionsUID\n\n" + + "sdmaccess: two byte access rights.\n" + "Each nibble is a key, or E for plain mirror and F for no mirroring\n" + "Order is Reserved, SDMCtrRet, SDMMetaRead and SDMFileRead\n\n" + + "sdm_data: Three bytes of data used to control SDM settings. Can be specified multiple times.\n" + "Data means different things depending on settings.\n\n" + + "Note: Not all of these settings will be written. It depends on the option byte, and the keys set. See AN12196 for more information.\n" + "You must also start with sdmdata1, then sdmdata2, up to the number of sdm_data you want to write", + + "hf ntag424 changefs --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 40 -a 00E0 -s C1 -c F000 --data1 000020 --data2 000043 --data3 000043" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "fileno", "", "File number"), + arg_int1(NULL, "keyno", "", "Key number"), + arg_str1("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_str0("o", "options", "", "File options byte (HEX 1 byte)"), + arg_str0("a", "access", "", "File access settings (HEX 2 bytes)"), + arg_str0("s", "sdmoptions", "", "SDM options (HEX 1 byte)"), + arg_str0("c", "sdmaccess", "", "SDM access settings (HEX 2 bytes)"), + arg_str0(NULL, "data1", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data2", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data3", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data4", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data5", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data6", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data7", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data8", "", "SDM data (HEX 3 bytes)"), + // Sorry, couldn't figure out how to work with arg_strn... + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fileno = arg_get_int(ctx, 1); + + int keyno; + uint8_t key[16] = {0}; + + uint8_t has_options = 0; + uint8_t options[1]; + uint8_t has_access = 0; + uint8_t access[2]; + uint8_t has_sdmoptions = 0; + uint8_t sdmoptions[1]; + uint8_t has_sdmaccess = 0; + uint8_t sdmaccess[2]; + uint8_t num_sdm_data = 0; + uint8_t sdm_data[8][3]; + + if (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get key settings"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int len = 1; + if (arg_get_str(ctx, 4)->count == 1) { + has_options = 1; + CLIGetHexWithReturn(ctx, 4, options, &len); + if (len != 1) { + PrintAndLogEx(ERR, "Options must be 1 byte, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 2; + if (arg_get_str(ctx, 5)->count == 1) { + has_access = 1; + CLIGetHexWithReturn(ctx, 5, access, &len); + if (len != 2) { + PrintAndLogEx(ERR, "Access must be 2 bytes, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 1; + if (arg_get_str(ctx, 6)->count == 1) { + has_sdmoptions = 1; + CLIGetHexWithReturn(ctx, 6, sdmoptions, &len); + if (len != 1) { + PrintAndLogEx(ERR, "SDM Options must be 1 byte, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 2; + if (arg_get_str(ctx, 7)->count == 1) { + has_sdmaccess = 1; + CLIGetHexWithReturn(ctx, 7, sdmaccess, &len); + if (len != 2) { + PrintAndLogEx(ERR, "SDM Access must be 2 bytes, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + for (int i = 0; i < 8; i++) { + if (arg_get_str(ctx, 8 + i)->count == 1) { + len = 3; + num_sdm_data++; + CLIGetHexWithReturn(ctx, 8 + i, sdm_data[i], &len); + if (len != 3) { + PrintAndLogEx(ERR, "sdmdata must be 3 bytes, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + break; + } + } + + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + ntag424_file_settings_t settings = {0}; + if (ntag424_get_file_settings(fileno, &settings) != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session = {0}; + res = ntag424_authenticate_ev2_first(keyno, key, &session); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + DropField(); + return res; + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + + if (has_options) { + settings.options = options[0]; + } + + if (has_access) { + memcpy(settings.access, access, 2); + } + + if (has_sdmoptions) { + settings.optional_sdm_settings.sdm_options = sdmoptions[0]; + } + + if (has_sdmaccess) { + memcpy(settings.optional_sdm_settings.sdm_access, sdmaccess, 2); + } + + for (int i = 0; i < num_sdm_data; i++) { + settings.optional_sdm_settings.sdm_data[i][2] = sdm_data[i][0]; + settings.optional_sdm_settings.sdm_data[i][1] = sdm_data[i][1]; + settings.optional_sdm_settings.sdm_data[i][0] = sdm_data[i][2]; + } + + res = ntag424_write_file_settings(fileno, &settings, &session); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write settings ( " _GREEN_("ok") " )"); + ntag424_print_file_settings(fileno, &settings); + } else { + PrintAndLogEx(ERR, "Write settings (" _RED_("fail") " )"); + } + return res; +} + +static int CmdHF_ntag424_changekey(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 changekey", + "Change a key.\n" + "Authentication key must currently be different to the one we want to change.\n", + "hf ntag424 changekey --keyno 1 --oldkey 00000000000000000000000000000000 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1\n" + "hf ntag424 changekey --keyno 0 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "keyno", "", "Key number to change"), + arg_str0(NULL, "oldkey", "", "Old key (only needed when changing key 1-4, HEX 16 bytes)"), + arg_str1(NULL, "newkey", "", "New key (HEX 16 bytes)"), + arg_str1(NULL, "key0", "", "Authentication key (must be key 0, HEX 16 bytes)"), + arg_int1(NULL, "kv", "", "New key version number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t version = arg_get_int(ctx, 6); + int keyno = arg_get_int(ctx, 1); + + uint8_t oldkey[16] = {0}; + if (keyno != 0) { + if (ntag424_cli_get_auth_information(ctx, 0, 2, NULL, oldkey) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get keyno or old key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + uint8_t newkey[16] = {0}; + if (ntag424_cli_get_auth_information(ctx, 0, 3, NULL, newkey) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get new key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t authkey[16] = {0}; + if (ntag424_cli_get_auth_information(ctx, 0, 4, NULL, authkey) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get authentication key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session = {0}; + res = ntag424_authenticate_ev2_first(0, authkey, &session); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Auth ( " _RED_("fail") " )"); + return PM3_ESOFT; + } else { + PrintAndLogEx(SUCCESS, "Auth ( " _GREEN_("ok") " )"); + } + + res = ntag424_change_key(keyno, newkey, oldkey, version, &session); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Change key %d ( " _GREEN_("ok") " )", keyno); + } else { + PrintAndLogEx(ERR, "Change key %d ( "_RED_("fail") " )", keyno); + } + + return res; +} + static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, - {"info", CmdHF_ntag424_info, IfPm3Iso14443a, "Tag information"}, -// {"ndefread", CmdHF_ntag424_sdm, IfPm3Iso14443a, "Prints NDEF records from card"}, - {"sdm", CmdHF_ntag424_sdm, IfPm3Iso14443a, "Prints NDEF records from card"}, - {"view", CmdHF_ntag424_view, AlwaysAvailable, "Display content from tag dump file"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, + {"info", CmdHF_ntag424_info, IfPm3Iso14443a, "Tag information"}, + {"view", CmdHF_ntag424_view, AlwaysAvailable, "Display content from tag dump file"}, + {"auth", CmdHF_ntag424_auth, IfPm3Iso14443a, "Test authentication with key"}, + {"read", CmdHF_ntag424_read, IfPm3Iso14443a, "Read file"}, + {"write", CmdHF_ntag424_write, IfPm3Iso14443a, "Write file"}, + {"getfs", CmdHF_ntag424_getfilesettings, IfPm3Iso14443a, "Get file settings"}, + {"changefs", CmdHF_ntag424_changefilesettings, IfPm3Iso14443a, "Change file settings"}, + {"changekey", CmdHF_ntag424_changekey, IfPm3Iso14443a, "Change key"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index 86dfe0584..9a2f84e7c 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -404,10 +404,15 @@ int CmdHFST25TANdefRead(const char *Cmd) { return PM3_ESOFT; } - if (fnlen != 0) { - saveFile(filename, ".bin", response + 2, resplen - 4); - } NDEFRecordsDecodeAndPrint(response + 2, resplen - 4, verbose); + + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(response, resplen, &n) != PM3_SUCCESS) + n = resplen; + + pm3_save_dump(filename, response + 2, n, jsfNDEF); + return PM3_SUCCESS; } diff --git a/client/src/cmdhftexkom.c b/client/src/cmdhftexkom.c index 6cb77a54e..6063e807c 100644 --- a/client/src/cmdhftexkom.c +++ b/client/src/cmdhftexkom.c @@ -429,7 +429,7 @@ static void TexcomReverseCode(const uint8_t *code, int length, uint8_t *reverse_ for (int i = 0; i < length; i++) { reverse_code[i] = code[(length - 1) - i]; } -}; +} static int texkom_get_type(texkom_card_select_t *card, bool verbose) { @@ -493,7 +493,7 @@ static int texkom_get_type(texkom_card_select_t *card, bool verbose) { noiselvl = TEXKOM_NOISE_THRESHOLD; } - uint32_t implengths[256] = {}; + uint32_t implengths[256] = { 0 }; uint32_t implengthslen = 0; uint32_t impulseindx = 0; uint32_t impulsecnt = 0; @@ -707,7 +707,7 @@ static int CmdHFTexkomReader(const char *Cmd) { //PrintAndLogEx(WARNING, "--- indx: %d, len: %d, max: %d, noise: %d", sindx, slen, maxlvl, noiselvl); - uint32_t implengths[256] = {}; + uint32_t implengths[256] = { 0 }; uint32_t implengthslen = 0; uint32_t impulseindx = 0; uint32_t impulsecnt = 0; @@ -872,7 +872,7 @@ static int CmdHFTexkomSim(const char *Cmd) { uint8_t data[8]; uint8_t modulation; uint32_t timeout; - } PACKED payload = {}; + } PACKED payload = {0}; bool verbose = arg_get_lit(ctx, 1); payload.modulation = 0; // tk-13 diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index 649882416..5b8ab2677 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -838,13 +838,13 @@ static int CmdHFTopazDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz dump", - "Dump TOPAZ tag to binary file\n" + "Dump TOPAZ tag to file (bin/json)\n" "If no given, UID will be used as filename", "hf topaz dump\n"); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; @@ -897,9 +897,9 @@ static int CmdHFTopazDump(const char *Cmd) { } if (topaz_tag.size) - pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t) + topaz_tag.size, jsfTopaz, TOPAZ_BLOCK_SIZE); + pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t) + topaz_tag.size, jsfTopaz); else - pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t), jsfTopaz, TOPAZ_BLOCK_SIZE); + pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t), jsfTopaz); if (set_dynamic) { free(topaz_tag.dynamic_memory); @@ -916,7 +916,7 @@ static int CmdHFTopazView(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/client/src/cmdhfvas.c b/client/src/cmdhfvas.c index 4e4a305e5..0c6cc5df8 100644 --- a/client/src/cmdhfvas.c +++ b/client/src/cmdhfvas.c @@ -356,7 +356,7 @@ static int VASReader(uint8_t *pidHash, const char *url, size_t urlLen, uint8_t * }; if (SelectCard14443A_4_WithParameters(false, false, NULL, &polling_parameters) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "No card in field"); + PrintAndLogEx(WARNING, "No ISO14443-A Card in field"); return PM3_ECARDEXCHANGE; } @@ -586,10 +586,10 @@ static command_t CommandTable[] = { int CmdHFVAS(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); -}; +} static int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); return PM3_SUCCESS; -}; +} diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c index fec69855e..5f6f1e11c 100644 --- a/client/src/cmdhfxerox.c +++ b/client/src/cmdhfxerox.c @@ -611,9 +611,7 @@ static int CmdHFXeroxDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool decrypt = arg_get_lit(ctx, 2); - CLIParserFree(ctx); iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); @@ -682,7 +680,6 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); -// PrintAndLogEx(INPLACE, "blk %3d", blocknum); } } @@ -773,23 +770,13 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(NORMAL, ""); if (0 == filename[0]) { // generate filename from uid - /* - PrintAndLogEx(INFO, "Using UID as filename"); - - sprintf(filename, "hf-xerox-%02X%02X%02X%02X%02X%02X%02X%02X-dump%s", - card.uid[7],card.uid[6],card.uid[5],card.uid[4],card.uid[3],card.uid[2],card.uid[1],card.uid[0], - decrypt ? "-dec" : ""); - */ char *fptr = filename; PrintAndLogEx(INFO, "Using UID as filename"); fptr += snprintf(fptr, sizeof(filename), "hf-xerox-"); FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), decrypt ? "-dump-dec" : "-dump", card.uidlen); } - size_t datalen = blocknum * 4; - saveFile(filename, ".bin", data, datalen); - saveFileEML(filename, data, datalen, 4); -// saveFileJSON(filename, jsf15, data, datalen, NULL); + pm3_save_dump(filename, data, blocknum * 4, jsf14b_v2); return PM3_SUCCESS; } diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 4b26bc00e..d0a7102cf 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -35,6 +35,7 @@ #include "pmflash.h" // rdv40validation_t #include "cmdflashmem.h" // get_signature.. #include "uart/uart.h" // configure timeout +#include "util_posix.h" static int CmdHelp(const char *Cmd); @@ -44,6 +45,7 @@ static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) { case 0x270B0A40: asBuff = "AT91SAM7S512 Rev A"; break; + case 0x270B0A4E: case 0x270B0A4F: asBuff = "AT91SAM7S512 Rev B"; break; @@ -152,6 +154,7 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) { case 0x270B0A40: asBuff = "AT91SAM7S512 Rev A"; break; + case 0x270B0A4E: case 0x270B0A4F: asBuff = "AT91SAM7S512 Rev B"; break; @@ -809,19 +812,29 @@ static int CmdStatus(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw status", "Show runtime status information about the connected Proxmark3", - "hw status" + "hw status\n" + "hw status --ms 1000 -> Test connection speed with 1000ms timeout\n" ); void *argtable[] = { arg_param_begin, + arg_int0("m", "ms", "", "speed test timeout in micro seconds"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + int32_t speedTestTimeout = arg_get_int_def(ctx, 1, -1); CLIParserFree(ctx); + clearCommandBuffer(); PacketResponseNG resp; - SendCommandNG(CMD_STATUS, NULL, 0); - if (WaitForResponseTimeout(CMD_STATUS, &resp, 2000) == false) { + if (speedTestTimeout < 0) { + speedTestTimeout = 0; + SendCommandNG(CMD_STATUS, NULL, 0); + } else { + SendCommandNG(CMD_STATUS, (uint8_t *)&speedTestTimeout, sizeof(speedTestTimeout)); + } + + if (WaitForResponseTimeout(CMD_STATUS, &resp, 2000 + speedTestTimeout) == false) { PrintAndLogEx(WARNING, "Status command timeout. Communication speed test timed out"); return PM3_ETIMEOUT; } @@ -931,14 +944,14 @@ static int CmdTimeout(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw timeout", "Set the communication timeout on the client side", - "hw timeout --> Show current timeout\n" - "hw timeout -t 20 --> Set the timeout to 20ms\n" - "hw timeout -t 500 --> Set the timeout to 500ms\n" + "hw timeout --> Show current timeout\n" + "hw timeout -m 20 --> Set the timeout to 20ms\n" + "hw timeout --ms 500 --> Set the timeout to 500ms\n" ); void *argtable[] = { arg_param_begin, - arg_int0("t", "timeout", "", "timeout in ms"), + arg_int0("m", "ms", "", "timeout in micro seconds"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -949,20 +962,20 @@ static int CmdTimeout(const char *Cmd) { // timeout is not given/invalid, just show the current timeout then return if (arg < 0) { - PrintAndLogEx(INFO, "Current communication timeout: %ums", oldTimeout); + PrintAndLogEx(INFO, "Current communication timeout... " _GREEN_("%u") " ms", oldTimeout); return PM3_SUCCESS; } uint32_t newTimeout = arg; // UART_USB_CLIENT_RX_TIMEOUT_MS is considered as the minimum required timeout. if (newTimeout < UART_USB_CLIENT_RX_TIMEOUT_MS) { - PrintAndLogEx(WARNING, "Timeout less than %ums might cause errors.", UART_USB_CLIENT_RX_TIMEOUT_MS); + PrintAndLogEx(WARNING, "Timeout less than %u ms might cause errors.", UART_USB_CLIENT_RX_TIMEOUT_MS); } else if (newTimeout > 5000) { - PrintAndLogEx(WARNING, "Timeout greater than 5000ms makes the client unresponsive."); + PrintAndLogEx(WARNING, "Timeout greater than 5000 ms makes the client unresponsive."); } uart_reconfigure_timeouts(newTimeout); - PrintAndLogEx(INFO, "Old communication timeout: %ums", oldTimeout); - PrintAndLogEx(INFO, "New communication timeout: %ums", newTimeout); + PrintAndLogEx(INFO, "Old communication timeout... %u ms", oldTimeout); + PrintAndLogEx(INFO, "New communication timeout... " _GREEN_("%u") " ms", newTimeout); return PM3_SUCCESS; } @@ -979,6 +992,7 @@ static int CmdPing(const char *Cmd) { arg_u64_0("l", "len", "", "length of payload to send"), arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, true); uint32_t len = arg_get_u32_def(ctx, 1, 32); CLIParserFree(ctx); @@ -987,7 +1001,7 @@ static int CmdPing(const char *Cmd) { len = PM3_CMD_DATA_SIZE; if (len) { - PrintAndLogEx(INFO, "Ping sent with payload len " _YELLOW_("%d"), len); + PrintAndLogEx(INFO, "Ping sent with payload len... " _YELLOW_("%d"), len); } else { PrintAndLogEx(INFO, "Ping sent"); } @@ -996,16 +1010,22 @@ static int CmdPing(const char *Cmd) { PacketResponseNG resp; uint8_t data[PM3_CMD_DATA_SIZE] = {0}; - for (uint16_t i = 0; i < len; i++) + for (uint16_t i = 0; i < len; i++) { data[i] = i & 0xFF; + } + uint64_t tms = msclock(); SendCommandNG(CMD_PING, data, len); if (WaitForResponseTimeout(CMD_PING, &resp, 1000)) { + tms = msclock() - tms; if (len) { bool error = (memcmp(data, resp.data.asBytes, len) != 0); - PrintAndLogEx((error) ? ERR : SUCCESS, "Ping response " _GREEN_("received") " and content () %s )", error ? _RED_("fail") : _GREEN_("ok")); + PrintAndLogEx((error) ? ERR : SUCCESS, "Ping response " _GREEN_("received") + " in " _YELLOW_("%" PRIu64) " ms and content ( %s )", + tms, error ? _RED_("fail") : _GREEN_("ok")); } else { - PrintAndLogEx(SUCCESS, "Ping response " _GREEN_("received")); + PrintAndLogEx(SUCCESS, "Ping response " _GREEN_("received") + " in " _YELLOW_("%" PRIu64) " ms", tms); } } else PrintAndLogEx(WARNING, "Ping response " _RED_("timeout")); diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 87095631b..1d57378c7 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +// //----------------------------------------------------------------------------- // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify @@ -469,7 +469,7 @@ int CmdFlexdemod(const char *Cmd) { sum += data[i++]; } bits[bit] = (sum > 0) ? 1 : 0; - PrintAndLogEx(NORMAL, "bit %d sum %d", bit, sum); + // PrintAndLogEx(NORMAL, "bit %d sum %d", bit, sum); } for (bit = 0; bit < 64; bit++) { diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 29ad61bc9..a8e70fe36 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -528,8 +528,9 @@ static int CmdEM410xBrute(const char *Cmd) { //The line start with # is comment, skip if (buf[0] == '#') continue; - if (param_gethex(buf, 0, uid, 10)) { - PrintAndLogEx(FAILED, "EM Tag IDs must include 5 hex bytes (10 hex symbols)"); + int uidlen = 0; + if (param_gethex_ex(buf, 0, uid, &uidlen) && (uidlen != 10)) { + PrintAndLogEx(FAILED, "EM Tag IDs must include 5 hex bytes (10 hex symbols), got ( " _RED_("%d") " )", uidlen); free(uidblock); fclose(f); return PM3_ESOFT; diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 665518a40..f93db58d3 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -514,6 +514,7 @@ int CmdEM4x05Dump(const char *Cmd) { arg_param_begin, arg_str0("p", "pwd", "", "password (00000000)"), arg_str0("f", "file", "", "override filename prefix (optional). Default is based on UID"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -522,6 +523,7 @@ int CmdEM4x05Dump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool nosave = arg_get_lit(ctx, 3); CLIParserFree(ctx); uint8_t addr = 0; @@ -693,6 +695,13 @@ int CmdEM4x05Dump(const char *Cmd) { } else { } + if (nosave) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + // all ok save dump to file if (success == PM3_SUCCESS) { @@ -709,9 +718,9 @@ int CmdEM4x05Dump(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); if (card_type == EM_4369 || card_type == EM_4469) - pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x69, 4); + pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x69); else - pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x05, 4); + pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x05); } PrintAndLogEx(NORMAL, ""); return success; @@ -741,16 +750,16 @@ int CmdEM4x05Read(const char *Cmd) { bool use_pwd = false; if (addr > 15) { - PrintAndLogEx(ERR, "Address must be between 0 and 15"); + PrintAndLogEx(ERR, "Address must be between 0 and 15, got " _RED_("%d"), addr); return PM3_EINVARG; } if (inputpwd == 0xFFFFFFFFFFFFFFFF) { - PrintAndLogEx(INFO, "Reading address %02u", addr); + PrintAndLogEx(INFO, "Reading address " _YELLOW_("%02u"), addr); } else { pwd = (inputpwd & 0xFFFFFFFF); use_pwd = true; - PrintAndLogEx(INFO, "Reading address %02u using password %08X", addr, pwd); + PrintAndLogEx(INFO, "Reading address " _YELLOW_("%02u") " using password " _YELLOW_("%08X"), addr, pwd); } uint32_t word = 0; @@ -818,14 +827,14 @@ int CmdEM4x05Write(const char *Cmd) { if (use_pwd) { if (protect_operation) - PrintAndLogEx(INFO, "Writing protection words data %08X using password %08X", data, pwd); + PrintAndLogEx(INFO, "Writing protection words data " _YELLOW_("%08X") " using password " _YELLOW_("%08X"), data, pwd); else - PrintAndLogEx(INFO, "Writing address %d data %08X using password %08X", addr, data, pwd); + PrintAndLogEx(INFO, "Writing address " _YELLOW_("%d") " data " _YELLOW_("%08X") " using password " _YELLOW_("%08X"), addr, data, pwd); } else { if (protect_operation) - PrintAndLogEx(INFO, "Writing protection words data %08X", data); + PrintAndLogEx(INFO, "Writing protection words data " _YELLOW_("%08X"), data); else - PrintAndLogEx(INFO, "Writing address %d data %08X", addr, data); + PrintAndLogEx(INFO, "Writing address " _YELLOW_("%d") " data " _YELLOW_("%08X"), addr, data); } res = PM3_SUCCESS; @@ -850,7 +859,7 @@ int CmdEM4x05Write(const char *Cmd) { if (status == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Data written and verified"); else if (status == PM3_EFAILED) - PrintAndLogEx(ERR, "Tag denied %s operation", protect_operation ? "Protect" : "Write"); + PrintAndLogEx(ERR, "Tag denied " _RED_("%s") " operation", protect_operation ? "Protect" : "Write"); else PrintAndLogEx(DEBUG, "No answer from tag"); @@ -2020,7 +2029,7 @@ int CmdEM4x05Sniff(const char *Cmd) { PrintAndLogEx(SUCCESS, "-------+-------------+----------+-----+------------------------------------------------------------"); smartbuf bits = { 0 }; - bits.ptr = malloc(EM4X05_BITS_BUFSIZE); + bits.ptr = calloc(EM4X05_BITS_BUFSIZE, sizeof(uint8_t)); bits.size = EM4X05_BITS_BUFSIZE; bits.idx = 0; size_t idx = 0; diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index a304a84fe..b94acfcc5 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -194,7 +194,7 @@ int CmdEM4x50ELoad(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "dump filename (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; @@ -223,7 +223,7 @@ int CmdEM4x50ELoad(const char *Cmd) { int CmdEM4x50ESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 esave", - "Saves bin/eml/json dump file of emulator memory.", + "Saves bin/json dump file of emulator memory.", "lf em 4x50 esave -> use UID as filename\n" "lf em 4x50 esave -f mydump\n" ); @@ -264,7 +264,7 @@ int CmdEM4x50ESave(const char *Cmd) { FillFileNameByUID(fptr, (uint8_t *)&data[4 * EM4X50_DEVICE_ID], "-dump", 4); } - pm3_save_dump(filename, data, DUMP_FILESIZE, jsfEM4x50, 4); + pm3_save_dump(filename, data, DUMP_FILESIZE, jsfEM4x50); return PM3_SUCCESS; } @@ -796,7 +796,7 @@ 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/json) format", "lf em 4x50 dump\n" "lf em 4x50 dump -f mydump\n" "lf em 4x50 dump -p 12345678\n" @@ -805,7 +805,7 @@ int CmdEM4x50Dump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "specify dump filename"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -867,7 +867,7 @@ int CmdEM4x50Dump(const char *Cmd) { memcpy(data + (i * 4), words[i].byte, 4); } - pm3_save_dump(filename, data, sizeof(data), jsfEM4x50, 4); + pm3_save_dump(filename, data, sizeof(data), jsfEM4x50); return PM3_SUCCESS; } @@ -1113,7 +1113,7 @@ int CmdEM4x50Restore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "uid, 4 hex bytes, msb"), - arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "specify a filename for dump file"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index d197a6dcb..7e3763ec5 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -44,6 +44,7 @@ #include "wiegand_formats.h" #include "wiegand_formatutils.h" #include "cmdlfem4x05.h" // EM defines +#include "loclass/cipherutils.h" // bitstreamout #ifndef BITS # define BITS 96 @@ -399,14 +400,32 @@ 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); - if (res != bin_len) { - PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); - return PM3_EINVARG; + + uint8_t hex[12]; + memset(hex, 0, sizeof(hex)); + BitstreamOut_t bout = {hex, 0, 0 }; + + for (int i = 0; i < 96 - bin_len - 1; i++) { + pushBit(&bout, 0); } - packed.Top = top; - packed.Mid = mid; - packed.Bot = bot; + // add binary sentinel bit. + pushBit(&bout, 1); + + // convert binary string to hex bytes + for (int i = 0; i < bin_len; i++) { + char c = bin[i]; + if (c == '1') + pushBit(&bout, 1); + else if (c == '0') + pushBit(&bout, 0); + } + + packed.Length = bin_len; + packed.Top = bytes_to_num(hex, 4); + packed.Mid = bytes_to_num(hex + 4, 4); + packed.Bot = bytes_to_num(hex + 8, 4); + add_HID_header(&packed); + } else { if (HIDPack(format_idx, &card, &packed, true) == false) { PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 1cbae7bd3..45206750f 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -1127,7 +1127,7 @@ static int CmdLFHitag2Dump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Dumping tag memory..."); - pm3_save_dump(filename, data, 48, jsfHitag, 4); + pm3_save_dump(filename, data, 48, jsfHitag); return PM3_SUCCESS; } diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 5033b5d54..e181f8667 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -2217,7 +2217,7 @@ static int CmdT55xxDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf t55xx dump", "This command dumps a T55xx card Page 0 block 0-7.\n" - "It will create three files (bin/eml/json)", + "It will create two files (bin/json)", "lf t55xx dump\n" "lf t55xx dump -p aabbccdd --override\n" "lf t55xx dump -f my_lf_dump" @@ -2298,8 +2298,14 @@ static int CmdT55xxDump(const char *Cmd) { } } + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + // all ok, save dump to file - if (success && nosave == false) { + if (success) { // set default filename, if not set by user if (strlen(filename) == 0) { @@ -2315,17 +2321,13 @@ static int CmdT55xxDump(const char *Cmd) { } // Swap endian so the files match the txt display - uint32_t data[T55x7_BLOCK_COUNT]; + uint32_t data[T55x7_BLOCK_COUNT] = {0}; for (int i = 0; i < T55x7_BLOCK_COUNT; i++) { data[i] = BSWAP_32(cardmem[i].blockdata); } - // saveFileEML will add .eml extension to filename - // saveFile (binary) passes in the .bin extension. - saveFileJSON(filename, jsfT55x7, (uint8_t *)data, T55x7_BLOCK_COUNT * sizeof(uint32_t), NULL); - saveFileEML(filename, (uint8_t *)data, T55x7_BLOCK_COUNT * sizeof(uint32_t), sizeof(uint32_t)); - saveFile(filename, ".bin", data, sizeof(data)); + pm3_save_dump(filename, (uint8_t *)data, (T55x7_BLOCK_COUNT * sizeof(uint32_t)), jsfT55x7); } return PM3_SUCCESS; @@ -2340,7 +2342,7 @@ static int CmdT55xxRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump file"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_str0("p", "pwd", "", "password if target card has password set (4 hex bytes)"), arg_param_end }; @@ -3498,11 +3500,12 @@ out: // some return all page 1 (64 bits) and others return just that block (32 bits) // unfortunately the 64 bits makes this more likely to get a false positive... bool tryDetectP1(bool getData) { - uint8_t preamble[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; - size_t startIdx = 0; - uint8_t fc1 = 0, fc2 = 0, ans = 0; - int clk = 0, firstClockEdge = 0; - bool st = true; + uint8_t preamble_atmel[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; + uint8_t preamble_silicon[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1}; + size_t startIdx = 0; + uint8_t fc1 = 0, fc2 = 0, ans = 0; + int clk = 0, firstClockEdge = 0; + bool st = true; if (getData) { if (!AcquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, false, 0, 0)) @@ -3512,15 +3515,29 @@ bool tryDetectP1(bool getData) { // try fsk clock detect. if successful it cannot be any other type of modulation... (in theory...) ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); if (ans && ((fc1 == 10 && fc2 == 8) || (fc1 == 8 && fc2 == 5))) { - if ((FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + + if (FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((FSKrawDemod(0, 1, 0, 0, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + + if (FSKrawDemod(0, 1, 0, 0, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } return false; } @@ -3528,44 +3545,82 @@ bool tryDetectP1(bool getData) { // try ask clock detect. it could be another type even if successful. clk = GetAskClock("", false); if (clk > 0) { - if ((ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) { + + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } st = true; - if ((ASKDemod_ext(0, 1, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKDemod_ext(0, 1, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } } // try NRZ clock detect. it could be another type even if successful. clk = GetNrzClock("", false); //has the most false positives :( if (clk > 0) { - if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((NRZrawDemod(0, 1, 1, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + + if (NRZrawDemod(0, 1, 1, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } } @@ -3577,33 +3632,54 @@ bool tryDetectP1(bool getData) { // save_restoreGB(GRAPH_SAVE); // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) //CmdLtrim("-i 160"); - if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + if (PSKDemod(0, 0, 6, false) == PM3_SUCCESS) { //save_restoreGB(GRAPH_RESTORE); - return true; + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((PSKDemod(0, 1, 6, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + + if (PSKDemod(0, 1, 6, false) == PM3_SUCCESS) { //save_restoreGB(GRAPH_RESTORE); - return true; + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } + // PSK2 - needs a call to psk1TOpsk2. if (PSKDemod(0, 0, 6, false) == PM3_SUCCESS) { psk1TOpsk2(g_DemodBuffer, g_DemodBufferLen); - if (preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && + + //save_restoreGB(GRAPH_RESTORE); + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - //save_restoreGB(GRAPH_RESTORE); return true; } } // inverse waves does not affect PSK2 demod //undo trim samples //save_restoreGB(GRAPH_RESTORE); // no other modulation clocks = 2 or 4 so quit searching - if (fc1 != 8) return false; + if (fc1 != 8) { + return false; + } } - return false; } // does this need to be a callable command? diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index 431464076..06ddf0f10 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -113,26 +113,48 @@ static int CmdNfcDecode(const char *Cmd) { return res; } - // convert from MFC dump file to a pure NDEF byte array - if (HasMADKey(dump)) { - PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); - uint8_t ndef[4096] = {0}; - uint16_t ndeflen = 0; + uint8_t *tmp = dump; - if (convert_mad_to_arr(dump, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Failed converting, aborting..."); - free(dump); - return PM3_ESOFT; + // if not MIFARE Classic default sizes, assume its Ultralight/NTAG + if (bytes_read != MIFARE_4K_MAX_BYTES + && bytes_read != MIFARE_2K_MAX_BYTES + && bytes_read != MIFARE_1K_MAX_BYTES + && bytes_read != MIFARE_MINI_MAX_BYTES) { + + uint8_t **pd = &tmp; + mfu_df_e df = detect_mfu_dump_format(pd, verbose); + if (df == MFU_DF_OLDBIN) { + tmp += OLD_MFU_DUMP_PREFIX_LENGTH + (4 * 4); + bytes_read -= OLD_MFU_DUMP_PREFIX_LENGTH + (4 * 4); + } else if (df == MFU_DF_NEWBIN) { + tmp += MFU_DUMP_PREFIX_LENGTH + (4 * 4); + bytes_read -= MFU_DUMP_PREFIX_LENGTH + (4 * 4); } + pd = NULL; - memcpy(dump, ndef, ndeflen); - bytes_read = ndeflen; + } else { + + // convert from MFC dump file to a pure NDEF byte array + if (HasMADKey(tmp)) { + PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); + uint8_t ndef[4096] = {0}; + uint16_t ndeflen = 0; + + if (convert_mad_to_arr(tmp, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed converting, aborting..."); + free(dump); + return PM3_ESOFT; + } + + memcpy(tmp, ndef, ndeflen); + bytes_read = ndeflen; + } } - res = NDEFDecodeAndPrint(dump, bytes_read, verbose); + res = NDEFDecodeAndPrint(tmp, bytes_read, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); - res = NDEFRecordsDecodeAndPrint(dump, bytes_read, verbose); + res = NDEFRecordsDecodeAndPrint(tmp, bytes_read, verbose); } free(dump); diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 0abab9b01..91c1ce50f 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -22,6 +22,7 @@ #ifdef HAVE_PYTHON //#define PY_SSIZE_T_CLEAN #include +#include #include #endif diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 072178ea9..c030746f8 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -179,7 +179,7 @@ static void PrintATR(uint8_t *atr, size_t atrlen) { if (T0 & 0x80) { uint8_t TD1 = atr[2 + T1len]; - PrintAndLogEx(INFO, " - TD1 (First offered transmission protocol, presence of TA2..TD2) [ 0x%02x ] Protocol T%d", TD1, TD1 & 0x0f); + PrintAndLogEx(INFO, " - TD1 (First offered transmission protocol, presence of TA2..TD2) [ 0x%02x ] Protocol " _GREEN_("T%d"), TD1, TD1 & 0x0f); protocol_T0_present = false; if ((TD1 & 0x0f) == 0) { protocol_T0_present = true; @@ -204,7 +204,7 @@ static void PrintATR(uint8_t *atr, size_t atrlen) { } if (TD1 & 0x80) { uint8_t TDi = atr[2 + T1len + TD1len]; - PrintAndLogEx(INFO, " - TD2 (A supported protocol or more global parameters, presence of TA3..TD3) [ 0x%02x ] Protocol T%d", TDi, TDi & 0x0f); + PrintAndLogEx(INFO, " - TD2 (A supported protocol or more global parameters, presence of TA3..TD3) [ 0x%02x ] Protocol " _GREEN_("T%d"), TDi, TDi & 0x0f); if ((TDi & 0x0f) == 0) { protocol_T0_present = true; } @@ -333,7 +333,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { needGetData = true; } - if (needGetData == true) { + if (needGetData) { // Don't discard data we already received except the SW code. // If we only received 1 byte, this is the echo of INS, we discard it. totallen -= 2; @@ -360,6 +360,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { 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); + payload->wait_delay = 0; memcpy(payload->data, cmd_getresp, sizeof(cmd_getresp)); clearCommandBuffer(); @@ -421,6 +422,7 @@ static int CmdSmartRaw(const char *Cmd) { arg_lit0("s", NULL, "active smartcard with select (get ATR)"), arg_lit0("t", "tlv", "executes TLV decoder if it possible"), arg_lit0("0", NULL, "use protocol T=0"), + arg_int0(NULL, "timeout", "", "Timeout in MS waiting for SIM to respond. (def 337ms)"), arg_str1("d", "data", "", "bytes to send"), arg_param_end }; @@ -431,10 +433,11 @@ static int CmdSmartRaw(const char *Cmd) { bool active_select = arg_get_lit(ctx, 3); bool decode_tlv = arg_get_lit(ctx, 4); bool use_t0 = arg_get_lit(ctx, 5); + int timeout = arg_get_int_def(ctx, 6, -1); int dlen = 0; uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - int res = CLIParamHexToBuf(arg_get_str(ctx, 6), data, sizeof(data), &dlen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 7), data, sizeof(data), &dlen); CLIParserFree(ctx); if (res) { @@ -458,6 +461,13 @@ static int CmdSmartRaw(const char *Cmd) { payload->flags |= SC_SELECT; } + payload->wait_delay = 0; + if (timeout > -1) { + payload->flags |= SC_WAIT; + payload->wait_delay = timeout; + } + PrintAndLogEx(DEBUG, "SIM Card timeout... %u ms", payload->wait_delay); + if (dlen > 0) { if (use_t0) payload->flags |= SC_RAW_T0; @@ -500,9 +510,9 @@ static int CmdSmartRaw(const char *Cmd) { data[4] = 0; } - if (decode_tlv && len > 4) + if (decode_tlv && len > 4) { TLVPrintFromBuffer(buf, len - 2); - else { + } else { if (len > 2) { PrintAndLogEx(INFO, "Response data:"); PrintAndLogEx(INFO, " # | bytes | ascii"); @@ -518,16 +528,16 @@ out: } static int CmdSmartUpgrade(const char *Cmd) { - PrintAndLogEx(INFO, "-------------------------------------------------------------------"); + PrintAndLogEx(INFO, "--------------------------------------------------------------------"); PrintAndLogEx(WARNING, _RED_("WARNING") " - sim module firmware upgrade"); PrintAndLogEx(WARNING, _RED_("A dangerous command, do wrong and you could brick the sim module")); - PrintAndLogEx(INFO, "-------------------------------------------------------------------"); + PrintAndLogEx(INFO, "--------------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); CLIParserContext *ctx; CLIParserInit(&ctx, "smart upgrade", "Upgrade RDV4 sim module firmware", - "smart upgrade -f sim013.bin" + "smart upgrade -f sim014.bin" ); void *argtable[] = { @@ -585,9 +595,10 @@ static int CmdSmartUpgrade(const char *Cmd) { } hashstring[128] = '\0'; + int hash1n = 0; uint8_t hash_1[64]; - if (param_gethex(hashstring, 0, hash_1, 128)) { - PrintAndLogEx(FAILED, "Couldn't read SHA-512 file"); + if (param_gethex_ex(hashstring, 0, hash_1, &hash1n) && hash1n != 128) { + PrintAndLogEx(FAILED, "Couldn't read SHA-512 file. expect 128 hex bytes, got ( "_RED_("%d") " )", hash1n); free(hashstring); free(firmware); return PM3_ESOFT; @@ -903,6 +914,7 @@ static void smart_brute_prim(void) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + 5); payload->flags = SC_RAW_T0; payload->len = 5; + payload->wait_delay = 0; memcpy(payload->data, get_card_data + i, 5); clearCommandBuffer(); @@ -946,6 +958,7 @@ static int smart_brute_sfi(bool decodeTLV) { 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); + payload->wait_delay = 0; memcpy(payload->data, READ_RECORD, sizeof(READ_RECORD)); clearCommandBuffer(); @@ -1099,7 +1112,7 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + hexlen); payload->flags = SC_RAW_T0; payload->len = hexlen; - + payload->wait_delay = 0; memcpy(payload->data, cmddata, hexlen); clearCommandBuffer(); SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + hexlen); @@ -1335,6 +1348,7 @@ int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCa payload->flags |= (SC_SELECT | SC_CONNECT); } payload->len = datainlen; + payload->wait_delay = 0; memcpy(payload->data, datain, datainlen); clearCommandBuffer(); @@ -1366,8 +1380,9 @@ int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCa } bool smart_select(bool verbose, smart_card_atr_t *atr) { - if (atr) + if (atr) { memset(atr, 0, sizeof(smart_card_atr_t)); + } clearCommandBuffer(); SendCommandNG(CMD_SMART_ATR, NULL, 0); @@ -1385,8 +1400,9 @@ bool smart_select(bool verbose, smart_card_atr_t *atr) { smart_card_atr_t card; memcpy(&card, (smart_card_atr_t *)resp.data.asBytes, sizeof(smart_card_atr_t)); - if (atr) + if (atr) { memcpy(atr, &card, sizeof(smart_card_atr_t)); + } if (verbose) PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); diff --git a/client/src/comms.c b/client/src/comms.c index a7ed0fd06..9460c7024 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -666,19 +666,28 @@ int TestProxmark(pm3_device_t *dev) { g_conn.send_via_fpc_usart = g_pm3_capabilities.via_fpc; g_conn.uart_speed = g_pm3_capabilities.baudrate; - bool is_tcp_conn = (memcmp(g_conn.serial_port_name, "tcp:", 4) == 0); + bool is_tcp_conn = (g_conn.send_via_ip == PM3_TCPv4 || g_conn.send_via_ip == PM3_TCPv6); bool is_bt_conn = (memcmp(g_conn.serial_port_name, "bt:", 3) == 0); + bool is_udp_conn = (g_conn.send_via_ip == PM3_UDPv4 || g_conn.send_via_ip == PM3_UDPv6); - PrintAndLogEx(INFO, "Communicating with PM3 over %s%s%s", + PrintAndLogEx(INFO, "Communicating with PM3 over %s%s%s%s", (g_conn.send_via_fpc_usart) ? _YELLOW_("FPC UART") : _YELLOW_("USB-CDC"), (is_tcp_conn) ? " over " _YELLOW_("TCP") : "", - (is_bt_conn) ? " over " _YELLOW_("BT") : "" + (is_bt_conn) ? " over " _YELLOW_("BT") : "", + (is_udp_conn) ? " over " _YELLOW_("UDP") : "" ); - if (g_conn.send_via_fpc_usart) { PrintAndLogEx(INFO, "PM3 UART serial baudrate: " _YELLOW_("%u") "\n", g_conn.uart_speed); } else { - int res = uart_reconfigure_timeouts(is_tcp_conn ? UART_TCP_CLIENT_RX_TIMEOUT_MS : UART_USB_CLIENT_RX_TIMEOUT_MS); + int res; + if (g_conn.send_via_local_ip) { + // (g_conn.send_via_local_ip == true) -> ((is_tcp_conn || is_udp_conn) == true) + res = uart_reconfigure_timeouts(is_tcp_conn ? UART_TCP_LOCAL_CLIENT_RX_TIMEOUT_MS : UART_UDP_LOCAL_CLIENT_RX_TIMEOUT_MS); + } else if (is_tcp_conn || is_udp_conn) { + res = uart_reconfigure_timeouts(UART_NET_CLIENT_RX_TIMEOUT_MS); + } else { + res = uart_reconfigure_timeouts(UART_USB_CLIENT_RX_TIMEOUT_MS); + } if (res != PM3_SUCCESS) { return res; } @@ -789,7 +798,7 @@ bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms show_warning = false; } // just to avoid CPU busy loop: - msleep(10); + msleep(1); } return false; } diff --git a/client/src/comms.h b/client/src/comms.h index 0c1329cda..333b2bf1c 100644 --- a/client/src/comms.h +++ b/client/src/comms.h @@ -54,6 +54,14 @@ typedef enum { FPGA_MEM, } DeviceMemType_t; +typedef enum { + PM3_TCPv4, + PM3_TCPv6, + PM3_UDPv4, + PM3_UDPv6, + PM3_NONE, +} CommunicationProtocol_t; + typedef struct { bool run; // If TRUE, continue running the uart_communication thread bool block_after_ACK; // if true, block after receiving an ACK package @@ -62,6 +70,10 @@ typedef struct { bool send_with_crc_on_fpc; // "Session" flag, to tell via which interface next msgs are sent: USB or FPC USART bool send_via_fpc_usart; + // to tell if we are using TCP/UDP/TCP(IPv6)/UDP(IPv6) + CommunicationProtocol_t send_via_ip; + // to tell if the target address is local address(127.0.0.1/localhost/::1) + bool send_via_local_ip; // To memorise baudrate uint32_t uart_speed; uint16_t last_command; diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index 025e200ca..32dc8f051 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -15,7 +15,7 @@ //----------------------------------------------------------------------------- // asn.1 dumping //----------------------------------------------------------------------------- -#define _POSIX_C_SOURCE 200809L // need for strnlen() + #include "asn1dump.h" #include "commonutil.h" // ARRAYLEN @@ -344,17 +344,17 @@ static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag } else { const char *ppstr = NULL; mbedtls_oid_get_attr_short_name(&asn1_buf, &ppstr); - if (ppstr && strnlen(ppstr, 1)) { + if (ppstr && str_nlen(ppstr, 1)) { PrintAndLogEx(NORMAL, " (%s)", ppstr); return; } mbedtls_oid_get_sig_alg_desc(&asn1_buf, &ppstr); - if (ppstr && strnlen(ppstr, 1)) { + if (ppstr && str_nlen(ppstr, 1)) { PrintAndLogEx(NORMAL, " (%s)", ppstr); return; } mbedtls_oid_get_extended_key_usage(&asn1_buf, &ppstr); - if (ppstr && strnlen(ppstr, 1)) { + if (ppstr && str_nlen(ppstr, 1)) { PrintAndLogEx(NORMAL, " (%s)", ppstr); return; } diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 4e119f245..3634e9e4d 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -629,8 +629,10 @@ int blowfish_decrypt(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, mbedtls_blowfish_init(&blow); if (mbedtls_blowfish_setkey(&blow, key, 64)) return 1; + if (mbedtls_blowfish_crypt_cbc(&blow, MBEDTLS_BLOWFISH_DECRYPT, length, iiv, input, output)) return 2; + mbedtls_blowfish_free(&blow); return 0; @@ -647,7 +649,7 @@ int ansi_x963_sha256(uint8_t *sharedSecret, size_t sharedSecretLen, uint8_t *sha uint32_t counter = 0x00000001; for (int i = 0; i < (keyDataLen / 32); ++i) { - uint8_t *hashMaterial = malloc(4 + sharedSecretLen + sharedInfoLen); + uint8_t *hashMaterial = calloc(4 + sharedSecretLen + sharedInfoLen, sizeof(uint8_t)); memcpy(hashMaterial, sharedSecret, sharedSecretLen); hashMaterial[sharedSecretLen] = (counter >> 24); hashMaterial[sharedSecretLen + 1] = (counter >> 16) & 0xFF; diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 5462f6eed..47cd6de55 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -44,28 +44,30 @@ static int CmdHelp(const char *Cmd); #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) static void ParamLoadDefaults(struct tlvdb *tlvRoot) { - //9F02:(Amount, authorized (Numeric)) len:6 + // 9F02:(Amount, authorized (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); - //9F1A:(Terminal Country Code) len:2 + // 9F1A:(Terminal Country Code) len:2 TLV_ADD(0x9F1A, "ru"); - //5F2A:(Transaction Currency Code) len:2 + // 5F2A:(Transaction Currency Code) len:2 // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 + TLV_ADD(0x5F2A, "\x090\x78"); + // 9A:(Transaction Date) len:3 TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + // 9C:(Transaction Type) len:1 + // | 00 => Goods and Service + // | 01 => Cash TLV_ADD(0x9C, "\x00"); - // 9F37 Unpredictable Number len:4 + // 9F37 Unpredictable Number (UN) len:4 TLV_ADD(0x9F37, "\x01\x02\x03\x04"); // 9F6A Unpredictable Number (MSD for UDOL) len:4 TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + // 9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC - //95:(Terminal Verification Results) len:5 + // 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"); + TLV_ADD(0x9F4E, "proxmark3rdv4\x00"); } static void PrintChannel(Iso7816CommandChannel channel) { @@ -298,14 +300,14 @@ static int emv_parse_track1(const uint8_t *d, size_t n, bool verbose) { case 0: { size_t a = strlen(token); if (a == 16) { - PrintAndLogEx(INFO, "PAN...................... %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c", + PrintAndLogEx(INFO, "PAN...................... " _GREEN_("%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c"), token[1], token[2], token[3], token[4], token[5], token[6], token[7], token[8], token[9], token[10], token[11], token[12], token[13], token[14], token[15], token[16] ); } else if (a == 19) { - PrintAndLogEx(INFO, "PAN...................... %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c", + PrintAndLogEx(INFO, "PAN...................... " _GREEN_("%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c"), token[1], token[2], token[3], token[4], token[5], token[6], token[7], token[8], token[9], token[10], token[11], token[12], @@ -362,7 +364,7 @@ static int emv_parse_track2(const uint8_t *d, size_t n, bool verbose) { if (tmp[0] == ';') tmp++; - PrintAndLogEx(INFO, "PAN...................... %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c", + PrintAndLogEx(INFO, "PAN...................... "_GREEN_("%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c"), tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], @@ -479,7 +481,7 @@ static int emv_parse_card_details(uint8_t *response, size_t reslen, bool verbose if (apan_full != NULL) { const struct tlv *apan_tlv = tlvdb_get_tlv(apan_full); if (apan_tlv->len == 8) { - PrintAndLogEx(INFO, "PAN.................. " _YELLOW_("%02x%02x %02x%02x %02x%02x %02x%02x"), + PrintAndLogEx(INFO, "PAN.................. " _GREEN_("%02x%02x %02x%02x %02x%02x %02x%02x"), apan_tlv->value[0], apan_tlv->value[1], apan_tlv->value[2], @@ -543,6 +545,33 @@ static int emv_parse_card_details(uint8_t *response, size_t reslen, bool verbose // Track 3 Data // to be impl. + // Unpredicable Number (UN) + struct tlvdb *un1_full = tlvdb_find_full(root, 0x9f37); + if (un1_full != NULL) { + const struct tlv *un1_tlv = tlvdb_get_tlv(un1_full); + if (un1_tlv->len) { + PrintAndLogEx(INFO, "9F37 Unpredicable Number... " _YELLOW_("%s"), sprint_hex_inrow(un1_tlv->value, un1_tlv->len)); + } + } + + // Unpredicable Number (UN) + struct tlvdb *un_full = tlvdb_find_full(root, 0x9f6a); + if (un_full != NULL) { + const struct tlv *un_tlv = tlvdb_get_tlv(un_full); + if (un_tlv->len) { + PrintAndLogEx(INFO, "9F6A Unpredicable Number... " _YELLOW_("%s"), sprint_hex_inrow(un_tlv->value, un_tlv->len)); + emv_parse_track2(un_tlv->value, un_tlv->len, verbose); + } + } + + struct tlvdb *merch_full = tlvdb_find_full(root, 0x9f4e); + if (merch_full != NULL) { + const struct tlv *merch_tlv = tlvdb_get_tlv(merch_full); + if (merch_tlv->len) { + PrintAndLogEx(INFO, "Merchant Name and Location... " _YELLOW_("%s"), sprint_hex_inrow(merch_tlv->value, merch_tlv->len)); + } + } + tlvdb_free(root); return PM3_SUCCESS; } @@ -571,7 +600,7 @@ static int CmdEMVSelect(const char *Cmd) { bool activateField = arg_get_lit(ctx, 1); bool leaveSignalON = arg_get_lit(ctx, 2); - bool APDULogging = arg_get_lit(ctx, 3); + bool show_apdu = arg_get_lit(ctx, 3); bool decodeTLV = arg_get_lit(ctx, 4); Iso7816CommandChannel channel = CC_CONTACTLESS; if (arg_get_lit(ctx, 5)) @@ -580,7 +609,7 @@ static int CmdEMVSelect(const char *Cmd) { CLIGetHexWithReturn(ctx, 6, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -597,6 +626,7 @@ static int CmdEMVSelect(const char *Cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -621,7 +651,7 @@ static int CmdEMVSearch(const char *Cmd) { bool activateField = arg_get_lit(ctx, 1); bool leaveSignalON = arg_get_lit(ctx, 2); - bool APDULogging = arg_get_lit(ctx, 3); + bool show_apdu = arg_get_lit(ctx, 3); bool decodeTLV = arg_get_lit(ctx, 4); Iso7816CommandChannel channel = CC_CONTACTLESS; @@ -632,13 +662,14 @@ static int CmdEMVSearch(const char *Cmd) { PrintChannel(channel); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); const char *al = "Applets list"; struct tlvdb *t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t, false)) { tlvdb_free(t); + SetAPDULogging(false); return PM3_ERFTRANS; } @@ -651,6 +682,7 @@ static int CmdEMVSearch(const char *Cmd) { tlvdb_free(t); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -678,19 +710,23 @@ static int CmdEMVPPSE(const char *Cmd) { bool activateField = arg_get_lit(ctx, 1); bool leaveSignalON = arg_get_lit(ctx, 2); uint8_t PSENum = 2; - if (arg_get_lit(ctx, 3)) + if (arg_get_lit(ctx, 3)) { PSENum = 1; - if (arg_get_lit(ctx, 4)) + } + if (arg_get_lit(ctx, 4)) { PSENum = 2; - bool APDULogging = arg_get_lit(ctx, 5); + } + bool show_apdu = arg_get_lit(ctx, 5); bool decodeTLV = arg_get_lit(ctx, 6); + Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 7)) + if (arg_get_lit(ctx, 7)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -707,6 +743,7 @@ static int CmdEMVPPSE(const char *Cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -738,16 +775,17 @@ static int CmdEMVGPO(const char *Cmd) { bool leaveSignalON = arg_get_lit(ctx, 1); bool paramsLoadFromFile = arg_get_lit(ctx, 2); bool dataMakeFromPDOL = arg_get_lit(ctx, 3); - bool APDULogging = arg_get_lit(ctx, 4); + bool show_apdu = arg_get_lit(ctx, 4); bool decodeTLV = arg_get_lit(ctx, 5); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 6)) + if (arg_get_lit(ctx, 6)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 7, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // Init TLV tree const char *alr = "Root terminal TLV tree"; @@ -775,6 +813,7 @@ static int CmdEMVGPO(const char *Cmd) { PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); + SetAPDULogging(false); return PM3_ESOFT; } } else { @@ -790,8 +829,10 @@ static int CmdEMVGPO(const char *Cmd) { PrintAndLogEx(ERR, "Can't create PDOL data."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); - if (pdol_data_tlv != &data_tlv) + if (pdol_data_tlv != &data_tlv) { free(pdol_data_tlv); + } + SetAPDULogging(false); return PM3_ESOFT; } PrintAndLogEx(INFO, "PDOL data[%zu]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); @@ -817,6 +858,7 @@ static int CmdEMVGPO(const char *Cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -843,11 +885,12 @@ static int CmdEMVReadRecord(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool leaveSignalON = arg_get_lit(ctx, 1); - bool APDULogging = arg_get_lit(ctx, 2); + bool show_apdu = arg_get_lit(ctx, 2); bool decodeTLV = arg_get_lit(ctx, 3); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 4)) + if (arg_get_lit(ctx, 4)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 5, data, &datalen); CLIParserFree(ctx); @@ -857,13 +900,14 @@ static int CmdEMVReadRecord(const char *Cmd) { return PM3_EINVARG; } - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; int res = EMVReadRecord(channel, leaveSignalON, data[0], data[1], buf, sizeof(buf), &len, &sw, NULL); + SetAPDULogging(false); if (sw) PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); @@ -910,14 +954,17 @@ static int CmdEMVAC(const char *Cmd) { bool trTypeCDA = arg_get_lit(ctx, 2); uint8_t termDecision = 0xff; if (arg_get_str_len(ctx, 3)) { - if (!strncmp(arg_get_str(ctx, 3)->sval[0], "aac", 4)) + if (!strncmp(arg_get_str(ctx, 3)->sval[0], "aac", 4)) { termDecision = EMVAC_AAC; - if (!strncmp(arg_get_str(ctx, 3)->sval[0], "tc", 4)) + } + if (!strncmp(arg_get_str(ctx, 3)->sval[0], "tc", 4)) { termDecision = EMVAC_TC; - if (!strncmp(arg_get_str(ctx, 3)->sval[0], "arqc", 4)) + } + if (!strncmp(arg_get_str(ctx, 3)->sval[0], "arqc", 4)) { termDecision = EMVAC_ARQC; + } - if (termDecision == 0xff) { + if (termDecision == 0xFF) { PrintAndLogEx(ERR, "ERROR: can't find terminal decision '%s'", arg_get_str(ctx, 3)->sval[0]); CLIParserFree(ctx); return PM3_EINVARG; @@ -925,22 +972,25 @@ static int CmdEMVAC(const char *Cmd) { } else { termDecision = EMVAC_TC; } - if (trTypeCDA) + + if (trTypeCDA) { termDecision = termDecision | EMVAC_CDAREQ; + } bool paramsLoadFromFile = arg_get_lit(ctx, 4); bool dataMakeFromCDOL = arg_get_lit(ctx, 5); - bool APDULogging = arg_get_lit(ctx, 6); + bool show_apdu = arg_get_lit(ctx, 6); bool decodeTLV = arg_get_lit(ctx, 7); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 8)) + if (arg_get_lit(ctx, 8)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 9, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // Init TLV tree const char *alr = "Root terminal TLV tree"; @@ -969,6 +1019,7 @@ static int CmdEMVAC(const char *Cmd) { PrintAndLogEx(ERR, "Can't create CDOL TLV."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); + SetAPDULogging(false); return PM3_ESOFT; } } else { @@ -985,6 +1036,7 @@ static int CmdEMVAC(const char *Cmd) { size_t len = 0; uint16_t sw = 0; int res = EMVAC(channel, leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + SetAPDULogging(false); if (cdol_data_tlv != &data_tlv) free(cdol_data_tlv); @@ -1023,20 +1075,22 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool leaveSignalON = arg_get_lit(ctx, 1); - bool APDULogging = arg_get_lit(ctx, 2); + bool show_apdu = arg_get_lit(ctx, 2); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 3)) + if (arg_get_lit(ctx, 3)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; int res = EMVGenerateChallenge(channel, leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + SetAPDULogging(false); if (sw) PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); @@ -1046,8 +1100,9 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { PrintAndLogEx(SUCCESS, "Challenge: %s", sprint_hex(buf, len)); - if (len != 4 && len != 8) - PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, but it %zu", len); + if (len != 4 && len != 8) { + PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, got " _YELLOW_("%zu"), len); + } return PM3_SUCCESS; } @@ -1081,16 +1136,17 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { bool leaveSignalON = arg_get_lit(ctx, 1); bool paramsLoadFromFile = arg_get_lit(ctx, 2); bool dataMakeFromDDOL = arg_get_lit(ctx, 3); - bool APDULogging = arg_get_lit(ctx, 4); + bool show_apdu = arg_get_lit(ctx, 4); bool decodeTLV = arg_get_lit(ctx, 5); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 6)) + if (arg_get_lit(ctx, 6)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 7, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // Init TLV tree const char *alr = "Root terminal TLV tree"; @@ -1119,6 +1175,7 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { PrintAndLogEx(ERR, "Can't create DDOL TLV."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); + SetAPDULogging(false); return PM3_ESOFT; } } else { @@ -1135,6 +1192,7 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { size_t len = 0; uint16_t sw = 0; int res = EMVInternalAuthenticate(channel, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + SetAPDULogging(false); if (ddol_data_tlv != &data_tlv) free(ddol_data_tlv); @@ -1154,7 +1212,14 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { return PM3_SUCCESS; } -#define dreturn(n) {free(pdol_data_tlv); tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); DropFieldEx( channel ); return n;} +#define dreturn(n) { \ + free(pdol_data_tlv); \ + tlvdb_free(tlvSelect); \ + tlvdb_free(tlvRoot); \ + DropFieldEx( channel ); \ + SetAPDULogging(false); \ + return n; \ + } static void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) { @@ -1297,21 +1362,24 @@ static int CmdEMVExec(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool activateField = arg_get_lit(ctx, 1); - bool showAPDU = arg_get_lit(ctx, 2); + bool show_apdu = arg_get_lit(ctx, 2); bool decodeTLV = arg_get_lit(ctx, 3); bool paramLoadJSON = arg_get_lit(ctx, 4); bool forceSearch = arg_get_lit(ctx, 5); enum TransactionType TrType = TT_MSD; - if (arg_get_lit(ctx, 7)) + if (arg_get_lit(ctx, 7)) { TrType = TT_QVSDCMCHIP; + } - if (arg_get_lit(ctx, 8)) + if (arg_get_lit(ctx, 8)) { TrType = TT_CDA; + } - if (arg_get_lit(ctx, 9)) + if (arg_get_lit(ctx, 9)) { TrType = TT_VSDC; + } bool GenACGPO = arg_get_lit(ctx, 10); @@ -1324,14 +1392,14 @@ static int CmdEMVExec(const char *Cmd) { uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; CLIParserFree(ctx); - if (!IfPm3Smartcard()) { + if (IfPm3Smartcard() == false) { if (channel == CC_CONTACT) { PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support. Exiting."); return PM3_EDEVNOTSUPP; } } - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -1357,7 +1425,7 @@ static int CmdEMVExec(const char *Cmd) { // PPSE PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "* PPSE."); - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); res = EMVSearchPSE(channel, activateField, true, psenum, decodeTLV, tlvSelect); // check PPSE instead of PSE and vice versa @@ -1398,7 +1466,7 @@ static int CmdEMVExec(const char *Cmd) { // Select PrintAndLogEx(INFO, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -1406,8 +1474,9 @@ static int CmdEMVExec(const char *Cmd) { dreturn(PM3_ERFTRANS); } - if (decodeTLV) + if (decodeTLV) { TLVPrintFromBuffer(buf, len); + } PrintAndLogEx(INFO, "* Selected."); PrintAndLogEx(INFO, "\n* Init transaction parameters."); @@ -1462,8 +1531,9 @@ static int CmdEMVExec(const char *Cmd) { PrintAndLogEx(INFO, "\n* Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); - if (!AFL || !AFL->len) + if (!AFL || !AFL->len) { PrintAndLogEx(WARNING, "WARNING: AFL not found."); + } while (AFL && AFL->len) { if (AFL->len % 4) { @@ -1584,19 +1654,24 @@ static int CmdEMVExec(const char *Cmd) { TLVPrintFromTLVLev(cvr, 1); PrintAndLogEx(INFO, " IDD option id: 0x%02x", IAD->value[8]); PrintAndLogEx(INFO, " IDD: %s", sprint_hex(&IAD->value[9], 23)); + } else if (IAD->len >= IAD->value[0] + 1) { PrintAndLogEx(INFO, " Key index: 0x%02x", IAD->value[1]); PrintAndLogEx(INFO, " Crypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); PrintAndLogEx(INFO, " 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) + if (iddLen >= 1) { PrintAndLogEx(NORMAL, " IDD option id: 0x%02x", IAD->value[8]); - if (iddLen >= 2) + } + + if (iddLen >= 2) { PrintAndLogEx(NORMAL, " IDD: %s", sprint_hex(&IAD->value[9], iddLen - 1)); + } } } } else { @@ -1611,6 +1686,7 @@ static int CmdEMVExec(const char *Cmd) { // Mastercard M/CHIP if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)) { + const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag PrintAndLogEx(INFO, "\n--> Mastercard M/Chip transaction."); @@ -1621,6 +1697,7 @@ static int CmdEMVExec(const char *Cmd) { PrintAndLogEx(ERR, "Error GetChallenge. APDU error %4x", sw); dreturn(PM3_ERFTRANS); } + if (len < 4) { PrintAndLogEx(ERR, "Error GetChallenge. Wrong challenge length %zu", len); dreturn(PM3_ESOFT); @@ -1651,8 +1728,9 @@ static int CmdEMVExec(const char *Cmd) { dreturn(PM3_ERFTRANS); } - if (decodeTLV) + if (decodeTLV) { TLVPrintFromBuffer(buf, len); + } // CDA PrintAndLogEx(INFO, "\n* CDA:"); @@ -1675,7 +1753,8 @@ static int CmdEMVExec(const char *Cmd) { if (CID) { emv_tag_dump(CID, 1); PrintAndLogEx(INFO, "------------------------------"); - if (CID->len > 0) { + + if (CID->len) { switch (CID->value[0] & EMVAC_AC_MASK) { case EMVAC_AAC: PrintAndLogEx(INFO, "Transaction DECLINED."); @@ -1725,8 +1804,9 @@ static int CmdEMVExec(const char *Cmd) { .len = 3, .value = (uint8_t *)"\x9f\x6a\x04", }; - if (!UDOL) + if (!UDOL) { PrintAndLogEx(INFO, "Use default UDOL."); + } struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv) { @@ -1801,6 +1881,7 @@ static int CmdEMVExec(const char *Cmd) { uint8_t IDDlen = 0; // Issuer discretionary data length PrintAndLogEx(INFO, "IAD length: %zu", IAD->len); PrintAndLogEx(INFO, "VDDlen: %d", VDDlen); + if (VDDlen < IAD->len - 1) { IDDlen = IAD->value[VDDlen + 1]; } @@ -1822,9 +1903,11 @@ static int CmdEMVExec(const char *Cmd) { PrintAndLogEx(WARNING, "Wrong CVR length! CVR: %s", sprint_hex(&IAD->value[3], VDDlen - 2)); } } + if (IDDlen) { PrintAndLogEx(INFO, "IDD: %s", sprint_hex(&IAD->value[VDDlen + 1], IDDlen)); } + } else { PrintAndLogEx(WARNING, "Issuer Application Data (IAD) not found."); } @@ -1898,6 +1981,7 @@ static int CmdEMVExec(const char *Cmd) { tlvdb_free(tlvRoot); PrintAndLogEx(SUCCESS, "\n* Transaction completed."); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -1928,25 +2012,29 @@ static int CmdEMVScan(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool showAPDU = arg_get_lit(ctx, 1); + bool show_apdu = arg_get_lit(ctx, 1); bool decodeTLV = arg_get_lit(ctx, 2); bool extractTLVElements = arg_get_lit(ctx, 3); bool paramLoadJSON = arg_get_lit(ctx, 4); enum TransactionType TrType = TT_MSD; - if (arg_get_lit(ctx, 6)) + if (arg_get_lit(ctx, 6)) { TrType = TT_QVSDCMCHIP; - if (arg_get_lit(ctx, 7)) + } + if (arg_get_lit(ctx, 7)) { TrType = TT_CDA; - if (arg_get_lit(ctx, 8)) + } + if (arg_get_lit(ctx, 8)) { TrType = TT_VSDC; + } bool GenACGPO = arg_get_lit(ctx, 9); bool MergeJSON = arg_get_lit(ctx, 10); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 11)) + if (arg_get_lit(ctx, 11)) { channel = CC_CONTACT; + } PrintChannel(channel); @@ -1958,15 +2046,13 @@ static int CmdEMVScan(const char *Cmd) { CLIParserFree(ctx); - if (!IfPm3Smartcard()) { + if (IfPm3Smartcard() == false) { if (channel == CC_CONTACT) { PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support, exiting"); return PM3_EDEVNOTSUPP; } } - SetAPDULogging(showAPDU); - uint8_t AID[APDU_AID_LEN] = {0}; size_t AIDlen = 0; uint8_t buf[APDU_RES_LEN] = {0}; @@ -1996,6 +2082,8 @@ static int CmdEMVScan(const char *Cmd) { root = json_object(); } + SetAPDULogging(show_apdu); + // drop field at start DropFieldEx(channel); @@ -2073,7 +2161,7 @@ static int CmdEMVScan(const char *Cmd) { } // EMV SELECT application - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); EMVSelectApplication(tlvSelect, AID, &AIDlen); tlvdb_free(tlvSelect); @@ -2093,7 +2181,7 @@ static int CmdEMVScan(const char *Cmd) { // EMV SELECT applet PrintAndLogEx(INFO, "Selecting AID: " _GREEN_("%s"), sprint_hex_inrow(AID, AIDlen)); - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -2184,10 +2272,12 @@ static int CmdEMVScan(const char *Cmd) { sfijson = json_path_get(root, "$.Application.Records"); } + if (!json_is_array(sfijson)) { PrintAndLogEx(ERR, "Internal logic error. `$.Application.Records` is not an array."); break; } + for (int i = 0; i < AFL->len / 4; i++) { uint8_t SFI = AFL->value[i * 4 + 0] >> 3; uint8_t SFIstart = AFL->value[i * 4 + 1]; @@ -2243,11 +2333,11 @@ static int CmdEMVScan(const char *Cmd) { JsonSaveHex(jsonelm, "Offline", SFIoffline, 1); struct tlvdb *rsfi = tlvdb_parse_multi(buf, len); - if (extractTLVElements) + if (extractTLVElements) { JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi); - else + } else { JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false); - + } tlvdb_free(rsfi); } } @@ -2273,7 +2363,7 @@ static int CmdEMVScan(const char *Cmd) { tlvdb_free(tlvRoot); DropFieldEx(channel); - + SetAPDULogging(false); if (MergeJSON == false) { // create unique new name @@ -2356,7 +2446,7 @@ static int CmdEMVRoca(const char *Cmd) { CLIParserFree(ctx); PrintChannel(channel); - if (!IfPm3Smartcard()) { + if (IfPm3Smartcard() == false) { if (channel == CC_CONTACT) { PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support, exiting"); return PM3_EDEVNOTSUPP; @@ -2395,6 +2485,7 @@ static int CmdEMVRoca(const char *Cmd) { PrintAndLogEx(ERR, "Can't found any of EMV AID, exiting"); tlvdb_free(tlvSelect); DropFieldEx(channel); + SetAPDULogging(false); return PM3_ERFTRANS; } @@ -2590,6 +2681,7 @@ static int CmdEMVRoca(const char *Cmd) { } out: + SetAPDULogging(false); tlvdb_free(tlvRoot); DropFieldEx(channel); return ret; @@ -2685,7 +2777,6 @@ static int CmdEMVReader(const char *Cmd) { uint8_t log_file_records = 31; struct tlvdb *tlogDB = NULL; - // try getting the LOG TEMPLATE. bool log_found = false; bool log_template_found = false; @@ -2777,8 +2868,9 @@ static int CmdEMVReader(const char *Cmd) { continue; } - if (sw == 0x6A83) + if (sw == 0x6A83) { break; + } PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "Transaction log # " _YELLOW_("%u"), i); diff --git a/client/src/emv/emv_pki.c b/client/src/emv/emv_pki.c index 17eb7e0ea..8f430e290 100644 --- a/client/src/emv/emv_pki.c +++ b/client/src/emv/emv_pki.c @@ -37,7 +37,7 @@ void PKISetStrictExecution(bool se) { strictExecution = se; } -static const unsigned char empty_tlv_value[] = {}; +static const unsigned char empty_tlv_value[] = {0}; static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value}; static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, }; diff --git a/client/src/emv/emv_tags.c b/client/src/emv/emv_tags.c index 0ae242905..9536ce66a 100644 --- a/client/src/emv/emv_tags.c +++ b/client/src/emv/emv_tags.c @@ -822,9 +822,11 @@ bool emv_tag_dump(const struct tlv *tlv, int level) { emv_tag_dump_string(tlv, tag, level); break; case EMV_TAG_NUMERIC: + PrintAndLogEx(NORMAL, ""); emv_tag_dump_numeric(tlv, tag, level); break; case EMV_TAG_YYMMDD: + PrintAndLogEx(NORMAL, ""); emv_tag_dump_yymmdd(tlv, tag, level); break; case EMV_TAG_CVR: diff --git a/client/src/emv/tlv.h b/client/src/emv/tlv.h index 39f1bcacd..2582066a9 100644 --- a/client/src/emv/tlv.h +++ b/client/src/emv/tlv.h @@ -22,6 +22,7 @@ #define TLV_H #include "common.h" +#include typedef uint32_t tlv_tag_t; @@ -41,7 +42,7 @@ struct tlvdb { struct tlvdb_root { struct tlvdb db; size_t len; - unsigned char buf[0]; + unsigned char buf[]; }; typedef void (*tlv_cb)(void *data, const struct tlv *tlv, int level, bool is_leaf); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 7383c396f..d8c4afe90 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -27,9 +27,7 @@ #include "proxmark3.h" #include "util.h" #include "cmdhficlass.h" // pagemap -#include "protocols.h" // iclass defines -#include "cmdhftopaz.h" // TOPAZ defines -#include "mifare/mifaredefault.h" // MFP / AES defines +#include "iclass_cmd.h" #ifdef _WIN32 #include "scandir.h" @@ -88,6 +86,10 @@ DumpFileType_t getfiletype(const char *filename) { o = DICTIONARY; } else if (str_endswith(s, "mct")) { o = MCT; + } else if (str_endswith(s, "nfc")) { + o = FLIPPER; + } else if (str_endswith(s, "picopass")) { + o = FLIPPER; } else { // mfd, trc, trace is binary o = BIN; @@ -266,57 +268,6 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si return PM3_SUCCESS; } -// dump file -int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t blocksize) { - - if (data == NULL || datalen == 0) { - return PM3_EINVARG; - } - - char *fileName = newfilenamemcopyEx(preferredName, ".eml", spDump); - if (fileName == NULL) { - return PM3_EMALLOC; - } - - int retval = PM3_SUCCESS; - int blocks = datalen / blocksize; - uint16_t currblock = 1; - - // We should have a valid filename now, e.g. dumpdata-3.bin - - // Opening file for writing in text mode - FILE *f = fopen(fileName, "w+"); - if (!f) { - PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); - retval = PM3_EFILE; - goto out; - } - - for (size_t i = 0; i < datalen; i++) { - fprintf(f, "%02X", data[i]); - - // no extra line in the end - if ((i + 1) % blocksize == 0 && currblock != blocks) { - fprintf(f, "\n"); - currblock++; - } - } - // left overs - if (datalen % blocksize != 0) { - int index = blocks * blocksize; - for (size_t j = 0; j < datalen % blocksize; j++) { - fprintf(f, "%02X", data[index + j]); - } - } - fflush(f); - fclose(f); - PrintAndLogEx(SUCCESS, "saved " _YELLOW_("%" PRId32) " blocks to text file " _YELLOW_("%s"), blocks, fileName); - -out: - free(fileName); - return retval; -} - // dump file (normally, we also got preference file, etc) int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)) { return saveFileJSONex(preferredName, ftype, data, datalen, true, callback, spDump); @@ -329,12 +280,8 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, } } - char *fileName = newfilenamemcopyEx(preferredName, ".json", e_save_path); - if (fileName == NULL) { - return PM3_EMALLOC; - } - int retval = PM3_SUCCESS; + char path[PATH_MAX_LENGTH] = {0}; json_t *root = json_object(); JsonSaveStr(root, "Created", "proxmark3"); @@ -344,26 +291,74 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "raw", data, datalen); break; } - case jsfCardMemory: { - iso14a_mf_extdump_t *xdump = (iso14a_mf_extdump_t *)(void *) data; - JsonSaveStr(root, "FileType", "mfcard"); - JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen); - JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2); - JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1); - for (size_t i = 0; i < (xdump->dumplen / 16); i++) { - char path[PATH_MAX_LENGTH] = {0}; + case jsfMfc_v2: { + + iso14a_mf_extdump_t xdump; + memcpy(&xdump, data, sizeof(iso14a_mf_extdump_t)); + + JsonSaveStr(root, "FileType", "mfc v2"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump.card_info.uid, xdump.card_info.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump.card_info.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump.card_info.sak), 1); + for (size_t i = 0; i < (xdump.dumplen / MFBLOCK_SIZE); i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16], 16); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], MFBLOCK_SIZE); if (mfIsSectorTrailer(i)) { snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyA", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16], 6); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], 6); snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyB", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16 + 10], 6); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 10], 6); - uint8_t *adata = &xdump->dump[i * 16 + 6]; + uint8_t *adata = &xdump.dump[i * MFBLOCK_SIZE + 6]; snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditions", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16 + 6], 4); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 6], 4); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 3); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(0, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 2); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(1, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 1); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(2, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(3, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.UserData", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &adata[3], 1); + } + } + break; + } + case jsfMfc_v3: { + + iso14a_mf_dump_ev1_t xdump; + memcpy(&xdump, data, sizeof(iso14a_mf_dump_ev1_t)); + + JsonSaveStr(root, "FileType", "mfc v3"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump.card.ev1.uid, xdump.card.ev1.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump.card.ev1.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump.card.ev1.sak), 1); + JsonSaveBufAsHexCompact(root, "$.Card.ATS", xdump.card.ev1.ats, sizeof(xdump.card.ev1.ats_len)); + JsonSaveBufAsHexCompact(root, "$.Card.SIGNATURE", xdump.card.ev1.signature, sizeof(xdump.card.ev1.signature)); + + for (size_t i = 0; i < (xdump.dumplen / MFBLOCK_SIZE); i++) { + + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], MFBLOCK_SIZE); + if (mfIsSectorTrailer(i)) { + snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyA", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], 6); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyB", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 10], 6); + + uint8_t *adata = &xdump.dump[i * MFBLOCK_SIZE + 6]; + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditions", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 6], 4); snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 3); JsonSaveStr(root, path, mfGetAccessConditionsDesc(0, adata)); @@ -384,86 +379,87 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, break; } case jsfFudan: { - iso14a_mf_extdump_t *xdump = (iso14a_mf_extdump_t *)(void *) data; + iso14a_mf_extdump_t xdump; + memcpy(&xdump, data, sizeof(iso14a_mf_extdump_t)); + JsonSaveStr(root, "FileType", "fudan"); - JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen); - JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2); - JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1); - for (size_t i = 0; i < (xdump->dumplen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump.card_info.uid, xdump.card_info.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump.card_info.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump.card_info.sak), 1); + for (size_t i = 0; i < (xdump.dumplen / 4); i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 4], 4); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * 4], 4); } break; } case jsfMfuMemory: { - JsonSaveStr(root, "FileType", "mfu"); - - mfu_dump_t *tmp = (mfu_dump_t *)data; + mfu_dump_t tmp; + memcpy(&tmp, data, sizeof(mfu_dump_t)); uint8_t uid[7] = {0}; - memcpy(uid, tmp->data, 3); - memcpy(uid + 3, tmp->data + 4, 4); - - char path[PATH_MAX_LENGTH] = {0}; + memcpy(uid, tmp.data, 3); + memcpy(uid + 3, tmp.data + 4, 4); + JsonSaveStr(root, "FileType", "mfu"); JsonSaveBufAsHexCompact(root, "$.Card.UID", uid, sizeof(uid)); - JsonSaveBufAsHexCompact(root, "$.Card.Version", tmp->version, sizeof(tmp->version)); - JsonSaveBufAsHexCompact(root, "$.Card.TBO_0", tmp->tbo, sizeof(tmp->tbo)); - JsonSaveBufAsHexCompact(root, "$.Card.TBO_1", tmp->tbo1, sizeof(tmp->tbo1)); - JsonSaveBufAsHexCompact(root, "$.Card.Signature", tmp->signature, sizeof(tmp->signature)); + JsonSaveBufAsHexCompact(root, "$.Card.Version", tmp.version, sizeof(tmp.version)); + JsonSaveBufAsHexCompact(root, "$.Card.TBO_0", tmp.tbo, sizeof(tmp.tbo)); + JsonSaveBufAsHexCompact(root, "$.Card.TBO_1", tmp.tbo1, sizeof(tmp.tbo1)); + JsonSaveBufAsHexCompact(root, "$.Card.Signature", tmp.signature, sizeof(tmp.signature)); for (uint8_t i = 0; i < 3; i ++) { snprintf(path, sizeof(path), "$.Card.Counter%d", i); - JsonSaveBufAsHexCompact(root, path, tmp->counter_tearing[i], 3); + JsonSaveBufAsHexCompact(root, path, tmp.counter_tearing[i], 3); snprintf(path, sizeof(path), "$.Card.Tearing%d", i); - JsonSaveBufAsHexCompact(root, path, tmp->counter_tearing[i] + 3, 1); + JsonSaveBufAsHexCompact(root, path, tmp.counter_tearing[i] + 3, 1); } // size of header 56b - size_t len = (datalen - MFU_DUMP_PREFIX_LENGTH) / 4; + + size_t len = (datalen - MFU_DUMP_PREFIX_LENGTH) / MFU_BLOCK_SIZE; for (size_t i = 0; i < len; i++) { snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, tmp->data + (i * 4), 4); + JsonSaveBufAsHexCompact(root, path, tmp.data + (i * MFU_BLOCK_SIZE), MFU_BLOCK_SIZE); } break; } case jsfHitag: { - JsonSaveStr(root, "FileType", "hitag"); uint8_t uid[4] = {0}; memcpy(uid, data, 4); - + JsonSaveStr(root, "FileType", "hitag"); JsonSaveBufAsHexCompact(root, "$.Card.UID", uid, sizeof(uid)); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } break; } case jsfIclass: { + + picopass_hdr_t hdr; + memcpy(&hdr, data, sizeof(picopass_hdr_t)); + JsonSaveStr(root, "FileType", "iclass"); + JsonSaveBufAsHexCompact(root, "$.Card.CSN", hdr.csn, sizeof(hdr.csn)); + JsonSaveBufAsHexCompact(root, "$.Card.Configuration", (uint8_t *)&hdr.conf, sizeof(hdr.conf)); - 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); + uint8_t pagemap = get_pagemap(&hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { - 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)); + picopass_ns_hdr_t ns_hdr; + memcpy(&ns_hdr, data, sizeof(picopass_ns_hdr_t)); + 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)); - JsonSaveBufAsHexCompact(root, "$.Card.Kd", hdr->key_d, sizeof(hdr->key_d)); - JsonSaveBufAsHexCompact(root, "$.Card.Kc", hdr->key_c, sizeof(hdr->key_c)); - JsonSaveBufAsHexCompact(root, "$.Card.AIA", hdr->app_issuer_area, sizeof(hdr->app_issuer_area)); + JsonSaveBufAsHexCompact(root, "$.Card.Epurse", hdr.epurse, sizeof(hdr.epurse)); + JsonSaveBufAsHexCompact(root, "$.Card.Kd", hdr.key_d, sizeof(hdr.key_d)); + JsonSaveBufAsHexCompact(root, "$.Card.Kc", hdr.key_c, sizeof(hdr.key_c)); + JsonSaveBufAsHexCompact(root, "$.Card.AIA", hdr.app_issuer_area, sizeof(hdr.app_issuer_area)); } - for (size_t i = 0; i < (datalen / 8); i++) { - char path[PATH_MAX_LENGTH] = {0}; + for (size_t i = 0; i < (datalen / PICOPASS_BLOCK_SIZE); i++) { snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, data + (i * 8), 8); + JsonSaveBufAsHexCompact(root, path, data + (i * PICOPASS_BLOCK_SIZE), PICOPASS_BLOCK_SIZE); } break; @@ -475,25 +471,49 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.ConfigBlock", conf, sizeof(conf)); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } break; } - case jsf14b: { - JsonSaveStr(root, "FileType", "14b"); - JsonSaveBufAsHexCompact(root, "raw", data, datalen); + case jsf14b_v2: { + JsonSaveStr(root, "FileType", "14b v2"); + for (size_t i = 0; i < datalen / 4; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 4], 4); + } break; } - case jsf15: { - JsonSaveStr(root, "FileType", "15693"); - JsonSaveBufAsHexCompact(root, "raw", data, datalen); + // handles ISO15693 w blocksize of 4 bytes + case jsf15_v2: { + JsonSaveStr(root, "FileType", "15693 v2"); + for (size_t i = 0; i < datalen / 4; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 4], 4); + } break; } - case jsfLegic: { - JsonSaveStr(root, "FileType", "legic"); - JsonSaveBufAsHexCompact(root, "raw", data, datalen); + // handles ISO15693 w blocksize of 8 bytes + case jsf15_v3: { + JsonSaveStr(root, "FileType", "15693 v3"); + for (size_t i = 0; i < datalen / 8; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 8], 8); + } + break; + } + case jsfLegic_v2: { + JsonSaveStr(root, "FileType", "legic v2"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", data, 4); + size_t i = 0; + for (; i < datalen / 16; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], 16); + } + if (datalen % 16) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], (datalen % 16)); + } break; } case jsfT5555: { @@ -503,7 +523,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.ConfigBlock", conf, sizeof(conf)); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -517,7 +536,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.Protection2", data + (15 * 4), 4); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -530,7 +548,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.Config", data + (4 * 4), 4); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -544,7 +561,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.UID", data + (33 * 4), 4); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -564,8 +580,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, memcpy(vdata, data + (14 + atslen), 2 * 64 * 17); for (size_t i = 0; i < datalen; i++) { - char path[PATH_MAX_LENGTH] = {0}; - if (vdata[0][i][0]) { snprintf(path, sizeof(path), "$.SectorKeys.%zu.KeyA", i); JsonSaveBufAsHexCompact(root, path, &vdata[0][i][1], AES_KEY_LEN); @@ -591,31 +605,27 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, memcpy(dvdata, &data[14 + datslen], 4 * 0xE * (24 + 1)); for (int i = 0; i < (int)datalen; i++) { - char path[PATH_MAX_LENGTH] = {0}; if (dvdata[0][i][0]) { snprintf(path, sizeof(path), "$.DES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], 8); + JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], DES_KEY_LEN); } if (dvdata[1][i][0]) { snprintf(path, sizeof(path), "$.3DES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], 16); + JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], T2DES_KEY_LEN); } if (dvdata[2][i][0]) { snprintf(path, sizeof(path), "$.AES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], 16); + JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], AES_KEY_LEN); } if (dvdata[3][i][0]) { snprintf(path, sizeof(path), "$.K3KDES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], 24); + JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], T3DES_KEY_LEN); } } break; } - case jsfFido: { - break; - } case jsfCustom: { (*callback)(root); break; @@ -626,8 +636,8 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.UID", tag->uid, sizeof(tag->uid)); JsonSaveBufAsHexCompact(root, "$.Card.H0R1", tag->HR01, sizeof(tag->HR01)); JsonSaveBufAsHexCompact(root, "$.Card.Size", (uint8_t *) & (tag->size), 2); + for (size_t i = 0; i < TOPAZ_STATIC_MEMORY / 8; i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, &tag->data_blocks[i][0], TOPAZ_BLOCK_SIZE); } @@ -638,24 +648,67 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, break; } + case jsfLto: { + JsonSaveStr(root, "FileType", "lto"); + for (size_t i = 0; i < datalen / 32; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 32], 32); + } + break; + } + case jsfCryptorf: { + JsonSaveStr(root, "FileType", "cryptorf"); + for (size_t i = 0; i < datalen / 8; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 8], 8); + } + break; + } + case jsfNDEF: { + JsonSaveStr(root, "FileType", "ndef"); + JsonSaveInt(root, "Ndef.Size", datalen); + size_t i = 0; + for (; i < datalen / 16; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], 16); + } + if (datalen % 16) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], (datalen % 16)); + } + break; + } + // no action + case jsfFido: + break; + // depricated + case jsfCardMemory: + case jsf14b: + case jsf15: + case jsfLegic: default: break; } - int res = json_dump_file(root, fileName, JSON_INDENT(2)); - if (res) { - PrintAndLogEx(FAILED, "error: can't save the file: " _YELLOW_("%s"), fileName); + char *fn = newfilenamemcopyEx(preferredName, ".json", e_save_path); + if (fn == NULL) { + return PM3_EMALLOC; + } + + if (json_dump_file(root, fn, JSON_INDENT(2))) { + PrintAndLogEx(FAILED, "error: can't save the file: " _YELLOW_("%s"), fn); retval = 200; + free(fn); goto out; } if (verbose) { - PrintAndLogEx(SUCCESS, "saved to json file " _YELLOW_("%s"), fileName); + PrintAndLogEx(SUCCESS, "saved to json file " _YELLOW_("%s"), fn); } + free(fn); out: json_decref(root); - free(fileName); return retval; } int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose) { @@ -961,6 +1014,239 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { return retval; } +int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, nfc_df_e ft) { + + if (data == NULL) return PM3_EINVARG; + + *datalen = 0; + int retval = PM3_SUCCESS; + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // 256 + 2 newline chars + 1 null terminator + char line[256 + 2 + 1]; + memset(line, 0, sizeof(line)); + + udata_t udata = (udata_t)data; + int n = 0; + uint32_t counter = 0; + + while (!feof(f)) { + + memset(line, 0, sizeof(line)); + + if (fgets(line, sizeof(line), f) == NULL) { + if (feof(f)) + break; + + fclose(f); + PrintAndLogEx(FAILED, "File reading error."); + return PM3_EFILE; + } + + if (line[0] == '#') + continue; + + strcleanrn(line, sizeof(line)); + str_lower(line); + + if (str_startswith(line, "uid:")) { + if (ft == NFC_DF_MFC) { +// param_gethex_to_eol(line + 4, 0, udata.mfc->card_info.uid, sizeof(udata.mfc->card_info.uid), &n); + } + continue; + } + + if (str_startswith(line, "atqa:")) { + if (ft == NFC_DF_MFC) { +// param_gethex_to_eol(line + 5, 0, udata.mfc->card_info.atqa, sizeof(udata.mfc->card_info.atqa), &n); + } + continue; + } + + if (str_startswith(line, "sak:")) { + if (ft == NFC_DF_MFC) { + int sak = 0; + sscanf(line, "sak: %d", &sak); +// udata.mfc->card_info.sak = sak & 0xFF; + } + continue; + } + + if (str_startswith(line, "signature:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(line + 11, 0, udata.mfu->signature, sizeof(udata.mfu->signature), &n); + } + continue; + } + + if (str_startswith(line, "mifare version:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(line + 16, 0, udata.mfu->version, sizeof(udata.mfu->version), &n); + } + continue; + } + + if (str_startswith(line, "counter 0:")) { + int no = 0; + sscanf(line, "counter 0: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[0][0] = no & 0xFF; + udata.mfu->counter_tearing[0][1] = no & 0xFF; + udata.mfu->counter_tearing[0][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 0:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + uint32_t b = 0; + sscanf(line, "tearing 0: %02x", &b); + udata.mfu->counter_tearing[0][3] = b & 0xFF; + } + continue; + } + + if (str_startswith(line, "counter 1:")) { + int no = 0; + sscanf(line, "counter 1: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[1][0] = no & 0xFF; + udata.mfu->counter_tearing[1][1] = no & 0xFF; + udata.mfu->counter_tearing[1][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 1:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + uint32_t b = 0; + sscanf(line, "tearing 1: %02x", &b); + udata.mfu->counter_tearing[1][3] = b & 0xFF; + } + continue; + } + + if (str_startswith(line, "counter 2:")) { + int no = 0; + sscanf(line, "counter 2: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[2][0] = no & 0xFF; + udata.mfu->counter_tearing[2][1] = no & 0xFF; + udata.mfu->counter_tearing[2][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 2:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + uint32_t b = 0; + sscanf(line, "tearing 2: %02x", &b); + udata.mfu->counter_tearing[2][3] = b & 0xFF; + } + continue; + } + + if (str_startswith(line, "pages total:")) { + sscanf(line, "pages total: %d", &n); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->pages = n; + } + continue; + } + + // Page 0: 04 10 56 CA + if (str_startswith(line, "page ")) { + int pageno = 0; + sscanf(line, "page %d:", &pageno); + + if (strcmp(line, "??") == 0) { + PrintAndLogEx(INFO, "Missing data detected in page %i, skipping...", pageno); + continue; + } + + if (((pageno * MFU_BLOCK_SIZE) + MFU_BLOCK_SIZE) >= maxdatalen) { + continue; + } + + char *p = line; + while (*p++ != ':') {}; + p++; + + if (ft == NFC_DF_MFU) { + n = 0; + param_gethex_to_eol(p, 0, udata.mfu->data + (pageno * MFU_BLOCK_SIZE), MFU_BLOCK_SIZE, &n); + *datalen += MFU_BLOCK_SIZE; + } + continue; + } + + // Block 0: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + if (str_startswith(line, "block ")) { + int blockno = 0; + sscanf(line, "block %d:", &blockno); + + if (strcmp(line, "??") == 0) { + PrintAndLogEx(INFO, "Missing data detected in block %i, skipping...", blockno); + continue; + } + + if (((blockno * MFBLOCK_SIZE) + MFBLOCK_SIZE) >= maxdatalen) { + continue; + } + + char *p = line; + while (*p++ != ':') {}; + p++; + + if (ft == NFC_DF_MFC) { + uint8_t block[MFBLOCK_SIZE] = {0}; + param_gethex_to_eol(p, 0, block, MFBLOCK_SIZE, &n); + memcpy(&udata.bytes[(blockno * MFBLOCK_SIZE)], block, MFBLOCK_SIZE); + counter += MFBLOCK_SIZE; + } else if (ft == NFC_DF_PICOPASS) { + uint8_t block[PICOPASS_BLOCK_SIZE] = {0}; + param_gethex_to_eol(p, 0, block, PICOPASS_BLOCK_SIZE, &n); + memcpy(&udata.bytes[(blockno * PICOPASS_BLOCK_SIZE)], block, PICOPASS_BLOCK_SIZE); + counter += PICOPASS_BLOCK_SIZE; + } + continue; + } + } + + // add header length + if (ft == NFC_DF_MFC || ft == NFC_DF_PICOPASS) { + *datalen = counter; + } else if (ft == NFC_DF_MFU) { + *datalen += MFU_DUMP_PREFIX_LENGTH; + } + + fclose(f); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from NFC file `" _YELLOW_("%s") "`", *datalen, preferredName); + return retval; +} + int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); @@ -1048,6 +1334,17 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { return retval; } +static int load_file_sanity(char *s, uint32_t datalen, int i, size_t len) { + if (len == 0) { + PrintAndLogEx(WARNING, "WARNING: json %s block %d has zero-length data", s, i); + PrintAndLogEx(INFO, "file parsing stopped"); + return false; + } else if (len != datalen) { + PrintAndLogEx(WARNING, "WARNING: json %s block %d only has %zu bytes, expected %d (will fill with zero data)", s, i, len, datalen); + } + return true; +} + int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, void (*callback)(json_t *)) { return loadFileJSONex(preferredName, data, maxdatalen, datalen, true, callback); } @@ -1066,8 +1363,9 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz json_error_t error; json_t *root = json_load_file(path, 0, &error); - if (verbose) + if (verbose) { PrintAndLogEx(SUCCESS, "loaded from JSON file `" _YELLOW_("%s") "`", path); + } free(path); @@ -1083,64 +1381,105 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz goto out; } - typedef union UDATA { - void *v; - uint8_t *bytes; - mfu_dump_t *mfu; - topaz_tag_t *topaz; - } UDATA; - UDATA udata = (UDATA)data; char ctype[100] = {0}; JsonLoadStr(root, "$.FileType", ctype); - if (!strcmp(ctype, "raw")) { - JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + // Proxmark3 settings file. Nothing to do except call the callback function + if (!strcmp(ctype, "settings")) { + goto out; } - if (!strcmp(ctype, "mfcard")) { + udata_t udata = (udata_t)data; + + size_t len = 0; + char blocks[PATH_MAX_LENGTH] = {0}; + + if (!strcmp(ctype, "raw")) { + JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + goto out; + } + + // depricated mfcard + if (!strcmp(ctype, "mfcard") || !strcmp(ctype, "mfc v2")) { size_t sptr = 0; - for (int i = 0; i < 256; i++) { - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; - uint8_t block[16]; - JsonLoadBufAsHex(root, blocks, block, 16, &len); - if (!len) - break; - - if (sptr + 16 > maxdatalen) { + // load blocks (i) from 0..N, but check sptr against total data length, not `i` + for (int i = 0; sptr < maxdatalen; i++) { + if (sptr + MFBLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - memcpy(&udata.bytes[sptr], block, 16); - sptr += len; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + uint8_t block[MFBLOCK_SIZE] = {0}; // ensure zero-filled when partial block of data read + JsonLoadBufAsHex(root, blocks, block, MFBLOCK_SIZE, &len); + if (load_file_sanity(ctype, MFBLOCK_SIZE, i, len) == false) { + break; + } + + memcpy(&udata.bytes[sptr], block, MFBLOCK_SIZE); + sptr += MFBLOCK_SIZE; // always increment pointer by the full block size, even if only partial data read from dump file } *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "mfc v3")) { + + JsonLoadBufAsHex(root, "$.Card.UID", udata.mfc_ev1->card.ev1.uid, udata.mfc_ev1->card.ev1.uidlen, datalen); + JsonLoadBufAsHex(root, "$.Card.ATQA", udata.mfc_ev1->card.ev1.atqa, 2, datalen); + JsonLoadBufAsHex(root, "$.Card.SAK", &(udata.mfc_ev1->card.ev1.sak), 1, datalen); + JsonLoadBufAsHex(root, "$.Card.ATS", udata.mfc_ev1->card.ev1.ats, sizeof(udata.mfc_ev1->card.ev1.ats_len), datalen); + JsonLoadBufAsHex(root, "$.Card.SIGNATURE", udata.mfc_ev1->card.ev1.signature, sizeof(udata.mfc_ev1->card.ev1.signature), datalen); + + *datalen = MFU_DUMP_PREFIX_LENGTH; + + size_t sptr = 0; + // load blocks (i) from 0..N, but check sptr against total data length, not `i` + for (int i = 0; sptr < maxdatalen; i++) { + if (sptr + MFBLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + uint8_t block[MFBLOCK_SIZE] = {0}; // ensure zero-filled when partial block of data read + JsonLoadBufAsHex(root, blocks, block, MFBLOCK_SIZE, &len); + + if (load_file_sanity(ctype, MFBLOCK_SIZE, i, len) == false) { + break; + } + + memcpy(&udata.bytes[sptr], block, MFBLOCK_SIZE); + sptr += MFBLOCK_SIZE; // always increment pointer by the full block size, even if only partial data read from dump file + } + + *datalen = sptr; + goto out; } if (!strcmp(ctype, "fudan")) { size_t sptr = 0; - for (int i = 0; i < 256; i++) { + for (int i = 0; i < maxdatalen; i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) - break; + if (load_file_sanity(ctype, 4, i, len) == false) { + break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "mfu")) { @@ -1160,17 +1499,17 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz size_t sptr = 0; for (int i = 0; i < 256; i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; JsonLoadBufAsHex(root, blocks, &udata.mfu->data[sptr], MFU_BLOCK_SIZE, &len); - if (!len) + + if (load_file_sanity(ctype, MFU_BLOCK_SIZE, i, len) == false) { break; + } sptr += len; udata.mfu->pages++; @@ -1179,99 +1518,169 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz --udata.mfu->pages; *datalen += sptr; + goto out; } if (!strcmp(ctype, "hitag")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 4); i++) { + for (int i = 0; i < (maxdatalen / 4); i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) + if (load_file_sanity(ctype, 4, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "iclass")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 8); i++) { - if (sptr + 8 > maxdatalen) { + for (int i = 0; i < (maxdatalen / PICOPASS_BLOCK_SIZE); i++) { + if (sptr + PICOPASS_BLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); - if (!len) + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], PICOPASS_BLOCK_SIZE, &len); + if (load_file_sanity(ctype, PICOPASS_BLOCK_SIZE, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "t55x7")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 4); i++) { + for (int i = 0; i < (maxdatalen / 4); i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) + if (load_file_sanity(ctype, 4, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "EM4X50")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 4); i++) { + for (int i = 0; i < (maxdatalen / 4); i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) + if (load_file_sanity(ctype, 4, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } + // depricated if (!strcmp(ctype, "15693")) { JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + goto out; } + // handles ISO15693 w blocksize of 4 bytes. + if (!strcmp(ctype, "15693 v2")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 4); i++) { + if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); + if (load_file_sanity(ctype, 4, i, len) == false) { + break; + } + + sptr += len; + } + + *datalen = sptr; + goto out; + } + // handles ISO15693 w blocksize of 8 bytes. + if (!strcmp(ctype, "15693 v3")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 8); i++) { + if (sptr + 8 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); + if (load_file_sanity(ctype, 8, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "legic v2")) { + size_t sptr = 0; + for (int i = 0; i < 64; i++) { + if (sptr + 16 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 16, &len); + if (load_file_sanity(ctype, 16, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + // depricated if (!strcmp(ctype, "legic")) { JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + goto out; } if (!strcmp(ctype, "topaz")) { @@ -1284,26 +1693,25 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz for (int i = 0; i < (TOPAZ_STATIC_MEMORY / 8); i++) { if (sptr + TOPAZ_BLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; JsonLoadBufAsHex(root, blocks, &udata.topaz->data_blocks[sptr][0], TOPAZ_BLOCK_SIZE, &len); - if (!len) + if (load_file_sanity(ctype, TOPAZ_BLOCK_SIZE, i, len) == false) { break; + } sptr += len; - // ICEMAN todo: add dynamic memory. // uint16_z Size // uint8_t *dynamic_memory; } *datalen += sptr; + goto out; } if (!strcmp(ctype, "mfpkeys")) { @@ -1319,28 +1727,166 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz size_t sptr = (14 + atslen); // memcpy(vdata, udata.bytes + (14 + atslen), 2 * 64 * 17); - for (size_t i = 0; i < 64; i++) { + for (int i = 0; i < 64; i++) { if ((sptr + (AES_KEY_LEN * 2)) > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); break; } size_t offset = (14 + atslen) + (i * 2 * AES_KEY_LEN); - char blocks[40] = {0}; - snprintf(blocks, sizeof(blocks), "$.SectorKeys.%zu.KeyA", i); + snprintf(blocks, sizeof(blocks), "$.SectorKeys.%d.KeyA", i); JsonLoadBufAsHex(root, blocks, udata.bytes + offset, AES_KEY_LEN, datalen); - snprintf(blocks, sizeof(blocks), "$.SectorKeys.%zu.KeyB", i); + snprintf(blocks, sizeof(blocks), "$.SectorKeys.%d.KeyB", i); JsonLoadBufAsHex(root, blocks, udata.bytes + offset + AES_KEY_LEN, AES_KEY_LEN, datalen); sptr += (2 * AES_KEY_LEN); } *datalen += sptr; + goto out; + } + + if (!strcmp(ctype, "mfdes")) { + JsonLoadBufAsHex(root, "$.Card.UID", udata.bytes, 7, datalen); + JsonLoadBufAsHex(root, "$.Card.SAK", udata.bytes + 10, 1, datalen); + JsonLoadBufAsHex(root, "$.Card.ATQA", udata.bytes + 11, 2, datalen); + uint8_t atslen = udata.bytes[13]; + if (atslen > 0) { + JsonLoadBufAsHex(root, "$.Card.ATS", udata.bytes + 14, atslen, datalen); + } + +// size_t sptr = (14 + atslen); +// uint8_t dvdata[4][0xE][24 + 1] = {{{0}}}; + + /* + for (int i = 0; i < (int)datalen; i++) { + char path[PATH_MAX_LENGTH] = {0}; + + if (dvdata[0][i][0]) { + snprintf(path, sizeof(path), "$.DES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], DES_KEY_LEN); + } + + if (dvdata[1][i][0]) { + snprintf(path, sizeof(path), "$.3DES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], T2DES_KEY_LEN); + } + if (dvdata[2][i][0]) { + snprintf(path, sizeof(path), "$.AES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], AES_KEY_LEN); + } + if (dvdata[3][i][0]) { + snprintf(path, sizeof(path), "$.K3KDES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], T3DES_KEY_LEN); + } + } + */ +// memcpy(&data[14 + atslen], dvdata, 4 * 0xE * (24 + 1)); + + goto out; + } + + if (!strcmp(ctype, "14b v2")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 4); i++) { + if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); + if (load_file_sanity(ctype, 4, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "lto")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 32); i++) { + if (sptr + 32 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 32, &len); + if (load_file_sanity(ctype, 32, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "cryptorf")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 8); i++) { + if (sptr + 8 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); + if (load_file_sanity(ctype, 8, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "ndef")) { + + /* + // when we will read and return extra values from NDEF json + json_error_t up_error = {0}; + int i1 = 0; + size_t ndefsize = 0; + if (json_unpack_ex(root, &up_error, 0, "{s:i}", "Ndef.Size", &i1) == 0) { + ndefsize = i1; + } + */ + + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 16); i++) { + if (sptr + 16 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 16, &len); + if (load_file_sanity(ctype, 16, i, len) == false) { + break; + } + + sptr += len; + } + + *datalen = sptr; + goto out; } out: + if (callback != NULL) { (*callback)(root); } @@ -1627,7 +2173,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya return PM3_SUCCESS; } -mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { +mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose) { mfu_df_e retval = MFU_DF_UNKNOWN; uint8_t bcc0, bcc1; @@ -1680,6 +2226,107 @@ mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { return retval; } +nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + nfc_df_e retval = NFC_DF_UNKNOWN; + + char line[256]; + memset(line, 0, sizeof(line)); + + while (!feof(f)) { + + memset(line, 0, sizeof(line)); + + if (fgets(line, sizeof(line), f) == NULL) { + if (feof(f)) { + break; + } + + fclose(f); + PrintAndLogEx(FAILED, "File reading error."); + return PM3_EFILE; + } + + strcleanrn(line, sizeof(line)); + str_lower(line); + + if (str_startswith(line, "device type: ntag")) { + retval = NFC_DF_MFU; + break; + } + if (str_startswith(line, "device type: mifare classic")) { + retval = NFC_DF_MFC; + break; + } + if (str_startswith(line, "device type: mifare desfire")) { + retval = NFC_DF_MFDES; + break; + } + if (str_startswith(line, "device type: iso14443-3a")) { + retval = NFC_DF_14_3A; + break; + } + if (str_startswith(line, "device type: iso14443-3b")) { + retval = NFC_DF_14_3B; + break; + } + if (str_startswith(line, "device type: iso14443-4a")) { + retval = NFC_DF_14_4A; + break; + } + if (str_startswith(line, "filetype: flipper picopass device")) { + retval = NFC_DF_PICOPASS; + break; + } + + } + fclose(f); + + if (verbose) { + switch (retval) { + case NFC_DF_MFU: + PrintAndLogEx(INFO, "detected MIFARE Ultralight / NTAG based dump format"); + break; + case NFC_DF_MFC: + PrintAndLogEx(INFO, "detected MIFARE Classic based dump format"); + break; + case NFC_DF_MFDES: + PrintAndLogEx(INFO, "detected MIFARE DESFire based dump format"); + break; + case NFC_DF_14_3A: + PrintAndLogEx(INFO, "detected ISO14443-3A based dump format. No data available"); + break; + case NFC_DF_14_3B: + PrintAndLogEx(INFO, "detected ISO14443-3B based dump format. No data available"); + break; + case NFC_DF_14_4A: + PrintAndLogEx(INFO, "detected ISO14443-4A based dump format. No data available"); + break; + case NFC_DF_PICOPASS: + PrintAndLogEx(INFO, "detected PICOPASS based dump format"); + break; + case NFC_DF_UNKNOWN: + PrintAndLogEx(WARNING, "failed to detected dump format"); + break; + } + } + return retval; +} + static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) { mfu_dump_t *mfu = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); @@ -1756,7 +2403,7 @@ int convert_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { return PM3_EINVARG; } - mfu_df_e res = detect_mfu_dump_format(dump, dumplen, verbose); + mfu_df_e res = detect_mfu_dump_format(dump, verbose); switch (res) { case MFU_DF_NEWBIN: @@ -2082,17 +2729,20 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { - int res = 0; + int res = PM3_SUCCESS; DumpFileType_t dftype = getfiletype(fn); switch (dftype) { case BIN: { res = loadFile_safe(fn, ".bin", pdump, dumplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "File IO failed"); + } break; } case EML: { res = loadFileEML_safe(fn, pdump, dumplen); - if (res == PM3_ESOFT) { - PrintAndLogEx(WARNING, "file IO failed"); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "File IO failed"); } break; } @@ -2103,8 +2753,9 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl return PM3_EMALLOC; } res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL); - if (res == PM3_SUCCESS) + if (res == PM3_SUCCESS) { return res; + } free(*pdump); @@ -2116,27 +2767,84 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl break; } case DICTIONARY: { - PrintAndLogEx(ERR, "Error: Only BIN/EML/JSON formats allowed"); + PrintAndLogEx(ERR, "Only value; \ } -MAKE_ENUM_TYPE(uint8_t); +MAKE_ENUM_TYPE(uint8_t) // KSX6924LookupCardType MAKE_ENUM_CONST(CardType, uint8_t, { 0x00, "Pre-paid" }, { 0x10, "Post-pay" }, { 0x20, "Mobile post-pay" }, - ); + ) // KSX6924LookupAlg MAKE_ENUM_CONST(Alg, uint8_t, { 0x00, "SEED" }, { 0x10, "3DES" }, - ); + ) // KSX6924LookupTMoneyIDCenter MAKE_ENUM_CONST(TMoneyIDCenter, uint8_t, @@ -139,20 +139,17 @@ MAKE_ENUM_CONST(TMoneyIDCenter, uint8_t, { 0x0b, "EB Card Corporation" }, { 0x0c, "Seoul Bus Transport Association" }, { 0x0d, "Cardnet" }, - ); + ) // KSX6924LookupTMoneyUserCode MAKE_ENUM_CONST(TMoneyUserCode, uint8_t, { 0x01, "Regular/normal" }, { 0x02, "Child" }, - { 0x04, "Youth" }, - { 0x06, "elderly" }, - { 0x0f, "Test" }, { 0xff, "Inactive" }, - ); + ) // KSX6924LookupTMoneyDisRate MAKE_ENUM_CONST(TMoneyDisRate, uint8_t, @@ -163,7 +160,7 @@ MAKE_ENUM_CONST(TMoneyDisRate, uint8_t, { 0x20, "Merit, basic" }, { 0x21, "Merit, companion" }, - ); + ) // KSX6924LookupTMoneyTCode MAKE_ENUM_CONST(TMoneyTCode, uint8_t, @@ -171,7 +168,7 @@ MAKE_ENUM_CONST(TMoneyTCode, uint8_t, { 0x01, "SK Telecom" }, { 0x02, "Korea Telecom" }, { 0x03, "LG Uplus" }, - ); + ) // KSX6924LookupTMoneyCCode MAKE_ENUM_CONST(TMoneyCCode, uint8_t, @@ -187,7 +184,7 @@ MAKE_ENUM_CONST(TMoneyCCode, uint8_t, { 0x09, "Woori Card" }, { 0x0a, "Hana SK Card" }, { 0x0b, "Hyundai Card" }, - ); + ) static const char *KSX6924_UNKNOWN = "Unknown"; @@ -320,7 +317,7 @@ bool KSX6924ParsePurseInfo(const uint8_t *purseInfo, size_t purseLen, struct ksx // TODO return true; -}; +} /** * Prints out a ksx6924_purse_info @@ -523,7 +520,7 @@ bool KSX6924ParseInitializeCardResponse(const uint8_t *initCardResponse, size_t // TODO return true; -}; +} /** * Prints out a Initialize Card response diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 9c3326f83..077414c86 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -43,6 +43,7 @@ #include "mifare/mad.h" #include "mifare/aiddesfire.h" + const CLIParserOption DesfireAlgoOpts[] = { {T_DES, "des"}, {T_3DES, "2tdea"}, @@ -1749,7 +1750,7 @@ int DesfireFillAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS ap memcpy( appList[indx].appDFName, &buf[i * 24 + 1 + 5], - // strnlen((char *)&buf[i * 24 + 1 + 5], 16) + // str_nlen((char *)&buf[i * 24 + 1 + 5], 16) 16 ); } @@ -2879,7 +2880,7 @@ int DesfireISOSelect(DesfireContext_t *dctx, DesfireISOSelectControl cntr, uint8 } int DesfireISOSelectDF(DesfireContext_t *dctx, char *dfname, uint8_t *resp, size_t *resplen) { - return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen); + return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, str_nlen(dfname, 16), resp, resplen); } int DesfireISOGetChallenge(DesfireContext_t *dctx, DesfireCryptoAlgorithm keytype, uint8_t *resp, size_t *resplen) { diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index 48126ae50..750723dab 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -788,11 +788,11 @@ bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc) { } void iso14443a_crc_append(uint8_t *data, size_t len) { - return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1); + compute_crc(CRC_14443_A, data, len, data + len, data + len + 1); } void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) { - return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1); + compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1); } bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc) { diff --git a/client/src/mifare/gen4.c b/client/src/mifare/gen4.c new file mode 100644 index 000000000..b01085671 --- /dev/null +++ b/client/src/mifare/gen4.c @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Common functionality for low/high-frequency GALLAGHER tag encoding & decoding. +//----------------------------------------------------------------------------- +#include "gen4.h" + +#include +#include +#include +#include + +#include "commonutil.h" +#include "util.h" +#include "ui.h" +#include "mifaredefault.h" +#include "comms.h" +#include "cmdhf14a.h" +#include "protocols.h" +#include "mfkey.h" +#include "util_posix.h" +#include "cmdparser.h" + +static int mfG4ExCommand(uint8_t cmd, uint8_t *pwd, uint8_t *data, size_t datalen, uint8_t *response, size_t *responselen, bool verbose) { + struct p { + uint8_t cmdheader; + uint8_t pwd[4]; + uint8_t command; + uint8_t data[32]; + } PACKED payload; + memset(&payload, 0, sizeof(payload)); + + if (datalen > sizeof(payload.data)) { + return PM3_EINVARG; + } + + payload.cmdheader = 0xCF; + payload.command = cmd; + if (pwd != NULL) { + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + } + if (data != NULL && datalen > 0) { + memcpy(payload.data, data, datalen); + } + + int resplen = 0; + + clearCommandBuffer(); + SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_RAW | ISO14A_NO_RATS | ISO14A_APPEND_CRC, 6 + datalen, 0, (uint8_t *)&payload, 6 + datalen); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (resp.oldarg[0] != 2) { + if (verbose) PrintAndLogEx(ERR, "No card in the field."); + return PM3_ETIMEOUT; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + if (verbose) { + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); + } + } else { + if (verbose) PrintAndLogEx(ERR, "No card in the field."); + return PM3_ETIMEOUT; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + resplen = resp.oldarg[0]; + + if (!resplen) { + if (verbose) PrintAndLogEx(ERR, "No card response."); + return PM3_EFAILED; + } + + resplen = resplen - 2; // 14A CRC + if (resplen < 0) + resplen = 0; + + if (resplen > 40) { + if (verbose) PrintAndLogEx(ERR, "Buffer too small(%d).", resplen); + return PM3_EOVFLOW; + } + + if (response != NULL) + memcpy(response, resp.data.asBytes, resplen); + + if (responselen != NULL) + *responselen = resplen; + + return PM3_SUCCESS; + } else { + if (verbose) PrintAndLogEx(ERR, "Reply timeout."); + return PM3_ETIMEOUT; + } +} + +int mfG4GetConfig(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose) { + uint8_t resp[40] = {0}; + size_t resplen = 0; + + int res = mfG4ExCommand(GEN4_CMD_DUMP_CONFIG, pwd, NULL, 0, resp, &resplen, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + if (data != NULL) + memcpy(data, resp, resplen); + + if (datalen != NULL) + *datalen = resplen; + + return PM3_SUCCESS; +} + +int mfG4GetFactoryTest(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose) { + uint8_t resp[40] = {0}; + size_t resplen = 0; + + int res = mfG4ExCommand(GEN4_CMD_FACTORY_TEST, pwd, NULL, 0, resp, &resplen, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + if (data != NULL) + memcpy(data, resp, resplen); + + if (datalen != NULL) + *datalen = resplen; + + return PM3_SUCCESS; +} + +int mfG4ChangePassword(uint8_t *pwd, uint8_t *newpwd, bool verbose) { + uint8_t resp[40] = {0}; + size_t resplen = 0; + + int res = mfG4ExCommand(GEN4_CMD_CHANGE_PASSWORD, pwd, newpwd, 4, resp, &resplen, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + if (resplen != 2 || resp[0] != 0x90 || resp[1] != 0x00) + return PM3_EAPDU_FAIL; + + return PM3_SUCCESS; +} + +int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { + struct p { + uint8_t blockno; + uint8_t pwd[4]; + uint8_t workFlags; + } PACKED payload; + payload.blockno = blockno; + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + payload.workFlags = workFlags; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_RDBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_RDBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) { + return PM3_EUNDEF; + } + memcpy(data, resp.data.asBytes, MFBLOCK_SIZE); + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + +int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { + struct p { + uint8_t blockno; + uint8_t pwd[4]; + uint8_t data[MFBLOCK_SIZE]; + uint8_t workFlags; + } PACKED payload; + payload.blockno = blockno; + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + memcpy(payload.data, data, sizeof(payload.data)); + payload.workFlags = workFlags; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_WRBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) { + return PM3_EUNDEF; + } + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} diff --git a/client/src/mifare/gen4.h b/client/src/mifare/gen4.h new file mode 100644 index 000000000..b4e22fb98 --- /dev/null +++ b/client/src/mifare/gen4.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency ISO14443A commands +//----------------------------------------------------------------------------- +#ifndef __GEN4_H +#define __GEN4_H + +#include "common.h" + +#define GEN4_CMD_CONFIG_GTU 0x32 +#define GEN4_CMD_CONFIG_ATS 0x34 +#define GEN4_CMD_CONFIG_ATQA_SAK 0x35 +#define GEN4_CMD_CONFIG_UID_LEN 0x68 +#define GEN4_CMD_CONFIG_UL_ENABLE 0x69 +#define GEN4_CMD_CONFIG_UL_MODE 0x6A +#define GEN4_CMD_CONFIG_UL_SECTOR_COUNT 0x6A +#define GEN4_CMD_DUMP_CONFIG 0xC6 +#define GEN4_CMD_FACTORY_TEST 0xCC +#define GEN4_CMD_WRITE_BLOCK 0xCD +#define GEN4_CMD_READ_BLOCK 0xCE +#define GEN4_CMD_BL0_DIRECT_WRITE_EN 0xCF +#define GEN4_CMD_SET_CONFIG 0xF0 +#define GEN4_CMD_SET_CONFIG_PERMANENT 0xF1 +#define GEN4_CMD_CHANGE_PASSWORD 0xFE + +int mfG4GetConfig(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose); +int mfG4GetFactoryTest(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose); + +int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); +int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); + +int mfG4ChangePassword(uint8_t *pwd, uint8_t *newpwd, bool verbose); + +#endif diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index 7bb7e144a..f399bb7b0 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -21,10 +21,13 @@ #include "common.h" +#define DES_KEY_LEN 8 #define AES_KEY_LEN 16 +#define T2DES_KEY_LEN 16 +#define T3DES_KEY_LEN 24 + #define MAX_AES_KEYS_LIST_LEN 1024 -#define MFKEY_SIZE 6 #define MFBLOCK_SIZE 16 #define MIFARE_4K_MAXBLOCK 256 diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 76c392490..13d925fdd 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -40,6 +40,8 @@ #include "crypto/libpcrypto.h" #include "util.h" // xor #include "mbedtls/sha1.h" // SHA1 +#include "cmdhf14a.h" +#include "gen4.h" int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -1173,58 +1175,6 @@ int mfGen3Freeze(void) { } } -int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { - struct p { - uint8_t blockno; - uint8_t pwd[4]; - uint8_t workFlags; - } PACKED payload; - payload.blockno = blockno; - memcpy(payload.pwd, pwd, sizeof(payload.pwd)); - payload.workFlags = workFlags; - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_G4_RDBL, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_RDBL, &resp, 1500)) { - if (resp.status != PM3_SUCCESS) { - return PM3_EUNDEF; - } - memcpy(data, resp.data.asBytes, MFBLOCK_SIZE); - } else { - PrintAndLogEx(WARNING, "command execute timeout"); - return PM3_ETIMEOUT; - } - return PM3_SUCCESS; -} - -int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { - struct p { - uint8_t blockno; - uint8_t pwd[4]; - uint8_t data[MFBLOCK_SIZE]; - uint8_t workFlags; - } PACKED payload; - payload.blockno = blockno; - memcpy(payload.pwd, pwd, sizeof(payload.pwd)); - memcpy(payload.data, data, sizeof(payload.data)); - payload.workFlags = workFlags; - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_G4_WRBL, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_WRBL, &resp, 1500)) { - if (resp.status != PM3_SUCCESS) { - return PM3_EUNDEF; - } - } else { - PrintAndLogEx(WARNING, "command execute timeout"); - return PM3_ETIMEOUT; - } - return PM3_SUCCESS; -} - - // variables uint32_t cuid = 0; // uid part used for crypto1. diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index ccefbbd40..6f131f924 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -96,9 +96,6 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); int mfGen3Freeze(void); -int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); -int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); - int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index e0bc83c14..a8227ddc7 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -634,6 +634,7 @@ static const char *ndef_wifi_auth_lookup(uint8_t *d) { return ""; } + static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { if (ndef->PayloadLen == 0) { PrintAndLogEx(INFO, "no payload"); @@ -649,7 +650,7 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { if (ndef->Payload[pos] != 0x10) { n -= 1; - pos -= 1; + pos += 1; continue; } @@ -713,7 +714,7 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { pos += len; } - // NETWORK_IDX + // NETWORK_IDX - always set to 1, deprecated if (memcmp(&ndef->Payload[pos], "\x10\x26", 2) == 0) { // 10 26 00 01 01 uint8_t len = 3; @@ -775,19 +776,34 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { pos += 2; pos += len; } + + // rf-bands + if (memcmp(&ndef->Payload[pos], "\x10\x3C", 2) == 0) { + uint8_t len = 3; + + if (ndef->Payload[pos + 2 + 2] == 0x01) { + PrintAndLogEx(INFO, "RF Bands........ %s ( " _YELLOW_("2.4 GHZ")" )", sprint_hex(&ndef->Payload[pos + 2], len)); + } else if (ndef->Payload[pos + 2 + 2] == 0x02) { + PrintAndLogEx(INFO, "RF Bands........ %s ( " _YELLOW_("5.0 GHZ")" )", sprint_hex(&ndef->Payload[pos + 2], len)); + } + + n -= 2; + n -= len; + pos += 2; + pos += len; + } } /* ap-channel 0, 6 + credential device-name - mac-address + manufacturer model-name model-number + oob-password primary-device-type - rf-bands secondary-device-type-list serial-number ssid @@ -1182,6 +1198,7 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("NDEF parsing") " ----------------"); while (indx < ndefLen) { + switch (ndef[indx]) { case 0x00: { indx++; @@ -1246,8 +1263,9 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(SUCCESS, "Found NDEF message ( " _YELLOW_("%u") " bytes )", len); int res = NDEFRecordsDecodeAndPrint(&ndef[indx], len, verbose); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } } indx += len; @@ -1269,12 +1287,42 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { return PM3_SUCCESS; } default: { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "unknown tag 0x%02x", ndef[indx]); - + } return PM3_ESOFT; } } } return PM3_SUCCESS; } + + +int NDEFGetTotalLength(uint8_t *ndef, size_t ndeflen, size_t *outlen) { + + size_t idx = 0; + while (idx < ndeflen) { + + if (ndef[idx] == 0x00 || + ndef[idx] == 0x01 || + ndef[idx] == 0x02 || + ndef[idx] == 0x03 || + ndef[idx] == 0xFD) { + idx++; + idx += ndefTLVGetLength(&ndef[idx], &idx); + continue; + } + + if (ndef[idx] == 0xFE) { + idx++; + break; + } + + // invalid NDEF + *outlen = 0; + return PM3_ESOFT; + } + + *outlen = idx; + return PM3_SUCCESS; +} diff --git a/client/src/nfc/ndef.h b/client/src/nfc/ndef.h index 643bb70a7..31967c61a 100644 --- a/client/src/nfc/ndef.h +++ b/client/src/nfc/ndef.h @@ -76,5 +76,5 @@ typedef struct { int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose); int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, bool verbose); - +int NDEFGetTotalLength(uint8_t *ndef, size_t ndeflen, size_t *outlen); #endif // _NDEF_H_ diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index 5cbe8b495..46c33389d 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -42,8 +42,9 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs help" }, { 1, "prefs show" }, { 1, "prefs get barmode" }, - { 1, "prefs get clientdebug" }, - { 1, "prefs get clientdelay" }, + { 1, "prefs get client.debug" }, + { 1, "prefs get client.delay" }, + { 1, "prefs get client.timeout" }, { 1, "prefs get color" }, { 1, "prefs get savepaths" }, { 1, "prefs get emoji" }, @@ -52,8 +53,9 @@ const static vocabulary_t vocabulary[] = { { 1, "prefs get plotsliders" }, { 1, "prefs set help" }, { 1, "prefs set barmode" }, - { 1, "prefs set clientdebug" }, - { 1, "prefs set clientdelay" }, + { 1, "prefs set client.debug" }, + { 1, "prefs set client.delay" }, + { 1, "prefs set client.timeout" }, { 1, "prefs set color" }, { 1, "prefs set emoji" }, { 1, "prefs set hints" }, @@ -105,6 +107,7 @@ const static vocabulary_t vocabulary[] = { { 1, "data atr" }, { 1, "data bin2hex" }, { 0, "data bitsamples" }, + { 1, "data bmap" }, { 1, "data clear" }, { 1, "data diff" }, { 0, "data hexsamples" }, @@ -269,7 +272,7 @@ const static vocabulary_t vocabulary[] = { { 1, "hf iclass help" }, { 1, "hf iclass list" }, { 0, "hf iclass dump" }, - { 1, "hf iclass info" }, + { 0, "hf iclass info" }, { 0, "hf iclass rdbl" }, { 0, "hf iclass reader" }, { 0, "hf iclass restore" }, @@ -285,7 +288,7 @@ const static vocabulary_t vocabulary[] = { { 0, "hf iclass esave" }, { 0, "hf iclass esetblk" }, { 0, "hf iclass eview" }, - { 1, "hf iclass configcard" }, + { 0, "hf iclass configcard" }, { 1, "hf iclass calcnewkey" }, { 1, "hf iclass encode" }, { 1, "hf iclass encrypt" }, @@ -363,17 +366,20 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mf gen3uid" }, { 0, "hf mf gen3blk" }, { 0, "hf mf gen3freeze" }, + { 0, "hf mf ginfo" }, { 0, "hf mf ggetblk" }, { 0, "hf mf gload" }, { 0, "hf mf gsave" }, { 0, "hf mf gsetblk" }, { 0, "hf mf gview" }, + { 0, "hf mf gchpwd" }, { 0, "hf mf gdmcfg" }, { 0, "hf mf gdmsetcfg" }, { 0, "hf mf gdmsetblk" }, { 0, "hf mf ndefformat" }, { 0, "hf mf ndefread" }, { 0, "hf mf ndefwrite" }, + { 0, "hf mf encodehid" }, { 1, "hf mfp help" }, { 1, "hf mfp list" }, { 0, "hf mfp auth" }, @@ -451,8 +457,13 @@ const static vocabulary_t vocabulary[] = { { 1, "hf mfdes test" }, { 1, "hf ntag424 help" }, { 0, "hf ntag424 info" }, - { 0, "hf ntag424 sdm" }, { 1, "hf ntag424 view" }, + { 0, "hf ntag424 auth" }, + { 0, "hf ntag424 read" }, + { 0, "hf ntag424 write" }, + { 0, "hf ntag424 getfs" }, + { 0, "hf ntag424 changefs" }, + { 0, "hf ntag424 changekey" }, { 1, "hf seos help" }, { 0, "hf seos info" }, { 1, "hf seos list" }, diff --git a/client/src/preferences.c b/client/src/preferences.c index e3aef4ccb..dd697da74 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -26,14 +26,15 @@ //----------------------------------------------------------------------------- #include "preferences.h" -#include "comms.h" -#include "emv/emvjson.h" #include -#include "cmdparser.h" #include #include -#include +#include "proxmark3.h" +#include "comms.h" +#include "emv/emvjson.h" +#include "cmdparser.h" #include "cliparser.h" +#include "uart/uart.h" // uart_reconfigure_timeouts static int CmdHelp(const char *Cmd); static int setCmdHelp(const char *Cmd); @@ -44,7 +45,7 @@ static char *prefGetFilename(void) { if (searchHomeFilePath(&path, NULL, preferencesFilename, false) == PM3_SUCCESS) return path; else - return strdup(preferencesFilename); + return str_dup(preferencesFilename); } int preferences_load(void) { @@ -52,6 +53,8 @@ int preferences_load(void) { // Set all defaults g_session.client_debug_level = cdbOFF; // g_session.device_debug_level = ddbOFF; + g_session.timeout = uart_get_timeouts(); + g_session.window_changed = false; g_session.plot.x = 10; g_session.plot.y = 30; @@ -260,7 +263,7 @@ void preferences_save_callback(json_t *root) { } */ JsonSaveInt(root, "client.exe.delay", g_session.client_exe_delay); - + JsonSaveInt(root, "client.timeout", g_session.timeout); } void preferences_load_callback(json_t *root) { json_error_t up_error = {0}; @@ -355,6 +358,10 @@ void preferences_load_callback(json_t *root) { // client command execution delay if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.exe.delay", &i1) == 0) g_session.client_exe_delay = i1; + + // client command timeout + if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.timeout", &i1) == 0) + g_session.timeout = i1; } // Help Functions @@ -379,44 +386,44 @@ static void showEmojiState(prefShowOpt_t opt) { switch (g_session.emoji_mode) { case EMO_ALIAS: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("alias"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... "_GREEN_("alias"), prefShowMsg(opt)); break; case EMO_EMOJI: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("emoji"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... "_GREEN_("emoji"), prefShowMsg(opt)); break; case EMO_ALTTEXT: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("alttext"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... "_GREEN_("alttext"), prefShowMsg(opt)); break; case EMO_NONE: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("none"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... "_GREEN_("none"), prefShowMsg(opt)); break; default: - PrintAndLogEx(INFO, " %s emoji.................. "_RED_("unknown"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... "_RED_("unknown"), prefShowMsg(opt)); } } static void showColorState(prefShowOpt_t opt) { if (g_session.supports_colors) - PrintAndLogEx(INFO, " %s color.................. "_GREEN_("ansi"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s color................... "_GREEN_("ansi"), prefShowMsg(opt)); else - PrintAndLogEx(INFO, " %s color.................. "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s color................... "_WHITE_("off"), prefShowMsg(opt)); } static void showClientDebugState(prefShowOpt_t opt) { switch (g_session.client_debug_level) { case cdbOFF: - PrintAndLogEx(INFO, " %s client debug........... "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ "_WHITE_("off"), prefShowMsg(opt)); break; case cdbSIMPLE: - PrintAndLogEx(INFO, " %s client debug........... "_GREEN_("simple"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ "_GREEN_("simple"), prefShowMsg(opt)); break; case cdbFULL: - PrintAndLogEx(INFO, " %s client debug........... "_GREEN_("full"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ "_GREEN_("full"), prefShowMsg(opt)); break; default: - PrintAndLogEx(INFO, " %s client debug........... "_RED_("unknown"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ "_RED_("unknown"), prefShowMsg(opt)); } } /* @@ -448,17 +455,17 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { char s[50]; switch (path_index) { case spDefault: - strcpy(s, "default save path......"); + strcpy(s, "default save path......."); break; case spDump: - strcpy(s, "dump save path........."); + strcpy(s, "dump save path.........."); break; case spTrace: - strcpy(s, "trace save path........"); + strcpy(s, "trace save path........."); break; case spItemCount: default: - strcpy(s, _RED_("unknown")" save path......"); + strcpy(s, _RED_("unknown")" save path......."); } if (path_index < spItemCount) { @@ -478,7 +485,7 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { } static void showPlotPosState(void) { - PrintAndLogEx(INFO, " Plot window............ X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), + PrintAndLogEx(INFO, " plot window............. X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), g_session.plot.x, g_session.plot.y, g_session.plot.h, @@ -487,7 +494,7 @@ static void showPlotPosState(void) { } static void showOverlayPosState(void) { - PrintAndLogEx(INFO, " Slider/Overlay window.. X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), + PrintAndLogEx(INFO, " slider/overlay window... X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), g_session.overlay.x, g_session.overlay.y, g_session.overlay.h, @@ -497,44 +504,47 @@ static void showOverlayPosState(void) { static void showHintsState(prefShowOpt_t opt) { if (g_session.show_hints) - PrintAndLogEx(INFO, " %s hints.................. "_GREEN_("on"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s hints................... "_GREEN_("on"), prefShowMsg(opt)); else - PrintAndLogEx(INFO, " %s hints.................. "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s hints................... "_WHITE_("off"), prefShowMsg(opt)); } static void showPlotSliderState(prefShowOpt_t opt) { if (g_session.overlay_sliders) - PrintAndLogEx(INFO, " %s show plot sliders...... "_GREEN_("on"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s show plot sliders....... "_GREEN_("on"), prefShowMsg(opt)); else - PrintAndLogEx(INFO, " %s show plot sliders...... "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s show plot sliders....... "_WHITE_("off"), prefShowMsg(opt)); } static void showBarModeState(prefShowOpt_t opt) { switch (g_session.bar_mode) { case STYLE_BAR: - PrintAndLogEx(INFO, " %s barmode................ "_GREEN_("bar"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. "_GREEN_("bar"), prefShowMsg(opt)); break; case STYLE_MIXED: - PrintAndLogEx(INFO, " %s barmode................ "_GREEN_("mixed"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. "_GREEN_("mixed"), prefShowMsg(opt)); break; case STYLE_VALUE: - PrintAndLogEx(INFO, " %s barmode................ "_GREEN_("value"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. "_GREEN_("value"), prefShowMsg(opt)); break; default: - PrintAndLogEx(INFO, " %s barmode............... "_RED_("unknown"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................ "_RED_("unknown"), prefShowMsg(opt)); } } static void showOutputState(prefShowOpt_t opt) { - PrintAndLogEx(INFO, " %s output................. %s", prefShowMsg(opt), + PrintAndLogEx(INFO, " %s output.................. %s", prefShowMsg(opt), g_session.dense_output ? _GREEN_("dense") : _WHITE_("normal")); } static void showClientExeDelayState(void) { - PrintAndLogEx(INFO, " Cmd execution delay.... "_GREEN_("%u"), g_session.client_exe_delay); + PrintAndLogEx(INFO, " cmd execution delay..... "_GREEN_("%u"), g_session.client_exe_delay); } +static void showClientTimeoutState(void) { + PrintAndLogEx(INFO, " communication timeout... " _GREEN_("%u") " ms", g_session.timeout); +} static int setCmdEmoji(const char *Cmd) { CLIParserContext *ctx; @@ -636,9 +646,9 @@ static int setCmdColor(const char *Cmd) { static int setCmdDebug(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs set clientdebug ", + CLIParserInit(&ctx, "prefs set client.debug ", "Set persistent preference of using clientside debug level", - "prefs set clientdebug --simple" + "prefs set client.debug --simple" ); void *argtable[] = { @@ -798,10 +808,10 @@ static int setCmdOutput(const char *Cmd) { static int setCmdExeDelay(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs set clientdelay", + CLIParserInit(&ctx, "prefs set client.delay", "Set persistent preference of delay before executing a command in the client", - "prefs set clientdelay --ms 0 --> unsets any delay\n" - "prefs set clientdelay --ms 1000 --> sets 1000ms delay" + "prefs set client.delay --ms 0 --> unsets any delay\n" + "prefs set client.delay --ms 1000 --> sets 1000ms delay" ); void *argtable[] = { @@ -824,9 +834,47 @@ static int setCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } +static int setClientTimeout(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs set client.timeout", + "Set persistent preference of client communication timeout", + "prefs set client.timeout --ms 0 --> unsets any timeout\n" + "prefs set client.timeout -m 20 --> Set the timeout to 20ms\n" + "prefs set client.timeout --ms 500 --> Set the timeout to 500ms\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("m", "ms", "", "timeout in micro seconds"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint32_t new_value = (uint32_t)arg_get_int_def(ctx, 1, 0); + CLIParserFree(ctx); + + // UART_USB_CLIENT_RX_TIMEOUT_MS is considered as the minimum required timeout. + if (new_value < UART_USB_CLIENT_RX_TIMEOUT_MS) { + PrintAndLogEx(WARNING, "Timeout less than %u ms might cause errors.", UART_USB_CLIENT_RX_TIMEOUT_MS); + } else if (new_value > 5000) { + PrintAndLogEx(WARNING, "Timeout greater than 5000 ms makes the client unresponsive."); + } + + if (g_session.timeout != new_value) { + showClientTimeoutState(); + g_session.timeout = new_value; + uart_reconfigure_timeouts(new_value); + showClientTimeoutState(); + preferences_save(); + } else { + showClientTimeoutState(); + } + return PM3_SUCCESS; +} + + static int setCmdHint(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs set hints ", + CLIParserInit(&ctx, "prefs set hints", "Set persistent preference of showing hint messages in the client", "prefs set hints --on" ); @@ -1087,9 +1135,9 @@ static int getCmdColor(const char *Cmd) { static int getCmdDebug(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs get clientdebug", + CLIParserInit(&ctx, "prefs get client.debug", "Get preference of using clientside debug level", - "prefs get clientdebug" + "prefs get client.debug" ); void *argtable[] = { arg_param_begin, @@ -1169,9 +1217,9 @@ static int getCmdSavePaths(const char *Cmd) { static int getCmdExeDelay(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs get clientdelay", + CLIParserInit(&ctx, "prefs get client.delay", "Get preference of delay time before execution of a command in the client", - "prefs get clientdelay" + "prefs get client.delay" ); void *argtable[] = { arg_param_begin, @@ -1183,10 +1231,27 @@ static int getCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } +static int getClientTimeout(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get client.timeout", + "Get preference of delay time before execution of a command in the client", + "prefs get client.timeout" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + showClientTimeoutState(); + return PM3_SUCCESS; +} + static command_t CommandTableGet[] = { {"barmode", getCmdBarMode, AlwaysAvailable, "Get bar mode preference"}, - {"clientdebug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, - {"clientdelay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, + {"client.debug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, + {"client.delay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, + {"client.timeout", getClientTimeout, AlwaysAvailable, "Get client execution delay preference"}, {"color", getCmdColor, AlwaysAvailable, "Get color support preference"}, {"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "}, // {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"}, @@ -1200,8 +1265,10 @@ static command_t CommandTableGet[] = { static command_t CommandTableSet[] = { {"help", setCmdHelp, AlwaysAvailable, "This help"}, {"barmode", setCmdBarMode, AlwaysAvailable, "Set bar mode"}, - {"clientdebug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, - {"clientdelay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, + {"client.debug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, + {"client.delay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, + {"client.timeout", setClientTimeout, AlwaysAvailable, "Set client communication timeout"}, + {"color", setCmdColor, AlwaysAvailable, "Set color support"}, {"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"}, {"hints", setCmdHint, AlwaysAvailable, "Set hint display"}, @@ -1265,6 +1332,7 @@ static int CmdPrefShow(const char *Cmd) { showBarModeState(prefShowNone); showClientExeDelayState(); showOutputState(prefShowNone); + showClientTimeoutState(); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index a91f74179..85b85f762 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -127,9 +127,10 @@ static void showBanner(void) { static const char *prompt_dev = ""; static const char *prompt_ctx = ""; +static const char *prompt_net = ""; -static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev) { - snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptctx); +static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev, const char *promptnet) { + snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); } static int check_comm(void) { @@ -138,7 +139,7 @@ static int check_comm(void) { PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n"); prompt_dev = PROXPROMPT_DEV_OFFLINE; char prompt[PROXPROMPT_MAX_SIZE] = {0}; - prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net); char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); pm3line_update_prompt(prompt_filtered); @@ -265,10 +266,30 @@ main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) { bool printprompt = false; if (g_session.pm3_present) { - if (g_conn.send_via_fpc_usart == false) - prompt_dev = PROXPROMPT_DEV_USB; - else + + switch (g_conn.send_via_ip) { + case PM3_TCPv4: + prompt_net = PROXPROMPT_NET_TCPV4; + break; + case PM3_TCPv6: + prompt_net = PROXPROMPT_NET_TCPV6; + break; + case PM3_UDPv4: + prompt_net = PROXPROMPT_NET_UDPV4; + break; + case PM3_UDPv6: + prompt_net = PROXPROMPT_NET_UDPV6; + break; + case PM3_NONE: + default: + break; + } + + if (g_conn.send_via_fpc_usart) prompt_dev = PROXPROMPT_DEV_FPC; + else + prompt_dev = PROXPROMPT_DEV_USB; + } else { prompt_dev = PROXPROMPT_DEV_OFFLINE; } @@ -341,7 +362,7 @@ check_script: pm3line_check(check_comm); prompt_ctx = PROXPROMPT_CTX_INTERACTIVE; char prompt[PROXPROMPT_MAX_SIZE] = {0}; - prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net); char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); g_pendingPrompt = true; @@ -391,7 +412,7 @@ check_script: g_printAndLog &= PRINTANDLOG_LOG; } char prompt[PROXPROMPT_MAX_SIZE] = {0}; - prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net); // always filter RL magic separators if not using readline char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_rlmarkers(prompt_filtered, prompt, sizeof(prompt_filtered)); diff --git a/client/src/proxmark3.h b/client/src/proxmark3.h index 71072422a..f58c7950e 100644 --- a/client/src/proxmark3.h +++ b/client/src/proxmark3.h @@ -24,7 +24,7 @@ #define PROXPROMPT_MAX_SIZE 255 -#define PROXPROMPT_COMPOSE "[" "%s%s" "] pm3 --> " +#define PROXPROMPT_COMPOSE "[" "%s%s%s" "] pm3 --> " #define PROXPROMPT_CTX_SCRIPTFILE "|" _RL_GREEN_("script") #define PROXPROMPT_CTX_SCRIPTCMD "|" _RL_GREEN_("script") @@ -35,6 +35,12 @@ #define PROXPROMPT_DEV_FPC _RL_BOLD_GREEN_("fpc") #define PROXPROMPT_DEV_OFFLINE _RL_BOLD_RED_("offline") +#define PROXPROMPT_NET_TCPV4 "|" _RL_BOLD_GREEN_("tcp") +#define PROXPROMPT_NET_UDPV4 "|" _RL_BOLD_GREEN_("udp") +#define PROXPROMPT_NET_TCPV6 "|" _RL_BOLD_GREEN_("tcp v6") +#define PROXPROMPT_NET_UDPV6 "|" _RL_BOLD_GREEN_("udp v6") + + #define PROXHISTORY "history.txt" #define PROXLOG "log_%Y%m%d%H%M%S.txt" #define MAX_NESTED_CMDSCRIPT 10 diff --git a/client/src/uart/ringbuffer.c b/client/src/uart/ringbuffer.c new file mode 100644 index 000000000..345abdf5a --- /dev/null +++ b/client/src/uart/ringbuffer.c @@ -0,0 +1,118 @@ +#include "ringbuffer.h" +#include + +RingBuffer *RingBuf_create(int capacity) { + RingBuffer *buffer = (RingBuffer *)malloc(sizeof(RingBuffer)); + if (!buffer) { + return NULL; + } + + buffer->data = (uint8_t *)calloc(capacity, sizeof(uint8_t)); + if (!buffer->data) { + free(buffer); + return NULL; + } + + buffer->capacity = capacity; + buffer->size = 0; + buffer->front = 0; + buffer->rear = 0; + + return buffer; +} + +inline bool RingBuf_isFull(RingBuffer *buffer) { + return buffer->size == buffer->capacity; +} + +inline bool RingBuf_isEmpty(RingBuffer *buffer) { + return buffer->size == 0; +} + +bool RingBuf_enqueue(RingBuffer *buffer, uint8_t value) { + if (RingBuf_isFull(buffer)) { + return false; + } + + buffer->data[buffer->rear] = value; + buffer->rear = (buffer->rear + 1) % buffer->capacity; + buffer->size++; + return true; +} + +bool RingBuf_dequeue(RingBuffer *buffer, uint8_t *value) { + if (RingBuf_isEmpty(buffer)) { + return false; + } + + *value = buffer->data[buffer->front]; + buffer->front = (buffer->front + 1) % buffer->capacity; + buffer->size--; + return true; +} + +int RingBuf_enqueueBatch(RingBuffer *buffer, const uint8_t *values, int count) { + int processed = 0; + + if (RingBuf_getAvailableSize(buffer) < count) { + count = RingBuf_getAvailableSize(buffer); + } + + for (int i = 0; i < count; i++) { + buffer->data[buffer->rear] = values[i]; + buffer->rear = (buffer->rear + 1) % buffer->capacity; + processed++; + } + + buffer->size += processed; + + return processed; +} + +int RingBuf_dequeueBatch(RingBuffer *buffer, uint8_t *values, int count) { + int processed = 0; + + if (buffer->size < count) { + count = buffer->size; + } + + for (int i = 0; i < count; i++) { + values[i] = buffer->data[buffer->front]; + buffer->front = (buffer->front + 1) % buffer->capacity; + processed++; + } + + buffer->size -= processed; + + return processed; +} + +inline int RingBuf_getUsedSize(RingBuffer *buffer) { + return buffer->size; +} + +inline int RingBuf_getAvailableSize(RingBuffer *buffer) { + return (buffer->capacity) - (buffer->size); +} + +void RingBuf_destroy(RingBuffer *buffer) { + if (buffer != NULL) + free(buffer->data); + free(buffer); +} + +inline int RingBuf_getContinousAvailableSize(RingBuffer *buffer) { + const int availableSize = RingBuf_getAvailableSize(buffer); + const int continousSize = (buffer->capacity) - (buffer->rear); + return (availableSize < continousSize) ? availableSize : continousSize; +} + +inline void RingBuf_postEnqueueBatch(RingBuffer *buffer, int count) { + // no check there + buffer->rear = (buffer->rear + count) % buffer->capacity; + buffer->size += count; +} + +inline uint8_t *RingBuf_getRearPtr(RingBuffer *buffer) { + return buffer->data + buffer->rear; +} diff --git a/client/src/uart/ringbuffer.h b/client/src/uart/ringbuffer.h new file mode 100644 index 000000000..ba29ddd77 --- /dev/null +++ b/client/src/uart/ringbuffer.h @@ -0,0 +1,31 @@ +#ifndef _RINGBUFFER_H_ +#define _RINGBUFFER_H_ + +#include +#include + +typedef struct { + uint8_t *data; + int capacity; + int size; + int front; + int rear; +} RingBuffer; + +RingBuffer *RingBuf_create(int capacity); +bool RingBuf_isFull(RingBuffer *buffer); +bool RingBuf_isEmpty(RingBuffer *buffer); +bool RingBuf_enqueue(RingBuffer *buffer, uint8_t value); +bool RingBuf_dequeue(RingBuffer *buffer, uint8_t *value); +int RingBuf_enqueueBatch(RingBuffer *buffer, const uint8_t *values, int count); +int RingBuf_dequeueBatch(RingBuffer *buffer, uint8_t *values, int count); +int RingBuf_getUsedSize(RingBuffer *buffer); +int RingBuf_getAvailableSize(RingBuffer *buffer); +void RingBuf_destroy(RingBuffer *buffer); + +// for direct write +int RingBuf_getContinousAvailableSize(RingBuffer *buffer); +void RingBuf_postEnqueueBatch(RingBuffer *buffer, int count); +uint8_t *RingBuf_getRearPtr(RingBuffer *buffer); + +#endif diff --git a/client/src/uart/uart.h b/client/src/uart/uart.h index ae3896f73..954fa6685 100644 --- a/client/src/uart/uart.h +++ b/client/src/uart/uart.h @@ -82,4 +82,8 @@ int uart_reconfigure_timeouts(uint32_t value); */ uint32_t uart_get_timeouts(void); +/* Specify the outbound address and port for TCP/UDP connections + */ +bool uart_bind(void *socket, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6); + #endif // _UART_H_ diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index 106c8d209..588e9ae90 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -21,6 +21,7 @@ #define _DEFAULT_SOURCE #include "uart.h" +#include "ringbuffer.h" #include #include @@ -30,9 +31,11 @@ #include #include #include +#include #include #include #include +#include #ifdef HAVE_BLUEZ #include @@ -46,12 +49,16 @@ #ifndef SOL_TCP # define SOL_TCP IPPROTO_TCP #endif +#ifndef SOL_UDP +# define SOL_UDP IPPROTO_UDP +#endif typedef struct termios term_info; typedef struct { int fd; // Serial port file descriptor term_info tiOld; // Terminal info before using the port term_info tiNew; // Terminal info during the transaction + RingBuffer *udpBuffer; } serial_port_unix_t_t; // see pm3_cmd.h @@ -81,10 +88,13 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { return INVALID_SERIAL_PORT; } + sp->udpBuffer = NULL; // init timeouts timeout.tv_usec = UART_FPC_CLIENT_RX_TIMEOUT_MS * 1000; + g_conn.send_via_local_ip = false; + g_conn.send_via_ip = PM3_NONE; - char *prefix = strdup(pcPortName); + char *prefix = str_dup(pcPortName); if (prefix == NULL) { PrintAndLogEx(ERR, "error: string duplication"); free(sp); @@ -102,35 +112,140 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { struct addrinfo *addr = NULL, *rp; - char *addrstr = strdup(pcPortName + 4); - if (addrstr == NULL) { + char *addrPortStr = str_dup(pcPortName + 4); + char *addrstr = addrPortStr; + const char *portstr; + if (addrPortStr == NULL) { PrintAndLogEx(ERR, "error: string duplication"); free(sp); return INVALID_SERIAL_PORT; } - timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; - char *colon = strrchr(addrstr, ':'); - const char *portstr; - if (colon) { - portstr = colon + 1; - *colon = '\0'; - } else { + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + + // find the start of the address + char *endBracket = strrchr(addrPortStr, ']'); + if (addrPortStr[0] == '[') { + addrstr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + + // assume v4 + g_conn.send_via_ip = PM3_TCPv4; + + // find the port + char *lColon = strchr(addrPortStr, ':'); + char *rColon = strrchr(addrPortStr, ':'); + if (rColon == NULL) { + // no colon + // "tcp:", "tcp:[]" portstr = "18888"; + } else if (lColon == rColon) { + // only one colon + // "tcp::", "tcp:[]:" + portstr = rColon + 1; + } else { + // two or more colon, IPv6 address + // "tcp:[]:" + // "tcp:", "tcp:[]" + if (endBracket != NULL && rColon == endBracket + 1) { + portstr = rColon + 1; + } else { + portstr = "18888"; + } + g_conn.send_via_ip = PM3_TCPv6; + } + + // handle the end of the address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; } struct addrinfo info; memset(&info, 0, sizeof(info)); + info.ai_family = PF_UNSPEC; info.ai_socktype = SOCK_STREAM; + if ((strstr(addrstr, "localhost") != NULL) || + (strstr(addrstr, "127.0.0.1") != NULL) || + (strstr(addrstr, "::1") != NULL)) { + g_conn.send_via_local_ip = true; + } + int s = getaddrinfo(addrstr, portstr, &info, &addr); if (s != 0) { PrintAndLogEx(ERR, "error: getaddrinfo: %s", gai_strerror(s)); freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); free(sp); return INVALID_SERIAL_PORT; } @@ -142,6 +257,15 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (sfd == -1) continue; + if (!uart_bind(&sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. errno: %d", errno); + close(sfd); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) break; @@ -149,7 +273,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); if (rp == NULL) { /* No address succeeded */ PrintAndLogEx(ERR, "error: Could not connect"); @@ -165,9 +289,194 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { free(sp); return INVALID_SERIAL_PORT; } + return sp; } + if (memcmp(prefix, "udp:", 4) == 0) { + free(prefix); + + if (strlen(pcPortName) <= 4) { + free(sp); + return INVALID_SERIAL_PORT; + } + + struct addrinfo *addr = NULL, *rp; + + char *addrPortStr = str_dup(pcPortName + 4); + char *addrstr = addrPortStr; + const char *portstr; + if (addrPortStr == NULL) { + PrintAndLogEx(ERR, "error: string duplication"); + free(sp); + return INVALID_SERIAL_PORT; + } + + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; + + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + + // find the start of the address + char *endBracket = strrchr(addrPortStr, ']'); + if (addrPortStr[0] == '[') { + addrstr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // Assume v4 + g_conn.send_via_ip = PM3_UDPv4; + + // find the port + char *lColon = strchr(addrPortStr, ':'); + char *rColon = strrchr(addrPortStr, ':'); + if (rColon == NULL) { + // no colon + // "udp:", "udp:[]" + portstr = "18888"; + } else if (lColon == rColon) { + // only one colon + // "udp::", "udp:[]:" + portstr = rColon + 1; + } else { + // two or more colon, IPv6 address + // "udp:[]:" + // "udp:", "udp:[]" + if (endBracket != NULL && rColon == endBracket + 1) { + portstr = rColon + 1; + } else { + portstr = "18888"; + } + g_conn.send_via_ip = PM3_UDPv6; + } + + // handle the end of the address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + struct addrinfo info; + + memset(&info, 0, sizeof(info)); + + info.ai_family = PF_UNSPEC; + info.ai_socktype = SOCK_DGRAM; + + if ((strstr(addrstr, "localhost") != NULL) || + (strstr(addrstr, "127.0.0.1") != NULL) || + (strstr(addrstr, "::1") != NULL)) { + g_conn.send_via_local_ip = true; + } + + int s = getaddrinfo(addrstr, portstr, &info, &addr); + if (s != 0) { + PrintAndLogEx(ERR, "error: getaddrinfo: %s", gai_strerror(s)); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + int sfd; + for (rp = addr; rp != NULL; rp = rp->ai_next) { + sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (sfd == -1) + continue; + + if (!uart_bind(&sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. errno: %d", errno); + close(sfd); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + break; + + close(sfd); + } + + freeaddrinfo(addr); + free(addrPortStr); + + if (rp == NULL) { /* No address succeeded */ + PrintAndLogEx(ERR, "error: Could not connect"); + free(sp); + return INVALID_SERIAL_PORT; + } + + sp->fd = sfd; + sp->udpBuffer = RingBuf_create(MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30); + return sp; + } + + if (memcmp(prefix, "bt:", 3) == 0) { free(prefix); @@ -211,6 +520,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } sp->fd = sfd; + + g_conn.send_via_ip = PM3_NONE; return sp; #else // HAVE_BLUEZ PrintAndLogEx(ERR, "Sorry, this client doesn't support native Bluetooth addresses"); @@ -231,7 +542,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } // we must use max timeout! - timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; size_t servernameLen = (strlen(pcPortName) - 7) + 1; char serverNameBuf[servernameLen]; @@ -262,6 +573,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } sp->fd = localsocket; + + g_conn.send_via_ip = PM3_NONE; return sp; } @@ -331,6 +644,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } } g_conn.uart_speed = uart_get_speed(sp); + g_conn.send_via_ip = PM3_NONE; return sp; } @@ -351,6 +665,7 @@ void uart_close(const serial_port sp) { //silent error message as it can be called from uart_open failing modes, e.g. when waiting for port to appear //PrintAndLogEx(ERR, "UART error while closing port"); } + RingBuf_destroy(spu->udpBuffer); close(spu->fd); free(sp); } @@ -359,6 +674,7 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin uint32_t byteCount; // FIONREAD returns size on 32b fd_set rfds; struct timeval tv; + const serial_port_unix_t_t *spu = (serial_port_unix_t_t *)sp; if (newtimeout_pending) { timeout.tv_usec = newtimeout_value * 1000; @@ -367,11 +683,30 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin // Reset the output count *pszRxLen = 0; do { + int res; + if (spu->udpBuffer != NULL) { + // for UDP connection, try to use the data from the buffer + + byteCount = RingBuf_getAvailableSize(spu->udpBuffer); + // Cap the number of bytes, so we don't overrun the buffer + if (pszMaxRxLen - (*pszRxLen) < byteCount) { +// PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); + byteCount = pszMaxRxLen - (*pszRxLen); + } + res = RingBuf_dequeueBatch(spu->udpBuffer, pbtRx + (*pszRxLen), byteCount); + *pszRxLen += res; + + if (*pszRxLen == pszMaxRxLen) { + // We have all the data we wanted. + return PM3_SUCCESS; + } + } + // Reset file descriptor FD_ZERO(&rfds); - FD_SET(((serial_port_unix_t_t *)sp)->fd, &rfds); + FD_SET(spu->fd, &rfds); tv = timeout; - int res = select(((serial_port_unix_t_t *)sp)->fd + 1, &rfds, NULL, NULL, &tv); + res = select(spu->fd + 1, &rfds, NULL, NULL, &tv); // Read error if (res < 0) { @@ -390,10 +725,31 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } // Retrieve the count of the incoming bytes - res = ioctl(((serial_port_unix_t_t *)sp)->fd, FIONREAD, &byteCount); + res = ioctl(spu->fd, FIONREAD, &byteCount); // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); if (res < 0) return PM3_ENOTTY; + // For UDP connection, put the incoming data into the buffer and handle them in the next round + if (spu->udpBuffer != NULL) { + if (RingBuf_getContinousAvailableSize(spu->udpBuffer) >= byteCount) { + // write to the buffer directly + res = read(spu->fd, RingBuf_getRearPtr(spu->udpBuffer), RingBuf_getAvailableSize(spu->udpBuffer)); + if (res >= 0) { + RingBuf_postEnqueueBatch(spu->udpBuffer, res); + } + } else { + // use transit buffer + uint8_t transitBuf[MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30]; + res = read(spu->fd, transitBuf, RingBuf_getAvailableSize(spu->udpBuffer)); + RingBuf_enqueueBatch(spu->udpBuffer, transitBuf, res); + } + // Stop if the OS has some troubles reading the data + if (res < 0) { + return PM3_EIO; + } + continue; + } + // Cap the number of bytes, so we don't overrun the buffer if (pszMaxRxLen - (*pszRxLen) < byteCount) { // PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); @@ -401,7 +757,7 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } // There is something available, read the data - res = read(((serial_port_unix_t_t *)sp)->fd, pbtRx + (*pszRxLen), byteCount); + res = read(spu->fd, pbtRx + (*pszRxLen), byteCount); // Stop if the OS has some troubles reading the data if (res <= 0) { @@ -423,13 +779,14 @@ int uart_send(const serial_port sp, const uint8_t *pbtTx, const uint32_t len) { uint32_t pos = 0; fd_set rfds; struct timeval tv; + const serial_port_unix_t_t *spu = (serial_port_unix_t_t *)sp; while (pos < len) { // Reset file descriptor FD_ZERO(&rfds); - FD_SET(((serial_port_unix_t_t *)sp)->fd, &rfds); + FD_SET(spu->fd, &rfds); tv = timeout; - int res = select(((serial_port_unix_t_t *)sp)->fd + 1, NULL, &rfds, NULL, &tv); + int res = select(spu->fd + 1, NULL, &rfds, NULL, &tv); // Write error if (res < 0) { @@ -444,7 +801,7 @@ int uart_send(const serial_port sp, const uint8_t *pbtTx, const uint32_t len) { } // Send away the bytes - res = write(((serial_port_unix_t_t *)sp)->fd, pbtTx + pos, len - pos); + res = write(spu->fd, pbtTx + pos, len - pos); // Stop if the OS has some troubles sending the data if (res <= 0) @@ -638,4 +995,37 @@ uint32_t uart_get_speed(const serial_port sp) { }; return uiPortSpeed; } + +bool uart_bind(void *socket, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { + if (bindAddrStr == NULL && bindPortStr == NULL) + return true; // no need to bind + + struct sockaddr_storage bindSockaddr; + memset(&bindSockaddr, 0, sizeof(bindSockaddr)); + int bindPort = 0; // 0: port unspecified + if (bindPortStr != NULL) + bindPort = atoi(bindPortStr); + + if (!isBindingIPv6) { + struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; + bindSockaddr4->sin_family = AF_INET; + bindSockaddr4->sin_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr4->sin_addr.s_addr = INADDR_ANY; + else + bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } else { + struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; + bindSockaddr6->sin6_family = AF_INET6; + bindSockaddr6->sin6_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr6->sin6_addr = in6addr_any; + else + inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } + + int res = bind(*(int *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); + return (res >= 0); +} + #endif diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index 541804a24..43299ca5a 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "uart.h" +#include "ringbuffer.h" #include #include @@ -33,16 +34,17 @@ #include typedef struct { - HANDLE hPort; // Serial port handle - DCB dcb; // Device control settings - COMMTIMEOUTS ct; // Serial port time-out configuration - SOCKET hSocket; // Socket handle + HANDLE hPort; // Serial port handle + DCB dcb; // Device control settings + COMMTIMEOUTS ct; // Serial port time-out configuration + SOCKET hSocket; // Socket handle + RingBuffer *udpBuffer; // Buffer for UDP } serial_port_windows_t; // this is for TCP connection struct timeval timeout = { .tv_sec = 0, // 0 second - .tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000 + .tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000 }; uint32_t newtimeout_value = 0; @@ -63,8 +65,7 @@ static int uart_reconfigure_timeouts_polling(serial_port sp) { return PM3_SUCCESS; newtimeout_pending = false; - serial_port_windows_t *spw; - spw = (serial_port_windows_t *)sp; + serial_port_windows_t *spw = (serial_port_windows_t *)sp; spw->ct.ReadIntervalTimeout = newtimeout_value; spw->ct.ReadTotalTimeoutMultiplier = 0; spw->ct.ReadTotalTimeoutConstant = newtimeout_value; @@ -90,7 +91,11 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { return INVALID_SERIAL_PORT; } - char *prefix = strdup(pcPortName); + sp->udpBuffer = NULL; + g_conn.send_via_local_ip = false; + g_conn.send_via_ip = PM3_NONE; + + char *prefix = str_dup(pcPortName); if (prefix == NULL) { PrintAndLogEx(ERR, "error: string duplication"); free(sp); @@ -109,22 +114,119 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { struct addrinfo *addr = NULL, *rp; - char *addrstr = strdup(pcPortName + 4); - if (addrstr == NULL) { + char *addrPortStr = str_dup(pcPortName + 4); + char *addrstr = addrPortStr; + const char *portstr; + if (addrPortStr == NULL) { PrintAndLogEx(ERR, "error: string duplication"); free(sp); return INVALID_SERIAL_PORT; } - timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; - char *colon = strrchr(addrstr, ':'); - const char *portstr; - if (colon) { - portstr = colon + 1; - *colon = '\0'; - } else { + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + + // find the start of the address + char *endBracket = strrchr(addrPortStr, ']'); + if (addrPortStr[0] == '[') { + addrstr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // Assume v4 + g_conn.send_via_ip = PM3_TCPv4; + + // find the port + char *lColon = strchr(addrPortStr, ':'); + char *rColon = strrchr(addrPortStr, ':'); + if (rColon == NULL) { + // no colon + // "tcp:", "tcp:[]" portstr = "18888"; + } else if (lColon == rColon) { + // only one colon + // "tcp::", "tcp:[]:" + portstr = rColon + 1; + } else { + // two or more colon, IPv6 address + // "tcp:[]:" + // "tcp:", "tcp:[]" + if (endBracket != NULL && rColon == endBracket + 1) { + portstr = rColon + 1; + } else { + portstr = "18888"; + } + g_conn.send_via_ip = PM3_TCPv6; + } + + // handle the end of the address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; } WSADATA wsaData; @@ -134,19 +236,27 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { PrintAndLogEx(ERR, "error: WSAStartup failed with error: %d", iResult); + free(addrPortStr); free(sp); return INVALID_SERIAL_PORT; } memset(&info, 0, sizeof(info)); + info.ai_family = AF_UNSPEC; info.ai_socktype = SOCK_STREAM; info.ai_protocol = IPPROTO_TCP; + if ((strstr(addrstr, "localhost") != NULL) || + (strstr(addrstr, "127.0.0.1") != NULL) || + (strstr(addrstr, "::1") != NULL)) { + g_conn.send_via_local_ip = true; + } + int s = getaddrinfo(addrstr, portstr, &info, &addr); if (s != 0) { - PrintAndLogEx(ERR, "error: getaddrinfo: %s", gai_strerror(s)); + PrintAndLogEx(ERR, "error: getaddrinfo: %d: %s", s, gai_strerror(s)); freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); free(sp); WSACleanup(); return INVALID_SERIAL_PORT; @@ -159,6 +269,17 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { if (hSocket == INVALID_SOCKET) continue; + if (!uart_bind(&hSocket, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. error: %u", WSAGetLastError()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) break; @@ -167,7 +288,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); if (rp == NULL) { /* No address succeeded */ PrintAndLogEx(ERR, "error: Could not connect"); @@ -189,6 +310,205 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { return sp; } + if (memcmp(prefix, "udp:", 4) == 0) { + free(prefix); + + if (strlen(pcPortName) <= 4) { + PrintAndLogEx(ERR, "error: tcp port name length too short"); + free(sp); + return INVALID_SERIAL_PORT; + } + + struct addrinfo *addr = NULL, *rp; + + char *addrPortStr = str_dup(pcPortName + 4); + char *addrstr = addrPortStr; + const char *portstr; + if (addrPortStr == NULL) { + PrintAndLogEx(ERR, "error: string duplication"); + free(sp); + return INVALID_SERIAL_PORT; + } + + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; + + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + char *bindAddrStr = NULL; + char *bindPortStr = NULL; + bool isBindingIPv6 = false; // Assume v4 + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; + bindAddrStr = bindAddrPortStr; + + // find the start of the bind address + char *endBracket = strrchr(bindAddrPortStr, ']'); + if (bindAddrPortStr[0] == '[') { + bindAddrStr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // find the bind port + char *lColon = strchr(bindAddrPortStr, ':'); + char *rColon = strrchr(bindAddrPortStr, ':'); + if (rColon == NULL) { + // no colon + // ",bind=", ",bind=[]" + bindPortStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ",bind=:", ",bind=[]:" + bindPortStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // ",bind=[]:" + // ",bind=", ",bind=[]" + if (endBracket != NULL && rColon == endBracket + 1) { + bindPortStr = rColon + 1; + } else { + bindPortStr = NULL; + } + isBindingIPv6 = true; + } + + // handle the end of the bind address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) + bindAddrStr = NULL; + if (bindPortStr != NULL && strlen(bindPortStr) == 0) + bindPortStr = NULL; + } + + // find the start of the address + char *endBracket = strrchr(addrPortStr, ']'); + if (addrPortStr[0] == '[') { + addrstr += 1; + if (endBracket == NULL) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched"); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + } + + // Assume v4 + g_conn.send_via_ip = PM3_UDPv4; + + // find the port + char *lColon = strchr(addrPortStr, ':'); + char *rColon = strrchr(addrPortStr, ':'); + if (rColon == NULL) { + // no colon + // "udp:", "udp:[]" + portstr = "18888"; + } else if (lColon == rColon) { + // only one colon + // "udp::", "udp:[]:" + portstr = rColon + 1; + } else { + // two or more colon, IPv6 address + // "udp:[]:" + // "udp:", "udp:[]" + if (endBracket != NULL && rColon == endBracket + 1) { + portstr = rColon + 1; + } else { + portstr = "18888"; + } + g_conn.send_via_ip = PM3_UDPv6; + } + + // handle the end of the address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + WSADATA wsaData; + struct addrinfo info; + int iResult; + + iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (iResult != 0) { + PrintAndLogEx(ERR, "error: WSAStartup failed with error: %d", iResult); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + memset(&info, 0, sizeof(info)); + info.ai_family = AF_UNSPEC; + info.ai_socktype = SOCK_DGRAM; + info.ai_protocol = IPPROTO_UDP; + + if ((strstr(addrstr, "localhost") != NULL) || + (strstr(addrstr, "127.0.0.1") != NULL) || + (strstr(addrstr, "::1") != NULL)) { + g_conn.send_via_local_ip = true; + } + + int s = getaddrinfo(addrstr, portstr, &info, &addr); + if (s != 0) { + PrintAndLogEx(ERR, "error: getaddrinfo: %d: %s", s, gai_strerror(s)); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + + SOCKET hSocket = INVALID_SOCKET; + for (rp = addr; rp != NULL; rp = rp->ai_next) { + hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + + if (hSocket == INVALID_SOCKET) + continue; + + if (!uart_bind(&hSocket, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. error: %u", WSAGetLastError()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + + if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) + break; + + closesocket(hSocket); + hSocket = INVALID_SOCKET; + } + + freeaddrinfo(addr); + free(addrPortStr); + + if (rp == NULL) { /* No address succeeded */ + PrintAndLogEx(ERR, "error: Could not connect"); + WSACleanup(); + free(sp); + return INVALID_SERIAL_PORT; + } + + sp->hSocket = hSocket; + sp->udpBuffer = RingBuf_create(MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30); + return sp; + } + // Copy the input "com?" to "\\.\COM?" format snprintf(acPortName, sizeof(acPortName), "\\\\.\\%s", pcPortName); _strupr(acPortName); @@ -231,6 +551,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } } g_conn.uart_speed = uart_get_speed(sp); + g_conn.send_via_ip = PM3_NONE; return sp; } @@ -241,13 +562,14 @@ void uart_close(const serial_port sp) { closesocket(spw->hSocket); WSACleanup(); } + RingBuf_destroy(spw->udpBuffer); if (spw->hPort != INVALID_HANDLE_VALUE) CloseHandle(spw->hPort); free(sp); } bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { - serial_port_windows_t *spw; + serial_port_windows_t *spw = (serial_port_windows_t *)sp; // Set port speed (Input and Output) switch (uiPortSpeed) { @@ -265,7 +587,6 @@ bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { return false; }; - spw = (serial_port_windows_t *)sp; spw->dcb.BaudRate = uiPortSpeed; bool result = SetCommState(spw->hPort, &spw->dcb); PurgeComm(spw->hPort, PURGE_RXABORT | PURGE_RXCLEAR); @@ -284,11 +605,12 @@ uint32_t uart_get_speed(const serial_port sp) { } int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uint32_t *pszRxLen) { - serial_port_windows_t *spw = (serial_port_windows_t *)sp; - if (spw->hSocket == INVALID_SOCKET) { // serial port + const serial_port_windows_t *spw = (serial_port_windows_t *)sp; + if (spw->hSocket == INVALID_SOCKET) { + // serial port uart_reconfigure_timeouts_polling(sp); - int res = ReadFile(((serial_port_windows_t *)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); + int res = ReadFile(spw->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); if (res) return PM3_SUCCESS; @@ -299,7 +621,8 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } return PM3_ENOTTY; - } else { // TCP + } else { + // TCP or UDP uint32_t byteCount; // FIONREAD returns size on 32b fd_set rfds; struct timeval tv; @@ -311,12 +634,31 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin // Reset the output count *pszRxLen = 0; do { + int res; + if (spw->udpBuffer != NULL) { + // for UDP connection, try to use the data from the buffer + + byteCount = RingBuf_getAvailableSize(spw->udpBuffer); + // Cap the number of bytes, so we don't overrun the buffer + if (pszMaxRxLen - (*pszRxLen) < byteCount) { + // PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); + byteCount = pszMaxRxLen - (*pszRxLen); + } + res = RingBuf_dequeueBatch(spw->udpBuffer, pbtRx + (*pszRxLen), byteCount); + *pszRxLen += res; + + if (*pszRxLen == pszMaxRxLen) { + // We have all the data we wanted. + return PM3_SUCCESS; + } + } + // Reset file descriptor FD_ZERO(&rfds); FD_SET(spw->hSocket, &rfds); tv = timeout; // the first argument nfds is ignored in Windows - int res = select(0, &rfds, NULL, NULL, &tv); + res = select(0, &rfds, NULL, NULL, &tv); // Read error if (res == SOCKET_ERROR) { @@ -336,9 +678,30 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin // Retrieve the count of the incoming bytes res = ioctlsocket(spw->hSocket, FIONREAD, (u_long *)&byteCount); - // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); + // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); if (res == SOCKET_ERROR) return PM3_ENOTTY; + // For UDP connection, put the incoming data into the buffer and handle them in the next round + if (spw->udpBuffer != NULL) { + if (RingBuf_getContinousAvailableSize(spw->udpBuffer) >= byteCount) { + // write to the buffer directly + res = recv(spw->hSocket, (char *)RingBuf_getRearPtr(spw->udpBuffer), RingBuf_getAvailableSize(spw->udpBuffer), 0); + if (res >= 0) { + RingBuf_postEnqueueBatch(spw->udpBuffer, res); + } + } else { + // use transit buffer + uint8_t transitBuf[MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30]; + res = recv(spw->hSocket, (char *)transitBuf, RingBuf_getAvailableSize(spw->udpBuffer), 0); + RingBuf_enqueueBatch(spw->udpBuffer, transitBuf, res); + } + // Stop if the OS has some troubles reading the data + if (res < 0) { + return PM3_EIO; + } + continue; + } + // Cap the number of bytes, so we don't overrun the buffer if (pszMaxRxLen - (*pszRxLen) < byteCount) { // PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); @@ -366,10 +729,10 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { - serial_port_windows_t *spw = (serial_port_windows_t *)sp; + const serial_port_windows_t *spw = (serial_port_windows_t *)sp; if (spw->hSocket == INVALID_SOCKET) { // serial port DWORD txlen = 0; - int res = WriteFile(((serial_port_windows_t *)sp)->hPort, p_tx, len, &txlen, NULL); + int res = WriteFile(spw->hPort, p_tx, len, &txlen, NULL); if (res) return PM3_SUCCESS; @@ -417,4 +780,36 @@ int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { } } +bool uart_bind(void *socket, char *bindAddrStr, char *bindPortStr, bool isBindingIPv6) { + if (bindAddrStr == NULL && bindPortStr == NULL) + return true; // no need to bind + + struct sockaddr_storage bindSockaddr; + memset(&bindSockaddr, 0, sizeof(bindSockaddr)); + int bindPort = 0; // 0: port unspecified + if (bindPortStr != NULL) + bindPort = atoi(bindPortStr); + + if (!isBindingIPv6) { + struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; + bindSockaddr4->sin_family = AF_INET; + bindSockaddr4->sin_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr4->sin_addr.s_addr = INADDR_ANY; + else + bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } else { + struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; + bindSockaddr6->sin6_family = AF_INET6; + bindSockaddr6->sin6_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr6->sin6_addr = in6addr_any; + else + inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } + + int res = bind(*(SOCKET *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); + return (res >= 0); +} + #endif diff --git a/client/src/ui.h b/client/src/ui.h index 4042550b3..4cfd75660 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -62,6 +62,7 @@ typedef struct { uint16_t client_exe_delay; char *history_path; pm3_device_t *current_device; + uint32_t timeout; } session_arg_t; extern session_arg_t g_session; diff --git a/client/src/util.c b/client/src/util.c index e531b51ea..205c2d32f 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -759,25 +759,6 @@ float param_getfloat(const char *line, int paramnum, float deflt) { return deflt; } -int param_gethex(const char *line, int paramnum, uint8_t *data, int hexcnt) { - int bg, en, i; - uint32_t temp; - - if (hexcnt & 1) return 1; - - if (param_getptr(line, &bg, &en, paramnum)) return 1; - - if (en - bg + 1 != hexcnt) return 1; - - for (i = 0; i < hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; - - sscanf((char[]) {line[bg + i], line[bg + i + 1], 0}, "%X", &temp); - data[i / 2] = temp & 0xff; - } - - return 0; -} int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) { int bg, en, i; uint32_t temp; @@ -785,8 +766,11 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) if (param_getptr(line, &bg, &en, paramnum)) return 1; *hexcnt = en - bg + 1; - if (*hexcnt % 2) //error if not complete hex bytes + + // error if not complete hex bytes + if (*hexcnt & 1) { return 1; + } for (i = 0; i < *hexcnt; i += 2) { if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; @@ -876,7 +860,7 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda if (strlen(buf) > 0) { uint32_t temp = 0; - sscanf(buf, "%d", &temp); + sscanf(buf, "%u", &temp); data[*datalen] = (uint8_t)(temp & 0xff); *buf = 0; (*datalen)++; @@ -944,11 +928,11 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { return count; } -// convert hex to human readable binary string +// convert hexstring to human readable binary string int hextobinstring(char *target, char *source) { return hextobinstring_n(target, source, strlen(source)); } - +// convert hexstring to human readable binary string int hextobinstring_n(char *target, char *source, int sourcelen) { int length = hextobinarray_n(target, source, sourcelen); if (length == 0) { @@ -958,6 +942,23 @@ int hextobinstring_n(char *target, char *source, int sourcelen) { return length; } +// convert bytes to binary string +void byte_2_binstr(char *target, const uint8_t *source, size_t sourcelen) { + //uint8_t *p = *source; + for (int i = 0 ; i < sourcelen; ++i) { + uint8_t b = *(source++); + *(target++) = ((b >> 7) & 0x1) + '0'; + *(target++) = ((b >> 6) & 0x1) + '0'; + *(target++) = ((b >> 5) & 0x1) + '0'; + *(target++) = ((b >> 4) & 0x1) + '0'; + *(target++) = ((b >> 3) & 0x1) + '0'; + *(target++) = ((b >> 2) & 0x1) + '0'; + *(target++) = ((b >> 1) & 0x1) + '0'; + *(target++) = (b & 0x1) + '0'; + } + *target = '\0'; +} + // convert binary array of 0x00/0x01 values to hex // return number of bits converted int binarraytohex(char *target, const size_t targetlen, const char *source, size_t srclen) { @@ -1007,8 +1008,9 @@ int binarraytohex(char *target, const size_t targetlen, const char *source, size // convert binary array to human readable binary void binarraytobinstring(char *target, char *source, int length) { - for (int i = 0 ; i < length; ++i) + for (int i = 0 ; i < length; ++i) { *(target++) = *(source++) + '0'; + } *target = '\0'; } @@ -1030,6 +1032,36 @@ int binstring2binarray(uint8_t *target, char *source, int length) { return count; } +void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src) { + size_t binlen = strlen(src); + if (binlen == 0) { + *targetlen = 0; + return; + } + + // Calculate padding needed + size_t padding = (8 - (binlen % 8)) % 8; + + // Determine the size of the hexadecimal array + *targetlen = (binlen + padding) / 8; + + uint8_t b = 0; + size_t bit_cnt = padding; + size_t idx = 0; + + // Process binary string + for (size_t i = 0; i < binlen; ++i) { + b = (b << 1) | (src[i] == '1'); + ++bit_cnt; + + if (bit_cnt == 8) { + target[idx++] = b; + b = 0; + bit_cnt = 0; + } + } +} + // return parity bit required to match type uint8_t GetParity(const uint8_t *bits, uint8_t type, int length) { int x; @@ -1166,6 +1198,16 @@ char *str_ndup(const char *src, size_t len) { return dest; } +size_t str_nlen(const char *src, size_t maxlen) { + size_t len = 0; + if (src) { + for (char c = *src; (len < maxlen && c != '\0'); c = *++src) { + len++; + } + } + return len; +} + /** * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers * one nibble at a time. diff --git a/client/src/util.h b/client/src/util.h index cfaf97167..1d6df987d 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -106,7 +106,6 @@ uint64_t param_get64ex(const char *line, int paramnum, int deflt, int base); float param_getfloat(const char *line, int paramnum, float deflt); uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination); 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); @@ -122,6 +121,9 @@ int binarraytohex(char *target, const size_t targetlen, const char *source, size void binarraytobinstring(char *target, char *source, int length); int binstring2binarray(uint8_t *target, char *source, int length); +void byte_2_binstr(char *target, const uint8_t *source, size_t sourcelen); +void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src); + uint8_t GetParity(const uint8_t *bits, uint8_t type, int length); void wiegand_add_parity(uint8_t *target, uint8_t *source, uint8_t length); void wiegand_add_parity_swapped(uint8_t *target, uint8_t *source, uint8_t length); @@ -145,6 +147,7 @@ void strcleanrn(char *buf, size_t len); void strcreplace(char *buf, size_t len, char from, char to); char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); +size_t str_nlen(const char *src, size_t maxlen); 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, const uint8_t *arr, int arrlen); diff --git a/common/cardhelper.c b/common/cardhelper.c index 7523c9a72..fc4fe2503 100644 --- a/common/cardhelper.c +++ b/common/cardhelper.c @@ -22,6 +22,7 @@ #include "cmdsmartcard.h" #include "ui.h" #include "util.h" +#include "commonutil.h" #define CARD_INS_DECRYPT 0x01 #define CARD_INS_ENCRYPT 0x02 @@ -60,39 +61,48 @@ bool IsCardHelperPresent(bool verbose) { bool IsHIDSamPresent(bool verbose) { if (IfPm3Smartcard() == false) { + PrintAndLogEx(WARNING, "Proxmark3 does not have SMARTCARD support enabled, exiting"); return false; } // detect SAM smart_card_atr_t card; smart_select(verbose, &card); - if (!card.atr_len) { + if (card.atr_len == 0) { PrintAndLogEx(ERR, "Can't get ATR from a smart card"); return false; } // SAM identification - uint8_t sam_atr[] = {0x3B, 0x95, 0x96, 0x80, 0xB1, 0xFE, 0x55, 0x1F, 0xC7, 0x47, 0x72, 0x61, 0x63, 0x65, 0x13}; - if (memcmp(card.atr, sam_atr, card.atr_len) < 0) { - - uint8_t sam_atr2[] = {0x3b, 0x90, 0x96, 0x91, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0xd4}; - if (memcmp(card.atr, sam_atr2, card.atr_len) < 0) { - if (verbose) { - PrintAndLogEx(SUCCESS, "Not detecting a SAM"); - } - return false; + smart_card_atr_t supported[] = { + {15, {0x3B, 0x95, 0x96, 0x80, 0xB1, 0xFE, 0x55, 0x1F, 0xC7, 0x47, 0x72, 0x61, 0x63, 0x65, 0x13}}, + {11, {0x3b, 0x90, 0x96, 0x91, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0xd4}}, + }; + bool found = false; + for (int i = 0; i < ARRAYLEN(supported); i++) { + if ((card.atr_len == supported[i].atr_len) && + (memcmp(card.atr, supported[i].atr, supported[i].atr_len) == 0)) { + found = true; + break; } } - - // Suspect some SAMs has version name in their ATR - uint8_t T0 = card.atr[1]; - uint8_t K = T0 & 0x0F; - if (K > 4 && verbose) { - if (byte_strstr(card.atr, card.atr_len, (const uint8_t *)"Grace", 5) > -1) { - PrintAndLogEx(SUCCESS, "SAM (Grace) detected"); - } else if (byte_strstr(card.atr, card.atr_len, (const uint8_t *)"Hopper", 6) > -1) { - PrintAndLogEx(SUCCESS, "SAM (Hopper) detected"); + if (found == false) { + if (verbose) { + PrintAndLogEx(SUCCESS, "Not detecting a SAM"); } + return false; + } + + // Suspect some SAMs has version name in the historical bytes + uint8_t T0 = card.atr[1]; + uint8_t K = T0 & 0x0F; // Number of historical bytes + if (K > 0 && (K < (card.atr_len - 3)) && verbose) { + // Last byte of ATR is CRC and before that we have K bytes of + // "historical bytes". + // By construction K can't go above 15 + char sam_name[16] = {0}; + memcpy(sam_name, &card.atr[card.atr_len - 1 - K], K); + PrintAndLogEx(SUCCESS, "SAM (%s) detected", sam_name); } return true; } diff --git a/common/commonutil.c b/common/commonutil.c index 58e694d76..c9ae5981c 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -485,3 +485,45 @@ void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest) { dest[i] = src[(src_len - 1) - i]; } } + +static int hexchar_to_dec(char ch) { + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } + if (ch >= 'a' && ch <= 'f') { + return ch - 'a' + 10; + } + if (ch >= 'A' && ch <= 'F') { + return ch - 'A' + 10; + } + return -1; +} + +// no spaces allowed for input hex string +bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n) { + + size_t hexstr_len = strlen(hexstr); + if (hexstr_len & 1) { + return false; + } + + *n = (hexstr_len >> 1); + + for (int i = 0; i < *n; i++) { + + char c1 = *hexstr++; + char c2 = *hexstr++; + + if (c1 == '\0' || c2 == '\0') { + return false; + } + + int b = (hexchar_to_dec(c1) << 4) | hexchar_to_dec(c2); + if (b < 0) { + // Error: invalid hex character + return false; + } + d[i] = (uint8_t) b; + } + return true; +} diff --git a/common/commonutil.h b/common/commonutil.h index aef8240a6..f057486a2 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -109,4 +109,5 @@ uint16_t get_sw(const uint8_t *d, uint16_t n); void reverse_array(uint8_t *d, size_t n); void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest); +bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n); #endif diff --git a/common_arm/usb_cdc.c b/common_arm/usb_cdc.c index cb2d0c64a..4787ce112 100644 --- a/common_arm/usb_cdc.c +++ b/common_arm/usb_cdc.c @@ -777,7 +777,7 @@ int usb_write(const uint8_t *data, const size_t len) { } UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); - while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) {}; + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY)) {}; while (length) { // Send next chunk @@ -797,7 +797,7 @@ int usb_write(const uint8_t *data, const size_t len) { while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP) {}; UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); - while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) {}; + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY)) {}; } // Wait for the end of transfer @@ -810,7 +810,7 @@ int usb_write(const uint8_t *data, const size_t len) { if (len % AT91C_EP_IN_SIZE == 0) { - + // like AT91F_USB_SendZlp(), in non ping-pong mode UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {}; @@ -869,6 +869,8 @@ void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length) { //*---------------------------------------------------------------------------- void AT91F_USB_SendZlp(AT91PS_UDP pudp) { UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY); + // for non ping-pong operation, wait until the FIFO is released + // the flag for FIFO released is AT91C_UDP_TXCOMP rather than AT91C_UDP_TXPKTRDY while (!(pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP)) {}; UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); while (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) {}; diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index a859f4392..0dd86c2a0 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -79,7 +79,7 @@ Read iCLASS Block Options --- -k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer + --blk The block number to read as an integer --ki Key index to select key from memory 'hf iclass managekeys' --credit key is assumed to be the credit key --elite elite computations applied to key @@ -94,7 +94,7 @@ Write to iCLASS Block Options --- -k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer + --blk The block number to read as an integer -d, --data data to write as 16 hex symbols --ki Key index to select key from memory 'hf iclass managekeys' --credit key is assumed to be the credit key @@ -140,7 +140,7 @@ Decrypt iCLASS Block / file ``` Options --- --f, --file filename of dumpfile +-f, --file Specify a filename for dump file -d, --data 3DES encrypted data -k, --key 3DES transport key -v, --verbose verbose output @@ -153,7 +153,7 @@ Load iCLASS dump into memory for simulation ``` Options --- --f, --file filename of dump +-f, --file Specify a filename for dump file --json load JSON type dump --eml load EML type dump @@ -261,8 +261,8 @@ Dump MIFARE Classic card contents ``` Options: --- --f, --file filename of dump --k, --keys filename of keys +-f, --file Specify a filename for dump file +-k, --keys Specify a filename for keys file --mini MIFARE Classic Mini / S20 --1k MIFARE Classic 1k / S50 (default) --2k MIFARE Classic/Plus 2k @@ -335,7 +335,7 @@ Accepts (BIN/EML/JSON) ``` Options --- --f, --file filename of dump +-f, --file Specify a filename for dump file --mini MIFARE Classic Mini / S20 --1k MIFARE Classic 1k / S50 (def) --2k MIFARE Classic/Plus 2k @@ -712,7 +712,7 @@ pm3 --> mem load -f iclass_default_keys --iclass Upgrade Sim Module firmware ``` -pm3 --> smart upgrade -f sim013.bin +pm3 --> smart upgrade -f sim014.bin ``` ## Smart Card diff --git a/doc/commands.json b/doc/commands.json index 55da69cd3..03751cbe2 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -275,6 +275,21 @@ ], "usage": "data bitsamples [-h]" }, + "data bmap": { + "command": "data bmap", + "description": "Breaks down a hex value to binary according a template data bmap -d 16 -m 4,4 This will give two rows each with four bits", + "notes": [ + "data bmap -d 3B", + "data bmap -d 3B -m 2,5,1" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-d hex string", + "-m binary template" + ], + "usage": "data bmap [-h] [-d ] [-m ]" + }, "data clear": { "command": "data clear", "description": "This function clears the bigbuff on deviceside and graph window", @@ -1538,7 +1553,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose verbose output" ], "usage": "hf 14b view [-hv] -f " @@ -1570,7 +1585,7 @@ }, "hf 15 dump": { "command": "hf 15 dump", - "description": "This command dumps the contents of a ISO-15693 tag and save it to file", + "description": "This command dumps the contents of a ISO-15693 tag and save to file (bin/json)", "notes": [ "hf 15 dump", "hf 15 dump -*", @@ -1584,7 +1599,7 @@ "-* scan for tag", "-2 use slower '1 out of 256' mode", "-o, --opt set OPTION Flag (needed for TI)", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf 15 dump [-h*2o] [-u ] [--ua] [-f ]" }, @@ -1603,18 +1618,18 @@ }, "hf 15 esave": { "command": "hf 15 esave", - "description": "Save emulator memory into three files (BIN/EML/JSON)", + "description": "Save emulator memory into two files (bin/json)", "notes": [ "hf 15 esave -f hf-15-01020304hf 15 esave -b 8 -c 42 -f hf-15-01020304" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", - "-b, --blocksize block size, defaults to 4", + "-f, --file Specify a filename for dump file", + "--bsize block size, defaults to 4", "-c, --count number of blocks to export, defaults to all" ], - "usage": "hf 15 esave [-h] -f [-b ] [-c ]" + "usage": "hf 15 esave [-h] -f [--bsize ] [-c ]" }, "hf 15 eview": { "command": "hf 15 eview", @@ -1784,7 +1799,7 @@ }, "hf 15 restore": { "command": "hf 15 restore", - "description": "This command restore the contents of a dump file onto a ISO-15693 tag", + "description": "This command restore the contents of a dump file (bin/eml/json) onto a ISO-15693 tag", "notes": [ "hf 15 restore", "hf 15 restore -*", @@ -1798,7 +1813,7 @@ "-* scan for tag", "-2 use slower '1 out of 256' mode", "-o, --opt set OPTION Flag (needed for TI)", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-r, --retry number of retries (def 3)", "--bs block size (def 4)", "-v, --verbose verbose output" @@ -1919,7 +1934,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)" + "-f, --file Specify a filename for dump file" ], "usage": "hf 15 view [-h] -f " }, @@ -2807,14 +2822,14 @@ }, "hf fudan dump": { "command": "hf fudan dump", - "description": "Dump FUDAN tag to binary file If no given, UID will be used as filename", + "description": "Dump FUDAN tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf fudan dump -f mydump -> dump using filename" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf fudan dump [-h] [-f ]" }, @@ -2858,7 +2873,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf fudan view [-h] -f " }, @@ -2987,9 +3002,10 @@ "--nki New key index to select key from memory 'hf iclass managekeys'", "--csn Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)", "--elite Elite computations applied to new key", - "--elite2 Elite computations applied to both old and new key" + "--elite2 Elite computations applied to both old and new key", + "--oldelite Elite computations applied only to old key" ], - "usage": "hf iclass calcnewkey [-h] [--old ] [--oki ] [--new ] [--nki ] [--csn ] [--elite] [--elite2]" + "usage": "hf iclass calcnewkey [-h] [--old ] [--oki ] [--new ] [--nki ] [--csn ] [--elite] [--elite2] [--oldelite]" }, "hf iclass chk": { "command": "hf iclass chk", @@ -3011,14 +3027,14 @@ }, "hf iclass configcard": { "command": "hf iclass configcard", - "description": "Manage reader configuration card via Cardhelper, The generated config card will be uploaded to device emulator memory. You can start simulating `hf iclass sim -t 3` or use the emul commands", + "description": "Manage reader configuration card via Cardhelper or internal database, The generated config card will be uploaded to device emulator memory. You can start simulating `hf iclass sim -t 3` or use the emul commands", "notes": [ - "hf iclass configcard -l -> download config card settings", - "hf iclass configcard -p -> print all config cards", + "hf iclass configcard -l -> download config card settings from cardhelper", + "hf iclass configcard -p -> print all config cards in the database", "hf iclass configcard --ci 1 -> view config card setting in slot 1", "hf iclass configcard -g --ci 0 -> generate config file from slot 0" ], - "offline": true, + "offline": false, "options": [ "-h, --help This help", "--ci use config slot at index", @@ -3051,7 +3067,7 @@ }, "hf iclass decrypt": { "command": "hf iclass decrypt", - "description": "3DES decrypt data This is a naive implementation, it tries to decrypt every block after block 6. Correct behaviour would be to decrypt only the application areas where the key is valid, which is defined by the configuration block. OBS! In order to use this function, the file `iclass_decryptionkey.bin` must reside in the resources directory. The file should be 16 bytes binary data or... make sure your cardhelper is placed in the sim module", + "description": "3DES decrypt data This is a naive implementation, it tries to decrypt every block after block 6. Correct behaviour would be to decrypt only the application areas where the key is valid, which is defined by the configuration block. OBS! In order to use this function, the file `iclass_decryptionkey.bin` must reside in the resources directory. The file must be 16 bytes binary data or... make sure your cardhelper is placed in the sim module", "notes": [ "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin", "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin -k 000102030405060708090a0b0c0d0e0f", @@ -3060,7 +3076,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump file (bin/eml/json)", + "-f, --file Specify a filename for dump file", "-d, --data 3DES encrypted data", "-k, --key 3DES transport key", "-v, --verbose verbose output", @@ -3092,21 +3108,22 @@ "--nr replay of NR/MAC", "-z, --dense dense dump output style", "--force force unsecure card read", - "--shallow use shallow (ASK) reader modulation instead of OOK" + "--shallow use shallow (ASK) reader modulation instead of OOK", + "--ns no save to file" ], - "usage": "hf iclass dump [-hz] [-f ] [-k ] [--ki ] [--credit ] [--ci ] [--elite] [--raw] [--nr] [--force] [--shallow]" + "usage": "hf iclass dump [-hz] [-f ] [-k ] [--ki ] [--credit ] [--ci ] [--elite] [--raw] [--nr] [--force] [--shallow] [--ns]" }, "hf iclass eload": { "command": "hf iclass eload", - "description": "Load emulator memory with data from (bin/eml/json) iCLASS dump file", + "description": "Load emulator memory with data from (bin/json) iCLASS dump file", "notes": [ - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml", + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.json", "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -m" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)", + "-f, --file Specify a filename for dump file", "-m, --mem use RDV4 spiffs", "-v, --verbose verbose output" ], @@ -3155,7 +3172,7 @@ }, "hf iclass esave": { "command": "hf iclass esave", - "description": "Save emulator memory to file. if filename is not supplied, CSN will be used.", + "description": "Save emulator memory to file (bin/json) if filename is not supplied, CSN will be used.", "notes": [ "hf iclass esave", "hf iclass esave -f hf-iclass-dump", @@ -3164,7 +3181,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump file", + "-f, --file Specify a filename for dump file", "-s, --size <256|2048> number of bytes to save (default 256)" ], "usage": "hf iclass esave [-h] [-f ] [-s <256|2048>]" @@ -3173,15 +3190,15 @@ "command": "hf iclass esetblk", "description": "Sets an individual block in emulator memory.", "notes": [ - "hf iclass esetblk -b 7 -d 0000000000000000" + "hf iclass esetblk --blk 7 -d 0000000000000000" ], "offline": false, "options": [ "-h, --help This help", - "-b, --blk block number", + "--blk block number", "-d, --data bytes to write, 8 hex bytes" ], - "usage": "hf iclass esetblk [-h] -b [-d ]" + "usage": "hf iclass esetblk [-h] --blk [-d ]" }, "hf iclass eview": { "command": "hf iclass eview", @@ -3206,7 +3223,7 @@ "notes": [ "hf iclass info" ], - "offline": true, + "offline": false, "options": [ "-h, --help This help", "--shallow use shallow (ASK) reader modulation instead of OOK" @@ -3309,16 +3326,16 @@ "command": "hf iclass rdbl", "description": "Read a iCLASS block from tag", "notes": [ - "hf iclass rdbl -b 6 -k 0011223344556677", - "hf iclass rdbl -b 27 -k 0011223344556677 --credit", - "hf iclass rdbl -b 10 --ki 0" + "hf iclass rdbl --blk 6 -k 0011223344556677", + "hf iclass rdbl --blk 27 -k 0011223344556677 --credit", + "hf iclass rdbl --blk 10 --ki 0" ], "offline": false, "options": [ "-h, --help This help", "-k, --key Access key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", - "-b, --block The block number to read", + "--blk Block number", "--credit key is assumed to be the credit key", "--elite elite computations applied to key", "--raw no computations applied to key", @@ -3326,7 +3343,7 @@ "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass rdbl [-hv] [-k ] [--ki ] -b [--credit] [--elite] [--raw] [--nr] [--shallow]" + "usage": "hf iclass rdbl [-hv] [-k ] [--ki ] --blk [--credit] [--elite] [--raw] [--nr] [--shallow]" }, "hf iclass reader": { "command": "hf iclass reader", @@ -3344,7 +3361,7 @@ }, "hf iclass restore": { "command": "hf iclass restore", - "description": "Restore data from dumpfile onto a iCLASS tag", + "description": "Restore data from dumpfile (bin/eml/json) onto a iCLASS tag", "notes": [ "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0", "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite", @@ -3353,7 +3370,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file specify a filename to restore (bin/eml/json)", + "-f, --file specify a filename to restore", "-k, --key Access key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", "--first The first block number to restore", @@ -3368,17 +3385,16 @@ }, "hf iclass sam": { "command": "hf iclass sam", - "description": "Manage via SAM", + "description": "Extract PACS via a HID SAM", "notes": [ "hf iclass sam" ], "offline": false, "options": [ "-h, --help This help", - "-d, --data data", "-v, --verbose verbose output" ], - "usage": "hf iclass sam [-hv] [-d ]" + "usage": "hf iclass sam [-hv]" }, "hf iclass sim": { "command": "hf iclass sim", @@ -3425,7 +3441,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)", + "-f, --file Specify a filename for dump file", "--first Begin printing from this block (default first user block)", "--last End printing at this block (default 0, ALL)", "-v, --verbose verbose output", @@ -3437,16 +3453,16 @@ "command": "hf iclass wrbl", "description": "Write data to an iCLASS tag", "notes": [ - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B", - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit", - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA --ki 0" + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B", + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit", + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0" ], "offline": false, "options": [ "-h, --help This help", "-k, --key Access key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", - "-b, --block The block number to read", + "--blk block number", "-d, --data data to write as 8 hex bytes", "-m, --mac replay mac data (4 hex bytes)", "--credit key is assumed to be the credit key", @@ -3456,7 +3472,7 @@ "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass wrbl [-hv] [-k ] [--ki ] -b -d [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow]" + "usage": "hf iclass wrbl [-hv] [-k ] [--ki ] --blk -d [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow]" }, "hf jooki clone": { "command": "hf jooki clone", @@ -3623,7 +3639,7 @@ }, "hf legic dump": { "command": "hf legic dump", - "description": "Read all memory from LEGIC Prime tags and saves to (bin/eml/json) dump file It autodetects card type (MIM22, MIM256, MIM1024)", + "description": "Read all memory from LEGIC Prime tags and saves to (bin/json) dump file It autodetects card type (MIM22, MIM256, MIM1024)", "notes": [ "hf legic dump -> use UID as filename", "hf legic dump -f myfile", @@ -3670,7 +3686,7 @@ }, "hf legic esave": { "command": "hf legic esave", - "description": "Saves a (bin/eml/json) dump file of emulator memory", + "description": "Saves a (bin/json) dump file of emulator memory", "notes": [ "hf legic esave -> uses UID as filename", "hf legic esave -f myfile --22", @@ -3778,7 +3794,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file Filename to restore", + "-f, --file Specify a filename to restore", "--ob obfuscate dump data (xor with MCC)" ], "usage": "hf legic restore [-h] -f [--ob]" @@ -3807,7 +3823,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file Filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose verbose output" ], "usage": "hf legic view [-hv] -f " @@ -3951,15 +3967,15 @@ "command": "hf lto wrbl", "description": "Write data to block on LTO tag", "notes": [ - "hf lto wrbl --block 128 -d 0001020304050607080910111213141516171819202122232425262728293031" + "hf lto wrbl --blk 128 -d 0001020304050607080910111213141516171819202122232425262728293031" ], "offline": false, "options": [ "-h, --help This help", "-d, --data 32 bytes of data to write (64 hex symbols, no spaces)", - "--block The block number to write to as an integer" + "--blk The block number to write to as an integer" ], - "usage": "hf lto wrbl [-h] -d --block " + "usage": "hf lto wrbl [-h] -d --blk " }, "hf mf acl": { "command": "hf mf acl", @@ -3997,7 +4013,8 @@ "hf mf autopwn", "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF -> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'", "hf mf autopwn --1k -f mfc_default_keys -> target MFC 1K card, default dictionary", - "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys -> combo of the two above samples" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys -> combo of the two above samples", + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -k a0a1a2a3a4a5 -> multiple user supplied keys" ], "offline": false, "options": [ @@ -4021,7 +4038,7 @@ "--i2 AVX2", "--i5 AVX512" ], - "usage": "hf mf autopwn [-hablv] [-k ] [-s ] [-f ] [--slow] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" + "usage": "hf mf autopwn [-hablv] [-k ]... [-s ] [-f ] [--slow] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" }, "hf mf cgetblk": { "command": "hf mf cgetblk", @@ -4092,14 +4109,14 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--emu from emulator memory" ], "usage": "hf mf cload [-h] [-f ] [--emu]" }, "hf mf csave": { "command": "hf mf csave", - "description": "Save magic gen1a card memory into three files (BIN/EML/JSON)or into emulator memory", + "description": "Save magic gen1a card memory to file (bin/json)or into emulator memory", "notes": [ "hf mf csave", "hf mf csave --4k" @@ -4107,7 +4124,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", @@ -4218,7 +4235,7 @@ }, "hf mf dump": { "command": "hf mf dump", - "description": "Dump MIFARE Classic tag to binary file If no given, UID will be used as filename", + "description": "Dump MIFARE Classic tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf mf dump --mini -> MIFARE Mini", "hf mf dump --1k -> MIFARE Classic 1k", @@ -4229,7 +4246,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-k, --keys filename of keys", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", @@ -4327,7 +4344,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", @@ -4339,9 +4356,27 @@ ], "usage": "hf mf eload [-hmv] -f [--mini] [--1k] [--2k] [--4k] [--ul] [-q ]" }, + "hf mf encodehid": { + "command": "hf mf encodehid", + "description": "Encode binary wiegand to card Use either --bin or --wiegand/--fc/--cn", + "notes": [ + "hf mf encodehid --bin 10001111100000001010100011 -> FC 31 CN 337 (H10301)", + "hf mf encodehid -w H10301 --fc 31 --cn 337" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--bin Binary string i.e 0001001001", + "--fc facility code", + "--cn card number", + "-w, --wiegand see `wiegand list` for available formats", + "-v, --verbose verbose output" + ], + "usage": "hf mf encodehid [-hv] [--bin ] [--fc ] [--cn ] [-w ]" + }, "hf mf esave": { "command": "hf mf esave", - "description": "Save emulator memory into three files (BIN/EML/JSON)", + "description": "Save emulator memory to file (bin/json)", "notes": [ "hf mf esave", "hf mf esave --4k", @@ -4350,7 +4385,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", @@ -4419,6 +4454,21 @@ ], "usage": "hf mf fchk [-h] [-k ]... [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [--mem] [-f ]" }, + "hf mf gchpwd": { + "command": "hf mf gchpwd", + "description": "Change access password for Gen4 GTU card. WARNING! If you dont KNOW the password - you CAN'T access it!!!", + "notes": [ + "hf mf gchpwd --pwd 00000000 --newpwd 01020304" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd password 4 bytes", + "-n, --newpwd new password 4 bytes", + "-v, --verbose verbose output" + ], + "usage": "hf mf gchpwd [-hv] [-p ] [-n ]" + }, "hf mf gdmcfg": { "command": "hf mf gdmcfg", "description": "Get configuration data from magic gen4 GDM card.", @@ -4520,6 +4570,21 @@ ], "usage": "hf mf ggetblk [-hv] -b [-p ]" }, + "hf mf ginfo": { + "command": "hf mf ginfo", + "description": "Read info about magic gen4 GTU card.", + "notes": [ + "hf mf ginfo -> get info with default password 00000000", + "hf mf ginfo --pwd 01020304 -> get info with password" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "-p, --pwd password 4bytes" + ], + "usage": "hf mf ginfo [-hv] [-p ]" + }, "hf mf gload": { "command": "hf mf gload", "description": "Load magic gen4 gtu card with data from (bin/eml/json) dump file or from emulator memory.", @@ -4540,7 +4605,7 @@ "--4k MIFARE Classic 4k / S70", "-p, --pwd password 4bytes", "-v, --verbose verbose output", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--emu from emulator memory", "--start index of block to start writing (default 0)", "--end index of block to end writing (default last block)" @@ -4549,7 +4614,7 @@ }, "hf mf gsave": { "command": "hf mf gsave", - "description": "Save `magic gen4 gtu` card memory into three files (BIN/EML/JSON)or into emulator memory", + "description": "Save `magic gen4 gtu` card memory to file (bin/json)or into emulator memory", "notes": [ "hf mf gsave", "hf mf gsave --4k", @@ -4562,8 +4627,8 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "-p, --pwd password 4bytes", - "-f, --file filename of dump", + "-p, --pwd password 4 bytes", + "-f, --file Specify a filename for dump file", "--emu to emulator memory" ], "usage": "hf mf gsave [-h] [--mini] [--1k] [--2k] [--4k] [-p ] [-f ] [--emu]" @@ -4864,7 +4929,7 @@ "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", "-u, --uid uid, (4|7|10 hex bytes)", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file specify a filename for dump file", "-k, --kfn key filename", "--ka use specified keyfile to authenticate", "--force override warnings" @@ -4999,7 +5064,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose verbose output", "--sk Save extracted keys to file" ], @@ -5163,9 +5228,9 @@ "description": "Checks keys with MIFARE DESFire card.", "notes": [ "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456", - "hf mfdes chk -d mfdes_default_keys -> check keys from dictionary against all existing aid on card", - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys from dictionary against aid 0x123456", - "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to json", + "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card", + "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456", + "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`", "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00" ], "offline": false, @@ -6224,7 +6289,7 @@ }, "hf mfp dump": { "command": "hf mfp dump", - "description": "Dump MIFARE Plus tag to binary file If no given, UID will be used as filename", + "description": "Dump MIFARE Plus tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf mfp dump", "hf mfp dump --keys hf-mf-066C8B78-key.bin -> MIFARE Plus with keys from specified file" @@ -6232,8 +6297,8 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", - "-k, --keys filename of keys", + "-f, --file Specify a filename for dump file", + "-k, --keys Specify a filename for keys file", "--ns no save to file", "-v, --verbose Verbose mode" ], @@ -6447,7 +6512,7 @@ }, "hf mfu dump": { "command": "hf mfu dump", - "description": "Dump MIFARE Ultralight/NTAG tag to binary/eml/json files. It autodetects card type.Supports: Ultralight, Ultralight-C, Ultralight EV1 NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216", + "description": "Dump MIFARE Ultralight/NTAG tag to files (bin/json) It autodetects card type.Supports: Ultralight, Ultralight-C, Ultralight EV1 NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216", "notes": [ "hf mfu dump -f myfile", "hf mfu dump -k AABBCCDD -> dump whole tag using pwd AABBCCDD", @@ -6477,7 +6542,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file Filename of dump", + "-f, --file Specify a filename for dump file", "-q, --qty Number of blocks to load from eml file", "-v, --verbose verbose output" ], @@ -6485,17 +6550,17 @@ }, "hf mfu esave": { "command": "hf mfu esave", - "description": "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/eml/json) By default number of pages saved depends on defined tag type. You can override this with option --end.", + "description": "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/json) By default number of pages saved depends on defined tag type. You can override this with option --end.", "notes": [ "hf mfu esave", "hf mfu esave --end 255 -> saves whole memory", - "hf mfu esave -f hf-mfu-04010203040506-dump.json" + "hf mfu esave -f hf-mfu-04010203040506-dump" ], "offline": false, "options": [ "-h, --help This help", "-e, --end index of last block", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf mfu esave [-h] [-e ] [-f ]" }, @@ -6642,7 +6707,7 @@ }, "hf mfu restore": { "command": "hf mfu restore", - "description": "Restore MIFARE Ultralight/NTAG dump file to tag.", + "description": "Restore MIFARE Ultralight/NTAG dump file (bin/eml/json) to tag.", "notes": [ "hf mfu restore -f myfile -s -> special write", "hf mfu restore -f myfile -k AABBCCDD -s -> special write, use key", @@ -6651,7 +6716,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file Specify a filename for dump file", "-k, --key key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)", "-l swap entered key's endianness", "-s enable special write UID -MAGIC TAG ONLY-", @@ -6733,7 +6798,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file Filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose Verbose output" ], "usage": "hf mfu view [-hv] -f " @@ -6757,6 +6822,78 @@ ], "usage": "hf mfu wrbl [-hl] [-k ] -b -d [--force]" }, + "hf ntag424 auth": { + "command": "hf ntag424 auth", + "description": "Authenticate with selected key against NTAG424.", + "notes": [ + "hf ntag424 auth --keyno 0 -k 00000000000000000000000000000000" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--keyno Key number", + "-k, --key Key for authenticate (HEX 16 bytes)" + ], + "usage": "hf ntag424 auth [-h] --keyno -k " + }, + "hf ntag424 changefs": { + "command": "hf ntag424 changefs", + "description": "Updates file settings for file, must be authenticated. This is a short explanation of the settings. See AN12196 for more information: options: byte with bit flags Bit: Setting: 6 Enable SDM and mirroring access: two byte access rights. Each nibble is a key number, or E for free access. Order is key for readwrite, change, read and write sdmoptions: byte with bit flags Bit: Setting: 0 ASCII encoding 4 SDMEncFileData 5 SDMReadCtrLimit 6 SDMReadCtr 7 SDMOptionsUID sdmaccess: two byte access rights. Each nibble is a key, or E for plain mirror and F for no mirroring Order is Reserved, SDMCtrRet, SDMMetaRead and SDMFileRead sdm_data: Three bytes of data used to control SDM settings. Can be specified multiple times. Data means different things depending on settings. Note: Not all of these settings will be written. It depends on the option byte, and the keys set. See AN12196 for more information. You must also start with sdmdata1, then sdmdata2, up to the number of sdm_data you want to write", + "notes": [ + "hf ntag424 changefs --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 40 -a 00E0 -s C1 -c F000 --data1 000020 --data2 000043 --data3 000043" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--fileno File number", + "--keyno Key number", + "-k, --key Key for authentication (HEX 16 bytes)", + "-o, --options File options byte (HEX 1 byte)", + "-a, --access File access settings (HEX 2 bytes)", + "-s, --sdmoptions SDM options (HEX 1 byte)", + "-c, --sdmaccess SDM access settings (HEX 2 bytes)", + "--data1 SDM data (HEX 3 bytes)", + "--data2 SDM data (HEX 3 bytes)", + "--data3 SDM data (HEX 3 bytes)", + "--data4 SDM data (HEX 3 bytes)", + "--data5 SDM data (HEX 3 bytes)", + "--data6 SDM data (HEX 3 bytes)", + "--data7 SDM data (HEX 3 bytes)", + "--data8 SDM data (HEX 3 bytes)" + ], + "usage": "hf ntag424 changefs [-h] --fileno --keyno -k [-o ] [-a ] [-s ] [-c ] [--data1 ] [--data2 ] [--data3 ] [--data4 ] [--data5 ] [--data6 ] [--data7 ] [--data8 ]" + }, + "hf ntag424 changekey": { + "command": "hf ntag424 changekey", + "description": "Change a key. Authentication key must currently be different to the one we want to change.", + "notes": [ + "hf ntag424 changekey --keyno 1 --oldkey 00000000000000000000000000000000 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1", + "hf ntag424 changekey --keyno 0 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--keyno Key number to change", + "--oldkey Old key (only needed when changing key 1-4, HEX 16 bytes)", + "--newkey New key (HEX 16 bytes)", + "--key0 Authentication key (must be key 0, HEX 16 bytes)", + "--kv New key version number" + ], + "usage": "hf ntag424 changekey [-h] --keyno [--oldkey ] --newkey --key0 --kv " + }, + "hf ntag424 getfs": { + "command": "hf ntag424 getfs", + "description": "Read and print file settings for file", + "notes": [ + "hf ntag424 getfs --fileno 2" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--fileno File number" + ], + "usage": "hf ntag424 getfs [-h] --fileno " + }, "hf ntag424 info": { "command": "hf ntag424 info", "description": "Get info about NXP NTAG424 DNA Family styled tag.", @@ -6769,17 +6906,25 @@ ], "usage": "hf ntag424 info [-h]" }, - "hf ntag424 sdm": { - "command": "hf ntag424 sdm", - "description": "Validate a SDM message", + "hf ntag424 read": { + "command": "hf ntag424 read", + "description": "Read and print data from file on NTAG424 tag. Will authenticate if key information is provided.", "notes": [ - "hf ntag424 sdm" + "hf ntag424 read --fileno 1 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 32", + "hf ntag424 read --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 256", + "hf ntag424 read --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -l 128 -m encrypt" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "--fileno <1|2|3> File number", + "--keyno Key number", + "-k, --key Key for authentication (HEX 16 bytes)", + "-o, --offset Offset to read in file (def 0)", + "-l, --length Number of bytes to read", + "-m, --cmode Communication mode" ], - "usage": "hf ntag424 sdm [-h]" + "usage": "hf ntag424 read [-h] --fileno <1|2|3> [--keyno ] [-k ] [-o ] -l [-m ]" }, "hf ntag424 view": { "command": "hf ntag424 view", @@ -6790,11 +6935,30 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file Filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose Verbose output" ], "usage": "hf ntag424 view [-hv] -f " }, + "hf ntag424 write": { + "command": "hf ntag424 write", + "description": "Write data to file on NTAG424 tag. Will authenticate if key information is provided.", + "notes": [ + "hf ntag424 write --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788", + "hf ntag424 write --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788 -m encrypt" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--fileno <1|2|3> File number (def 2)", + "--keyno Key number", + "-k, --key Key for authentication (HEX 16 bytes)", + "-o, --offset Offset to write in file (def 0)", + "-d, --data Data to write", + "-m, --cmode Communication mode" + ], + "usage": "hf ntag424 write [-h] --fileno <1|2|3> [--keyno ] [-k ] [-o ] -d [-m ]" + }, "hf plot": { "command": "hf plot", "description": "Plots HF signal after RF signal path and A/D conversion.", @@ -7087,14 +7251,14 @@ }, "hf topaz dump": { "command": "hf topaz dump", - "description": "Dump TOPAZ tag to binary file If no given, UID will be used as filename", + "description": "Dump TOPAZ tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf topaz dump" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--ns no save to file" ], "usage": "hf topaz dump [-h] [-f ] [--ns]" @@ -7208,7 +7372,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)" + "-f, --file Specify a filename for dump file" ], "usage": "hf topaz view [-h] -f " }, @@ -7538,13 +7702,15 @@ "command": "hw status", "description": "Show runtime status information about the connected Proxmark3", "notes": [ - "hw status" + "hw status", + "hw status --ms 1000 -> Test connection speed with 1000ms timeout" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-m, --ms speed test timeout in micro seconds" ], - "usage": "hw status [-h]" + "usage": "hw status [-h] [-m ]" }, "hw tearoff": { "command": "hw tearoff", @@ -7581,15 +7747,15 @@ "description": "Set the communication timeout on the client side", "notes": [ "hw timeout -> Show current timeout", - "hw timeout -t 20 -> Set the timeout to 20ms", - "hw timeout -t 500 -> Set the timeout to 500ms" + "hw timeout -m 20 -> Set the timeout to 20ms", + "hw timeout --ms 500 -> Set the timeout to 500ms" ], "offline": true, "options": [ "-h, --help This help", - "-t, --timeout timeout in ms" + "-m, --ms timeout in micro seconds" ], - "usage": "hw timeout [-h] [-t ]" + "usage": "hw timeout [-h] [-m ]" }, "hw tune": { "command": "hw tune", @@ -8020,9 +8186,10 @@ "options": [ "-h, --help This help", "-p, --pwd password (00000000)", - "-f, --file override filename prefix (optional). Default is based on UID" + "-f, --file override filename prefix (optional). Default is based on UID", + "--ns no save to file" ], - "usage": "lf em 4x05 dump [-h] [-p ] [-f ]" + "usage": "lf em 4x05 dump [-h] [-p ] [-f ] [--ns]" }, "lf em 4x05 info": { "command": "lf em 4x05 info", @@ -8159,7 +8326,7 @@ }, "lf em 4x50 dump": { "command": "lf em 4x50 dump", - "description": "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format", + "description": "Reads all blocks/words from EM4x50 tag and saves dump in (bin/json) format", "notes": [ "lf em 4x50 dump", "lf em 4x50 dump -f mydump", @@ -8169,7 +8336,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file specify dump filename", "-p, --pwd password, 4 hex bytes, lsb" ], "usage": "lf em 4x50 dump [-h] [-f ] [-p ]" @@ -8183,13 +8350,13 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file dump filename (bin/eml/json)" + "-f, --file Specify a filename for dump file" ], "usage": "lf em 4x50 eload [-h] -f " }, "lf em 4x50 esave": { "command": "lf em 4x50 esave", - "description": "Saves bin/eml/json dump file of emulator memory.", + "description": "Saves bin/json dump file of emulator memory.", "notes": [ "lf em 4x50 esave -> use UID as filename", "lf em 4x50 esave -f mydump" @@ -8284,7 +8451,7 @@ "options": [ "-h, --help This help", "-u, --uid uid, 4 hex bytes, msb", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file specify a filename for dump file", "-p, --pwd password, 4 hex bytes, lsb" ], "usage": "lf em 4x50 restore [-h] [-u ] [-f ] [-p ]" @@ -8806,7 +8973,7 @@ "-1, --ht1 Card type Hitag 1", "-2, --ht2 Card type Hitag 2", "-s, --hts Card type Hitag S", - "-m, --htm Card type Hitag \u00ce\u00bc" + "-m, --htm Card type Hitag \u03bc" ], "usage": "lf hitag eload [-h12sm] -f " }, @@ -10176,7 +10343,7 @@ }, "lf t55xx dump": { "command": "lf t55xx dump", - "description": "This command dumps a T55xx card Page 0 block 0-7. It will create three files (bin/eml/json)", + "description": "This command dumps a T55xx card Page 0 block 0-7. It will create two files (bin/json)", "notes": [ "lf t55xx dump", "lf t55xx dump -p aabbccdd --override", @@ -10328,7 +10495,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump file", + "-f, --file Specify a filename for dump file", "-p, --pwd password if target card has password set (4 hex bytes)" ], "usage": "lf t55xx restore [-h] [-f ] [-p ]" @@ -10713,16 +10880,15 @@ "description": "Dumps device SPIFFS file to a local file Size is handled by first sending a STAT command against file to verify existence", "notes": [ "mem spiffs dump -s tag.bin -> download binary file from device", - "mem spiffs dump -s tag.bin -d aaa -e -> download tag.bin, save as aaa.eml format" + "mem spiffs dump -s tag.bin -d a001 -e -> download tag.bin, save as `a001.bin`" ], "offline": false, "options": [ "-h, --help This help", "-s, --src SPIFFS file to save", - "-d, --dest file name to save to ", - "-e, --eml also save in EML format" + "-d, --dest file name to save to " ], - "usage": "mem spiffs dump [-he] -s [-d ]" + "usage": "mem spiffs dump [-h] -s [-d ]" }, "mem spiffs info": { "command": "mem spiffs info", @@ -11222,29 +11388,41 @@ ], "usage": "prefs get barmode [-h]" }, - "prefs get clientdebug": { - "command": "prefs get clientdebug", + "prefs get client.debug": { + "command": "prefs get client.debug", "description": "Get preference of using clientside debug level", "notes": [ - "prefs get clientdebug" + "prefs get client.debug" ], "offline": true, "options": [ "-h, --help This help" ], - "usage": "prefs get clientdebug [-h]" + "usage": "prefs get client.debug [-h]" }, - "prefs get clientdelay": { - "command": "prefs get clientdelay", + "prefs get client.delay": { + "command": "prefs get client.delay", "description": "Get preference of delay time before execution of a command in the client", "notes": [ - "prefs get clientdelay" + "prefs get client.delay" ], "offline": true, "options": [ "-h, --help This help" ], - "usage": "prefs get clientdelay [-h]" + "usage": "prefs get client.delay [-h]" + }, + "prefs get client.timeout": { + "command": "prefs get client.timeout", + "description": "Get preference of delay time before execution of a command in the client", + "notes": [ + "prefs get client.timeout" + ], + "offline": true, + "options": [ + "-h, --help This help" + ], + "usage": "prefs get client.timeout [-h]" }, "prefs get color": { "command": "prefs get color", @@ -11333,11 +11511,11 @@ ], "usage": "prefs set barmode [-h] [--bar] [--mix] [--val]" }, - "prefs set clientdebug": { - "command": "prefs set clientdebug", + "prefs set client.debug": { + "command": "prefs set client.debug", "description": "Set persistent preference of using clientside debug level", "notes": [ - "prefs set clientdebug --simple" + "prefs set client.debug --simple" ], "offline": true, "options": [ @@ -11346,21 +11524,36 @@ "--simple simple debug messages", "--full full debug messages" ], - "usage": "prefs set clientdebug [-h] [--off] [--simple] [--full]" + "usage": "prefs set client.debug [-h] [--off] [--simple] [--full]" }, - "prefs set clientdelay": { - "command": "prefs set clientdelay", + "prefs set client.delay": { + "command": "prefs set client.delay", "description": "Set persistent preference of delay before executing a command in the client", "notes": [ - "prefs set clientdelay --ms 0 -> unsets any delay", - "prefs set clientdelay --ms 1000 -> sets 1000ms delay" + "prefs set client.delay --ms 0 -> unsets any delay", + "prefs set client.delay --ms 1000 -> sets 1000ms delay" ], "offline": true, "options": [ "-h, --help This help", "--ms delay in micro seconds" ], - "usage": "prefs set clientdelay [-h] [--ms ]" + "usage": "prefs set client.delay [-h] [--ms ]" + }, + "prefs set client.timeout": { + "command": "prefs set client.timeout", + "description": "Set persistent preference of client communication timeout", + "notes": [ + "prefs set client.timeout --ms 0 -> unsets any timeout", + "prefs set client.timeout -m 20 -> Set the timeout to 20ms", + "prefs set client.timeout --ms 500 -> Set the timeout to 500ms" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-m, --ms timeout in micro seconds" + ], + "usage": "prefs set client.timeout [-h] [-m ]" }, "prefs set color": { "command": "prefs set color", @@ -11579,9 +11772,10 @@ "-s active smartcard with select (get ATR)", "-t, --tlv executes TLV decoder if it possible", "-0 use protocol T=0", + "--timeout Timeout in MS waiting for SIM to respond. (def 337ms)", "-d, --data bytes to send" ], - "usage": "smart raw [-hrast0] -d " + "usage": "smart raw [-hrast0] [--timeout ] -d " }, "smart reader": { "command": "smart reader", @@ -11855,8 +12049,8 @@ } }, "metadata": { - "commands_extracted": 687, + "commands_extracted": 698, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2023-09-10T12:59:25" + "extracted_on": "2023-11-09T16:29:08" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 0c94700e6..94c3aa8ee 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -37,8 +37,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`prefs get barmode `|Y |`Get bar mode preference` -|`prefs get clientdebug `|Y |`Get client debug level preference` -|`prefs get clientdelay `|Y |`Get client execution delay preference` +|`prefs get client.debug `|Y |`Get client debug level preference` +|`prefs get client.delay `|Y |`Get client execution delay preference` +|`prefs get client.timeout`|Y |`Get client execution delay preference` |`prefs get color `|Y |`Get color support preference` |`prefs get savepaths `|Y |`Get file folder ` |`prefs get emoji `|Y |`Get emoji display preference` @@ -55,8 +56,9 @@ Check column "offline" for their availability. |------- |------- |----------- |`prefs set help `|Y |`This help` |`prefs set barmode `|Y |`Set bar mode` -|`prefs set clientdebug `|Y |`Set client debug level` -|`prefs set clientdelay `|Y |`Set client execution delay` +|`prefs set client.debug `|Y |`Set client debug level` +|`prefs set client.delay `|Y |`Set client execution delay` +|`prefs set client.timeout`|Y |`Set client communication timeout` |`prefs set color `|Y |`Set color support` |`prefs set emoji `|Y |`Set emoji display` |`prefs set hints `|Y |`Set hint display` @@ -124,6 +126,7 @@ Check column "offline" for their availability. |`data atr `|Y |`ATR lookup` |`data bin2hex `|Y |`Converts binary to hexadecimal` |`data bitsamples `|N |`Get raw samples as bitstring` +|`data bmap `|Y |`Convert hex value according a binary template` |`data clear `|Y |`Clears bigbuf on deviceside and graph window` |`data diff `|Y |`Diff of input files` |`data hexsamples `|N |`Dump big buffer as hex bytes` @@ -408,7 +411,7 @@ Check column "offline" for their availability. |`hf iclass help `|Y |`This help` |`hf iclass list `|Y |`List iclass history` |`hf iclass dump `|N |`Dump Picopass / iCLASS tag to file` -|`hf iclass info `|Y |`Tag information` +|`hf iclass info `|N |`Tag information` |`hf iclass rdbl `|N |`Read Picopass / iCLASS block` |`hf iclass reader `|N |`Act like a Picopass / iCLASS reader` |`hf iclass restore `|N |`Restore a dump file onto a Picopass / iCLASS tag` @@ -424,7 +427,7 @@ Check column "offline" for their availability. |`hf iclass esave `|N |`Save emulator memory to file` |`hf iclass esetblk `|N |`Set emulator memory block data` |`hf iclass eview `|N |`View emulator memory` -|`hf iclass configcard `|Y |`Reader configuration card` +|`hf iclass configcard `|N |`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` @@ -526,17 +529,20 @@ Check column "offline" for their availability. |`hf mf gen3uid `|N |`Set UID without changing manufacturer block` |`hf mf gen3blk `|N |`Overwrite manufacturer block` |`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible` +|`hf mf ginfo `|N |`Info about configuration of the card` |`hf mf ggetblk `|N |`Read block from card` |`hf mf gload `|N |`Load dump to card` |`hf mf gsave `|N |`Save dump from card into file or emulator` |`hf mf gsetblk `|N |`Write block to card` |`hf mf gview `|N |`View card` +|`hf mf gchpwd `|N |`Change card access password. Warning!` |`hf mf gdmcfg `|N |`Read config block from card` |`hf mf gdmsetcfg `|N |`Write config block to card` |`hf mf gdmsetblk `|N |`Write block to card` |`hf mf ndefformat `|N |`Format MIFARE Classic Tag as NFC Tag` |`hf mf ndefread `|N |`Read and print NDEF records from card` |`hf mf ndefwrite `|N |`Write NDEF records to card` +|`hf mf encodehid `|N |`Encode a HID Credential / NDEF record to card` ### hf mfp @@ -646,8 +652,13 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf ntag424 help `|Y |`This help` |`hf ntag424 info `|N |`Tag information` -|`hf ntag424 sdm `|N |`Prints NDEF records from card` |`hf ntag424 view `|Y |`Display content from tag dump file` +|`hf ntag424 auth `|N |`Test authentication with key` +|`hf ntag424 read `|N |`Read file` +|`hf ntag424 write `|N |`Write file` +|`hf ntag424 getfs `|N |`Get file settings` +|`hf ntag424 changefs `|N |`Change file settings` +|`hf ntag424 changekey `|N |`Change key` ### hf seos diff --git a/doc/datasheets/Atmel SAM7S Series Datasheet AT91SAM 6175M–ATARM–26-Oct-12.pdf b/doc/datasheets/Atmel_SAM7S_Series_Datasheet_AT91SAM_6175M-ATARM-26-Oct-12.pdf similarity index 100% rename from doc/datasheets/Atmel SAM7S Series Datasheet AT91SAM 6175M–ATARM–26-Oct-12.pdf rename to doc/datasheets/Atmel_SAM7S_Series_Datasheet_AT91SAM_6175M-ATARM-26-Oct-12.pdf diff --git a/doc/datasheets/DS_N76E003_EN_Rev1.09.pdf b/doc/datasheets/DS_N76E003_EN_Rev1.09.pdf new file mode 100644 index 000000000..0fae083ac Binary files /dev/null and b/doc/datasheets/DS_N76E003_EN_Rev1.09.pdf differ diff --git a/doc/datasheets/W25X40CL_H 20220301.pdf b/doc/datasheets/W25X40CL_H_20220301.pdf similarity index 100% rename from doc/datasheets/W25X40CL_H 20220301.pdf rename to doc/datasheets/W25X40CL_H_20220301.pdf diff --git a/doc/datasheets/XILINX Spartan-II FPGA Family.pdf b/doc/datasheets/XILINX_Spartan-II_FPGA_Family.pdf similarity index 100% rename from doc/datasheets/XILINX Spartan-II FPGA Family.pdf rename to doc/datasheets/XILINX_Spartan-II_FPGA_Family.pdf diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index c588d3b54..ab125ffb6 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -8,20 +8,29 @@ Useful docs: # Table of Contents - +- [Low frequency](#low-frequency) + * [T55xx](#t55xx) + * [EM4x05](#em4x05) + * [ID82xx series](#id82xx-series) + * [ID8265](#id8265) + * [ID-F8268](#id-f8268) + * [K8678](#k8678) + * [H series](#h-series) + * [H1](#h1) + * [H5.5 / H7](h55--h7) + * [i57 / i57v2](#i57--i57v2) - [ISO14443A](#iso14443a) * [Identifying broken ISO14443A magic](#identifying-broken-iso14443a-magic) - [MIFARE Classic](#mifare-classic) * [MIFARE Classic block0](#mifare-classic-block0) * [MIFARE Classic Gen1A aka UID](#mifare-classic-gen1a-aka-uid) * [MIFARE Classic Gen1B](#mifare-classic-gen1b) - * [MIFARE Classic Gen1A OTP/One Time Programming](#mifare-classic-gen1a-otpone-time-programming) + * [MIFARE Classic OTP2](#mifare-classic-otp2) * [MIFARE Classic DirectWrite aka Gen2 aka CUID](#mifare-classic-directwrite-aka-gen2-aka-cuid) * [MIFARE Classic DirectWrite, FUID version aka 1-write](#mifare-classic-directwrite-fuid-version-aka-1-write) - * [MIFARE Classic DirectWrite, UFUID version](#mifare-classic-directwrite-ufuid-version) - * [MIFARE Classic, other versions](#mifare-classic-other-versions) * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) - * [MIFARE Classic Gen4 aka GDM](#mifare-classic-gen4-aka-gdm) + * [MIFARE Classic USCUID](#mifare-classic-uscuid) + * [MIFARE Classic, other versions](#mifare-classic-other-versions) * [MIFARE Classic Super](#mifare-classic-super) - [MIFARE Ultralight](#mifare-ultralight) * [MIFARE Ultralight blocks 0..2](#mifare-ultralight-blocks-02) @@ -30,6 +39,11 @@ Useful docs: * [MIFARE Ultralight EV1 DirectWrite](#mifare-ultralight-ev1-directwrite) * [MIFARE Ultralight C Gen1A](#mifare-ultralight-c-gen1a) * [MIFARE Ultralight C DirectWrite](#mifare-ultralight-c-directwrite) + * [UL series (RU)](#ul-series-ru) + * [UL-Y](#ul-y) + * [ULtra](#ultra) + * [UL-5](#ul-5) + * [UL, other chips](#ul-other-chips) - [NTAG](#ntag) * [NTAG213 DirectWrite](#ntag213-directwrite) * [NTAG21x](#ntag21x) @@ -37,12 +51,195 @@ Useful docs: * ["DESFire" APDU, 7b UID](#desfire-apdu-7b-uid) * ["DESFire" APDU, 4b UID](#desfire-apdu-4b-uid) - [ISO14443B](#iso14443b) - * [ISO14443B magic](#iso14443b-magic) + * [Tiananxin TCOS CPU card](#tiananxin-tcos-cpu-card) - [ISO15693](#iso15693) * [ISO15693 magic](#iso15693-magic) - [Multi](#multi) - * [Gen 4 GTU](#gen-4-gtu) + * [UMC](#umc) +- [Other](#other) + * [SID](#sid) + * [NSCK-II](#nsck-ii) +# Low frequency + +## T55xx +^[Top](#top) + +The temic T55xx/Atmel ATA5577 is the most commonly used chip for cloning LF RFIDs. + +A useful document can be found [here](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/T5577_Guide.md). + +### Characteristics + +* 28/24 bytes of user memory (without/with password) +* Universal output settings (data rate, modulation, etc) +* Password protection (4 bytes), usually "19920427" +* Lock bits per page +* Analog frontend setup +* Other names: + * 5577 + * 5200 (CN) + - Cut down version of T55xx chip (no analog frontend setup, no test mode support). + * H2 (RU) + - Seems to be renamed 5200 chip. + * RW125T5 (RU) +* Old variant "T5555" is hard to come across + +### Detect + +``` +[usb] pm3 --> lf search +... +[+] Chipset detection: T55xx +``` + +This will **not** work if you have a downlink mode other than fixed bit length! + +### Commands + +*See ATMEL ATA5577C datasheet for sending commands to chip* + +* **Do not mix "password read" and "regular write" commands! You risk potentially writing incorrect data. +* When replying, the chip will use the modulation and data rate specified in block 0. + +## EM4x05 +^[Top](#top) + +The EM4305 and EM4205 (and 4469/4569) chips are the 2nd most common used chips for cloning LF RFIDs. +It is also used by HID Global (but with a custom chip) for HIDProx credentials. + +### Characteristics + +* 36 bytes of user memory +* Output settings are limited (ASK only, FSK added on HID variant) +* Password protection (4 bytes), usually "84AC15E2" +* Lock page used +* Other names: + * H3 (RU) + * RW125EM (RU) + +### Detect + +``` +[usb] pm3 --> lf search +... +[+] Chipset detection: EM4x05 / EM4x69 +``` + +### Commands + +*See EM microelectronic EM4305 datasheet for sending commands to chip* + +## ID82xx series +^[Top](#top) + +These are custom chinese chips designed to clone EM IDs only. Often times, these are redesigned clones of Hitag chips. + +### ID8265 +^[Top](#top) + +This is the cheapest and most common ID82xx chip available. It is usually sold as T55xx on AliExpress, with excuses to use cloners. + +#### Characteristics + +* Chip is likely a Hitag μ (micro) +* Password protection (4b), usually "1AC4999C" +* Currently unimplemented in proxmark3 client +* Other names: + * ID8210 (CN) + * H-125 (CN) + * H5 (RU) + - The sales of "H5" have been ceased because "the chip was leaked". + +#### Detect + +``` +[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00011 -s 3000 +[usb] pm3 --> data plot +``` + +Check the green line of the plot. It must be a straight line at the end with no big waves. + +### ID-F8268 +^[Top](#top) + +This is an "improved" variant of ID82xx chips, bypassing some magic detection in China. + +#### Characteristics + +* Chip is likely a Hitag 1 +* Unsure whether password protection is used +* Currently unimplemeneted in proxmark3 client +* Other names: + - F8278 (CN) + - F8310 (CN) + +#### Detect + +``` +[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 -s 3000 +[usb] pm3 --> data plot +``` + +Check the green line of the plot. It must be a straight line at the end with no big waves. + +### K8678 +^[Top](#top) + +This is an "even better" chip, manufactured by Hyctec. + +#### Characteristics + +* Chip is likely a Hitag S256 +* Plain mode used, no password protection +* Currently unimplemented in proxmark3 client +* Memory access is odd (chip doesnt reply to memory access commands for unknown reason) + +#### Detect + +``` +[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 -s 3000 +[usb] pm3 --> data plot +``` + +Check the green line of the plot. It must be a straight line at the end with no big waves. + +## H series +^[Top](#top) + +These are chips sold in Russia, manufactured by iKey LLC. Often times these are custom. + +### H1 +^[Top](#top) + +Simplest EM ID cloning chip available. Officially discontinued. + +#### Characteristics + +* Currently almost all structure is unknown +* No locking or password protection + * "OTP" chip is same chip, but with EM ID of zeroes. Locked after first write +* Other names: + * RW64bit + * RW125FL + + +### H5.5 / H7 +^[Top](#top) + +First "advanced" custom chip with H naming. + +#### Characteristics + +* Currently all structure is unknown +* No password protection +* Only supported by Russian "TMD"/"RFD" cloners +* H7 is advertised to work with "Stroymaster" access control +* Setting ID to "3F0096F87E" will make the chip show up like T55xx + +### i57 / i57v2 + +\[ Chip is discontinued, no info \] # ISO14443A @@ -109,7 +306,8 @@ UID 7b: ## MIFARE Classic Gen1A aka UID ^[Top](#top) -aka MF ZERO +* Other names: + - ZERO (RU) ### Identify ^[Top](#top) @@ -267,16 +465,17 @@ hf 14a info * Read: `40(7)`, `30xx` * Write: `40(7)`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc -## MIFARE Classic Gen1A OTP/One Time Programming +## MIFARE Classic OTP2 ^[Top](#top) -aka MF OTP 2.0 - Similar to Gen1A, but after first block 0 edit, tag no longer replies to 0x40 command. -Initial UID is 00000000 +### Characteristics -All bytes are 00 from factory wherever possible. +* Initial UID is 00000000 +* BCC: unknown +* SAK/ATQA: fixed +* All bytes are 00 from factory wherever possible. ### Identify ^[Top](#top) @@ -287,6 +486,7 @@ Only possible before personalization. hf 14a info ... [+] Magic capabilities : Gen 1a +[+] Prng detection: hard ``` ### Magic commands @@ -299,6 +499,11 @@ hf 14a info (also referred as MCT compatible by some sellers) +* Other names: + * MF-8 (RU) + * MF3 (RU) + - What's so special about this chip in particular..? + ### Identify ^[Top](#top) @@ -442,16 +647,19 @@ hf 14a reader ## MIFARE Classic DirectWrite, FUID version aka 1-write ^[Top](#top) -aka MF OTP - Same as MIFARE Classic DirectWrite, but block0 can be written only once. -Initial UID is AA55C396 +* Other names: + - OTP (RU) + +### Characteristics + +* Initial UID is AA55C396 ### Identify ^[Top](#top) -Only possible before personalization. +Only possible before personalization. *It is also possible after, but unknown how.* ``` hf 14a info @@ -459,28 +667,6 @@ hf 14a info [+] Magic capabilities : Write Once / FUID ``` -## MIFARE Classic DirectWrite, UFUID version -^[Top](#top) - -Same as MIFARE Classic DirectWrite, but block0 can be locked with special command. - -### Identify -^[Top](#top) - -**TODO** - -### Proxmark3 commands -^[Top](#top) - -To lock definitively block0: -``` -hf 14a raw -a -k -b 7 40 -hf 14a raw -k 43 -hf 14a raw -k -c e000 -hf 14a raw -k -c e100 -hf 14a raw -c 85000000000000000000000000000008 -``` - ## MIFARE Classic Gen3 aka APDU ^[Top](#top) @@ -554,85 +740,131 @@ hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000 hf 14a raw -s -c 90FD111100 ``` -## MIFARE Classic Gen4 aka GDM +## MIFARE Classic USCUID ^[Top](#top) -Tag has shadow mode enabled from start. -Meaning every write or changes to normal MFC memory is restored back to a copy from persistent memory after about 3 seconds -off rfid field. -Tag also seems to support Gen2 style, direct write, to block 0 to the normal MFC memory. +TLDR: These magic cards have a 16 byte long configuration page, which usually starts with 0x85. +All of the known tags using this, except for Ultralight tags, are listed here. -The persistent memory is also writable. For that tag uses its own backdoor commands. -for example to write, you must use a customer authentication byte, 0x80, to authenticate with an all zeros key, 0x0000000000. -Then send the data to be written. - -This tag has simular commands to the [UFUID](#mifare-classic-directwrite-ufuid-version) -This indicates that both tagtypes are developed by the same person. - -**OBS** - -When writing to persistent memory it is possible to write _bad_ ACL and perm-brick the tag. - -**OBS** - -It is possible to write a configuration that perma locks the tag, i.e. no more magic - -### Identify -^[Top](#top) - -``` -hf 14a info -... -[+] Magic capabilities : Gen 4 GDM -``` -### Magic commands -^[Top](#top) - -* Auth: `80xx`+crc -* Write: `A8xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc -* Read config: `E000`+crc -* Write config: `E100`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc +You cannot turn a Classic tag into an Ultralight and vice-versa! ### Characteristics ^[Top](#top) -* Have no knowledge in ATQA/SAK/BCC quirks or if there is a wipe, softbrick recover -* Its magic part seem to be three identified custom command. -* Auth command 0x80, with the key 0x0000000000, Write 0xA8 allows writing to persistent memory, Read 0xE0 which seems to return a configuration. This is unknown today what these bytes are. +* UID: 4/7 bytes +* ATQA: always read from block 0 +* SAK: read from backdoor or configuration +* BCC: read from memory, beware! +* ATS: no/unknown -Read config: -1. sending custom auth with all zeros key -2. send 0xE000, will return the configuration bytes. -`results: 850000000000000000005A5A00000008` +### Magic commands +^[Top](#top) +* Magic authentication: select, `8000+crc`, `[Crypto1 Auth: 000000000000]` + - Backdoor read: `38xx+crc` + - Backdoor write: `A8xx+crc`, `[16 bytes data]+crc` -Mapping of configuration bytes so far: + - Read configuration: `E000+crc` + - Write configuration: `E100+crc`; `[16 bytes data]+crc` +* Magic wakeup (A: 00): `40(7)`, `43` +* Magic wakeup (B: 85): `20(7)`, `23` + - Backdoor read main block: `30xx+crc` + - Backdoor write main block: `A0xx+crc`, `[16 bytes data]+crc` + - Read hidden block: `38xx+crc` + - Write hidden block: `A8xx+crc`, `[16 bytes data]+crc` + + - Read configuration: `E000+crc` + - Write configuration: `E100+crc` + + **DANGER** + - Set main memory and config to 00 `F000+crc` + - Set main memory and config to FF `F100+crc` + - Set main memory and config to 55 (no 0A response) `F600+crc` + - Set backdoor memory to 00 `F800+crc` + - Set backdoor memory to FF `F900+crc` + - Set backdoor memory to 55 (no 0A response) `FE00+crc` + +### USCUID configuration guide +^[Top](#top) + +1. Configuration ``` -850000000000000000005A5A00000008 - ^^ --> SAK +85000000000000000000000000000008 + ^^^^^^ ^^ ^^ >> ??? Mystery ??? +^^^^ >> Gen1a mode (works with bitflip) + ^^ >> Magic wakeup command (00 for 40-43; 85 for 20-23) + ^^ >> Block use of Key B if readable by ACL + ^^ >> CUID mode + ^^ >> MFC EV1 CL2 Perso config* + ^^ >> Shadow mode** + ^^ >> Magic Auth command + ^^ >> Static encrypted nonce mode + ^^ >> Signature sector + ^^ >> SAK*** + +To enable an option, set it to 5A. +* 5A - unfused F0. C3 - F0: CL2 UID; A5 - F1: CL2 UID with anticollision shortcut; 87 - F2: CL1 Random UID; 69 - F3: CL1 non-UID. Anything else is going to be ignored, and set as 4 bytes. +** Do not change the real ACL! Backdoor commands only acknowledge FF0780. To recover, disable this byte and issue regular write to sector trailer. +*** If perso byte is enabled, this SAK is ignored, and hidden SAK is used instead. +``` +* Gen1a mode: Allow using custom wakeup commands, like real gen1a chip, to run backdoor commands, as well as some extras. +* Magic wakeup command: Use different wakeup commands for entering Gen1a mode. A) 00 - 40(7), 43; B) 85 - 20(7), 23. +* Block use of Key B if readable by ACL: Per the MF1ICS50 datasheet, if Key B is readable by the ACL, using it shall give a Cmd Error 04. This option controls whether it happens or not. +* CUID mode: Allow direct write to block 0, instead of giving Cmd Error 04. +* MFC EV1 CL2 Perso config: When configured, the tag behaves like a real Mifare Classic EV1 7B UID tag, and reads UID from backdoor blocks. Otherwise, the tag acts like a 4 byte tag. +* Shadow mode: Writes to memory persisting in tag RAM. As soon as no power is left, the contents are restored to saved data. +* Magic Auth Command: Acknowledge command `8000` after selection, and call for Crypto1 auth with key `000000000000`. +* Static encrypted nonce mode: Use static encrypted nonces for authentication, making key recovery impossible. +* Signature sector: Acknowledge auth commands to sector 17, which is stored in backdoor sector 1. +* SAK: If perso byte is not set, after UID select, send this value. + + +2. Backdoor blocks ``` -Write config: -1. sending custom auth with all zeros key -2. send 0xE100 -3. send 16 bytes +Sector 0 +88 04 BD E5 D4 04 6A BB 5B 80 0A 08 44 00 00 00 - Block 0: Perso F0, F1 data +^^ ^^ ^^ ^^ - UID0 + ^^ - BCC0 + ^^ - SAK0 (0x04 to call for CL2) + ^^ ^^ ^^ ^^ - UID1 + ^^ - BCC1 + ^^ - SAK1 + ^^ ^^ ^^ ^^ - Unused +04 BD E5 6A 36 08 00 00 00 00 00 00 00 00 00 00 - Block 1: Perso F3 data +^^ ^^ ^^ ^^ - UID0 + ^^ - BCC0 + ^^ - SAK0 + ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ - Unused +Block 2: unused +Block 3: ignored (custom keys, acl; broken acl ignored - anticollision will still work) +Sector 1 +[Signature sector (#17) - needs config byte 13 (from 0) enabled to allow auth] +Sectors 2-15 +[Unused] +``` -**Warning** +### Variations +^[Top](#top) +| Factory configuration | Name | +| --- | --- | +| 850000000000000000005A5A00000008 | GDMIC | +| 850000000000005A0000005A5A5A0008 | UCUID | +| 8500000000005A00005A005A005A0008 | "7 byte hard" | +| 7AFF850102015A00005A005A005A0008 | M1-7B | +| 7AFF85000000000000FF000000000008 | FUID | +| 7AFF000000000000BAFA358500000008 | PFUID | +| 7AFF000000000000BAFA000000000008 | UFUID | -Example of configuration to Perma lock tag: -`85000000000000000000000000000008` +*Not all tags are the same!* UFUID and PFUID* are not full implementations of Magic85 - they only acknowledge the first 8 (except wakeup command) and last config byte(s). +*Read and write config commands are flipped -It is unknown what kind of block 0 changes the tag supports -* UID: 4b -* ATQA/SAK: unknown -* BCC: unknown -* ATS: none - -### Proxmark3 commands +#### Proxmark3 commands ^[Top](#top) ``` -# Write to persistent memory +Using magic auth: +# Write to persistent memory: hf mf gdmsetblk # Read configuration (0xE0): @@ -651,15 +883,14 @@ No implemented commands today **TODO** -* ZXUID, EUID, ICUID, KUID, HUID, RFUID ? -* Some cards exhibit a specific SAK=28 ?? +* ZXUID, EUID, ICUID, KUID? ## MIFARE Classic Super ^[Top](#top) It behaves like regular Mifare Classic but records reader auth attempts. -#### MIFARE Classic Super Gen1 +### MIFARE Classic Super Gen1 ^[Top](#top) Old type of cards, hard to obtain. They are DirectWrite, UID can be changed via 0 block or backdoor commands. @@ -684,19 +915,19 @@ Backdoor commands provided over APDU. Format: 👉 You can't change UID with backdoor command if incorrect data is written to the 0 sector trailer! -#### MIFARE Classic Super Gen1B +### MIFARE Classic Super Gen1B DirectWrite card, ATS unknown. Probably same as Gen1, except backdoor commands. Implementation: https://github.com/netscylla/super-card/blob/master/libnfc-1.7.1/utils/nfc-super.c -#### MIFARE Classic Super Gen2 +### MIFARE Classic Super Gen2 ^[Top](#top) New generation of cards, based on limited Gen4 chip. Emulates Gen1 backdoor protocol, but can store up to 7 different traces. -Card always answer `ff ff ff ff` to auth, so writing/reading it via Mifare protocol is impossible. +Card always answers `ff ff ff ff` as `at`, so reading/writing it via Mifare protocol is impossible. -UID is changeable via Gen4 backdoor write to 0 block. +UID is changeable via UMC backdoor write to 0 block. * UID: 4b and 7b versions * ATQA/SAK: fixed @@ -707,12 +938,53 @@ Gen4 commands available: ``` CF 34 <1b length><0-16b ATS> // Configure ATS -CF CC // Factory test, returns 00 00 00 02 AA +CF CC // Version information, returns 00 00 00 02 AA CF CD <1b block number><16b block data> // Backdoor write 16b block CF CE <1b block number> // Backdoor read 16b block CF FE <4b new_password> // Change password ``` +### MIFARE Classic Super Furui +^[Top](#top) + +#### Characteristics +^[Top](#top) + +* SAK/ATQA: play blindly the block0 bytes, beware! +* BCC: play blindly the block0 BCC bytes, beware! +* PRNG: hard + +**!!!WARNING!!!** This tag can die for no reason (no reply to WUPA/REQA). We don't know why this happens. + +#### Identify +^[Top](#top) + +``` +[usb] pm3 --> hf 14a raw -sct 250 AAA500000000000000000000000000000000 +[+] 90 00 +``` + +#### Magic commands +^[Top](#top) + +* Configure: `AAA5[16 byte config]`+crc +* Write block 0: `AAA4[4b UID][1b BCC][1b SAK][2b ATQA reversed]0000000000000000`+crc +* Recover trace: `AAA8[00/01][00-08]`+crc + +Caution: tag does not append CRC to magic responses! + +Please use config as 00 bytes. + +Parsing traces: +``` +44 33 22 11 03 61 08 68 7A C7 4B 62 43 A6 11 6F 64 F3 +^^ ^^ ^^ ^^ -- UID + ^^ ^^ -- auth command, reversed + ^^ ^^ ^^ ^^ -- Auth (nt) + ^^ ^^ ^^ ^^ -- Auth (nr) + ^^ ^^ ^^ ^^ -- Auth (ar) +``` + ### Identify ^[Top](#top) @@ -724,6 +996,15 @@ hf 14a info [+] Magic capabilities : Super card (Gen ?) ``` +### Proxmark3 commands + +``` +[usb] pm3 --> hf mf supercard +... + +[usb] pm3 --> hf mf supercard --furui +... +``` # MIFARE Ultralight ^[Top](#top) @@ -746,6 +1027,7 @@ Int is internal, typically 0x48 Anticol shortcut (CL1/3000) is supported for UL, ULC, NTAG except NTAG I2C +Some cards have a password: `B6AA558D`. Usually "copykey" chips. ## MIFARE Ultralight Gen1A ^[Top](#top) @@ -874,8 +1156,6 @@ See `--uid` and `--full` ## MIFARE Ultralight EV1 DirectWrite ^[Top](#top) -aka UL2 - Similar to MFUL DirectWrite ### Identify @@ -957,11 +1237,6 @@ hf 14a info * ATS: 0A78008102DBA0C119402AB5 * Anticol shortcut (CL1/3000): fails -**TODO** - -* UL-X, UL-Y, UL-Z, ULtra, UL-5 ? - - # NTAG ^[Top](#top) @@ -1027,6 +1302,78 @@ Anticol shortcut (CL1/3000): fails script run hf_mfu_magicwrite -h ``` +## UL series (RU) +^[Top](#top) + +Custom chips, manufactured by iKey LLC for cloning Ultralight tags. + +### UL-Y +^[Top](#top) + +Ultralight magic, 16 pages. Recommended for Vizit RF3.1 with markings "3.1" or "4.1". +Behavior: allows writes to page 0-2. + +#### Identify +^[Top](#top) + +``` +hf mfu rdbl --force -b 16 +hf 14a raw -sct 250 60 +``` +If tag replies with +`Cmd Error: 00` +`00 00 00 00 00 00 00 00` +then it is UL-Y. + +### ULtra +^[Top](#top) + +Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. +Behavior: allows writes to page 0-2. + +#### Identify +^[Top](#top) + +``` +hf mfu info +... +[=] TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 +[=] --- Tag Version +[=] Raw bytes: 00 34 21 01 01 00 0E 03 +``` + +Remember that this is not a reliable method of identification, as it interferes with locked [UL-5](#mifare-ul-5). + +### UL-5 +^[Top](#top) + +Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page and if [ULtra](#mifare-ultra) has failed. + +Behavior: similar to Ultra, but after editing page 0, tag becomes original Mifare Ultralight EV1. + +**WARNING!** When using UL-5 to clone, write UID pages in inverse (from 2 to 0) and do NOT make mistakes! This tag does not allow reversing one-way actions (OTP page, lock bits). + +#### Identify +^[Top](#top) + +``` +hf mfu info +[=] UID: AA 55 C3 A1 30 61 80 +TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 +[=] --- Tag Version +[=] Raw bytes: 00 34 21 01 01 00 0E 03 +``` + +After personalization it is not possible to identify UL-5. + +Some chips have UID of `AA 55 C3 A4 30 61 80`. + +### UL, other chips + +**TODO** + +UL-X, UL-Z - ? + # DESFire ^[Top](#top) @@ -1084,7 +1431,8 @@ Android compatible ### Characteristics ^[Top](#top) -* ATQA: 0008 ??? This is not DESFire, 0008/20 doesn't match anything +* ATQA: 0008 + * This is FM1208-9, NOT DESFire! * SAK: 20 * ATS: 0675338102005110 or 06757781028002F0 @@ -1129,12 +1477,37 @@ hf 14a info # ISO14443B ^[Top](#top) -## ISO14443B magic +## Tiananxin TCOS CPU card ^[Top](#top) -No such card is available. +This is a card sold on Taobao for testing readers. +ISO14443-4 compliant. -Some vendor allow to specify an ID (PUPI) when ordering a card. +### Identify + +``` +hf 14a apdu -s 90B2900000 // Get Card OS version +>>> 90 B2 90 00 00 +<<< 54 43 4F 53 20 56 31 2E 34 2E 30 90 00 | TCOS V1.4.0.. +``` + +### Magic commands + +All commands in APDU. + +``` +CL IN P1 P2 Lc Data +90 F4 CC CC 01 [..1 ] // Change protocol used (1: ISO14443 [AA - type A, BB - type B]) +90 F6 CC CC 01 [TA1 ] // Change TA1 value (transfer speed) +90 F8 CC CC 01 [..1 ] // Use random UID/PUPI value (1: FF: static, AB: random) +90 F8 DD DD 01 [..1 ] // Set UID length (1: bytes in UID (04, 07, 0A for 4, 7, 10 bytes accordingly)) +90 F8 EE EE 0B [... ] // Set UID/PUPI value (FF+enter UID value here). To clear, use Lc=01; data=00. +90 FA CC CC 01 [FSCI] // Set FSCI (1: value 0-8) +90 FC CC CC 01 [SFGI] // Set SFGI (DO NOT SET TOO HIGH!) (1: value 0-E) +90 FE CC CC 01 [FWI ] // Set FWI (DO NOT SET BELOW 4!!!) (value 0-E) +``` + +More commands to follow. Be careful with some. # ISO15693 ^[Top](#top) @@ -1164,7 +1537,7 @@ script run hf_15_magic -u E004013344556677 # Multi ^[Top](#top) -## Gen 4 GTU +## UMC ^[Top](#top) A.k.a ultimate magic card, most promenent feature is shadow mode (GTU) and optional password protected backdoor commands. @@ -1197,6 +1570,8 @@ Can emulate MIFARE Classic, Ultralight/NTAG families, 14b UID & App Data 👉 **TODO** If the password is not default, Tag doesn't get identified correctly by latest Proxmark3 client (it might get mislabeled as MFC Gen2/CUID, Gen3/APDU or NTAG21x Modifiable, depending on configured UID/ATQA/SAK/ATS) +👉 **TODO** Using C6 command can change config due to a bug in some cards. CC should be used instead. + ``` hf 14a info [+] Magic capabilities : Gen 4 GTU @@ -1217,7 +1592,7 @@ There are two ways to program this card. ***OR*** - 2. Use the hf_mf_ultimatecard.lua script commands designated but the `script run hf_mf_ulimatecard` examples. + 2. Use the hf_mf_ultimatecard.lua script commands designated but the `script run hf_mf_ultimatecard` examples. script run hf_mf_ultimatecard.lua -h @@ -1289,7 +1664,7 @@ CF 69 <00-01> // (De)Activate Ultralight mode CF 6A <00-03> // Select Ultralight mode CF 6B <1b> // Set Ultralight and M1 maximum read/write sectors CF C6 // Dump configuration -CF CC // Factory test, returns 6666 +CF CC // Version info, returns `00 00 00 [03 A0 (old) / 06 A0 (new) ]` CF CD <1b block number><16b block data> // Backdoor write 16b block CF CE <1b block number> // Backdoor read 16b block CF CF <1b param> // (De)Activate direct write to block 0 @@ -1304,10 +1679,10 @@ Default ``: `00000000` * UID: 4b, 7b and 10b versions * ATQA/SAK: changeable -* BCC: auto +* BCC: computed * ATS: changeable, can be disabled -* Card Type: changeable -* Shadow mode: GTU +* Card Type: changeable +* Shadow mode: GTU * Backdoor password mode ### Proxmark3 commands @@ -1446,9 +1821,9 @@ Ultralight mode, 10b UID ### Set 14443B UID and ATQB ^[Top](#top) ^^[Gen4](#g4top) -UID and ATQB are configured according to block0 with a (14a) backdoor write. - -UID size is always 4 bytes. +* UID and ATQB are configured according to block0 with a (14a) backdoor write. +* UID size is always 4 bytes. +* 14B will show up only on new cards. Example: ``` @@ -1557,6 +1932,7 @@ hf 14a raw -s -c -t 1000 CF32<1b param> * `` * `00`: pre-write, shadow data can be written * `01`: restore mode + - WARNING: new UMC (06a0) cards return garbage data when using 01, please use 04! * `02`: disabled * `03`: disabled, high speed R/W mode for Ultralight? @@ -1612,7 +1988,9 @@ hf 14a raw -s -c -t 1000 CF00000000CF01 ### Change backdoor password ^[Top](#top) ^^[Gen4](#g4top) -All backdoor operations are protected by a password. If password is forgotten, the card can't be recovered. Default password is `00000000`. +All backdoor operations are protected by a password. If password is forgotten, it can't be recovered. Default password is `00000000`. + +WARNING: new UMC (06A0) returns 6300 when issuing password change command. Please write the password using F0 and entering the full configuration, but with the new password. Change password: ``` @@ -1758,3 +2136,72 @@ hf mfu wrbl -b 250 -d 00040402 --force hf mfu wrbl -b 251 -d 01001303 --force hf mfu info ``` + +# Other +^[Top](#top) + +These are chips to clone other ICs. Usually the originals are only sold in China. + +## SID +^[Top](#top) + +- Magic tag for Fudan FM1208-9 chips + +### Characteristics +^[Top](#top) +- ISO14443-A tag +- ATQA-SAK: `0008`-`20` +- ATS: `10 78 80 A0 02 00 9D 46 16 40 00 A3 [UID]` +- Compared to real FM1208 chip: + - CLA byte is ignored + - Command parsing is irregular (some replies are wrong) + +### Magic commands +^[Top](#top) + +**WARNING!!!** Risk of bricking tag - cause is unknown +- Below you can find a list of all INS bytes not present on real FM1208 chip, and what their output is when executed (P1, P2, Lc = 00) + - Results may vary between chips: +``` +INS | RES +0A | 44454641554C540000002018112840000000000000000000000000000000000000000000000000000000400000000000 +3B | 00000000001C0EF90000000000000000000000000000000000000000000000002000000000C09040009002840000000000000000000000000000000000006C0FC08700EB1A9F1BA01801010019000000000000000000000000000090000000000000094B066600000000007D000000000000000000000000000000003B000000107880A002009D46164000A3CA81E15000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +3C* | 0000 +3D | 6700 +7D | Tag does not reply (if 0