Merge branch 'master' of https://github.com/RfidResearchGroup/proxmark3 into function/desfire-standalone

This commit is contained in:
Anže Jenšterle 2021-03-13 02:16:26 +01:00
commit 180577695e
67 changed files with 2957 additions and 1897 deletions

View file

@ -251,7 +251,7 @@ print-%: ; @echo $* = $($*)
cliparser: cliparser:
# Get list of all commands # 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 # Get list of cliparserized commands
grep -r CLIParserInit ./client/src/ | cut -f 2 -d "\"" | awk '{$$1=$$1};1' > cliparser_done.tmp grep -r CLIParserInit ./client/src/ | cut -f 2 -d "\"" | awk '{$$1=$$1};1' > cliparser_done.tmp
# Determine commands that still need cliparser conversion # Determine commands that still need cliparser conversion

View file

@ -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)| |[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) | |[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)| |[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) || |[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) ||
|[Donations](#Donations)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.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)| ||[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)| ||**[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 ## 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. `make install` is now available and if you want to know more.
- [Notes for maintainers](/doc/md/Development/Maintainers.md) - [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 ## Proxmark3 GUI
The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer. The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer.

View file

@ -71,13 +71,13 @@
static void DownloadTraceInstructions(void) { static void DownloadTraceInstructions(void) {
Dbprintf(""); Dbprintf("");
Dbprintf("To get the trace from flash and display it:"); Dbprintf("To get the trace from flash and display it:");
Dbprintf("1. mem spiffs dump o "HF_14ASNIFF_LOGFILE" f trace.trc"); Dbprintf("1. mem spiffs dump -s "HF_14ASNIFF_LOGFILE" -d trace.trc");
Dbprintf("2. trace load trace.trc"); Dbprintf("2. trace load -f trace.trc");
Dbprintf("3. trace list 14a 1"); Dbprintf("3. trace list -t 14a -1");
} }
void ModInfo(void) { void ModInfo(void) {
DbpString("hf_14asniff: standalone 'hf 14a sniff', storing in flashmem"); DbpString(" ISO14443a sniff, storing in flashmem");
DownloadTraceInstructions(); DownloadTraceInstructions();
} }

View file

@ -130,27 +130,27 @@ static void download_instructions(uint8_t t) {
switch (t) { switch (t) {
case ICE_STATE_FULLSIM: { case ICE_STATE_FULLSIM: {
DbpString("The emulator memory was saved to SPIFFS"); 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)); DbpString("2. " _YELLOW_("hf iclass view -f " HF_ICLASS_FULLSIM_MOD_BIN));
break; break;
} }
case ICE_STATE_ATTACK: { case ICE_STATE_ATTACK: {
DbpString("The collected data was saved to SPIFFS. The file names below may differ"); DbpString("The collected data was saved to SPIFFS. The file names below may differ");
DbpString("1. " _YELLOW_("mem spiffs tree")); 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)); DbpString("3. " _YELLOW_("hf iclass loclass -f " HF_ICLASS_ATTACK_BIN));
break; break;
} }
case ICE_STATE_READER: { case ICE_STATE_READER: {
DbpString("The found tags was saved to SPIFFS"); DbpString("The found tags was saved to SPIFFS");
DbpString("1. " _YELLOW_("mem spiffs tree")); DbpString("1. " _YELLOW_("mem spiffs tree"));
DbpString("2. " _YELLOW_("mem spiffs dump h")); DbpString("2. " _YELLOW_("mem spiffs dump -h"));
break; break;
} }
case ICE_STATE_DUMP_SIM: { case ICE_STATE_DUMP_SIM: {
DbpString("The found tag will be dumped to " HF_ICALSSS_READSIM_TEMP_BIN); DbpString("The found tag will be dumped to " HF_ICALSSS_READSIM_TEMP_BIN);
DbpString("1. " _YELLOW_("mem spiffs tree")); DbpString("1. " _YELLOW_("mem spiffs tree"));
DbpString("2. " _YELLOW_("mem spiffs dump h")); DbpString("2. " _YELLOW_("mem spiffs dump -h"));
break; break;
} }
} }

View file

@ -39,7 +39,7 @@
* *
* To delete a dump file from flash: * 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("[=] " _YELLOW_("-") " mem spiffs tree");
Dbprintf(""); Dbprintf("");
Dbprintf("[=] To save a dump file from flash to client:"); 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 #endif

View file

@ -36,7 +36,7 @@
* *
* To retrieve log file from flash: * 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. * Copies log file from flash to your client.
* *
* 2. exit the Proxmark3 client * 2. exit the Proxmark3 client
@ -48,7 +48,7 @@
* *
* To delete the log file from flash: * 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" #define LF_HIDCOLLECT_LOGFILE "lf_hidcollect.log"
@ -57,7 +57,7 @@
static void DownloadLogInstructions(void) { static void DownloadLogInstructions(void) {
Dbprintf(""); Dbprintf("");
Dbprintf("[=] To get the logfile from flash and display it:"); 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_("2.") " exit proxmark3 client");
Dbprintf("[=] " _YELLOW_("3.") " cat "LF_HIDCOLLECT_LOGFILE); Dbprintf("[=] " _YELLOW_("3.") " cat "LF_HIDCOLLECT_LOGFILE);
} }

View file

@ -42,25 +42,25 @@
* - LED D: unmounting/sync'ing flash (normally < 100ms) * - LED D: unmounting/sync'ing flash (normally < 100ms)
* *
* To upload input file (eml format) to flash: * To upload input file (eml format) to flash:
* - mem spiffs load f <filename> o lf_em4x50_simulate.eml * - mem spiffs upload -s <filename> -d lf_em4x50_simulate.eml
* *
* To retrieve password file from flash: * To retrieve password file from flash:
* - mem spiffs dump o lf_em4x50_passwords.log f <filename> * - mem spiffs dump -s lf_em4x50_passwords.log
* *
* To retrieve log file from flash: * To retrieve log file from flash:
* - mem spiffs dump o lf_em4x50_collect.log f <filename> * - mem spiffs dump -s lf_em4x50_collect.log
* *
* This module emits debug strings during normal operation -- so try it out in * 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. * the lab connected to PM3 client before taking it into the field.
* *
* To delete the input file from flash: * 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: * 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: * 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 #define STATE_SIM 0
@ -77,14 +77,14 @@ static void LoadDataInstructions(const char *inputfile) {
Dbprintf("To load datafile to flash and display it:"); Dbprintf("To load datafile to flash and display it:");
Dbprintf("1. edit input file %s", inputfile); Dbprintf("1. edit input file %s", inputfile);
Dbprintf("2. start proxmark3 client"); Dbprintf("2. start proxmark3 client");
Dbprintf("3. mem spiffs load f <filename> o %s", inputfile); Dbprintf("3. mem spiffs upload -s <filename> -d %s", inputfile);
Dbprintf("4. start standalone mode"); Dbprintf("4. start standalone mode");
} }
static void DownloadLogInstructions(const char *logfile) { static void DownloadLogInstructions(const char *logfile) {
Dbprintf(""); Dbprintf("");
Dbprintf("To get the logfile from flash and display it:"); Dbprintf("To get the logfile from flash and display it:");
Dbprintf("1. mem spiffs dump o %s f <filename>", logfile); Dbprintf("1. mem spiffs dump -s %s", logfile);
Dbprintf("2. exit proxmark3 client"); Dbprintf("2. exit proxmark3 client");
Dbprintf("3. cat <filename>"); Dbprintf("3. cat <filename>");
} }

View file

@ -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_SMARTCARD :=
STANDALONE_MODES_REQ_FLASH := STANDALONE_MODES_REQ_FLASH :=
STANDALONE_MODES_REQ_BT :=
``` ```
## Update MAKEFILE.INC ## Update MAKEFILE.INC

View file

@ -73,8 +73,8 @@ extern uint32_t _stack_start, _stack_end;
struct common_area common_area __attribute__((section(".commonarea"))); struct common_area common_area __attribute__((section(".commonarea")));
static int button_status = BUTTON_NO_CLICK; static int button_status = BUTTON_NO_CLICK;
static bool allow_send_wtx = false; static bool allow_send_wtx = false;
static uint16_t tearoff_delay_us = 0; uint16_t tearoff_delay_us = 0;
static bool tearoff_enabled = false; bool tearoff_enabled = false;
int tearoff_hook(void) { int tearoff_hook(void) {
if (tearoff_enabled) { 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); 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 // 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(); LED_B_OFF();
break; break;
} }
@ -2087,78 +2087,94 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t filename[32]; uint8_t filename[32];
uint8_t *pfilename = packet->data.asBytes; uint8_t *pfilename = packet->data.asBytes;
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); 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(); int changed = rdv40_spiffs_lazy_mount();
uint32_t size = size_in_spiffs((char *)filename); uint32_t size = size_in_spiffs((char *)filename);
if (changed) rdv40_spiffs_lazy_unmount(); if (changed) {
reply_mix(CMD_ACK, size, 0, 0, 0, 0); rdv40_spiffs_lazy_unmount();
}
reply_ng(CMD_SPIFFS_STAT, PM3_SUCCESS, (uint8_t *)&size, sizeof(uint32_t));
LED_B_OFF(); LED_B_OFF();
break; break;
} }
case CMD_SPIFFS_REMOVE: { case CMD_SPIFFS_REMOVE: {
LED_B_ON(); LED_B_ON();
uint8_t filename[32];
uint8_t *pfilename = packet->data.asBytes; struct p {
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); uint8_t len;
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs REMOVE : %s", filename); uint8_t fn[32];
rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE); } 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(); LED_B_OFF();
break; break;
} }
case CMD_SPIFFS_RENAME: { case CMD_SPIFFS_RENAME: {
LED_B_ON(); LED_B_ON();
struct p {
uint8_t slen;
uint8_t src[32]; uint8_t src[32];
uint8_t dlen;
uint8_t dest[32]; uint8_t dest[32];
uint8_t *pfilename = packet->data.asBytes; } PACKED;
char *token; struct p *payload = (struct p *) packet->data.asBytes;
token = strtok((char *)pfilename, ",");
strncpy((char *)src, token, sizeof(src) - 1);
token = strtok(NULL, ",");
strncpy((char *)dest, token, sizeof(dest) - 1);
if (DBGLEVEL >= DBG_DEBUG) { if (DBGLEVEL >= DBG_DEBUG) {
Dbprintf("Filename received as source for spiffs RENAME : %s", src); Dbprintf("SPIFFS RENAME");
Dbprintf("Filename received as destination for spiffs RENAME : %s", dest); 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(); LED_B_OFF();
break; break;
} }
case CMD_SPIFFS_COPY: { case CMD_SPIFFS_COPY: {
LED_B_ON(); LED_B_ON();
struct p {
uint8_t slen;
uint8_t src[32]; uint8_t src[32];
uint8_t dlen;
uint8_t dest[32]; uint8_t dest[32];
uint8_t *pfilename = packet->data.asBytes; } PACKED;
char *token; struct p *payload = (struct p *) packet->data.asBytes;
token = strtok((char *)pfilename, ",");
strncpy((char *)src, token, sizeof(src) - 1);
token = strtok(NULL, ",");
strncpy((char *)dest, token, sizeof(dest) - 1);
if (DBGLEVEL >= DBG_DEBUG) { if (DBGLEVEL >= DBG_DEBUG) {
Dbprintf("Filename received as source for spiffs COPY : %s", src); Dbprintf("SPIFFS COPY");
Dbprintf("Filename received as destination for spiffs COPY : %s", dest); 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(); LED_B_OFF();
break; break;
} }
case CMD_SPIFFS_WRITE: { case CMD_SPIFFS_WRITE: {
LED_B_ON(); 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) { if (DBGLEVEL >= DBG_DEBUG) {
rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); Dbprintf("SPIFFS WRITE, dest `%s` with APPEND set to: %c", payload->fn, payload->append ? 'Y' : 'N');
} else {
rdv40_spiffs_append((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE);
} }
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(); LED_B_OFF();
break; break;
} }
@ -2202,6 +2218,10 @@ static void PacketReceived(PacketCommandNG *packet) {
Flash_CheckBusy(BUSY_TIMEOUT); Flash_CheckBusy(BUSY_TIMEOUT);
Flash_WriteEnable(); Flash_WriteEnable();
Flash_Erase4k(3, 0xB); 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); res = Flash_Write(startidx, data, len);

View file

@ -1881,7 +1881,7 @@ void iClass_WriteBlock(uint8_t *msg) {
iclass_send_as_reader(write, write_len, &start_time, &eof_time); 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; res = false;
switch_off(); switch_off();
if (payload->req.send_reply) if (payload->req.send_reply)

View file

@ -2972,7 +2972,7 @@ void ReaderIso14443a(PacketCommandNG *c) {
} }
} }
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
FpgaDisableTracing(); FpgaDisableTracing();
reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); reply_mix(CMD_ACK, 0, 0, 0, NULL, 0);
} else { } else {

View file

@ -2092,7 +2092,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) {
uint32_t eof_time = 0; uint32_t eof_time = 0;
CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time); CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
FpgaDisableTracing(); FpgaDisableTracing();
reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0); reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0);
} else { } else {

View file

@ -1477,7 +1477,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t
tosend_t *ts = get_tosend(); tosend_t *ts = get_tosend();
TransmitTo15693Tag(ts->buf, ts->max, &start_time); TransmitTo15693Tag(ts->buf, ts->max, &start_time);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
res = PM3_ETEAROFF; res = PM3_ETEAROFF;
@ -1598,7 +1598,7 @@ void ReaderIso15693(uint32_t parameter) {
uint32_t eof_time; uint32_t eof_time;
int recvlen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &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); reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0);
} else { } else {
@ -1928,7 +1928,7 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint
uint32_t start_time = 0; uint32_t start_time = 0;
int recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time); 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); reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0);
} else { } else {

View file

@ -2639,7 +2639,7 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) {
SendForward(len, false); SendForward(len, false);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
StopTicks(); StopTicks();
reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_ETEAROFF, NULL, 0); reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_ETEAROFF, NULL, 0);
} else { } else {
@ -2681,7 +2681,7 @@ void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd) {
SendForward(len, false); SendForward(len, false);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
StopTicks(); StopTicks();
reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_ETEAROFF, NULL, 0); reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_ETEAROFF, NULL, 0);
} else { } else {

View file

@ -2713,20 +2713,15 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) {
// //
// Tear-off attack against MFU. // Tear-off attack against MFU.
// - Moebius et al // - 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 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 (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off");
if (tearoff_time > 43000) if (tearoff_time > 43000)
tearoff_time = 43000; tearoff_time = 43000;
tearoff_delay_us = tearoff_time;
MifareUWriteBlockEx(blockNo, 0, data_fullwrite, false); tearoff_enabled = true;
LEDsoff(); LEDsoff();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); 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; return;
}; };
// send // send
ReaderTransmit(cmd, sizeof(cmd), NULL);
// Wait before cutting power. aka tear-off
LED_D_ON(); LED_D_ON();
ReaderTransmit(cmd, sizeof(cmd), NULL);
SpinDelayUsPrecision(tearoff_time); tearoff_hook();
if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!"));
switch_off();
reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0); reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0);
} }

