diff --git a/Makefile b/Makefile index e42c0caa9..b2dcba430 100644 --- a/Makefile +++ b/Makefile @@ -251,7 +251,7 @@ print-%: ; @echo $* = $($*) cliparser: # Get list of all commands - cat doc/commands.md | grep -e ^\|\` | cut -f 2 -d "\`" | grep -v 'help\|list\|mem spiffs\|quit\|exit' | awk '{$$1=$$1};1' > cliparser_all_commands.tmp + cat doc/commands.md | grep -e ^\|\` | cut -f 2 -d "\`" | grep -v 'help\|list\|script run\|quit\|exit' | awk '{$$1=$$1};1' > cliparser_all_commands.tmp # Get list of cliparserized commands grep -r CLIParserInit ./client/src/ | cut -f 2 -d "\"" | awk '{$$1=$$1};1' > cliparser_done.tmp # Determine commands that still need cliparser conversion diff --git a/README.md b/README.md index 224e822af..02c08a7f8 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,13 @@ | ------------------- |:-------------------:| -------------------:| |[What has changed?](#what-has-changed) | **[Setup and build for Linux](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md)** | [Compilation Instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md)| |[Development](#development) | **[Important notes on ModemManager for Linux users](/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md)** | [Validating proxmark client functionality](/doc/md/Use_of_Proxmark/1_Validation.md) | -|[Why didn't you base it on official Proxmark3 Master?](#why-didnt-you-base-it-on-official-proxmark3-master)| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| +|| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| |[Proxmark3 GUI](#proxmark3-gui)|**[Setup and build for Windows](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md)**|[Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)| |[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) || |[Donations](#Donations)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.md)| ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| -||**[JTAG](/doc/jtag_notes.md)**|[T55xx Guide](/doc/T5577_Guide.md)| +||**[JTAG](/doc/jtag_notes.md)**|[T5577 Introduction Guide](/doc/T5577_Guide.md)| ## Notes / helpful documents @@ -154,10 +154,6 @@ To all distro, package maintainers, we tried to make your life easier. `make install` is now available and if you want to know more. - [Notes for maintainers](/doc/md/Development/Maintainers.md) -## Why didn't you base it on official Proxmark3 Master? - -The separation from official Proxmark3 repo gives us a lot of freedom to create a firmware/client that suits the RDV40 features. We don't want to mess up the official Proxmark3 repo with RDV40 specific code. - ## Proxmark3 GUI The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer. diff --git a/armsrc/Standalone/hf_14asniff.c b/armsrc/Standalone/hf_14asniff.c index c52928825..0a6b48b48 100644 --- a/armsrc/Standalone/hf_14asniff.c +++ b/armsrc/Standalone/hf_14asniff.c @@ -71,13 +71,13 @@ static void DownloadTraceInstructions(void) { Dbprintf(""); Dbprintf("To get the trace from flash and display it:"); - Dbprintf("1. mem spiffs dump o "HF_14ASNIFF_LOGFILE" f trace.trc"); - Dbprintf("2. trace load trace.trc"); - Dbprintf("3. trace list 14a 1"); + Dbprintf("1. mem spiffs dump -s "HF_14ASNIFF_LOGFILE" -d trace.trc"); + Dbprintf("2. trace load -f trace.trc"); + Dbprintf("3. trace list -t 14a -1"); } void ModInfo(void) { - DbpString("hf_14asniff: standalone 'hf 14a sniff', storing in flashmem"); + DbpString(" ISO14443a sniff, storing in flashmem"); DownloadTraceInstructions(); } diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 27e1bf5ab..bbebcab6e 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -130,27 +130,27 @@ static void download_instructions(uint8_t t) { switch (t) { case ICE_STATE_FULLSIM: { DbpString("The emulator memory was saved to SPIFFS"); - DbpString("1. " _YELLOW_("mem spiffs dump o " HF_ICLASS_FULLSIM_MOD_BIN " f " HF_ICLASS_FULLSIM_MOD" e")); + DbpString("1. " _YELLOW_("mem spiffs dump -s " HF_ICLASS_FULLSIM_MOD_BIN " -d " HF_ICLASS_FULLSIM_MOD" -e")); DbpString("2. " _YELLOW_("hf iclass view -f " HF_ICLASS_FULLSIM_MOD_BIN)); break; } case ICE_STATE_ATTACK: { DbpString("The collected data was saved to SPIFFS. The file names below may differ"); DbpString("1. " _YELLOW_("mem spiffs tree")); - DbpString("2. " _YELLOW_("mem spiffs dump o " HF_ICLASS_ATTACK_BIN " f " HF_ICLASS_ATTACK_BIN)); + DbpString("2. " _YELLOW_("mem spiffs dump -s " HF_ICLASS_ATTACK_BIN " -d " HF_ICLASS_ATTACK_BIN)); DbpString("3. " _YELLOW_("hf iclass loclass -f " HF_ICLASS_ATTACK_BIN)); break; } case ICE_STATE_READER: { DbpString("The found tags was saved to SPIFFS"); DbpString("1. " _YELLOW_("mem spiffs tree")); - DbpString("2. " _YELLOW_("mem spiffs dump h")); + DbpString("2. " _YELLOW_("mem spiffs dump -h")); break; } case ICE_STATE_DUMP_SIM: { DbpString("The found tag will be dumped to " HF_ICALSSS_READSIM_TEMP_BIN); DbpString("1. " _YELLOW_("mem spiffs tree")); - DbpString("2. " _YELLOW_("mem spiffs dump h")); + DbpString("2. " _YELLOW_("mem spiffs dump -h")); break; } } diff --git a/armsrc/Standalone/hf_legic.c b/armsrc/Standalone/hf_legic.c index 6ebe6eff6..40174ce87 100644 --- a/armsrc/Standalone/hf_legic.c +++ b/armsrc/Standalone/hf_legic.c @@ -39,7 +39,7 @@ * * To delete a dump file from flash: * - * 1. mem spiffs remove hf-legic-XXYYZZWW-dump.bin + * 1. mem spiffs remove -f hf-legic-XXYYZZWW-dump.bin * */ @@ -50,7 +50,7 @@ static void DownloadLogInstructions(void) { Dbprintf("[=] " _YELLOW_("-") " mem spiffs tree"); Dbprintf(""); Dbprintf("[=] To save a dump file from flash to client:"); - Dbprintf("[=] " _YELLOW_("-") " mem spiffs dump o hf-legic-UID-dump.bin f hf-legic-UID-dump.bin"); + Dbprintf("[=] " _YELLOW_("-") " mem spiffs dump -s hf-legic-UID-dump.bin -d hf-legic-UID-dump.bin"); } #endif diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c index d125b0c03..0b1e6cbfe 100644 --- a/armsrc/Standalone/lf_icehid.c +++ b/armsrc/Standalone/lf_icehid.c @@ -36,7 +36,7 @@ * * To retrieve log file from flash: * - * 1. mem spiffs dump o lf_hidcollect.log f lf_hidcollect.log + * 1. mem spiffs dump -s lf_hidcollect.log -d lf_hidcollect.log * Copies log file from flash to your client. * * 2. exit the Proxmark3 client @@ -48,7 +48,7 @@ * * To delete the log file from flash: * - * 1. mem spiffs remove lf_hidcollect.log + * 1. mem spiffs remove -f lf_hidcollect.log */ #define LF_HIDCOLLECT_LOGFILE "lf_hidcollect.log" @@ -57,7 +57,7 @@ static void DownloadLogInstructions(void) { Dbprintf(""); Dbprintf("[=] To get the logfile from flash and display it:"); - Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump o "LF_HIDCOLLECT_LOGFILE" f "LF_HIDCOLLECT_LOGFILE); + Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump -s "LF_HIDCOLLECT_LOGFILE" -d "LF_HIDCOLLECT_LOGFILE); Dbprintf("[=] " _YELLOW_("2.") " exit proxmark3 client"); Dbprintf("[=] " _YELLOW_("3.") " cat "LF_HIDCOLLECT_LOGFILE); } diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c index 2a7a686fb..f06cd9b07 100644 --- a/armsrc/Standalone/lf_tharexde.c +++ b/armsrc/Standalone/lf_tharexde.c @@ -42,25 +42,25 @@ * - LED D: unmounting/sync'ing flash (normally < 100ms) * * To upload input file (eml format) to flash: - * - mem spiffs load f o lf_em4x50_simulate.eml + * - mem spiffs upload -s -d lf_em4x50_simulate.eml * * To retrieve password file from flash: - * - mem spiffs dump o lf_em4x50_passwords.log f + * - mem spiffs dump -s lf_em4x50_passwords.log * * To retrieve log file from flash: - * - mem spiffs dump o lf_em4x50_collect.log f + * - mem spiffs dump -s lf_em4x50_collect.log * * This module emits debug strings during normal operation -- so try it out in * the lab connected to PM3 client before taking it into the field. * * To delete the input file from flash: - * - mem spiffs remove lf_em4x50_simulate.eml + * - mem spiffs remove -f lf_em4x50_simulate.eml * * To delete the log file from flash: - * - mem spiffs remove lf_em4x50_passwords.log + * - mem spiffs remove -f lf_em4x50_passwords.log * * To delete the log file from flash: - * - mem spiffs remove lf_em4x50_collect.log + * - mem spiffs remove -f lf_em4x50_collect.log */ #define STATE_SIM 0 @@ -77,14 +77,14 @@ static void LoadDataInstructions(const char *inputfile) { Dbprintf("To load datafile to flash and display it:"); Dbprintf("1. edit input file %s", inputfile); Dbprintf("2. start proxmark3 client"); - Dbprintf("3. mem spiffs load f o %s", inputfile); + Dbprintf("3. mem spiffs upload -s -d %s", inputfile); Dbprintf("4. start standalone mode"); } static void DownloadLogInstructions(const char *logfile) { Dbprintf(""); Dbprintf("To get the logfile from flash and display it:"); - Dbprintf("1. mem spiffs dump o %s f ", logfile); + Dbprintf("1. mem spiffs dump -s %s", logfile); Dbprintf("2. exit proxmark3 client"); Dbprintf("3. cat "); } diff --git a/armsrc/Standalone/readme.md b/armsrc/Standalone/readme.md index 3775d848c..69880f1a3 100644 --- a/armsrc/Standalone/readme.md +++ b/armsrc/Standalone/readme.md @@ -77,6 +77,7 @@ If your mode is using one of the unique features of the RDV4, add it to the prop ``` STANDALONE_MODES_REQ_SMARTCARD := STANDALONE_MODES_REQ_FLASH := +STANDALONE_MODES_REQ_BT := ``` ## Update MAKEFILE.INC diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 6a6058d3b..77af73cb2 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -73,8 +73,8 @@ extern uint32_t _stack_start, _stack_end; struct common_area common_area __attribute__((section(".commonarea"))); static int button_status = BUTTON_NO_CLICK; static bool allow_send_wtx = false; -static uint16_t tearoff_delay_us = 0; -static bool tearoff_enabled = false; +uint16_t tearoff_delay_us = 0; +bool tearoff_enabled = false; int tearoff_hook(void) { if (tearoff_enabled) { @@ -2078,7 +2078,7 @@ static void PacketReceived(PacketCommandNG *packet) { Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result); } // Trigger a finish downloading signal with an ACK frame - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } @@ -2087,78 +2087,94 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs STAT : %s", filename); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Filename received for spiffs STAT : %s", filename); + } + int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)filename); - if (changed) rdv40_spiffs_lazy_unmount(); - reply_mix(CMD_ACK, size, 0, 0, 0, 0); + if (changed) { + rdv40_spiffs_lazy_unmount(); + } + + reply_ng(CMD_SPIFFS_STAT, PM3_SUCCESS, (uint8_t *)&size, sizeof(uint32_t)); LED_B_OFF(); break; } case CMD_SPIFFS_REMOVE: { LED_B_ON(); - uint8_t filename[32]; - uint8_t *pfilename = packet->data.asBytes; - memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs REMOVE : %s", filename); - rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE); + + struct p { + uint8_t len; + uint8_t fn[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Filename received for spiffs REMOVE : %s", payload->fn); + } + + rdv40_spiffs_remove((char *)payload->fn, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_REMOVE, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } case CMD_SPIFFS_RENAME: { LED_B_ON(); - uint8_t src[32]; - uint8_t dest[32]; - uint8_t *pfilename = packet->data.asBytes; - char *token; - token = strtok((char *)pfilename, ","); - strncpy((char *)src, token, sizeof(src) - 1); - token = strtok(NULL, ","); - strncpy((char *)dest, token, sizeof(dest) - 1); + struct p { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Filename received as source for spiffs RENAME : %s", src); - Dbprintf("Filename received as destination for spiffs RENAME : %s", dest); + Dbprintf("SPIFFS RENAME"); + Dbprintf("Source........ %s", payload->src); + Dbprintf("Destination... %s", payload->dest); } - rdv40_spiffs_rename((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_rename((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_RENAME, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } case CMD_SPIFFS_COPY: { LED_B_ON(); - uint8_t src[32]; - uint8_t dest[32]; - uint8_t *pfilename = packet->data.asBytes; - char *token; - token = strtok((char *)pfilename, ","); - strncpy((char *)src, token, sizeof(src) - 1); - token = strtok(NULL, ","); - strncpy((char *)dest, token, sizeof(dest) - 1); + struct p { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Filename received as source for spiffs COPY : %s", src); - Dbprintf("Filename received as destination for spiffs COPY : %s", dest); + Dbprintf("SPIFFS COPY"); + Dbprintf("Source........ %s", payload->src); + Dbprintf("Destination... %s", payload->dest); } - rdv40_spiffs_copy((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_copy((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE); + reply_ng(CMD_SPIFFS_COPY, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } case CMD_SPIFFS_WRITE: { LED_B_ON(); - uint8_t filename[32]; - uint32_t append = packet->oldarg[0]; - uint32_t size = packet->oldarg[1]; - uint8_t *data = packet->data.asBytes; - uint8_t *pfilename = packet->data.asBytes; - memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - data += SPIFFS_OBJ_NAME_LEN; - if (DBGLEVEL >= DBG_DEBUG) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); + flashmem_write_t *payload = (flashmem_write_t *)packet->data.asBytes; - if (!append) { - rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); - } else { - rdv40_spiffs_append((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("SPIFFS WRITE, dest `%s` with APPEND set to: %c", payload->fn, payload->append ? 'Y' : 'N'); } - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + + if (payload->append) { + rdv40_spiffs_append((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_write((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE); + } + + reply_ng(CMD_SPIFFS_WRITE, PM3_SUCCESS, NULL, 0); LED_B_OFF(); break; } @@ -2202,6 +2218,10 @@ static void PacketReceived(PacketCommandNG *packet) { Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xB); + } else if (startidx == FLASH_MEM_SIGNATURE_OFFSET) { + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + Flash_Erase4k(3, 0xF); } res = Flash_Write(startidx, data, len); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 4d5975324..e149254f7 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1881,7 +1881,7 @@ void iClass_WriteBlock(uint8_t *msg) { iclass_send_as_reader(write, write_len, &start_time, &eof_time); - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred res = false; switch_off(); if (payload->req.send_reply) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index cd07a4007..1509cc80c 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -2972,7 +2972,7 @@ void ReaderIso14443a(PacketCommandNG *c) { } } - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred FpgaDisableTracing(); reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); } else { diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 09c5e775e..c747a0e27 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -2092,7 +2092,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { uint32_t eof_time = 0; CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time); - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred FpgaDisableTracing(); reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0); } else { diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index de7a409bd..66539c807 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1477,7 +1477,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, &start_time); - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred res = PM3_ETEAROFF; @@ -1598,7 +1598,7 @@ void ReaderIso15693(uint32_t parameter) { uint32_t eof_time; int recvlen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); - if (recvlen == PM3_ETEAROFF) { // tearoff occured + if (recvlen == PM3_ETEAROFF) { // tearoff occurred reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0); } else { @@ -1928,7 +1928,7 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint uint32_t start_time = 0; int recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time); - if (recvlen == PM3_ETEAROFF) { // tearoff occured + if (recvlen == PM3_ETEAROFF) { // tearoff occurred reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0); } else { diff --git a/armsrc/lfops.c b/armsrc/lfops.c index b4daed774..9555e59c1 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2639,7 +2639,7 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) { SendForward(len, false); - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred StopTicks(); reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_ETEAROFF, NULL, 0); } else { @@ -2681,7 +2681,7 @@ void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd) { SendForward(len, false); - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred StopTicks(); reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_ETEAROFF, NULL, 0); } else { diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 2ea7aa1b8..bcb47aa24 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2713,20 +2713,15 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { // // Tear-off attack against MFU. // - Moebius et al -void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) { +void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_testwrite) { uint8_t blockNo = blno; - uint8_t data_fullwrite[4] = {0x00}; - uint8_t data_testwrite[4] = {0x00}; - memcpy(data_fullwrite, datain, 4); - memcpy(data_testwrite, datain + 4, 4); if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off"); if (tearoff_time > 43000) tearoff_time = 43000; - - MifareUWriteBlockEx(blockNo, 0, data_fullwrite, false); - + tearoff_delay_us = tearoff_time; + tearoff_enabled = true; LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -2750,15 +2745,9 @@ void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) { return; }; // send - ReaderTransmit(cmd, sizeof(cmd), NULL); - - // Wait before cutting power. aka tear-off LED_D_ON(); - - SpinDelayUsPrecision(tearoff_time); - if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!")); - switch_off(); - + ReaderTransmit(cmd, sizeof(cmd), NULL); + tearoff_hook(); reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0); } diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index a1b3e8e39..8242df9fc 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -208,15 +208,12 @@ static void remove_from_spiffs(const char *filename) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); } -static spiffs_stat stat_in_spiffs(const char *filename) { - spiffs_stat s; - if (SPIFFS_stat(&fs, filename, &s) < 0) - Dbprintf("errno %i\n", SPIFFS_errno(&fs)); - return s; -} - uint32_t size_in_spiffs(const char *filename) { - spiffs_stat s = stat_in_spiffs(filename); + spiffs_stat s; + if (SPIFFS_stat(&fs, filename, &s) < 0) { + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + return 0; + } return s.size; } diff --git a/client/deps/cliparser/argtable3.c b/client/deps/cliparser/argtable3.c index 783bd303b..dfcccb81b 100644 --- a/client/deps/cliparser/argtable3.c +++ b/client/deps/cliparser/argtable3.c @@ -4639,11 +4639,13 @@ void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) { /* print GNU style [OPTION] string */ arg_print_gnuswitch(fp, table); + size_t len = 0; + /* print remaining options in abbreviated style */ for (tabindex = 0; table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { - char syntax[200] = ""; + char syntax[400] = ""; const char *shortopts, *longopts, *datatype; /* skip short options without arg values (they were printed by arg_print_gnu_switch) */ @@ -4681,6 +4683,12 @@ void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) { break; } } + + len += strlen(syntax); + if (len > 60) { + fprintf(fp, "\n "); + len = 0; + } } if (suffix) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 15b70cddf..a0c8b9943 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1296,3 +1296,75 @@ ff9a84635bd2 f1a1239a4487 # b882fd4a9f78 +CD7FFFF81C4A +AA0857C641A3 +C8AACD7CF3D1 +9FFDA233B496 +26B85DCA4321 +D4B2D140CB2D +A7395CCB42A0 +541C417E57C0 +D14E615E0545 +69D92108C8B5 +703265497350 +D75971531042 +10510049D725 +35C649004000 +5B0C7EC83645 +05F5EC05133C +521B517352C7 +94B6A644DFF6 +2CA4A4D68B8E +A7765C952DDF +E2F14D0A0E28 +DC018FC1D126 +4927C97F1D57 +046154274C11 +155332417E00 +6B13935CD550 +C151D998C669 +D973D917A4C7 +130662240200 +9386E2A48280 +52750A0E592A +541C417E57C0 +D14E615E0545 +075D1A4DD323 +32CA52054416 +460661C93045 +5429D67E1F57 +0C734F230E13 +1F0128447C00 +411053C05273 +42454C4C4147 +C428C4550A75 +730956C72BC2 +28D70900734C +4F75030AD12B +6307417353C1 +D65561530174 +D1F71E05AD9D +F7FA2F629BB1 +0E620691B9FE +43E69C28F08C +735175696421 +424C0FFBF657 +D01AFEEB890A +75CCB59C9BED +4B791BEA7BCC +51E97FFF51E9 +E7316853E731 +5C8FF9990DA2 +00460740D722 +35D152154017 +5D0762D13401 +0F35D5660653 +1170553E4304 +0C4233587119 +F678905568C3 +50240A68D1D8 +69D92108C8B5 +2E71D3BD262A +540D5E6355CC +D1417E431949 +4BF6DE347FB6 \ No newline at end of file diff --git a/client/luascripts/mem_spiffs_readpwd.lua b/client/luascripts/mem_spiffs_readpwd.lua index b4995e406..ef529440c 100644 --- a/client/luascripts/mem_spiffs_readpwd.lua +++ b/client/luascripts/mem_spiffs_readpwd.lua @@ -76,7 +76,7 @@ local function main(args) if removeflag then print('Deleting file '..filename.. ' from SPIFFS if exists') - core.console("mem spiffs remove " ..filename) + core.console("mem spiffs remove -f " ..filename) return end diff --git a/client/luascripts/tests/lf_t55xx_defaultask.lua b/client/luascripts/tests/lf_t55xx_defaultask.lua index efb6ce8fe..9e81110b3 100644 --- a/client/luascripts/tests/lf_t55xx_defaultask.lua +++ b/client/luascripts/tests/lf_t55xx_defaultask.lua @@ -20,7 +20,7 @@ The outlined procedure is as following: -- manchester -- bit rate -"lf t55xx write b 0 d 00008040" +"lf t55xx write -b 0 -d 00008040" "lf t55xx detect" "lf t55xx info" @@ -118,7 +118,7 @@ local function test() elseif _ == 1 then local config = pcmd:format(config1, y, config2) - dbg(('lf t55xx write b 0 d %s'):format(config)) + dbg(('lf t55xx write -b 0 -d %s'):format(config)) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags) local wc = Command:newNG{cmd = cmds.CMD_LF_T55XX_WRITEBL, data = data} diff --git a/client/luascripts/tests/lf_t55xx_defaultbi.lua b/client/luascripts/tests/lf_t55xx_defaultbi.lua index 495faef02..2590dc38b 100644 --- a/client/luascripts/tests/lf_t55xx_defaultbi.lua +++ b/client/luascripts/tests/lf_t55xx_defaultbi.lua @@ -14,7 +14,7 @@ The outlined procedure is as following: --BIPHASE 00010040 -- -"lf t55xx write b 0 d 00010040" +"lf t55xx write -b 0 -d 00010040" "lf t55xx detect" "lf t55xx info" @@ -112,7 +112,7 @@ local function test() elseif _ == 1 then local config = pcmd:format(config1, y, config2) - dbg(('lf t55xx write b 0 d %s'):format(config)) + dbg(('lf t55xx write -b 0 -d %s'):format(config)) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags) diff --git a/client/luascripts/tests/lf_t55xx_defaultfsk.lua b/client/luascripts/tests/lf_t55xx_defaultfsk.lua index 2a5c272c4..9aa50f641 100644 --- a/client/luascripts/tests/lf_t55xx_defaultfsk.lua +++ b/client/luascripts/tests/lf_t55xx_defaultfsk.lua @@ -17,7 +17,7 @@ The outlined procedure is as following: -- FSK1 -- bit rate -"lf t55xx write b 0 d 00007040" +"lf t55xx write -b 0 -d 00007040" "lf t55xx detect" "lf t55xx info" @@ -114,7 +114,7 @@ local function test(modulation) elseif _ == 1 then local config = pcmd:format(config1, y, modulation, config2) - dbg(('lf t55xx write b 0 d %s'):format(config)) + dbg(('lf t55xx write -b 0 -d %s'):format(config)) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags) local wc = Command:newNG{cmd = cmds.CMD_LF_T55XX_WRITEBL, data = data} diff --git a/client/luascripts/tests/lf_t55xx_defaultpsk.lua b/client/luascripts/tests/lf_t55xx_defaultpsk.lua index 8b4f0447a..09a4063e8 100644 --- a/client/luascripts/tests/lf_t55xx_defaultpsk.lua +++ b/client/luascripts/tests/lf_t55xx_defaultpsk.lua @@ -11,7 +11,7 @@ desc = [[ This script will program a T55x7 TAG with the configuration: block 0x00 data 0x00088040 The outlined procedure is as following: -"lf t55xx write b 0 d 00088040" +"lf t55xx write -b 0 -d 00088040" "lf t55xx detect" "lf t55xx info" @@ -118,7 +118,7 @@ local function test(modulation) dbg('Writing to T55x7 TAG') local config = cmd:format(bitrate, modulation, clockrate) - dbg(('lf t55xx write b 0 d %s'):format(config)) + dbg(('lf t55xx write -b 0 -d %s'):format(config)) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags) diff --git a/client/luascripts/tests/lf_t55xx_writetest.lua b/client/luascripts/tests/lf_t55xx_writetest.lua index cfe46d565..48053db5d 100644 --- a/client/luascripts/tests/lf_t55xx_writetest.lua +++ b/client/luascripts/tests/lf_t55xx_writetest.lua @@ -17,10 +17,10 @@ It will then try to detect and read back those block data and compare if read da lf t55xx wipe lf t55xx detect -lf t55xx write b 1 d 00000000 -lf t55xx write b 2 d ffffffff -lf t55xx write b 3 d 80000000 -lf t55xx write b 4 d 00000001 +lf t55xx write -b 1 -d 00000000 +lf t55xx write -b 2 -d ffffffff +lf t55xx write -b 3 -d 80000000 +lf t55xx write -b 4 -d 00000001 Loop: @@ -278,7 +278,7 @@ local function WipeCard() core.console('rem [ERR:DETECT:WIPED] Failed to detect after wipe') return false else - local wipe_data_cmd = 'lf t55xx write b %s d %s' + local wipe_data_cmd = 'lf t55xx write -b %s -d %s' for _ = 1, #data_blocks_cmds do local val = data_blocks_cmds[_] local c = string.format(wipe_data_cmd, _, val) @@ -321,7 +321,7 @@ local function test(modulation) core.clearCommandBuffer() -- Write Config block - dbg(('lf t55xx write b 0 d %s'):format(p_config_cmd)) + dbg(('lf t55xx write -b 0 -d %s'):format(p_config_cmd)) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(p_config_cmd, 32), password, block, flags) diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 82f05bb59..095725b35 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -251,15 +251,7 @@ static int usage_data_buffclear(void) { PrintAndLogEx(NORMAL, " h This help"); return PM3_SUCCESS; } -static int usage_data_fsktonrz(void) { - PrintAndLogEx(NORMAL, "Usage: data fsktonrz c l f "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " c enter the a clock (omit to autodetect)"); - PrintAndLogEx(NORMAL, " l enter a field clock (omit to autodetect)"); - PrintAndLogEx(NORMAL, " f enter a field clock (omit to autodetect)"); - return PM3_SUCCESS; -} + //set the demod buffer with given array of binary (one bit per byte) //by marshmellow @@ -2295,36 +2287,27 @@ static int FSKToNRZ(int *data, size_t *dataLen, uint8_t clk, uint8_t LowToneFC, } static int CmdFSKToNRZ(const char *Cmd) { - // take clk, fc_low, fc_high - // blank = auto; - bool errors = false; - char cmdp = 0; - int clk = 0, fc_low = 10, fc_high = 8; - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_data_fsktonrz(); - case 'c': - clk = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'f': - fc_high = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'l': - fc_low = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - if (errors) break; - } - //Validations - if (errors) return usage_data_fsktonrz(); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data fsktonrz", + "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n" + "Omitted values are autodetect instead", + "data fsktonrz\n" + "data fsktonrz -c 32 --low 8 --hi 10"); + + void *argtable[] = { + arg_param_begin, + arg_int0("c", "clk", "", "clock"), + arg_int0(NULL, "low", "", "low field clock"), + arg_int0(NULL, "hi", "", "high field clock"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int clk = arg_get_int_def(ctx, 1, 0); + int fc_low = arg_get_int_def(ctx, 2, 0); + int fc_high = arg_get_int_def(ctx, 3, 0); + CLIParserFree(ctx); setClockGrid(0, 0); DemodBufferLen = 0; @@ -2567,11 +2550,9 @@ static command_t CommandTable[] = { {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, " -- Shift 0 for Graphed wave + or - shift value"}, {"timescale", CmdTimeScale, AlwaysAvailable, "Set a timescale to get a differential reading between the yellow and purple markers as time duration\n"}, {"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"}, - {"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"}, {"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"}, - {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"}, {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"}, {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 23acad47d..34f60bd51 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -39,7 +39,10 @@ static int CmdHelp(const char *Cmd); "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" +//------------------------------------------------------------------------------------- +// Sample private RSA Key // Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) + // private key - Exponent D #define RSA_D "24BF6185468786FDD303083D25E64EFC" \ "66CA472BC44D253102F8B4A9D3BFA750" \ @@ -136,14 +139,14 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen){ if (!resp.oldarg[0]) { PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")"); } else { - PrintAndLogEx(SUCCESS, "Writing signature ( "_GREEN_("ok") " ) at offset %u", FLASH_MEM_SIGNATURE_OFFSET); + PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET); return PM3_SUCCESS; } } return PM3_EFAILED; } -static int CmdFlashmemSpiBaudrate(const char *Cmd) { +static int CmdFlashmemSpiBaud(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem baudrate", @@ -352,6 +355,7 @@ static int CmdFlashMemDump(const char *Cmd) { arg_int0("l", "len", "", "length"), arg_lit0("v", "view", "view dump"), arg_strx0("f", "file", "", "file name"), + arg_int0("c", "cols", "", "column breaks (def 32)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -362,6 +366,7 @@ static int CmdFlashMemDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + int breaks = arg_get_int_def(ctx, 5, 32); CLIParserFree(ctx); uint8_t *dump = calloc(len, sizeof(uint8_t)); @@ -379,7 +384,7 @@ static int CmdFlashMemDump(const char *Cmd) { if (view) { PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------"); - print_hex_break(dump, len, 32); + print_hex_break(dump, len, breaks); } if (filename[0] != '\0') { @@ -445,29 +450,39 @@ static int CmdFlashMemInfo(const char *Cmd) { CLIParserInit(&ctx, "mem info", "Collect signature and verify it from flash memory", "mem info" -// "mem info -s" +// "mem info -s -d 0102030405060708" ); void *argtable[] = { arg_param_begin, -// arg_lit0("s", NULL, "create a signature"), -// arg_lit0("w", NULL, "write signature to flash memory"), + arg_lit0("s", "sign", "create a signature"), + arg_str0("d", NULL, "", "flash memory id, 8 hex bytes"), +// arg_lit0("w", "write", "write signature to flash memory"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool shall_sign = false, shall_write = false; -// shall_sign = arg_get_lit(ctx, 1); -// shall_write = arg_get_lit(ctx, 2); + shall_sign = arg_get_lit(ctx, 1); + + int dlen = 0; + uint8_t id[8] = {0}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), id, sizeof(id), &dlen); + +// shall_write = arg_get_lit(ctx, 3); CLIParserFree(ctx); - // validate signature data + if (dlen > 0 && dlen < sizeof(id) ) { + PrintAndLogEx(FAILED, "Error parsing flash memory id, expect 8, got %d", dlen); + return PM3_EINVARG; + } + + // validate devicesignature data rdv40_validation_t mem; - int res = rdv4_get_signature(&mem); + res = rdv4_get_signature(&mem); if (res != PM3_SUCCESS) { return res; } - res = rdv4_validate(&mem); // Flash ID hash (sha1) @@ -479,6 +494,11 @@ static int CmdFlashMemInfo(const char *Cmd) { PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); + PrintAndLogEx( + (res == PM3_SUCCESS) ? SUCCESS : FAILED, + "Signature............ ( %s )", + (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail") + ); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------"); for (int i = 0; i < (sizeof(mem.signature) / 32); i++) { @@ -520,13 +540,21 @@ static int CmdFlashMemInfo(const char *Cmd) { PrintAndLogEx(INFO, " %.64s", str_pk + 192); PrintAndLogEx(NORMAL, ""); - bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0); + bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0); PrintAndLogEx( (is_keyok) ? SUCCESS : FAILED, - "RSA key validation... ( %s )", + "RSA public key validation.... ( %s )", (is_keyok) ? _GREEN_("ok") : _RED_("fail") ); + is_keyok = (mbedtls_rsa_check_privkey(&rsa) == 0); + PrintAndLogEx( + (is_keyok) ? SUCCESS : FAILED, + "RSA private key validation... ( %s )", + (is_keyok) ? _GREEN_("ok") : _RED_("fail") + ); + + // to be verified uint8_t from_device[RRG_RSA_KEY_LEN]; memcpy(from_device, mem.signature, RRG_RSA_KEY_LEN); @@ -537,6 +565,13 @@ static int CmdFlashMemInfo(const char *Cmd) { // Signing (private key) if (shall_sign) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter signing") " --------------------"); + + if (dlen == 8) { + mbedtls_sha1(id, sizeof(id), sha_hash); + } + PrintAndLogEx(INFO, "Signing %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); PrintAndLogEx( @@ -555,27 +590,30 @@ static int CmdFlashMemInfo(const char *Cmd) { } // Verify (public key) - int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device); + bool is_verified = (mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device) == 0); mbedtls_rsa_free(&rsa); PrintAndLogEx( - (is_verified == 0) ? SUCCESS : FAILED, - "RSA verification..... ( %s )", - (is_verified == 0) ? _GREEN_("ok") : _RED_("fail") + (is_verified) ? SUCCESS : FAILED, + "RSA verification..... ( %s )", + (is_verified) ? _GREEN_("ok") : _RED_("fail") ); + if (is_verified) { + PrintAndLogEx(SUCCESS, "Genuine Proxmark3 RDV4 signature detected"); + } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"baudrate", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate"}, - {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "High level SPI FileSystem Flash manipulation"}, - {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, - {"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory"}, - {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, - {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"}, + {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"}, + {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, + {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, + {"load", CmdFlashMemLoad, IfPm3Flash, "Load data to flash memory"}, + {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index 11c4db2d7..9409e52ee 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -8,352 +8,16 @@ // Proxmark3 RDV40 Flash memory commands //----------------------------------------------------------------------------- #include "cmdflashmemspiffs.h" - #include - -#include "cmdparser.h" // command_t +#include "cmdparser.h" // command_t #include "pmflash.h" #include "fileutils.h" //saveFile -#include "comms.h" //getfromdevice +#include "comms.h" //getfromdevice +#include "cliparser.h" static int CmdHelp(const char *Cmd); -static int usage_flashmemspiffs_remove(void) { - PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs remove "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs remove lasttag.bin")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_rename(void) { - PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs rename "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs rename lasttag.bin oldtag.bin")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_copy(void) { - PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs copy "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs copy lasttag.bin lasttag_cpy.bin")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_dump(void) { - PrintAndLogEx(NORMAL, "Dumps flash memory on device into a file or in console"); - PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs dump o [f [e]] [p]"); - PrintAndLogEx(NORMAL, " o - filename in SPIFFS"); - PrintAndLogEx(NORMAL, " f - file name to save to "); - PrintAndLogEx(NORMAL, " p - print dump in console"); - PrintAndLogEx(NORMAL, " e - also save in EML format (good for tags save and dictonnary files)"); - PrintAndLogEx(NORMAL, " You must specify at lease option f or option p, both if you wish"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin f lasttag e")); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin p")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_load(void) { - PrintAndLogEx(NORMAL, "Uploads binary-wise file into device filesystem"); - PrintAndLogEx(NORMAL, "Warning: mem area to be written must have been wiped first"); - PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)\n"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); - PrintAndLogEx(NORMAL, " o - destination filename"); - PrintAndLogEx(NORMAL, " f - local filename"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs load f myfile o myapp.conf")); - return PM3_SUCCESS; -} - -static int usage_flashmemspiffs_wipe(void) { - PrintAndLogEx(NORMAL, "wipes all files on the device filesystem " _RED_("* Warning *")); - PrintAndLogEx(NORMAL, "Usage: mem spiffs wipe [h]"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs wipe")); - return PM3_SUCCESS; -} -static int CmdFlashMemSpiFFSMount(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSUnmount(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSTest(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_TEST, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSCheck(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSTree(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSInfo(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - clearCommandBuffer(); - SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSRemove(const char *Cmd) { - - int len = strlen(Cmd); - if (len < 1) { - return usage_flashmemspiffs_remove(); - } - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (len == 1 && ctmp == 'h') { - return usage_flashmemspiffs_remove(); - } - - char filename[32] = {0}; - bool errors = false; - - if (param_getstr(Cmd, 0, filename, 32) >= 32) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - } - - // check null filename ? - if (errors) { - usage_flashmemspiffs_remove(); - return PM3_EINVARG; - } - - SendCommandMIX(CMD_SPIFFS_REMOVE, 0, 0, 0, (uint8_t *)filename, 32); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSRename(const char *Cmd) { - - int len = strlen(Cmd); - if (len < 1) { - return usage_flashmemspiffs_rename(); - } - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (len == 1 && ctmp == 'h') { - return usage_flashmemspiffs_rename(); - } - - char srcfilename[32] = {0}; - char destfilename[32] = {0}; - bool errors = false; - - if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (srcfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - // check null filename ? - if (errors) { - usage_flashmemspiffs_rename(); - return PM3_EINVARG; - } - - char data[65]; - sprintf(data, "%s,%s", srcfilename, destfilename); - SendCommandMIX(CMD_SPIFFS_RENAME, 0, 0, 0, (uint8_t *)data, 65); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSCopy(const char *Cmd) { - int len = strlen(Cmd); - if (len < 1) { - return usage_flashmemspiffs_copy(); - } - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (len == 1 && ctmp == 'h') { - return usage_flashmemspiffs_copy(); - } - - - char srcfilename[32] = {0}; - char destfilename[32] = {0}; - bool errors = false; - - if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (srcfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { - PrintAndLogEx(FAILED, "Source Filename too long"); - errors = true; - } - if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "Source Filename missing or invalid"); - errors = true; - } - - // check null filename ? - if (errors) { - usage_flashmemspiffs_copy(); - return PM3_EINVARG; - } - - char data[65]; - sprintf(data, "%s,%s", srcfilename, destfilename); - SendCommandMIX(CMD_SPIFFS_COPY, 0, 0, 0, (uint8_t *)data, 65); - return PM3_SUCCESS; -} - -static int CmdFlashMemSpiFFSDump(const char *Cmd) { - - char filename[FILE_PATH_SIZE] = {0}; - uint8_t cmdp = 0; - bool errors = false; - bool print = false; - uint32_t start_index = 0, len = FLASH_MEM_MAX_SIZE; - char destfilename[32] = {0}; - bool eml = false; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmemspiffs_dump(); - /*case 'l': - len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); - cmdp += 2; - break;*/ - case 'o': - param_getstr(Cmd, cmdp + 1, destfilename, 32); - cmdp += 2; - break; - case 'p': - print = true; - cmdp += 1; - break; - case 'e': - eml = true; - cmdp += 1; - break; - case 'f': - // File handling - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - break; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - if ((filename[0] == '\0') && (!print)) { - PrintAndLogEx(FAILED, "No print asked and local dump filename missing or invalid"); - errors = true; - } - - if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "SPIFFS filename missing or invalid"); - errors = true; - } - - // Validations - if (errors || cmdp == 0) { - usage_flashmemspiffs_dump(); - return PM3_EINVARG; - } - - // get size from spiffs itself ! - SendCommandMIX(CMD_SPIFFS_STAT, 0, 0, 0, (uint8_t *)destfilename, 32); - PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - - len = resp.oldarg[0]; - - uint8_t *dump = calloc(len, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(ERR, "error, cannot allocate memory "); - return PM3_EMALLOC; - } - - PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from spiffs (flashmem)", len); - if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)destfilename, 32, NULL, -1, true)) { - PrintAndLogEx(FAILED, "ERROR; downloading from spiffs(flashmemory)"); - free(dump); - return PM3_EFLASH; - } - - if (print) { - print_hex_break(dump, len, 32); - } - - if (filename[0] != '\0') { - saveFile(filename, ".bin", dump, len); - if (eml) { - uint8_t eml_len = 16; - - if (strstr(filename, "class") != NULL) - eml_len = 8; - else if (strstr(filename, "mfu") != NULL) - eml_len = 4; - - saveFileEML(filename, dump, len, eml_len); - } - } - - free(dump); - return PM3_SUCCESS; -} - -int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { +int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen) { int ret_val = PM3_SUCCESS; @@ -364,33 +28,35 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { // Send to device uint32_t bytes_sent = 0; uint32_t bytes_remaining = datalen; - uint32_t append = 0; // fast push mode conn.block_after_ACK = true; while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + flashmem_write_t *payload = calloc(1, sizeof(flashmem_write_t) + bytes_in_packet); + + payload->append = (bytes_sent > 0); + payload->fnlen = strlen(destfn); + memcpy(payload->fn, destfn, strlen(destfn)); + + payload->bytes_in_packet = bytes_in_packet; + memset(payload->data, 0, bytes_in_packet); + memcpy(payload->data, data + bytes_sent, bytes_in_packet); + + PacketResponseNG resp; clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_WRITE, (uint8_t *)payload, sizeof(flashmem_write_t) + bytes_in_packet); - char fdata[32 + bytes_in_packet]; - memset(fdata, 0, sizeof(fdata)); - memcpy(fdata, destfn, 32); - memcpy(fdata + 32, data + bytes_sent, bytes_in_packet); - - if (bytes_sent > 0) - append = 1; - - SendCommandOLD(CMD_SPIFFS_WRITE, append, bytes_in_packet, 0, fdata, 32 + bytes_in_packet); + free(payload); bytes_remaining -= bytes_in_packet; bytes_sent += bytes_in_packet; - PacketResponseNG resp; - uint8_t retry = 3; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); retry--; if (retry == 0) { @@ -398,13 +64,6 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { goto out; } } - - uint8_t isok = resp.oldarg[0] & 0xFF; - if (!isok) { - PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); - ret_val = PM3_EFLASH; - break; - } } out: @@ -416,99 +75,470 @@ out: // We want to unmount after these to set things back to normal but more than this // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); - return ret_val; } -static int CmdFlashMemSpiFFSWipe(const char *Cmd) { +static int CmdFlashMemSpiFFSMount(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs mount", + "Mount the SPIFFS file system if not already mounted", + "mem spiffs mount"); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (ctmp == 'h') { - return usage_flashmemspiffs_wipe(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSUnmount(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs unmount", + "Un-mount the SPIFFS file system", + "mem spiffs unmount"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs test", + "Test SPIFFS Operations, require wiping pages 0 and 1", + "mem spiffs test"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_TEST, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCheck(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs check", + "Check/try to defrag faulty/fragmented SPIFFS file system", + "mem spiffs check"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTree(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs tree", + "Print the Flash memory file system tree", + "mem spiffs tree"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs info", + "Print file system info and usage statistics", + "mem spiffs info"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRemove(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs remove", + "Remove a file from SPIFFS filesystem", + "mem spiffs remove -f lasttag.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "filename", "", "file to remove"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, 32, &fnlen); + CLIParserFree(ctx); + + PrintAndLogEx(DEBUG, "Removing `" _YELLOW_("%s") "`", filename); + struct { + uint8_t len; + uint8_t fn[32]; + } PACKED payload; + payload.len = fnlen; + memcpy(payload.fn, filename, fnlen); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_REMOVE, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_SPIFFS_REMOVE, &resp); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRename(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs rename", + "Rename/move a file from SPIFFS filesystem.", + "mem spiffs rename -s aaa.bin -d bbb.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "source file name"), + arg_str1("d", "dest", "", "destination file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int dlen = 0; + char dest[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen); + CLIParserFree(ctx); + + PrintAndLogEx(DEBUG, "Rename from `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest); + + struct { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED payload; + payload.slen = slen; + payload.dlen = dlen; + + memcpy(payload.src, src, slen); + memcpy(payload.dest, dest, dlen); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_RENAME, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_SPIFFS_RENAME, &resp); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCopy(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs copy", + "Copy a file to another (destructively) in SPIFFS file system", + "mem spiffs copy -s aaa.bin -d aaa_cpy.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "source file name"), + arg_str1("d", "dest", "", "destination file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int dlen = 0; + char dest[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen); + CLIParserFree(ctx); + + struct { + uint8_t slen; + uint8_t src[32]; + uint8_t dlen; + uint8_t dest[32]; + } PACKED payload; + payload.slen = slen; + payload.dlen = dlen; + + memcpy(payload.src, src, slen); + memcpy(payload.dest, dest, dlen); + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_COPY, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_SPIFFS_COPY, &resp); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs dump", + "Dumps device SPIFFS file to a local file\n" + "Size is handled by first sending a STAT command against file to verify existence", + "mem spiffs dump -s tag.bin --> download binary file from device\n" + "mem spiffs dump -s tag.bin -d aaa -e --> download tag.bin, save as aaa.eml format" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "SPIFFS file to save"), + arg_str0("d", "dest", "", "file name to save to "), + arg_lit0("e", "eml", "also save in EML format"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int dlen = 0; + char dest[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, FILE_PATH_SIZE, &dlen); + + bool eml = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + // get size from spiffs itself ! + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; } - PrintAndLogEx(INFO, "Wiping all files from SPIFFS FileSystem"); + uint32_t len = resp.data.asDwords[0]; + uint8_t *dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + // download from device + uint32_t start_index = 0; + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src); + if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true)) { + PrintAndLogEx(FAILED, "error, downloading from spiffs"); + free(dump); + return PM3_EFLASH; + } + + // save to file + char fn[FILE_PATH_SIZE] = {0}; + if (dlen == 0) { + strncpy(fn, src, slen); + } else { + strncpy(fn, dest, dlen); + } + + saveFile(fn, ".bin", dump, len); + if (eml) { + uint8_t eml_len = 16; + if (strstr(fn, "class") != NULL) + eml_len = 8; + else if (strstr(fn, "mfu") != NULL) + eml_len = 4; + + saveFileEML(fn, dump, len, eml_len); + } + free(dump); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSWipe(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs wipe", + _RED_("* * * Warning * * *") " \n" + _CYAN_("This command wipes all files on the device SPIFFS file system"), + "mem spiffs wipe"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Wiping all files from SPIFFS file system"); PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0); WaitForResponse(CMD_SPIFFS_WIPE, &resp); - PrintAndLogEx(INFO, "Done!"); - PrintAndLogEx(HINT, "Try use '" _YELLOW_("mem spiffs tree") "' to verify."); + if (resp.status == PM3_SUCCESS) + PrintAndLogEx(INFO, "Done!"); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); return PM3_SUCCESS; } -static int CmdFlashMemSpiFFSLoad(const char *Cmd) { +static int CmdFlashMemSpiFFSUpload(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs upload", + "Uploads binary-wise file into device file system\n" + "Warning: mem area to be written must have been wiped first.\n" + "This is already taken care when loading dictionaries.\n" + "File names can only be 32 bytes long on device SPIFFS", + "mem spiffs upload -s local.bin -d dest.bin" + ); - char filename[FILE_PATH_SIZE] = {0}; - uint8_t destfilename[32] = {0}; - bool errors = false; - uint8_t cmdp = 0; + void *argtable[] = { + arg_param_begin, + arg_str1("s", "src", "", "source file name"), + arg_str1("d", "dest", "", "destination file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmemspiffs_load(); - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - } - cmdp += 2; - break; - case 'o': - param_getstr(Cmd, cmdp + 1, (char *)destfilename, 32); - if (strlen((char *)destfilename) == 0) { - PrintAndLogEx(FAILED, "Destination Filename missing or invalid"); - errors = true; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + int slen = 0; + char src[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, FILE_PATH_SIZE, &slen); - // Validations - if (errors || cmdp == 0) - return usage_flashmemspiffs_load(); + int dlen = 0; + char dest[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen); + CLIParserFree(ctx); + + PrintAndLogEx(DEBUG, "Upload `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest); size_t datalen = 0; uint8_t *data = NULL; - int res = loadFile_safe(filename, "", (void **)&data, &datalen); - // int res = loadFileEML( filename, data, &datalen); + int res = loadFile_safe(src, "", (void **)&data, &datalen); if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } - res = flashmem_spiffs_load(destfilename, data, datalen); - + res = flashmem_spiffs_load(dest, data, datalen); free(data); if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, destfilename); + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, dest); + PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify"); return res; } -static command_t CommandTable[] = { +static int CmdFlashMemSpiFFSView(const char *Cmd) { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented Filesystem"}, - {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, - {"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, - {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, - {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"}, - {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, - {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, - {"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS FileSystem." _RED_("* dangerous *") }, + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem spiffs view", + "View a file on flash memory on devicer in console", + "mem spiffs view -f tag.bin" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "SPIFFS file to view"), + arg_int0("c", "cols", "", "column breaks (def 32)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int slen = 0; + char src[32] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); + + int breaks = arg_get_int_def(ctx, 2, 32); + CLIParserFree(ctx); + + // get size from spiffs itself ! + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + uint32_t len = resp.data.asDwords[0]; + if (len == 0) { + PrintAndLogEx(ERR, "error, failed to retrieve file stats on SPIFFSS"); + return PM3_EFAILED; + } + + uint8_t *dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + uint32_t start_index = 0; + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src); + + if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true)) { + PrintAndLogEx(FAILED, "error, downloading from spiffs"); + free(dump); + return PM3_EFLASH; + } + + PrintAndLogEx(NORMAL, ""); + print_hex_break(dump, len, breaks); + PrintAndLogEx(NORMAL, ""); + free(dump); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"}, + {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"}, + {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"}, + {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"}, + {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"}, + {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"}, + {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"}, + {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations"}, + {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash memory file system tree"}, + {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS file system"}, + {"upload", CmdFlashMemSpiFFSUpload, IfPm3Flash, "Upload file into SPIFFS file system"}, + {"view", CmdFlashMemSpiFFSView, IfPm3Flash, "View file on SPIFFS file system"}, + {"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS file system * " _RED_("dangerous") " *" }, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdflashmemspiffs.h b/client/src/cmdflashmemspiffs.h index 9324a541b..385d78401 100644 --- a/client/src/cmdflashmemspiffs.h +++ b/client/src/cmdflashmemspiffs.h @@ -14,6 +14,6 @@ #include "common.h" int CmdFlashMemSpiFFS(const char *Cmd); -int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen); +int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen); #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 2fda649b8..7225a443b 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -103,14 +103,6 @@ static inline uint32_t leadingzeros(uint64_t a) { #else return 0; #endif -} -static inline uint32_t countones(uint64_t a) { -#if defined __GNUC__ - return __builtin_popcountll(a); -#else - return 0; -#endif - } const char *card_types[] = { @@ -2034,7 +2026,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { uint64_t a = bytes_to_num(data, 8); bool starts = (leadingzeros(a) < 12); - bool ones = (countones(a) > 16 && countones(a) < 48); + bool ones = (bitcount64(a) > 16 && bitcount64(a) < 48); if (starts && ones) { PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible"); diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 6e292ad6f..d4dcb892f 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -1290,9 +1290,10 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 MifareAuthState = masNrAr; if (AuthData.first_auth) { AuthData.nt = bytes_to_num(cmd, 4); + AuthData.nt_enc_par = 0; } else { AuthData.nt_enc = bytes_to_num(cmd, 4); - AuthData.nt_enc_par = parity[0]; + AuthData.nt_enc_par = parity[0] & 0xF0; } return; } else { @@ -1304,6 +1305,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 snprintf(exp, size, "AUTH: nr ar (enc)"); MifareAuthState = masAt; AuthData.nr_enc = bytes_to_num(cmd, 4); + AuthData.nr_enc_par = parity[0] & 0xF0; AuthData.ar_enc = bytes_to_num(&cmd[4], 4); AuthData.ar_enc_par = parity[0] << 4; return; @@ -1316,7 +1318,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 snprintf(exp, size, "AUTH: at (enc)"); MifareAuthState = masAuthComplete; AuthData.at_enc = bytes_to_num(cmd, 4); - AuthData.at_enc_par = parity[0]; + AuthData.at_enc_par = parity[0] & 0xF0; return; } else { MifareAuthState = masError; @@ -1335,6 +1337,17 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 } +static void mf_get_paritybinstr(char *s, uint32_t val, uint8_t par) { + uint8_t foo[4] = {0,0,0,0}; + num_to_bytes(val, sizeof(uint32_t), foo); + for (uint8_t i = 0; i < 4; i++) { + if (oddparity8(foo[i]) != ((par >> (7 - (i & 0x0007))) & 0x01)) + sprintf(s++, "1"); + else + sprintf(s++, "0"); + } +} + bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount) { static struct Crypto1State *traceCrypto1; @@ -1428,7 +1441,29 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes //hardnested if (!traceCrypto1) { - PrintAndLogEx(NORMAL, "hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + + //PrintAndLogEx(NORMAL, "hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc); + + char snt[5] = {0,0,0,0,0}; + mf_get_paritybinstr(snt, AuthData.nt_enc, AuthData.nt_enc_par); + char sar[5] = {0,0,0,0,0}; + mf_get_paritybinstr(sar, AuthData.ar_enc, AuthData.ar_enc_par); + char sat[5] = {0,0,0,0,0}; + mf_get_paritybinstr(sat, AuthData.at_enc, AuthData.at_enc_par); + + PrintAndLogEx(NORMAL, "Nested authentication detected. "); + PrintAndLogEx(NORMAL, "tools/mf_nonce_brute/mf_nonce_brute %x %x %s %x %x %s %x %s %s\n" + , AuthData.uid + , AuthData.nt_enc + , snt + , AuthData.nr_enc + , AuthData.ar_enc + , sar + , AuthData.at_enc + , sat + , sprint_hex_inrow(cmd, cmdsize) + ); + MifareAuthState = masError; /* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index ff7f7f799..b59c9fca2 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -18,6 +18,7 @@ typedef struct { uint32_t nt_enc; // encrypted tag challenge uint8_t nt_enc_par; // encrypted tag challenge parity uint32_t nr_enc; // encrypted reader challenge + uint8_t nr_enc_par; // encrypted reader challenge parity uint32_t ar_enc; // encrypted reader response uint8_t ar_enc_par; // encrypted reader response parity uint32_t at_enc; // encrypted tag response diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index cb338826b..757d40288 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -523,20 +523,21 @@ static int32_t initSectorTable(sector_t **src, int32_t items) { static void decode_print_st(uint16_t blockno, uint8_t *data) { if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Sector trailer decoded:"); - PrintAndLogEx(NORMAL, "----------------------------------------------"); - PrintAndLogEx(NORMAL, "Key A " _GREEN_("%s"), sprint_hex_inrow(data, 6)); - PrintAndLogEx(NORMAL, "Key B " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); - PrintAndLogEx(NORMAL, "Access rights"); + PrintAndLogEx(INFO, "--------- " _CYAN_("Sector trailer") " -------------"); + PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6)); + PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3)); + PrintAndLogEx(INFO, "user / gdb... " _GREEN_("%02x"), data[9]); + PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); + PrintAndLogEx(INFO, "Access rights decoded"); int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; for (int i = 0; i < 4; i++) { - PrintAndLogEx(NORMAL, " block %d%s " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? "+" : ""), mfGetAccessConditionsDesc(i, &data[6])); + PrintAndLogEx(INFO, " block %d%s " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? "+" : ""), mfGetAccessConditionsDesc(i, &data[6])); bln += blinc; } - PrintAndLogEx(NORMAL, "UserData " _YELLOW_("0x%02x"), data[9]); - PrintAndLogEx(NORMAL, "----------------------------------------------"); + + PrintAndLogEx(INFO, "--------------------------------------"); } } @@ -789,7 +790,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "--sector no %d, key %c - %s ", sectorNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); + PrintAndLogEx(NORMAL, ""); uint8_t sc_size = mfNumBlocksPerSector(sectorNo) * 16; uint8_t *data = calloc(sc_size, sizeof(uint8_t)); @@ -804,8 +805,16 @@ static int CmdHF14AMfRdSc(const char *Cmd) { uint8_t blocks = NumBlocksPerSector(sectorNo); uint8_t start = FirstBlockOfSector(sectorNo); + PrintAndLogEx(INFO, " # | data - sector %02d / 0x%02X | ascii", sectorNo, sectorNo); + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); for (int i = 0; i < blocks; i++) { - PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16)); + if (start + i == 0) { + PrintAndLogEx(INFO, "%3d | " _RED_("%s"), start + i, sprint_hex_ascii(data + (i * 16), 16)); + } else if (mfIsSectorTrailer(i)) { + PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data + (i * 16), 16)); + } else { + PrintAndLogEx(INFO, "%3d | %s ", start + i, sprint_hex_ascii(data + (i * 16), 16)); + } } decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16)); @@ -3771,8 +3780,10 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "\n # | data - sector %02d / 0x%02X ", sector, sector); - PrintAndLogEx(NORMAL, "----+------------------------------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " # | data - sector %02d / 0x%02X | ascii", sector, sector); + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + uint8_t blocks = 4; uint8_t start = sector * 4; if (sector >= 32) { @@ -3785,11 +3796,11 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { int res = mfEmlGetMem(data, start + i, 1); if (res == PM3_SUCCESS) { if (start + i == 0) { - PrintAndLogEx(INFO, "%03d | " _RED_("%s"), start + i, sprint_hex_ascii(data, sizeof(data))); + PrintAndLogEx(INFO, "%3d | " _RED_("%s"), start + i, sprint_hex_ascii(data, sizeof(data))); } else if (mfIsSectorTrailer(i)) { - PrintAndLogEx(INFO, "%03d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data, sizeof(data))); + PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data, sizeof(data))); } else { - PrintAndLogEx(INFO, "%03d | %s ", start + i, sprint_hex_ascii(data, sizeof(data))); + PrintAndLogEx(INFO, "%3d | %s ", start + i, sprint_hex_ascii(data, sizeof(data))); } } } @@ -4467,8 +4478,10 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "\n # | data - sector %02d / 0x%02X ", sector, sector); - PrintAndLogEx(NORMAL, "----+------------------------------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " # | data - sector %02d / 0x%02X | ascii", sector, sector); + PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); + uint8_t blocks = 4; uint8_t start = sector * 4; if (sector >= 32) { @@ -4487,7 +4500,7 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res); return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data, 16)); + PrintAndLogEx(INFO, "%3d | %s ", start + i, sprint_hex_ascii(data, sizeof(data))); } decode_print_st(start + blocks - 1, data); return PM3_SUCCESS; diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index 4e4f5c6f4..9488378fd 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -3102,7 +3102,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { "hf mfu otptear -b 3 -i 100 -s 1000\n" "hf mfu otptear -b 3 -i 1 -e 200\n" "hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE\n" - "hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quite when OTP is reset" + "hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quit when OTP is reset" ); void *argtable[] = { @@ -3112,7 +3112,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { arg_u64_0("e", "end", "", "end time (def 3000 us)"), arg_u64_0("s", "start", "", "start time (def 0 us)"), arg_str0("d", "data", "", "initialise data before run (4 bytes)"), - arg_str0("t", "test", "", "test write data (4 bytes)"), + arg_str0("t", "test", "", "test write data (4 bytes, 00000000 by default)"), arg_str0("m", "match", "", "exit criteria, if block matches this value (4 bytes)"), arg_param_end }; @@ -3126,6 +3126,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { int d_len = 0; uint8_t data[4] = {0x00}; CLIGetHexWithReturn(ctx, 5, data, &d_len); + bool use_data = (d_len > 0); int t_len = 0; uint8_t test[4] = {0x00}; @@ -3150,7 +3151,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return PM3_EINVARG; } if (start > (end - steps)) { - PrintAndLogEx(WARNING, "Start time larger then (end time + steps)"); + PrintAndLogEx(WARNING, "Start time larger than (end time + steps)"); return PM3_EINVARG; } @@ -3169,26 +3170,34 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { return PM3_EINVARG; } - uint8_t teardata[8] = {0x00}; - memcpy(teardata, data, sizeof(data)); - memcpy(teardata + sizeof(data), test, sizeof(test)); + uint8_t teardata[4] = {0x00}; + memcpy(teardata, test, sizeof(test)); PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Tear off") " ---------------------"); PrintAndLogEx(INFO, "Starting Tear-off test"); PrintAndLogEx(INFO, "Target block no: %u", blockno); - PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(teardata, 4)); - PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata + 4, 4)); + if (use_data) { + PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(data, 4)); + } + PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata, 4)); + if (use_match) { + PrintAndLogEx(INFO, "Target match block data : %s", sprint_hex_inrow(match, 4)); + } PrintAndLogEx(INFO, "----------------------------------------------------"); uint8_t isOK; - bool got_pre = false, got_post = false, lock_on = false; + bool lock_on = false; uint8_t pre[4] = {0}; uint8_t post[4] = {0}; uint32_t current = start; - int phase_clear = -1; - int phase_newwr = -1; + int phase_begin_clear = -1; + int phase_end_clear = -1; + int phase_begin_newwr = -1; + int phase_end_newwr = -1; + bool skip_phase1 = false; uint8_t retries = 0; + uint8_t error_retries = 0; - while (current <= (end - steps)) { + while ((current <= (end - steps)) && (error_retries < 10)) { if (kbd_enter_pressed()) { PrintAndLogEx(INFO, "\naborted via keyboard!\n"); @@ -3198,10 +3207,27 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", current); clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0); PacketResponseNG resp; - got_pre = false; + if (use_data) { + SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, 0, 0, data, d_len); + bool got_written = false; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + isOK = resp.oldarg[0] & 0xff; + if (isOK) { + got_written = true; + } + } + if (! got_written) { + PrintAndLogEx(FAILED, "Failed to write block BEFORE"); + error_retries++; + continue; // try again + } + } + + SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0); + + bool got_pre = false; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { isOK = resp.oldarg[0] & 0xFF; if (isOK) { @@ -3209,9 +3235,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { got_pre = true; } } - + if (! got_pre) { + PrintAndLogEx(FAILED, "Failed to read block BEFORE"); + error_retries++; + continue; // try again + } clearCommandBuffer(); - SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, 8); + SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, sizeof(teardata)); // we be getting ACK that we are silently ignoring here.. @@ -3222,10 +3252,11 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Tear off reporting failure to select tag"); + error_retries++; continue; } - got_post = false; + bool got_post = false; clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { @@ -3235,55 +3266,53 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { got_post = true; } } + if (! got_post) { + PrintAndLogEx(FAILED, "Failed to read block BEFORE"); + error_retries++; + continue; // try again + } + error_retries = 0; + char prestr[20] = {0}; + snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); + char poststr[20] = {0}; + snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); - if (got_pre && got_post) { - - char prestr[20] = {0}; - snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); - char poststr[20] = {0}; - snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post))); - - if (memcmp(pre, post, sizeof(pre)) == 0) { - - PrintAndLogEx(INFO, "Current %02d (0x%02X) %s" - , blockno - , blockno - , poststr - ); - } else { - - // skip first message, since its the reset write. - if (current == start) { - PrintAndLogEx(INFO, "Inital write"); - } else { - PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %02d (0x%02X) %s vs " _RED_("%s") - , blockno - , blockno - , prestr - , poststr - ); - - lock_on = true; - - if (phase_clear == -1) - phase_clear = current; - - // new write phase must be atleast 100us later.. - if (phase_clear > -1 && phase_newwr == -1 && current > (phase_clear + 100)) - phase_newwr = current; - } - } - - if (use_match && memcmp(pre, match, sizeof(pre)) == 0) { - PrintAndLogEx(SUCCESS, "Block matches!\n"); - break; - } + if (memcmp(pre, post, sizeof(pre)) == 0) { + PrintAndLogEx(INFO, "Current : %02d (0x%02X) %s" + , blockno + , blockno + , poststr + ); } else { - if (got_pre == false) - PrintAndLogEx(FAILED, "Failed to read block BEFORE"); - if (got_post == false) - PrintAndLogEx(FAILED, "Failed to read block AFTER"); + PrintAndLogEx(INFO, _CYAN_("Tear off occurred") " : %02d (0x%02X) %s => " _RED_("%s") + , blockno + , blockno + , prestr + , poststr + ); + + lock_on = true; + + if ((phase_begin_clear == -1) && (bitcount32(*(uint32_t*)pre) > bitcount32(*(uint32_t*)post))) + phase_begin_clear = current; + + if ((phase_begin_clear > -1) && (phase_end_clear == -1) && (bitcount32(*(uint32_t*)post) == 0)) + phase_end_clear = current; + + if ((current == start) && (phase_end_clear > -1)) + skip_phase1 = true; + // new write phase must be atleast 100us later.. + if (((bitcount32(*(uint32_t*)pre) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(*(uint32_t*)post) != 0) && (skip_phase1 || (current > (phase_end_clear + 100)))) + phase_begin_newwr = current; + + if ((phase_begin_newwr > -1) && (phase_end_newwr == -1) && (memcmp(post, teardata, sizeof(teardata)) == 0)) + phase_end_newwr = current; + } + + if (use_match && memcmp(post, match, sizeof(post)) == 0) { + PrintAndLogEx(SUCCESS, "Block matches stop condition!\n"); + break; } /* TEMPORALLY DISABLED @@ -3314,11 +3343,17 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { } PrintAndLogEx(INFO, "----------------------------------------------------"); - if (phase_clear > - 1) { - PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_clear); + if ((phase_begin_clear > - 1) && (phase_begin_clear != start)) { + PrintAndLogEx(INFO, "Erase phase start boundary around " _YELLOW_("%5d") " us", phase_begin_clear); } - if (phase_newwr > - 1) { - PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_newwr); + if ((phase_end_clear > - 1) && (phase_end_clear != start)){ + PrintAndLogEx(INFO, "Erase phase end boundary around " _YELLOW_("%5d") " us", phase_end_clear); + } + if (phase_begin_newwr > - 1) { + PrintAndLogEx(INFO, "Write phase start boundary around " _YELLOW_("%5d") " us", phase_begin_newwr); + } + if (phase_end_newwr > - 1) { + PrintAndLogEx(INFO, "Write phase end boundary around " _YELLOW_("%5d") " us", phase_end_newwr); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index fb560b2ee..8bd691e25 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -975,7 +975,7 @@ void pm3_version(bool verbose, bool oneliner) { } PrintAndLogEx(NORMAL, " device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch")); - PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("RDV4")); + PrintAndLogEx(NORMAL, " firmware.................. %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _YELLOW_("RDV4")); PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent")); PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent")); diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 2096c6eb0..65f0386c9 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -768,6 +768,7 @@ int lfsim_upload_gb(void) { break; } PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); payload_up.flag = 0; } PrintAndLogEx(NORMAL, ""); @@ -1321,7 +1322,7 @@ static bool CheckChipType(bool getDeviceData) { goto out; } - PrintAndLogEx(NORMAL, "Couldn't identify a chipset"); + PrintAndLogEx(INFO, "Couldn't identify a chipset"); out: save_restoreGB(GRAPH_RESTORE); save_restoreDB(GRAPH_RESTORE); diff --git a/client/src/cmdlfem.c b/client/src/cmdlfem.c index 7ad0da3dc..378bf6729 100644 --- a/client/src/cmdlfem.c +++ b/client/src/cmdlfem.c @@ -24,10 +24,10 @@ static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"410x", CmdLFEM410X, AlwaysAvailable, "EM 4102 commands..."}, - {"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4205 / 4305 / 4369 / 4469 commands..."}, - {"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4350 / 4450 commands..."}, - {"4x70", CmdLFEM4X70, AlwaysAvailable, "EM 4070 / 4170 commands..."}, + {"410x", CmdLFEM410X, AlwaysAvailable, "{ EM 4102 commands... }"}, + {"4x05", CmdLFEM4X05, AlwaysAvailable, "{ EM 4205 / 4305 / 4369 / 4469 commands... }"}, + {"4x50", CmdLFEM4X50, AlwaysAvailable, "{ EM 4350 / 4450 commands... }"}, + {"4x70", CmdLFEM4X70, AlwaysAvailable, "{ EM 4070 / 4170 commands... }"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 0a65c1cb2..917dd21e3 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -1692,11 +1692,8 @@ int CmdEM4x05Unlock(const char *Cmd) { } // write - res = unlock_write_protect(use_pwd, pwd, write_value, verbose); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "failed unlock write"); - return PM3_ESOFT; - } + // don't check the return value. As a tear-off occurred, the write failed. + unlock_write_protect(use_pwd, pwd, write_value, verbose); // read after trigger res = em4x05_read_word_ext(14, pwd, use_pwd, &word14); @@ -1962,11 +1959,11 @@ int CmdEM4x05Sniff(const char *Cmd) { bool haveData, sampleData = true; CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x05_sniff", + CLIParserInit(&ctx, "lf em 4x05 sniff", "Sniff EM4x05 commands sent from a programmer", - "lf em 4x05_sniff -> sniff via lf sniff\n" - "lf em 4x05_sniff -1 -> sniff from data loaded into the buffer\n" - "lf em 4x05_sniff -r -> reverse the bit order when showing block data" + "lf em 4x05 sniff --> sniff via lf sniff\n" + "lf em 4x05 sniff -1 --> sniff from data loaded into the buffer\n" + "lf em 4x05 sniff -r --> reverse the bit order when showing block data" ); void *argtable[] = { diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index ea60c84b9..9fda23296 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -471,7 +471,7 @@ int CmdEM4x50Chk(const char *Cmd) { // upload to flash. datalen = MIN(bytes_remaining, keyblock); - res = flashmem_spiffs_load(destfn, keys, datalen); + res = flashmem_spiffs_load((char*)destfn, keys, datalen); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "SPIFFS upload failed"); return res; @@ -568,7 +568,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { int CmdEM4x50Read(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50 read", + CLIParserInit(&ctx, "lf em 4x50 rdbl", "Reads single EM4x50 block/word.", "lf em 4x50 rdbl -b 3\n" "lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n" diff --git a/client/src/cmdlffdxb.c b/client/src/cmdlffdxb.c index fdead8ea9..4287db630 100644 --- a/client/src/cmdlffdxb.c +++ b/client/src/cmdlffdxb.c @@ -118,16 +118,6 @@ static void verify_values(uint64_t *animalid, uint32_t *countryid, uint32_t *ext } } -static inline uint32_t bitcount(uint32_t a) { -#if defined __GNUC__ - return __builtin_popcountl(a); -#else - a = a - ((a >> 1) & 0x55555555); - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); - return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24; -#endif -} - // FDX-B ISO11784/85 demod (aka animal tag) BIPHASE, inverted, rf/32, with preamble of 00000000001 (128bits) // 8 databits + 1 parity (1) // CIITT 16 chksum @@ -594,7 +584,7 @@ int demodFDXB(bool verbose) { uint8_t bt_par = (extended & 0x100) >> 8; uint8_t bt_temperature = extended & 0xff; - uint8_t bt_calc_parity = (bitcount(bt_temperature) & 0x1) ? 0 : 1; + uint8_t bt_calc_parity = (bitcount32(bt_temperature) & 0x1) ? 0 : 1; uint8_t is_bt_temperature = (bt_calc_parity == bt_par) && !(extended & 0xe00) ; if (is_bt_temperature) { diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index b98d83705..cb711a65c 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -113,19 +113,6 @@ static int usage_hitag_writer(void) { PrintAndLogEx(NORMAL, " 27 Write page, password mode. Default: 4D494B52 (\"MIKR\")"); return PM3_SUCCESS; } -static int usage_hitag_checkchallenges(void) { - PrintAndLogEx(NORMAL, "Check challenges, load a file with save hitag crypto challenges and test them all."); - PrintAndLogEx(NORMAL, "The file should be 8 * 60 bytes long, the file extension defaults to " _YELLOW_("`.cc`")); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: lf hitag cc [h] f "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " f Load data from BIN file"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf hitag cc f lf-hitag-challenges"); - return PM3_SUCCESS; -} static int CmdLFHitagList(const char *Cmd) { char args[128] = {0}; @@ -614,50 +601,46 @@ static int CmdLFHitagReader(const char *Cmd) { static int CmdLFHitagCheckChallenges(const char *Cmd) { - char filename[FILE_PATH_SIZE] = { 0x00 }; - size_t datalen = 0; - int res = 0; - bool file_given = false; - bool errors = false; - uint8_t cmdp = 0; - uint8_t *data = calloc(8 * 60, sizeof(uint8_t)); + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag cc", + "Check challenges, load a file with saved hitag crypto challenges and test them all.\n" + "The file should be 8 * 60 bytes long, the file extension defaults to " _YELLOW_("`.cc`") " ", + "lf hitag cc -f my_hitag_challenges" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - free(data); - return usage_hitag_checkchallenges(); - case 'f': - //file with all the challenges to try - param_getstr(Cmd, cmdp + 1, filename, sizeof(filename)); - res = loadFile(filename, ".cc", data, 8 * 60, &datalen); - if (res > 0) { - errors = true; - break; - } - file_given = true; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + void *argtable[] = { + arg_param_begin, + arg_str0("f", "filename", "", "filename to load from"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - //Validations - if (errors || strlen(Cmd) == 0) { - free(data); - return usage_hitag_checkchallenges(); - } + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); clearCommandBuffer(); - if (file_given) - SendCommandOLD(CMD_LF_HITAGS_TEST_TRACES, 1, 0, 0, data, datalen); - else - SendCommandMIX(CMD_LF_HITAGS_TEST_TRACES, 0, 0, 0, NULL, 0); - free(data); + if (fnlen > 0) { + uint8_t *data = NULL; + size_t datalen = 0; + int res = loadFile_safe(filename, ".cc", (void **)&data, &datalen); + if (res == PM3_SUCCESS) { + if (datalen == (8 * 60) ) { + SendCommandOLD(CMD_LF_HITAGS_TEST_TRACES, 1, 0, 0, data, datalen); + } else { + PrintAndLogEx(ERR, "Error, file length mismatch. Expected %d, got %d", 8*60, datalen); + } + } + if (data) { + free(data); + } + } else { + SendCommandMIX(CMD_LF_HITAGS_TEST_TRACES, 0, 0, 0, NULL, 0); + } + return PM3_SUCCESS; } diff --git a/client/src/cmdlfnedap.c b/client/src/cmdlfnedap.c index 20cb5eec5..e45d35c3d 100644 --- a/client/src/cmdlfnedap.c +++ b/client/src/cmdlfnedap.c @@ -37,20 +37,10 @@ const uint8_t translateTable[10] = {8, 2, 1, 12, 4, 5, 10, 13, 0, 9}; const uint8_t invTranslateTable[16] = {8, 2, 1, 0xff, 4, 5, 0xff, 0xff, 0, 9, 6, 0xff, 3, 7, 0xff, 0xff}; const uint8_t preamble[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}; // zero inside -static inline uint32_t bitcount(uint32_t a) { -#if defined __GNUC__ - return __builtin_popcountl(a); -#else - a = a - ((a >> 1) & 0x55555555); - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); - return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24; -#endif -} - static uint8_t isEven_64_63(const uint8_t *data) { // 8 uint32_t tmp[2]; memcpy(tmp, data, 8); - return (bitcount(tmp[0]) + (bitcount(tmp[1] & 0xfeffffff))) & 1; + return (bitcount32(tmp[0]) + (bitcount32(tmp[1] & 0xfeffffff))) & 1; } //NEDAP demod - ASK/Biphase (or Diphase), RF/64 with preamble of 1111111110 (always a 128 bit data stream) @@ -252,25 +242,25 @@ static int CmdLFNedapDemod(const char *Cmd) { configuration -lf t55xx wr b 0 d 00170082 +lf t55xx wr -b 0 -d 00170082 1) uid 049033 -lf t55 wr b 1 d FF8B4168 -lf t55 wr b 2 d C90B5359 -lf t55 wr b 3 d 19A40087 -lf t55 wr b 4 d 120115CF +lf t55xx wr -b 1 -d FF8B4168 +lf t55xx wr -b 2 -d C90B5359 +lf t55xx wr -b 3 -d 19A40087 +lf t55xx wr -b 4 -d 120115CF 2) uid 001630 -lf t55 wr b 1 d FF8B6B20 -lf t55 wr b 2 d F19B84A3 -lf t55 wr b 3 d 18058007 -lf t55 wr b 4 d 1200857C +lf t55xx wr -b 1 -d FF8B6B20 +lf t55xx wr -b 2 -d F19B84A3 +lf t55xx wr -b 3 -d 18058007 +lf t55xx wr -b 4 -d 1200857C 3) uid 39feff -lf t55xx wr b 1 d ffbfa73e -lf t55xx wr b 2 d 4c0003ff -lf t55xx wr b 3 d ffbfa73e -lf t55xx wr b 4 d 4c0003ff +lf t55xx wr -b 1 -d ffbfa73e +lf t55xx wr -b 2 -d 4c0003ff +lf t55xx wr -b 3 -d ffbfa73e +lf t55xx wr -b 4 -d 4c0003ff */ @@ -469,7 +459,7 @@ static int CmdLFNedapClone(const char *Cmd) { if (res == PM3_SUCCESS) { PrintAndLogEx(INFO, "The block 0 was changed (eXtended) which can be hard to detect."); - PrintAndLogEx(INFO, "Configure it manually " _YELLOW_("`lf t55xx config b 64 d BI i 1 o 32`")); + PrintAndLogEx(INFO, "Configure it manually " _YELLOW_("`lf t55xx config -b 64 --BI -i -o 32`")); } else { PrintAndLogEx(NORMAL, ""); } diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 1c3993454..ae2beddd9 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -50,7 +50,7 @@ t55xx_conf_block_t config = { .inverted = false, .offset = 0x00, .block0 = 0x00, - .block0Status = notSet, + .block0Status = NOTSET, .Q5 = false, .usepwd = false, .downlink_mode = refFixedBit @@ -66,18 +66,7 @@ void Set_t55xx_Config(t55xx_conf_block_t conf) { config = conf; } -static void print_usage_t55xx_downloadlink(uint8_t ShowAll, uint8_t dl_mode_default) { - if (ShowAll == T55XX_DLMODE_ALL) - PrintAndLogEx(NORMAL, " r - downlink encoding 0|1|2|3|4"); - else - PrintAndLogEx(NORMAL, " r - downlink encoding 0|1|2|3"); - PrintAndLogEx(NORMAL, " 0 - fixed bit length%s", (dl_mode_default == 0) ? " (detected default)" : ""); // default will be whats in config struct - PrintAndLogEx(NORMAL, " 1 - long leading reference%s", (dl_mode_default == 1) ? " (detected default)" : ""); - PrintAndLogEx(NORMAL, " 2 - leading zero%s", (dl_mode_default == 2) ? " (detected default)" : ""); - PrintAndLogEx(NORMAL, " 3 - 1 of 4 coding reference%s", (dl_mode_default == 3) ? " (detected default)" : ""); - if (ShowAll == T55XX_DLMODE_ALL) - PrintAndLogEx(NORMAL, " 4 - Try all downlink modes%s", (dl_mode_default == 4) ? " (default)" : ""); -} +static int CmdHelp(const char *Cmd); static void arg_add_t55xx_downloadlink(void *at[], uint8_t *idx, uint8_t show, uint8_t dl_mode_def) { @@ -106,144 +95,15 @@ static void arg_add_t55xx_downloadlink(void *at[], uint8_t *idx, uint8_t show, u *idx = n; } -static int usage_t55xx_config(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx config [c ] [d ] [i [0/1]] [o ] [Q5 [0/1]] [ST [0/1]]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, " c - set configuration from a block0"); - PrintAndLogEx(NORMAL, " b <8|16|32|40|50|64|100|128> - Set bitrate"); - PrintAndLogEx(NORMAL, " d - Set demodulation FSK / ASK / PSK / NRZ / Biphase / Biphase A"); - PrintAndLogEx(NORMAL, " i [0/1] - Set/reset data signal inversion"); - PrintAndLogEx(NORMAL, " o [offset] - Set offset, where data should start decode in bitstream"); - PrintAndLogEx(NORMAL, " Q5 [0/1] - Set/reset as Q5/T5555 chip instead of T55x7"); - PrintAndLogEx(NORMAL, " ST [0/1] - Set/reset Sequence Terminator on"); - PrintAndLogEx(NORMAL, ""); // layout is a little differnet, so seperate until a better fix - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx config d FSK") " - FSK demodulation"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx config d FSK i 1") " - FSK demodulation, inverse data"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx config d FSK i 1 o 3") " - FSK demodulation, inverse data, offset=3,start from position 3 to decode data"); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_read(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx read [r ] b [p ] [o] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b - block number to read. Between 0-7"); - PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); - PrintAndLogEx(NORMAL, " o - OPTIONAL override safety check"); - PrintAndLogEx(NORMAL, " 1 - OPTIONAL 0|1 read Page 1 instead of Page 0"); - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, " " _RED_("**** WARNING ****")); - PrintAndLogEx(NORMAL, " Use of read with password on a tag not configured"); - PrintAndLogEx(NORMAL, " for a password can damage the tag"); - PrintAndLogEx(NORMAL, " " _RED_("*****************")); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx read b 0") " - read data from block 0"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx read b 0 p feedbeef") " - read data from block 0 password feedbeef"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx read b 0 p feedbeef o") " - read data from block 0 password feedbeef safety check"); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_resetread(void) { - PrintAndLogEx(NORMAL, "Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx resetread [r ]"); - PrintAndLogEx(NORMAL, "Options:"); - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx resetread")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_write(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx write [r ] b d [p ] [1] [t] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b - block number to write. Between 0-7"); - PrintAndLogEx(NORMAL, " d - 4 bytes of data to write (8 hex characters)"); - PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex characters)"); - PrintAndLogEx(NORMAL, " 1 - OPTIONAL write Page 1 instead of Page 0"); - PrintAndLogEx(NORMAL, " t - OPTIONAL test mode write - ****DANGER****"); - PrintAndLogEx(NORMAL, " v - OPTIONAL validate data afterwards"); - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx write b 3 d 11223344") " - write 11223344 to block 3"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx write b 3 d 11223344 p feedbeef") " - write 11223344 to block 3 password feedbeef"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx write b 3 d 11223344 v") " - write 11223344 to block 3 and try to validate data"); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_trace(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx trace [1] [r mode]"); - PrintAndLogEx(NORMAL, "Options:"); - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx trace")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx trace 1")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_info(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx info [1] [r ] [c [q]]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " (default) - read data from tag."); - PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer instead of reading tag."); - PrintAndLogEx(NORMAL, " c - set configuration from a block0"); - PrintAndLogEx(NORMAL, " if set, use these data instead of reading tag."); - PrintAndLogEx(NORMAL, " q - if set, provided data are interpreted as Q5 config."); - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx info")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx info 1")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx info d 00083040")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx info d 6001805A q")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx info p 11223344")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_dump(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx dump [r ] [p [o]]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, " o - OPTIONAL override, force pwd read despite danger to card"); - PrintAndLogEx(NORMAL, " f - override filename prefix (optional). Default is based on blk 0"); - print_usage_t55xx_downloadlink(T55XX_DLMODE_SINGLE, config.downlink_mode); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx dump")); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx dump p feedbeef o")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} -static int usage_t55xx_restore(void) { - PrintAndLogEx(NORMAL, "Usage: lf t55xx restore f [p password]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " f - filename of the dump file (.bin/.eml)"); - PrintAndLogEx(NORMAL, " p - optional password if target card has password set"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, _YELLOW_(" Assumes lf t55 detect has been run first!")); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" lf t55xx restore f lf-t55xx-00148040-dump.bin")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_t55xx_clonehelp(void) { +static int CmdT55xxCloneHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far PrintAndLogEx(NORMAL, "For cloning specific techs on T55xx tags, see commands available in corresponding LF sub-menus, e.g.:"); PrintAndLogEx(NORMAL, _GREEN_("lf awid clone")); PrintAndLogEx(NORMAL, _GREEN_("lf destron clone")); - PrintAndLogEx(NORMAL, _GREEN_("lf em 410x_clone")); + PrintAndLogEx(NORMAL, _GREEN_("lf em 410x clone")); // todo: implement restore -// PrintAndLogEx(NORMAL, _GREEN_("lf em 4x05_write")); -// PrintAndLogEx(NORMAL, _GREEN_("lf em 4x50_write")); +// PrintAndLogEx(NORMAL, _GREEN_("lf em 4x05 write")); +// PrintAndLogEx(NORMAL, _GREEN_("lf em 4x50 write")); PrintAndLogEx(NORMAL, _GREEN_("lf fdxb clone")); PrintAndLogEx(NORMAL, _GREEN_("lf gallagher clone")); PrintAndLogEx(NORMAL, _GREEN_("lf gproxii clone")); @@ -252,9 +112,10 @@ static int usage_t55xx_clonehelp(void) { PrintAndLogEx(NORMAL, _GREEN_("lf io clone")); PrintAndLogEx(NORMAL, _GREEN_("lf jablotron clone")); PrintAndLogEx(NORMAL, _GREEN_("lf keri clone")); - PrintAndLogEx(NORMAL, _GREEN_("lf nedap clone")); - PrintAndLogEx(NORMAL, _GREEN_("lf noralsy clone")); PrintAndLogEx(NORMAL, _GREEN_("lf motorola clone")); + PrintAndLogEx(NORMAL, _GREEN_("lf nedap clone")); + PrintAndLogEx(NORMAL, _GREEN_("lf nexwatch clone")); + PrintAndLogEx(NORMAL, _GREEN_("lf noralsy clone")); PrintAndLogEx(NORMAL, _GREEN_("lf pac clone")); PrintAndLogEx(NORMAL, _GREEN_("lf paradox clone")); PrintAndLogEx(NORMAL, _GREEN_("lf presco clone")); @@ -265,13 +126,6 @@ static int usage_t55xx_clonehelp(void) { return PM3_SUCCESS; } -static int CmdHelp(const char *Cmd); - -static int CmdT55xxCloneHelp(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - return usage_t55xx_clonehelp(); -} - static void T55x7_SaveBlockData(uint8_t idx, uint32_t data) { if (idx < T55x7_BLOCK_COUNT) { cardmem[idx].valid = true; @@ -558,141 +412,191 @@ void SetConfigWithBlock0Ex(uint32_t block0, uint8_t offset, bool Q5) { } static int CmdT55xxSetConfig(const char *Cmd) { - // No args - if (strlen(Cmd) == 0) return printConfiguration(config); + if (strlen(Cmd) == 0) { + PrintAndLogEx(INFO, "--- " _CYAN_("current t55xx config") " --------------------------"); + return printConfiguration(config); + } + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx config", + "Set/Get T55XX configuration of the pm3 client. Like modulation, inverted, offset, rate etc.\n" + "Offset is start position to decode data.", + "lf t55xx config --FSK --> FSK demodulation\n" + "lf t55xx config --FSK -i --> FSK demodulation, inverse data\n" + "lf t55xx config --FSK -i -o 3 --> FSK demodulation, inverse data, offset 3\n" + ); + + // 1 (help) + 19 (user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[1 + 12 + 6 + 5] = { + arg_param_begin, + arg_lit0(NULL, "FSK", "set demodulation FSK"), + arg_lit0(NULL, "FSK1", "set demodulation FSK 1"), + arg_lit0(NULL, "FSK1A", "set demodulation FSK 1a (inv)"), + arg_lit0(NULL, "FSK2", "set demodulation FSK 2"), + arg_lit0(NULL, "FSK2A", "set demodulation FSK 2a (inv)"), + arg_lit0(NULL, "ASK", "set demodulation ASK"), + arg_lit0(NULL, "PSK1", "set demodulation PSK 1"), + arg_lit0(NULL, "PSK2", "set demodulation PSK 2"), + arg_lit0(NULL, "PSK3", "set demodulation PSK 3"), + arg_lit0(NULL, "NRZ", "set demodulation NRZ"), + arg_lit0(NULL, "BI", "set demodulation Biphase"), + arg_lit0(NULL, "BIA", "set demodulation Diphase (inverted biphase)"), + arg_lit0("i", "inv", "set/reset data signal inversion"), + arg_lit0(NULL, "q5", "set/reset as Q5/T5555 chip instead of T55x7"), + arg_lit0(NULL, "st", "set/reset Sequence Terminator on"), + arg_int0(NULL, "rate", "", "set bitrate <8|16|32|40|50|64|100|128>"), + arg_str0("c", "blk0", "", "set configuration from a block0 (4 hex bytes)"), + arg_int0("o", "offset", "<0-255>", "set offset, where data should start decode in bitstream "), + }; + + uint8_t idx = 19; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + idx = 1; + bool mods[12]; + int verify_mods = 0; + while (idx - 1 < sizeof(mods)) { + mods[idx - 1] = arg_get_lit(ctx, idx); + verify_mods += mods[idx - 1]; + idx++; + } + + // Not these flags are used to Toggle the values. + // If not flag then dont set or reset, leave as is since the call may just be be setting a different value. + bool invert = arg_get_lit(ctx, idx++); + bool use_q5 = arg_get_lit(ctx, idx++); + bool use_st = arg_get_lit(ctx, idx++); + + int bitrate = arg_get_int_def(ctx, idx, -1); + idx++; - uint8_t offset = 0, bitRate = 0; - char modulation[6] = {0x00}; - uint8_t rates[9] = {8, 16, 32, 40, 50, 64, 100, 128, 0}; - uint8_t cmdp = 0; - uint8_t downlink_mode = 0; - bool errors = false; - uint32_t block0 = 0; bool gotconf = false; + uint32_t block0 = 0; + int res = arg_get_u32_hexstr_def_nlen(ctx, idx++, 0, &block0, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "block0 data must be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (res == 1) { + gotconf = true; + } - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - char tmp = tolower(param_getchar(Cmd, cmdp)); - switch (tmp) { - case 'h': - return usage_t55xx_config(); - case 'b': - errors |= param_getdec(Cmd, cmdp + 1, &bitRate); - if (!errors) { - uint8_t i = 0; - for (; i < 9; i++) { - if (rates[i] == bitRate) { - config.bitrate = i; - config.block0 = ((config.block0 & ~(0x1c0000)) | (i << 18)); - break; - } - } - if (i == 9) errors = true; - } - cmdp += 2; - break; - case 'c': - block0 = param_get32ex(Cmd, cmdp + 1, 0, 16); - gotconf = true; - cmdp += 2; - break; - case 'd': - param_getstr(Cmd, cmdp + 1, modulation, sizeof(modulation)); - cmdp += 2; + int offset = arg_get_int_def(ctx, idx, -1); + idx++; - if (strcmp(modulation, "FSK") == 0) { - config.modulation = DEMOD_FSK; - } else if (strcmp(modulation, "FSK1") == 0) { - config.modulation = DEMOD_FSK1; - config.inverted = 1; - } else if (strcmp(modulation, "FSK1a") == 0) { - config.modulation = DEMOD_FSK1a; - config.inverted = 0; - } else if (strcmp(modulation, "FSK2") == 0) { - config.modulation = DEMOD_FSK2; - config.inverted = 0; - } else if (strcmp(modulation, "FSK2a") == 0) { - config.modulation = DEMOD_FSK2a; - config.inverted = 1; - } else if (strcmp(modulation, "ASK") == 0) { - config.modulation = DEMOD_ASK; - } else if (strcmp(modulation, "NRZ") == 0) { - config.modulation = DEMOD_NRZ; - } else if (strcmp(modulation, "PSK1") == 0) { - config.modulation = DEMOD_PSK1; - } else if (strcmp(modulation, "PSK2") == 0) { - config.modulation = DEMOD_PSK2; - } else if (strcmp(modulation, "PSK3") == 0) { - config.modulation = DEMOD_PSK3; - } else if (strcmp(modulation, "BIa") == 0) { - config.modulation = DEMOD_BIa; - config.inverted = 1; - } else if (strcmp(modulation, "BI") == 0) { - config.modulation = DEMOD_BI; - config.inverted = 0; - } else { - PrintAndLogEx(WARNING, "Unknown modulation '%s'", modulation); - errors = true; - } - config.block0 = ((config.block0 & ~(0x1f000)) | (config.modulation << 12)); - break; - case 'i': - if ((param_getchar(Cmd, cmdp + 1) == '0') || (param_getchar(Cmd, cmdp + 1) == '1')) { - config.inverted = param_getchar(Cmd, cmdp + 1) == '1'; - cmdp += 2; - } else { - config.inverted = true; - cmdp += 1; - } - break; - case 'o': - errors |= param_getdec(Cmd, cmdp + 1, &offset); - if (!errors) - config.offset = offset; - cmdp += 2; - break; - case 'q': - if ((param_getchar(Cmd, cmdp + 1) == '0') || (param_getchar(Cmd, cmdp + 1) == '1')) { - config.Q5 = param_getchar(Cmd, cmdp + 1) == '1'; - cmdp += 2; - } else { - config.Q5 = true; - cmdp += 1; - } - break; - case 's': - if ((param_getchar(Cmd, cmdp + 1) == '0') || (param_getchar(Cmd, cmdp + 1) == '1')) { - config.ST = param_getchar(Cmd, cmdp + 1) == '1'; - cmdp += 2; - } else { - config.ST = true; - cmdp += 1; - } - config.block0 = ((config.block0 & ~(0x8)) | (config.ST << 3)); - break; - case 'r': - errors = param_getdec(Cmd, cmdp + 1, &downlink_mode); - if (downlink_mode > 3) - downlink_mode = 0; - if (!errors) - config.downlink_mode = downlink_mode; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; + bool r0 = arg_get_lit(ctx, idx++); + bool r1 = arg_get_lit(ctx, idx++); + bool r2 = arg_get_lit(ctx, idx++); + bool r3 = arg_get_lit(ctx, idx++); + CLIParserFree(ctx); + + // validate user specified downlink mode + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; + } + + // validate user specified modulation FSK,FSK1,...BIA + if (verify_mods > 1) { + PrintAndLogEx(FAILED, "Error multiple demodulations, select one"); + return PM3_EINVARG; + } + + // validate user specified bitrate + uint8_t rates[9] = {8, 16, 32, 40, 50, 64, 100, 128, 0}; + if (bitrate != -1) { + uint8_t i = 0; + for (; i < ARRAYLEN(rates); i++) { + if (rates[i] == bitrate) { + config.bitrate = i; + config.block0 = ((config.block0 & ~(0x1c0000)) | (i << 18)); break; + } + } + if (i == 9){ + PrintAndLogEx(FAILED, "Error select a valid bitrate"); + return PM3_EINVARG; } } - //Validations - if (errors) return usage_t55xx_config(); + // validate user specified offset + if (offset > -1 && offset < 0x100) { + config.offset = offset; + } - config.block0Status = userSet; + // validate user specific T5555 / Q5 - use the flag to toggle between T5577 and Q5 + config.Q5 ^= use_q5; + + // validate user specific sequence terminator + // if use_st flag was supplied, then toggle and update the config block0; if not supplied skip the config block0 update. + if (use_st) { + config.ST ^= use_st; + config.block0 = ((config.block0 & ~(0x8)) | (config.ST << 3)); + } + + // validate user specific invert + // In theory this should also be set in the config block 0; butit requries the extend mode config, which will change other things. + // as such, leave in user config for decoding the data until a full fix can be added. + // use the flag to toggle if invert is on or off. + config.inverted ^= invert; + + // validate user specific downlink mode + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; + + config.downlink_mode = downlink_mode; + + // validate user specific modulation + if (mods[0]){ + config.modulation = DEMOD_FSK; + } else if (mods[1]) { + config.modulation = DEMOD_FSK1; + config.inverted = 0; + } else if (mods[2]) { + config.modulation = DEMOD_FSK1a; + config.inverted = 1; + } else if (mods[3]) { + config.modulation = DEMOD_FSK2; + config.inverted = 0; + } else if (mods[4]) { + config.modulation = DEMOD_FSK2a; + config.inverted = 1; + } else if (mods[5]) { + config.modulation = DEMOD_ASK; + } else if (mods[6]) { + config.modulation = DEMOD_PSK1; + } else if (mods[7]) { + config.modulation = DEMOD_PSK2; + } else if (mods[8]) { + config.modulation = DEMOD_PSK3; + } else if (mods[9]) { + config.modulation = DEMOD_NRZ; + } else if (mods[10]) { + config.modulation = DEMOD_BI; + config.inverted = 0; + } else if (mods[11]) { + config.modulation = DEMOD_BIa; + config.inverted = 1; + } + + config.block0 = ((config.block0 & ~(0x1f000)) | (config.modulation << 12)); + + config.block0Status = USERSET; if (gotconf) { SetConfigWithBlock0Ex(block0, config.offset, config.Q5); } + PrintAndLogEx(INFO, "--- " _CYAN_("current t55xx config") " --------------------------"); return printConfiguration(config); } int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, uint8_t override, uint32_t password, uint8_t downlink_mode) { @@ -711,6 +615,7 @@ int T55xxReadBlockEx(uint8_t block, bool page1, bool usepwd, uint8_t override, u if (t55xxTryDetectModulationEx(downlink_mode, false, 0, password) == false) { PrintAndLogEx(WARNING, "Safety check: Could not detect if PWD bit is set in config block. Exits."); + PrintAndLogEx(HINT, "Consider using the override parameter to force read."); return PM3_EWRONGANSWER; } else { PrintAndLogEx(WARNING, "Safety check: PWD bit is NOT set in config block. Reading without password..."); @@ -735,51 +640,67 @@ int T55xxReadBlockEx(uint8_t block, bool page1, bool usepwd, uint8_t override, u } static int CmdT55xxReadBlock(const char *Cmd) { - uint8_t block = REGULAR_READ_MODE_BLOCK; - uint8_t override = 0; - uint8_t cmdp = 0; - uint8_t downlink_mode = config.downlink_mode; - uint32_t password = 0; //default to blank Block 7 + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx read", + "Read T55xx block data. This commands defaults to page 0.\n\n" + _RED_(" * * * WARNING * * *") "\n" + _CYAN_("Use of read with password on a tag not configured") "\n" + _CYAN_("for a password can damage the tag") "\n" + _RED_(" * * * * * * * * * *"), + "lf t55xx read -b 0 --> read data from block 0\n" + "lf t55xx read -b 0 --pwd 01020304 --> read data from block 0, pwd 01020304\n" + "lf t55xx read -b 0 --pwd 01020304 -o --> read data from block 0, pwd 01020304, override\n" + ); + + // 1 (help) + 4(four user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[5 + 5] = { + arg_param_begin, + arg_int1("b", "blk", "<0-7>", "block number to read"), + arg_str0("p", "pwd", "", "password (4 hex bytes)"), + arg_lit0("o", "override", "override safety check"), + arg_lit0(NULL, "pg1", "read page 1"), + }; + uint8_t idx = 5; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int block = arg_get_int_def(ctx, 1, REGULAR_READ_MODE_BLOCK); + bool usepwd = false; - bool page1 = false; - bool errors = false; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_read(); - case 'b': - errors |= param_getdec(Cmd, cmdp + 1, &block); - cmdp += 2; - break; - case 'o': - override = 1; - cmdp++; - break; - case 'p': - password = param_get32ex(Cmd, cmdp + 1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case '1': - page1 = true; - cmdp++; - break; - case 'r': - downlink_mode = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (downlink_mode > 3) - downlink_mode = 0; - - cmdp += 2; - break; - - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + uint32_t password = 0; + int res = arg_get_u32_hexstr_def_nlen(ctx, 2, 0, &password, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "Password should be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (errors || cmdp == 0) return usage_t55xx_read(); + if (res == 1) { + usepwd = true; + } + + uint8_t override = arg_get_lit(ctx, 3); + bool page1 = arg_get_lit(ctx, 4); + + bool r0 = arg_get_lit(ctx, 5); + bool r1 = arg_get_lit(ctx, 6); + bool r2 = arg_get_lit(ctx, 7); + bool r3 = arg_get_lit(ctx, 8); + CLIParserFree(ctx); + + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; + } + + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; if (block > 7 && block != REGULAR_READ_MODE_BLOCK) { PrintAndLogEx(NORMAL, "Block must be between 0 and 7"); @@ -997,10 +918,10 @@ static int CmdT55xxDetect(const char *Cmd) { downlink_mode = refLeading0; else if (r3) downlink_mode = ref1of4; - - if (ra) + else // This will set the default to user all d/l modes which will cover the ra flag as well. try_all_dl_modes = true; + bool try_with_pwd = false; bool found = false; bool usewake = false; @@ -1026,9 +947,9 @@ static int CmdT55xxDetect(const char *Cmd) { do { // do ... while to check without password then loop back if password supplied do { - if (try_all_dl_modes) { - for (uint8_t m = downlink_mode; m < 4; m++) { + // Loop from 1st d/l mode refFixedBit to the last d/l mode ref1of4 + for (uint8_t m = refFixedBit; m <= ref1of4; m++) { if (usewake) { // call wake if (try_with_pwd) @@ -1270,7 +1191,7 @@ bool t55xxTryDetectModulationEx(uint8_t downlink_mode, bool print_config, uint32 config.pwd = pwd & 0xffffffff; } - config.block0Status = autoDetect; + config.block0Status = AUTODETECT; if (print_config) printConfiguration(config); @@ -1306,7 +1227,7 @@ bool t55xxTryDetectModulationEx(uint8_t downlink_mode, bool print_config, uint32 PrintAndLogEx(NORMAL, "--[%d]---------------", i + 1); } - config.block0Status = autoDetect; + config.block0Status = AUTODETECT; if (print_config) printConfiguration(tests[i]); } @@ -1362,17 +1283,16 @@ bool GetT55xxBlockData(uint32_t *blockdata) { void printT55xxBlock(uint8_t blockNum, bool page1) { - uint32_t blockData = 0; - uint8_t bytes[4] = {0}; - - if (GetT55xxBlockData(&blockData) == false) + uint32_t val = 0; + if (GetT55xxBlockData(&val) == false) return; - num_to_bytes(blockData, 4, bytes); + uint8_t bytes[4] = {0}; + num_to_bytes(val, 4, bytes); - T55x7_SaveBlockData((page1) ? blockNum + 8 : blockNum, blockData); + T55x7_SaveBlockData((page1) ? blockNum + 8 : blockNum, val); - PrintAndLogEx(SUCCESS, " %02d | %08X | %s | %s", blockNum, blockData, sprint_bin(DemodBuffer + config.offset, 32), sprint_ascii(bytes, 4)); + PrintAndLogEx(SUCCESS, " %02d | %08X | %s | %s", blockNum, val, sprint_bin(DemodBuffer + config.offset, 32), sprint_ascii(bytes, 4)); } static bool testModulation(uint8_t mode, uint8_t modread) { @@ -1583,91 +1503,103 @@ int CmdT55xxSpecial(const char *Cmd) { } int printConfiguration(t55xx_conf_block_t b) { - PrintAndLogEx(INFO, " Chip Type : " _GREEN_("%s"), (b.Q5) ? "Q5/T5555" : "T55x7"); - PrintAndLogEx(INFO, " Modulation : " _GREEN_("%s"), GetSelectedModulationStr(b.modulation)); - PrintAndLogEx(INFO, " Bit Rate : %s", GetBitRateStr(b.bitrate, (b.block0 & T55x7_X_MODE && (b.block0 >> 28 == 6 || b.block0 >> 28 == 9)))); - PrintAndLogEx(INFO, " Inverted : %s", (b.inverted) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(INFO, " Offset : %d", b.offset); - PrintAndLogEx(INFO, " Seq. Term. : %s", (b.ST) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(INFO, " Block0 : 0x%08X %s", b.block0, GetConfigBlock0Source(b.block0Status)); - PrintAndLogEx(INFO, " Downlink Mode : %s", GetDownlinkModeStr(b.downlink_mode)); - PrintAndLogEx(INFO, " Password Set : %s", (b.usepwd) ? _RED_("Yes") : _GREEN_("No")); + PrintAndLogEx(INFO, " Chip type......... " _GREEN_("%s"), (b.Q5) ? "Q5/T5555" : "T55x7"); + PrintAndLogEx(INFO, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b.modulation)); + PrintAndLogEx(INFO, " Bit rate.......... %s", GetBitRateStr(b.bitrate, (b.block0 & T55x7_X_MODE && (b.block0 >> 28 == 6 || b.block0 >> 28 == 9)))); + PrintAndLogEx(INFO, " Inverted.......... %s", (b.inverted) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Offset............ %d", b.offset); + PrintAndLogEx(INFO, " Seq. terminator... %s", (b.ST) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Block0............ %08X %s", b.block0, GetConfigBlock0Source(b.block0Status)); + PrintAndLogEx(INFO, " Downlink mode..... %s", GetDownlinkModeStr(b.downlink_mode)); + PrintAndLogEx(INFO, " Password set...... %s", (b.usepwd) ? _RED_("Yes") : _GREEN_("No")); if (b.usepwd) { - PrintAndLogEx(INFO, " Password : %08X", b.pwd); + PrintAndLogEx(INFO, " Password.......... %08X", b.pwd); } PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static int CmdT55xxWriteBlock(const char *Cmd) { - uint8_t block = 0xFF; // default to invalid block - uint32_t data = 0; // default to blank Block - uint32_t password = 0; // default to blank Block 7 - bool usepwd = false; - bool page1 = false; - bool gotdata = false; - bool testMode = false; - bool errors = false; - bool validate = false; - uint8_t cmdp = 0; - uint32_t downlink_mode = config.downlink_mode; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx write", + "Write T55xx block data", + "lf t55xx write -b 3 -d 11223344 --> write 11223344 to block 3\n" + "lf t55xx write -b 3 -d 11223344 --pwd 01020304 --> write 11223344 to block 3, pwd 01020304\n" + "lf t55xx write -b 3 -d 11223344 --pwd 01020304 --verify --> write 11223344 to block 3 and try validating write" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_write(); - case 'b': - errors |= param_getdec(Cmd, cmdp + 1, &block); - cmdp += 2; + // 1 (help) + 6 (six user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[7 + 5] = { + arg_param_begin, + arg_int1("b", "blk", "<0-7>", "block number to write"), + arg_str0("d", "data", "", "data to write (4 hex bytes)"), + arg_str0("p", "pwd", "", "password (4 hex bytes)"), + arg_lit0("t", "tm", "test mode write ( " _RED_("danger") " )"), + arg_lit0(NULL, "pg1", "write page 1"), + arg_lit0(NULL, "verify", "try validate data afterward"), + }; + uint8_t idx = 7; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); + CLIExecWithReturn(ctx, Cmd, argtable, true); - if (block > 7) { - PrintAndLogEx(WARNING, "Block number must be between 0 and 7"); - errors = true; - } - break; - case 'd': - data = param_get32ex(Cmd, cmdp + 1, 0, 16); - gotdata = true; - cmdp += 2; - break; - case 'p': - password = param_get32ex(Cmd, cmdp + 1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case 't': - testMode = true; - cmdp++; - break; - case '1': - page1 = true; - cmdp++; - break; - case 'r': - downlink_mode = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (downlink_mode > 3) - downlink_mode = 0; + int block = arg_get_int_def(ctx, 1, REGULAR_READ_MODE_BLOCK); - cmdp += 2; - break; - case 'v': - validate = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + uint32_t data = 0; // default to blank Block + int res = arg_get_u32_hexstr_def_nlen(ctx, 2, 0, &data, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "data must be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (errors || !gotdata) return usage_t55xx_write(); - char pwdStr[16] = {0}; - snprintf(pwdStr, sizeof(pwdStr), "pwd: 0x%08X", password); + bool usepwd = false; + uint32_t password = 0; // default to blank Block 7 + res = arg_get_u32_hexstr_def_nlen(ctx, 3, 0, &password, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "Password should be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (res == 1) { + usepwd = true; + } - PrintAndLogEx(INFO, "Writing page %d block: %02d data: 0x%08X %s", page1, block, data, (usepwd) ? pwdStr : ""); + bool testmode = arg_get_lit(ctx, 4); + bool page1 = arg_get_lit(ctx, 5); + bool validate = arg_get_lit(ctx, 6); - if (t55xxWrite(block, page1, usepwd, testMode, password, downlink_mode, data) != PM3_SUCCESS) { + bool r0 = arg_get_lit(ctx, 7); + bool r1 = arg_get_lit(ctx, 8); + bool r2 = arg_get_lit(ctx, 9); + bool r3 = arg_get_lit(ctx, 10); + CLIParserFree(ctx); + + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; + } + + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; + + if (block > 7 && block != REGULAR_READ_MODE_BLOCK) { + PrintAndLogEx(NORMAL, "Block must be between 0 and 7"); + return PM3_ESOFT; + } + + char pwdstr[16] = {0}; + snprintf(pwdstr, sizeof(pwdstr), "pwd: 0x%08X", password); + + PrintAndLogEx(INFO, "Writing page %d block: %02d data: 0x%08X %s", page1, block, data, (usepwd) ? pwdstr : ""); + + if (t55xxWrite(block, page1, usepwd, testmode, password, downlink_mode, data) != PM3_SUCCESS) { PrintAndLogEx(ERR, "Write failed"); return PM3_ESOFT; } @@ -1738,36 +1670,46 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { static int CmdT55xxReadTrace(const char *Cmd) { - bool frombuff = false; - uint8_t downlink_mode = config.downlink_mode; - uint8_t cmdp = 0; - bool errors = false; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx trace", + "Show T55x7 configuration data (page 0/ blk 0) from reading the configuration block", + "lf t55xx trace\n" + "lf t55xx trace -1" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_trace(); - case 'r': - downlink_mode = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (downlink_mode > 3) - downlink_mode = 0; + // 1 (help) + 1 (one user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[2 + 5] = { + arg_param_begin, + arg_lit0("1", NULL, "extract using data from graphbuffer"), + }; + uint8_t idx = 2; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); + CLIExecWithReturn(ctx, Cmd, argtable, true); - cmdp += 2; - break; - case '1': - frombuff = true; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + bool use_gb = arg_get_lit(ctx, 1); + + bool r0 = arg_get_lit(ctx, 2); + bool r1 = arg_get_lit(ctx, 3); + bool r2 = arg_get_lit(ctx, 4); + bool r3 = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; } - if (errors) return usage_t55xx_trace(); + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; - if (!frombuff) { + if (use_gb == false) { // sanity check. if (SanityOfflineCheck(false) != PM3_SUCCESS) return PM3_ENODATA; @@ -1780,12 +1722,18 @@ static int CmdT55xxReadTrace(const char *Cmd) { } if (config.Q5) { - if (!DecodeT5555TraceBlock()) return PM3_ESOFT; + if (DecodeT5555TraceBlock() == false) { + return PM3_ESOFT; + } } else { - if (!DecodeT55xxBlock()) return PM3_ESOFT; + if (DecodeT55xxBlock() == false) { + return PM3_ESOFT; + } } - if (!DemodBufferLen) return PM3_ESOFT; + if (DemodBufferLen == 0){ + return PM3_ESOFT; + } RepaintGraphWindow(); uint8_t repeat = (config.offset > 5) ? 32 : 0; @@ -1890,22 +1838,21 @@ static int CmdT55xxReadTrace(const char *Cmd) { } void printT55x7Trace(t55x7_tracedata_t data, uint8_t repeat) { - PrintAndLogEx(NORMAL, "--- " _CYAN_("T55x7 Trace Information") " ----------------------------------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " ACL Allocation class (ISO/IEC 15963-1) : 0x%02X (%d)", data.acl, data.acl); - PrintAndLogEx(NORMAL, " MFC Manufacturer ID (ISO/IEC 7816-6) : 0x%02X (%d) - %s", data.mfc, data.mfc, getTagInfo(data.mfc)); - PrintAndLogEx(NORMAL, " CID : 0x%02X (%d) - %s", data.cid, data.cid, GetModelStrFromCID(data.cid)); - PrintAndLogEx(NORMAL, " ICR IC Revision : %d", data.icr); - PrintAndLogEx(NORMAL, " Manufactured"); - PrintAndLogEx(NORMAL, " Year/Quarter : %d/%d", data.year, data.quarter); - PrintAndLogEx(NORMAL, " Lot ID : %d", data.lotid); - PrintAndLogEx(NORMAL, " Wafer number : %d", data.wafer); - PrintAndLogEx(NORMAL, " Die Number : %d", data.dw); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Raw Data - Page 1"); - PrintAndLogEx(NORMAL, " Block 1 : 0x%08X %s", data.bl1, sprint_bin(DemodBuffer + config.offset + repeat, 32)); - PrintAndLogEx(NORMAL, " Block 2 : 0x%08X %s", data.bl2, sprint_bin(DemodBuffer + config.offset + repeat + 32, 32)); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("T55x7 Trace Information") " ----------------------------------"); + PrintAndLogEx(INFO, " ACL Allocation class (ISO/IEC 15963-1) : 0x%02X ( %d )", data.acl, data.acl); + PrintAndLogEx(INFO, " MFC Manufacturer ID (ISO/IEC 7816-6) : 0x%02X ( %d ) - %s", data.mfc, data.mfc, getTagInfo(data.mfc)); + PrintAndLogEx(INFO, " CID : 0x%02X ( %d ) - %s", data.cid, data.cid, GetModelStrFromCID(data.cid)); + PrintAndLogEx(INFO, " ICR IC Revision : %d", data.icr); + PrintAndLogEx(INFO, " Manufactured"); + PrintAndLogEx(INFO, " Year/Quarter... %d/%d", data.year, data.quarter); + PrintAndLogEx(INFO, " Lot ID......... %d", data.lotid); + PrintAndLogEx(INFO, " Wafer number... %d", data.wafer); + PrintAndLogEx(INFO, " Die Number..... %d", data.dw); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, " Raw Data - Page 1"); + PrintAndLogEx(INFO, " Block 1... %08X - %s", data.bl1, sprint_bin(DemodBuffer + config.offset + repeat, 32)); + PrintAndLogEx(INFO, " Block 2... %08X - %s", data.bl2, sprint_bin(DemodBuffer + config.offset + repeat + 32, 32)); + PrintAndLogEx(NORMAL, ""); /* Trace info. @@ -1937,16 +1884,15 @@ void printT55x7Trace(t55x7_tracedata_t data, uint8_t repeat) { } void printT5555Trace(t5555_tracedata_t data, uint8_t repeat) { - PrintAndLogEx(NORMAL, "--- " _CYAN_("Q5/T5555 Trace Information") " ---------------------------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " ICR IC Revision : %d", data.icr); - PrintAndLogEx(NORMAL, " Lot : %c%d", data.lotidc, data.lotid); - PrintAndLogEx(NORMAL, " Wafer number : %d", data.wafer); - PrintAndLogEx(NORMAL, " Die Number : %d", data.dw); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Raw Data - Page 1"); - PrintAndLogEx(NORMAL, " Block 1 : 0x%08X %s", data.bl1, sprint_bin(DemodBuffer + config.offset + repeat, 32)); - PrintAndLogEx(NORMAL, " Block 2 : 0x%08X %s", data.bl2, sprint_bin(DemodBuffer + config.offset + repeat + 32, 32)); + PrintAndLogEx(INFO, "--- " _CYAN_("Q5/T5555 Trace Information") " ---------------------------"); + PrintAndLogEx(INFO, " ICR IC Revision.... %d", data.icr); + PrintAndLogEx(INFO, " Lot ID......... %c%d", data.lotidc, data.lotid); + PrintAndLogEx(INFO, " Wafer number... %d", data.wafer); + PrintAndLogEx(INFO, " Die Number..... %d", data.dw); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, " Raw Data - Page 1"); + PrintAndLogEx(INFO, " Block 1... %08X - %s", data.bl1, sprint_bin(DemodBuffer + config.offset + repeat, 32)); + PrintAndLogEx(INFO, " Block 2... %08X - %s", data.bl2, sprint_bin(DemodBuffer + config.offset + repeat + 32, 32)); /* ** Q5 ** @@ -2040,75 +1986,121 @@ static void printT5x7KnownBlock0(uint32_t b0) { break; } - if (strlen(s) > 0) - PrintAndLogEx(SUCCESS, "\nConfig block match : " _YELLOW_("%s"), s); + if (strlen(s) > 0) { + PrintAndLogEx(SUCCESS, "Config block match : " _YELLOW_("%s"), s); + } } static int CmdT55xxInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx info", + "Show T55x7 configuration data (page 0/ blk 0) from reading the configuration block\n" + "from tag. Use `-c` to specify a config block data to be used instead of reading tag.", + "lf t55xx info\n" + "lf t55xx info -1\n" + "lf t55xx info -p 11223344\n" + "lf t55xx info -c 00083040\n" + "lf t55xx info -c 6001805A --q5" + ); + + // 1 (help) + 4 (four user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[5 + 5] = { + arg_param_begin, + arg_lit0("1", NULL, "extract using data from graphbuffer"), + arg_str0("p", "pwd", "", "password (4 hex bytes)"), + arg_str0("c", "blk0", "", "use these data instead (4 hex bytes)"), + arg_lit0(NULL, "q5", "interprete provided data as T5555/Q5 config"), + }; + uint8_t idx = 5; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool use_gb = arg_get_lit(ctx, 1); + + bool usepwd = false; + uint32_t password = 0; + int res = arg_get_u32_hexstr_def_nlen(ctx, 2, 0, &password, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "Password must be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (res == 1) { + usepwd = true; + } + + bool gotdata = false; + uint32_t block0 = 0; + res = arg_get_u32_hexstr_def_nlen(ctx, 3, 0, &block0, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "block0 data must be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (res == 1) { + gotdata = true; + } + + bool dataasq5 = arg_get_lit(ctx, 4); + + bool r0 = arg_get_lit(ctx, 5); + bool r1 = arg_get_lit(ctx, 6); + bool r2 = arg_get_lit(ctx, 7); + bool r3 = arg_get_lit(ctx, 8); + CLIParserFree(ctx); + + if (gotdata && use_gb) { + PrintAndLogEx(FAILED, "Must select one of user supplied data and use graphbuffer"); + return PM3_EINVARG; + } + + if (dataasq5 && gotdata == false) { + PrintAndLogEx(FAILED, "Must specify user supplied Q5 data"); + return PM3_EINVARG; + } + + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; + } + + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; + + /* Page 0 Block 0 Configuration data. Normal mode Extended mode */ - bool frombuff = false, gotdata = false, dataasq5 = false, usepwd = false; - uint8_t cmdp = 0; - uint8_t downlink_mode = config.downlink_mode; - uint32_t block0 = 0, password = 0; - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_info(); - case 'c': - block0 = param_get32ex(Cmd, cmdp + 1, 0, 16); - gotdata = true; - cmdp += 2; - break; - case 'p': - password = param_get32ex(Cmd, cmdp + 1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case '1': - frombuff = true; - cmdp += 2; - break; - case 'q': - dataasq5 = true; - cmdp += 2; - break; - case 'r': - downlink_mode = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (downlink_mode > 3) - downlink_mode = 0; + if (use_gb == false && gotdata == false) { + // sanity check. + if (SanityOfflineCheck(false) != PM3_SUCCESS) { + return PM3_ENODATA; + } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - return usage_t55xx_info(); + if (!AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password, downlink_mode)) { + return PM3_ENODATA; } } - if (gotdata && frombuff) - return usage_t55xx_info(); - - if (dataasq5 && !gotdata) - return usage_t55xx_info(); - - if (!frombuff && !gotdata) { - // sanity check. - if (SanityOfflineCheck(false) != PM3_SUCCESS) return PM3_ENODATA; - - if (!AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password, downlink_mode)) - return PM3_ENODATA; - } - - if (!gotdata) { - if (!DecodeT55xxBlock()) return PM3_ESOFT; + if (gotdata == false) { + if (DecodeT55xxBlock() == false) { + return PM3_ESOFT; + } // too little space to start with - if (DemodBufferLen < 32 + config.offset) return PM3_ESOFT; + if (DemodBufferLen < 32 + config.offset) { + return PM3_ESOFT; + } //PrintAndLogEx(NORMAL, "Offset+32 ==%d\n DemodLen == %d", config.offset + 32, DemodBufferLen); block0 = PackBits(config.offset, 32, DemodBuffer); @@ -2127,19 +2119,18 @@ static int CmdT55xxInfo(const char *Cmd) { uint32_t datamod = (block0 >> (32 - 28)) & 0x07; uint32_t maxblk = (block0 >> (32 - 31)) & 0x07; uint32_t st = block0 & 0x01; - PrintAndLogEx(NORMAL, "--- " _CYAN_("Q5 Configuration & Information") " ------------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Header : 0x%03X%s", header, (header != 0x600) ? _RED_(" - Warning") : ""); - PrintAndLogEx(NORMAL, " Page select : %d", ps); - PrintAndLogEx(NORMAL, " Fast Write : %s", (fw) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " Data bit rate : %s", GetBitRateStr(dbr, 1)); - PrintAndLogEx(NORMAL, " AOR - Answer on Request : %s", (aor) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " Password mode : %s", (pwd) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " PSK clock frequency : %s", GetPskCfStr(pskcf, 1)); - PrintAndLogEx(NORMAL, " Inverse data : %s", (inv) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " Modulation : %s", GetQ5ModulationStr(datamod)); - PrintAndLogEx(NORMAL, " Max block : %d", maxblk); - PrintAndLogEx(NORMAL, " Sequence Terminator : %s", (st) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, "--- " _CYAN_("Q5 Configuration & Information") " ------------"); + PrintAndLogEx(INFO, " Header : 0x%03X%s", header, (header != 0x600) ? _RED_(" - Warning") : ""); + PrintAndLogEx(INFO, " Page select : %d", ps); + PrintAndLogEx(INFO, " Fast Write : %s", (fw) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Data bit rate : %s", GetBitRateStr(dbr, 1)); + PrintAndLogEx(INFO, " AOR - Answer on Request : %s", (aor) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Password mode : %s", (pwd) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " PSK clock frequency : %s", GetPskCfStr(pskcf, 1)); + PrintAndLogEx(INFO, " Inverse data : %s", (inv) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Modulation : %s", GetQ5ModulationStr(datamod)); + PrintAndLogEx(INFO, " Max block : %d", maxblk); + PrintAndLogEx(INFO, " Sequence Terminator : %s", (st) ? _GREEN_("Yes") : "No"); } else { uint32_t safer = (block0 >> (32 - 4)) & 0x0F; uint32_t extend = (block0 >> (32 - 15)) & 0x01; @@ -2162,82 +2153,101 @@ static int CmdT55xxInfo(const char *Cmd) { uint32_t inv = (block0 >> (32 - 31)) & 0x01; uint32_t por = (block0 >> (32 - 32)) & 0x01; - PrintAndLogEx(NORMAL, "--- " _CYAN_("T55x7 Configuration & Information") " ---------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Safer key : %s", GetSaferStr(safer)); - PrintAndLogEx(NORMAL, " reserved : %d", resv); - PrintAndLogEx(NORMAL, " Data bit rate : %s", GetBitRateStr(dbr, extend)); - PrintAndLogEx(NORMAL, " eXtended mode : %s", (extend) ? _YELLOW_("Yes - Warning") : "No"); - PrintAndLogEx(NORMAL, " Modulation : %s", GetModulationStr(datamod, extend)); - PrintAndLogEx(NORMAL, " PSK clock frequency : %s", GetPskCfStr(pskcf, 0)); - PrintAndLogEx(NORMAL, " AOR - Answer on Request : %s", (aor) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " OTP - One Time Pad : %s", (otp) ? ((extend) ? _YELLOW_("Yes - Warning") : _RED_("Yes - Warning")) : "No"); - PrintAndLogEx(NORMAL, " Max block : %d", maxblk); - PrintAndLogEx(NORMAL, " Password mode : %s", (pwd) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " Sequence %-12s : %s", (extend) ? "Start Marker" : "Terminator", (sst) ? _GREEN_("Yes") : "No"); - PrintAndLogEx(NORMAL, " Fast Write : %s", (fw) ? ((extend) ? _GREEN_("Yes") : _RED_("Yes - Warning")) : "No"); - PrintAndLogEx(NORMAL, " Inverse data : %s", (inv) ? ((extend) ? _GREEN_("Yes") : _RED_("Yes - Warning")) : "No"); - PrintAndLogEx(NORMAL, " POR-Delay : %s", (por) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, "--- " _CYAN_("T55x7 Configuration & Information") " ---------"); + PrintAndLogEx(INFO, " Safer key : %s", GetSaferStr(safer)); + PrintAndLogEx(INFO, " reserved : %d", resv); + PrintAndLogEx(INFO, " Data bit rate : %s", GetBitRateStr(dbr, extend)); + PrintAndLogEx(INFO, " eXtended mode : %s", (extend) ? _YELLOW_("Yes - Warning") : "No"); + PrintAndLogEx(INFO, " Modulation : %s", GetModulationStr(datamod, extend)); + PrintAndLogEx(INFO, " PSK clock frequency : %s", GetPskCfStr(pskcf, 0)); + PrintAndLogEx(INFO, " AOR - Answer on Request : %s", (aor) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " OTP - One Time Pad : %s", (otp) ? ((extend) ? _YELLOW_("Yes - Warning") : _RED_("Yes - Warning")) : "No"); + PrintAndLogEx(INFO, " Max block : %d", maxblk); + PrintAndLogEx(INFO, " Password mode : %s", (pwd) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Sequence %-12s : %s", (extend) ? "Start Marker" : "Terminator", (sst) ? _GREEN_("Yes") : "No"); + PrintAndLogEx(INFO, " Fast Write : %s", (fw) ? ((extend) ? _GREEN_("Yes") : _RED_("Yes - Warning")) : "No"); + PrintAndLogEx(INFO, " Inverse data : %s", (inv) ? ((extend) ? _GREEN_("Yes") : _RED_("Yes - Warning")) : "No"); + PrintAndLogEx(INFO, " POR-Delay : %s", (por) ? _GREEN_("Yes") : "No"); } - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Raw Data - Page 0, block 0"); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, " Raw Data - Page 0, block 0"); if (gotdata) - PrintAndLogEx(NORMAL, " 0x" _GREEN_("%08X"), block0); + PrintAndLogEx(INFO, " " _GREEN_("%08X"), block0); else - PrintAndLogEx(NORMAL, " 0x" _GREEN_("%08X") " %s", block0, sprint_bin(DemodBuffer + config.offset, 32)); + PrintAndLogEx(INFO, " " _GREEN_("%08X") " - %s", block0, sprint_bin(DemodBuffer + config.offset, 32)); - if (((!gotdata) && (!config.Q5)) || (gotdata && (!dataasq5))) + if (((!gotdata) && (!config.Q5)) || (gotdata && (!dataasq5))) { + PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint") " ------------"); printT5x7KnownBlock0(block0); + } - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - + PrintAndLogEx(NORMAL, ""); + //PrintAndLogEx(INFO, "-------------------------------------------------------------"); return PM3_SUCCESS; } static int CmdT55xxDump(const char *Cmd) { - uint32_t password = 0; - uint8_t override = 0; - uint8_t downlink_mode = config.downlink_mode; + 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)", + "lf t55xx dump\n" + "lf t55xx dump -p aabbccdd --override\n" + "lf t55xx dump -f my_lf_dump" + ); + + // 1 (help) + 3 (two user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[4 + 5] = { + arg_param_begin, + arg_str0("f", "filename", "", "filename (default is generated on blk 0)"), + arg_lit0("o", "override", "override, force pwd read despite danger to card"), + arg_str0("p", "pwd", "", "password (4 hex bytes)"), + }; + uint8_t idx = 4; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, T55XX_DLMODE_SINGLE); + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, sizeof(filename), &fnlen); + + uint8_t override = arg_get_lit(ctx, 2) ? 1 : 0; + bool usepwd = false; - bool errors = false; - uint8_t cmdp = 0; - char preferredName[FILE_PATH_SIZE] = {0}; - bool success = true; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_dump(); - case 'r': - downlink_mode = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (downlink_mode > 3) - downlink_mode = 0; - - cmdp += 2; - break; - case 'p': - password = param_get32ex(Cmd, cmdp + 1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case 'o': - override = 1; - cmdp++; - break; - case 'f': - param_getstr(Cmd, cmdp + 1, preferredName, FILE_PATH_SIZE); - cmdp += 2; - if (strlen(preferredName) == 0) - errors = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + uint32_t password = 0; + int res = arg_get_u32_hexstr_def_nlen(ctx, 3, 0, &password, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "Password should be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (errors) return usage_t55xx_dump(); + if (res == 1) { + usepwd = true; + } + + bool r0 = arg_get_lit(ctx, 4); + bool r1 = arg_get_lit(ctx, 5); + bool r2 = arg_get_lit(ctx, 6); + bool r3 = arg_get_lit(ctx, 7); + CLIParserFree(ctx); + + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; + } + + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; + + bool success = true; // Due to the few different T55xx cards and number of blocks supported // will save the dump file if ALL page 0 is OK @@ -2245,157 +2255,175 @@ static int CmdT55xxDump(const char *Cmd) { for (uint8_t i = 0; i < 8; ++i) { if (T55xxReadBlock(i, 0, usepwd, override, password, downlink_mode) != PM3_SUCCESS) success = false; - // idea for better user experience and display. + // only show override warning on the first block read - if (override == 1) override++; // flag not to show safty for 2nd and on. + if (override == 1) { + override++; + } } printT5xxHeader(1); for (uint8_t i = 0; i < 4; i++) if (T55xxReadBlock(i, 1, usepwd, override, password, downlink_mode) != PM3_SUCCESS) T55x7_SaveBlockData(8 + i, 0x00); + // all ok, save dump to file + if (success) { - if (success) { // all ok save dump to file - // saveFileEML will add .eml extension to filename - // saveFile (binary) passes in the .bin extension. - if (strcmp(preferredName, "") == 0) { // Set default filename, if not set by user - strcpy(preferredName, "lf-t55xx"); + // set default filename, if not set by user + if (strlen(filename) == 0) { + strcpy(filename, "lf-t55xx"); for (uint8_t i = 1; i <= 7; i++) { - if ((cardmem[i].blockdata != 0x00) && (cardmem[i].blockdata != 0xFFFFFFFF)) - snprintf(preferredName + strlen(preferredName), sizeof(preferredName) - strlen(preferredName), "-%08X", cardmem[i].blockdata); - else + if ((cardmem[i].blockdata != 0x00) && (cardmem[i].blockdata != 0xFFFFFFFF)) { + snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename), "-%08X", cardmem[i].blockdata); + } else { break; + } } - strcat(preferredName, "-dump"); + strcat(filename, "-dump"); } // Swap endian so the files match the txt display uint32_t data[T55x7_BLOCK_COUNT]; - for (int i = 0; i < T55x7_BLOCK_COUNT; i++) + for (int i = 0; i < T55x7_BLOCK_COUNT; i++) { data[i] = BSWAP_32(cardmem[i].blockdata); + } - saveFileJSON(preferredName, jsfT55x7, (uint8_t *)data, T55x7_BLOCK_COUNT * sizeof(uint32_t), NULL); - saveFileEML(preferredName, (uint8_t *)data, T55x7_BLOCK_COUNT * sizeof(uint32_t), sizeof(uint32_t)); - saveFile(preferredName, ".bin", data, sizeof(data)); + // 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)); } return PM3_SUCCESS; } static int CmdT55xxRestore(const char *Cmd) { - bool errors = false; - uint8_t cmdp = 0; - char preferredName[FILE_PATH_SIZE] = {0}; - char ext[FILE_PATH_SIZE] = {0}; - int success = PM3_ESOFT; - uint32_t password = 0x00; - bool usepwd = false; - uint32_t data[12] = {0}; - size_t datalen = 0; - uint8_t blockidx; - uint8_t downlink_mode; - char writeCmdOpt[100]; - char pwdOpt [11] = {0}; // p XXXXXXXX + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx restore", + "This command restores T55xx card page 0/1 n blocks", + "lf t55xx restore -f lf-t55xx-00148040-dump.bin" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_restore(); - case 'f': - param_getstr(Cmd, cmdp + 1, preferredName, FILE_PATH_SIZE); - if (strlen(preferredName) == 0) - errors = true; - cmdp += 2; - break; - case 'p': - password = param_get32ex(Cmd, cmdp + 1, 0, 16); - usepwd = true; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + void *argtable[] = { + arg_param_begin, + arg_str0("f", "filename", "", "filename of the dump file (bin|eml)"), + arg_str0("p", "pwd", "", "password if target card has password set (4 hex bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, sizeof(filename), &fnlen); + + bool usepwd = false; + uint32_t password = 0; + int res = arg_get_u32_hexstr_def_nlen(ctx, 2, 0, &password, 4, true); + if (res == 0 || res == 2) { + PrintAndLogEx(ERR, "Password should be 4 hex bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (res == 1) { + usepwd = true; + } + CLIParserFree(ctx); + + if (fnlen == 0) { + PrintAndLogEx(ERR, "Must specify a filename"); + return PM3_EINVARG; } - // File name expected to be .eml .bin or .json so sould be at least 4 - if (errors || (strlen(preferredName) == 0)) return usage_t55xx_restore(); + char ext[FILE_PATH_SIZE] = {0}; + uint32_t data[12] = {0}; + size_t datalen = 0; - // split file name into prefix and ext. - int fnLength; - - fnLength = strlen(preferredName); - - success = PM3_ESOFT; - if (fnLength > 4) { // Holds extension [.bin|.eml] - memcpy(ext, &preferredName[fnLength - 4], 4); + int retval = PM3_ESOFT; + if (fnlen > 4) { // Holds extension [.bin|.eml] + memcpy(ext, &filename[fnlen - 4], 4); ext[5] = 0x00; // check if valid file extension and attempt to load data - if (memcmp(ext, ".bin", 4) == 0) { - preferredName[fnLength - 4] = 0x00; - success = loadFile(preferredName, ".bin", data, sizeof(data), &datalen); + filename[fnlen - 4] = 0x00; + retval = loadFile(filename, ".bin", data, sizeof(data), &datalen); } else if (memcmp(ext, ".eml", 4) == 0) { - preferredName[fnLength - 4] = 0x00; + filename[fnlen - 4] = 0x00; datalen = 12; - success = loadFileEML(preferredName, (uint8_t *)data, &datalen); + retval = loadFileEML(filename, (uint8_t *)data, &datalen); - } else - PrintAndLogEx(WARNING, "\nWarning: invalid dump filename "_YELLOW_("%s")" to restore!\n", preferredName); - } - - if (success == PM3_SUCCESS) { // Got data, so write to cards - if (datalen == T55x7_BLOCK_COUNT * 4) { // 12 blocks * 4 bytes per block - if (usepwd) - snprintf(pwdOpt, sizeof(pwdOpt), "p %08X", password); - - // Restore endien for writing to card - for (blockidx = 0; blockidx < 12; blockidx++) - data[blockidx] = BSWAP_32(data[blockidx]); - - // Have data ready, lets write - // Order - // write blocks 1..7 page 0 - // write blocks 1..3 page 1 - // update downlink mode (if needed) and write b 0 - downlink_mode = 0; - if ((((data[11] >> 28) & 0xf) == 6) || (((data[11] >> 28) & 0xf) == 9)) - downlink_mode = (data[11] >> 10) & 3; - - // write out blocks 1-7 page 0 - for (blockidx = 1; blockidx <= 7; blockidx++) { - snprintf(writeCmdOpt, sizeof(writeCmdOpt), "b %d d %08X %s", blockidx, data[blockidx], pwdOpt); - - if (CmdT55xxWriteBlock(writeCmdOpt) != PM3_SUCCESS) - PrintAndLogEx(WARNING, "Warning: error writing blk %d", blockidx); - } - - // if password was set on the "blank" update as we may have just changed it - if (usepwd) - snprintf(pwdOpt, sizeof(pwdOpt), "p %08X", data[7]); - - // write out blocks 1-3 page 1 - for (blockidx = 9; blockidx <= 11; blockidx++) { - snprintf(writeCmdOpt, sizeof(writeCmdOpt), "b %d 1 d %08X %s", blockidx - 8, data[blockidx], pwdOpt); - - if (CmdT55xxWriteBlock(writeCmdOpt) != PM3_SUCCESS) - PrintAndLogEx(WARNING, "Warning: error writing blk %d", blockidx); - } - - // Update downlink mode for the page 0 config write. - config.downlink_mode = downlink_mode; - - // Write the page 0 config - snprintf(writeCmdOpt, sizeof(writeCmdOpt), "b 0 d %08X %s", data[0], pwdOpt); - if (CmdT55xxWriteBlock(writeCmdOpt) != PM3_SUCCESS) - PrintAndLogEx(WARNING, "Warning: error writing blk 0"); + } else { + PrintAndLogEx(WARNING, "\nWarning: invalid dump filename "_YELLOW_("%s")" to restore!\n", filename); } } + if (retval != PM3_SUCCESS) { + return retval; + } + + if (datalen == T55x7_BLOCK_COUNT * 4) { + // 12 blocks * 4 bytes per block + + // this fct creats strings to call "lf t55 write" command. + // + // + uint8_t downlink_mode; + char wcmd[100]; + char pwdopt [14] = {0}; // p XXXXXXXX + + if (usepwd) + snprintf(pwdopt, sizeof(pwdopt), "-p %08X", password); + + uint8_t idx; + // Restore endien for writing to card + for (idx = 0; idx < 12; idx++) { + data[idx] = BSWAP_32(data[idx]); + } + + // Have data ready, lets write + // Order + // write blocks 1..7 page 0 + // write blocks 1..3 page 1 + // update downlink mode (if needed) and write b 0 + downlink_mode = 0; + if ((((data[11] >> 28) & 0xf) == 6) || (((data[11] >> 28) & 0xf) == 9)) + downlink_mode = (data[11] >> 10) & 3; + + // write out blocks 1-7 page 0 + for (idx = 1; idx <= 7; idx++) { + snprintf(wcmd, sizeof(wcmd), "-b %d -d %08X %s", idx, data[idx], pwdopt); + + if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); + } + } + + // if password was set on the "blank" update as we may have just changed it + if (usepwd) { + snprintf(pwdopt, sizeof(pwdopt), "-p %08X", data[7]); + } + + // write out blocks 1-3 page 1 + for (idx = 9; idx <= 11; idx++) { + snprintf(wcmd, sizeof(wcmd), "-b %d --pg1 -d %08X %s", idx - 8, data[idx], pwdopt); + + if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Warning: error writing blk %d", idx); + } + } + + // Update downlink mode for the page 0 config write. + config.downlink_mode = downlink_mode; + + // Write the page 0 config + snprintf(wcmd, sizeof(wcmd), "-b 0 -d %08X %s", data[0], pwdopt); + if (CmdT55xxWriteBlock(wcmd) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Warning: error writing blk 0"); + } + } return PM3_SUCCESS; } /* @@ -2687,17 +2715,17 @@ char *GetConfigBlock0Source(uint8_t id) { char *retStr = buf; switch (id) { - case autoDetect: - snprintf(retStr, sizeof(buf), _YELLOW_("(Auto detect)")); + case AUTODETECT: + snprintf(retStr, sizeof(buf), _YELLOW_("(auto detect)")); break; - case userSet: - snprintf(retStr, sizeof(buf), _YELLOW_("(User set)")); + case USERSET: + snprintf(retStr, sizeof(buf), _YELLOW_("(user set)")); break; - case tagRead: - snprintf(retStr, sizeof(buf), _GREEN_("(Tag read)")); + case TAGREAD: + snprintf(retStr, sizeof(buf), _GREEN_("(tag read)")); break; default: - snprintf(retStr, sizeof(buf), _RED_("(Unknown)")); + snprintf(retStr, sizeof(buf), _RED_("(n/a)")); break; } return buf; @@ -2781,38 +2809,51 @@ static void t55x7_create_config_block(int tagtype) { static int CmdResetRead(const char *Cmd) { - uint8_t downlink_mode = config.downlink_mode; - uint8_t flags = 0; - uint8_t cmdp = 0; - bool errors = false; + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf t55xx resetread", + "Send Reset Cmd then `lf read` the stream to attempt\n" + "to identify the start of it (needs a demod and/or plot after)", + "lf t55xx resetread" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_resetread(); - case 'r': - downlink_mode = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (downlink_mode > 3) - downlink_mode = 0; + // 1 (help) + 0(one user specified params) + (5 T55XX_DLMODE_SINGLE) + void *argtable[0 + 5] = { + arg_param_begin, + arg_lit0("1", NULL, "extract using data from graphbuffer"), + }; + uint8_t idx = 1; + arg_add_t55xx_downloadlink(argtable, &idx, T55XX_DLMODE_SINGLE, config.downlink_mode); + CLIExecWithReturn(ctx, Cmd, argtable, true); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + bool r0 = arg_get_lit(ctx, 1); + bool r1 = arg_get_lit(ctx, 2); + bool r2 = arg_get_lit(ctx, 3); + bool r3 = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if ((r0 + r1 + r2 + r3) > 1) { + PrintAndLogEx(FAILED, "Error multiple downlink encoding"); + return PM3_EINVARG; } - if (errors) return usage_t55xx_resetread(); + uint8_t downlink_mode = config.downlink_mode; + if (r0) + downlink_mode = refFixedBit; + else if (r1) + downlink_mode = refLongLeading; + else if (r2) + downlink_mode = refLeading0; + else if (r3) + downlink_mode = ref1of4; - flags = downlink_mode << 3; + uint8_t flags = downlink_mode << 3; + + PrintAndLogEx(INFO, "Sending reset command..."); PacketResponseNG resp; - clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_RESET_READ, &flags, sizeof(flags)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_RESET_READ, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_RESET_READ, &resp, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -2826,6 +2867,7 @@ static int CmdResetRead(const char *Cmd) { return PM3_EMALLOC; } + PrintAndLogEx(INFO, "Downloading samples..."); if (!GetFromDevice(BIG_BUF, got, gotsize, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(got); @@ -2834,6 +2876,8 @@ static int CmdResetRead(const char *Cmd) { setGraphBuf(got, gotsize); free(got); } + + PrintAndLogEx(INFO, "Done"); return PM3_SUCCESS; } @@ -2903,35 +2947,36 @@ static int CmdT55xxWipe(const char *Cmd) { PrintAndLogEx(INFO, "Begin wiping..."); // Creating cmd string for write block :) - char writeData[36] = {0}; - char *ptrData = writeData; - snprintf(ptrData, sizeof(writeData), "b 0 "); + char wcmd[36] = {0}; + char *pwcmd = wcmd; + + snprintf(pwcmd, sizeof(wcmd), "-b 0 "); if (usepwd) { - snprintf(ptrData + strlen(writeData), sizeof(writeData) - strlen(writeData), "p %08x ", password); + snprintf(pwcmd + strlen(wcmd), sizeof(wcmd) - strlen(wcmd), "-p %08x ", password); } - snprintf(ptrData + strlen(writeData), sizeof(writeData) - strlen(writeData), "d %08X", block0); + snprintf(pwcmd + strlen(wcmd), sizeof(wcmd) - strlen(wcmd), "-d %08X", block0); - if (CmdT55xxWriteBlock(ptrData) != PM3_SUCCESS) + if (CmdT55xxWriteBlock(pwcmd) != PM3_SUCCESS) PrintAndLogEx(WARNING, "Warning: error writing blk 0"); for (uint8_t blk = 1; blk < 8; blk++) { - snprintf(ptrData, sizeof(writeData), "b %d d 0", blk); + snprintf(pwcmd, sizeof(wcmd), "-b %d -d 00000000", blk); - if (CmdT55xxWriteBlock(ptrData) != PM3_SUCCESS) + if (CmdT55xxWriteBlock(pwcmd) != PM3_SUCCESS) PrintAndLogEx(WARNING, "Warning: error writing blk %d", blk); - memset(writeData, 0x00, sizeof(writeData)); + memset(wcmd, 0x00, sizeof(wcmd)); } // Check and rest t55xx downlink mode. if (config.downlink_mode != T55XX_DLMODE_FIXED) { // Detect found a different mode so card must support - snprintf(ptrData, sizeof(writeData), "b 3 1 d 00000000"); - if (CmdT55xxWriteBlock(ptrData) != PM3_SUCCESS) { + snprintf(pwcmd, sizeof(wcmd), "-b 3 --pg1 -d 00000000"); + if (CmdT55xxWriteBlock(pwcmd) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Warning: failed writing block 3 page 1 (config)"); } - memset(writeData, 0x00, sizeof(writeData)); + memset(wcmd, 0x00, sizeof(wcmd)); } return PM3_SUCCESS; } @@ -3012,20 +3057,21 @@ static int CmdT55xxChkPwds(const char *Cmd) { return PM3_EINVARG; } - uint8_t downlink_mode = config.downlink_mode; - if (r0) - downlink_mode = refFixedBit; - else if (r1) + uint8_t downlink_mode = refFixedBit; // Password checks should always start with default/fixed bit unluess requested by user for specific mode + // if (r0 || ra) // ra should start downlink mode ad fixed bit to loop through all modes correctly + // downlink_mode = refFixedBit; + // else + if (r1) downlink_mode = refLongLeading; else if (r2) downlink_mode = refLeading0; else if (r3) downlink_mode = ref1of4; - bool use_pwd_file = false; + bool use_pwd_file = true; // Assume we are going to use a file, unless turned off later. + if (strlen(filename) == 0) { snprintf(filename, sizeof(filename), "t55xx_default_pwds"); - use_pwd_file = true; } PrintAndLogEx(INFO, "press " _GREEN_("'enter'") " to cancel the command"); @@ -3046,6 +3092,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { uint8_t flags = downlink_mode << 3; if (from_flash) { + use_pwd_file = false; // turn of local password file since we are checking from flash. clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_CHK_PWDS, &flags, sizeof(flags)); PacketResponseNG resp; @@ -3142,17 +3189,16 @@ static int CmdT55xxChkPwds(const char *Cmd) { PrintAndLogEx(INFO, "testing %08"PRIX32, curr_password); for (dl_mode = downlink_mode; dl_mode <= 3; dl_mode++) { - - if (!AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password, dl_mode)) { - continue; + // If aquire fails, then we still need to check if we are only trying a single downlink mode. + // If we continue on fail, it will skip that test and try the next downlink mode; thus slowing down the check + // when on a single downlink mode is wanted. + if (AcquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr_password, dl_mode)) { + found = t55xxTryDetectModulationEx(dl_mode, T55XX_PrintConfig, 0, curr_password); + if (found) { + PrintAndLogEx(SUCCESS, "found valid password: [ " _GREEN_("%08"PRIX32) " ]", curr_password); + break; + } } - - found = t55xxTryDetectModulationEx(dl_mode, T55XX_PrintConfig, 0, curr_password); - if (found) { - PrintAndLogEx(SUCCESS, "found valid password: [ " _GREEN_("%08"PRIX32) " ]", curr_password); - break; - } - if (ra == false) // Exit loop if not trying all downlink modes break; } @@ -3218,10 +3264,12 @@ static int CmdT55xxBruteForce(const char *Cmd) { return PM3_EINVARG; } - uint8_t downlink_mode = config.downlink_mode; - if (r0) - downlink_mode = refFixedBit; - else if (r1) + uint8_t downlink_mode = refFixedBit; // if no downlink mode suppliled use fixed bit/default as the is the most common + // Since we dont know the password the config.downlink mode is of little value. +// if (r0 || ra) // if try all (ra) then start at fixed bit for correct try all +// downlink_mode = refFixedBit; +// else + if (r1) downlink_mode = refLongLeading; else if (r2) downlink_mode = refLeading0; @@ -4191,8 +4239,12 @@ static int CmdT55xxSniff(const char *Cmd) { } static command_t CommandTable[] = { - {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, + {"-----------", CmdHelp, AlwaysAvailable, "---------------------------- " _CYAN_("notice") " -----------------------------"}, + {"", CmdHelp, AlwaysAvailable, "Remember to run `" _YELLOW_("lf t55xx detect") "` first whenever a new card"}, + {"", CmdHelp, AlwaysAvailable, "is placed on the Proxmark3 or the config block changed."}, + {"", CmdHelp, AlwaysAvailable, ""}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, {"clonehelp", CmdT55xxCloneHelp, IfPm3Lf, "Shows the available clone commands"}, {"config", CmdT55xxSetConfig, AlwaysAvailable, "Set/Get T55XX configuration (modulation, inverted, offset, rate)"}, {"dangerraw", CmdT55xxDangerousRaw, IfPm3Lf, "Sends raw bitstream. Dangerous, do not use!!"}, @@ -4202,7 +4254,7 @@ static command_t CommandTable[] = { {"info", CmdT55xxInfo, AlwaysAvailable, "Show T55x7 configuration data (page 0/ blk 0)"}, {"p1detect", CmdT55xxDetectPage1, IfPm3Lf, "Try detecting if this is a t55xx tag by reading page 1"}, {"read", CmdT55xxReadBlock, IfPm3Lf, "Read T55xx block data"}, - {"resetread", CmdResetRead, IfPm3Lf, "Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)"}, + {"resetread", CmdResetRead, IfPm3Lf, "Send Reset Cmd then lf read the stream to attempt to identify the start of it"}, {"restore", CmdT55xxRestore, IfPm3Lf, "Restore T55xx card Page 0 / Page 1 blocks"}, {"trace", CmdT55xxReadTrace, AlwaysAvailable, "Show T55x7 traceability data (page 1/ blk 0-1)"}, {"wakeup", CmdT55xxWakeUp, IfPm3Lf, "Send AOR wakeup command"}, diff --git a/client/src/cmdlft55xx.h b/client/src/cmdlft55xx.h index 83a35b848..d9061d039 100644 --- a/client/src/cmdlft55xx.h +++ b/client/src/cmdlft55xx.h @@ -32,6 +32,7 @@ #define T55X7_NORALSY_CONFIG_BLOCK 0x00088C6A // ASK, compat mode, (NORALSY - KCP3000), data rate 32, 3 data blocks #define T55X7_PRESCO_CONFIG_BLOCK 0x00088088 // ASK, data rate 32, Manchester, 4 data blocks, STT #define T55X7_SECURAKEY_CONFIG_BLOCK 0x000C8060 // ASK, Manchester, data rate 40, 3 data blocks +#define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ... // FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag // we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo, @@ -126,10 +127,10 @@ typedef struct { uint8_t offset; uint32_t block0; enum { - notSet = 0x00, - autoDetect = 0x01, - userSet = 0x02, - tagRead = 0x03, + NOTSET = 0x00, + AUTODETECT = 0x01, + USERSET = 0x02, + TAGREAD = 0x03, } block0Status; enum { RF_8 = 0x00, diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index fe4fa0b84..7e0c823a4 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -298,10 +298,10 @@ static command_t CommandTable[] = { {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"}, {"clear", CmdClear, AlwaysAvailable, "Clear screen"}, - {"help", CmdHelp, AlwaysAvailable, "This help. Use " _YELLOW_("' help'") " for details of a particular command."}, + {"help", CmdHelp, AlwaysAvailable, "Use " _YELLOW_("' help'") " for details of a particular command."}, {"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"}, {"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"}, - {"pref", CmdPref, AlwaysAvailable, "Edit preferences"}, + {"pref", CmdPref, AlwaysAvailable, "{ Edit preferences... }"}, {"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"}, {"quit", CmdQuit, AlwaysAvailable, ""}, {"exit", CmdQuit, AlwaysAvailable, "Exit program"}, diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 4f3aad7d9..7c44c8978 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -429,9 +429,9 @@ static int CmdScriptRun(const char *Cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "Usage info"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdScriptList, AlwaysAvailable, "List available scripts"}, - {"run", CmdScriptRun, AlwaysAvailable, " -- execute a script"}, + {"run", CmdScriptRun, AlwaysAvailable, " - execute a script"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 4369d3a71..ba1987502 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -242,7 +242,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } for (int j = 0; j < data_len && j / 18 < 18; j++) { - uint8_t parityBits = parityBytes[j >> 3]; if (protocol != LEGIC && protocol != ISO_14443B @@ -256,7 +255,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr && protocol != FELICA && protocol != LTO && protocol != PROTO_CRYPTORF - && (hdr->isResponse || protocol == ISO_14443A) + && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE) && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) { snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]); @@ -280,9 +279,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr if (markCRCBytes) { //CRC-command if (crcStatus == 0 || crcStatus == 1) { - char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4); + char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4) - 1; (*pos1) = '['; - char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4); + char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4) - 1; sprintf(pos2, "%c", ']'); } } diff --git a/client/src/comms.c b/client/src/comms.c index 57a03afe7..26c68d889 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -839,6 +839,9 @@ static bool dl_it(uint8_t *dest, uint32_t bytes, PacketResponseNG *response, siz if (response->cmd == CMD_ACK) return true; + // Spiffs download is converted to NG, + if (response->cmd == CMD_SPIFFS_DOWNLOAD) + return true; // sample_buf is a array pointer, located in data.c // arg0 = offset in transfer. Startindex of this chunk diff --git a/client/src/flash.c b/client/src/flash.c index 9693d8803..2f5f037c3 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -412,7 +412,7 @@ static void flash_suggest_update_bootloader(void) { PrintAndLogEx(ERR, _RED_("reboot the Proxmark3 then only update the main firmware") "\n"); PrintAndLogEx(ERR, "Follow these steps :"); PrintAndLogEx(ERR, " 1) ./pm3-flash-bootrom"); - PrintAndLogEx(ERR, " 2) ./pm3-flash-flash-all"); + PrintAndLogEx(ERR, " 2) ./pm3-flash-all"); PrintAndLogEx(ERR, " 3) ./pm3"); PrintAndLogEx(INFO, "--------------------------------------------------------"); g_printed_msg = true; diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index c6e02013e..4a34421ba 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -695,7 +695,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl mem[4] = (chunk & 0xFF); // upload to flash. - res = flashmem_spiffs_load(destfn, mem, 5 + (chunk * 6)); + res = flashmem_spiffs_load((char*)destfn, mem, 5 + (chunk * 6)); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "\nSPIFFS upload failed"); free(mem); diff --git a/client/src/preferences.c b/client/src/preferences.c index d961d2c6f..41b8fc1a6 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -761,7 +761,7 @@ static int setCmdHint(const char *Cmd) { static int setCmdPlotSliders(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set plotsliders ", + CLIParserInit(&ctx, "pref set plotsliders", "Set presistent preference of showing the plotslider control in the client", "pref set plotsliders --on" ); @@ -803,7 +803,7 @@ static int setCmdPlotSliders(const char *Cmd) { static int setCmdSavePaths(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "pref set savepath", + CLIParserInit(&ctx, "pref set savepaths", "Set presistent preference of file paths in the client", "pref set savepaths --dump /home/mydumpfolder -> all dump files will be saved into this folder\n" "pref set savepaths --def /home/myfolder -c -> create if needed, all files will be saved into this folder" @@ -930,36 +930,113 @@ static int setCmdBarMode(const char *Cmd) { } static int getCmdEmoji(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get emoji", + "Get preference of using emojis in the client", + "pref get emoji" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showEmojiState(prefShowNone); return PM3_SUCCESS; } static int getCmdHint(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get hints", + "Get preference of showing hint messages in the client", + "pref get hints" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showHintsState(prefShowNone); return PM3_SUCCESS; } static int getCmdColor(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get color", + "Get preference of using colors in the client", + "pref get color" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showColorState(prefShowNone); return PM3_SUCCESS; } static int getCmdDebug(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get clientdebug", + "Get preference of using clientside debug level", + "pref get clientdebug" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showClientDebugState(prefShowNone); return PM3_SUCCESS; } static int getCmdPlotSlider(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get plotsliders", + "Get preference of showing the plotslider control in the client", + "pref get plotsliders" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showPlotSliderState(prefShowNone); return PM3_SUCCESS; } static int getCmdBarMode(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get barmode", + "Get preference of HF/LF tune command styled output in the client", + "pref get barmode" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showBarModeState(prefShowNone); return PM3_SUCCESS; } static int getCmdSavePaths(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref get savepaths", + "Get preference of file paths in the client", + "pref get savepaths" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); showSavePathState(spDefault, prefShowNone); showSavePathState(spDump, prefShowNone); showSavePathState(spTrace, prefShowNone); @@ -1008,6 +1085,17 @@ static int CmdPrefSet(const char *Cmd) { } static int CmdPrefShow(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "pref show", + "Show all persistent preferences", + "pref show" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); if (session.preferences_loaded) { char *fn = prefGetFilename(); @@ -1043,8 +1131,8 @@ static int CmdPrefSave (const char *Cmd) { */ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"get", CmdPrefGet, AlwaysAvailable, "Get a preference"}, - {"set", CmdPrefSet, AlwaysAvailable, "Set a preference"}, + {"get", CmdPrefGet, AlwaysAvailable, "{ Get a preference }"}, + {"set", CmdPrefSet, AlwaysAvailable, "{ Set a preference }"}, {"show", CmdPrefShow, AlwaysAvailable, "Show all preferences"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/util.c b/client/src/util.c index 53825f28b..978cb4b45 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -183,45 +183,54 @@ void print_hex(const uint8_t *data, const size_t len) { } void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) { - if (data == NULL || len == 0) return; + if (data == NULL || len == 0 || breaks == 0) return; - int rownum = 0; - PrintAndLogEx(NORMAL, "[%02d] | " NOLF, rownum); - for (size_t i = 0; i < len; ++i) { - - PrintAndLogEx(NORMAL, "%02X " NOLF, data[i]); - - // check if a line break is needed - if (breaks > 0 && !((i + 1) % breaks) && (i + 1 < len)) { - ++rownum; - PrintAndLogEx(NORMAL, "\n[%02d] | " NOLF, rownum); + uint16_t rownum = 0; + int i; + for (i = 0; i < len; i += breaks, rownum++) { + if (len - i < breaks) { // incomplete block, will be treated out of the loop + break; } + PrintAndLogEx(INFO, "%02u | %s", rownum, sprint_hex_ascii(data + i, breaks)); + } + + // the last odd bytes + uint8_t mod = len % breaks; + + if (mod) { + char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; + memset(buf, 0, sizeof(buf)); + hex_to_buffer((uint8_t *)buf, data + i, mod, (sizeof(buf) - 1), 0, 1, true); + + // add the spaces... + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " "); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod)); + PrintAndLogEx(INFO, "%02u | %s", rownum, buf); } - PrintAndLogEx(NORMAL, ""); } -void print_buffer(const uint8_t *data, const size_t len, int level) { +static void print_buffer_ex(const uint8_t *data, const size_t len, int level, uint8_t breaks) { if (len < 1) return; char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; int i; - for (i = 0; i < len; i += 16) { - if (len - i < 16) { // incomplete block, will be treated out of the loop + for (i = 0; i < len; i += breaks) { + if (len - i < breaks) { // incomplete block, will be treated out of the loop break; } // (16 * 3) + (16) + + 1 memset(buf, 0, sizeof(buf)); sprintf(buf, "%*s%02x: ", (level * 4), " ", i); - hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, 16, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, 16)); + hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, breaks, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, breaks)); PrintAndLogEx(INFO, "%s", buf); } // the last odd bytes - uint8_t mod = len % 16; + uint8_t mod = len % breaks; if (mod) { memset(buf, 0, sizeof(buf)); @@ -229,13 +238,17 @@ void print_buffer(const uint8_t *data, const size_t len, int level) { hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, mod, (sizeof(buf) - strlen(buf) - 1), 0, 1, true); // add the spaces... - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((16 - mod) * 3), " "); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " "); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod)); PrintAndLogEx(INFO, "%s", buf); } } +void print_buffer(const uint8_t *data, const size_t len, int level) { + print_buffer_ex(data, len, level, 16); +} + void print_blocks(uint32_t *data, size_t len) { PrintAndLogEx(SUCCESS, "Blk | Data "); PrintAndLogEx(SUCCESS, "----+------------"); diff --git a/doc/T5577_Guide.md b/doc/T5577_Guide.md index cdffadd20..61d926b22 100644 --- a/doc/T5577_Guide.md +++ b/doc/T5577_Guide.md @@ -1,8 +1,9 @@ # T5577 Introduction Guide -### Based on RRG proxmark3 fork. +### Based on RRG/Iceman Proxmark3 repo ### Ver.1 8 Sep 2019 +### Ver.2 7 March 2021 | Contents | | ----------------------------------------------------------------------------------- | @@ -23,17 +24,17 @@ ## Introduction -The T5577 is a generic LF (Low Frequency) RFID card the is used in the +The T5577 is a generic LF (Low Frequency) RFID card that is used in the 125 Khz frequency space. It is a good card to use to learn about RFID and learn how to use the proxmark3. -It is highly recommend that when learning about RFID that learning how +It is highly recommended that when learning about RFID that learning how to read the data sheets be near the top of the list. It can be very hard as the data sheet will hold the information you need, but you don’t yet know what it means. As such, I will attempt to point to sections of the data sheet and would highly advise that you look at the data sheet as you go. Overtime the data sheet may change, as a result things may not -always be reference correctly. +always be referenced correctly. As at writing this guide, the data sheet can be found at : @@ -63,9 +64,9 @@ the chip how to behave. ## What data is on my T5577 -Let’s have a look and see what a card might look in the proxmark3 +Let’s have a look and see what a card might look like in the proxmark3 software. Since we can change the configuration of how the T5577 will -output data, the proxmark3 software need to work out how to interpreted +output data, the proxmark3 software needs to work out how to interpret the data it receives, we do this with the following command. It should be noted that the T5577 has many clones. As such the default @@ -77,16 +78,18 @@ examples shown, it will be assumed you have run the detect command. ``` [usb] pm3 --> lf t55xx detect ``` -You should see a results simular to the following: +You should see a results similar to the following: ``` - Chip Type : T55x7 - Modulation : ASK - Bit Rate : 2 - RF/32 - Inverted : No - Offset : 32 - Seq. Term. : Yes - Block0 : 0x000880E0 - Downlink Mode : default/fixed bit length + [=] Chip type......... T55x7 + [=] Modulation........ ASK + [=] Bit rate.......... 2 - RF/32 + [=] Inverted.......... No + [=] Offset............ 33 + [=] Seq. terminator... Yes + [=] Block0............ 000880E0 (auto detect) + [=] Downlink mode..... default/fixed bit length + [=] Password set...... No + ``` Now that the proxmark3 has detected a T55x7 chip, and found some information about it, we should be able to see all the data on the chip. @@ -99,20 +102,20 @@ Your results should look similar to the following: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... -[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 01 | 00000000 | 00000000000000000000000000000000 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... +[+] 04 | 00000000 | 00000000000000000000000000000000 | .... +[+] 05 | 00000000 | 00000000000000000000000000000000 | .... +[+] 06 | 00000000 | 00000000000000000000000000000000 | .... +[+] 07 | 00000000 | 00000000000000000000000000000000 | .... [+] Reading Page 1: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... -[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H -[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 01 | 00000000 | 00000000000000000000000000000000 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... ``` I will cover the meaning of this data as we go, but for now, lets keep it simple. @@ -123,7 +126,7 @@ The basic function of using the proxmark3 with rfid cards is to read and write data. This reading and writing must be done in the correct way needed for the chip (and its configuration). Lucky for us, the developers have done a great job and gave us commands. What we need to -know is that with the T5577 data is read/written one complete block at a +know is that with the T5577, data is read/written one complete block at a time. Each block holds 32 bits of data (hence the binary output shown) Since we know that the card has data and configuration blocks, lets say @@ -140,23 +143,23 @@ can see the card) as, run a low frequency (lf) command for the T55xx chip (t55xx) and read block (b) number 1. ``` - [usb] pm3 --> lf t55xx read b 1 + [usb] pm3 --> lf t55xx read -b 1 ``` result: ``` [+] Reading Page 0: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- - [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 01 | 00000000 | 00000000000000000000000000000000 | .... ``` - Note: Depending on the history of your card your data may vary, but + Note: Depending on the history of your card, your data may vary, but should match the dump data. 2) Write some new data into block 1 on the card. - We use the d option to supply the data ‘12345678’ + We use the -d option to supply the data ‘12345678’ ``` - [usb] pm3 --> lf t55xx write b 1 d 12345678 + [usb] pm3 --> lf t55xx write -b 1 -d 12345678 ``` result: ``` @@ -164,7 +167,7 @@ can see the card) ``` 3) Now, lets check if the data was written. ``` - [usb] pm3 --> lf t55xx read b 1 + [usb] pm3 --> lf t55xx read -b 1 ``` result: ``` @@ -174,8 +177,8 @@ can see the card) [+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx ``` 4) The data is written in Hexadecimal. A single hex digit holds 4 bits - of data. So to store 32 bits in a block we need to supply 8 hex - digits (8 \* 4 = 32). If you are familiar with hex and binary do a + of data. So to store 32 bits in a block, we need to supply 8 hex + digits (8 \* 4 = 32). If you are not familiar with hex and binary do a little bit of home work to learn. The following is a quick start. | Hex | Binary | Decimal | @@ -202,7 +205,7 @@ can see the card) Lets try and write 89ABCDEF ``` - [usb] pm3 --> lf t55xx write b 1 d 89abcdef + [usb] pm3 --> lf t55xx write -b 1 -d 89abcdef ``` result: ``` @@ -210,7 +213,7 @@ can see the card) ``` and check ``` - [usb] pm3 --> lf t55xx read b 1 + [usb] pm3 --> lf t55xx read -b 1 ``` result: ``` @@ -235,31 +238,34 @@ result: [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... -[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... -[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... +[+] 04 | 00000000 | 00000000000000000000000000000000 | .... +[+] 05 | 00000000 | 00000000000000000000000000000000 | .... +[+] 06 | 00000000 | 00000000000000000000000000000000 | .... +[+] 07 | 00000000 | 00000000000000000000000000000000 | .... [+] Reading Page 1: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... -[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H -[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. -[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... +[+] 01 | 00000000 | 00000000000000000000000000000000 | .... +[+] 02 | 00000000 | 00000000000000000000000000000000 | .... +[+] 03 | 00000000 | 00000000000000000000000000000000 | .... ``` -Practice reading and writing to blocks 1 to 7 until you are happy you +Practice reading and writing to blocks 1 to 6 until you are happy you can do it and get the results you wanted (i.e. the data you want stored -is written to the block you want it stored in). +is written to the block you want it stored in). I recommend staying +away from block 7 as this is where the password is stored, if used. +If you forget this data/password, you wont be able to read or write +to the card. ## How do I use a password This can be a little tricky for beginners. ***If you forget your password you will lose access to your card***. -To tell the T5577 to use a password we have to change the data in the +To tell the T5577 to use a password, we have to change the data in the configuration block (0). To help learn this and make it as simple as I can, please read and follow exactly. If your results DON’T match 100% as required, please do not proceed. @@ -272,9 +278,10 @@ required, please do not proceed. ``` Result: ``` - [=] Begin wiping T55x7 tag + [=] Target T55x7 tag + [=] Default configuration block 000880E0 - [=] Default configation block 000880E0 + [=] Begin wiping... [=] Writing page 0 block: 00 data: 0x000880E0 [=] Writing page 0 block: 01 data: 0x00000000 [=] Writing page 0 block: 02 data: 0x00000000 @@ -291,14 +298,15 @@ required, please do not proceed. ``` result: ``` - Chip Type : T55x7 - Modulation : ASK - Bit Rate : 2 - RF/32 - Inverted : No - Offset : 32 - Seq. Term. : Yes - Block0 : 0x000880E0 - Downlink Mode : default/fixed bit length + [=] Chip type......... T55x7 + [=] Modulation........ ASK + [=] Bit rate.......... 2 - RF/32 + [=] Inverted.......... No + [=] Offset............ 33 + [=] Seq. terminator... Yes + [=] Block0............ 000880E0 (auto detect) + [=] Downlink mode..... default/fixed bit length + [=] Password set...... No ``` If block 0 does not hold the hex data **0x00088040 resolve this @@ -309,7 +317,7 @@ required, please do not proceed. The password is saved in block 7 of page 0. ``` - [usb] pm3 --> lf t55xx write b 7 d 12345678 + [usb] pm3 --> lf t55xx write -b 7 -d 12345678 ``` result: ``` @@ -326,20 +334,20 @@ required, please do not proceed. [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... - [+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... - [+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 01 | 00000000 | 00000000000000000000000000000000 | .... + [+] 02 | 00000000 | 00000000000000000000000000000000 | .... + [+] 03 | 00000000 | 00000000000000000000000000000000 | .... + [+] 04 | 00000000 | 00000000000000000000000000000000 | .... + [+] 05 | 00000000 | 00000000000000000000000000000000 | .... + [+] 06 | 00000000 | 00000000000000000000000000000000 | .... [+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx [+] Reading Page 1: [+] blk | hex data | binary | ascii [+] ----+----------+----------------------------------+------- [+] 00 | 000880E0 | 00000000000010001000000011100000 | .... - [+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H - [+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. - [+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... + [+] 01 | 00000000 | 00000000000000000000000000000000 | .... + [+] 02 | 00000000 | 00000000000000000000000000000000 | .... + [+] 03 | 00000000 | 00000000000000000000000000000000 | .... ``` ***Important : If block 0 and block 7 don’t match exactly, do not continue.*** @@ -353,7 +361,7 @@ required, please do not proceed. ![](./t55xx_block0.png) We will cover other things in the configuration later. But the key - note here is we ONLY want to change bit 28 and nothing else. + note here, is we ONLY want to change bit 28 and nothing else. Current Block 0 : ***00088040*** New Block 0 : ***00088050*** @@ -361,7 +369,7 @@ required, please do not proceed. To understand what happened to get from 00088040 to 00088050 we need to look at the binary data. - While this can be confusing it is important to understand this as we + While this can be confusing, it is important to understand this as we do more advanced things. Bit Location (28) @@ -375,7 +383,7 @@ required, please do not proceed. - See how in the above we change the bit in location 28 from a 0 to 1 + See how in the above we changed the bit in location 28 from a 0 to 1 0 = No Password, 1 = Use Password Note how we did NOT change any other part of the configuration, only bit 28. @@ -389,7 +397,7 @@ required, please do not proceed. If you have completed all steps and have the exact same results, we are ready to apply the new configuration. ``` - [usb] pm3 --> lf t55xx write b 0 d 00088050 + [usb] pm3 --> lf t55xx write -b 0 -d 00088050 ``` result: ``` @@ -410,25 +418,27 @@ required, please do not proceed. card. Lets try again, but this time supply the password. We use the option - p followed by the password. + -p followed by the password. ``` - [usb] pm3 --> lf t55 detect p 12345678 + [usb] pm3 --> lf t55 detect -p 12345678 ``` result: ``` - Chip Type : T55x7 - Modulation : ASK - Bit Rate : 2 - RF/32 - Inverted : No - Offset : 32 - Seq. Term. : Yes - Block0 : 0x00088050 - Downlink Mode : default/fixed bit length + [=] Chip type......... T55x7 + [=] Modulation........ ASK + [=] Bit rate.......... 2 - RF/32 + [=] Inverted.......... No + [=] Offset............ 33 + [=] Seq. terminator... Yes + [=] Block0............ 00088050 (auto detect) + [=] Downlink mode..... default/fixed bit length + [=] Password set...... Yes + [=] Password.......... 12345678 ``` 7) Write a block of data with a password ``` - [usb] pm3 --> lf t55xx write b 1 d 1234abcd p 12345678 + [usb] pm3 --> lf t55xx write -b 1 -d 1234abcd -p 12345678 ``` result: ``` @@ -445,7 +455,7 @@ required, please do not proceed. The proxmark3 has a safety check\! ``` - [usb] pm3 --> lf t55xx read b 1 p 12345678 + [usb] pm3 --> lf t55xx read -b 1 -p 12345678 ``` result: ``` @@ -455,12 +465,12 @@ required, please do not proceed. [!] Safety check: Could not detect if PWD bit is set in config block. Exits. ``` - Note that the proxmark3 did not read the block, the safty kicked in - and wants us to confirm by supply the override option ‘o’. + Note that the proxmark3 did not read the block, the safety kicked in + and wants us to confirm by supply the override option ‘-o’. - Lets try again with the ‘o’ option as we know the password is set. + Lets try again with the ‘-o’ option as we know the password is set. ``` - [usb] pm3 --> lf t55xx read b 1 p 12345678 o + [usb] pm3 --> lf t55xx read -b 1 -p 12345678 -o ``` result: ``` @@ -479,14 +489,14 @@ required, please do not proceed. this from above. Remember if we don’t know the config and write this config to the - card, it will over write all other settings. This can recoved the + card, it will overwrite all other settings. This can recover the card, but will lose any settings you may want. So it’s a good idea to read the config, and set bit 28 to 0, rather than just overwrite the config and change the way the card works. In our examples we know what it should be : 00088040 ``` - [usb] pm3 --> lf t55xx write b 0 d 00088040 p 12345678 + [usb] pm3 --> lf t55xx write -b 0 -d 00088040 -p 12345678 ``` result: ``` @@ -498,16 +508,17 @@ required, please do not proceed. ``` result: ``` - Chip Type : T55x7 - Modulation : ASK - Bit Rate : 2 - RF/32 - Inverted : No - Offset : 32 - Seq. Term. : Yes - Block0 : 0x00088040 - Downlink Mode : default/fixed bit length + [=] Chip type......... T55x7 + [=] Modulation........ ASK + [=] Bit rate.......... 2 - RF/32 + [=] Inverted.......... No + [=] Offset............ 33 + [=] Seq. terminator... Yes + [=] Block0............ 00088040 (auto detect) + [=] Downlink mode..... default/fixed bit length + [=] Password set...... No ``` - Yes we can and we can see Block 0 is the correct config 00088040 + Yes we can! We can see Block 0 is the correct config 00088040 # Part 2 – Configuration Blocks @@ -523,7 +534,7 @@ from Block 0 in Page 0. It will use this in both default read mode (where is sends out the blocks from 1 to x on power up), as well as when it responds to commands. -In the Read To Card, the T5577 will encode the data using the settings +In the Reader To Card, the T5577 will encode the data using the settings from Block 3 Page 1. If the command is not encoded correctly it will ignore the command and revert back to default read mode. @@ -533,10 +544,10 @@ For this configuration the settings chosen will be for the purpose of the card when used in production. E.G. If you want the card to act like an EM4100, then we need to choose the settings that work like the EM4100; same goes for others like HID. I am not going to cover these -here, rather use an example. Others have collect these and posted on the -forum. +here, rather use an example. Others have collected these and posted on the +forum or can be found by searching the web. -To get started lets look back at the tech sheet. +To get started lets look back at the data sheet. ![](./t55xx_clock0_cfg.png) @@ -571,14 +582,15 @@ password set (if not, review and get you card back to this state). 1) Lets turn you T5577 into an EM4100 with ID 1122334455 ``` - [usb] pm3 --> lf em 410x_write 1122334455 1 + [usb] pm3 --> lf em 410x clone --id 1122334455 ``` result: ``` - [+] Writing T55x7 tag with UID 0x1122334455 (clock rate: 64) - #db# Started writing T55x7 tag ... - #db# Clock rate: 64 - #db# Tag T55x7 written with 0xff8c65298c94a940 + [+] Preparing to clone EM4102 to T55x7 tag with ID 1122334455 (RF/64) + [#] Clock rate: 64 + [#] Tag T55x7 written with 0xff8c65298c94a940 + + [+] Done ``` 2) Check this has work. @@ -586,89 +598,84 @@ password set (if not, review and get you card back to this state). [usb] pm3 --> lf search ``` result: - ``` - [=] NOTE: some demods output possible binary - [=] if it finds something that looks like a tag - [=] False Positives ARE possible - [=] - [=] Checking for known tags... - - [+] EM410x pattern found - - EM TAG ID : 1122334455 - - Possible de-scramble patterns - - Unique TAG ID : 8844CC22AA - HoneyWell IdentKey { - DEZ 8 : 03359829 - DEZ 10 : 0573785173 - DEZ 5.5 : 08755.17493 - DEZ 3.5A : 017.17493 - DEZ 3.5B : 034.17493 - DEZ 3.5C : 051.17493 - DEZ 14/IK2 : 00073588229205 - DEZ 15/IK3 : 000585269781162 - DEZ 20/ZK : 08080404121202021010 - } - Other : 17493_051_03359829 - Pattern Paxton : 289899093 [0x11478255] - Pattern 1 : 5931804 [0x5A831C] - Pattern Sebury : 17493 51 3359829 [0x4455 0x33 0x334455] - - [+] Valid EM410x ID found! - - - [+] Chipset detection : T55xx found - - [+] Try `lf t55xx` commands + ``` + [=] NOTE: some demods output possible binary + [=] if it finds something that looks like a tag + [=] False Positives ARE possible + [=] + [=] Checking for known tags... + [=] + [+] EM 410x ID 0F0368568B + [+] EM410x ( RF/64 ) + [=] -------- Possible de-scramble patterns --------- + [+] Unique TAG ID : F0C0166AD1 + [=] HoneyWell IdentKey + [+] DEZ 8 : 06837899 + [+] DEZ 10 : 0057169547 + [+] DEZ 5.5 : 00872.22155 + [+] DEZ 3.5A : 015.22155 + [+] DEZ 3.5B : 003.22155 + [+] DEZ 3.5C : 104.22155 + [+] DEZ 14/IK2 : 00064481678987 + [+] DEZ 15/IK3 : 001034014845649 + [+] DEZ 20/ZK : 15001200010606101301 + [=] + [+] Other : 22155_104_06837899 + [+] Pattern Paxton : 259822731 [0xF7C948B] + [+] Pattern 1 : 9750181 [0x94C6A5] + [+] Pattern Sebury : 22155 104 6837899 [0x568B 0x68 0x68568B] + [=] ------------------------------------------------ + + [+] Valid EM410x ID found! + + [+] Chipset detection: T55xx + [?] Hint: try `lf t55xx` commands ``` Looks good. -3) Now lest see what the T5577 detect and info shows +3) Now lets see what the T5577 detect and info shows ``` [usb] pm3 --> lf t55 detect ``` result: ``` - [usb] pm3 --> lf t55 detect - Chip Type : T55x7 - Modulation : ASK - Bit Rate : 5 - RF/64 - Inverted : No - Offset : 32 - Seq. Term. : Yes - Block0 : 0x00148040 - Downlink Mode : default/fixed bit length + [=] Chip type......... T55x7 + [=] Modulation........ ASK + [=] Bit rate.......... 5 - RF/64 + [=] Inverted.......... No + [=] Offset............ 33 + [=] Seq. terminator... Yes + [=] Block0............ 00148040 (auto detect) + [=] Downlink mode..... default/fixed bit length + [=] Password set...... No ``` ``` [usb] pm3 --> lf t55xx info ``` result: ``` - - -- T55x7 Configuration & Tag Information -------------------- - ------------------------------------------------------------- - Safer key : 0 - reserved : 0 - Data bit rate : 5 - RF/64 - eXtended mode : No - Modulation : 8 - Manchester - PSK clock frequency : 0 - RF/2 - AOR - Answer on Request : No - OTP - One Time Pad : No - Max block : 2 - Password mode : No - Sequence Terminator : No - Fast Write : No - Inverse data : No - POR-Delay : No - ------------------------------------------------------------- - Raw Data - Page 0 - Block 0 : 0x00148040 00000000000101001000000001000000 - - Config block match : EM unique, Paxton - ------------------------------------------------------------- + + [=] --- T55x7 Configuration & Information --------- + [=] Safer key : 0 + [=] reserved : 0 + [=] Data bit rate : 5 - RF/64 + [=] eXtended mode : No + [=] Modulation : 8 - Manchester + [=] PSK clock frequency : 0 - RF/2 + [=] AOR - Answer on Request : No + [=] OTP - One Time Pad : No + [=] Max block : 2 + [=] Password mode : No + [=] Sequence Terminator : No + [=] Fast Write : No + [=] Inverse data : No + [=] POR-Delay : No + [=] ------------------------------------------------------------- + [=] Raw Data - Page 0, block 0 + [=] 00148040 - 00000000000101001000000001000000 + [=] --- Fingerprint ------------ + [+] Config block match : EM unique, Paxton + ``` We can see that the info gave us more information and confirmed what we decoded by hand. But remember, the detect is still needed so the diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 1358c8f3d..4ff857d9c 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -561,7 +561,7 @@ EM is ASK HID Prox is FSK Indala is PSK -pm3 --> lf t55xx config FSK +pm3 --> lf t55xx config --FSK ``` Set timings to default @@ -579,7 +579,7 @@ Write to T55xx block b : block number to write. Between 0-7 d : 4 bytes of data to write (8 hex characters) -pm3 --> lf t55xx wr b 0 d 00081040 +pm3 --> lf t55xx wr -b 0 -d 00081040 ``` Wipe a T55xx tag and set defaults diff --git a/doc/cliparser_todo.txt b/doc/cliparser_todo.txt index 44f0bac7d..188870467 100644 --- a/doc/cliparser_todo.txt +++ b/doc/cliparser_todo.txt @@ -1,5 +1,4 @@ clear -pref analyse foo data biphaserawdecode data detectclock @@ -32,7 +31,6 @@ data print data samples data setdebugmode data tune -hf 14b sriwrite hf 15 dump hf 15 info hf 15 raw @@ -85,21 +83,6 @@ hf mf cview hf mf gen3uid hf mf gen3blk hf mf gen3freeze -lf em 410x -lf em 4x05 -lf em 4x50 -lf em 4x70 lf hitag reader -lf hitag sim lf hitag writer lf hitag dump -lf hitag cc -lf t55xx config -lf t55xx dump -lf t55xx info -lf t55xx read -lf t55xx resetread -lf t55xx restore -lf t55xx trace -lf t55xx write -script run diff --git a/doc/cloner_notes.md b/doc/cloner_notes.md index 71cada411..77edc244b 100644 --- a/doc/cloner_notes.md +++ b/doc/cloner_notes.md @@ -60,8 +60,8 @@ Standard password is normally (for T55xx): AA55BBBB # Restore page1 data ``` -lf t55xx write b 1 d E0150A48 1 -If t55xx write b 2 d 2D782308 1 +lf t55xx write -b 1 -d E0150A48 --pg1 +If t55xx write -b 2 -d 2D782308 --pg1 ``` # Sniffing the comms @@ -69,12 +69,12 @@ The T55x7 protocol uses a pwm based protocol for writing to tags. In order to m ``` -- after threshold limit 20 is triggered, skip 10000 samples before collecting samples. -lf config s 10000 t 20 +lf config -s 10000 -t 20 lf t55xx sniff -- if you have a save trace from before, try data load -f xxxxxxx.pm3 -lf t55xx sniff 1 +lf t55xx sniff -1 ``` It uses the existing `lf sniff` command to collect the data, so setting that first as per normal sniffing is recommended. Once you have a sniff, you can "re-sniff" from the stored sniffed data and try different settings, if you think the data is not clean. diff --git a/doc/commands.md b/doc/commands.md index 1c9938d16..862ae353f 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -12,10 +12,9 @@ Check column "offline" for their availability. |------- |------- |----------- |`auto `|N |`Automated detection process for unknown tags` |`clear `|Y |`Clear screen` -|`help `|Y |`This help. Use ' help' for details of a particular command.` +|`help `|Y |`Use ' help' for details of a particular command.` |`hints `|Y |`Turn hints on / off` |`msleep `|Y |`Add a pause in milliseconds` -|`pref `|Y |`Edit preferences` |`rem `|Y |`Add a text line in log file` |`quit `|Y |`` |`exit `|Y |`Exit program` @@ -606,10 +605,80 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em help `|Y |`This help` -|`lf em 410x `|Y |`EM 4102 commands...` -|`lf em 4x05 `|Y |`EM 4205 / 4305 / 4369 / 4469 commands...` -|`lf em 4x50 `|Y |`EM 4350 / 4450 commands...` -|`lf em 4x70 `|Y |`EM 4070 / 4170 commands...` + + +### lf em 410x + + { EM 4102 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 410x help `|Y |`This help` +|`lf em 410x demod `|Y |`demodulate a EM410x tag from the GraphBuffer` +|`lf em 410x reader `|N |`attempt to read and extract tag data` +|`lf em 410x sim `|N |`simulate EM410x tag` +|`lf em 410x brute `|N |`reader bruteforce attack by simulating EM410x tags` +|`lf em 410x watch `|N |`watches for EM410x 125/134 kHz tags (option 'h' for 134)` +|`lf em 410x spoof `|N |`watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)` +|`lf em 410x clone `|N |`write EM410x UID to T55x7 or Q5/T5555 tag` + + +### lf em 4x05 + + { EM 4205 / 4305 / 4369 / 4469 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 4x05 help `|Y |`This help` +|`lf em 4x05 brute `|N |`Bruteforce password` +|`lf em 4x05 chk `|N |`Check passwords from dictionary` +|`lf em 4x05 demod `|Y |`demodulate a EM4x05/EM4x69 tag from the GraphBuffer` +|`lf em 4x05 dump `|N |`dump EM4x05/EM4x69 tag` +|`lf em 4x05 info `|N |`tag information EM4x05/EM4x69` +|`lf em 4x05 read `|N |`read word data from EM4x05/EM4x69` +|`lf em 4x05 sniff `|Y |`Attempt to recover em4x05 commands from sample buffer` +|`lf em 4x05 unlock `|N |`execute tear off against EM4x05/EM4x69` +|`lf em 4x05 wipe `|N |`wipe EM4x05/EM4x69 tag` +|`lf em 4x05 write `|N |`write word data to EM4x05/EM4x69` + + +### lf em 4x50 + + { EM 4350 / 4450 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 4x50 help `|Y |`This help` +|`lf em 4x50 brute `|N |`guess password of EM4x50` +|`lf em 4x50 chk `|N |`check passwords from dictionary` +|`lf em 4x50 dump `|N |`dump EM4x50 tag` +|`lf em 4x50 info `|N |`tag information EM4x50` +|`lf em 4x50 login `|N |`login into EM4x50` +|`lf em 4x50 rdbl `|N |`read word data from EM4x50` +|`lf em 4x50 wrbl `|N |`write word data to EM4x50` +|`lf em 4x50 writepwd `|N |`change password of EM4x50` +|`lf em 4x50 wipe `|N |`wipe EM4x50 tag` +|`lf em 4x50 reader `|N |`show standard read mode data of EM4x50` +|`lf em 4x50 restore `|N |`restore EM4x50 dump to tag` +|`lf em 4x50 sim `|N |`simulate EM4x50 tag` +|`lf em 4x50 eload `|N |`upload dump of EM4x50 to emulator memory` +|`lf em 4x50 esave `|N |`save emulator memory to file` +|`lf em 4x50 eview `|N |`view EM4x50 content in emulator memory` + + +### lf em 4x70 + + { EM 4070 / 4170 commands... } + +|command |offline |description +|------- |------- |----------- +|`lf em 4x70 help `|Y |`This help` +|`lf em 4x70 info `|N |`Tag information EM4x70` +|`lf em 4x70 write `|N |`Write EM4x70` +|`lf em 4x70 unlock `|N |`Unlock EM4x70 for writing` +|`lf em 4x70 auth `|N |`Authenticate EM4x70` +|`lf em 4x70 writepin `|N |`Write PIN` +|`lf em 4x70 writekey `|N |`Write Crypt Key` ### lf fdxb @@ -673,6 +742,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf hitag help `|Y |`This help` +|`lf hitag eload `|N |`Load Hitag dump file into emulator memory` |`lf hitag list `|N |`List Hitag trace history` |`lf hitag info `|N |`Tag information` |`lf hitag reader `|N |`Act like a Hitag Reader` @@ -905,7 +975,7 @@ Check column "offline" for their availability. |`lf t55xx info `|Y |`Show T55x7 configuration data (page 0/ blk 0)` |`lf t55xx p1detect `|N |`Try detecting if this is a t55xx tag by reading page 1` |`lf t55xx read `|N |`Read T55xx block data` -|`lf t55xx resetread `|N |`Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)` +|`lf t55xx resetread `|N |`Send Reset Cmd then lf read the stream to attempt to identify the start of it` |`lf t55xx restore `|N |`Restore T55xx card Page 0 / Page 1 blocks` |`lf t55xx trace `|Y |`Show T55x7 traceability data (page 1/ blk 0-1)` |`lf t55xx wakeup `|N |`Send AOR wakeup command` @@ -953,13 +1023,34 @@ Check column "offline" for their availability. |------- |------- |----------- |`mem help `|Y |`This help` |`mem baudrate `|N |`Set Flash memory Spi baudrate` -|`mem spiffs `|N |`High level SPI FileSystem Flash manipulation` -|`mem info `|N |`Flash memory information` -|`mem load `|N |`Load data into flash memory` |`mem dump `|N |`Dump data from flash memory` +|`mem info `|N |`Flash memory information` +|`mem load `|N |`Load data to flash memory` |`mem wipe `|N |`Wipe data from flash memory` +### mem spiffs + + { SPI File system } + +|command |offline |description +|------- |------- |----------- +|`mem spiffs help `|Y |`This help` +|`mem spiffs copy `|N |`Copy a file to another (destructively) in SPIFFS file system` +|`mem spiffs check `|N |`Check/try to defrag faulty/fragmented file system` +|`mem spiffs dump `|N |`Dump a file from SPIFFS file system` +|`mem spiffs info `|N |`Print file system info and usage statistics` +|`mem spiffs mount `|N |`Mount the SPIFFS file system if not already mounted` +|`mem spiffs remove `|N |`Remove a file from SPIFFS file system` +|`mem spiffs rename `|N |`Rename/move a file in SPIFFS file system` +|`mem spiffs test `|N |`Test SPIFFS Operations` +|`mem spiffs tree `|N |`Print the Flash memory file system tree` +|`mem spiffs unmount `|N |`Un-mount the SPIFFS file system` +|`mem spiffs upload `|N |`Upload file into SPIFFS file system` +|`mem spiffs view `|N |`View file on SPIFFS file system` +|`mem spiffs wipe `|N |`Wipe all files from SPIFFS file system * dangerous *` + + ### reveng { CRC calculations from RevEng software... } @@ -988,9 +1079,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- -|`script help `|Y |`Usage info` +|`script help `|Y |`This help` |`script list `|Y |`List available scripts` -|`script run `|Y |` -- execute a script` +|`script run `|Y |` - execute a script` ### trace @@ -1034,3 +1125,44 @@ Check column "offline" for their availability. |`wiegand decode `|Y |`Convert raw hex to decoded wiegand format (currently for HID Prox)` +### pref + + { Edit preferences... } + +|command |offline |description +|------- |------- |----------- +|`pref help `|Y |`This help` +|`pref show `|Y |`Show all preferences` + + +### pref get + + { Get a preference } + +|command |offline |description +|------- |------- |----------- +|`pref get barmode `|Y |`Get bar mode preference` +|`pref get clientdebug `|Y |`Get client debug level preference` +|`pref get color `|Y |`Get color support preference` +|`pref get savepaths `|Y |`Get file folder ` +|`pref get emoji `|Y |`Get emoji display preference` +|`pref get hints `|Y |`Get hint display preference` +|`pref get plotsliders `|Y |`Get plot slider display preference` + + +### pref set + + { Set a preference } + +|command |offline |description +|------- |------- |----------- +|`pref set help `|Y |`This help` +|`pref set barmode `|Y |`Set bar mode` +|`pref set clientdebug `|Y |`Set client debug level` +|`pref set color `|Y |`Set color support` +|`pref set emoji `|Y |`Set emoji display` +|`pref set hints `|Y |`Set hint display` +|`pref set savepaths `|Y |`... to be adjusted next ... ` +|`pref set plotsliders `|Y |`Set plot slider display` + + diff --git a/doc/ext_flash_notes.md b/doc/ext_flash_notes.md index 46059d9ca..76b4d9279 100644 --- a/doc/ext_flash_notes.md +++ b/doc/ext_flash_notes.md @@ -39,8 +39,8 @@ Page 3: * used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details * to dump it: `mem dump f page3_dump o 196608 l 65536` * to erase it: - * **Beware** it will erase your flash signature (see below) so better to back it up first as you won't be able to regenerate it by yourself! - * It's possible to erase completely page 3 by erase the entire flash memory with the voluntarily undocumented command `mem wipe i`. + * **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself! + * edit the source code to enable Page 3 as a valid input in the `mem wipe` command. * Updating keys dictionaries doesn't require to erase page 3. ## Page3 Layout @@ -64,7 +64,7 @@ Page3 is used as follows by the Proxmark3 RDV4 firmware: * length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure) * **RSA SIGNATURE**, see below for details - * offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F + * offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015) * length: 128 bytes * offset should have been 0x3FF80 but historically it's one byte off and therefore the last byte of the flash is unused @@ -73,23 +73,39 @@ Page3 is used as follows by the Proxmark3 RDV4 firmware: To ensure your Proxmark3 RDV4 is not a counterfeit product, its external flash contains a RSA signature of the flash unique ID. You can verify it with: `mem info` + +Here below is a sample output of a RDV4 device. ``` -[usb] pm3 --> mem info - -[=] --- Flash memory Information --------- - -[=] ------------------------------------------------------------- -[=] ID | xx xx xx xx xx xx xx xx -[=] SHA1 | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -[=] RSA SIGNATURE | -[00] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -[01] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -[02] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -[03] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx -[=] KEY length | 128 -[+] RSA key validation ok -[+] RSA Verification ok +[usb] pm3 --> mem info + +[=] --- Flash memory Information --------- +[=] ID................... 25AD99A782A867D5 +[=] SHA1................. 67C3B9BA2FA90AD4B283926B70017066C082C156 +[+] Signature............ ( ok ) + +[=] --- RDV4 RSA signature --------------- +[=] C7C7DF7FA3A2391A2B36E97D227C746ED8BB475E8766F54A13BAA9AAB29299BE +[=] 37546AACCC29157ABF8AFBF3A1CFB24275442D565F7E996C6B08090528ADE25E +[=] ED1498E3089C72C68348D83CBD13F1247327BDBC9D75B09ECE3E051E19FE19BB +[=] 98CB038757F2EDFD2DC5060D05C3296BC19A6F768290D555DFD50407E0E13A70 + +[=] --- RDV4 RSA Public key -------------- +[=] Len.................. 128 +[=] Exponent............. 010001 +[=] Public key modulus N +[=] E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF +[=] 4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F +[=] 9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33 +[=] DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5 + +[+] RSA public key validation.... ( ok ) +[+] RSA private key validation... ( ok ) +[+] RSA verification..... ( ok ) +[+] Genuine Proxmark3 RDV4 signature detected ``` -For a backup of the signature: `mem dump p f flash_signature_dump o 262015 l 128` +# backup first! +To make a backup of the signature to file: + +`mem dump p f flash_signature_dump o 262015 l 128` diff --git a/doc/jtag_notes.md b/doc/jtag_notes.md index 9029958e6..1e6369767 100644 --- a/doc/jtag_notes.md +++ b/doc/jtag_notes.md @@ -61,7 +61,7 @@ GND | 6 3.3 | 1 # Where to find more information? -There has been lots of articles and blogposts about recoving, debricking, JTAG your Proxmark3 and you find here below a sortiment of resources that will be of help. +There has been lots of articles and blogposts about recovering, debricking, JTAG your Proxmark3 and you find here below an assortiment of resources that will be of help. ## Third party notes on using a BusPirate diff --git a/doc/new_frame_format.md b/doc/new_frame_format.md index 6075fb2c9..102ef9c18 100644 --- a/doc/new_frame_format.md +++ b/doc/new_frame_format.md @@ -373,9 +373,9 @@ It was needed to tune pm3 RX usart `maxtry` : time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "lf read" 6.28s - time client/proxmark3 -p /dev/ttyACM0 -c "mem dump f foo_usb" + time client/proxmark3 -p /dev/ttyACM0 -c "mem dump -f foo_usb" 1.48s - time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "mem dump f foo_fpc" + time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "mem dump -f foo_fpc" 25.34s diff --git a/doc/path_notes.md b/doc/path_notes.md index 76c3f7a1a..491f01cd0 100644 --- a/doc/path_notes.md +++ b/doc/path_notes.md @@ -65,7 +65,7 @@ Dictionaries used by the client will be copied to ``` Here you find the default dictionaries used for commands like `hf mf chk`, `hf mf fchk`, `lf t55xx chk` -A dictionary file is a text based file with one key per line in hexdecimal form. +A dictionary file is a text based file with one key per line in hexadecimal form. The length of the key is decided by the Proxmark3 client for the different commands. All chars afterwards on line is ignored. if key isn't a hex number, the key is ignored. diff --git a/include/common.h b/include/common.h index 3c6245e52..f0ba424c8 100644 --- a/include/common.h +++ b/include/common.h @@ -56,6 +56,10 @@ struct version_information { #define DBG_EXTENDED 4 // errors + info + debug + breaking debug messages extern int DBGLEVEL; +// tear-off +extern uint16_t tearoff_delay_us; +extern bool tearoff_enabled; + // reader voltage field detector #define MF_MINFIELDV 4000 diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 6b7f5df07..a2193a85b 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -384,6 +384,15 @@ typedef struct { bool off; } PACKED tearoff_params_t; +// when writing to SPIFFS +typedef struct { + bool append : 1; + uint16_t bytes_in_packet : 15; + uint8_t fnlen; + uint8_t fn[32]; + uint8_t data[]; +} PACKED flashmem_write_t; + // For the bootloader #define CMD_DEVICE_INFO 0x0000 //#define CMD_SETUP_WRITE 0x0001 @@ -565,6 +574,8 @@ typedef struct { #define CMD_LF_HITAGS_READ 0x0373 #define CMD_LF_HITAGS_WRITE 0x0375 +#define CMD_LF_HITAG_ELOAD 0x0376 + #define CMD_HF_ISO14443A_ANTIFUZZ 0x0380 #define CMD_HF_ISO14443B_SIMULATE 0x0381 #define CMD_HF_ISO14443B_SNIFF 0x0382 diff --git a/tools/mf_nonce_brute/Makefile b/tools/mf_nonce_brute/Makefile index cf565e72c..c68b828de 100644 --- a/tools/mf_nonce_brute/Makefile +++ b/tools/mf_nonce_brute/Makefile @@ -1,6 +1,6 @@ -MYSRCPATHS = ../../common ../../common/crapto1 +MYSRCPATHS = ../../common ../../common/crapto1 MYSRCS = crypto1.c crapto1.c bucketsort.c iso14443crc.c sleep.c -MYINCLUDES = -I../../include -I../../common +MYINCLUDES = -I../../include -I../../common MYCFLAGS = MYDEFS = MYLDLIBS = @@ -8,7 +8,7 @@ ifneq ($(SKIPPTHREAD),1) MYLDLIBS += -lpthread endif -BINS = mf_nonce_brute +BINS = mf_nonce_brute mf_trace_brute INSTALLTOOLS = $(BINS) include ../../Makefile.host @@ -21,3 +21,5 @@ ifneq (,$(findstring MINGW,$(platform))) endif mf_nonce_brute : $(OBJDIR)/mf_nonce_brute.o $(MYOBJS) + +mf_trace_brute : $(OBJDIR)/mf_trace_brute.o $(MYOBJS) \ No newline at end of file diff --git a/tools/mf_nonce_brute/README.md b/tools/mf_nonce_brute/README.md index c2e5f3e6f..6119412be 100644 --- a/tools/mf_nonce_brute/README.md +++ b/tools/mf_nonce_brute/README.md @@ -85,7 +85,7 @@ Example with parity (from this trace http://www.proxmark.org/forum/viewtopic.php ``` => ``` -./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 3e709c8a +./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 8e0e5db9 | | | | | | | | | +UID +nt_enc | +nr_enc +ar_enc | +at_enc | +encrypted next cmd +nt_par_err +at_par_err +at_par_err diff --git a/tools/mf_nonce_brute/mf_nonce_brute.c b/tools/mf_nonce_brute/mf_nonce_brute.c index 9891b99cc..631976c15 100644 --- a/tools/mf_nonce_brute/mf_nonce_brute.c +++ b/tools/mf_nonce_brute/mf_nonce_brute.c @@ -14,10 +14,17 @@ #include #include #include +#include #include "crapto1/crapto1.h" #include "protocol.h" #include "iso14443crc.h" +#define AEND "\x1b[0m" +#define _RED_(s) "\x1b[31m" s AEND +#define _GREEN_(s) "\x1b[32m" s AEND +#define _YELLOW_(s) "\x1b[33m" s AEND +#define _CYAN_(s) "\x1b[36m" s AEND + #define odd_parity(i) (( (i) ^ (i)>>1 ^ (i)>>2 ^ (i)>>3 ^ (i)>>4 ^ (i)>>5 ^ (i)>>6 ^ (i)>>7 ^ 1) & 0x01) // a global mutex to prevent interlaced printing from different threads @@ -42,24 +49,149 @@ typedef struct thread_args { bool ev1; } targs; +#define ENC_LEN (200) +typedef struct thread_key_args { + int thread; + int idx; + uint32_t uid; + uint32_t part_key; + uint32_t nt_enc; + uint32_t nr_enc; + uint16_t enc_len; + uint8_t enc[ENC_LEN]; // next encrypted command + a full read/write +} targs_key; + //------------------------------------------------------------------ -uint8_t cmds[] = { - ISO14443A_CMD_READBLOCK, - ISO14443A_CMD_WRITEBLOCK, - MIFARE_AUTH_KEYA, - MIFARE_AUTH_KEYB, - MIFARE_CMD_INC, - MIFARE_CMD_DEC, - MIFARE_CMD_RESTORE, - MIFARE_CMD_TRANSFER + +uint8_t cmds[8][2] = { + {ISO14443A_CMD_READBLOCK, 18}, + {ISO14443A_CMD_WRITEBLOCK, 18}, + {MIFARE_AUTH_KEYA, 0}, + {MIFARE_AUTH_KEYB, 0}, + {MIFARE_CMD_INC, 6}, + {MIFARE_CMD_DEC, 6}, + {MIFARE_CMD_RESTORE, 6}, + {MIFARE_CMD_TRANSFER, 0} }; int global_counter = 0; -int global_fin_flag = 0; int global_found = 0; int global_found_candidate = 0; +uint64_t global_candiate_key = 0; size_t thread_count = 2; +static int param_getptr(const char *line, int *bg, int *en, int paramnum) { + int i; + int len = strlen(line); + + *bg = 0; + *en = 0; + + // skip spaces + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + if (*bg >= len) { + return 1; + } + + for (i = 0; i < paramnum; i++) { + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + + if (line[*bg] == '\0') return 1; + } + + *en = *bg; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + + (*en)--; + + return 0; +} + +static int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen) { + int bg, en; + uint32_t temp; + char buf[5] = {0}; + + if (param_getptr(line, &bg, &en, paramnum)) return 1; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; + continue; + } + + if (isxdigit(line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + +static void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, + const size_t min_str_len, const size_t spaces_between, bool uppercase) { + + if (buf == NULL) return; + + char *tmp = (char *)buf; + size_t i; + memset(tmp, 0x00, hex_max_len); + + size_t max_len = (hex_len > hex_max_len) ? hex_max_len : hex_len; + + for (i = 0; i < max_len; ++i, tmp += 2 + spaces_between) { + sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + + for (size_t j = 0; j < spaces_between; j++) + sprintf(tmp + 2 + j, " "); + } + + i *= (2 + spaces_between); + + size_t mlen = min_str_len > i ? min_str_len : 0; + if (mlen > hex_max_len) + mlen = hex_max_len; + + for (; i < mlen; i++, tmp += 1) + sprintf(tmp, " "); + + // remove last space + *tmp = '\0'; + return; +} + +static char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + static char buf[100] = {0}; + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true); + return buf; +} + static uint16_t parity_from_err(uint32_t data, uint16_t par_err) { uint16_t par = 0; @@ -196,13 +328,33 @@ static bool candidate_nonce(uint32_t xored, uint32_t nt, bool ev1) { static bool checkValidCmd(uint32_t decrypted) { uint8_t cmd = (decrypted >> 24) & 0xFF; - for (int i = 0; i < sizeof(cmds); ++i) { - if (cmd == cmds[i]) + for (int i = 0; i < 8; ++i) { + if (cmd == cmds[i][0]) return true; } return false; } +static bool checkValidCmdByte(uint8_t *cmd, uint16_t n) { + + bool ok = false; + for (int i = 0; i < 8; ++i) { + if (cmd[0] == cmds[i][0]) { + + if (n >= 4) + ok = CheckCrc14443(CRC_14443_A, cmd, 4); + + if (cmds[i][1] > 0 && n >= cmds[i][1]) + ok = CheckCrc14443(CRC_14443_A, cmd + 4, cmds[i][1]); + + if (ok) { + return true; + } + } + } + return false; +} + static bool checkCRC(uint32_t decrypted) { uint8_t data[] = { (decrypted >> 24) & 0xFF, @@ -227,14 +379,12 @@ static void *brute_thread(void *arguments) { uint32_t p64 = 0; uint32_t count; - int found = 0; // TC == 4 ( // threads calls 0 ev1 == false // threads calls 0,1,2 ev1 == true for (count = args->idx; count < 0xFFFF; count += thread_count - 1) { - found = global_found; - if (found) { + if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { break; } @@ -265,8 +415,8 @@ static void *brute_thread(void *arguments) { #endif if (cmd_enc) { uint32_t decrypted = ks4 ^ cmd_enc; - printf("CMD enc(%08x)\n", cmd_enc); - printf(" dec(%08x)\t", decrypted); + printf("CMD enc( %08x )\n", cmd_enc); + printf(" dec( %08x ) ", decrypted); // check if cmd exists uint8_t isOK = checkValidCmd(decrypted); @@ -275,12 +425,12 @@ static void *brute_thread(void *arguments) { // Add a crc-check. isOK = checkCRC(decrypted); if (isOK == false) { - printf("<-- not a valid cmd\n"); + printf(_RED_("<-- not a valid cmd\n")); pthread_mutex_unlock(&print_lock); free(revstate); continue; } else { - printf("<-- Valid cmd\n"); + printf("<-- valid cmd\n"); } } @@ -293,32 +443,97 @@ static void *brute_thread(void *arguments) { free(revstate); if (args->ev1) { - printf("\nKey candidate: [%012" PRIx64 "]\n\n", key); + // if it was EV1, we know for sure xxxAAAAAAAA recovery + printf("\nKey candidate [ " _YELLOW_("....%08" PRIx64 )" ]\n\n", key & 0xFFFFFFFF); __sync_fetch_and_add(&global_found_candidate, 1); } else { - printf("\nValid Key found: [%012" PRIx64 "]\n\n", key); + printf("\nKey candidate [ " _GREEN_("....%08" PRIx64) " ]\n\n", key & 0xFFFFFFFF); __sync_fetch_and_add(&global_found, 1); } + __sync_fetch_and_add(&global_candiate_key, key); //release lock pthread_mutex_unlock(&print_lock); + break; } } free(args); return NULL; } +static void *brute_key_thread(void *arguments) { + + struct thread_key_args *args = (struct thread_key_args *) arguments; + uint64_t key; + uint8_t local_enc[args->enc_len]; + memcpy(local_enc, args->enc, args->enc_len); + + for (uint64_t count = args->idx; count < 0xFFFF; count += thread_count) { + + if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) { + break; + } + + key = (count << 32 | args->part_key); + + // Init cipher with key + struct Crypto1State *pcs = crypto1_create(key); + + // NESTED decrypt nt with help of new key + crypto1_word(pcs, args->nt_enc ^ args->uid, 1); + crypto1_word(pcs, args->nr_enc, 1); + crypto1_word(pcs, 0, 0); + crypto1_word(pcs, 0, 0); + + // decrypt 22 bytes + uint8_t dec[args->enc_len]; + for (int i = 0; i < args->enc_len; i++) + dec[i] = crypto1_byte(pcs, 0x00, 0) ^ local_enc[i]; + + crypto1_deinit(pcs); + + // check if cmd exists + uint8_t isOK = checkValidCmdByte(dec, args->enc_len); + if (isOK == false) { + continue; + } + + // lock this section to avoid interlacing prints from different threats + pthread_mutex_lock(&print_lock); + printf("\nenc: %s\n", sprint_hex_inrow_ex(local_enc, args->enc_len, 0)); + printf("dec: %s\n", sprint_hex_inrow_ex(dec, args->enc_len, 0)); + printf("\nValid Key found [ " _GREEN_("%012" PRIx64) " ]\n\n", key); + pthread_mutex_unlock(&print_lock); + __sync_fetch_and_add(&global_found, 1); + break; + } + free(args); + return NULL; +} + static int usage(void) { - printf(" syntax: mf_nonce_brute []\n\n"); - printf(" example: nt in trace = 8c! 42 e6! 4e!\n"); - printf(" nt = 8c42e64e\n"); - printf(" nt_par_err = 1011\n\n"); - printf("\n expected outcome:\n"); - printf(" KEY 0xFFFFFFFFFFFF == fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n"); + printf("\n"); + printf("syntax: mf_nonce_brute []\n\n"); + printf("how to convert trace data to needed input:\n"); + printf(" nt in trace = 8c! 42 e6! 4e!\n"); + printf(" nt = 8c42e64e\n"); + printf(" nt_par_err = 1011\n\n"); + printf("samples:\n"); + printf("\n"); + printf(" ./mf_nonce_brute fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n"); + printf("\n"); + printf("**** Possible key candidate ****\n"); + printf("Key candidate: [ffffffffffff]\n"); + printf("\n"); + printf(" ./mf_nonce_brute 96519578 d7e3c6ac 0011 cd311951 9da49e49 0010 2bb22e00 0100 a4f7f398ebdb4e484d1cb2b174b939d18b469f3fa5d9caab\n"); + printf("\n"); + printf("enc: A4F7F398EBDB4E484D1CB2B174B939D18B469F3FA5D9CAABBFA018EC7E0CC5721DE2E590F64BD0A5B4EFCE71\n"); + printf("dec: 30084A24302F8102F44CA5020500A60881010104763930084A24302F8102F44CA5020500A608810101047639\n"); + printf("Valid Key found: [3b7e4fd575ad]\n\n"); return 1; } int main(int argc, char *argv[]) { - printf("Mifare classic nested auth key recovery. Phase 1.\n"); + printf("\nMifare classic nested auth key recovery\n\n"); if (argc < 9) return usage(); @@ -331,21 +546,27 @@ int main(int argc, char *argv[]) { sscanf(argv[7], "%x", &at_enc); sscanf(argv[8], "%x", &at_par_err); - if (argc > 9) - sscanf(argv[9], "%x", &cmd_enc); + int enc_len = 0; + uint8_t enc[ENC_LEN] = {0}; // next encrypted command + a full read/write + if (argc > 9) { +// sscanf(argv[9], "%x", &cmd_enc); + param_gethex_to_eol(argv[9], 0, enc, sizeof(enc), &enc_len); + cmd_enc = (enc[0] << 24 | enc[1] << 16 | enc[2] << 8 | enc[3]); + } - printf("-------------------------------------------------\n"); - printf("uid:\t\t%08x\n", uid); - printf("nt encrypted:\t%08x\n", nt_enc); - printf("nt parity err:\t%04x\n", nt_par_err); - printf("nr encrypted:\t%08x\n", nr_enc); - printf("ar encrypted:\t%08x\n", ar_enc); - printf("ar parity err:\t%04x\n", ar_par_err); - printf("at encrypted:\t%08x\n", at_enc); - printf("at parity err:\t%04x\n", at_par_err); + printf("----------- " _CYAN_("Phase 1") " ------------------------\n"); + printf("uid.................. %08x\n", uid); + printf("nt encrypted......... %08x\n", nt_enc); + printf("nt parity err........ %04x\n", nt_par_err); + printf("nr encrypted......... %08x\n", nr_enc); + printf("ar encrypted......... %08x\n", ar_enc); + printf("ar parity err........ %04x\n", ar_par_err); + printf("at encrypted......... %08x\n", at_enc); + printf("at parity err........ %04x\n", at_par_err); - if (argc > 9) - printf("next cmd enc:\t%08x\n\n", cmd_enc); + if (argc > 9) { + printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc, enc_len ,0)); + } clock_t t1 = clock(); uint16_t nt_par = parity_from_err(nt_enc, nt_par_err); @@ -361,7 +582,7 @@ int main(int argc, char *argv[]) { thread_count = 2; #endif /* _WIN32 */ - printf("\nBruteforce using %zu threads to find encrypted tagnonce last bytes\n", thread_count); + printf("\nBruteforce using " _YELLOW_("%zu") " threads to find encrypted tagnonce last bytes\n", thread_count); pthread_t threads[thread_count]; @@ -386,6 +607,51 @@ int main(int argc, char *argv[]) { pthread_create(&threads[i + 1], NULL, brute_thread, (void *)b); } + // wait for threads to terminate: + for (int i = 0; i < thread_count; ++i) + pthread_join(threads[i], NULL); + + t1 = clock() - t1; + printf("execution time %.2f sec\n", (float)t1 / 1000000.0); + + + if (!global_found && !global_found_candidate) { + printf("\nFailed to find a key\n\n"); + goto out; + } + + if (enc_len < 4) { + printf("Too few next cmd bytes, skipping phase 2\n"); + goto out; + } + + // reset thread signals + global_found = 0; + global_found_candidate = 0; + + printf("\n----------- " _CYAN_("Phase 2") " ------------------------\n"); + printf("uid.......... %08x\n", uid); + printf("partial key.. %08x\n", (uint32_t)(global_candiate_key & 0xFFFFFFFF)); + printf("nt enc....... %08x\n", nt_enc); + printf("nr enc....... %08x\n", nr_enc); + printf("next encrypted cmd: %s\n", sprint_hex_inrow_ex(enc, enc_len ,0)); + printf("\nStart bruteforce to find upper 16 bits of key\n"); + fflush(stdout); + + // threads + for (int i = 0; i < thread_count; ++i) { + struct thread_key_args *b = malloc(sizeof(struct thread_key_args)); + b->thread = i; + b->idx = i; + b->uid = uid; + b->part_key = (uint32_t)(global_candiate_key & 0xFFFFFFFF); + b->nt_enc = nt_enc; + b->nr_enc = nr_enc; + b->enc_len = enc_len; + memcpy(b->enc, enc, enc_len); + pthread_create(&threads[i], NULL, brute_key_thread, (void *)b); + } + // wait for threads to terminate: for (int i = 0; i < thread_count; ++i) pthread_join(threads[i], NULL); @@ -394,10 +660,7 @@ int main(int argc, char *argv[]) { printf("\nFailed to find a key\n\n"); } - t1 = clock() - t1; - if (t1 > 0) - printf("Execution time: %.0f ticks\n", (float)t1); - +out: // clean up mutex pthread_mutex_destroy(&print_lock); return 0; diff --git a/tools/mf_nonce_brute/mf_trace_brute.c b/tools/mf_nonce_brute/mf_trace_brute.c new file mode 100644 index 000000000..c3973f5dc --- /dev/null +++ b/tools/mf_nonce_brute/mf_trace_brute.c @@ -0,0 +1,315 @@ +// +// bruteforce the upper 16bits of a partial key recovered from mf_nonce_brute. +// J-run's original idea was a two part recovery vector with first a offline trace and then online for 2 bytes. +// +// This idea is two use only offline, to recover a nested authentication key. +// Assumption, we get a read/write command after a nested auth, we need 22 bytes of data. +// Iceman, 2021, +// + +#define __STDC_FORMAT_MACROS + +#if !defined(_WIN64) +#if defined(_WIN32) || defined(__WIN32__) +# define _USE_32BIT_TIME_T 1 +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "ctype.h" +#include +#include "crapto1/crapto1.h" +#include "protocol.h" +#include "iso14443crc.h" + +// a global mutex to prevent interlaced printing from different threads +pthread_mutex_t print_lock; + +#define ENC_LEN (4 + 16 + 2) +//--------------------- define options here + +typedef struct thread_args { + int thread; + int idx; + uint32_t uid; + uint32_t part_key; + uint32_t nt_enc; + uint32_t nr_enc; + uint8_t enc[ENC_LEN]; // next encrypted command + a full read/write +} targs; + +//------------------------------------------------------------------ +uint8_t cmds[] = { + ISO14443A_CMD_READBLOCK, + ISO14443A_CMD_WRITEBLOCK, + MIFARE_AUTH_KEYA, + MIFARE_AUTH_KEYB, + MIFARE_CMD_INC, + MIFARE_CMD_DEC, + MIFARE_CMD_RESTORE, + MIFARE_CMD_TRANSFER +}; + +int global_counter = 0; +int global_fin_flag = 0; +int global_found = 0; +int global_found_candidate = 0; +size_t thread_count = 2; + +static int param_getptr(const char *line, int *bg, int *en, int paramnum) { + int i; + int len = strlen(line); + + *bg = 0; + *en = 0; + + // skip spaces + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + if (*bg >= len) { + return 1; + } + + for (i = 0; i < paramnum; i++) { + while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++; + while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++; + + if (line[*bg] == '\0') return 1; + } + + *en = *bg; + while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++; + + (*en)--; + + return 0; +} + +static int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen) { + int bg, en; + uint32_t temp; + char buf[5] = {0}; + + if (param_getptr(line, &bg, &en, paramnum)) return 1; + + *datalen = 0; + + int indx = bg; + while (line[indx]) { + if (line[indx] == '\t' || line[indx] == ' ') { + indx++; + continue; + } + + if (isxdigit(line[indx])) { + buf[strlen(buf) + 1] = 0x00; + buf[strlen(buf)] = line[indx]; + } else { + // if we have symbols other than spaces and hex + return 1; + } + + if (*datalen >= maxdatalen) { + // if we dont have space in buffer and have symbols to translate + return 2; + } + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[*datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + (*datalen)++; + } + + indx++; + } + + if (strlen(buf) > 0) + //error when not completed hex bytes + return 3; + + return 0; +} + +static void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len, + const size_t min_str_len, const size_t spaces_between, bool uppercase) { + + if (buf == NULL) return; + + char *tmp = (char *)buf; + size_t i; + memset(tmp, 0x00, hex_max_len); + + size_t max_len = (hex_len > hex_max_len) ? hex_max_len : hex_len; + + for (i = 0; i < max_len; ++i, tmp += 2 + spaces_between) { + sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]); + + for (size_t j = 0; j < spaces_between; j++) + sprintf(tmp + 2 + j, " "); + } + + i *= (2 + spaces_between); + + size_t mlen = min_str_len > i ? min_str_len : 0; + if (mlen > hex_max_len) + mlen = hex_max_len; + + for (; i < mlen; i++, tmp += 1) + sprintf(tmp, " "); + + // remove last space + *tmp = '\0'; + return; +} + +static char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) { + static char buf[100] = {0}; + hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true); + return buf; +} + +static void *brute_thread(void *arguments) { + + //int shift = (int)arg; + struct thread_args *args = (struct thread_args *) arguments; + + uint64_t key; // recovered key candidate + int found = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs = &mpcs; + + uint8_t local_enc[ENC_LEN] = {0}; + memcpy(local_enc, args->enc, sizeof(local_enc)); + + for (uint64_t count = args->idx; count < 0xFFFF; count += thread_count) { + + found = global_found; + if (found) { + break; + } + + key = (count << 32 | args->part_key); + + // Init cipher with key + pcs = crypto1_create(key); + + // NESTED decrypt nt with help of new key +// if (args->use_nested) +// crypto1_word(pcs, args->nt_enc ^ args->uid, 1) ^ args->nt_enc; +// else + crypto1_word(pcs, args->nt_enc ^ args->uid, 1); + + crypto1_word(pcs, args->nr_enc, 1); + crypto1_word(pcs, 0, 0); + crypto1_word(pcs, 0, 0); + + // decrypt 22 bytes + uint8_t dec[ENC_LEN] = {0}; + for (int i = 0; i < ENC_LEN; i++) + dec[i] = crypto1_byte(pcs, 0x00, 0) ^ local_enc[i]; + + crypto1_deinit(pcs); + + if (CheckCrc14443(CRC_14443_A, dec , 4)) { + + // check crc-16 in the end + + if (CheckCrc14443(CRC_14443_A, dec + 4, 18)) { + + // lock this section to avoid interlacing prints from different threats + pthread_mutex_lock(&print_lock); + printf("\nValid Key found: [%012" PRIx64 "]\n", key); + + printf("enc: %s\n", sprint_hex_inrow_ex(local_enc, ENC_LEN, 0)); + printf(" xx crcA crcA\n"); + printf("dec: %s\n", sprint_hex_inrow_ex(dec, ENC_LEN, 0)); + pthread_mutex_unlock(&print_lock); + + __sync_fetch_and_add(&global_found, 1); + } + } + } + free(args); + return NULL; +} + +static int usage(void) { + printf(" syntax: mf_trace_brute []\n\n"); + return 1; +} + +int main(int argc, char *argv[]) { + printf("Mifare classic nested auth key recovery. Phase 2.\n"); + if (argc < 3) return usage(); + + uint32_t uid = 0; // serial number + uint32_t part_key = 0; // last 4 keys of key + uint32_t nt_enc = 0; // noncce tag + uint32_t nr_enc = 0; // nonce reader encrypted + + sscanf(argv[1], "%x", &uid); + sscanf(argv[2], "%x", &part_key); + sscanf(argv[3], "%x", &nt_enc); + sscanf(argv[4], "%x", &nr_enc); + + int enc_len = 0; + uint8_t enc[ENC_LEN] = {0}; // next encrypted command + a full read/write + param_gethex_to_eol(argv[5], 0, enc, sizeof(enc), &enc_len); + + printf("-------------------------------------------------\n"); + printf("uid.......... %08x\n", uid); + printf("partial key.. %08x\n", part_key); + printf("nt enc....... %08x\n", nt_enc); + printf("nr enc....... %08x\n", nr_enc); + printf("next encrypted cmd: %s\n", sprint_hex_inrow_ex(enc, ENC_LEN ,0)); + + clock_t t1 = clock(); + +#if !defined(_WIN32) || !defined(__WIN32__) + thread_count = sysconf(_SC_NPROCESSORS_CONF); + if (thread_count < 2) + thread_count = 2; +#endif /* _WIN32 */ + + printf("\nBruteforce using %zu threads to find upper 16bits of key\n", thread_count); + + pthread_t threads[thread_count]; + + // create a mutex to avoid interlacing print commands from our different threads + pthread_mutex_init(&print_lock, NULL); + + // threads + for (int i = 0; i < thread_count; ++i) { + struct thread_args *a = malloc(sizeof(struct thread_args)); + a->thread = i; + a->idx = i; + a->uid = uid; + a->part_key = part_key; + a->nt_enc = nt_enc; + a->nr_enc = nr_enc; + memcpy(a->enc, enc, sizeof(a->enc)); + pthread_create(&threads[i], NULL, brute_thread, (void *)a); + } + + // wait for threads to terminate: + for (int i = 0; i < thread_count; ++i) + pthread_join(threads[i], NULL); + + if (!global_found && !global_found_candidate) { + printf("\nFailed to find a key\n\n"); + } + + t1 = clock() - t1; + if (t1 > 0) + printf("Execution time: %.0f ticks\n", (float)t1); + + // clean up mutex + pthread_mutex_destroy(&print_lock); + return 0; +} diff --git a/tools/recover_pk.py b/tools/recover_pk.py index 0f01337a7..b242ff4cc 100755 --- a/tools/recover_pk.py +++ b/tools/recover_pk.py @@ -102,7 +102,8 @@ def selftests(): 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, {'name': "DESFire EV2", 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", - "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"], + "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33", + "040D259A965B80","B158073A7100C88C3726F4299FA58311FC3CB18744686DE3F234928AD74578F5CAD7FCEC1DCB962ECC7CC000B8557B37F45B76DC6573A58F"], 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, {'name': "DESFire EV3", 'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6", @@ -146,6 +147,7 @@ def selftests(): for c in curvenames: for h in [None, "md5", "sha1", "sha256", "sha512"]: recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h) + if (len(recovered) == 1): pk = recovered.pop() pk = binascii.hexlify(pk).decode('utf8')