View file

@ -208,15 +208,12 @@ static void remove_from_spiffs(const char *filename) {
Dbprintf("errno %i\n", SPIFFS_errno(&fs)); 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) { 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; return s.size;
} }

View file

@ -4639,11 +4639,13 @@ void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) {
/* print GNU style [OPTION] string */ /* print GNU style [OPTION] string */
arg_print_gnuswitch(fp, table); arg_print_gnuswitch(fp, table);
size_t len = 0;
/* print remaining options in abbreviated style */ /* print remaining options in abbreviated style */
for (tabindex = 0; for (tabindex = 0;
table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR); table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
tabindex++) { tabindex++) {
char syntax[200] = ""; char syntax[400] = "";
const char *shortopts, *longopts, *datatype; const char *shortopts, *longopts, *datatype;
/* skip short options without arg values (they were printed by arg_print_gnu_switch) */ /* 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; break;
} }
} }
len += strlen(syntax);
if (len > 60) {
fprintf(fp, "\n ");
len = 0;
}
} }
if (suffix) if (suffix)

View file

@ -1296,3 +1296,75 @@ ff9a84635bd2
f1a1239a4487 f1a1239a4487
# #
b882fd4a9f78 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

View file

@ -76,7 +76,7 @@ local function main(args)
if removeflag then if removeflag then
print('Deleting file '..filename.. ' from SPIFFS if exists') print('Deleting file '..filename.. ' from SPIFFS if exists')
core.console("mem spiffs remove " ..filename) core.console("mem spiffs remove -f " ..filename)
return return
end end

View file

@ -20,7 +20,7 @@ The outlined procedure is as following:
-- manchester -- manchester
-- bit rate -- bit rate
"lf t55xx write b 0 d 00008040" "lf t55xx write -b 0 -d 00008040"
"lf t55xx detect" "lf t55xx detect"
"lf t55xx info" "lf t55xx info"
@ -118,7 +118,7 @@ local function test()
elseif _ == 1 then elseif _ == 1 then
local config = pcmd:format(config1, y, config2) 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 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} local wc = Command:newNG{cmd = cmds.CMD_LF_T55XX_WRITEBL, data = data}

View file

@ -14,7 +14,7 @@ The outlined procedure is as following:
--BIPHASE 00010040 --BIPHASE 00010040
-- --
"lf t55xx write b 0 d 00010040" "lf t55xx write -b 0 -d 00010040"
"lf t55xx detect" "lf t55xx detect"
"lf t55xx info" "lf t55xx info"
@ -112,7 +112,7 @@ local function test()
elseif _ == 1 then elseif _ == 1 then
local config = pcmd:format(config1, y, config2) 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 data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags)

View file

@ -17,7 +17,7 @@ The outlined procedure is as following:
-- FSK1 -- FSK1
-- bit rate -- bit rate
"lf t55xx write b 0 d 00007040" "lf t55xx write -b 0 -d 00007040"
"lf t55xx detect" "lf t55xx detect"
"lf t55xx info" "lf t55xx info"
@ -114,7 +114,7 @@ local function test(modulation)
elseif _ == 1 then elseif _ == 1 then
local config = pcmd:format(config1, y, modulation, config2) 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 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} local wc = Command:newNG{cmd = cmds.CMD_LF_T55XX_WRITEBL, data = data}

View file

@ -11,7 +11,7 @@ desc = [[
This script will program a T55x7 TAG with the configuration: block 0x00 data 0x00088040 This script will program a T55x7 TAG with the configuration: block 0x00 data 0x00088040
The outlined procedure is as following: 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 detect"
"lf t55xx info" "lf t55xx info"
@ -118,7 +118,7 @@ local function test(modulation)
dbg('Writing to T55x7 TAG') dbg('Writing to T55x7 TAG')
local config = cmd:format(bitrate, modulation, clockrate) 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) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags)

View file

@ -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 wipe
lf t55xx detect lf t55xx detect
lf t55xx write b 1 d 00000000 lf t55xx write -b 1 -d 00000000
lf t55xx write b 2 d ffffffff lf t55xx write -b 2 -d ffffffff
lf t55xx write b 3 d 80000000 lf t55xx write -b 3 -d 80000000
lf t55xx write b 4 d 00000001 lf t55xx write -b 4 -d 00000001
Loop: Loop:
@ -278,7 +278,7 @@ local function WipeCard()
core.console('rem [ERR:DETECT:WIPED] Failed to detect after wipe') core.console('rem [ERR:DETECT:WIPED] Failed to detect after wipe')
return false return false
else 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 for _ = 1, #data_blocks_cmds do
local val = data_blocks_cmds[_] local val = data_blocks_cmds[_]
local c = string.format(wipe_data_cmd, _, val) local c = string.format(wipe_data_cmd, _, val)
@ -321,7 +321,7 @@ local function test(modulation)
core.clearCommandBuffer() core.clearCommandBuffer()
-- Write Config block -- 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) local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(p_config_cmd, 32), password, block, flags)

View file

@ -251,15 +251,7 @@ static int usage_data_buffclear(void) {
PrintAndLogEx(NORMAL, " h This help"); PrintAndLogEx(NORMAL, " h This help");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int usage_data_fsktonrz(void) {
PrintAndLogEx(NORMAL, "Usage: data fsktonrz c <clock> l <fc_low> f <fc_high>");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h This help");
PrintAndLogEx(NORMAL, " c <clock> enter the a clock (omit to autodetect)");
PrintAndLogEx(NORMAL, " l <fc_low> enter a field clock (omit to autodetect)");
PrintAndLogEx(NORMAL, " f <fc_high> enter a field clock (omit to autodetect)");
return PM3_SUCCESS;
}
//set the demod buffer with given array of binary (one bit per byte) //set the demod buffer with given array of binary (one bit per byte)
//by marshmellow //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) { static int CmdFSKToNRZ(const char *Cmd) {
// take clk, fc_low, fc_high
// blank = auto; CLIParserContext *ctx;
bool errors = false; CLIParserInit(&ctx, "data fsktonrz",
char cmdp = 0; "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n"
int clk = 0, fc_low = 10, fc_high = 8; "Omitted values are autodetect instead",
while (param_getchar(Cmd, cmdp) != 0x00) { "data fsktonrz\n"
switch (tolower(param_getchar(Cmd, cmdp))) { "data fsktonrz -c 32 --low 8 --hi 10");
case 'h':
return usage_data_fsktonrz(); void *argtable[] = {
case 'c': arg_param_begin,
clk = param_get32ex(Cmd, cmdp + 1, 0, 10); arg_int0("c", "clk", "<dec>", "clock"),
cmdp += 2; arg_int0(NULL, "low", "<dec>", "low field clock"),
break; arg_int0(NULL, "hi", "<dec>", "high field clock"),
case 'f': arg_param_end
fc_high = param_get32ex(Cmd, cmdp + 1, 0, 10); };
cmdp += 2; CLIExecWithReturn(ctx, Cmd, argtable, true);
break;
case 'l': int clk = arg_get_int_def(ctx, 1, 0);
fc_low = param_get32ex(Cmd, cmdp + 1, 0, 10); int fc_low = arg_get_int_def(ctx, 2, 0);
cmdp += 2; int fc_high = arg_get_int_def(ctx, 3, 0);
break; CLIParserFree(ctx);
default:
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true;
break;
}
if (errors) break;
}
//Validations
if (errors) return usage_data_fsktonrz();
setClockGrid(0, 0); setClockGrid(0, 0);
DemodBufferLen = 0; DemodBufferLen = 0;
@ -2567,11 +2550,9 @@ static command_t CommandTable[] = {
{"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "<shift> -- Shift 0 for Graphed wave + or - shift value"}, {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "<shift> -- 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"}, {"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"}, {"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"},
{"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"}, {"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"}, {"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"},
{"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"}, {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"},
{"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"}, {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"},
{"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"},

View file

@ -39,7 +39,10 @@ static int CmdHelp(const char *Cmd);
"9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \
"DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5"
//-------------------------------------------------------------------------------------
// Sample private RSA Key
// Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) // Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c)
// private key - Exponent D // private key - Exponent D
#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ #define RSA_D "24BF6185468786FDD303083D25E64EFC" \
"66CA472BC44D253102F8B4A9D3BFA750" \ "66CA472BC44D253102F8B4A9D3BFA750" \
@ -136,14 +139,14 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen){
if (!resp.oldarg[0]) { if (!resp.oldarg[0]) {
PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")"); PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")");
} else { } 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_SUCCESS;
} }
} }
return PM3_EFAILED; return PM3_EFAILED;
} }
static int CmdFlashmemSpiBaudrate(const char *Cmd) { static int CmdFlashmemSpiBaud(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "mem baudrate", CLIParserInit(&ctx, "mem baudrate",
@ -352,6 +355,7 @@ static int CmdFlashMemDump(const char *Cmd) {
arg_int0("l", "len", "<dec>", "length"), arg_int0("l", "len", "<dec>", "length"),
arg_lit0("v", "view", "view dump"), arg_lit0("v", "view", "view dump"),
arg_strx0("f", "file", "<filename>", "file name"), arg_strx0("f", "file", "<filename>", "file name"),
arg_int0("c", "cols", "<dec>", "column breaks (def 32)"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -362,6 +366,7 @@ static int CmdFlashMemDump(const char *Cmd) {
int fnlen = 0; int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0}; char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
int breaks = arg_get_int_def(ctx, 5, 32);
CLIParserFree(ctx); CLIParserFree(ctx);
uint8_t *dump = calloc(len, sizeof(uint8_t)); uint8_t *dump = calloc(len, sizeof(uint8_t));
@ -379,7 +384,7 @@ static int CmdFlashMemDump(const char *Cmd) {
if (view) { if (view) {
PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------"); PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------");
print_hex_break(dump, len, 32); print_hex_break(dump, len, breaks);
} }
if (filename[0] != '\0') { if (filename[0] != '\0') {
@ -445,29 +450,39 @@ static int CmdFlashMemInfo(const char *Cmd) {
CLIParserInit(&ctx, "mem info", CLIParserInit(&ctx, "mem info",
"Collect signature and verify it from flash memory", "Collect signature and verify it from flash memory",
"mem info" "mem info"
// "mem info -s" // "mem info -s -d 0102030405060708"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
// arg_lit0("s", NULL, "create a signature"), arg_lit0("s", "sign", "create a signature"),
// arg_lit0("w", NULL, "write signature to flash memory"), arg_str0("d", NULL, "<hex>", "flash memory id, 8 hex bytes"),
// arg_lit0("w", "write", "write signature to flash memory"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
bool shall_sign = false, shall_write = false; bool shall_sign = false, shall_write = false;
// shall_sign = arg_get_lit(ctx, 1); shall_sign = arg_get_lit(ctx, 1);
// shall_write = arg_get_lit(ctx, 2);
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); 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; rdv40_validation_t mem;
int res = rdv4_get_signature(&mem); res = rdv4_get_signature(&mem);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
res = rdv4_validate(&mem); res = rdv4_validate(&mem);
// Flash ID hash (sha1) // Flash ID hash (sha1)
@ -479,6 +494,11 @@ static int CmdFlashMemInfo(const char *Cmd) {
PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------");
PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); 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(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(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------"); PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------");
for (int i = 0; i < (sizeof(mem.signature) / 32); i++) { 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(INFO, " %.64s", str_pk + 192);
PrintAndLogEx(NORMAL, ""); 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( PrintAndLogEx(
(is_keyok) ? SUCCESS : FAILED, (is_keyok) ? SUCCESS : FAILED,
"RSA key validation... ( %s )", "RSA public key validation.... ( %s )",
(is_keyok) ? _GREEN_("ok") : _RED_("fail") (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 // to be verified
uint8_t from_device[RRG_RSA_KEY_LEN]; uint8_t from_device[RRG_RSA_KEY_LEN];
memcpy(from_device, mem.signature, 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) // Signing (private key)
if (shall_sign) { 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); int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign);
PrintAndLogEx( PrintAndLogEx(
@ -555,26 +590,29 @@ static int CmdFlashMemInfo(const char *Cmd) {
} }
// Verify (public key) // 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); mbedtls_rsa_free(&rsa);
PrintAndLogEx( PrintAndLogEx(
(is_verified == 0) ? SUCCESS : FAILED, (is_verified) ? SUCCESS : FAILED,
"RSA verification..... ( %s )", "RSA verification..... ( %s )",
(is_verified == 0) ? _GREEN_("ok") : _RED_("fail") (is_verified) ? _GREEN_("ok") : _RED_("fail")
); );
if (is_verified) {
PrintAndLogEx(SUCCESS, "Genuine Proxmark3 RDV4 signature detected");
}
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"},
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"baudrate", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate"}, {"baudrate", CmdFlashmemSpiBaud, 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"}, {"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"}, {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -8,352 +8,16 @@
// Proxmark3 RDV40 Flash memory commands // Proxmark3 RDV40 Flash memory commands
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
#include "cmdflashmemspiffs.h" #include "cmdflashmemspiffs.h"
#include <ctype.h> #include <ctype.h>
#include "cmdparser.h" // command_t #include "cmdparser.h" // command_t
#include "pmflash.h" #include "pmflash.h"
#include "fileutils.h" //saveFile #include "fileutils.h" //saveFile
#include "comms.h" //getfromdevice #include "comms.h" //getfromdevice
#include "cliparser.h"
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
static int usage_flashmemspiffs_remove(void) { int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen) {
PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem\n");
PrintAndLogEx(NORMAL, "Usage: mem spiffs remove <filename>");
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 <source> <destination>");
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 <source> <destination>");
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 <filename> [f <file name> [e]] [p]");
PrintAndLogEx(NORMAL, " o <filename> - filename in SPIFFS");
PrintAndLogEx(NORMAL, " f <filename> - file name to save to <w/o .bin>");
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 <filename> f <filename>");
PrintAndLogEx(NORMAL, " o <filename> - destination filename");
PrintAndLogEx(NORMAL, " f <filename> - 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 ret_val = PM3_SUCCESS; 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 // Send to device
uint32_t bytes_sent = 0; uint32_t bytes_sent = 0;
uint32_t bytes_remaining = datalen; uint32_t bytes_remaining = datalen;
uint32_t append = 0;
// fast push mode // fast push mode
conn.block_after_ACK = true; conn.block_after_ACK = true;
while (bytes_remaining > 0) { while (bytes_remaining > 0) {
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); 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(); clearCommandBuffer();
SendCommandNG(CMD_SPIFFS_WRITE, (uint8_t *)payload, sizeof(flashmem_write_t) + bytes_in_packet);
char fdata[32 + bytes_in_packet]; free(payload);
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);
bytes_remaining -= bytes_in_packet; bytes_remaining -= bytes_in_packet;
bytes_sent += bytes_in_packet; bytes_sent += bytes_in_packet;
PacketResponseNG resp;
uint8_t retry = 3; 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."); PrintAndLogEx(WARNING, "timeout while waiting for reply.");
retry--; retry--;
if (retry == 0) { if (retry == 0) {
@ -398,13 +64,6 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) {
goto out; 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: out:
@ -416,99 +75,470 @@ out:
// We want to unmount after these to set things back to normal but more than this // 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 // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory
SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
return ret_val; 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)); void *argtable[] = {
if (ctmp == 'h') { arg_param_begin,
return usage_flashmemspiffs_wipe(); arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
clearCommandBuffer();
SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
return PM3_SUCCESS;
} }
PrintAndLogEx(INFO, "Wiping all files from SPIFFS FileSystem"); 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", "<fn>", "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", "<fn>", "source file name"),
arg_str1("d", "dest", "<fn>", "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", "<fn>", "source file name"),
arg_str1("d", "dest", "<fn>", "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", "<fn>", "SPIFFS file to save"),
arg_str0("d", "dest", "<fn>", "file name to save to <w/o .bin>"),
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;
}
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; PacketResponseNG resp;
clearCommandBuffer(); clearCommandBuffer();
SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0); SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0);
WaitForResponse(CMD_SPIFFS_WIPE, &resp); WaitForResponse(CMD_SPIFFS_WIPE, &resp);
if (resp.status == PM3_SUCCESS)
PrintAndLogEx(INFO, "Done!"); PrintAndLogEx(INFO, "Done!");
PrintAndLogEx(HINT, "Try use '" _YELLOW_("mem spiffs tree") "' to verify.");
PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
return PM3_SUCCESS; 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}; void *argtable[] = {
uint8_t destfilename[32] = {0}; arg_param_begin,
bool errors = false; arg_str1("s", "src", "<fn>", "source file name"),
uint8_t cmdp = 0; arg_str1("d", "dest", "<fn>", "destination file name"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { int slen = 0;
switch (tolower(param_getchar(Cmd, cmdp))) { char src[FILE_PATH_SIZE] = {0};
case 'h': CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, FILE_PATH_SIZE, &slen);
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;
}
}
// Validations int dlen = 0;
if (errors || cmdp == 0) char dest[32] = {0};
return usage_flashmemspiffs_load(); 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; size_t datalen = 0;
uint8_t *data = NULL; uint8_t *data = NULL;
int res = loadFile_safe(filename, "", (void **)&data, &datalen); int res = loadFile_safe(src, "", (void **)&data, &datalen);
// int res = loadFileEML( filename, data, &datalen);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
free(data); free(data);
return PM3_EFILE; return PM3_EFILE;
} }
res = flashmem_spiffs_load(destfilename, data, datalen); res = flashmem_spiffs_load(dest, data, datalen);
free(data); free(data);
if (res == PM3_SUCCESS) 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; return res;
} }
static command_t CommandTable[] = { static int CmdFlashMemSpiFFSView(const char *Cmd) {
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", "<fn>", "SPIFFS file to view"),
arg_int0("c", "cols", "<dec>", "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"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"}, {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"},
{"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented Filesystem"}, {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"},
{"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"},
{"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"},
{"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"},
{"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"},
{"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"},
{"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations"},
{"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"}, {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash memory file system tree"},
{"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS file system"},
{"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, {"upload", CmdFlashMemSpiFFSUpload, IfPm3Flash, "Upload file into SPIFFS file system"},
{"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS FileSystem." _RED_("* dangerous *") }, {"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} {NULL, NULL, NULL, NULL}
}; };

View file

@ -14,6 +14,6 @@
#include "common.h" #include "common.h"
int CmdFlashMemSpiFFS(const char *Cmd); 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 #endif

View file

@ -103,14 +103,6 @@ static inline uint32_t leadingzeros(uint64_t a) {
#else #else
return 0; return 0;
#endif #endif
}
static inline uint32_t countones(uint64_t a) {
#if defined __GNUC__
return __builtin_popcountll(a);
#else
return 0;
#endif
} }
const char *card_types[] = { const char *card_types[] = {
@ -2034,7 +2026,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
uint64_t a = bytes_to_num(data, 8); uint64_t a = bytes_to_num(data, 8);
bool starts = (leadingzeros(a) < 12); bool starts = (leadingzeros(a) < 12);
bool ones = (countones(a) > 16 && countones(a) < 48); bool ones = (bitcount64(a) > 16 && bitcount64(a) < 48);
if (starts && ones) { if (starts && ones) {
PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible"); PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible");

View file

@ -1290,9 +1290,10 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8
MifareAuthState = masNrAr; MifareAuthState = masNrAr;
if (AuthData.first_auth) { if (AuthData.first_auth) {
AuthData.nt = bytes_to_num(cmd, 4); AuthData.nt = bytes_to_num(cmd, 4);
AuthData.nt_enc_par = 0;
} else { } else {
AuthData.nt_enc = bytes_to_num(cmd, 4); AuthData.nt_enc = bytes_to_num(cmd, 4);
AuthData.nt_enc_par = parity[0]; AuthData.nt_enc_par = parity[0] & 0xF0;
} }
return; return;
} else { } 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)"); snprintf(exp, size, "AUTH: nr ar (enc)");
MifareAuthState = masAt; MifareAuthState = masAt;
AuthData.nr_enc = bytes_to_num(cmd, 4); 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 = bytes_to_num(&cmd[4], 4);
AuthData.ar_enc_par = parity[0] << 4; AuthData.ar_enc_par = parity[0] << 4;
return; 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)"); snprintf(exp, size, "AUTH: at (enc)");
MifareAuthState = masAuthComplete; MifareAuthState = masAuthComplete;
AuthData.at_enc = bytes_to_num(cmd, 4); AuthData.at_enc = bytes_to_num(cmd, 4);
AuthData.at_enc_par = parity[0]; AuthData.at_enc_par = parity[0] & 0xF0;
return; return;
} else { } else {
MifareAuthState = masError; 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) { 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; static struct Crypto1State *traceCrypto1;
@ -1428,7 +1441,29 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes
//hardnested //hardnested
if (!traceCrypto1) { 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; MifareAuthState = masError;
/* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests /* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests

View file

@ -18,6 +18,7 @@ typedef struct {
uint32_t nt_enc; // encrypted tag challenge uint32_t nt_enc; // encrypted tag challenge
uint8_t nt_enc_par; // encrypted tag challenge parity uint8_t nt_enc_par; // encrypted tag challenge parity
uint32_t nr_enc; // encrypted reader challenge uint32_t nr_enc; // encrypted reader challenge
uint8_t nr_enc_par; // encrypted reader challenge parity
uint32_t ar_enc; // encrypted reader response uint32_t ar_enc; // encrypted reader response
uint8_t ar_enc_par; // encrypted reader response parity uint8_t ar_enc_par; // encrypted reader response parity
uint32_t at_enc; // encrypted tag response uint32_t at_enc; // encrypted tag response

View file

@ -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) { static void decode_print_st(uint16_t blockno, uint8_t *data) {
if (mfIsSectorTrailer(blockno)) { if (mfIsSectorTrailer(blockno)) {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Sector trailer decoded:"); PrintAndLogEx(INFO, "--------- " _CYAN_("Sector trailer") " -------------");
PrintAndLogEx(NORMAL, "----------------------------------------------"); PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6));
PrintAndLogEx(NORMAL, "Key A " _GREEN_("%s"), sprint_hex_inrow(data, 6)); PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3));
PrintAndLogEx(NORMAL, "Key B " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); PrintAndLogEx(INFO, "user / gdb... " _GREEN_("%02x"), data[9]);
PrintAndLogEx(NORMAL, "Access rights"); PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
PrintAndLogEx(INFO, "Access rights decoded");
int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int bln = mfFirstBlockOfSector(mfSectorNum(blockno));
int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1;
for (int i = 0; i < 4; i++) { 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; 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"); PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols");
return PM3_ESOFT; 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 sc_size = mfNumBlocksPerSector(sectorNo) * 16;
uint8_t *data = calloc(sc_size, sizeof(uint8_t)); 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 blocks = NumBlocksPerSector(sectorNo);
uint8_t start = FirstBlockOfSector(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++) { 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)); decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16));
@ -3771,8 +3780,10 @@ static int CmdHF14AMfEGetSc(const char *Cmd) {
return PM3_ESOFT; 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 blocks = 4;
uint8_t start = sector * 4; uint8_t start = sector * 4;
if (sector >= 32) { if (sector >= 32) {
@ -3785,11 +3796,11 @@ static int CmdHF14AMfEGetSc(const char *Cmd) {
int res = mfEmlGetMem(data, start + i, 1); int res = mfEmlGetMem(data, start + i, 1);
if (res == PM3_SUCCESS) { if (res == PM3_SUCCESS) {
if (start + i == 0) { 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)) { } 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 { } 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; 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 blocks = 4;
uint8_t start = sector * 4; uint8_t start = sector * 4;
if (sector >= 32) { 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); PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res);
return PM3_ESOFT; 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); decode_print_st(start + blocks - 1, data);
return PM3_SUCCESS; return PM3_SUCCESS;

View file

@ -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 100 -s 1000\n"
"hf mfu otptear -b 3 -i 1 -e 200\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\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[] = { void *argtable[] = {
@ -3112,7 +3112,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
arg_u64_0("e", "end", "<dec>", "end time (def 3000 us)"), arg_u64_0("e", "end", "<dec>", "end time (def 3000 us)"),
arg_u64_0("s", "start", "<dec>", "start time (def 0 us)"), arg_u64_0("s", "start", "<dec>", "start time (def 0 us)"),
arg_str0("d", "data", "<hex>", "initialise data before run (4 bytes)"), arg_str0("d", "data", "<hex>", "initialise data before run (4 bytes)"),
arg_str0("t", "test", "<hex>", "test write data (4 bytes)"), arg_str0("t", "test", "<hex>", "test write data (4 bytes, 00000000 by default)"),
arg_str0("m", "match", "<hex>", "exit criteria, if block matches this value (4 bytes)"), arg_str0("m", "match", "<hex>", "exit criteria, if block matches this value (4 bytes)"),
arg_param_end arg_param_end
}; };
@ -3126,6 +3126,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
int d_len = 0; int d_len = 0;
uint8_t data[4] = {0x00}; uint8_t data[4] = {0x00};
CLIGetHexWithReturn(ctx, 5, data, &d_len); CLIGetHexWithReturn(ctx, 5, data, &d_len);
bool use_data = (d_len > 0);
int t_len = 0; int t_len = 0;
uint8_t test[4] = {0x00}; uint8_t test[4] = {0x00};
@ -3150,7 +3151,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
if (start > (end - steps)) { 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; return PM3_EINVARG;
} }
@ -3169,26 +3170,34 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
uint8_t teardata[8] = {0x00}; uint8_t teardata[4] = {0x00};
memcpy(teardata, data, sizeof(data)); memcpy(teardata, test, sizeof(test));
memcpy(teardata + sizeof(data), test, sizeof(test));
PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Tear off") " ---------------------"); PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Tear off") " ---------------------");
PrintAndLogEx(INFO, "Starting Tear-off test"); PrintAndLogEx(INFO, "Starting Tear-off test");
PrintAndLogEx(INFO, "Target block no: %u", blockno); PrintAndLogEx(INFO, "Target block no: %u", blockno);
PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(teardata, 4)); if (use_data) {
PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata + 4, 4)); 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, "----------------------------------------------------"); PrintAndLogEx(INFO, "----------------------------------------------------");
uint8_t isOK; uint8_t isOK;
bool got_pre = false, got_post = false, lock_on = false; bool lock_on = false;
uint8_t pre[4] = {0}; uint8_t pre[4] = {0};
uint8_t post[4] = {0}; uint8_t post[4] = {0};
uint32_t current = start; uint32_t current = start;
int phase_clear = -1; int phase_begin_clear = -1;
int phase_newwr = -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 retries = 0;
uint8_t error_retries = 0;
while (current <= (end - steps)) { while ((current <= (end - steps)) && (error_retries < 10)) {
if (kbd_enter_pressed()) { if (kbd_enter_pressed()) {
PrintAndLogEx(INFO, "\naborted via keyboard!\n"); 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); PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", current);
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
PacketResponseNG resp; 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)) { if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
isOK = resp.oldarg[0] & 0xFF; isOK = resp.oldarg[0] & 0xFF;
if (isOK) { if (isOK) {
@ -3209,9 +3235,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
got_pre = true; got_pre = true;
} }
} }
if (! got_pre) {
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
error_retries++;
continue; // try again
}
clearCommandBuffer(); 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.. // 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) { if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Tear off reporting failure to select tag"); PrintAndLogEx(WARNING, "Tear off reporting failure to select tag");
error_retries++;
continue; continue;
} }
got_post = false; bool got_post = false;
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0); SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
@ -3235,9 +3266,12 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
got_post = true; got_post = true;
} }
} }
if (! got_post) {
if (got_pre && got_post) { PrintAndLogEx(FAILED, "Failed to read block BEFORE");
error_retries++;
continue; // try again
}
error_retries = 0;
char prestr[20] = {0}; char prestr[20] = {0};
snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre)));
char poststr[20] = {0}; char poststr[20] = {0};
@ -3245,18 +3279,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
if (memcmp(pre, post, sizeof(pre)) == 0) { if (memcmp(pre, post, sizeof(pre)) == 0) {
PrintAndLogEx(INFO, "Current %02d (0x%02X) %s" PrintAndLogEx(INFO, "Current : %02d (0x%02X) %s"
, blockno , blockno
, blockno , blockno
, poststr , poststr
); );
} else { } else {
PrintAndLogEx(INFO, _CYAN_("Tear off occurred") " : %02d (0x%02X) %s => " _RED_("%s")
// 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
, blockno , blockno
, prestr , prestr
@ -3265,27 +3294,27 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
lock_on = true; lock_on = true;
if (phase_clear == -1) if ((phase_begin_clear == -1) && (bitcount32(*(uint32_t*)pre) > bitcount32(*(uint32_t*)post)))
phase_clear = current; 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.. // new write phase must be atleast 100us later..
if (phase_clear > -1 && phase_newwr == -1 && current > (phase_clear + 100)) if (((bitcount32(*(uint32_t*)pre) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(*(uint32_t*)post) != 0) && (skip_phase1 || (current > (phase_end_clear + 100))))
phase_newwr = current; 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(pre, match, sizeof(pre)) == 0) { if (use_match && memcmp(post, match, sizeof(post)) == 0) {
PrintAndLogEx(SUCCESS, "Block matches!\n"); PrintAndLogEx(SUCCESS, "Block matches stop condition!\n");
break; break;
} }
} else {
if (got_pre == false)
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
if (got_post == false)
PrintAndLogEx(FAILED, "Failed to read block AFTER");
}
/* TEMPORALLY DISABLED /* TEMPORALLY DISABLED
uint8_t d0, d1, d2, d3; uint8_t d0, d1, d2, d3;
d0 = *resp.data.asBytes; d0 = *resp.data.asBytes;
@ -3314,11 +3343,17 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
} }
PrintAndLogEx(INFO, "----------------------------------------------------"); PrintAndLogEx(INFO, "----------------------------------------------------");
if (phase_clear > - 1) { if ((phase_begin_clear > - 1) && (phase_begin_clear != start)) {
PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_clear); PrintAndLogEx(INFO, "Erase phase start boundary around " _YELLOW_("%5d") " us", phase_begin_clear);
} }
if (phase_newwr > - 1) { if ((phase_end_clear > - 1) && (phase_end_clear != start)){
PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_newwr); 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, ""); PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS; return PM3_SUCCESS;

View file

@ -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, " 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, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent"));
PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _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")); PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent"));

View file

@ -768,6 +768,7 @@ int lfsim_upload_gb(void) {
break; break;
} }
PrintAndLogEx(NORMAL, "." NOLF); PrintAndLogEx(NORMAL, "." NOLF);
fflush(stdout);
payload_up.flag = 0; payload_up.flag = 0;
} }
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
@ -1321,7 +1322,7 @@ static bool CheckChipType(bool getDeviceData) {
goto out; goto out;
} }
PrintAndLogEx(NORMAL, "Couldn't identify a chipset"); PrintAndLogEx(INFO, "Couldn't identify a chipset");
out: out:
save_restoreGB(GRAPH_RESTORE); save_restoreGB(GRAPH_RESTORE);
save_restoreDB(GRAPH_RESTORE); save_restoreDB(GRAPH_RESTORE);

View file

@ -24,10 +24,10 @@ static int CmdHelp(const char *Cmd);
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"410x", CmdLFEM410X, AlwaysAvailable, "EM 4102 commands..."}, {"410x", CmdLFEM410X, AlwaysAvailable, "{ EM 4102 commands... }"},
{"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4205 / 4305 / 4369 / 4469 commands..."}, {"4x05", CmdLFEM4X05, AlwaysAvailable, "{ EM 4205 / 4305 / 4369 / 4469 commands... }"},
{"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4350 / 4450 commands..."}, {"4x50", CmdLFEM4X50, AlwaysAvailable, "{ EM 4350 / 4450 commands... }"},
{"4x70", CmdLFEM4X70, AlwaysAvailable, "EM 4070 / 4170 commands..."}, {"4x70", CmdLFEM4X70, AlwaysAvailable, "{ EM 4070 / 4170 commands... }"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -1692,11 +1692,8 @@ int CmdEM4x05Unlock(const char *Cmd) {
} }
// write // write
res = unlock_write_protect(use_pwd, pwd, write_value, verbose); // don't check the return value. As a tear-off occurred, the write failed.
if (res != PM3_SUCCESS) { unlock_write_protect(use_pwd, pwd, write_value, verbose);
PrintAndLogEx(WARNING, "failed unlock write");
return PM3_ESOFT;
}
// read after trigger // read after trigger
res = em4x05_read_word_ext(14, pwd, use_pwd, &word14); res = em4x05_read_word_ext(14, pwd, use_pwd, &word14);
@ -1962,11 +1959,11 @@ int CmdEM4x05Sniff(const char *Cmd) {
bool haveData, sampleData = true; bool haveData, sampleData = true;
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x05_sniff", CLIParserInit(&ctx, "lf em 4x05 sniff",
"Sniff EM4x05 commands sent from a programmer", "Sniff EM4x05 commands sent from a programmer",
"lf em 4x05_sniff -> sniff via lf sniff\n" "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 -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 -r --> reverse the bit order when showing block data"
); );
void *argtable[] = { void *argtable[] = {

View file

@ -471,7 +471,7 @@ int CmdEM4x50Chk(const char *Cmd) {
// upload to flash. // upload to flash.
datalen = MIN(bytes_remaining, keyblock); datalen = MIN(bytes_remaining, keyblock);
res = flashmem_spiffs_load(destfn, keys, datalen); res = flashmem_spiffs_load((char*)destfn, keys, datalen);
if (res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "SPIFFS upload failed"); PrintAndLogEx(WARNING, "SPIFFS upload failed");
return res; return res;
@ -568,7 +568,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
int CmdEM4x50Read(const char *Cmd) { int CmdEM4x50Read(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x50 read", CLIParserInit(&ctx, "lf em 4x50 rdbl",
"Reads single EM4x50 block/word.", "Reads single EM4x50 block/word.",
"lf em 4x50 rdbl -b 3\n" "lf em 4x50 rdbl -b 3\n"
"lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n" "lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n"

View file

@ -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) // FDX-B ISO11784/85 demod (aka animal tag) BIPHASE, inverted, rf/32, with preamble of 00000000001 (128bits)
// 8 databits + 1 parity (1) // 8 databits + 1 parity (1)
// CIITT 16 chksum // CIITT 16 chksum
@ -594,7 +584,7 @@ int demodFDXB(bool verbose) {
uint8_t bt_par = (extended & 0x100) >> 8; uint8_t bt_par = (extended & 0x100) >> 8;
uint8_t bt_temperature = extended & 0xff; 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) ; uint8_t is_bt_temperature = (bt_calc_parity == bt_par) && !(extended & 0xe00) ;
if (is_bt_temperature) { if (is_bt_temperature) {

View file

@ -113,19 +113,6 @@ static int usage_hitag_writer(void) {
PrintAndLogEx(NORMAL, " 27 <password> <page> <byte0...byte3> Write page, password mode. Default: 4D494B52 (\"MIKR\")"); PrintAndLogEx(NORMAL, " 27 <password> <page> <byte0...byte3> Write page, password mode. Default: 4D494B52 (\"MIKR\")");
return PM3_SUCCESS; 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 <filename w/o extension>");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h This help");
PrintAndLogEx(NORMAL, " f <filename> 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) { static int CmdLFHitagList(const char *Cmd) {
char args[128] = {0}; char args[128] = {0};
@ -614,50 +601,46 @@ static int CmdLFHitagReader(const char *Cmd) {
static int CmdLFHitagCheckChallenges(const char *Cmd) { static int CmdLFHitagCheckChallenges(const char *Cmd) {
char filename[FILE_PATH_SIZE] = { 0x00 }; CLIParserContext *ctx;
size_t datalen = 0; CLIParserInit(&ctx, "lf hitag cc",
int res = 0; "Check challenges, load a file with saved hitag crypto challenges and test them all.\n"
bool file_given = false; "The file should be 8 * 60 bytes long, the file extension defaults to " _YELLOW_("`.cc`") " ",
bool errors = false; "lf hitag cc -f my_hitag_challenges"
uint8_t cmdp = 0; );
uint8_t *data = calloc(8 * 60, sizeof(uint8_t));
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { void *argtable[] = {
switch (tolower(param_getchar(Cmd, cmdp))) { arg_param_begin,
case 'h': arg_str0("f", "filename", "<fn w/o ext>", "filename to load from"),
free(data); arg_param_end
return usage_hitag_checkchallenges(); };
case 'f': CLIExecWithReturn(ctx, Cmd, argtable, true);
//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;
}
}
//Validations int fnlen = 0;
if (errors || strlen(Cmd) == 0) { char filename[FILE_PATH_SIZE] = {0};
free(data); CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
return usage_hitag_checkchallenges();
} CLIParserFree(ctx);
clearCommandBuffer(); 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);
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); free(data);
}
} else {
SendCommandMIX(CMD_LF_HITAGS_TEST_TRACES, 0, 0, 0, NULL, 0);
}
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -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 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 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 static uint8_t isEven_64_63(const uint8_t *data) { // 8
uint32_t tmp[2]; uint32_t tmp[2];
memcpy(tmp, data, 8); 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) //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 configuration
lf t55xx wr b 0 d 00170082 lf t55xx wr -b 0 -d 00170082
1) uid 049033 1) uid 049033
lf t55 wr b 1 d FF8B4168 lf t55xx wr -b 1 -d FF8B4168
lf t55 wr b 2 d C90B5359 lf t55xx wr -b 2 -d C90B5359
lf t55 wr b 3 d 19A40087 lf t55xx wr -b 3 -d 19A40087
lf t55 wr b 4 d 120115CF lf t55xx wr -b 4 -d 120115CF
2) uid 001630 2) uid 001630
lf t55 wr b 1 d FF8B6B20 lf t55xx wr -b 1 -d FF8B6B20
lf t55 wr b 2 d F19B84A3 lf t55xx wr -b 2 -d F19B84A3
lf t55 wr b 3 d 18058007 lf t55xx wr -b 3 -d 18058007
lf t55 wr b 4 d 1200857C lf t55xx wr -b 4 -d 1200857C
3) uid 39feff 3) uid 39feff
lf t55xx wr b 1 d ffbfa73e lf t55xx wr -b 1 -d ffbfa73e
lf t55xx wr b 2 d 4c0003ff lf t55xx wr -b 2 -d 4c0003ff
lf t55xx wr b 3 d ffbfa73e lf t55xx wr -b 3 -d ffbfa73e
lf t55xx wr b 4 d 4c0003ff lf t55xx wr -b 4 -d 4c0003ff
*/ */
@ -469,7 +459,7 @@ static int CmdLFNedapClone(const char *Cmd) {
if (res == PM3_SUCCESS) { if (res == PM3_SUCCESS) {
PrintAndLogEx(INFO, "The block 0 was changed (eXtended) which can be hard to detect."); 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 { } else {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
} }

File diff suppressed because it is too large Load diff

View file

@ -32,6 +32,7 @@
#define T55X7_NORALSY_CONFIG_BLOCK 0x00088C6A // ASK, compat mode, (NORALSY - KCP3000), data rate 32, 3 data blocks #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_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_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 // 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, // 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; uint8_t offset;
uint32_t block0; uint32_t block0;
enum { enum {
notSet = 0x00, NOTSET = 0x00,
autoDetect = 0x01, AUTODETECT = 0x01,
userSet = 0x02, USERSET = 0x02,
tagRead = 0x03, TAGREAD = 0x03,
} block0Status; } block0Status;
enum { enum {
RF_8 = 0x00, RF_8 = 0x00,

View file

@ -298,10 +298,10 @@ static command_t CommandTable[] = {
{"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"},
{"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"}, {"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"},
{"clear", CmdClear, AlwaysAvailable, "Clear screen"}, {"clear", CmdClear, AlwaysAvailable, "Clear screen"},
{"help", CmdHelp, AlwaysAvailable, "This help. Use " _YELLOW_("'<command> help'") " for details of a particular command."}, {"help", CmdHelp, AlwaysAvailable, "Use " _YELLOW_("'<command> help'") " for details of a particular command."},
{"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"}, {"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"},
{"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"}, {"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"}, {"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"},
{"quit", CmdQuit, AlwaysAvailable, ""}, {"quit", CmdQuit, AlwaysAvailable, ""},
{"exit", CmdQuit, AlwaysAvailable, "Exit program"}, {"exit", CmdQuit, AlwaysAvailable, "Exit program"},

View file

@ -429,9 +429,9 @@ static int CmdScriptRun(const char *Cmd) {
} }
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "Usage info"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdScriptList, AlwaysAvailable, "List available scripts"}, {"list", CmdScriptList, AlwaysAvailable, "List available scripts"},
{"run", CmdScriptRun, AlwaysAvailable, "<name> -- execute a script"}, {"run", CmdScriptRun, AlwaysAvailable, "<name> - execute a script"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -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++) { for (int j = 0; j < data_len && j / 18 < 18; j++) {
uint8_t parityBits = parityBytes[j >> 3]; uint8_t parityBits = parityBytes[j >> 3];
if (protocol != LEGIC if (protocol != LEGIC
&& protocol != ISO_14443B && protocol != ISO_14443B
@ -256,7 +255,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
&& protocol != FELICA && protocol != FELICA
&& protocol != LTO && protocol != LTO
&& protocol != PROTO_CRYPTORF && protocol != PROTO_CRYPTORF
&& (hdr->isResponse || protocol == ISO_14443A) && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE)
&& (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) { && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) {
snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]); 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) { if (markCRCBytes) {
//CRC-command //CRC-command
if (crcStatus == 0 || crcStatus == 1) { 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) = '['; (*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", ']'); sprintf(pos2, "%c", ']');
} }
} }

View file

@ -839,6 +839,9 @@ static bool dl_it(uint8_t *dest, uint32_t bytes, PacketResponseNG *response, siz
if (response->cmd == CMD_ACK) if (response->cmd == CMD_ACK)
return true; 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 // sample_buf is a array pointer, located in data.c
// arg0 = offset in transfer. Startindex of this chunk // arg0 = offset in transfer. Startindex of this chunk

View file

@ -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, _RED_("reboot the Proxmark3 then only update the main firmware") "\n");
PrintAndLogEx(ERR, "Follow these steps :"); PrintAndLogEx(ERR, "Follow these steps :");
PrintAndLogEx(ERR, " 1) ./pm3-flash-bootrom"); PrintAndLogEx(ERR, " 1) ./pm3-flash-bootrom");
PrintAndLogEx(ERR, " 2) ./pm3-flash-flash-all"); PrintAndLogEx(ERR, " 2) ./pm3-flash-all");
PrintAndLogEx(ERR, " 3) ./pm3"); PrintAndLogEx(ERR, " 3) ./pm3");
PrintAndLogEx(INFO, "--------------------------------------------------------"); PrintAndLogEx(INFO, "--------------------------------------------------------");
g_printed_msg = true; g_printed_msg = true;

View file

@ -695,7 +695,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl
mem[4] = (chunk & 0xFF); mem[4] = (chunk & 0xFF);
// upload to flash. // 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) { if (res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "\nSPIFFS upload failed"); PrintAndLogEx(WARNING, "\nSPIFFS upload failed");
free(mem); free(mem);

View file

@ -803,7 +803,7 @@ static int setCmdPlotSliders(const char *Cmd) {
static int setCmdSavePaths(const char *Cmd) { static int setCmdSavePaths(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "pref set savepath", CLIParserInit(&ctx, "pref set savepaths",
"Set presistent preference of file paths in the client", "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 --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" "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) { 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); showEmojiState(prefShowNone);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int getCmdHint(const char *Cmd) { 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); showHintsState(prefShowNone);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int getCmdColor(const char *Cmd) { 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); showColorState(prefShowNone);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int getCmdDebug(const char *Cmd) { 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); showClientDebugState(prefShowNone);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int getCmdPlotSlider(const char *Cmd) { 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); showPlotSliderState(prefShowNone);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int getCmdBarMode(const char *Cmd) { 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); showBarModeState(prefShowNone);
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int getCmdSavePaths(const char *Cmd) { 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(spDefault, prefShowNone);
showSavePathState(spDump, prefShowNone); showSavePathState(spDump, prefShowNone);
showSavePathState(spTrace, prefShowNone); showSavePathState(spTrace, prefShowNone);
@ -1008,6 +1085,17 @@ static int CmdPrefSet(const char *Cmd) {
} }
static int CmdPrefShow(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) { if (session.preferences_loaded) {
char *fn = prefGetFilename(); char *fn = prefGetFilename();
@ -1043,8 +1131,8 @@ static int CmdPrefSave (const char *Cmd) {
*/ */
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"get", CmdPrefGet, AlwaysAvailable, "Get a preference"}, {"get", CmdPrefGet, AlwaysAvailable, "{ Get a preference }"},
{"set", CmdPrefSet, AlwaysAvailable, "Set a preference"}, {"set", CmdPrefSet, AlwaysAvailable, "{ Set a preference }"},
{"show", CmdPrefShow, AlwaysAvailable, "Show all preferences"}, {"show", CmdPrefShow, AlwaysAvailable, "Show all preferences"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -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) { 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; uint16_t rownum = 0;
PrintAndLogEx(NORMAL, "[%02d] | " NOLF, rownum); int i;
for (size_t i = 0; i < len; ++i) { for (i = 0; i < len; i += breaks, rownum++) {
if (len - i < breaks) { // incomplete block, will be treated out of the loop
PrintAndLogEx(NORMAL, "%02X " NOLF, data[i]); break;
// check if a line break is needed
if (breaks > 0 && !((i + 1) % breaks) && (i + 1 < len)) {
++rownum;
PrintAndLogEx(NORMAL, "\n[%02d] | " NOLF, rownum);
} }
} PrintAndLogEx(INFO, "%02u | %s", rownum, sprint_hex_ascii(data + i, breaks));
PrintAndLogEx(NORMAL, "");
} }
void print_buffer(const uint8_t *data, const size_t len, int level) { // 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);
}
}
static void print_buffer_ex(const uint8_t *data, const size_t len, int level, uint8_t breaks) {
if (len < 1) if (len < 1)
return; return;
char buf[UTIL_BUFFER_SIZE_SPRINT + 3]; char buf[UTIL_BUFFER_SIZE_SPRINT + 3];
int i; int i;
for (i = 0; i < len; i += 16) { for (i = 0; i < len; i += breaks) {
if (len - i < 16) { // incomplete block, will be treated out of the loop if (len - i < breaks) { // incomplete block, will be treated out of the loop
break; break;
} }
// (16 * 3) + (16) + + 1 // (16 * 3) + (16) + + 1
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
sprintf(buf, "%*s%02x: ", (level * 4), " ", i); 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); 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, 16)); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, breaks));
PrintAndLogEx(INFO, "%s", buf); PrintAndLogEx(INFO, "%s", buf);
} }
// the last odd bytes // the last odd bytes
uint8_t mod = len % 16; uint8_t mod = len % breaks;
if (mod) { if (mod) {
memset(buf, 0, sizeof(buf)); 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); hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, mod, (sizeof(buf) - strlen(buf) - 1), 0, 1, true);
// add the spaces... // 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)); snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod));
PrintAndLogEx(INFO, "%s", buf); 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) { void print_blocks(uint32_t *data, size_t len) {
PrintAndLogEx(SUCCESS, "Blk | Data "); PrintAndLogEx(SUCCESS, "Blk | Data ");
PrintAndLogEx(SUCCESS, "----+------------"); PrintAndLogEx(SUCCESS, "----+------------");

View file

@ -1,8 +1,9 @@
# T5577 Introduction Guide # T5577 Introduction Guide
### Based on RRG proxmark3 fork. ### Based on RRG/Iceman Proxmark3 repo
### Ver.1 8 Sep 2019 ### Ver.1 8 Sep 2019
### Ver.2 7 March 2021
| Contents | | Contents |
| ----------------------------------------------------------------------------------- | | ----------------------------------------------------------------------------------- |
@ -23,17 +24,17 @@
## Introduction ## 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 125 Khz frequency space. It is a good card to use to learn about RFID and
learn how to use the proxmark3. 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 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 dont yet as the data sheet will hold the information you need, but you dont yet
know what it means. As such, I will attempt to point to sections of the 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 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 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 : 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 ## What data is on my T5577
Lets have a look and see what a card might look in the proxmark3 Lets 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 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. 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 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 [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 [=] Chip type......... T55x7
Modulation : ASK [=] Modulation........ ASK
Bit Rate : 2 - RF/32 [=] Bit rate.......... 2 - RF/32
Inverted : No [=] Inverted.......... No
Offset : 32 [=] Offset............ 33
Seq. Term. : Yes [=] Seq. terminator... Yes
Block0 : 0x000880E0 [=] Block0............ 000880E0 (auto detect)
Downlink Mode : default/fixed bit length [=] Downlink mode..... default/fixed bit length
[=] Password set...... No
``` ```
Now that the proxmark3 has detected a T55x7 chip, and found some 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. 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 [+] blk | hex data | binary | ascii
[+] ----+----------+----------------------------------+------- [+] ----+----------+----------------------------------+-------
[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 01 | 00000000 | 00000000000000000000000000000000 | ....
[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 02 | 00000000 | 00000000000000000000000000000000 | ....
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 03 | 00000000 | 00000000000000000000000000000000 | ....
[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 04 | 00000000 | 00000000000000000000000000000000 | ....
[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 05 | 00000000 | 00000000000000000000000000000000 | ....
[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 06 | 00000000 | 00000000000000000000000000000000 | ....
[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 07 | 00000000 | 00000000000000000000000000000000 | ....
[+] Reading Page 1: [+] Reading Page 1:
[+] blk | hex data | binary | ascii [+] blk | hex data | binary | ascii
[+] ----+----------+----------------------------------+------- [+] ----+----------+----------------------------------+-------
[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H [+] 01 | 00000000 | 00000000000000000000000000000000 | ....
[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. [+] 02 | 00000000 | 00000000000000000000000000000000 | ....
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 03 | 00000000 | 00000000000000000000000000000000 | ....
``` ```
I will cover the meaning of this data as we go, but for now, lets keep I will cover the meaning of this data as we go, but for now, lets keep
it simple. 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 write data. This reading and writing must be done in the correct way
needed for the chip (and its configuration). Lucky for us, the 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 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) 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 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 as, run a low frequency (lf) command for the T55xx chip (t55xx) and
read block (b) number 1. read block (b) number 1.
``` ```
[usb] pm3 --> lf t55xx read b 1 [usb] pm3 --> lf t55xx read -b 1
``` ```
result: result:
``` ```
[+] Reading Page 0: [+] Reading Page 0:
[+] blk | hex data | binary | ascii [+] 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. should match the dump data.
2) Write some new data into block 1 on the card. 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: result:
``` ```
@ -164,7 +167,7 @@ can see the card)
``` ```
3) Now, lets check if the data was written. 3) Now, lets check if the data was written.
``` ```
[usb] pm3 --> lf t55xx read b 1 [usb] pm3 --> lf t55xx read -b 1
``` ```
result: result:
``` ```
@ -174,8 +177,8 @@ can see the card)
[+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx [+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx
``` ```
4) The data is written in Hexadecimal. A single hex digit holds 4 bits 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 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 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. little bit of home work to learn. The following is a quick start.
| Hex | Binary | Decimal | | Hex | Binary | Decimal |
@ -202,7 +205,7 @@ can see the card)
Lets try and write 89ABCDEF Lets try and write 89ABCDEF
``` ```
[usb] pm3 --> lf t55xx write b 1 d 89abcdef [usb] pm3 --> lf t55xx write -b 1 -d 89abcdef
``` ```
result: result:
``` ```
@ -210,7 +213,7 @@ can see the card)
``` ```
and check and check
``` ```
[usb] pm3 --> lf t55xx read b 1 [usb] pm3 --> lf t55xx read -b 1
``` ```
result: result:
``` ```
@ -235,31 +238,34 @@ result:
[+] ----+----------+----------------------------------+------- [+] ----+----------+----------------------------------+-------
[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
[+] 01 | 89ABCDEF | 10001001101010111100110111101111 | .... [+] 01 | 89ABCDEF | 10001001101010111100110111101111 | ....
[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 02 | 00000000 | 00000000000000000000000000000000 | ....
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 03 | 00000000 | 00000000000000000000000000000000 | ....
[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 04 | 00000000 | 00000000000000000000000000000000 | ....
[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 05 | 00000000 | 00000000000000000000000000000000 | ....
[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 06 | 00000000 | 00000000000000000000000000000000 | ....
[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 07 | 00000000 | 00000000000000000000000000000000 | ....
[+] Reading Page 1: [+] Reading Page 1:
[+] blk | hex data | binary | ascii [+] blk | hex data | binary | ascii
[+] ----+----------+----------------------------------+------- [+] ----+----------+----------------------------------+-------
[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H [+] 01 | 00000000 | 00000000000000000000000000000000 | ....
[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. [+] 02 | 00000000 | 00000000000000000000000000000000 | ....
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 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 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 ## How do I use a password
This can be a little tricky for beginners. This can be a little tricky for beginners.
***If you forget your password you will lose access to your card***. ***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 configuration block (0). To help learn this and make it as simple as I
can, please read and follow exactly. If your results DONT match 100% as can, please read and follow exactly. If your results DONT match 100% as
required, please do not proceed. required, please do not proceed.
@ -272,9 +278,10 @@ required, please do not proceed.
``` ```
Result: 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: 00 data: 0x000880E0
[=] Writing page 0 block: 01 data: 0x00000000 [=] Writing page 0 block: 01 data: 0x00000000
[=] Writing page 0 block: 02 data: 0x00000000 [=] Writing page 0 block: 02 data: 0x00000000
@ -291,14 +298,15 @@ required, please do not proceed.
``` ```
result: result:
``` ```
Chip Type : T55x7 [=] Chip type......... T55x7
Modulation : ASK [=] Modulation........ ASK
Bit Rate : 2 - RF/32 [=] Bit rate.......... 2 - RF/32
Inverted : No [=] Inverted.......... No
Offset : 32 [=] Offset............ 33
Seq. Term. : Yes [=] Seq. terminator... Yes
Block0 : 0x000880E0 [=] Block0............ 000880E0 (auto detect)
Downlink Mode : default/fixed bit length [=] Downlink mode..... default/fixed bit length
[=] Password set...... No
``` ```
If block 0 does not hold the hex data **0x00088040 resolve this 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. 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: result:
``` ```
@ -326,20 +334,20 @@ required, please do not proceed.
[+] blk | hex data | binary | ascii [+] blk | hex data | binary | ascii
[+] ----+----------+----------------------------------+------- [+] ----+----------+----------------------------------+-------
[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 01 | 00000000 | 00000000000000000000000000000000 | ....
[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 02 | 00000000 | 00000000000000000000000000000000 | ....
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 03 | 00000000 | 00000000000000000000000000000000 | ....
[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 04 | 00000000 | 00000000000000000000000000000000 | ....
[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 05 | 00000000 | 00000000000000000000000000000000 | ....
[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 06 | 00000000 | 00000000000000000000000000000000 | ....
[+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx [+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx
[+] Reading Page 1: [+] Reading Page 1:
[+] blk | hex data | binary | ascii [+] blk | hex data | binary | ascii
[+] ----+----------+----------------------------------+------- [+] ----+----------+----------------------------------+-------
[+] 00 | 000880E0 | 00000000000010001000000011100000 | .... [+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H [+] 01 | 00000000 | 00000000000000000000000000000000 | ....
[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#. [+] 02 | 00000000 | 00000000000000000000000000000000 | ....
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | .... [+] 03 | 00000000 | 00000000000000000000000000000000 | ....
``` ```
***Important : If block 0 and block 7 dont match exactly, do not continue.*** ***Important : If block 0 and block 7 dont match exactly, do not continue.***
@ -353,7 +361,7 @@ required, please do not proceed.
![](./t55xx_block0.png) ![](./t55xx_block0.png)
We will cover other things in the configuration later. But the key 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*** Current Block 0 : ***00088040***
New Block 0 : ***00088050*** 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 understand what happened to get from 00088040 to 00088050 we need
to look at the binary data. 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. do more advanced things.
Bit Location (28) 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 0 = No Password, 1 = Use Password
Note how we did NOT change any other part of the configuration, only bit 28. 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 If you have completed all steps and have the exact same results, we are
ready to apply the new configuration. 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: result:
``` ```
@ -410,25 +418,27 @@ required, please do not proceed.
card. card.
Lets try again, but this time supply the password. We use the option 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: result:
``` ```
Chip Type : T55x7 [=] Chip type......... T55x7
Modulation : ASK [=] Modulation........ ASK
Bit Rate : 2 - RF/32 [=] Bit rate.......... 2 - RF/32
Inverted : No [=] Inverted.......... No
Offset : 32 [=] Offset............ 33
Seq. Term. : Yes [=] Seq. terminator... Yes
Block0 : 0x00088050 [=] Block0............ 00088050 (auto detect)
Downlink Mode : default/fixed bit length [=] Downlink mode..... default/fixed bit length
[=] Password set...... Yes
[=] Password.......... 12345678
``` ```
7) Write a block of data with a password 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: result:
``` ```
@ -445,7 +455,7 @@ required, please do not proceed.
The proxmark3 has a safety check\! 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: result:
``` ```
@ -455,12 +465,12 @@ required, please do not proceed.
[!] Safety check: Could not detect if PWD bit is set in config block. Exits. [!] 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 Note that the proxmark3 did not read the block, the safety kicked in
and wants us to confirm by supply the override option o. 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: result:
``` ```
@ -479,14 +489,14 @@ required, please do not proceed.
this from above. this from above.
Remember if we dont know the config and write this config to the Remember if we dont 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 its a good idea card, but will lose any settings you may want. So its a good idea
to read the config, and set bit 28 to 0, rather than just overwrite to read the config, and set bit 28 to 0, rather than just overwrite
the config and change the way the card works. the config and change the way the card works.
In our examples we know what it should be : 00088040 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: result:
``` ```
@ -498,16 +508,17 @@ required, please do not proceed.
``` ```
result: result:
``` ```
Chip Type : T55x7 [=] Chip type......... T55x7
Modulation : ASK [=] Modulation........ ASK
Bit Rate : 2 - RF/32 [=] Bit rate.......... 2 - RF/32
Inverted : No [=] Inverted.......... No
Offset : 32 [=] Offset............ 33
Seq. Term. : Yes [=] Seq. terminator... Yes
Block0 : 0x00088040 [=] Block0............ 00088040 (auto detect)
Downlink Mode : default/fixed bit length [=] 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 # 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 (where is sends out the blocks from 1 to x on power up), as well as when
it responds to commands. 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 from Block 3 Page 1. If the command is not encoded correctly it will
ignore the command and revert back to default read mode. 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 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 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 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 here, rather use an example. Others have collected these and posted on the
forum. 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) ![](./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 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: result:
``` ```
[+] Writing T55x7 tag with UID 0x1122334455 (clock rate: 64) [+] Preparing to clone EM4102 to T55x7 tag with ID 1122334455 (RF/64)
#db# Started writing T55x7 tag ... [#] Clock rate: 64
#db# Clock rate: 64 [#] Tag T55x7 written with 0xff8c65298c94a940
#db# Tag T55x7 written with 0xff8c65298c94a940
[+] Done
``` ```
2) Check this has work. 2) Check this has work.
@ -592,54 +604,50 @@ password set (if not, review and get you card back to this state).
[=] False Positives ARE possible [=] False Positives ARE possible
[=] [=]
[=] Checking for known tags... [=] Checking for known tags...
[=]
[+] EM410x pattern found [+] EM 410x ID 0F0368568B
[+] EM410x ( RF/64 )
EM TAG ID : 1122334455 [=] -------- Possible de-scramble patterns ---------
[+] Unique TAG ID : F0C0166AD1
Possible de-scramble patterns [=] HoneyWell IdentKey
[+] DEZ 8 : 06837899
Unique TAG ID : 8844CC22AA [+] DEZ 10 : 0057169547
HoneyWell IdentKey { [+] DEZ 5.5 : 00872.22155
DEZ 8 : 03359829 [+] DEZ 3.5A : 015.22155
DEZ 10 : 0573785173 [+] DEZ 3.5B : 003.22155
DEZ 5.5 : 08755.17493 [+] DEZ 3.5C : 104.22155
DEZ 3.5A : 017.17493 [+] DEZ 14/IK2 : 00064481678987
DEZ 3.5B : 034.17493 [+] DEZ 15/IK3 : 001034014845649
DEZ 3.5C : 051.17493 [+] DEZ 20/ZK : 15001200010606101301
DEZ 14/IK2 : 00073588229205 [=]
DEZ 15/IK3 : 000585269781162 [+] Other : 22155_104_06837899
DEZ 20/ZK : 08080404121202021010 [+] Pattern Paxton : 259822731 [0xF7C948B]
} [+] Pattern 1 : 9750181 [0x94C6A5]
Other : 17493_051_03359829 [+] Pattern Sebury : 22155 104 6837899 [0x568B 0x68 0x68568B]
Pattern Paxton : 289899093 [0x11478255] [=] ------------------------------------------------
Pattern 1 : 5931804 [0x5A831C]
Pattern Sebury : 17493 51 3359829 [0x4455 0x33 0x334455]
[+] Valid EM410x ID found! [+] Valid EM410x ID found!
[+] Chipset detection: T55xx
[+] Chipset detection : T55xx found [?] Hint: try `lf t55xx` commands
[+] Try `lf t55xx` commands
``` ```
Looks good. 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 [usb] pm3 --> lf t55 detect
``` ```
result: result:
``` ```
[usb] pm3 --> lf t55 detect [=] Chip type......... T55x7
Chip Type : T55x7 [=] Modulation........ ASK
Modulation : ASK [=] Bit rate.......... 5 - RF/64
Bit Rate : 5 - RF/64 [=] Inverted.......... No
Inverted : No [=] Offset............ 33
Offset : 32 [=] Seq. terminator... Yes
Seq. Term. : Yes [=] Block0............ 00148040 (auto detect)
Block0 : 0x00148040 [=] Downlink mode..... default/fixed bit length
Downlink Mode : default/fixed bit length [=] Password set...... No
``` ```
``` ```
[usb] pm3 --> lf t55xx info [usb] pm3 --> lf t55xx info
@ -647,28 +655,27 @@ password set (if not, review and get you card back to this state).
result: result:
``` ```
-- T55x7 Configuration & Tag Information -------------------- [=] --- T55x7 Configuration & Information ---------
------------------------------------------------------------- [=] Safer key : 0
Safer key : 0 [=] reserved : 0
reserved : 0 [=] Data bit rate : 5 - RF/64
Data bit rate : 5 - RF/64 [=] eXtended mode : No
eXtended mode : No [=] Modulation : 8 - Manchester
Modulation : 8 - Manchester [=] PSK clock frequency : 0 - RF/2
PSK clock frequency : 0 - RF/2 [=] AOR - Answer on Request : No
AOR - Answer on Request : No [=] OTP - One Time Pad : No
OTP - One Time Pad : No [=] Max block : 2
Max block : 2 [=] Password mode : No
Password mode : No [=] Sequence Terminator : No
Sequence Terminator : No [=] Fast Write : No
Fast Write : No [=] Inverse data : No
Inverse data : No [=] POR-Delay : No
POR-Delay : No [=] -------------------------------------------------------------
------------------------------------------------------------- [=] Raw Data - Page 0, block 0
Raw Data - Page 0 [=] 00148040 - 00000000000101001000000001000000
Block 0 : 0x00148040 00000000000101001000000001000000 [=] --- Fingerprint ------------
[+] Config block match : EM unique, Paxton
Config block match : EM unique, Paxton
-------------------------------------------------------------
``` ```
We can see that the info gave us more information and confirmed what 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 we decoded by hand. But remember, the detect is still needed so the

View file

@ -561,7 +561,7 @@ EM is ASK
HID Prox is FSK HID Prox is FSK
Indala is PSK Indala is PSK
pm3 --> lf t55xx config FSK pm3 --> lf t55xx config --FSK
``` ```
Set timings to default Set timings to default
@ -579,7 +579,7 @@ Write to T55xx block
b <block> : block number to write. Between 0-7 b <block> : block number to write. Between 0-7
d <data> : 4 bytes of data to write (8 hex characters) d <data> : 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 Wipe a T55xx tag and set defaults

View file

@ -1,5 +1,4 @@
clear clear
pref
analyse foo analyse foo
data biphaserawdecode data biphaserawdecode
data detectclock data detectclock
@ -32,7 +31,6 @@ data print
data samples data samples
data setdebugmode data setdebugmode
data tune data tune
hf 14b sriwrite
hf 15 dump hf 15 dump
hf 15 info hf 15 info
hf 15 raw hf 15 raw
@ -85,21 +83,6 @@ hf mf cview
hf mf gen3uid hf mf gen3uid
hf mf gen3blk hf mf gen3blk
hf mf gen3freeze hf mf gen3freeze
lf em 410x
lf em 4x05
lf em 4x50
lf em 4x70
lf hitag reader lf hitag reader
lf hitag sim
lf hitag writer lf hitag writer
lf hitag dump 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

View file

@ -60,8 +60,8 @@ Standard password is normally (for T55xx): AA55BBBB
# Restore page1 data # Restore page1 data
``` ```
lf t55xx write b 1 d E0150A48 1 lf t55xx write -b 1 -d E0150A48 --pg1
If t55xx write b 2 d 2D782308 1 If t55xx write -b 2 -d 2D782308 --pg1
``` ```
# Sniffing the comms # 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. -- 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 lf t55xx sniff
-- if you have a save trace from before, try -- if you have a save trace from before, try
data load -f xxxxxxx.pm3 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. 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.

View file

@ -12,10 +12,9 @@ Check column "offline" for their availability.
|------- |------- |----------- |------- |------- |-----------
|`auto `|N |`Automated detection process for unknown tags` |`auto `|N |`Automated detection process for unknown tags`
|`clear `|Y |`Clear screen` |`clear `|Y |`Clear screen`
|`help `|Y |`This help. Use '<command> help' for details of a particular command.` |`help `|Y |`Use '<command> help' for details of a particular command.`
|`hints `|Y |`Turn hints on / off` |`hints `|Y |`Turn hints on / off`
|`msleep `|Y |`Add a pause in milliseconds` |`msleep `|Y |`Add a pause in milliseconds`
|`pref `|Y |`Edit preferences`
|`rem `|Y |`Add a text line in log file` |`rem `|Y |`Add a text line in log file`
|`quit `|Y |`` |`quit `|Y |``
|`exit `|Y |`Exit program` |`exit `|Y |`Exit program`
@ -606,10 +605,80 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`lf em help `|Y |`This help` |`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 410x
|`lf em 4x70 `|Y |`EM 4070 / 4170 commands...`
{ 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 ### lf fdxb
@ -673,6 +742,7 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`lf hitag help `|Y |`This help` |`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 list `|N |`List Hitag trace history`
|`lf hitag info `|N |`Tag information` |`lf hitag info `|N |`Tag information`
|`lf hitag reader `|N |`Act like a Hitag Reader` |`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 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 p1detect `|N |`Try detecting if this is a t55xx tag by reading page 1`
|`lf t55xx read `|N |`Read T55xx block data` |`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 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 trace `|Y |`Show T55x7 traceability data (page 1/ blk 0-1)`
|`lf t55xx wakeup `|N |`Send AOR wakeup command` |`lf t55xx wakeup `|N |`Send AOR wakeup command`
@ -953,13 +1023,34 @@ Check column "offline" for their availability.
|------- |------- |----------- |------- |------- |-----------
|`mem help `|Y |`This help` |`mem help `|Y |`This help`
|`mem baudrate `|N |`Set Flash memory Spi baudrate` |`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 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 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 ### reveng
{ CRC calculations from RevEng software... } { CRC calculations from RevEng software... }
@ -988,9 +1079,9 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`script help `|Y |`Usage info` |`script help `|Y |`This help`
|`script list `|Y |`List available scripts` |`script list `|Y |`List available scripts`
|`script run `|Y |`<name> -- execute a script` |`script run `|Y |`<name> - execute a script`
### trace ### 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)` |`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`

View file

@ -39,8 +39,8 @@ Page 3:
* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details * 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 dump it: `mem dump f page3_dump o 196608 l 65536`
* to erase it: * 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! * **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!
* It's possible to erase completely page 3 by erase the entire flash memory with the voluntarily undocumented command `mem wipe i`. * 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. * Updating keys dictionaries doesn't require to erase page 3.
## Page3 Layout ## 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) * length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure)
* **RSA SIGNATURE**, see below for details * **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 * 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 * 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. 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` You can verify it with: `mem info`
Here below is a sample output of a RDV4 device.
``` ```
[usb] pm3 --> mem info [usb] pm3 --> mem info
[=] --- Flash memory Information --------- [=] --- Flash memory Information ---------
[=] ID................... 25AD99A782A867D5
[=] SHA1................. 67C3B9BA2FA90AD4B283926B70017066C082C156
[+] Signature............ ( ok )
[=] ------------------------------------------------------------- [=] --- RDV4 RSA signature ---------------
[=] ID | xx xx xx xx xx xx xx xx [=] C7C7DF7FA3A2391A2B36E97D227C746ED8BB475E8766F54A13BAA9AAB29299BE
[=] SHA1 | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx [=] 37546AACCC29157ABF8AFBF3A1CFB24275442D565F7E996C6B08090528ADE25E
[=] RSA SIGNATURE | [=] ED1498E3089C72C68348D83CBD13F1247327BDBC9D75B09ECE3E051E19FE19BB
[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 [=] 98CB038757F2EDFD2DC5060D05C3296BC19A6F768290D555DFD50407E0E13A70
[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 [=] --- RDV4 RSA Public key --------------
[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 [=] Len.................. 128
[=] KEY length | 128 [=] Exponent............. 010001
[+] RSA key validation ok [=] Public key modulus N
[+] RSA Verification ok [=] 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`

View file

@ -61,7 +61,7 @@ GND | 6
3.3 | 1 3.3 | 1
# Where to find more information? # 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 ## Third party notes on using a BusPirate

View file

@ -373,9 +373,9 @@ It was needed to tune pm3 RX usart `maxtry` :
time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "lf read" time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "lf read"
6.28s 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 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 25.34s

View file

@ -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` 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. 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. if key isn't a hex number, the key is ignored.

View file

@ -56,6 +56,10 @@ struct version_information {
#define DBG_EXTENDED 4 // errors + info + debug + breaking debug messages #define DBG_EXTENDED 4 // errors + info + debug + breaking debug messages
extern int DBGLEVEL; extern int DBGLEVEL;
// tear-off
extern uint16_t tearoff_delay_us;
extern bool tearoff_enabled;
// reader voltage field detector // reader voltage field detector
#define MF_MINFIELDV 4000 #define MF_MINFIELDV 4000

View file

@ -384,6 +384,15 @@ typedef struct {
bool off; bool off;
} PACKED tearoff_params_t; } 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 // For the bootloader
#define CMD_DEVICE_INFO 0x0000 #define CMD_DEVICE_INFO 0x0000
//#define CMD_SETUP_WRITE 0x0001 //#define CMD_SETUP_WRITE 0x0001
@ -565,6 +574,8 @@ typedef struct {
#define CMD_LF_HITAGS_READ 0x0373 #define CMD_LF_HITAGS_READ 0x0373
#define CMD_LF_HITAGS_WRITE 0x0375 #define CMD_LF_HITAGS_WRITE 0x0375
#define CMD_LF_HITAG_ELOAD 0x0376
#define CMD_HF_ISO14443A_ANTIFUZZ 0x0380 #define CMD_HF_ISO14443A_ANTIFUZZ 0x0380
#define CMD_HF_ISO14443B_SIMULATE 0x0381 #define CMD_HF_ISO14443B_SIMULATE 0x0381
#define CMD_HF_ISO14443B_SNIFF 0x0382 #define CMD_HF_ISO14443B_SNIFF 0x0382

View file

@ -8,7 +8,7 @@ ifneq ($(SKIPPTHREAD),1)
MYLDLIBS += -lpthread MYLDLIBS += -lpthread
endif endif
BINS = mf_nonce_brute BINS = mf_nonce_brute mf_trace_brute
INSTALLTOOLS = $(BINS) INSTALLTOOLS = $(BINS)
include ../../Makefile.host include ../../Makefile.host
@ -21,3 +21,5 @@ ifneq (,$(findstring MINGW,$(platform)))
endif endif
mf_nonce_brute : $(OBJDIR)/mf_nonce_brute.o $(MYOBJS) mf_nonce_brute : $(OBJDIR)/mf_nonce_brute.o $(MYOBJS)
mf_trace_brute : $(OBJDIR)/mf_trace_brute.o $(MYOBJS)

View file

@ -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 +UID +nt_enc | +nr_enc +ar_enc | +at_enc | +encrypted next cmd
+nt_par_err +at_par_err +at_par_err +nt_par_err +at_par_err +at_par_err

View file

@ -14,10 +14,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <ctype.h>
#include "crapto1/crapto1.h" #include "crapto1/crapto1.h"
#include "protocol.h" #include "protocol.h"
#include "iso14443crc.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) #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 // a global mutex to prevent interlaced printing from different threads
@ -42,24 +49,149 @@ typedef struct thread_args {
bool ev1; bool ev1;
} targs; } 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, uint8_t cmds[8][2] = {
ISO14443A_CMD_WRITEBLOCK, {ISO14443A_CMD_READBLOCK, 18},
MIFARE_AUTH_KEYA, {ISO14443A_CMD_WRITEBLOCK, 18},
MIFARE_AUTH_KEYB, {MIFARE_AUTH_KEYA, 0},
MIFARE_CMD_INC, {MIFARE_AUTH_KEYB, 0},
MIFARE_CMD_DEC, {MIFARE_CMD_INC, 6},
MIFARE_CMD_RESTORE, {MIFARE_CMD_DEC, 6},
MIFARE_CMD_TRANSFER {MIFARE_CMD_RESTORE, 6},
{MIFARE_CMD_TRANSFER, 0}
}; };
int global_counter = 0; int global_counter = 0;
int global_fin_flag = 0;
int global_found = 0; int global_found = 0;
int global_found_candidate = 0; int global_found_candidate = 0;
uint64_t global_candiate_key = 0;
size_t thread_count = 2; 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) { static uint16_t parity_from_err(uint32_t data, uint16_t par_err) {
uint16_t par = 0; 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) { static bool checkValidCmd(uint32_t decrypted) {
uint8_t cmd = (decrypted >> 24) & 0xFF; uint8_t cmd = (decrypted >> 24) & 0xFF;
for (int i = 0; i < sizeof(cmds); ++i) { for (int i = 0; i < 8; ++i) {
if (cmd == cmds[i]) if (cmd == cmds[i][0])
return true; return true;
} }
return false; 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) { static bool checkCRC(uint32_t decrypted) {
uint8_t data[] = { uint8_t data[] = {
(decrypted >> 24) & 0xFF, (decrypted >> 24) & 0xFF,
@ -227,14 +379,12 @@ static void *brute_thread(void *arguments) {
uint32_t p64 = 0; uint32_t p64 = 0;
uint32_t count; uint32_t count;
int found = 0;
// TC == 4 ( // TC == 4 (
// threads calls 0 ev1 == false // threads calls 0 ev1 == false
// threads calls 0,1,2 ev1 == true // threads calls 0,1,2 ev1 == true
for (count = args->idx; count < 0xFFFF; count += thread_count - 1) { for (count = args->idx; count < 0xFFFF; count += thread_count - 1) {
found = global_found; if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) {
if (found) {
break; break;
} }
@ -266,7 +416,7 @@ static void *brute_thread(void *arguments) {
if (cmd_enc) { if (cmd_enc) {
uint32_t decrypted = ks4 ^ cmd_enc; uint32_t decrypted = ks4 ^ cmd_enc;
printf("CMD enc( %08x )\n", cmd_enc); printf("CMD enc( %08x )\n", cmd_enc);
printf(" dec(%08x)\t", decrypted); printf(" dec( %08x ) ", decrypted);
// check if cmd exists // check if cmd exists
uint8_t isOK = checkValidCmd(decrypted); uint8_t isOK = checkValidCmd(decrypted);
@ -275,12 +425,12 @@ static void *brute_thread(void *arguments) {
// Add a crc-check. // Add a crc-check.
isOK = checkCRC(decrypted); isOK = checkCRC(decrypted);
if (isOK == false) { if (isOK == false) {
printf("<-- not a valid cmd\n"); printf(_RED_("<-- not a valid cmd\n"));
pthread_mutex_unlock(&print_lock); pthread_mutex_unlock(&print_lock);
free(revstate); free(revstate);
continue; continue;
} else { } else {
printf("<-- Valid cmd\n"); printf("<-- valid cmd\n");
} }
} }
@ -293,32 +443,97 @@ static void *brute_thread(void *arguments) {
free(revstate); free(revstate);
if (args->ev1) { 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); __sync_fetch_and_add(&global_found_candidate, 1);
} else { } 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_found, 1);
} }
__sync_fetch_and_add(&global_candiate_key, key);
//release lock //release lock
pthread_mutex_unlock(&print_lock); pthread_mutex_unlock(&print_lock);
break;
} }
} }
free(args); free(args);
return NULL; 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) { static int usage(void) {
printf("\n");
printf("syntax: mf_nonce_brute <uid> <nt> <nt_par_err> <nr> <ar> <ar_par_err> <at> <at_par_err> [<next_command>]\n\n"); printf("syntax: mf_nonce_brute <uid> <nt> <nt_par_err> <nr> <ar> <ar_par_err> <at> <at_par_err> [<next_command>]\n\n");
printf(" example: nt in trace = 8c! 42 e6! 4e!\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 = 8c42e64e\n");
printf(" nt_par_err = 1011\n\n"); printf(" nt_par_err = 1011\n\n");
printf("\n expected outcome:\n"); printf("samples:\n");
printf(" KEY 0xFFFFFFFFFFFF == fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\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; return 1;
} }
int main(int argc, char *argv[]) { 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(); if (argc < 9) return usage();
@ -331,21 +546,27 @@ int main(int argc, char *argv[]) {
sscanf(argv[7], "%x", &at_enc); sscanf(argv[7], "%x", &at_enc);
sscanf(argv[8], "%x", &at_par_err); sscanf(argv[8], "%x", &at_par_err);
if (argc > 9) int enc_len = 0;
sscanf(argv[9], "%x", &cmd_enc); 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("----------- " _CYAN_("Phase 1") " ------------------------\n");
printf("uid:\t\t%08x\n", uid); printf("uid.................. %08x\n", uid);
printf("nt encrypted:\t%08x\n", nt_enc); printf("nt encrypted......... %08x\n", nt_enc);
printf("nt parity err:\t%04x\n", nt_par_err); printf("nt parity err........ %04x\n", nt_par_err);
printf("nr encrypted:\t%08x\n", nr_enc); printf("nr encrypted......... %08x\n", nr_enc);
printf("ar encrypted:\t%08x\n", ar_enc); printf("ar encrypted......... %08x\n", ar_enc);
printf("ar parity err:\t%04x\n", ar_par_err); printf("ar parity err........ %04x\n", ar_par_err);
printf("at encrypted:\t%08x\n", at_enc); printf("at encrypted......... %08x\n", at_enc);
printf("at parity err:\t%04x\n", at_par_err); printf("at parity err........ %04x\n", at_par_err);
if (argc > 9) if (argc > 9) {
printf("next cmd enc:\t%08x\n\n", cmd_enc); printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc, enc_len ,0));
}
clock_t t1 = clock(); clock_t t1 = clock();
uint16_t nt_par = parity_from_err(nt_enc, nt_par_err); 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; thread_count = 2;
#endif /* _WIN32 */ #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]; 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); 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: // wait for threads to terminate:
for (int i = 0; i < thread_count; ++i) for (int i = 0; i < thread_count; ++i)
pthread_join(threads[i], NULL); pthread_join(threads[i], NULL);
@ -394,10 +660,7 @@ int main(int argc, char *argv[]) {
printf("\nFailed to find a key\n\n"); printf("\nFailed to find a key\n\n");
} }
t1 = clock() - t1; out:
if (t1 > 0)
printf("Execution time: %.0f ticks\n", (float)t1);
// clean up mutex // clean up mutex
pthread_mutex_destroy(&print_lock); pthread_mutex_destroy(&print_lock);
return 0; return 0;

View file

@ -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 <inttypes.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include "ctype.h"
#include <time.h>
#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 <uid> <partial key> <nt enc> <nr enc> [<next_command + 18 bytes>]\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;
}

View file

@ -102,7 +102,8 @@ def selftests():
'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, 'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"},
{'name': "DESFire EV2", {'name': "DESFire EV2",
'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508", 'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508",
"045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"], "045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33",
"040D259A965B80","B158073A7100C88C3726F4299FA58311FC3CB18744686DE3F234928AD74578F5CAD7FCEC1DCB962ECC7CC000B8557B37F45B76DC6573A58F"],
'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, 'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"},
{'name': "DESFire EV3", {'name': "DESFire EV3",
'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6", 'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6",
@ -146,6 +147,7 @@ def selftests():
for c in curvenames: for c in curvenames:
for h in [None, "md5", "sha1", "sha256", "sha512"]: for h in [None, "md5", "sha1", "sha256", "sha512"]:
recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h) recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h)
if (len(recovered) == 1): if (len(recovered) == 1):
pk = recovered.pop() pk = recovered.pop()
pk = binascii.hexlify(pk).decode('utf8') pk = binascii.hexlify(pk).decode('utf8')