mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
Merge branch 'master' of https://github.com/RfidResearchGroup/proxmark3 into function/desfire-standalone
This commit is contained in:
commit
180577695e
67 changed files with 2957 additions and 1897 deletions
2
Makefile
2
Makefile
|
@ -251,7 +251,7 @@ print-%: ; @echo $* = $($*)
|
|||
|
||||
cliparser:
|
||||
# Get list of all commands
|
||||
cat doc/commands.md | grep -e ^\|\` | cut -f 2 -d "\`" | grep -v 'help\|list\|mem spiffs\|quit\|exit' | awk '{$$1=$$1};1' > cliparser_all_commands.tmp
|
||||
cat doc/commands.md | grep -e ^\|\` | cut -f 2 -d "\`" | grep -v 'help\|list\|script run\|quit\|exit' | awk '{$$1=$$1};1' > cliparser_all_commands.tmp
|
||||
# Get list of cliparserized commands
|
||||
grep -r CLIParserInit ./client/src/ | cut -f 2 -d "\"" | awk '{$$1=$$1};1' > cliparser_done.tmp
|
||||
# Determine commands that still need cliparser conversion
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
| ------------------- |:-------------------:| -------------------:|
|
||||
|[What has changed?](#what-has-changed) | **[Setup and build for Linux](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md)** | [Compilation Instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md)|
|
||||
|[Development](#development) | **[Important notes on ModemManager for Linux users](/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md)** | [Validating proxmark client functionality](/doc/md/Use_of_Proxmark/1_Validation.md) |
|
||||
|[Why didn't you base it on official Proxmark3 Master?](#why-didnt-you-base-it-on-official-proxmark3-master)| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)|
|
||||
|| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)|
|
||||
|[Proxmark3 GUI](#proxmark3-gui)|**[Setup and build for Windows](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md)**|[Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)|
|
||||
|[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) ||
|
||||
|[Donations](#Donations)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.md)|
|
||||
||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)|
|
||||
||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)|
|
||||
||**[JTAG](/doc/jtag_notes.md)**|[T55xx Guide](/doc/T5577_Guide.md)|
|
||||
||**[JTAG](/doc/jtag_notes.md)**|[T5577 Introduction Guide](/doc/T5577_Guide.md)|
|
||||
|
||||
|
||||
## Notes / helpful documents
|
||||
|
@ -154,10 +154,6 @@ To all distro, package maintainers, we tried to make your life easier.
|
|||
`make install` is now available and if you want to know more.
|
||||
- [Notes for maintainers](/doc/md/Development/Maintainers.md)
|
||||
|
||||
## Why didn't you base it on official Proxmark3 Master?
|
||||
|
||||
The separation from official Proxmark3 repo gives us a lot of freedom to create a firmware/client that suits the RDV40 features. We don't want to mess up the official Proxmark3 repo with RDV40 specific code.
|
||||
|
||||
## Proxmark3 GUI
|
||||
|
||||
The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer.
|
||||
|
|
|
@ -71,13 +71,13 @@
|
|||
static void DownloadTraceInstructions(void) {
|
||||
Dbprintf("");
|
||||
Dbprintf("To get the trace from flash and display it:");
|
||||
Dbprintf("1. mem spiffs dump o "HF_14ASNIFF_LOGFILE" f trace.trc");
|
||||
Dbprintf("2. trace load trace.trc");
|
||||
Dbprintf("3. trace list 14a 1");
|
||||
Dbprintf("1. mem spiffs dump -s "HF_14ASNIFF_LOGFILE" -d trace.trc");
|
||||
Dbprintf("2. trace load -f trace.trc");
|
||||
Dbprintf("3. trace list -t 14a -1");
|
||||
}
|
||||
|
||||
void ModInfo(void) {
|
||||
DbpString("hf_14asniff: standalone 'hf 14a sniff', storing in flashmem");
|
||||
DbpString(" ISO14443a sniff, storing in flashmem");
|
||||
DownloadTraceInstructions();
|
||||
}
|
||||
|
||||
|
|
|
@ -130,27 +130,27 @@ static void download_instructions(uint8_t t) {
|
|||
switch (t) {
|
||||
case ICE_STATE_FULLSIM: {
|
||||
DbpString("The emulator memory was saved to SPIFFS");
|
||||
DbpString("1. " _YELLOW_("mem spiffs dump o " HF_ICLASS_FULLSIM_MOD_BIN " f " HF_ICLASS_FULLSIM_MOD" e"));
|
||||
DbpString("1. " _YELLOW_("mem spiffs dump -s " HF_ICLASS_FULLSIM_MOD_BIN " -d " HF_ICLASS_FULLSIM_MOD" -e"));
|
||||
DbpString("2. " _YELLOW_("hf iclass view -f " HF_ICLASS_FULLSIM_MOD_BIN));
|
||||
break;
|
||||
}
|
||||
case ICE_STATE_ATTACK: {
|
||||
DbpString("The collected data was saved to SPIFFS. The file names below may differ");
|
||||
DbpString("1. " _YELLOW_("mem spiffs tree"));
|
||||
DbpString("2. " _YELLOW_("mem spiffs dump o " HF_ICLASS_ATTACK_BIN " f " HF_ICLASS_ATTACK_BIN));
|
||||
DbpString("2. " _YELLOW_("mem spiffs dump -s " HF_ICLASS_ATTACK_BIN " -d " HF_ICLASS_ATTACK_BIN));
|
||||
DbpString("3. " _YELLOW_("hf iclass loclass -f " HF_ICLASS_ATTACK_BIN));
|
||||
break;
|
||||
}
|
||||
case ICE_STATE_READER: {
|
||||
DbpString("The found tags was saved to SPIFFS");
|
||||
DbpString("1. " _YELLOW_("mem spiffs tree"));
|
||||
DbpString("2. " _YELLOW_("mem spiffs dump h"));
|
||||
DbpString("2. " _YELLOW_("mem spiffs dump -h"));
|
||||
break;
|
||||
}
|
||||
case ICE_STATE_DUMP_SIM: {
|
||||
DbpString("The found tag will be dumped to " HF_ICALSSS_READSIM_TEMP_BIN);
|
||||
DbpString("1. " _YELLOW_("mem spiffs tree"));
|
||||
DbpString("2. " _YELLOW_("mem spiffs dump h"));
|
||||
DbpString("2. " _YELLOW_("mem spiffs dump -h"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
*
|
||||
* To delete a dump file from flash:
|
||||
*
|
||||
* 1. mem spiffs remove hf-legic-XXYYZZWW-dump.bin
|
||||
* 1. mem spiffs remove -f hf-legic-XXYYZZWW-dump.bin
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -50,7 +50,7 @@ static void DownloadLogInstructions(void) {
|
|||
Dbprintf("[=] " _YELLOW_("-") " mem spiffs tree");
|
||||
Dbprintf("");
|
||||
Dbprintf("[=] To save a dump file from flash to client:");
|
||||
Dbprintf("[=] " _YELLOW_("-") " mem spiffs dump o hf-legic-UID-dump.bin f hf-legic-UID-dump.bin");
|
||||
Dbprintf("[=] " _YELLOW_("-") " mem spiffs dump -s hf-legic-UID-dump.bin -d hf-legic-UID-dump.bin");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
*
|
||||
* To retrieve log file from flash:
|
||||
*
|
||||
* 1. mem spiffs dump o lf_hidcollect.log f lf_hidcollect.log
|
||||
* 1. mem spiffs dump -s lf_hidcollect.log -d lf_hidcollect.log
|
||||
* Copies log file from flash to your client.
|
||||
*
|
||||
* 2. exit the Proxmark3 client
|
||||
|
@ -48,7 +48,7 @@
|
|||
*
|
||||
* To delete the log file from flash:
|
||||
*
|
||||
* 1. mem spiffs remove lf_hidcollect.log
|
||||
* 1. mem spiffs remove -f lf_hidcollect.log
|
||||
*/
|
||||
|
||||
#define LF_HIDCOLLECT_LOGFILE "lf_hidcollect.log"
|
||||
|
@ -57,7 +57,7 @@
|
|||
static void DownloadLogInstructions(void) {
|
||||
Dbprintf("");
|
||||
Dbprintf("[=] To get the logfile from flash and display it:");
|
||||
Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump o "LF_HIDCOLLECT_LOGFILE" f "LF_HIDCOLLECT_LOGFILE);
|
||||
Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump -s "LF_HIDCOLLECT_LOGFILE" -d "LF_HIDCOLLECT_LOGFILE);
|
||||
Dbprintf("[=] " _YELLOW_("2.") " exit proxmark3 client");
|
||||
Dbprintf("[=] " _YELLOW_("3.") " cat "LF_HIDCOLLECT_LOGFILE);
|
||||
}
|
||||
|
|
|
@ -42,25 +42,25 @@
|
|||
* - LED D: unmounting/sync'ing flash (normally < 100ms)
|
||||
*
|
||||
* 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:
|
||||
* - mem spiffs dump o lf_em4x50_passwords.log f <filename>
|
||||
* - mem spiffs dump -s lf_em4x50_passwords.log
|
||||
*
|
||||
* 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
|
||||
* the lab connected to PM3 client before taking it into the field.
|
||||
*
|
||||
* To delete the input file from flash:
|
||||
* - mem spiffs remove lf_em4x50_simulate.eml
|
||||
* - mem spiffs remove -f lf_em4x50_simulate.eml
|
||||
*
|
||||
* To delete the log file from flash:
|
||||
* - mem spiffs remove lf_em4x50_passwords.log
|
||||
* - mem spiffs remove -f lf_em4x50_passwords.log
|
||||
*
|
||||
* To delete the log file from flash:
|
||||
* - mem spiffs remove lf_em4x50_collect.log
|
||||
* - mem spiffs remove -f lf_em4x50_collect.log
|
||||
*/
|
||||
|
||||
#define STATE_SIM 0
|
||||
|
@ -77,14 +77,14 @@ static void LoadDataInstructions(const char *inputfile) {
|
|||
Dbprintf("To load datafile to flash and display it:");
|
||||
Dbprintf("1. edit input file %s", inputfile);
|
||||
Dbprintf("2. start proxmark3 client");
|
||||
Dbprintf("3. mem spiffs load f <filename> o %s", inputfile);
|
||||
Dbprintf("3. mem spiffs upload -s <filename> -d %s", inputfile);
|
||||
Dbprintf("4. start standalone mode");
|
||||
}
|
||||
|
||||
static void DownloadLogInstructions(const char *logfile) {
|
||||
Dbprintf("");
|
||||
Dbprintf("To get the logfile from flash and display it:");
|
||||
Dbprintf("1. mem spiffs dump o %s f <filename>", logfile);
|
||||
Dbprintf("1. mem spiffs dump -s %s", logfile);
|
||||
Dbprintf("2. exit proxmark3 client");
|
||||
Dbprintf("3. cat <filename>");
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ If your mode is using one of the unique features of the RDV4, add it to the prop
|
|||
```
|
||||
STANDALONE_MODES_REQ_SMARTCARD :=
|
||||
STANDALONE_MODES_REQ_FLASH :=
|
||||
STANDALONE_MODES_REQ_BT :=
|
||||
```
|
||||
|
||||
## Update MAKEFILE.INC
|
||||
|
|
112
armsrc/appmain.c
112
armsrc/appmain.c
|
@ -73,8 +73,8 @@ extern uint32_t _stack_start, _stack_end;
|
|||
struct common_area common_area __attribute__((section(".commonarea")));
|
||||
static int button_status = BUTTON_NO_CLICK;
|
||||
static bool allow_send_wtx = false;
|
||||
static uint16_t tearoff_delay_us = 0;
|
||||
static bool tearoff_enabled = false;
|
||||
uint16_t tearoff_delay_us = 0;
|
||||
bool tearoff_enabled = false;
|
||||
|
||||
int tearoff_hook(void) {
|
||||
if (tearoff_enabled) {
|
||||
|
@ -2078,7 +2078,7 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result);
|
||||
}
|
||||
// Trigger a finish downloading signal with an ACK frame
|
||||
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
||||
reply_ng(CMD_SPIFFS_DOWNLOAD, PM3_SUCCESS, NULL, 0);
|
||||
LED_B_OFF();
|
||||
break;
|
||||
}
|
||||
|
@ -2087,78 +2087,94 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
uint8_t filename[32];
|
||||
uint8_t *pfilename = packet->data.asBytes;
|
||||
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN);
|
||||
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs STAT : %s", filename);
|
||||
if (DBGLEVEL >= DBG_DEBUG) {
|
||||
Dbprintf("Filename received for spiffs STAT : %s", filename);
|
||||
}
|
||||
|
||||
int changed = rdv40_spiffs_lazy_mount();
|
||||
uint32_t size = size_in_spiffs((char *)filename);
|
||||
if (changed) rdv40_spiffs_lazy_unmount();
|
||||
reply_mix(CMD_ACK, size, 0, 0, 0, 0);
|
||||
if (changed) {
|
||||
rdv40_spiffs_lazy_unmount();
|
||||
}
|
||||
|
||||
reply_ng(CMD_SPIFFS_STAT, PM3_SUCCESS, (uint8_t *)&size, sizeof(uint32_t));
|
||||
LED_B_OFF();
|
||||
break;
|
||||
}
|
||||
case CMD_SPIFFS_REMOVE: {
|
||||
LED_B_ON();
|
||||
uint8_t filename[32];
|
||||
uint8_t *pfilename = packet->data.asBytes;
|
||||
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN);
|
||||
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs REMOVE : %s", filename);
|
||||
rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
|
||||
struct p {
|
||||
uint8_t len;
|
||||
uint8_t fn[32];
|
||||
} PACKED;
|
||||
struct p *payload = (struct p *) packet->data.asBytes;
|
||||
|
||||
if (DBGLEVEL >= DBG_DEBUG) {
|
||||
Dbprintf("Filename received for spiffs REMOVE : %s", payload->fn);
|
||||
}
|
||||
|
||||
rdv40_spiffs_remove((char *)payload->fn, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
reply_ng(CMD_SPIFFS_REMOVE, PM3_SUCCESS, NULL, 0);
|
||||
LED_B_OFF();
|
||||
break;
|
||||
}
|
||||
case CMD_SPIFFS_RENAME: {
|
||||
LED_B_ON();
|
||||
uint8_t src[32];
|
||||
uint8_t dest[32];
|
||||
uint8_t *pfilename = packet->data.asBytes;
|
||||
char *token;
|
||||
token = strtok((char *)pfilename, ",");
|
||||
strncpy((char *)src, token, sizeof(src) - 1);
|
||||
token = strtok(NULL, ",");
|
||||
strncpy((char *)dest, token, sizeof(dest) - 1);
|
||||
struct p {
|
||||
uint8_t slen;
|
||||
uint8_t src[32];
|
||||
uint8_t dlen;
|
||||
uint8_t dest[32];
|
||||
} PACKED;
|
||||
struct p *payload = (struct p *) packet->data.asBytes;
|
||||
|
||||
if (DBGLEVEL >= DBG_DEBUG) {
|
||||
Dbprintf("Filename received as source for spiffs RENAME : %s", src);
|
||||
Dbprintf("Filename received as destination for spiffs RENAME : %s", dest);
|
||||
Dbprintf("SPIFFS RENAME");
|
||||
Dbprintf("Source........ %s", payload->src);
|
||||
Dbprintf("Destination... %s", payload->dest);
|
||||
}
|
||||
rdv40_spiffs_rename((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
rdv40_spiffs_rename((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
reply_ng(CMD_SPIFFS_RENAME, PM3_SUCCESS, NULL, 0);
|
||||
LED_B_OFF();
|
||||
break;
|
||||
}
|
||||
case CMD_SPIFFS_COPY: {
|
||||
LED_B_ON();
|
||||
uint8_t src[32];
|
||||
uint8_t dest[32];
|
||||
uint8_t *pfilename = packet->data.asBytes;
|
||||
char *token;
|
||||
token = strtok((char *)pfilename, ",");
|
||||
strncpy((char *)src, token, sizeof(src) - 1);
|
||||
token = strtok(NULL, ",");
|
||||
strncpy((char *)dest, token, sizeof(dest) - 1);
|
||||
struct p {
|
||||
uint8_t slen;
|
||||
uint8_t src[32];
|
||||
uint8_t dlen;
|
||||
uint8_t dest[32];
|
||||
} PACKED;
|
||||
struct p *payload = (struct p *) packet->data.asBytes;
|
||||
|
||||
if (DBGLEVEL >= DBG_DEBUG) {
|
||||
Dbprintf("Filename received as source for spiffs COPY : %s", src);
|
||||
Dbprintf("Filename received as destination for spiffs COPY : %s", dest);
|
||||
Dbprintf("SPIFFS COPY");
|
||||
Dbprintf("Source........ %s", payload->src);
|
||||
Dbprintf("Destination... %s", payload->dest);
|
||||
}
|
||||
rdv40_spiffs_copy((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
rdv40_spiffs_copy((char *)payload->src, (char *)payload->dest, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
reply_ng(CMD_SPIFFS_COPY, PM3_SUCCESS, NULL, 0);
|
||||
LED_B_OFF();
|
||||
break;
|
||||
}
|
||||
case CMD_SPIFFS_WRITE: {
|
||||
LED_B_ON();
|
||||
uint8_t filename[32];
|
||||
uint32_t append = packet->oldarg[0];
|
||||
uint32_t size = packet->oldarg[1];
|
||||
uint8_t *data = packet->data.asBytes;
|
||||
uint8_t *pfilename = packet->data.asBytes;
|
||||
memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN);
|
||||
data += SPIFFS_OBJ_NAME_LEN;
|
||||
|
||||
if (DBGLEVEL >= DBG_DEBUG) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append);
|
||||
flashmem_write_t *payload = (flashmem_write_t *)packet->data.asBytes;
|
||||
|
||||
if (!append) {
|
||||
rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
} else {
|
||||
rdv40_spiffs_append((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
if (DBGLEVEL >= DBG_DEBUG) {
|
||||
Dbprintf("SPIFFS WRITE, dest `%s` with APPEND set to: %c", payload->fn, payload->append ? 'Y' : 'N');
|
||||
}
|
||||
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
|
||||
|
||||
if (payload->append) {
|
||||
rdv40_spiffs_append((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
} else {
|
||||
rdv40_spiffs_write((char *) payload->fn, payload->data, payload->bytes_in_packet, RDV40_SPIFFS_SAFETY_SAFE);
|
||||
}
|
||||
|
||||
reply_ng(CMD_SPIFFS_WRITE, PM3_SUCCESS, NULL, 0);
|
||||
LED_B_OFF();
|
||||
break;
|
||||
}
|
||||
|
@ -2202,6 +2218,10 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
Flash_CheckBusy(BUSY_TIMEOUT);
|
||||
Flash_WriteEnable();
|
||||
Flash_Erase4k(3, 0xB);
|
||||
} else if (startidx == FLASH_MEM_SIGNATURE_OFFSET) {
|
||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
||||
Flash_WriteEnable();
|
||||
Flash_Erase4k(3, 0xF);
|
||||
}
|
||||
|
||||
res = Flash_Write(startidx, data, len);
|
||||
|
|
|
@ -1881,7 +1881,7 @@ void iClass_WriteBlock(uint8_t *msg) {
|
|||
|
||||
iclass_send_as_reader(write, write_len, &start_time, &eof_time);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
res = false;
|
||||
switch_off();
|
||||
if (payload->req.send_reply)
|
||||
|
|
|
@ -2972,7 +2972,7 @@ void ReaderIso14443a(PacketCommandNG *c) {
|
|||
}
|
||||
}
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
FpgaDisableTracing();
|
||||
reply_mix(CMD_ACK, 0, 0, 0, NULL, 0);
|
||||
} else {
|
||||
|
|
|
@ -2092,7 +2092,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) {
|
|||
uint32_t eof_time = 0;
|
||||
CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
FpgaDisableTracing();
|
||||
reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0);
|
||||
} else {
|
||||
|
|
|
@ -1477,7 +1477,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t
|
|||
tosend_t *ts = get_tosend();
|
||||
TransmitTo15693Tag(ts->buf, ts->max, &start_time);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
|
||||
res = PM3_ETEAROFF;
|
||||
|
||||
|
@ -1598,7 +1598,7 @@ void ReaderIso15693(uint32_t parameter) {
|
|||
uint32_t eof_time;
|
||||
int recvlen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time);
|
||||
|
||||
if (recvlen == PM3_ETEAROFF) { // tearoff occured
|
||||
if (recvlen == PM3_ETEAROFF) { // tearoff occurred
|
||||
reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0);
|
||||
} else {
|
||||
|
||||
|
@ -1928,7 +1928,7 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint
|
|||
uint32_t start_time = 0;
|
||||
int recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time);
|
||||
|
||||
if (recvlen == PM3_ETEAROFF) { // tearoff occured
|
||||
if (recvlen == PM3_ETEAROFF) { // tearoff occurred
|
||||
reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0);
|
||||
} else {
|
||||
|
||||
|
|
|
@ -2639,7 +2639,7 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) {
|
|||
|
||||
SendForward(len, false);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
StopTicks();
|
||||
reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_ETEAROFF, NULL, 0);
|
||||
} else {
|
||||
|
@ -2681,7 +2681,7 @@ void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd) {
|
|||
|
||||
SendForward(len, false);
|
||||
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
|
||||
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
|
||||
StopTicks();
|
||||
reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_ETEAROFF, NULL, 0);
|
||||
} else {
|
||||
|
|
|
@ -2713,20 +2713,15 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) {
|
|||
//
|
||||
// Tear-off attack against MFU.
|
||||
// - Moebius et al
|
||||
void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) {
|
||||
void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_testwrite) {
|
||||
uint8_t blockNo = blno;
|
||||
uint8_t data_fullwrite[4] = {0x00};
|
||||
uint8_t data_testwrite[4] = {0x00};
|
||||
memcpy(data_fullwrite, datain, 4);
|
||||
memcpy(data_testwrite, datain + 4, 4);
|
||||
|
||||
if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off");
|
||||
|
||||
if (tearoff_time > 43000)
|
||||
tearoff_time = 43000;
|
||||
|
||||
MifareUWriteBlockEx(blockNo, 0, data_fullwrite, false);
|
||||
|
||||
tearoff_delay_us = tearoff_time;
|
||||
tearoff_enabled = true;
|
||||
|
||||
LEDsoff();
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
@ -2750,15 +2745,9 @@ void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) {
|
|||
return;
|
||||
};
|
||||
// send
|
||||
ReaderTransmit(cmd, sizeof(cmd), NULL);
|
||||
|
||||
// Wait before cutting power. aka tear-off
|
||||
LED_D_ON();
|
||||
|
||||
SpinDelayUsPrecision(tearoff_time);
|
||||
if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!"));
|
||||
switch_off();
|
||||
|
||||
ReaderTransmit(cmd, sizeof(cmd), NULL);
|
||||
tearoff_hook();
|
||||
reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -208,15 +208,12 @@ static void remove_from_spiffs(const char *filename) {
|
|||
Dbprintf("errno %i\n", SPIFFS_errno(&fs));
|
||||
}
|
||||
|
||||
static spiffs_stat stat_in_spiffs(const char *filename) {
|
||||
spiffs_stat s;
|
||||
if (SPIFFS_stat(&fs, filename, &s) < 0)
|
||||
Dbprintf("errno %i\n", SPIFFS_errno(&fs));
|
||||
return s;
|
||||
}
|
||||
|
||||
uint32_t size_in_spiffs(const char *filename) {
|
||||
spiffs_stat s = stat_in_spiffs(filename);
|
||||
spiffs_stat s;
|
||||
if (SPIFFS_stat(&fs, filename, &s) < 0) {
|
||||
Dbprintf("errno %i\n", SPIFFS_errno(&fs));
|
||||
return 0;
|
||||
}
|
||||
return s.size;
|
||||
}
|
||||
|
||||
|
|
|
@ -4639,11 +4639,13 @@ void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) {
|
|||
/* print GNU style [OPTION] string */
|
||||
arg_print_gnuswitch(fp, table);
|
||||
|
||||
size_t len = 0;
|
||||
|
||||
/* print remaining options in abbreviated style */
|
||||
for (tabindex = 0;
|
||||
table[tabindex] && !(table[tabindex]->flag & ARG_TERMINATOR);
|
||||
tabindex++) {
|
||||
char syntax[200] = "";
|
||||
char syntax[400] = "";
|
||||
const char *shortopts, *longopts, *datatype;
|
||||
|
||||
/* skip short options without arg values (they were printed by arg_print_gnu_switch) */
|
||||
|
@ -4681,6 +4683,12 @@ void arg_print_syntax(FILE *fp, void * *argtable, const char *suffix) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
len += strlen(syntax);
|
||||
if (len > 60) {
|
||||
fprintf(fp, "\n ");
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (suffix)
|
||||
|
|
|
@ -1296,3 +1296,75 @@ ff9a84635bd2
|
|||
f1a1239a4487
|
||||
#
|
||||
b882fd4a9f78
|
||||
CD7FFFF81C4A
|
||||
AA0857C641A3
|
||||
C8AACD7CF3D1
|
||||
9FFDA233B496
|
||||
26B85DCA4321
|
||||
D4B2D140CB2D
|
||||
A7395CCB42A0
|
||||
541C417E57C0
|
||||
D14E615E0545
|
||||
69D92108C8B5
|
||||
703265497350
|
||||
D75971531042
|
||||
10510049D725
|
||||
35C649004000
|
||||
5B0C7EC83645
|
||||
05F5EC05133C
|
||||
521B517352C7
|
||||
94B6A644DFF6
|
||||
2CA4A4D68B8E
|
||||
A7765C952DDF
|
||||
E2F14D0A0E28
|
||||
DC018FC1D126
|
||||
4927C97F1D57
|
||||
046154274C11
|
||||
155332417E00
|
||||
6B13935CD550
|
||||
C151D998C669
|
||||
D973D917A4C7
|
||||
130662240200
|
||||
9386E2A48280
|
||||
52750A0E592A
|
||||
541C417E57C0
|
||||
D14E615E0545
|
||||
075D1A4DD323
|
||||
32CA52054416
|
||||
460661C93045
|
||||
5429D67E1F57
|
||||
0C734F230E13
|
||||
1F0128447C00
|
||||
411053C05273
|
||||
42454C4C4147
|
||||
C428C4550A75
|
||||
730956C72BC2
|
||||
28D70900734C
|
||||
4F75030AD12B
|
||||
6307417353C1
|
||||
D65561530174
|
||||
D1F71E05AD9D
|
||||
F7FA2F629BB1
|
||||
0E620691B9FE
|
||||
43E69C28F08C
|
||||
735175696421
|
||||
424C0FFBF657
|
||||
D01AFEEB890A
|
||||
75CCB59C9BED
|
||||
4B791BEA7BCC
|
||||
51E97FFF51E9
|
||||
E7316853E731
|
||||
5C8FF9990DA2
|
||||
00460740D722
|
||||
35D152154017
|
||||
5D0762D13401
|
||||
0F35D5660653
|
||||
1170553E4304
|
||||
0C4233587119
|
||||
F678905568C3
|
||||
50240A68D1D8
|
||||
69D92108C8B5
|
||||
2E71D3BD262A
|
||||
540D5E6355CC
|
||||
D1417E431949
|
||||
4BF6DE347FB6
|
|
@ -76,7 +76,7 @@ local function main(args)
|
|||
|
||||
if removeflag then
|
||||
print('Deleting file '..filename.. ' from SPIFFS if exists')
|
||||
core.console("mem spiffs remove " ..filename)
|
||||
core.console("mem spiffs remove -f " ..filename)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ The outlined procedure is as following:
|
|||
-- manchester
|
||||
-- bit rate
|
||||
|
||||
"lf t55xx write b 0 d 00008040"
|
||||
"lf t55xx write -b 0 -d 00008040"
|
||||
"lf t55xx detect"
|
||||
"lf t55xx info"
|
||||
|
||||
|
@ -118,7 +118,7 @@ local function test()
|
|||
elseif _ == 1 then
|
||||
|
||||
local config = pcmd:format(config1, y, config2)
|
||||
dbg(('lf t55xx write b 0 d %s'):format(config))
|
||||
dbg(('lf t55xx write -b 0 -d %s'):format(config))
|
||||
local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags)
|
||||
|
||||
local wc = Command:newNG{cmd = cmds.CMD_LF_T55XX_WRITEBL, data = data}
|
||||
|
|
|
@ -14,7 +14,7 @@ The outlined procedure is as following:
|
|||
--BIPHASE 00010040
|
||||
--
|
||||
|
||||
"lf t55xx write b 0 d 00010040"
|
||||
"lf t55xx write -b 0 -d 00010040"
|
||||
"lf t55xx detect"
|
||||
"lf t55xx info"
|
||||
|
||||
|
@ -112,7 +112,7 @@ local function test()
|
|||
elseif _ == 1 then
|
||||
|
||||
local config = pcmd:format(config1, y, config2)
|
||||
dbg(('lf t55xx write b 0 d %s'):format(config))
|
||||
dbg(('lf t55xx write -b 0 -d %s'):format(config))
|
||||
|
||||
local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ The outlined procedure is as following:
|
|||
-- FSK1
|
||||
-- bit rate
|
||||
|
||||
"lf t55xx write b 0 d 00007040"
|
||||
"lf t55xx write -b 0 -d 00007040"
|
||||
"lf t55xx detect"
|
||||
"lf t55xx info"
|
||||
|
||||
|
@ -114,7 +114,7 @@ local function test(modulation)
|
|||
elseif _ == 1 then
|
||||
|
||||
local config = pcmd:format(config1, y, modulation, config2)
|
||||
dbg(('lf t55xx write b 0 d %s'):format(config))
|
||||
dbg(('lf t55xx write -b 0 -d %s'):format(config))
|
||||
local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags)
|
||||
|
||||
local wc = Command:newNG{cmd = cmds.CMD_LF_T55XX_WRITEBL, data = data}
|
||||
|
|
|
@ -11,7 +11,7 @@ desc = [[
|
|||
This script will program a T55x7 TAG with the configuration: block 0x00 data 0x00088040
|
||||
The outlined procedure is as following:
|
||||
|
||||
"lf t55xx write b 0 d 00088040"
|
||||
"lf t55xx write -b 0 -d 00088040"
|
||||
"lf t55xx detect"
|
||||
"lf t55xx info"
|
||||
|
||||
|
@ -118,7 +118,7 @@ local function test(modulation)
|
|||
dbg('Writing to T55x7 TAG')
|
||||
|
||||
local config = cmd:format(bitrate, modulation, clockrate)
|
||||
dbg(('lf t55xx write b 0 d %s'):format(config))
|
||||
dbg(('lf t55xx write -b 0 -d %s'):format(config))
|
||||
|
||||
local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(config, 32), password, block, flags)
|
||||
|
||||
|
|
|
@ -17,10 +17,10 @@ It will then try to detect and read back those block data and compare if read da
|
|||
|
||||
lf t55xx wipe
|
||||
lf t55xx detect
|
||||
lf t55xx write b 1 d 00000000
|
||||
lf t55xx write b 2 d ffffffff
|
||||
lf t55xx write b 3 d 80000000
|
||||
lf t55xx write b 4 d 00000001
|
||||
lf t55xx write -b 1 -d 00000000
|
||||
lf t55xx write -b 2 -d ffffffff
|
||||
lf t55xx write -b 3 -d 80000000
|
||||
lf t55xx write -b 4 -d 00000001
|
||||
|
||||
Loop:
|
||||
|
||||
|
@ -278,7 +278,7 @@ local function WipeCard()
|
|||
core.console('rem [ERR:DETECT:WIPED] Failed to detect after wipe')
|
||||
return false
|
||||
else
|
||||
local wipe_data_cmd = 'lf t55xx write b %s d %s'
|
||||
local wipe_data_cmd = 'lf t55xx write -b %s -d %s'
|
||||
for _ = 1, #data_blocks_cmds do
|
||||
local val = data_blocks_cmds[_]
|
||||
local c = string.format(wipe_data_cmd, _, val)
|
||||
|
@ -321,7 +321,7 @@ local function test(modulation)
|
|||
core.clearCommandBuffer()
|
||||
|
||||
-- Write Config block
|
||||
dbg(('lf t55xx write b 0 d %s'):format(p_config_cmd))
|
||||
dbg(('lf t55xx write -b 0 -d %s'):format(p_config_cmd))
|
||||
|
||||
local data = ('%s%s%s%s'):format(utils.SwapEndiannessStr(p_config_cmd, 32), password, block, flags)
|
||||
|
||||
|
|
|
@ -251,15 +251,7 @@ static int usage_data_buffclear(void) {
|
|||
PrintAndLogEx(NORMAL, " h This help");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_data_fsktonrz(void) {
|
||||
PrintAndLogEx(NORMAL, "Usage: data fsktonrz c <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)
|
||||
//by marshmellow
|
||||
|
@ -2295,36 +2287,27 @@ static int FSKToNRZ(int *data, size_t *dataLen, uint8_t clk, uint8_t LowToneFC,
|
|||
}
|
||||
|
||||
static int CmdFSKToNRZ(const char *Cmd) {
|
||||
// take clk, fc_low, fc_high
|
||||
// blank = auto;
|
||||
bool errors = false;
|
||||
char cmdp = 0;
|
||||
int clk = 0, fc_low = 10, fc_high = 8;
|
||||
while (param_getchar(Cmd, cmdp) != 0x00) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h':
|
||||
return usage_data_fsktonrz();
|
||||
case 'c':
|
||||
clk = param_get32ex(Cmd, cmdp + 1, 0, 10);
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'f':
|
||||
fc_high = param_get32ex(Cmd, cmdp + 1, 0, 10);
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'l':
|
||||
fc_low = param_get32ex(Cmd, cmdp + 1, 0, 10);
|
||||
cmdp += 2;
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
if (errors) break;
|
||||
}
|
||||
//Validations
|
||||
if (errors) return usage_data_fsktonrz();
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "data fsktonrz",
|
||||
"Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)\n"
|
||||
"Omitted values are autodetect instead",
|
||||
"data fsktonrz\n"
|
||||
"data fsktonrz -c 32 --low 8 --hi 10");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_int0("c", "clk", "<dec>", "clock"),
|
||||
arg_int0(NULL, "low", "<dec>", "low field clock"),
|
||||
arg_int0(NULL, "hi", "<dec>", "high field clock"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
int clk = arg_get_int_def(ctx, 1, 0);
|
||||
int fc_low = arg_get_int_def(ctx, 2, 0);
|
||||
int fc_high = arg_get_int_def(ctx, 3, 0);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
setClockGrid(0, 0);
|
||||
DemodBufferLen = 0;
|
||||
|
@ -2567,11 +2550,9 @@ static command_t CommandTable[] = {
|
|||
{"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "<shift> -- Shift 0 for Graphed wave + or - shift value"},
|
||||
{"timescale", CmdTimeScale, AlwaysAvailable, "Set a timescale to get a differential reading between the yellow and purple markers as time duration\n"},
|
||||
{"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"},
|
||||
|
||||
{"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"},
|
||||
{"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"},
|
||||
|
||||
|
||||
{"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"},
|
||||
{"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"},
|
||||
{"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"},
|
||||
|
|
|
@ -39,7 +39,10 @@ static int CmdHelp(const char *Cmd);
|
|||
"9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \
|
||||
"DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5"
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// Sample private RSA Key
|
||||
// Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c)
|
||||
|
||||
// private key - Exponent D
|
||||
#define RSA_D "24BF6185468786FDD303083D25E64EFC" \
|
||||
"66CA472BC44D253102F8B4A9D3BFA750" \
|
||||
|
@ -136,14 +139,14 @@ static int rdv4_sign_write(uint8_t *signature, uint8_t slen){
|
|||
if (!resp.oldarg[0]) {
|
||||
PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")");
|
||||
} else {
|
||||
PrintAndLogEx(SUCCESS, "Writing signature ( "_GREEN_("ok") " ) at offset %u", FLASH_MEM_SIGNATURE_OFFSET);
|
||||
PrintAndLogEx(SUCCESS, "Writing signature at offset %u ( "_GREEN_("ok") " )", FLASH_MEM_SIGNATURE_OFFSET);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
}
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
|
||||
static int CmdFlashmemSpiBaudrate(const char *Cmd) {
|
||||
static int CmdFlashmemSpiBaud(const char *Cmd) {
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem baudrate",
|
||||
|
@ -352,6 +355,7 @@ static int CmdFlashMemDump(const char *Cmd) {
|
|||
arg_int0("l", "len", "<dec>", "length"),
|
||||
arg_lit0("v", "view", "view dump"),
|
||||
arg_strx0("f", "file", "<filename>", "file name"),
|
||||
arg_int0("c", "cols", "<dec>", "column breaks (def 32)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
@ -362,6 +366,7 @@ static int CmdFlashMemDump(const char *Cmd) {
|
|||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
int breaks = arg_get_int_def(ctx, 5, 32);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
uint8_t *dump = calloc(len, sizeof(uint8_t));
|
||||
|
@ -379,7 +384,7 @@ static int CmdFlashMemDump(const char *Cmd) {
|
|||
|
||||
if (view) {
|
||||
PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------");
|
||||
print_hex_break(dump, len, 32);
|
||||
print_hex_break(dump, len, breaks);
|
||||
}
|
||||
|
||||
if (filename[0] != '\0') {
|
||||
|
@ -445,29 +450,39 @@ static int CmdFlashMemInfo(const char *Cmd) {
|
|||
CLIParserInit(&ctx, "mem info",
|
||||
"Collect signature and verify it from flash memory",
|
||||
"mem info"
|
||||
// "mem info -s"
|
||||
// "mem info -s -d 0102030405060708"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
// arg_lit0("s", NULL, "create a signature"),
|
||||
// arg_lit0("w", NULL, "write signature to flash memory"),
|
||||
arg_lit0("s", "sign", "create a signature"),
|
||||
arg_str0("d", NULL, "<hex>", "flash memory id, 8 hex bytes"),
|
||||
// arg_lit0("w", "write", "write signature to flash memory"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
bool shall_sign = false, shall_write = false;
|
||||
// shall_sign = arg_get_lit(ctx, 1);
|
||||
// shall_write = arg_get_lit(ctx, 2);
|
||||
shall_sign = arg_get_lit(ctx, 1);
|
||||
|
||||
int dlen = 0;
|
||||
uint8_t id[8] = {0};
|
||||
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), id, sizeof(id), &dlen);
|
||||
|
||||
// shall_write = arg_get_lit(ctx, 3);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
// validate signature data
|
||||
if (dlen > 0 && dlen < sizeof(id) ) {
|
||||
PrintAndLogEx(FAILED, "Error parsing flash memory id, expect 8, got %d", dlen);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
// validate devicesignature data
|
||||
rdv40_validation_t mem;
|
||||
int res = rdv4_get_signature(&mem);
|
||||
res = rdv4_get_signature(&mem);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = rdv4_validate(&mem);
|
||||
|
||||
// Flash ID hash (sha1)
|
||||
|
@ -479,6 +494,11 @@ static int CmdFlashMemInfo(const char *Cmd) {
|
|||
PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------");
|
||||
PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid)));
|
||||
PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash)));
|
||||
PrintAndLogEx(
|
||||
(res == PM3_SUCCESS) ? SUCCESS : FAILED,
|
||||
"Signature............ ( %s )",
|
||||
(res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")
|
||||
);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------");
|
||||
for (int i = 0; i < (sizeof(mem.signature) / 32); i++) {
|
||||
|
@ -520,13 +540,21 @@ static int CmdFlashMemInfo(const char *Cmd) {
|
|||
PrintAndLogEx(INFO, " %.64s", str_pk + 192);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
||||
bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0);
|
||||
bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0);
|
||||
PrintAndLogEx(
|
||||
(is_keyok) ? SUCCESS : FAILED,
|
||||
"RSA key validation... ( %s )",
|
||||
"RSA public key validation.... ( %s )",
|
||||
(is_keyok) ? _GREEN_("ok") : _RED_("fail")
|
||||
);
|
||||
|
||||
is_keyok = (mbedtls_rsa_check_privkey(&rsa) == 0);
|
||||
PrintAndLogEx(
|
||||
(is_keyok) ? SUCCESS : FAILED,
|
||||
"RSA private key validation... ( %s )",
|
||||
(is_keyok) ? _GREEN_("ok") : _RED_("fail")
|
||||
);
|
||||
|
||||
|
||||
// to be verified
|
||||
uint8_t from_device[RRG_RSA_KEY_LEN];
|
||||
memcpy(from_device, mem.signature, RRG_RSA_KEY_LEN);
|
||||
|
@ -537,6 +565,13 @@ static int CmdFlashMemInfo(const char *Cmd) {
|
|||
|
||||
// Signing (private key)
|
||||
if (shall_sign) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("Enter signing") " --------------------");
|
||||
|
||||
if (dlen == 8) {
|
||||
mbedtls_sha1(id, sizeof(id), sha_hash);
|
||||
}
|
||||
PrintAndLogEx(INFO, "Signing %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash)));
|
||||
|
||||
int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign);
|
||||
PrintAndLogEx(
|
||||
|
@ -555,27 +590,30 @@ static int CmdFlashMemInfo(const char *Cmd) {
|
|||
}
|
||||
|
||||
// Verify (public key)
|
||||
int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device);
|
||||
bool is_verified = (mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device) == 0);
|
||||
mbedtls_rsa_free(&rsa);
|
||||
|
||||
PrintAndLogEx(
|
||||
(is_verified == 0) ? SUCCESS : FAILED,
|
||||
"RSA verification..... ( %s )",
|
||||
(is_verified == 0) ? _GREEN_("ok") : _RED_("fail")
|
||||
(is_verified) ? SUCCESS : FAILED,
|
||||
"RSA verification..... ( %s )",
|
||||
(is_verified) ? _GREEN_("ok") : _RED_("fail")
|
||||
);
|
||||
if (is_verified) {
|
||||
PrintAndLogEx(SUCCESS, "Genuine Proxmark3 RDV4 signature detected");
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"baudrate", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate"},
|
||||
{"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "High level SPI FileSystem Flash manipulation"},
|
||||
{"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"},
|
||||
{"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory"},
|
||||
{"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"},
|
||||
{"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"},
|
||||
{"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"},
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"},
|
||||
{"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"},
|
||||
{"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"},
|
||||
{"load", CmdFlashMemLoad, IfPm3Flash, "Load data to flash memory"},
|
||||
{"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -8,352 +8,16 @@
|
|||
// Proxmark3 RDV40 Flash memory commands
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "cmdflashmemspiffs.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "pmflash.h"
|
||||
#include "fileutils.h" //saveFile
|
||||
#include "comms.h" //getfromdevice
|
||||
#include "comms.h" //getfromdevice
|
||||
#include "cliparser.h"
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
static int usage_flashmemspiffs_remove(void) {
|
||||
PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem\n");
|
||||
PrintAndLogEx(NORMAL, "Usage: mem spiffs remove <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 flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen) {
|
||||
|
||||
int ret_val = PM3_SUCCESS;
|
||||
|
||||
|
@ -364,33 +28,35 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) {
|
|||
// Send to device
|
||||
uint32_t bytes_sent = 0;
|
||||
uint32_t bytes_remaining = datalen;
|
||||
uint32_t append = 0;
|
||||
|
||||
// fast push mode
|
||||
conn.block_after_ACK = true;
|
||||
|
||||
while (bytes_remaining > 0) {
|
||||
|
||||
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
||||
|
||||
flashmem_write_t *payload = calloc(1, sizeof(flashmem_write_t) + bytes_in_packet);
|
||||
|
||||
payload->append = (bytes_sent > 0);
|
||||
payload->fnlen = strlen(destfn);
|
||||
memcpy(payload->fn, destfn, strlen(destfn));
|
||||
|
||||
payload->bytes_in_packet = bytes_in_packet;
|
||||
memset(payload->data, 0, bytes_in_packet);
|
||||
memcpy(payload->data, data + bytes_sent, bytes_in_packet);
|
||||
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_WRITE, (uint8_t *)payload, sizeof(flashmem_write_t) + bytes_in_packet);
|
||||
|
||||
char fdata[32 + bytes_in_packet];
|
||||
memset(fdata, 0, sizeof(fdata));
|
||||
memcpy(fdata, destfn, 32);
|
||||
memcpy(fdata + 32, data + bytes_sent, bytes_in_packet);
|
||||
|
||||
if (bytes_sent > 0)
|
||||
append = 1;
|
||||
|
||||
SendCommandOLD(CMD_SPIFFS_WRITE, append, bytes_in_packet, 0, fdata, 32 + bytes_in_packet);
|
||||
free(payload);
|
||||
|
||||
bytes_remaining -= bytes_in_packet;
|
||||
bytes_sent += bytes_in_packet;
|
||||
|
||||
PacketResponseNG resp;
|
||||
|
||||
uint8_t retry = 3;
|
||||
while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
|
||||
while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) {
|
||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||
retry--;
|
||||
if (retry == 0) {
|
||||
|
@ -398,13 +64,6 @@ int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) {
|
|||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t isok = resp.oldarg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent);
|
||||
ret_val = PM3_EFLASH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -416,99 +75,470 @@ out:
|
|||
// We want to unmount after these to set things back to normal but more than this
|
||||
// unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory
|
||||
SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSWipe(const char *Cmd) {
|
||||
static int CmdFlashMemSpiFFSMount(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs mount",
|
||||
"Mount the SPIFFS file system if not already mounted",
|
||||
"mem spiffs mount");
|
||||
|
||||
char ctmp = tolower(param_getchar(Cmd, 0));
|
||||
if (ctmp == 'h') {
|
||||
return usage_flashmemspiffs_wipe();
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSUnmount(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs unmount",
|
||||
"Un-mount the SPIFFS file system",
|
||||
"mem spiffs unmount");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSTest(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs test",
|
||||
"Test SPIFFS Operations, require wiping pages 0 and 1",
|
||||
"mem spiffs test");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_TEST, NULL, 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSCheck(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs check",
|
||||
"Check/try to defrag faulty/fragmented SPIFFS file system",
|
||||
"mem spiffs check");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSTree(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs tree",
|
||||
"Print the Flash memory file system tree",
|
||||
"mem spiffs tree");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSInfo(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs info",
|
||||
"Print file system info and usage statistics",
|
||||
"mem spiffs info");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSRemove(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs remove",
|
||||
"Remove a file from SPIFFS filesystem",
|
||||
"mem spiffs remove -f lasttag.bin"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("f", "filename", "<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;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Wiping all files from SPIFFS FileSystem");
|
||||
uint32_t len = resp.data.asDwords[0];
|
||||
uint8_t *dump = calloc(len, sizeof(uint8_t));
|
||||
if (!dump) {
|
||||
PrintAndLogEx(ERR, "error, cannot allocate memory ");
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
// download from device
|
||||
uint32_t start_index = 0;
|
||||
PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src);
|
||||
if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true)) {
|
||||
PrintAndLogEx(FAILED, "error, downloading from spiffs");
|
||||
free(dump);
|
||||
return PM3_EFLASH;
|
||||
}
|
||||
|
||||
// save to file
|
||||
char fn[FILE_PATH_SIZE] = {0};
|
||||
if (dlen == 0) {
|
||||
strncpy(fn, src, slen);
|
||||
} else {
|
||||
strncpy(fn, dest, dlen);
|
||||
}
|
||||
|
||||
saveFile(fn, ".bin", dump, len);
|
||||
if (eml) {
|
||||
uint8_t eml_len = 16;
|
||||
if (strstr(fn, "class") != NULL)
|
||||
eml_len = 8;
|
||||
else if (strstr(fn, "mfu") != NULL)
|
||||
eml_len = 4;
|
||||
|
||||
saveFileEML(fn, dump, len, eml_len);
|
||||
}
|
||||
free(dump);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSWipe(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs wipe",
|
||||
_RED_("* * * Warning * * *") " \n"
|
||||
_CYAN_("This command wipes all files on the device SPIFFS file system"),
|
||||
"mem spiffs wipe");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
PrintAndLogEx(INFO, "Wiping all files from SPIFFS file system");
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0);
|
||||
WaitForResponse(CMD_SPIFFS_WIPE, &resp);
|
||||
PrintAndLogEx(INFO, "Done!");
|
||||
PrintAndLogEx(HINT, "Try use '" _YELLOW_("mem spiffs tree") "' to verify.");
|
||||
if (resp.status == PM3_SUCCESS)
|
||||
PrintAndLogEx(INFO, "Done!");
|
||||
|
||||
PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdFlashMemSpiFFSLoad(const char *Cmd) {
|
||||
static int CmdFlashMemSpiFFSUpload(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs upload",
|
||||
"Uploads binary-wise file into device file system\n"
|
||||
"Warning: mem area to be written must have been wiped first.\n"
|
||||
"This is already taken care when loading dictionaries.\n"
|
||||
"File names can only be 32 bytes long on device SPIFFS",
|
||||
"mem spiffs upload -s local.bin -d dest.bin"
|
||||
);
|
||||
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
uint8_t destfilename[32] = {0};
|
||||
bool errors = false;
|
||||
uint8_t cmdp = 0;
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("s", "src", "<fn>", "source file name"),
|
||||
arg_str1("d", "dest", "<fn>", "destination file name"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h':
|
||||
return usage_flashmemspiffs_load();
|
||||
case 'f':
|
||||
if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) {
|
||||
PrintAndLogEx(FAILED, "Filename too long");
|
||||
errors = true;
|
||||
}
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'o':
|
||||
param_getstr(Cmd, cmdp + 1, (char *)destfilename, 32);
|
||||
if (strlen((char *)destfilename) == 0) {
|
||||
PrintAndLogEx(FAILED, "Destination Filename missing or invalid");
|
||||
errors = true;
|
||||
}
|
||||
cmdp += 2;
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int slen = 0;
|
||||
char src[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, FILE_PATH_SIZE, &slen);
|
||||
|
||||
// Validations
|
||||
if (errors || cmdp == 0)
|
||||
return usage_flashmemspiffs_load();
|
||||
int dlen = 0;
|
||||
char dest[32] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, 32, &dlen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
PrintAndLogEx(DEBUG, "Upload `" _YELLOW_("%s") "` -> `" _YELLOW_("%s") "`", src, dest);
|
||||
|
||||
size_t datalen = 0;
|
||||
uint8_t *data = NULL;
|
||||
|
||||
int res = loadFile_safe(filename, "", (void **)&data, &datalen);
|
||||
// int res = loadFileEML( filename, data, &datalen);
|
||||
int res = loadFile_safe(src, "", (void **)&data, &datalen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
free(data);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
res = flashmem_spiffs_load(destfilename, data, datalen);
|
||||
|
||||
res = flashmem_spiffs_load(dest, data, datalen);
|
||||
free(data);
|
||||
|
||||
if (res == PM3_SUCCESS)
|
||||
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, destfilename);
|
||||
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") " bytes to file "_GREEN_("%s"), datalen, dest);
|
||||
|
||||
PrintAndLogEx(HINT, "Try `" _YELLOW_("mem spiffs tree") "` to verify");
|
||||
return res;
|
||||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
static int CmdFlashMemSpiFFSView(const char *Cmd) {
|
||||
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"},
|
||||
{"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented Filesystem"},
|
||||
{"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"},
|
||||
{"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"},
|
||||
{"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"},
|
||||
{"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"},
|
||||
{"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"},
|
||||
{"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"},
|
||||
{"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"},
|
||||
{"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"},
|
||||
{"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"},
|
||||
{"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS FileSystem." _RED_("* dangerous *") },
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "mem spiffs view",
|
||||
"View a file on flash memory on devicer in console",
|
||||
"mem spiffs view -f tag.bin"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("f", "file", "<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"},
|
||||
{"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"},
|
||||
{"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"},
|
||||
{"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"},
|
||||
{"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"},
|
||||
{"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"},
|
||||
{"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"},
|
||||
{"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"},
|
||||
{"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations"},
|
||||
{"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash memory file system tree"},
|
||||
{"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS file system"},
|
||||
{"upload", CmdFlashMemSpiFFSUpload, IfPm3Flash, "Upload file into SPIFFS file system"},
|
||||
{"view", CmdFlashMemSpiFFSView, IfPm3Flash, "View file on SPIFFS file system"},
|
||||
{"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS file system * " _RED_("dangerous") " *" },
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,6 @@
|
|||
#include "common.h"
|
||||
|
||||
int CmdFlashMemSpiFFS(const char *Cmd);
|
||||
int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen);
|
||||
int flashmem_spiffs_load(char *destfn, uint8_t *data, size_t datalen);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -103,14 +103,6 @@ static inline uint32_t leadingzeros(uint64_t a) {
|
|||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
static inline uint32_t countones(uint64_t a) {
|
||||
#if defined __GNUC__
|
||||
return __builtin_popcountll(a);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
const char *card_types[] = {
|
||||
|
@ -2034,7 +2026,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
|
|||
|
||||
uint64_t a = bytes_to_num(data, 8);
|
||||
bool starts = (leadingzeros(a) < 12);
|
||||
bool ones = (countones(a) > 16 && countones(a) < 48);
|
||||
bool ones = (bitcount64(a) > 16 && bitcount64(a) < 48);
|
||||
|
||||
if (starts && ones) {
|
||||
PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible");
|
||||
|
|
|
@ -1290,9 +1290,10 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8
|
|||
MifareAuthState = masNrAr;
|
||||
if (AuthData.first_auth) {
|
||||
AuthData.nt = bytes_to_num(cmd, 4);
|
||||
AuthData.nt_enc_par = 0;
|
||||
} else {
|
||||
AuthData.nt_enc = bytes_to_num(cmd, 4);
|
||||
AuthData.nt_enc_par = parity[0];
|
||||
AuthData.nt_enc_par = parity[0] & 0xF0;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
|
@ -1304,6 +1305,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8
|
|||
snprintf(exp, size, "AUTH: nr ar (enc)");
|
||||
MifareAuthState = masAt;
|
||||
AuthData.nr_enc = bytes_to_num(cmd, 4);
|
||||
AuthData.nr_enc_par = parity[0] & 0xF0;
|
||||
AuthData.ar_enc = bytes_to_num(&cmd[4], 4);
|
||||
AuthData.ar_enc_par = parity[0] << 4;
|
||||
return;
|
||||
|
@ -1316,7 +1318,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8
|
|||
snprintf(exp, size, "AUTH: at (enc)");
|
||||
MifareAuthState = masAuthComplete;
|
||||
AuthData.at_enc = bytes_to_num(cmd, 4);
|
||||
AuthData.at_enc_par = parity[0];
|
||||
AuthData.at_enc_par = parity[0] & 0xF0;
|
||||
return;
|
||||
} else {
|
||||
MifareAuthState = masError;
|
||||
|
@ -1335,6 +1337,17 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8
|
|||
|
||||
}
|
||||
|
||||
static void mf_get_paritybinstr(char *s, uint32_t val, uint8_t par) {
|
||||
uint8_t foo[4] = {0,0,0,0};
|
||||
num_to_bytes(val, sizeof(uint32_t), foo);
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (oddparity8(foo[i]) != ((par >> (7 - (i & 0x0007))) & 0x01))
|
||||
sprintf(s++, "1");
|
||||
else
|
||||
sprintf(s++, "0");
|
||||
}
|
||||
}
|
||||
|
||||
bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount) {
|
||||
static struct Crypto1State *traceCrypto1;
|
||||
|
||||
|
@ -1428,7 +1441,29 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes
|
|||
|
||||
//hardnested
|
||||
if (!traceCrypto1) {
|
||||
PrintAndLogEx(NORMAL, "hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc);
|
||||
|
||||
//PrintAndLogEx(NORMAL, "hardnested not implemented. uid:%x nt:%x ar_enc:%x at_enc:%x\n", AuthData.uid, AuthData.nt, AuthData.ar_enc, AuthData.at_enc);
|
||||
|
||||
char snt[5] = {0,0,0,0,0};
|
||||
mf_get_paritybinstr(snt, AuthData.nt_enc, AuthData.nt_enc_par);
|
||||
char sar[5] = {0,0,0,0,0};
|
||||
mf_get_paritybinstr(sar, AuthData.ar_enc, AuthData.ar_enc_par);
|
||||
char sat[5] = {0,0,0,0,0};
|
||||
mf_get_paritybinstr(sat, AuthData.at_enc, AuthData.at_enc_par);
|
||||
|
||||
PrintAndLogEx(NORMAL, "Nested authentication detected. ");
|
||||
PrintAndLogEx(NORMAL, "tools/mf_nonce_brute/mf_nonce_brute %x %x %s %x %x %s %x %s %s\n"
|
||||
, AuthData.uid
|
||||
, AuthData.nt_enc
|
||||
, snt
|
||||
, AuthData.nr_enc
|
||||
, AuthData.ar_enc
|
||||
, sar
|
||||
, AuthData.at_enc
|
||||
, sat
|
||||
, sprint_hex_inrow(cmd, cmdsize)
|
||||
);
|
||||
|
||||
MifareAuthState = masError;
|
||||
|
||||
/* TOO SLOW( needs to have more strong filter. with this filter - aprox 4 mln tests
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef struct {
|
|||
uint32_t nt_enc; // encrypted tag challenge
|
||||
uint8_t nt_enc_par; // encrypted tag challenge parity
|
||||
uint32_t nr_enc; // encrypted reader challenge
|
||||
uint8_t nr_enc_par; // encrypted reader challenge parity
|
||||
uint32_t ar_enc; // encrypted reader response
|
||||
uint8_t ar_enc_par; // encrypted reader response parity
|
||||
uint32_t at_enc; // encrypted tag response
|
||||
|
|
|
@ -523,20 +523,21 @@ static int32_t initSectorTable(sector_t **src, int32_t items) {
|
|||
static void decode_print_st(uint16_t blockno, uint8_t *data) {
|
||||
if (mfIsSectorTrailer(blockno)) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Sector trailer decoded:");
|
||||
PrintAndLogEx(NORMAL, "----------------------------------------------");
|
||||
PrintAndLogEx(NORMAL, "Key A " _GREEN_("%s"), sprint_hex_inrow(data, 6));
|
||||
PrintAndLogEx(NORMAL, "Key B " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
|
||||
PrintAndLogEx(NORMAL, "Access rights");
|
||||
PrintAndLogEx(INFO, "--------- " _CYAN_("Sector trailer") " -------------");
|
||||
PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6));
|
||||
PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3));
|
||||
PrintAndLogEx(INFO, "user / gdb... " _GREEN_("%02x"), data[9]);
|
||||
PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
|
||||
PrintAndLogEx(INFO, "Access rights decoded");
|
||||
|
||||
int bln = mfFirstBlockOfSector(mfSectorNum(blockno));
|
||||
int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
PrintAndLogEx(NORMAL, " block %d%s " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? "+" : ""), mfGetAccessConditionsDesc(i, &data[6]));
|
||||
PrintAndLogEx(INFO, " block %d%s " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? "+" : ""), mfGetAccessConditionsDesc(i, &data[6]));
|
||||
bln += blinc;
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "UserData " _YELLOW_("0x%02x"), data[9]);
|
||||
PrintAndLogEx(NORMAL, "----------------------------------------------");
|
||||
|
||||
PrintAndLogEx(INFO, "--------------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -789,7 +790,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) {
|
|||
PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "--sector no %d, key %c - %s ", sectorNo, keyType ? 'B' : 'A', sprint_hex(key, 6));
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
||||
uint8_t sc_size = mfNumBlocksPerSector(sectorNo) * 16;
|
||||
uint8_t *data = calloc(sc_size, sizeof(uint8_t));
|
||||
|
@ -804,8 +805,16 @@ static int CmdHF14AMfRdSc(const char *Cmd) {
|
|||
uint8_t blocks = NumBlocksPerSector(sectorNo);
|
||||
uint8_t start = FirstBlockOfSector(sectorNo);
|
||||
|
||||
PrintAndLogEx(INFO, " # | data - sector %02d / 0x%02X | ascii", sectorNo, sectorNo);
|
||||
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
|
||||
for (int i = 0; i < blocks; i++) {
|
||||
PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16));
|
||||
if (start + i == 0) {
|
||||
PrintAndLogEx(INFO, "%3d | " _RED_("%s"), start + i, sprint_hex_ascii(data + (i * 16), 16));
|
||||
} else if (mfIsSectorTrailer(i)) {
|
||||
PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data + (i * 16), 16));
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "%3d | %s ", start + i, sprint_hex_ascii(data + (i * 16), 16));
|
||||
}
|
||||
}
|
||||
decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16));
|
||||
|
||||
|
@ -3771,8 +3780,10 @@ static int CmdHF14AMfEGetSc(const char *Cmd) {
|
|||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "\n # | data - sector %02d / 0x%02X ", sector, sector);
|
||||
PrintAndLogEx(NORMAL, "----+------------------------------------------------");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, " # | data - sector %02d / 0x%02X | ascii", sector, sector);
|
||||
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
|
||||
|
||||
uint8_t blocks = 4;
|
||||
uint8_t start = sector * 4;
|
||||
if (sector >= 32) {
|
||||
|
@ -3785,11 +3796,11 @@ static int CmdHF14AMfEGetSc(const char *Cmd) {
|
|||
int res = mfEmlGetMem(data, start + i, 1);
|
||||
if (res == PM3_SUCCESS) {
|
||||
if (start + i == 0) {
|
||||
PrintAndLogEx(INFO, "%03d | " _RED_("%s"), start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
PrintAndLogEx(INFO, "%3d | " _RED_("%s"), start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
} else if (mfIsSectorTrailer(i)) {
|
||||
PrintAndLogEx(INFO, "%03d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "%03d | %s ", start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
PrintAndLogEx(INFO, "%3d | %s ", start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4467,8 +4478,10 @@ static int CmdHF14AMfCGetSc(const char *Cmd) {
|
|||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "\n # | data - sector %02d / 0x%02X ", sector, sector);
|
||||
PrintAndLogEx(NORMAL, "----+------------------------------------------------");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, " # | data - sector %02d / 0x%02X | ascii", sector, sector);
|
||||
PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------");
|
||||
|
||||
uint8_t blocks = 4;
|
||||
uint8_t start = sector * 4;
|
||||
if (sector >= 32) {
|
||||
|
@ -4487,7 +4500,7 @@ static int CmdHF14AMfCGetSc(const char *Cmd) {
|
|||
PrintAndLogEx(ERR, "Can't read block. %d error=%d", start + i, res);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data, 16));
|
||||
PrintAndLogEx(INFO, "%3d | %s ", start + i, sprint_hex_ascii(data, sizeof(data)));
|
||||
}
|
||||
decode_print_st(start + blocks - 1, data);
|
||||
return PM3_SUCCESS;
|
||||
|
|
|
@ -3102,7 +3102,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
"hf mfu otptear -b 3 -i 100 -s 1000\n"
|
||||
"hf mfu otptear -b 3 -i 1 -e 200\n"
|
||||
"hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE\n"
|
||||
"hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quite when OTP is reset"
|
||||
"hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quit when OTP is reset"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
|
@ -3112,7 +3112,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
arg_u64_0("e", "end", "<dec>", "end time (def 3000 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("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_param_end
|
||||
};
|
||||
|
@ -3126,6 +3126,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
int d_len = 0;
|
||||
uint8_t data[4] = {0x00};
|
||||
CLIGetHexWithReturn(ctx, 5, data, &d_len);
|
||||
bool use_data = (d_len > 0);
|
||||
|
||||
int t_len = 0;
|
||||
uint8_t test[4] = {0x00};
|
||||
|
@ -3150,7 +3151,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
return PM3_EINVARG;
|
||||
}
|
||||
if (start > (end - steps)) {
|
||||
PrintAndLogEx(WARNING, "Start time larger then (end time + steps)");
|
||||
PrintAndLogEx(WARNING, "Start time larger than (end time + steps)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
|
@ -3169,26 +3170,34 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t teardata[8] = {0x00};
|
||||
memcpy(teardata, data, sizeof(data));
|
||||
memcpy(teardata + sizeof(data), test, sizeof(test));
|
||||
uint8_t teardata[4] = {0x00};
|
||||
memcpy(teardata, test, sizeof(test));
|
||||
|
||||
PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Tear off") " ---------------------");
|
||||
PrintAndLogEx(INFO, "Starting Tear-off test");
|
||||
PrintAndLogEx(INFO, "Target block no: %u", blockno);
|
||||
PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(teardata, 4));
|
||||
PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata + 4, 4));
|
||||
if (use_data) {
|
||||
PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(data, 4));
|
||||
}
|
||||
PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata, 4));
|
||||
if (use_match) {
|
||||
PrintAndLogEx(INFO, "Target match block data : %s", sprint_hex_inrow(match, 4));
|
||||
}
|
||||
PrintAndLogEx(INFO, "----------------------------------------------------");
|
||||
uint8_t isOK;
|
||||
bool got_pre = false, got_post = false, lock_on = false;
|
||||
bool lock_on = false;
|
||||
uint8_t pre[4] = {0};
|
||||
uint8_t post[4] = {0};
|
||||
uint32_t current = start;
|
||||
int phase_clear = -1;
|
||||
int phase_newwr = -1;
|
||||
int phase_begin_clear = -1;
|
||||
int phase_end_clear = -1;
|
||||
int phase_begin_newwr = -1;
|
||||
int phase_end_newwr = -1;
|
||||
bool skip_phase1 = false;
|
||||
uint8_t retries = 0;
|
||||
uint8_t error_retries = 0;
|
||||
|
||||
while (current <= (end - steps)) {
|
||||
while ((current <= (end - steps)) && (error_retries < 10)) {
|
||||
|
||||
if (kbd_enter_pressed()) {
|
||||
PrintAndLogEx(INFO, "\naborted via keyboard!\n");
|
||||
|
@ -3198,10 +3207,27 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", current);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
|
||||
PacketResponseNG resp;
|
||||
|
||||
got_pre = false;
|
||||
if (use_data) {
|
||||
SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, 0, 0, data, d_len);
|
||||
bool got_written = false;
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||
isOK = resp.oldarg[0] & 0xff;
|
||||
if (isOK) {
|
||||
got_written = true;
|
||||
}
|
||||
}
|
||||
if (! got_written) {
|
||||
PrintAndLogEx(FAILED, "Failed to write block BEFORE");
|
||||
error_retries++;
|
||||
continue; // try again
|
||||
}
|
||||
}
|
||||
|
||||
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
|
||||
|
||||
bool got_pre = false;
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||
isOK = resp.oldarg[0] & 0xFF;
|
||||
if (isOK) {
|
||||
|
@ -3209,9 +3235,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
got_pre = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! got_pre) {
|
||||
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
|
||||
error_retries++;
|
||||
continue; // try again
|
||||
}
|
||||
clearCommandBuffer();
|
||||
SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, 8);
|
||||
SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, sizeof(teardata));
|
||||
|
||||
// we be getting ACK that we are silently ignoring here..
|
||||
|
||||
|
@ -3222,10 +3252,11 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "Tear off reporting failure to select tag");
|
||||
error_retries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
got_post = false;
|
||||
bool got_post = false;
|
||||
clearCommandBuffer();
|
||||
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||
|
@ -3235,55 +3266,53 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
got_post = true;
|
||||
}
|
||||
}
|
||||
if (! got_post) {
|
||||
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
|
||||
error_retries++;
|
||||
continue; // try again
|
||||
}
|
||||
error_retries = 0;
|
||||
char prestr[20] = {0};
|
||||
snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre)));
|
||||
char poststr[20] = {0};
|
||||
snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post)));
|
||||
|
||||
if (got_pre && got_post) {
|
||||
|
||||
char prestr[20] = {0};
|
||||
snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre)));
|
||||
char poststr[20] = {0};
|
||||
snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post)));
|
||||
|
||||
if (memcmp(pre, post, sizeof(pre)) == 0) {
|
||||
|
||||
PrintAndLogEx(INFO, "Current %02d (0x%02X) %s"
|
||||
, blockno
|
||||
, blockno
|
||||
, poststr
|
||||
);
|
||||
} else {
|
||||
|
||||
// skip first message, since its the reset write.
|
||||
if (current == start) {
|
||||
PrintAndLogEx(INFO, "Inital write");
|
||||
} else {
|
||||
PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %02d (0x%02X) %s vs " _RED_("%s")
|
||||
, blockno
|
||||
, blockno
|
||||
, prestr
|
||||
, poststr
|
||||
);
|
||||
|
||||
lock_on = true;
|
||||
|
||||
if (phase_clear == -1)
|
||||
phase_clear = current;
|
||||
|
||||
// new write phase must be atleast 100us later..
|
||||
if (phase_clear > -1 && phase_newwr == -1 && current > (phase_clear + 100))
|
||||
phase_newwr = current;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_match && memcmp(pre, match, sizeof(pre)) == 0) {
|
||||
PrintAndLogEx(SUCCESS, "Block matches!\n");
|
||||
break;
|
||||
}
|
||||
if (memcmp(pre, post, sizeof(pre)) == 0) {
|
||||
|
||||
PrintAndLogEx(INFO, "Current : %02d (0x%02X) %s"
|
||||
, blockno
|
||||
, blockno
|
||||
, poststr
|
||||
);
|
||||
} else {
|
||||
if (got_pre == false)
|
||||
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
|
||||
if (got_post == false)
|
||||
PrintAndLogEx(FAILED, "Failed to read block AFTER");
|
||||
PrintAndLogEx(INFO, _CYAN_("Tear off occurred") " : %02d (0x%02X) %s => " _RED_("%s")
|
||||
, blockno
|
||||
, blockno
|
||||
, prestr
|
||||
, poststr
|
||||
);
|
||||
|
||||
lock_on = true;
|
||||
|
||||
if ((phase_begin_clear == -1) && (bitcount32(*(uint32_t*)pre) > bitcount32(*(uint32_t*)post)))
|
||||
phase_begin_clear = current;
|
||||
|
||||
if ((phase_begin_clear > -1) && (phase_end_clear == -1) && (bitcount32(*(uint32_t*)post) == 0))
|
||||
phase_end_clear = current;
|
||||
|
||||
if ((current == start) && (phase_end_clear > -1))
|
||||
skip_phase1 = true;
|
||||
// new write phase must be atleast 100us later..
|
||||
if (((bitcount32(*(uint32_t*)pre) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(*(uint32_t*)post) != 0) && (skip_phase1 || (current > (phase_end_clear + 100))))
|
||||
phase_begin_newwr = current;
|
||||
|
||||
if ((phase_begin_newwr > -1) && (phase_end_newwr == -1) && (memcmp(post, teardata, sizeof(teardata)) == 0))
|
||||
phase_end_newwr = current;
|
||||
}
|
||||
|
||||
if (use_match && memcmp(post, match, sizeof(post)) == 0) {
|
||||
PrintAndLogEx(SUCCESS, "Block matches stop condition!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* TEMPORALLY DISABLED
|
||||
|
@ -3314,11 +3343,17 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
|
|||
}
|
||||
|
||||
PrintAndLogEx(INFO, "----------------------------------------------------");
|
||||
if (phase_clear > - 1) {
|
||||
PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_clear);
|
||||
if ((phase_begin_clear > - 1) && (phase_begin_clear != start)) {
|
||||
PrintAndLogEx(INFO, "Erase phase start boundary around " _YELLOW_("%5d") " us", phase_begin_clear);
|
||||
}
|
||||
if (phase_newwr > - 1) {
|
||||
PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_newwr);
|
||||
if ((phase_end_clear > - 1) && (phase_end_clear != start)){
|
||||
PrintAndLogEx(INFO, "Erase phase end boundary around " _YELLOW_("%5d") " us", phase_end_clear);
|
||||
}
|
||||
if (phase_begin_newwr > - 1) {
|
||||
PrintAndLogEx(INFO, "Write phase start boundary around " _YELLOW_("%5d") " us", phase_begin_newwr);
|
||||
}
|
||||
if (phase_end_newwr > - 1) {
|
||||
PrintAndLogEx(INFO, "Write phase end boundary around " _YELLOW_("%5d") " us", phase_end_newwr);
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
|
|
|
@ -975,7 +975,7 @@ void pm3_version(bool verbose, bool oneliner) {
|
|||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, " device.................... %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _RED_("device / fw mismatch"));
|
||||
PrintAndLogEx(NORMAL, " firmware.................. %s", _YELLOW_("RDV4"));
|
||||
PrintAndLogEx(NORMAL, " firmware.................. %s", (is_genuine_rdv4) ? _GREEN_("RDV4") : _YELLOW_("RDV4"));
|
||||
PrintAndLogEx(NORMAL, " external flash............ %s", IfPm3Flash() ? _GREEN_("present") : _YELLOW_("absent"));
|
||||
PrintAndLogEx(NORMAL, " smartcard reader.......... %s", IfPm3Smartcard() ? _GREEN_("present") : _YELLOW_("absent"));
|
||||
PrintAndLogEx(NORMAL, " FPC USART for BT add-on... %s", IfPm3FpcUsartHost() ? _GREEN_("present") : _YELLOW_("absent"));
|
||||
|
|
|
@ -768,6 +768,7 @@ int lfsim_upload_gb(void) {
|
|||
break;
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "." NOLF);
|
||||
fflush(stdout);
|
||||
payload_up.flag = 0;
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
@ -1321,7 +1322,7 @@ static bool CheckChipType(bool getDeviceData) {
|
|||
goto out;
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "Couldn't identify a chipset");
|
||||
PrintAndLogEx(INFO, "Couldn't identify a chipset");
|
||||
out:
|
||||
save_restoreGB(GRAPH_RESTORE);
|
||||
save_restoreDB(GRAPH_RESTORE);
|
||||
|
|
|
@ -24,10 +24,10 @@ static int CmdHelp(const char *Cmd);
|
|||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"410x", CmdLFEM410X, AlwaysAvailable, "EM 4102 commands..."},
|
||||
{"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4205 / 4305 / 4369 / 4469 commands..."},
|
||||
{"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4350 / 4450 commands..."},
|
||||
{"4x70", CmdLFEM4X70, AlwaysAvailable, "EM 4070 / 4170 commands..."},
|
||||
{"410x", CmdLFEM410X, AlwaysAvailable, "{ EM 4102 commands... }"},
|
||||
{"4x05", CmdLFEM4X05, AlwaysAvailable, "{ EM 4205 / 4305 / 4369 / 4469 commands... }"},
|
||||
{"4x50", CmdLFEM4X50, AlwaysAvailable, "{ EM 4350 / 4450 commands... }"},
|
||||
{"4x70", CmdLFEM4X70, AlwaysAvailable, "{ EM 4070 / 4170 commands... }"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -1692,11 +1692,8 @@ int CmdEM4x05Unlock(const char *Cmd) {
|
|||
}
|
||||
|
||||
// write
|
||||
res = unlock_write_protect(use_pwd, pwd, write_value, verbose);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "failed unlock write");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
// don't check the return value. As a tear-off occurred, the write failed.
|
||||
unlock_write_protect(use_pwd, pwd, write_value, verbose);
|
||||
|
||||
// read after trigger
|
||||
res = em4x05_read_word_ext(14, pwd, use_pwd, &word14);
|
||||
|
@ -1962,11 +1959,11 @@ int CmdEM4x05Sniff(const char *Cmd) {
|
|||
bool haveData, sampleData = true;
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf em 4x05_sniff",
|
||||
CLIParserInit(&ctx, "lf em 4x05 sniff",
|
||||
"Sniff EM4x05 commands sent from a programmer",
|
||||
"lf em 4x05_sniff -> sniff via lf sniff\n"
|
||||
"lf em 4x05_sniff -1 -> sniff from data loaded into the buffer\n"
|
||||
"lf em 4x05_sniff -r -> reverse the bit order when showing block data"
|
||||
"lf em 4x05 sniff --> sniff via lf sniff\n"
|
||||
"lf em 4x05 sniff -1 --> sniff from data loaded into the buffer\n"
|
||||
"lf em 4x05 sniff -r --> reverse the bit order when showing block data"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
|
|
|
@ -471,7 +471,7 @@ int CmdEM4x50Chk(const char *Cmd) {
|
|||
|
||||
// upload to flash.
|
||||
datalen = MIN(bytes_remaining, keyblock);
|
||||
res = flashmem_spiffs_load(destfn, keys, datalen);
|
||||
res = flashmem_spiffs_load((char*)destfn, keys, datalen);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "SPIFFS upload failed");
|
||||
return res;
|
||||
|
@ -568,7 +568,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
|
|||
|
||||
int CmdEM4x50Read(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf em 4x50 read",
|
||||
CLIParserInit(&ctx, "lf em 4x50 rdbl",
|
||||
"Reads single EM4x50 block/word.",
|
||||
"lf em 4x50 rdbl -b 3\n"
|
||||
"lf em 4x50 rdbl -b 32 -p 12345678 -> reads block 32 with pwd 0x12345678\n"
|
||||
|
|
|
@ -118,16 +118,6 @@ static void verify_values(uint64_t *animalid, uint32_t *countryid, uint32_t *ext
|
|||
}
|
||||
}
|
||||
|
||||
static inline uint32_t bitcount(uint32_t a) {
|
||||
#if defined __GNUC__
|
||||
return __builtin_popcountl(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
// FDX-B ISO11784/85 demod (aka animal tag) BIPHASE, inverted, rf/32, with preamble of 00000000001 (128bits)
|
||||
// 8 databits + 1 parity (1)
|
||||
// CIITT 16 chksum
|
||||
|
@ -594,7 +584,7 @@ int demodFDXB(bool verbose) {
|
|||
|
||||
uint8_t bt_par = (extended & 0x100) >> 8;
|
||||
uint8_t bt_temperature = extended & 0xff;
|
||||
uint8_t bt_calc_parity = (bitcount(bt_temperature) & 0x1) ? 0 : 1;
|
||||
uint8_t bt_calc_parity = (bitcount32(bt_temperature) & 0x1) ? 0 : 1;
|
||||
uint8_t is_bt_temperature = (bt_calc_parity == bt_par) && !(extended & 0xe00) ;
|
||||
|
||||
if (is_bt_temperature) {
|
||||
|
|
|
@ -113,19 +113,6 @@ static int usage_hitag_writer(void) {
|
|||
PrintAndLogEx(NORMAL, " 27 <password> <page> <byte0...byte3> Write page, password mode. Default: 4D494B52 (\"MIKR\")");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
static int usage_hitag_checkchallenges(void) {
|
||||
PrintAndLogEx(NORMAL, "Check challenges, load a file with save hitag crypto challenges and test them all.");
|
||||
PrintAndLogEx(NORMAL, "The file should be 8 * 60 bytes long, the file extension defaults to " _YELLOW_("`.cc`"));
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(NORMAL, "Usage: lf hitag cc [h] f <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) {
|
||||
char args[128] = {0};
|
||||
|
@ -614,50 +601,46 @@ static int CmdLFHitagReader(const char *Cmd) {
|
|||
|
||||
static int CmdLFHitagCheckChallenges(const char *Cmd) {
|
||||
|
||||
char filename[FILE_PATH_SIZE] = { 0x00 };
|
||||
size_t datalen = 0;
|
||||
int res = 0;
|
||||
bool file_given = false;
|
||||
bool errors = false;
|
||||
uint8_t cmdp = 0;
|
||||
uint8_t *data = calloc(8 * 60, sizeof(uint8_t));
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "lf hitag cc",
|
||||
"Check challenges, load a file with saved hitag crypto challenges and test them all.\n"
|
||||
"The file should be 8 * 60 bytes long, the file extension defaults to " _YELLOW_("`.cc`") " ",
|
||||
"lf hitag cc -f my_hitag_challenges"
|
||||
);
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h':
|
||||
free(data);
|
||||
return usage_hitag_checkchallenges();
|
||||
case 'f':
|
||||
//file with all the challenges to try
|
||||
param_getstr(Cmd, cmdp + 1, filename, sizeof(filename));
|
||||
res = loadFile(filename, ".cc", data, 8 * 60, &datalen);
|
||||
if (res > 0) {
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
file_given = true;
|
||||
cmdp += 2;
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("f", "filename", "<fn w/o ext>", "filename to load from"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
//Validations
|
||||
if (errors || strlen(Cmd) == 0) {
|
||||
free(data);
|
||||
return usage_hitag_checkchallenges();
|
||||
}
|
||||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
clearCommandBuffer();
|
||||
if (file_given)
|
||||
SendCommandOLD(CMD_LF_HITAGS_TEST_TRACES, 1, 0, 0, data, datalen);
|
||||
else
|
||||
SendCommandMIX(CMD_LF_HITAGS_TEST_TRACES, 0, 0, 0, NULL, 0);
|
||||
|
||||
free(data);
|
||||
if (fnlen > 0) {
|
||||
uint8_t *data = NULL;
|
||||
size_t datalen = 0;
|
||||
int res = loadFile_safe(filename, ".cc", (void **)&data, &datalen);
|
||||
if (res == PM3_SUCCESS) {
|
||||
if (datalen == (8 * 60) ) {
|
||||
SendCommandOLD(CMD_LF_HITAGS_TEST_TRACES, 1, 0, 0, data, datalen);
|
||||
} else {
|
||||
PrintAndLogEx(ERR, "Error, file length mismatch. Expected %d, got %d", 8*60, datalen);
|
||||
}
|
||||
}
|
||||
if (data) {
|
||||
free(data);
|
||||
}
|
||||
} else {
|
||||
SendCommandMIX(CMD_LF_HITAGS_TEST_TRACES, 0, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,20 +37,10 @@ const uint8_t translateTable[10] = {8, 2, 1, 12, 4, 5, 10, 13, 0, 9};
|
|||
const uint8_t invTranslateTable[16] = {8, 2, 1, 0xff, 4, 5, 0xff, 0xff, 0, 9, 6, 0xff, 3, 7, 0xff, 0xff};
|
||||
const uint8_t preamble[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}; // zero inside
|
||||
|
||||
static inline uint32_t bitcount(uint32_t a) {
|
||||
#if defined __GNUC__
|
||||
return __builtin_popcountl(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t isEven_64_63(const uint8_t *data) { // 8
|
||||
uint32_t tmp[2];
|
||||
memcpy(tmp, data, 8);
|
||||
return (bitcount(tmp[0]) + (bitcount(tmp[1] & 0xfeffffff))) & 1;
|
||||
return (bitcount32(tmp[0]) + (bitcount32(tmp[1] & 0xfeffffff))) & 1;
|
||||
}
|
||||
|
||||
//NEDAP demod - ASK/Biphase (or Diphase), RF/64 with preamble of 1111111110 (always a 128 bit data stream)
|
||||
|
@ -252,25 +242,25 @@ static int CmdLFNedapDemod(const char *Cmd) {
|
|||
|
||||
|
||||
configuration
|
||||
lf t55xx wr b 0 d 00170082
|
||||
lf t55xx wr -b 0 -d 00170082
|
||||
|
||||
1) uid 049033
|
||||
lf t55 wr b 1 d FF8B4168
|
||||
lf t55 wr b 2 d C90B5359
|
||||
lf t55 wr b 3 d 19A40087
|
||||
lf t55 wr b 4 d 120115CF
|
||||
lf t55xx wr -b 1 -d FF8B4168
|
||||
lf t55xx wr -b 2 -d C90B5359
|
||||
lf t55xx wr -b 3 -d 19A40087
|
||||
lf t55xx wr -b 4 -d 120115CF
|
||||
|
||||
2) uid 001630
|
||||
lf t55 wr b 1 d FF8B6B20
|
||||
lf t55 wr b 2 d F19B84A3
|
||||
lf t55 wr b 3 d 18058007
|
||||
lf t55 wr b 4 d 1200857C
|
||||
lf t55xx wr -b 1 -d FF8B6B20
|
||||
lf t55xx wr -b 2 -d F19B84A3
|
||||
lf t55xx wr -b 3 -d 18058007
|
||||
lf t55xx wr -b 4 -d 1200857C
|
||||
|
||||
3) uid 39feff
|
||||
lf t55xx wr b 1 d ffbfa73e
|
||||
lf t55xx wr b 2 d 4c0003ff
|
||||
lf t55xx wr b 3 d ffbfa73e
|
||||
lf t55xx wr b 4 d 4c0003ff
|
||||
lf t55xx wr -b 1 -d ffbfa73e
|
||||
lf t55xx wr -b 2 -d 4c0003ff
|
||||
lf t55xx wr -b 3 -d ffbfa73e
|
||||
lf t55xx wr -b 4 -d 4c0003ff
|
||||
|
||||
*/
|
||||
|
||||
|
@ -469,7 +459,7 @@ static int CmdLFNedapClone(const char *Cmd) {
|
|||
|
||||
if (res == PM3_SUCCESS) {
|
||||
PrintAndLogEx(INFO, "The block 0 was changed (eXtended) which can be hard to detect.");
|
||||
PrintAndLogEx(INFO, "Configure it manually " _YELLOW_("`lf t55xx config b 64 d BI i 1 o 32`"));
|
||||
PrintAndLogEx(INFO, "Configure it manually " _YELLOW_("`lf t55xx config -b 64 --BI -i -o 32`"));
|
||||
} else {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -32,6 +32,7 @@
|
|||
#define T55X7_NORALSY_CONFIG_BLOCK 0x00088C6A // ASK, compat mode, (NORALSY - KCP3000), data rate 32, 3 data blocks
|
||||
#define T55X7_PRESCO_CONFIG_BLOCK 0x00088088 // ASK, data rate 32, Manchester, 4 data blocks, STT
|
||||
#define T55X7_SECURAKEY_CONFIG_BLOCK 0x000C8060 // ASK, Manchester, data rate 40, 3 data blocks
|
||||
#define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ...
|
||||
|
||||
// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag
|
||||
// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo,
|
||||
|
@ -126,10 +127,10 @@ typedef struct {
|
|||
uint8_t offset;
|
||||
uint32_t block0;
|
||||
enum {
|
||||
notSet = 0x00,
|
||||
autoDetect = 0x01,
|
||||
userSet = 0x02,
|
||||
tagRead = 0x03,
|
||||
NOTSET = 0x00,
|
||||
AUTODETECT = 0x01,
|
||||
USERSET = 0x02,
|
||||
TAGREAD = 0x03,
|
||||
} block0Status;
|
||||
enum {
|
||||
RF_8 = 0x00,
|
||||
|
|
|
@ -298,10 +298,10 @@ static command_t CommandTable[] = {
|
|||
{"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"},
|
||||
{"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"},
|
||||
{"clear", CmdClear, AlwaysAvailable, "Clear screen"},
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help. Use " _YELLOW_("'<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"},
|
||||
{"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"},
|
||||
{"pref", CmdPref, AlwaysAvailable, "Edit preferences"},
|
||||
{"pref", CmdPref, AlwaysAvailable, "{ Edit preferences... }"},
|
||||
{"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"},
|
||||
{"quit", CmdQuit, AlwaysAvailable, ""},
|
||||
{"exit", CmdQuit, AlwaysAvailable, "Exit program"},
|
||||
|
|
|
@ -429,9 +429,9 @@ static int CmdScriptRun(const char *Cmd) {
|
|||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "Usage info"},
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"list", CmdScriptList, AlwaysAvailable, "List available scripts"},
|
||||
{"run", CmdScriptRun, AlwaysAvailable, "<name> -- execute a script"},
|
||||
{"run", CmdScriptRun, AlwaysAvailable, "<name> - execute a script"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
|
@ -242,7 +242,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
}
|
||||
|
||||
for (int j = 0; j < data_len && j / 18 < 18; j++) {
|
||||
|
||||
uint8_t parityBits = parityBytes[j >> 3];
|
||||
if (protocol != LEGIC
|
||||
&& protocol != ISO_14443B
|
||||
|
@ -256,7 +255,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
&& protocol != FELICA
|
||||
&& protocol != LTO
|
||||
&& protocol != PROTO_CRYPTORF
|
||||
&& (hdr->isResponse || protocol == ISO_14443A)
|
||||
&& (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE)
|
||||
&& (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) {
|
||||
|
||||
snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
|
||||
|
@ -280,9 +279,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
if (markCRCBytes) {
|
||||
//CRC-command
|
||||
if (crcStatus == 0 || crcStatus == 1) {
|
||||
char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4);
|
||||
char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4) - 1;
|
||||
(*pos1) = '[';
|
||||
char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4);
|
||||
char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4) - 1;
|
||||
sprintf(pos2, "%c", ']');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -839,6 +839,9 @@ static bool dl_it(uint8_t *dest, uint32_t bytes, PacketResponseNG *response, siz
|
|||
|
||||
if (response->cmd == CMD_ACK)
|
||||
return true;
|
||||
// Spiffs download is converted to NG,
|
||||
if (response->cmd == CMD_SPIFFS_DOWNLOAD)
|
||||
return true;
|
||||
|
||||
// sample_buf is a array pointer, located in data.c
|
||||
// arg0 = offset in transfer. Startindex of this chunk
|
||||
|
|
|
@ -412,7 +412,7 @@ static void flash_suggest_update_bootloader(void) {
|
|||
PrintAndLogEx(ERR, _RED_("reboot the Proxmark3 then only update the main firmware") "\n");
|
||||
PrintAndLogEx(ERR, "Follow these steps :");
|
||||
PrintAndLogEx(ERR, " 1) ./pm3-flash-bootrom");
|
||||
PrintAndLogEx(ERR, " 2) ./pm3-flash-flash-all");
|
||||
PrintAndLogEx(ERR, " 2) ./pm3-flash-all");
|
||||
PrintAndLogEx(ERR, " 3) ./pm3");
|
||||
PrintAndLogEx(INFO, "--------------------------------------------------------");
|
||||
g_printed_msg = true;
|
||||
|
|
|
@ -695,7 +695,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl
|
|||
mem[4] = (chunk & 0xFF);
|
||||
|
||||
// upload to flash.
|
||||
res = flashmem_spiffs_load(destfn, mem, 5 + (chunk * 6));
|
||||
res = flashmem_spiffs_load((char*)destfn, mem, 5 + (chunk * 6));
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "\nSPIFFS upload failed");
|
||||
free(mem);
|
||||
|
|
|
@ -761,7 +761,7 @@ static int setCmdHint(const char *Cmd) {
|
|||
|
||||
static int setCmdPlotSliders(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref set plotsliders ",
|
||||
CLIParserInit(&ctx, "pref set plotsliders",
|
||||
"Set presistent preference of showing the plotslider control in the client",
|
||||
"pref set plotsliders --on"
|
||||
);
|
||||
|
@ -803,7 +803,7 @@ static int setCmdPlotSliders(const char *Cmd) {
|
|||
|
||||
static int setCmdSavePaths(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref set savepath",
|
||||
CLIParserInit(&ctx, "pref set savepaths",
|
||||
"Set presistent preference of file paths in the client",
|
||||
"pref set savepaths --dump /home/mydumpfolder -> all dump files will be saved into this folder\n"
|
||||
"pref set savepaths --def /home/myfolder -c -> create if needed, all files will be saved into this folder"
|
||||
|
@ -930,36 +930,113 @@ static int setCmdBarMode(const char *Cmd) {
|
|||
}
|
||||
|
||||
static int getCmdEmoji(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get emoji",
|
||||
"Get preference of using emojis in the client",
|
||||
"pref get emoji"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showEmojiState(prefShowNone);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int getCmdHint(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get hints",
|
||||
"Get preference of showing hint messages in the client",
|
||||
"pref get hints"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showHintsState(prefShowNone);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int getCmdColor(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get color",
|
||||
"Get preference of using colors in the client",
|
||||
"pref get color"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showColorState(prefShowNone);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int getCmdDebug(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get clientdebug",
|
||||
"Get preference of using clientside debug level",
|
||||
"pref get clientdebug"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showClientDebugState(prefShowNone);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int getCmdPlotSlider(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get plotsliders",
|
||||
"Get preference of showing the plotslider control in the client",
|
||||
"pref get plotsliders"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showPlotSliderState(prefShowNone);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int getCmdBarMode(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get barmode",
|
||||
"Get preference of HF/LF tune command styled output in the client",
|
||||
"pref get barmode"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showBarModeState(prefShowNone);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int getCmdSavePaths(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref get savepaths",
|
||||
"Get preference of file paths in the client",
|
||||
"pref get savepaths"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
showSavePathState(spDefault, prefShowNone);
|
||||
showSavePathState(spDump, prefShowNone);
|
||||
showSavePathState(spTrace, prefShowNone);
|
||||
|
@ -1008,6 +1085,17 @@ static int CmdPrefSet(const char *Cmd) {
|
|||
}
|
||||
|
||||
static int CmdPrefShow(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "pref show",
|
||||
"Show all persistent preferences",
|
||||
"pref show"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (session.preferences_loaded) {
|
||||
char *fn = prefGetFilename();
|
||||
|
@ -1043,8 +1131,8 @@ static int CmdPrefSave (const char *Cmd) {
|
|||
*/
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"get", CmdPrefGet, AlwaysAvailable, "Get a preference"},
|
||||
{"set", CmdPrefSet, AlwaysAvailable, "Set a preference"},
|
||||
{"get", CmdPrefGet, AlwaysAvailable, "{ Get a preference }"},
|
||||
{"set", CmdPrefSet, AlwaysAvailable, "{ Set a preference }"},
|
||||
{"show", CmdPrefShow, AlwaysAvailable, "Show all preferences"},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
};
|
||||
|
|
|
@ -183,45 +183,54 @@ void print_hex(const uint8_t *data, const size_t len) {
|
|||
}
|
||||
|
||||
void print_hex_break(const uint8_t *data, const size_t len, uint8_t breaks) {
|
||||
if (data == NULL || len == 0) return;
|
||||
if (data == NULL || len == 0 || breaks == 0) return;
|
||||
|
||||
int rownum = 0;
|
||||
PrintAndLogEx(NORMAL, "[%02d] | " NOLF, rownum);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
|
||||
PrintAndLogEx(NORMAL, "%02X " NOLF, data[i]);
|
||||
|
||||
// check if a line break is needed
|
||||
if (breaks > 0 && !((i + 1) % breaks) && (i + 1 < len)) {
|
||||
++rownum;
|
||||
PrintAndLogEx(NORMAL, "\n[%02d] | " NOLF, rownum);
|
||||
uint16_t rownum = 0;
|
||||
int i;
|
||||
for (i = 0; i < len; i += breaks, rownum++) {
|
||||
if (len - i < breaks) { // incomplete block, will be treated out of the loop
|
||||
break;
|
||||
}
|
||||
PrintAndLogEx(INFO, "%02u | %s", rownum, sprint_hex_ascii(data + i, breaks));
|
||||
}
|
||||
|
||||
// the last odd bytes
|
||||
uint8_t mod = len % breaks;
|
||||
|
||||
if (mod) {
|
||||
char buf[UTIL_BUFFER_SIZE_SPRINT + 3];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
hex_to_buffer((uint8_t *)buf, data + i, mod, (sizeof(buf) - 1), 0, 1, true);
|
||||
|
||||
// add the spaces...
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " ");
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod));
|
||||
PrintAndLogEx(INFO, "%02u | %s", rownum, buf);
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
||||
void print_buffer(const uint8_t *data, const size_t len, int level) {
|
||||
static void print_buffer_ex(const uint8_t *data, const size_t len, int level, uint8_t breaks) {
|
||||
|
||||
if (len < 1)
|
||||
return;
|
||||
|
||||
char buf[UTIL_BUFFER_SIZE_SPRINT + 3];
|
||||
int i;
|
||||
for (i = 0; i < len; i += 16) {
|
||||
if (len - i < 16) { // incomplete block, will be treated out of the loop
|
||||
for (i = 0; i < len; i += breaks) {
|
||||
if (len - i < breaks) { // incomplete block, will be treated out of the loop
|
||||
break;
|
||||
}
|
||||
// (16 * 3) + (16) + + 1
|
||||
memset(buf, 0, sizeof(buf));
|
||||
sprintf(buf, "%*s%02x: ", (level * 4), " ", i);
|
||||
|
||||
hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, 16, (sizeof(buf) - strlen(buf) - 1), 0, 1, true);
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, 16));
|
||||
hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, breaks, (sizeof(buf) - strlen(buf) - 1), 0, 1, true);
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, breaks));
|
||||
PrintAndLogEx(INFO, "%s", buf);
|
||||
}
|
||||
|
||||
// the last odd bytes
|
||||
uint8_t mod = len % 16;
|
||||
uint8_t mod = len % breaks;
|
||||
|
||||
if (mod) {
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
@ -229,13 +238,17 @@ void print_buffer(const uint8_t *data, const size_t len, int level) {
|
|||
hex_to_buffer((uint8_t *)(buf + strlen(buf)), data + i, mod, (sizeof(buf) - strlen(buf) - 1), 0, 1, true);
|
||||
|
||||
// add the spaces...
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((16 - mod) * 3), " ");
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%*s", ((breaks - mod) * 3), " ");
|
||||
|
||||
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "| %s", sprint_ascii(data + i, mod));
|
||||
PrintAndLogEx(INFO, "%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
void print_buffer(const uint8_t *data, const size_t len, int level) {
|
||||
print_buffer_ex(data, len, level, 16);
|
||||
}
|
||||
|
||||
void print_blocks(uint32_t *data, size_t len) {
|
||||
PrintAndLogEx(SUCCESS, "Blk | Data ");
|
||||
PrintAndLogEx(SUCCESS, "----+------------");
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
# T5577 Introduction Guide
|
||||
|
||||
### Based on RRG proxmark3 fork.
|
||||
### Based on RRG/Iceman Proxmark3 repo
|
||||
|
||||
### Ver.1 8 Sep 2019
|
||||
### Ver.2 7 March 2021
|
||||
|
||||
| Contents |
|
||||
| ----------------------------------------------------------------------------------- |
|
||||
|
@ -23,17 +24,17 @@
|
|||
|
||||
## Introduction
|
||||
|
||||
The T5577 is a generic LF (Low Frequency) RFID card the is used in the
|
||||
The T5577 is a generic LF (Low Frequency) RFID card that is used in the
|
||||
125 Khz frequency space. It is a good card to use to learn about RFID and
|
||||
learn how to use the proxmark3.
|
||||
|
||||
It is highly recommend that when learning about RFID that learning how
|
||||
It is highly recommended that when learning about RFID that learning how
|
||||
to read the data sheets be near the top of the list. It can be very hard
|
||||
as the data sheet will hold the information you need, but you don’t yet
|
||||
know what it means. As such, I will attempt to point to sections of the
|
||||
data sheet and would highly advise that you look at the data sheet as
|
||||
you go. Overtime the data sheet may change, as a result things may not
|
||||
always be reference correctly.
|
||||
always be referenced correctly.
|
||||
|
||||
As at writing this guide, the data sheet can be found at :
|
||||
|
||||
|
@ -63,9 +64,9 @@ the chip how to behave.
|
|||
|
||||
## What data is on my T5577
|
||||
|
||||
Let’s have a look and see what a card might look in the proxmark3
|
||||
Let’s have a look and see what a card might look like in the proxmark3
|
||||
software. Since we can change the configuration of how the T5577 will
|
||||
output data, the proxmark3 software need to work out how to interpreted
|
||||
output data, the proxmark3 software needs to work out how to interpret
|
||||
the data it receives, we do this with the following command.
|
||||
|
||||
It should be noted that the T5577 has many clones. As such the default
|
||||
|
@ -77,16 +78,18 @@ examples shown, it will be assumed you have run the detect command.
|
|||
```
|
||||
[usb] pm3 --> lf t55xx detect
|
||||
```
|
||||
You should see a results simular to the following:
|
||||
You should see a results similar to the following:
|
||||
```
|
||||
Chip Type : T55x7
|
||||
Modulation : ASK
|
||||
Bit Rate : 2 - RF/32
|
||||
Inverted : No
|
||||
Offset : 32
|
||||
Seq. Term. : Yes
|
||||
Block0 : 0x000880E0
|
||||
Downlink Mode : default/fixed bit length
|
||||
[=] Chip type......... T55x7
|
||||
[=] Modulation........ ASK
|
||||
[=] Bit rate.......... 2 - RF/32
|
||||
[=] Inverted.......... No
|
||||
[=] Offset............ 33
|
||||
[=] Seq. terminator... Yes
|
||||
[=] Block0............ 000880E0 (auto detect)
|
||||
[=] Downlink mode..... default/fixed bit length
|
||||
[=] Password set...... No
|
||||
|
||||
```
|
||||
Now that the proxmark3 has detected a T55x7 chip, and found some
|
||||
information about it, we should be able to see all the data on the chip.
|
||||
|
@ -99,20 +102,20 @@ Your results should look similar to the following:
|
|||
[+] blk | hex data | binary | ascii
|
||||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
|
||||
[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 01 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 02 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 03 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 04 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 05 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 06 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 07 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] Reading Page 1:
|
||||
[+] blk | hex data | binary | ascii
|
||||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
|
||||
[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H
|
||||
[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#.
|
||||
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 01 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 02 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 03 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
```
|
||||
I will cover the meaning of this data as we go, but for now, lets keep
|
||||
it simple.
|
||||
|
@ -123,7 +126,7 @@ The basic function of using the proxmark3 with rfid cards is to read and
|
|||
write data. This reading and writing must be done in the correct way
|
||||
needed for the chip (and its configuration). Lucky for us, the
|
||||
developers have done a great job and gave us commands. What we need to
|
||||
know is that with the T5577 data is read/written one complete block at a
|
||||
know is that with the T5577, data is read/written one complete block at a
|
||||
time. Each block holds 32 bits of data (hence the binary output shown)
|
||||
|
||||
Since we know that the card has data and configuration blocks, lets say
|
||||
|
@ -140,23 +143,23 @@ can see the card)
|
|||
as, run a low frequency (lf) command for the T55xx chip (t55xx) and
|
||||
read block (b) number 1.
|
||||
```
|
||||
[usb] pm3 --> lf t55xx read b 1
|
||||
[usb] pm3 --> lf t55xx read -b 1
|
||||
```
|
||||
result:
|
||||
```
|
||||
[+] Reading Page 0:
|
||||
[+] blk | hex data | binary | ascii
|
||||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 01 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
```
|
||||
Note: Depending on the history of your card your data may vary, but
|
||||
Note: Depending on the history of your card, your data may vary, but
|
||||
should match the dump data.
|
||||
|
||||
2) Write some new data into block 1 on the card.
|
||||
|
||||
We use the d option to supply the data ‘12345678’
|
||||
We use the -d option to supply the data ‘12345678’
|
||||
```
|
||||
[usb] pm3 --> lf t55xx write b 1 d 12345678
|
||||
[usb] pm3 --> lf t55xx write -b 1 -d 12345678
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -164,7 +167,7 @@ can see the card)
|
|||
```
|
||||
3) Now, lets check if the data was written.
|
||||
```
|
||||
[usb] pm3 --> lf t55xx read b 1
|
||||
[usb] pm3 --> lf t55xx read -b 1
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -174,8 +177,8 @@ can see the card)
|
|||
[+] 01 | 12345678 | 00010010001101000101011001111000 | .4Vx
|
||||
```
|
||||
4) The data is written in Hexadecimal. A single hex digit holds 4 bits
|
||||
of data. So to store 32 bits in a block we need to supply 8 hex
|
||||
digits (8 \* 4 = 32). If you are familiar with hex and binary do a
|
||||
of data. So to store 32 bits in a block, we need to supply 8 hex
|
||||
digits (8 \* 4 = 32). If you are not familiar with hex and binary do a
|
||||
little bit of home work to learn. The following is a quick start.
|
||||
|
||||
| Hex | Binary | Decimal |
|
||||
|
@ -202,7 +205,7 @@ can see the card)
|
|||
|
||||
Lets try and write 89ABCDEF
|
||||
```
|
||||
[usb] pm3 --> lf t55xx write b 1 d 89abcdef
|
||||
[usb] pm3 --> lf t55xx write -b 1 -d 89abcdef
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -210,7 +213,7 @@ can see the card)
|
|||
```
|
||||
and check
|
||||
```
|
||||
[usb] pm3 --> lf t55xx read b 1
|
||||
[usb] pm3 --> lf t55xx read -b 1
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -235,31 +238,34 @@ result:
|
|||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
|
||||
[+] 01 | 89ABCDEF | 10001001101010111100110111101111 | ....
|
||||
[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 07 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 02 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 03 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 04 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 05 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 06 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 07 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] Reading Page 1:
|
||||
[+] blk | hex data | binary | ascii
|
||||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
|
||||
[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H
|
||||
[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#.
|
||||
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 01 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 02 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 03 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
```
|
||||
|
||||
Practice reading and writing to blocks 1 to 7 until you are happy you
|
||||
Practice reading and writing to blocks 1 to 6 until you are happy you
|
||||
can do it and get the results you wanted (i.e. the data you want stored
|
||||
is written to the block you want it stored in).
|
||||
is written to the block you want it stored in). I recommend staying
|
||||
away from block 7 as this is where the password is stored, if used.
|
||||
If you forget this data/password, you wont be able to read or write
|
||||
to the card.
|
||||
|
||||
## How do I use a password
|
||||
|
||||
This can be a little tricky for beginners.
|
||||
***If you forget your password you will lose access to your card***.
|
||||
|
||||
To tell the T5577 to use a password we have to change the data in the
|
||||
To tell the T5577 to use a password, we have to change the data in the
|
||||
configuration block (0). To help learn this and make it as simple as I
|
||||
can, please read and follow exactly. If your results DON’T match 100% as
|
||||
required, please do not proceed.
|
||||
|
@ -272,9 +278,10 @@ required, please do not proceed.
|
|||
```
|
||||
Result:
|
||||
```
|
||||
[=] Begin wiping T55x7 tag
|
||||
[=] Target T55x7 tag
|
||||
[=] Default configuration block 000880E0
|
||||
|
||||
[=] Default configation block 000880E0
|
||||
[=] Begin wiping...
|
||||
[=] Writing page 0 block: 00 data: 0x000880E0
|
||||
[=] Writing page 0 block: 01 data: 0x00000000
|
||||
[=] Writing page 0 block: 02 data: 0x00000000
|
||||
|
@ -291,14 +298,15 @@ required, please do not proceed.
|
|||
```
|
||||
result:
|
||||
```
|
||||
Chip Type : T55x7
|
||||
Modulation : ASK
|
||||
Bit Rate : 2 - RF/32
|
||||
Inverted : No
|
||||
Offset : 32
|
||||
Seq. Term. : Yes
|
||||
Block0 : 0x000880E0
|
||||
Downlink Mode : default/fixed bit length
|
||||
[=] Chip type......... T55x7
|
||||
[=] Modulation........ ASK
|
||||
[=] Bit rate.......... 2 - RF/32
|
||||
[=] Inverted.......... No
|
||||
[=] Offset............ 33
|
||||
[=] Seq. terminator... Yes
|
||||
[=] Block0............ 000880E0 (auto detect)
|
||||
[=] Downlink mode..... default/fixed bit length
|
||||
[=] Password set...... No
|
||||
```
|
||||
|
||||
If block 0 does not hold the hex data **0x00088040 resolve this
|
||||
|
@ -309,7 +317,7 @@ required, please do not proceed.
|
|||
|
||||
The password is saved in block 7 of page 0.
|
||||
```
|
||||
[usb] pm3 --> lf t55xx write b 7 d 12345678
|
||||
[usb] pm3 --> lf t55xx write -b 7 -d 12345678
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -326,20 +334,20 @@ required, please do not proceed.
|
|||
[+] blk | hex data | binary | ascii
|
||||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
|
||||
[+] 01 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 02 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 04 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 05 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 06 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 01 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 02 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 03 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 04 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 05 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 06 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 07 | 12345678 | 00010010001101000101011001111000 | .4Vx
|
||||
[+] Reading Page 1:
|
||||
[+] blk | hex data | binary | ascii
|
||||
[+] ----+----------+----------------------------------+-------
|
||||
[+] 00 | 000880E0 | 00000000000010001000000011100000 | ....
|
||||
[+] 01 | E0150A48 | 11100000000101010000101001001000 | ...H
|
||||
[+] 02 | 2D782308 | 00101101011110000010001100001000 | -x#.
|
||||
[+] 03 | FFFFFFFF | 11111111111111111111111111111111 | ....
|
||||
[+] 01 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 02 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
[+] 03 | 00000000 | 00000000000000000000000000000000 | ....
|
||||
```
|
||||
***Important : If block 0 and block 7 don’t match exactly, do not continue.***
|
||||
|
||||
|
@ -353,7 +361,7 @@ required, please do not proceed.
|
|||

|
||||
|
||||
We will cover other things in the configuration later. But the key
|
||||
note here is we ONLY want to change bit 28 and nothing else.
|
||||
note here, is we ONLY want to change bit 28 and nothing else.
|
||||
|
||||
Current Block 0 : ***00088040***
|
||||
New Block 0 : ***00088050***
|
||||
|
@ -361,7 +369,7 @@ required, please do not proceed.
|
|||
To understand what happened to get from 00088040 to 00088050 we need
|
||||
to look at the binary data.
|
||||
|
||||
While this can be confusing it is important to understand this as we
|
||||
While this can be confusing, it is important to understand this as we
|
||||
do more advanced things.
|
||||
|
||||
Bit Location (28)
|
||||
|
@ -375,7 +383,7 @@ required, please do not proceed.
|
|||
|
||||
|
||||
|
||||
See how in the above we change the bit in location 28 from a 0 to 1
|
||||
See how in the above we changed the bit in location 28 from a 0 to 1
|
||||
0 = No Password, 1 = Use Password
|
||||
|
||||
Note how we did NOT change any other part of the configuration, only bit 28.
|
||||
|
@ -389,7 +397,7 @@ required, please do not proceed.
|
|||
If you have completed all steps and have the exact same results, we are
|
||||
ready to apply the new configuration.
|
||||
```
|
||||
[usb] pm3 --> lf t55xx write b 0 d 00088050
|
||||
[usb] pm3 --> lf t55xx write -b 0 -d 00088050
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -410,25 +418,27 @@ required, please do not proceed.
|
|||
card.
|
||||
|
||||
Lets try again, but this time supply the password. We use the option
|
||||
p followed by the password.
|
||||
-p followed by the password.
|
||||
```
|
||||
[usb] pm3 --> lf t55 detect p 12345678
|
||||
[usb] pm3 --> lf t55 detect -p 12345678
|
||||
```
|
||||
result:
|
||||
```
|
||||
Chip Type : T55x7
|
||||
Modulation : ASK
|
||||
Bit Rate : 2 - RF/32
|
||||
Inverted : No
|
||||
Offset : 32
|
||||
Seq. Term. : Yes
|
||||
Block0 : 0x00088050
|
||||
Downlink Mode : default/fixed bit length
|
||||
[=] Chip type......... T55x7
|
||||
[=] Modulation........ ASK
|
||||
[=] Bit rate.......... 2 - RF/32
|
||||
[=] Inverted.......... No
|
||||
[=] Offset............ 33
|
||||
[=] Seq. terminator... Yes
|
||||
[=] Block0............ 00088050 (auto detect)
|
||||
[=] Downlink mode..... default/fixed bit length
|
||||
[=] Password set...... Yes
|
||||
[=] Password.......... 12345678
|
||||
```
|
||||
|
||||
7) Write a block of data with a password
|
||||
```
|
||||
[usb] pm3 --> lf t55xx write b 1 d 1234abcd p 12345678
|
||||
[usb] pm3 --> lf t55xx write -b 1 -d 1234abcd -p 12345678
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -445,7 +455,7 @@ required, please do not proceed.
|
|||
|
||||
The proxmark3 has a safety check\!
|
||||
```
|
||||
[usb] pm3 --> lf t55xx read b 1 p 12345678
|
||||
[usb] pm3 --> lf t55xx read -b 1 -p 12345678
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -455,12 +465,12 @@ required, please do not proceed.
|
|||
[!] Safety check: Could not detect if PWD bit is set in config block. Exits.
|
||||
```
|
||||
|
||||
Note that the proxmark3 did not read the block, the safty kicked in
|
||||
and wants us to confirm by supply the override option ‘o’.
|
||||
Note that the proxmark3 did not read the block, the safety kicked in
|
||||
and wants us to confirm by supply the override option ‘-o’.
|
||||
|
||||
Lets try again with the ‘o’ option as we know the password is set.
|
||||
Lets try again with the ‘-o’ option as we know the password is set.
|
||||
```
|
||||
[usb] pm3 --> lf t55xx read b 1 p 12345678 o
|
||||
[usb] pm3 --> lf t55xx read -b 1 -p 12345678 -o
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -479,14 +489,14 @@ required, please do not proceed.
|
|||
this from above.
|
||||
|
||||
Remember if we don’t know the config and write this config to the
|
||||
card, it will over write all other settings. This can recoved the
|
||||
card, it will overwrite all other settings. This can recover the
|
||||
card, but will lose any settings you may want. So it’s a good idea
|
||||
to read the config, and set bit 28 to 0, rather than just overwrite
|
||||
the config and change the way the card works.
|
||||
|
||||
In our examples we know what it should be : 00088040
|
||||
```
|
||||
[usb] pm3 --> lf t55xx write b 0 d 00088040 p 12345678
|
||||
[usb] pm3 --> lf t55xx write -b 0 -d 00088040 -p 12345678
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
@ -498,16 +508,17 @@ required, please do not proceed.
|
|||
```
|
||||
result:
|
||||
```
|
||||
Chip Type : T55x7
|
||||
Modulation : ASK
|
||||
Bit Rate : 2 - RF/32
|
||||
Inverted : No
|
||||
Offset : 32
|
||||
Seq. Term. : Yes
|
||||
Block0 : 0x00088040
|
||||
Downlink Mode : default/fixed bit length
|
||||
[=] Chip type......... T55x7
|
||||
[=] Modulation........ ASK
|
||||
[=] Bit rate.......... 2 - RF/32
|
||||
[=] Inverted.......... No
|
||||
[=] Offset............ 33
|
||||
[=] Seq. terminator... Yes
|
||||
[=] Block0............ 00088040 (auto detect)
|
||||
[=] Downlink mode..... default/fixed bit length
|
||||
[=] Password set...... No
|
||||
```
|
||||
Yes we can and we can see Block 0 is the correct config 00088040
|
||||
Yes we can! We can see Block 0 is the correct config 00088040
|
||||
|
||||
# Part 2 – Configuration Blocks
|
||||
|
||||
|
@ -523,7 +534,7 @@ from Block 0 in Page 0. It will use this in both default read mode
|
|||
(where is sends out the blocks from 1 to x on power up), as well as when
|
||||
it responds to commands.
|
||||
|
||||
In the Read To Card, the T5577 will encode the data using the settings
|
||||
In the Reader To Card, the T5577 will encode the data using the settings
|
||||
from Block 3 Page 1. If the command is not encoded correctly it will
|
||||
ignore the command and revert back to default read mode.
|
||||
|
||||
|
@ -533,10 +544,10 @@ For this configuration the settings chosen will be for the purpose of
|
|||
the card when used in production. E.G. If you want the card to act like
|
||||
an EM4100, then we need to choose the settings that work like the
|
||||
EM4100; same goes for others like HID. I am not going to cover these
|
||||
here, rather use an example. Others have collect these and posted on the
|
||||
forum.
|
||||
here, rather use an example. Others have collected these and posted on the
|
||||
forum or can be found by searching the web.
|
||||
|
||||
To get started lets look back at the tech sheet.
|
||||
To get started lets look back at the data sheet.
|
||||
|
||||

|
||||
|
||||
|
@ -571,14 +582,15 @@ password set (if not, review and get you card back to this state).
|
|||
|
||||
1) Lets turn you T5577 into an EM4100 with ID 1122334455
|
||||
```
|
||||
[usb] pm3 --> lf em 410x_write 1122334455 1
|
||||
[usb] pm3 --> lf em 410x clone --id 1122334455
|
||||
```
|
||||
result:
|
||||
```
|
||||
[+] Writing T55x7 tag with UID 0x1122334455 (clock rate: 64)
|
||||
#db# Started writing T55x7 tag ...
|
||||
#db# Clock rate: 64
|
||||
#db# Tag T55x7 written with 0xff8c65298c94a940
|
||||
[+] Preparing to clone EM4102 to T55x7 tag with ID 1122334455 (RF/64)
|
||||
[#] Clock rate: 64
|
||||
[#] Tag T55x7 written with 0xff8c65298c94a940
|
||||
|
||||
[+] Done
|
||||
```
|
||||
|
||||
2) Check this has work.
|
||||
|
@ -586,89 +598,84 @@ password set (if not, review and get you card back to this state).
|
|||
[usb] pm3 --> lf search
|
||||
```
|
||||
result:
|
||||
```
|
||||
[=] NOTE: some demods output possible binary
|
||||
[=] if it finds something that looks like a tag
|
||||
[=] False Positives ARE possible
|
||||
[=]
|
||||
[=] Checking for known tags...
|
||||
|
||||
[+] EM410x pattern found
|
||||
|
||||
EM TAG ID : 1122334455
|
||||
|
||||
Possible de-scramble patterns
|
||||
|
||||
Unique TAG ID : 8844CC22AA
|
||||
HoneyWell IdentKey {
|
||||
DEZ 8 : 03359829
|
||||
DEZ 10 : 0573785173
|
||||
DEZ 5.5 : 08755.17493
|
||||
DEZ 3.5A : 017.17493
|
||||
DEZ 3.5B : 034.17493
|
||||
DEZ 3.5C : 051.17493
|
||||
DEZ 14/IK2 : 00073588229205
|
||||
DEZ 15/IK3 : 000585269781162
|
||||
DEZ 20/ZK : 08080404121202021010
|
||||
}
|
||||
Other : 17493_051_03359829
|
||||
Pattern Paxton : 289899093 [0x11478255]
|
||||
Pattern 1 : 5931804 [0x5A831C]
|
||||
Pattern Sebury : 17493 51 3359829 [0x4455 0x33 0x334455]
|
||||
|
||||
[+] Valid EM410x ID found!
|
||||
|
||||
|
||||
[+] Chipset detection : T55xx found
|
||||
|
||||
[+] Try `lf t55xx` commands
|
||||
```
|
||||
[=] NOTE: some demods output possible binary
|
||||
[=] if it finds something that looks like a tag
|
||||
[=] False Positives ARE possible
|
||||
[=]
|
||||
[=] Checking for known tags...
|
||||
[=]
|
||||
[+] EM 410x ID 0F0368568B
|
||||
[+] EM410x ( RF/64 )
|
||||
[=] -------- Possible de-scramble patterns ---------
|
||||
[+] Unique TAG ID : F0C0166AD1
|
||||
[=] HoneyWell IdentKey
|
||||
[+] DEZ 8 : 06837899
|
||||
[+] DEZ 10 : 0057169547
|
||||
[+] DEZ 5.5 : 00872.22155
|
||||
[+] DEZ 3.5A : 015.22155
|
||||
[+] DEZ 3.5B : 003.22155
|
||||
[+] DEZ 3.5C : 104.22155
|
||||
[+] DEZ 14/IK2 : 00064481678987
|
||||
[+] DEZ 15/IK3 : 001034014845649
|
||||
[+] DEZ 20/ZK : 15001200010606101301
|
||||
[=]
|
||||
[+] Other : 22155_104_06837899
|
||||
[+] Pattern Paxton : 259822731 [0xF7C948B]
|
||||
[+] Pattern 1 : 9750181 [0x94C6A5]
|
||||
[+] Pattern Sebury : 22155 104 6837899 [0x568B 0x68 0x68568B]
|
||||
[=] ------------------------------------------------
|
||||
|
||||
[+] Valid EM410x ID found!
|
||||
|
||||
[+] Chipset detection: T55xx
|
||||
[?] Hint: try `lf t55xx` commands
|
||||
```
|
||||
Looks good.
|
||||
|
||||
3) Now lest see what the T5577 detect and info shows
|
||||
3) Now lets see what the T5577 detect and info shows
|
||||
```
|
||||
[usb] pm3 --> lf t55 detect
|
||||
```
|
||||
result:
|
||||
```
|
||||
[usb] pm3 --> lf t55 detect
|
||||
Chip Type : T55x7
|
||||
Modulation : ASK
|
||||
Bit Rate : 5 - RF/64
|
||||
Inverted : No
|
||||
Offset : 32
|
||||
Seq. Term. : Yes
|
||||
Block0 : 0x00148040
|
||||
Downlink Mode : default/fixed bit length
|
||||
[=] Chip type......... T55x7
|
||||
[=] Modulation........ ASK
|
||||
[=] Bit rate.......... 5 - RF/64
|
||||
[=] Inverted.......... No
|
||||
[=] Offset............ 33
|
||||
[=] Seq. terminator... Yes
|
||||
[=] Block0............ 00148040 (auto detect)
|
||||
[=] Downlink mode..... default/fixed bit length
|
||||
[=] Password set...... No
|
||||
```
|
||||
```
|
||||
[usb] pm3 --> lf t55xx info
|
||||
```
|
||||
result:
|
||||
```
|
||||
|
||||
-- T55x7 Configuration & Tag Information --------------------
|
||||
-------------------------------------------------------------
|
||||
Safer key : 0
|
||||
reserved : 0
|
||||
Data bit rate : 5 - RF/64
|
||||
eXtended mode : No
|
||||
Modulation : 8 - Manchester
|
||||
PSK clock frequency : 0 - RF/2
|
||||
AOR - Answer on Request : No
|
||||
OTP - One Time Pad : No
|
||||
Max block : 2
|
||||
Password mode : No
|
||||
Sequence Terminator : No
|
||||
Fast Write : No
|
||||
Inverse data : No
|
||||
POR-Delay : No
|
||||
-------------------------------------------------------------
|
||||
Raw Data - Page 0
|
||||
Block 0 : 0x00148040 00000000000101001000000001000000
|
||||
|
||||
Config block match : EM unique, Paxton
|
||||
-------------------------------------------------------------
|
||||
|
||||
[=] --- T55x7 Configuration & Information ---------
|
||||
[=] Safer key : 0
|
||||
[=] reserved : 0
|
||||
[=] Data bit rate : 5 - RF/64
|
||||
[=] eXtended mode : No
|
||||
[=] Modulation : 8 - Manchester
|
||||
[=] PSK clock frequency : 0 - RF/2
|
||||
[=] AOR - Answer on Request : No
|
||||
[=] OTP - One Time Pad : No
|
||||
[=] Max block : 2
|
||||
[=] Password mode : No
|
||||
[=] Sequence Terminator : No
|
||||
[=] Fast Write : No
|
||||
[=] Inverse data : No
|
||||
[=] POR-Delay : No
|
||||
[=] -------------------------------------------------------------
|
||||
[=] Raw Data - Page 0, block 0
|
||||
[=] 00148040 - 00000000000101001000000001000000
|
||||
[=] --- Fingerprint ------------
|
||||
[+] Config block match : EM unique, Paxton
|
||||
|
||||
```
|
||||
We can see that the info gave us more information and confirmed what
|
||||
we decoded by hand. But remember, the detect is still needed so the
|
||||
|
|
|
@ -561,7 +561,7 @@ EM is ASK
|
|||
HID Prox is FSK
|
||||
Indala is PSK
|
||||
|
||||
pm3 --> lf t55xx config FSK
|
||||
pm3 --> lf t55xx config --FSK
|
||||
```
|
||||
|
||||
Set timings to default
|
||||
|
@ -579,7 +579,7 @@ Write to T55xx block
|
|||
b <block> : block number to write. Between 0-7
|
||||
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
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
clear
|
||||
pref
|
||||
analyse foo
|
||||
data biphaserawdecode
|
||||
data detectclock
|
||||
|
@ -32,7 +31,6 @@ data print
|
|||
data samples
|
||||
data setdebugmode
|
||||
data tune
|
||||
hf 14b sriwrite
|
||||
hf 15 dump
|
||||
hf 15 info
|
||||
hf 15 raw
|
||||
|
@ -85,21 +83,6 @@ hf mf cview
|
|||
hf mf gen3uid
|
||||
hf mf gen3blk
|
||||
hf mf gen3freeze
|
||||
lf em 410x
|
||||
lf em 4x05
|
||||
lf em 4x50
|
||||
lf em 4x70
|
||||
lf hitag reader
|
||||
lf hitag sim
|
||||
lf hitag writer
|
||||
lf hitag dump
|
||||
lf hitag cc
|
||||
lf t55xx config
|
||||
lf t55xx dump
|
||||
lf t55xx info
|
||||
lf t55xx read
|
||||
lf t55xx resetread
|
||||
lf t55xx restore
|
||||
lf t55xx trace
|
||||
lf t55xx write
|
||||
script run
|
||||
|
|
|
@ -60,8 +60,8 @@ Standard password is normally (for T55xx): AA55BBBB
|
|||
|
||||
# Restore page1 data
|
||||
```
|
||||
lf t55xx write b 1 d E0150A48 1
|
||||
If t55xx write b 2 d 2D782308 1
|
||||
lf t55xx write -b 1 -d E0150A48 --pg1
|
||||
If t55xx write -b 2 -d 2D782308 --pg1
|
||||
```
|
||||
|
||||
# Sniffing the comms
|
||||
|
@ -69,12 +69,12 @@ The T55x7 protocol uses a pwm based protocol for writing to tags. In order to m
|
|||
|
||||
```
|
||||
-- after threshold limit 20 is triggered, skip 10000 samples before collecting samples.
|
||||
lf config s 10000 t 20
|
||||
lf config -s 10000 -t 20
|
||||
lf t55xx sniff
|
||||
|
||||
-- if you have a save trace from before, try
|
||||
data load -f xxxxxxx.pm3
|
||||
lf t55xx sniff 1
|
||||
lf t55xx sniff -1
|
||||
```
|
||||
|
||||
It uses the existing `lf sniff` command to collect the data, so setting that first as per normal sniffing is recommended. Once you have a sniff, you can "re-sniff" from the stored sniffed data and try different settings, if you think the data is not clean.
|
||||
|
|
156
doc/commands.md
156
doc/commands.md
|
@ -12,10 +12,9 @@ Check column "offline" for their availability.
|
|||
|------- |------- |-----------
|
||||
|`auto `|N |`Automated detection process for unknown tags`
|
||||
|`clear `|Y |`Clear screen`
|
||||
|`help `|Y |`This help. Use '<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`
|
||||
|`msleep `|Y |`Add a pause in milliseconds`
|
||||
|`pref `|Y |`Edit preferences`
|
||||
|`rem `|Y |`Add a text line in log file`
|
||||
|`quit `|Y |``
|
||||
|`exit `|Y |`Exit program`
|
||||
|
@ -606,10 +605,80 @@ Check column "offline" for their availability.
|
|||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`lf em help `|Y |`This help`
|
||||
|`lf em 410x `|Y |`EM 4102 commands...`
|
||||
|`lf em 4x05 `|Y |`EM 4205 / 4305 / 4369 / 4469 commands...`
|
||||
|`lf em 4x50 `|Y |`EM 4350 / 4450 commands...`
|
||||
|`lf em 4x70 `|Y |`EM 4070 / 4170 commands...`
|
||||
|
||||
|
||||
### lf em 410x
|
||||
|
||||
{ EM 4102 commands... }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`lf em 410x help `|Y |`This help`
|
||||
|`lf em 410x demod `|Y |`demodulate a EM410x tag from the GraphBuffer`
|
||||
|`lf em 410x reader `|N |`attempt to read and extract tag data`
|
||||
|`lf em 410x sim `|N |`simulate EM410x tag`
|
||||
|`lf em 410x brute `|N |`reader bruteforce attack by simulating EM410x tags`
|
||||
|`lf em 410x watch `|N |`watches for EM410x 125/134 kHz tags (option 'h' for 134)`
|
||||
|`lf em 410x spoof `|N |`watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)`
|
||||
|`lf em 410x clone `|N |`write EM410x UID to T55x7 or Q5/T5555 tag`
|
||||
|
||||
|
||||
### lf em 4x05
|
||||
|
||||
{ EM 4205 / 4305 / 4369 / 4469 commands... }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`lf em 4x05 help `|Y |`This help`
|
||||
|`lf em 4x05 brute `|N |`Bruteforce password`
|
||||
|`lf em 4x05 chk `|N |`Check passwords from dictionary`
|
||||
|`lf em 4x05 demod `|Y |`demodulate a EM4x05/EM4x69 tag from the GraphBuffer`
|
||||
|`lf em 4x05 dump `|N |`dump EM4x05/EM4x69 tag`
|
||||
|`lf em 4x05 info `|N |`tag information EM4x05/EM4x69`
|
||||
|`lf em 4x05 read `|N |`read word data from EM4x05/EM4x69`
|
||||
|`lf em 4x05 sniff `|Y |`Attempt to recover em4x05 commands from sample buffer`
|
||||
|`lf em 4x05 unlock `|N |`execute tear off against EM4x05/EM4x69`
|
||||
|`lf em 4x05 wipe `|N |`wipe EM4x05/EM4x69 tag`
|
||||
|`lf em 4x05 write `|N |`write word data to EM4x05/EM4x69`
|
||||
|
||||
|
||||
### lf em 4x50
|
||||
|
||||
{ EM 4350 / 4450 commands... }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`lf em 4x50 help `|Y |`This help`
|
||||
|`lf em 4x50 brute `|N |`guess password of EM4x50`
|
||||
|`lf em 4x50 chk `|N |`check passwords from dictionary`
|
||||
|`lf em 4x50 dump `|N |`dump EM4x50 tag`
|
||||
|`lf em 4x50 info `|N |`tag information EM4x50`
|
||||
|`lf em 4x50 login `|N |`login into EM4x50`
|
||||
|`lf em 4x50 rdbl `|N |`read word data from EM4x50`
|
||||
|`lf em 4x50 wrbl `|N |`write word data to EM4x50`
|
||||
|`lf em 4x50 writepwd `|N |`change password of EM4x50`
|
||||
|`lf em 4x50 wipe `|N |`wipe EM4x50 tag`
|
||||
|`lf em 4x50 reader `|N |`show standard read mode data of EM4x50`
|
||||
|`lf em 4x50 restore `|N |`restore EM4x50 dump to tag`
|
||||
|`lf em 4x50 sim `|N |`simulate EM4x50 tag`
|
||||
|`lf em 4x50 eload `|N |`upload dump of EM4x50 to emulator memory`
|
||||
|`lf em 4x50 esave `|N |`save emulator memory to file`
|
||||
|`lf em 4x50 eview `|N |`view EM4x50 content in emulator memory`
|
||||
|
||||
|
||||
### lf em 4x70
|
||||
|
||||
{ EM 4070 / 4170 commands... }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`lf em 4x70 help `|Y |`This help`
|
||||
|`lf em 4x70 info `|N |`Tag information EM4x70`
|
||||
|`lf em 4x70 write `|N |`Write EM4x70`
|
||||
|`lf em 4x70 unlock `|N |`Unlock EM4x70 for writing`
|
||||
|`lf em 4x70 auth `|N |`Authenticate EM4x70`
|
||||
|`lf em 4x70 writepin `|N |`Write PIN`
|
||||
|`lf em 4x70 writekey `|N |`Write Crypt Key`
|
||||
|
||||
|
||||
### lf fdxb
|
||||
|
@ -673,6 +742,7 @@ Check column "offline" for their availability.
|
|||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`lf hitag help `|Y |`This help`
|
||||
|`lf hitag eload `|N |`Load Hitag dump file into emulator memory`
|
||||
|`lf hitag list `|N |`List Hitag trace history`
|
||||
|`lf hitag info `|N |`Tag information`
|
||||
|`lf hitag reader `|N |`Act like a Hitag Reader`
|
||||
|
@ -905,7 +975,7 @@ Check column "offline" for their availability.
|
|||
|`lf t55xx info `|Y |`Show T55x7 configuration data (page 0/ blk 0)`
|
||||
|`lf t55xx p1detect `|N |`Try detecting if this is a t55xx tag by reading page 1`
|
||||
|`lf t55xx read `|N |`Read T55xx block data`
|
||||
|`lf t55xx resetread `|N |`Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)`
|
||||
|`lf t55xx resetread `|N |`Send Reset Cmd then lf read the stream to attempt to identify the start of it`
|
||||
|`lf t55xx restore `|N |`Restore T55xx card Page 0 / Page 1 blocks`
|
||||
|`lf t55xx trace `|Y |`Show T55x7 traceability data (page 1/ blk 0-1)`
|
||||
|`lf t55xx wakeup `|N |`Send AOR wakeup command`
|
||||
|
@ -953,13 +1023,34 @@ Check column "offline" for their availability.
|
|||
|------- |------- |-----------
|
||||
|`mem help `|Y |`This help`
|
||||
|`mem baudrate `|N |`Set Flash memory Spi baudrate`
|
||||
|`mem spiffs `|N |`High level SPI FileSystem Flash manipulation`
|
||||
|`mem info `|N |`Flash memory information`
|
||||
|`mem load `|N |`Load data into flash memory`
|
||||
|`mem dump `|N |`Dump data from flash memory`
|
||||
|`mem info `|N |`Flash memory information`
|
||||
|`mem load `|N |`Load data to flash memory`
|
||||
|`mem wipe `|N |`Wipe data from flash memory`
|
||||
|
||||
|
||||
### mem spiffs
|
||||
|
||||
{ SPI File system }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`mem spiffs help `|Y |`This help`
|
||||
|`mem spiffs copy `|N |`Copy a file to another (destructively) in SPIFFS file system`
|
||||
|`mem spiffs check `|N |`Check/try to defrag faulty/fragmented file system`
|
||||
|`mem spiffs dump `|N |`Dump a file from SPIFFS file system`
|
||||
|`mem spiffs info `|N |`Print file system info and usage statistics`
|
||||
|`mem spiffs mount `|N |`Mount the SPIFFS file system if not already mounted`
|
||||
|`mem spiffs remove `|N |`Remove a file from SPIFFS file system`
|
||||
|`mem spiffs rename `|N |`Rename/move a file in SPIFFS file system`
|
||||
|`mem spiffs test `|N |`Test SPIFFS Operations`
|
||||
|`mem spiffs tree `|N |`Print the Flash memory file system tree`
|
||||
|`mem spiffs unmount `|N |`Un-mount the SPIFFS file system`
|
||||
|`mem spiffs upload `|N |`Upload file into SPIFFS file system`
|
||||
|`mem spiffs view `|N |`View file on SPIFFS file system`
|
||||
|`mem spiffs wipe `|N |`Wipe all files from SPIFFS file system * dangerous *`
|
||||
|
||||
|
||||
### reveng
|
||||
|
||||
{ CRC calculations from RevEng software... }
|
||||
|
@ -988,9 +1079,9 @@ Check column "offline" for their availability.
|
|||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`script help `|Y |`Usage info`
|
||||
|`script help `|Y |`This help`
|
||||
|`script list `|Y |`List available scripts`
|
||||
|`script run `|Y |`<name> -- execute a script`
|
||||
|`script run `|Y |`<name> - execute a script`
|
||||
|
||||
|
||||
### trace
|
||||
|
@ -1034,3 +1125,44 @@ Check column "offline" for their availability.
|
|||
|`wiegand decode `|Y |`Convert raw hex to decoded wiegand format (currently for HID Prox)`
|
||||
|
||||
|
||||
### pref
|
||||
|
||||
{ Edit preferences... }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`pref help `|Y |`This help`
|
||||
|`pref show `|Y |`Show all preferences`
|
||||
|
||||
|
||||
### pref get
|
||||
|
||||
{ Get a preference }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`pref get barmode `|Y |`Get bar mode preference`
|
||||
|`pref get clientdebug `|Y |`Get client debug level preference`
|
||||
|`pref get color `|Y |`Get color support preference`
|
||||
|`pref get savepaths `|Y |`Get file folder `
|
||||
|`pref get emoji `|Y |`Get emoji display preference`
|
||||
|`pref get hints `|Y |`Get hint display preference`
|
||||
|`pref get plotsliders `|Y |`Get plot slider display preference`
|
||||
|
||||
|
||||
### pref set
|
||||
|
||||
{ Set a preference }
|
||||
|
||||
|command |offline |description
|
||||
|------- |------- |-----------
|
||||
|`pref set help `|Y |`This help`
|
||||
|`pref set barmode `|Y |`Set bar mode`
|
||||
|`pref set clientdebug `|Y |`Set client debug level`
|
||||
|`pref set color `|Y |`Set color support`
|
||||
|`pref set emoji `|Y |`Set emoji display`
|
||||
|`pref set hints `|Y |`Set hint display`
|
||||
|`pref set savepaths `|Y |`... to be adjusted next ... `
|
||||
|`pref set plotsliders `|Y |`Set plot slider display`
|
||||
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ Page 3:
|
|||
* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details
|
||||
* to dump it: `mem dump f page3_dump o 196608 l 65536`
|
||||
* to erase it:
|
||||
* **Beware** it will erase your flash signature (see below) so better to back it up first as you won't be able to regenerate it by yourself!
|
||||
* It's possible to erase completely page 3 by erase the entire flash memory with the voluntarily undocumented command `mem wipe i`.
|
||||
* **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself!
|
||||
* edit the source code to enable Page 3 as a valid input in the `mem wipe` command.
|
||||
* Updating keys dictionaries doesn't require to erase page 3.
|
||||
|
||||
## Page3 Layout
|
||||
|
@ -64,7 +64,7 @@ Page3 is used as follows by the Proxmark3 RDV4 firmware:
|
|||
* length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure)
|
||||
|
||||
* **RSA SIGNATURE**, see below for details
|
||||
* offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F
|
||||
* offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015)
|
||||
* length: 128 bytes
|
||||
* offset should have been 0x3FF80 but historically it's one byte off and therefore the last byte of the flash is unused
|
||||
|
||||
|
@ -73,23 +73,39 @@ Page3 is used as follows by the Proxmark3 RDV4 firmware:
|
|||
To ensure your Proxmark3 RDV4 is not a counterfeit product, its external flash contains a RSA signature of the flash unique ID.
|
||||
You can verify it with: `mem info`
|
||||
|
||||
|
||||
Here below is a sample output of a RDV4 device.
|
||||
```
|
||||
[usb] pm3 --> mem info
|
||||
|
||||
[=] --- Flash memory Information ---------
|
||||
|
||||
[=] -------------------------------------------------------------
|
||||
[=] ID | xx xx xx xx xx xx xx xx
|
||||
[=] SHA1 | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
|
||||
[=] RSA SIGNATURE |
|
||||
[00] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
|
||||
[01] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
|
||||
[02] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
|
||||
[03] | xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx
|
||||
[=] KEY length | 128
|
||||
[+] RSA key validation ok
|
||||
[+] RSA Verification ok
|
||||
[usb] pm3 --> mem info
|
||||
|
||||
[=] --- Flash memory Information ---------
|
||||
[=] ID................... 25AD99A782A867D5
|
||||
[=] SHA1................. 67C3B9BA2FA90AD4B283926B70017066C082C156
|
||||
[+] Signature............ ( ok )
|
||||
|
||||
[=] --- RDV4 RSA signature ---------------
|
||||
[=] C7C7DF7FA3A2391A2B36E97D227C746ED8BB475E8766F54A13BAA9AAB29299BE
|
||||
[=] 37546AACCC29157ABF8AFBF3A1CFB24275442D565F7E996C6B08090528ADE25E
|
||||
[=] ED1498E3089C72C68348D83CBD13F1247327BDBC9D75B09ECE3E051E19FE19BB
|
||||
[=] 98CB038757F2EDFD2DC5060D05C3296BC19A6F768290D555DFD50407E0E13A70
|
||||
|
||||
[=] --- RDV4 RSA Public key --------------
|
||||
[=] Len.................. 128
|
||||
[=] Exponent............. 010001
|
||||
[=] Public key modulus N
|
||||
[=] E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF
|
||||
[=] 4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F
|
||||
[=] 9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33
|
||||
[=] DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5
|
||||
|
||||
[+] RSA public key validation.... ( ok )
|
||||
[+] RSA private key validation... ( ok )
|
||||
[+] RSA verification..... ( ok )
|
||||
[+] Genuine Proxmark3 RDV4 signature detected
|
||||
```
|
||||
|
||||
For a backup of the signature: `mem dump p f flash_signature_dump o 262015 l 128`
|
||||
# backup first!
|
||||
To make a backup of the signature to file:
|
||||
|
||||
`mem dump p f flash_signature_dump o 262015 l 128`
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ GND | 6
|
|||
3.3 | 1
|
||||
|
||||
# Where to find more information?
|
||||
There has been lots of articles and blogposts about recoving, debricking, JTAG your Proxmark3 and you find here below a sortiment of resources that will be of help.
|
||||
There has been lots of articles and blogposts about recovering, debricking, JTAG your Proxmark3 and you find here below an assortiment of resources that will be of help.
|
||||
|
||||
## Third party notes on using a BusPirate
|
||||
|
||||
|
|
|
@ -373,9 +373,9 @@ It was needed to tune pm3 RX usart `maxtry` :
|
|||
time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "lf read"
|
||||
6.28s
|
||||
|
||||
time client/proxmark3 -p /dev/ttyACM0 -c "mem dump f foo_usb"
|
||||
time client/proxmark3 -p /dev/ttyACM0 -c "mem dump -f foo_usb"
|
||||
1.48s
|
||||
time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "mem dump f foo_fpc"
|
||||
time client/proxmark3 -p /dev/ttyUSB0 -b 115200 -c "mem dump -f foo_fpc"
|
||||
25.34s
|
||||
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ Dictionaries used by the client will be copied to
|
|||
```
|
||||
|
||||
Here you find the default dictionaries used for commands like `hf mf chk`, `hf mf fchk`, `lf t55xx chk`
|
||||
A dictionary file is a text based file with one key per line in hexdecimal form.
|
||||
A dictionary file is a text based file with one key per line in hexadecimal form.
|
||||
The length of the key is decided by the Proxmark3 client for the different commands. All chars afterwards on line is ignored.
|
||||
if key isn't a hex number, the key is ignored.
|
||||
|
||||
|
|
|
@ -56,6 +56,10 @@ struct version_information {
|
|||
#define DBG_EXTENDED 4 // errors + info + debug + breaking debug messages
|
||||
extern int DBGLEVEL;
|
||||
|
||||
// tear-off
|
||||
extern uint16_t tearoff_delay_us;
|
||||
extern bool tearoff_enabled;
|
||||
|
||||
// reader voltage field detector
|
||||
#define MF_MINFIELDV 4000
|
||||
|
||||
|
|
|
@ -384,6 +384,15 @@ typedef struct {
|
|||
bool off;
|
||||
} PACKED tearoff_params_t;
|
||||
|
||||
// when writing to SPIFFS
|
||||
typedef struct {
|
||||
bool append : 1;
|
||||
uint16_t bytes_in_packet : 15;
|
||||
uint8_t fnlen;
|
||||
uint8_t fn[32];
|
||||
uint8_t data[];
|
||||
} PACKED flashmem_write_t;
|
||||
|
||||
// For the bootloader
|
||||
#define CMD_DEVICE_INFO 0x0000
|
||||
//#define CMD_SETUP_WRITE 0x0001
|
||||
|
@ -565,6 +574,8 @@ typedef struct {
|
|||
#define CMD_LF_HITAGS_READ 0x0373
|
||||
#define CMD_LF_HITAGS_WRITE 0x0375
|
||||
|
||||
#define CMD_LF_HITAG_ELOAD 0x0376
|
||||
|
||||
#define CMD_HF_ISO14443A_ANTIFUZZ 0x0380
|
||||
#define CMD_HF_ISO14443B_SIMULATE 0x0381
|
||||
#define CMD_HF_ISO14443B_SNIFF 0x0382
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
MYSRCPATHS = ../../common ../../common/crapto1
|
||||
MYSRCPATHS = ../../common ../../common/crapto1
|
||||
MYSRCS = crypto1.c crapto1.c bucketsort.c iso14443crc.c sleep.c
|
||||
MYINCLUDES = -I../../include -I../../common
|
||||
MYINCLUDES = -I../../include -I../../common
|
||||
MYCFLAGS =
|
||||
MYDEFS =
|
||||
MYLDLIBS =
|
||||
|
@ -8,7 +8,7 @@ ifneq ($(SKIPPTHREAD),1)
|
|||
MYLDLIBS += -lpthread
|
||||
endif
|
||||
|
||||
BINS = mf_nonce_brute
|
||||
BINS = mf_nonce_brute mf_trace_brute
|
||||
INSTALLTOOLS = $(BINS)
|
||||
|
||||
include ../../Makefile.host
|
||||
|
@ -21,3 +21,5 @@ ifneq (,$(findstring MINGW,$(platform)))
|
|||
endif
|
||||
|
||||
mf_nonce_brute : $(OBJDIR)/mf_nonce_brute.o $(MYOBJS)
|
||||
|
||||
mf_trace_brute : $(OBJDIR)/mf_trace_brute.o $(MYOBJS)
|
|
@ -85,7 +85,7 @@ Example with parity (from this trace http://www.proxmark.org/forum/viewtopic.php
|
|||
```
|
||||
=>
|
||||
```
|
||||
./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 3e709c8a
|
||||
./mf_nonce_brute 9c599b32 82a4166c 0000 a1e458ce 6eea41e0 0101 5cadf439 1001 8e0e5db9
|
||||
| | | | | | | | |
|
||||
+UID +nt_enc | +nr_enc +ar_enc | +at_enc | +encrypted next cmd
|
||||
+nt_par_err +at_par_err +at_par_err
|
||||
|
|
|
@ -14,10 +14,17 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include "crapto1/crapto1.h"
|
||||
#include "protocol.h"
|
||||
#include "iso14443crc.h"
|
||||
|
||||
#define AEND "\x1b[0m"
|
||||
#define _RED_(s) "\x1b[31m" s AEND
|
||||
#define _GREEN_(s) "\x1b[32m" s AEND
|
||||
#define _YELLOW_(s) "\x1b[33m" s AEND
|
||||
#define _CYAN_(s) "\x1b[36m" s AEND
|
||||
|
||||
#define odd_parity(i) (( (i) ^ (i)>>1 ^ (i)>>2 ^ (i)>>3 ^ (i)>>4 ^ (i)>>5 ^ (i)>>6 ^ (i)>>7 ^ 1) & 0x01)
|
||||
|
||||
// a global mutex to prevent interlaced printing from different threads
|
||||
|
@ -42,24 +49,149 @@ typedef struct thread_args {
|
|||
bool ev1;
|
||||
} targs;
|
||||
|
||||
#define ENC_LEN (200)
|
||||
typedef struct thread_key_args {
|
||||
int thread;
|
||||
int idx;
|
||||
uint32_t uid;
|
||||
uint32_t part_key;
|
||||
uint32_t nt_enc;
|
||||
uint32_t nr_enc;
|
||||
uint16_t enc_len;
|
||||
uint8_t enc[ENC_LEN]; // next encrypted command + a full read/write
|
||||
} targs_key;
|
||||
|
||||
//------------------------------------------------------------------
|
||||
uint8_t cmds[] = {
|
||||
ISO14443A_CMD_READBLOCK,
|
||||
ISO14443A_CMD_WRITEBLOCK,
|
||||
MIFARE_AUTH_KEYA,
|
||||
MIFARE_AUTH_KEYB,
|
||||
MIFARE_CMD_INC,
|
||||
MIFARE_CMD_DEC,
|
||||
MIFARE_CMD_RESTORE,
|
||||
MIFARE_CMD_TRANSFER
|
||||
|
||||
uint8_t cmds[8][2] = {
|
||||
{ISO14443A_CMD_READBLOCK, 18},
|
||||
{ISO14443A_CMD_WRITEBLOCK, 18},
|
||||
{MIFARE_AUTH_KEYA, 0},
|
||||
{MIFARE_AUTH_KEYB, 0},
|
||||
{MIFARE_CMD_INC, 6},
|
||||
{MIFARE_CMD_DEC, 6},
|
||||
{MIFARE_CMD_RESTORE, 6},
|
||||
{MIFARE_CMD_TRANSFER, 0}
|
||||
};
|
||||
|
||||
int global_counter = 0;
|
||||
int global_fin_flag = 0;
|
||||
int global_found = 0;
|
||||
int global_found_candidate = 0;
|
||||
uint64_t global_candiate_key = 0;
|
||||
size_t thread_count = 2;
|
||||
|
||||
static int param_getptr(const char *line, int *bg, int *en, int paramnum) {
|
||||
int i;
|
||||
int len = strlen(line);
|
||||
|
||||
*bg = 0;
|
||||
*en = 0;
|
||||
|
||||
// skip spaces
|
||||
while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++;
|
||||
if (*bg >= len) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < paramnum; i++) {
|
||||
while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++;
|
||||
while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++;
|
||||
|
||||
if (line[*bg] == '\0') return 1;
|
||||
}
|
||||
|
||||
*en = *bg;
|
||||
while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++;
|
||||
|
||||
(*en)--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen) {
|
||||
int bg, en;
|
||||
uint32_t temp;
|
||||
char buf[5] = {0};
|
||||
|
||||
if (param_getptr(line, &bg, &en, paramnum)) return 1;
|
||||
|
||||
*datalen = 0;
|
||||
|
||||
int indx = bg;
|
||||
while (line[indx]) {
|
||||
if (line[indx] == '\t' || line[indx] == ' ') {
|
||||
indx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isxdigit(line[indx])) {
|
||||
buf[strlen(buf) + 1] = 0x00;
|
||||
buf[strlen(buf)] = line[indx];
|
||||
} else {
|
||||
// if we have symbols other than spaces and hex
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (*datalen >= maxdatalen) {
|
||||
// if we dont have space in buffer and have symbols to translate
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (strlen(buf) >= 2) {
|
||||
sscanf(buf, "%x", &temp);
|
||||
data[*datalen] = (uint8_t)(temp & 0xff);
|
||||
*buf = 0;
|
||||
(*datalen)++;
|
||||
}
|
||||
|
||||
indx++;
|
||||
}
|
||||
|
||||
if (strlen(buf) > 0)
|
||||
//error when not completed hex bytes
|
||||
return 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex_len, const size_t hex_max_len,
|
||||
const size_t min_str_len, const size_t spaces_between, bool uppercase) {
|
||||
|
||||
if (buf == NULL) return;
|
||||
|
||||
char *tmp = (char *)buf;
|
||||
size_t i;
|
||||
memset(tmp, 0x00, hex_max_len);
|
||||
|
||||
size_t max_len = (hex_len > hex_max_len) ? hex_max_len : hex_len;
|
||||
|
||||
for (i = 0; i < max_len; ++i, tmp += 2 + spaces_between) {
|
||||
sprintf(tmp, (uppercase) ? "%02X" : "%02x", (unsigned int) hex_data[i]);
|
||||
|
||||
for (size_t j = 0; j < spaces_between; j++)
|
||||
sprintf(tmp + 2 + j, " ");
|
||||
}
|
||||
|
||||
i *= (2 + spaces_between);
|
||||
|
||||
size_t mlen = min_str_len > i ? min_str_len : 0;
|
||||
if (mlen > hex_max_len)
|
||||
mlen = hex_max_len;
|
||||
|
||||
for (; i < mlen; i++, tmp += 1)
|
||||
sprintf(tmp, " ");
|
||||
|
||||
// remove last space
|
||||
*tmp = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
static char *sprint_hex_inrow_ex(const uint8_t *data, const size_t len, const size_t min_str_len) {
|
||||
static char buf[100] = {0};
|
||||
hex_to_buffer((uint8_t *)buf, data, len, sizeof(buf) - 1, min_str_len, 0, true);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static uint16_t parity_from_err(uint32_t data, uint16_t par_err) {
|
||||
|
||||
uint16_t par = 0;
|
||||
|
@ -196,13 +328,33 @@ static bool candidate_nonce(uint32_t xored, uint32_t nt, bool ev1) {
|
|||
|
||||
static bool checkValidCmd(uint32_t decrypted) {
|
||||
uint8_t cmd = (decrypted >> 24) & 0xFF;
|
||||
for (int i = 0; i < sizeof(cmds); ++i) {
|
||||
if (cmd == cmds[i])
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (cmd == cmds[i][0])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkValidCmdByte(uint8_t *cmd, uint16_t n) {
|
||||
|
||||
bool ok = false;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
if (cmd[0] == cmds[i][0]) {
|
||||
|
||||
if (n >= 4)
|
||||
ok = CheckCrc14443(CRC_14443_A, cmd, 4);
|
||||
|
||||
if (cmds[i][1] > 0 && n >= cmds[i][1])
|
||||
ok = CheckCrc14443(CRC_14443_A, cmd + 4, cmds[i][1]);
|
||||
|
||||
if (ok) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool checkCRC(uint32_t decrypted) {
|
||||
uint8_t data[] = {
|
||||
(decrypted >> 24) & 0xFF,
|
||||
|
@ -227,14 +379,12 @@ static void *brute_thread(void *arguments) {
|
|||
|
||||
uint32_t p64 = 0;
|
||||
uint32_t count;
|
||||
int found = 0;
|
||||
// TC == 4 (
|
||||
// threads calls 0 ev1 == false
|
||||
// threads calls 0,1,2 ev1 == true
|
||||
for (count = args->idx; count < 0xFFFF; count += thread_count - 1) {
|
||||
|
||||
found = global_found;
|
||||
if (found) {
|
||||
if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -265,8 +415,8 @@ static void *brute_thread(void *arguments) {
|
|||
#endif
|
||||
if (cmd_enc) {
|
||||
uint32_t decrypted = ks4 ^ cmd_enc;
|
||||
printf("CMD enc(%08x)\n", cmd_enc);
|
||||
printf(" dec(%08x)\t", decrypted);
|
||||
printf("CMD enc( %08x )\n", cmd_enc);
|
||||
printf(" dec( %08x ) ", decrypted);
|
||||
|
||||
// check if cmd exists
|
||||
uint8_t isOK = checkValidCmd(decrypted);
|
||||
|
@ -275,12 +425,12 @@ static void *brute_thread(void *arguments) {
|
|||
// Add a crc-check.
|
||||
isOK = checkCRC(decrypted);
|
||||
if (isOK == false) {
|
||||
printf("<-- not a valid cmd\n");
|
||||
printf(_RED_("<-- not a valid cmd\n"));
|
||||
pthread_mutex_unlock(&print_lock);
|
||||
free(revstate);
|
||||
continue;
|
||||
} else {
|
||||
printf("<-- Valid cmd\n");
|
||||
printf("<-- valid cmd\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,32 +443,97 @@ static void *brute_thread(void *arguments) {
|
|||
free(revstate);
|
||||
|
||||
if (args->ev1) {
|
||||
printf("\nKey candidate: [%012" PRIx64 "]\n\n", key);
|
||||
// if it was EV1, we know for sure xxxAAAAAAAA recovery
|
||||
printf("\nKey candidate [ " _YELLOW_("....%08" PRIx64 )" ]\n\n", key & 0xFFFFFFFF);
|
||||
__sync_fetch_and_add(&global_found_candidate, 1);
|
||||
} else {
|
||||
printf("\nValid Key found: [%012" PRIx64 "]\n\n", key);
|
||||
printf("\nKey candidate [ " _GREEN_("....%08" PRIx64) " ]\n\n", key & 0xFFFFFFFF);
|
||||
__sync_fetch_and_add(&global_found, 1);
|
||||
}
|
||||
__sync_fetch_and_add(&global_candiate_key, key);
|
||||
//release lock
|
||||
pthread_mutex_unlock(&print_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *brute_key_thread(void *arguments) {
|
||||
|
||||
struct thread_key_args *args = (struct thread_key_args *) arguments;
|
||||
uint64_t key;
|
||||
uint8_t local_enc[args->enc_len];
|
||||
memcpy(local_enc, args->enc, args->enc_len);
|
||||
|
||||
for (uint64_t count = args->idx; count < 0xFFFF; count += thread_count) {
|
||||
|
||||
if (__atomic_load_n(&global_found, __ATOMIC_ACQUIRE) == 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
key = (count << 32 | args->part_key);
|
||||
|
||||
// Init cipher with key
|
||||
struct Crypto1State *pcs = crypto1_create(key);
|
||||
|
||||
// NESTED decrypt nt with help of new key
|
||||
crypto1_word(pcs, args->nt_enc ^ args->uid, 1);
|
||||
crypto1_word(pcs, args->nr_enc, 1);
|
||||
crypto1_word(pcs, 0, 0);
|
||||
crypto1_word(pcs, 0, 0);
|
||||
|
||||
// decrypt 22 bytes
|
||||
uint8_t dec[args->enc_len];
|
||||
for (int i = 0; i < args->enc_len; i++)
|
||||
dec[i] = crypto1_byte(pcs, 0x00, 0) ^ local_enc[i];
|
||||
|
||||
crypto1_deinit(pcs);
|
||||
|
||||
// check if cmd exists
|
||||
uint8_t isOK = checkValidCmdByte(dec, args->enc_len);
|
||||
if (isOK == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// lock this section to avoid interlacing prints from different threats
|
||||
pthread_mutex_lock(&print_lock);
|
||||
printf("\nenc: %s\n", sprint_hex_inrow_ex(local_enc, args->enc_len, 0));
|
||||
printf("dec: %s\n", sprint_hex_inrow_ex(dec, args->enc_len, 0));
|
||||
printf("\nValid Key found [ " _GREEN_("%012" PRIx64) " ]\n\n", key);
|
||||
pthread_mutex_unlock(&print_lock);
|
||||
__sync_fetch_and_add(&global_found, 1);
|
||||
break;
|
||||
}
|
||||
free(args);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int usage(void) {
|
||||
printf(" syntax: mf_nonce_brute <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(" nt = 8c42e64e\n");
|
||||
printf(" nt_par_err = 1011\n\n");
|
||||
printf("\n expected outcome:\n");
|
||||
printf(" KEY 0xFFFFFFFFFFFF == fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n");
|
||||
printf("\n");
|
||||
printf("syntax: mf_nonce_brute <uid> <nt> <nt_par_err> <nr> <ar> <ar_par_err> <at> <at_par_err> [<next_command>]\n\n");
|
||||
printf("how to convert trace data to needed input:\n");
|
||||
printf(" nt in trace = 8c! 42 e6! 4e!\n");
|
||||
printf(" nt = 8c42e64e\n");
|
||||
printf(" nt_par_err = 1011\n\n");
|
||||
printf("samples:\n");
|
||||
printf("\n");
|
||||
printf(" ./mf_nonce_brute fa247164 fb47c594 0000 71909d28 0c254817 1000 0dc7cfbd 1110\n");
|
||||
printf("\n");
|
||||
printf("**** Possible key candidate ****\n");
|
||||
printf("Key candidate: [ffffffffffff]\n");
|
||||
printf("\n");
|
||||
printf(" ./mf_nonce_brute 96519578 d7e3c6ac 0011 cd311951 9da49e49 0010 2bb22e00 0100 a4f7f398ebdb4e484d1cb2b174b939d18b469f3fa5d9caab\n");
|
||||
printf("\n");
|
||||
printf("enc: A4F7F398EBDB4E484D1CB2B174B939D18B469F3FA5D9CAABBFA018EC7E0CC5721DE2E590F64BD0A5B4EFCE71\n");
|
||||
printf("dec: 30084A24302F8102F44CA5020500A60881010104763930084A24302F8102F44CA5020500A608810101047639\n");
|
||||
printf("Valid Key found: [3b7e4fd575ad]\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("Mifare classic nested auth key recovery. Phase 1.\n");
|
||||
printf("\nMifare classic nested auth key recovery\n\n");
|
||||
|
||||
if (argc < 9) return usage();
|
||||
|
||||
|
@ -331,21 +546,27 @@ int main(int argc, char *argv[]) {
|
|||
sscanf(argv[7], "%x", &at_enc);
|
||||
sscanf(argv[8], "%x", &at_par_err);
|
||||
|
||||
if (argc > 9)
|
||||
sscanf(argv[9], "%x", &cmd_enc);
|
||||
int enc_len = 0;
|
||||
uint8_t enc[ENC_LEN] = {0}; // next encrypted command + a full read/write
|
||||
if (argc > 9) {
|
||||
// sscanf(argv[9], "%x", &cmd_enc);
|
||||
param_gethex_to_eol(argv[9], 0, enc, sizeof(enc), &enc_len);
|
||||
cmd_enc = (enc[0] << 24 | enc[1] << 16 | enc[2] << 8 | enc[3]);
|
||||
}
|
||||
|
||||
printf("-------------------------------------------------\n");
|
||||
printf("uid:\t\t%08x\n", uid);
|
||||
printf("nt encrypted:\t%08x\n", nt_enc);
|
||||
printf("nt parity err:\t%04x\n", nt_par_err);
|
||||
printf("nr encrypted:\t%08x\n", nr_enc);
|
||||
printf("ar encrypted:\t%08x\n", ar_enc);
|
||||
printf("ar parity err:\t%04x\n", ar_par_err);
|
||||
printf("at encrypted:\t%08x\n", at_enc);
|
||||
printf("at parity err:\t%04x\n", at_par_err);
|
||||
printf("----------- " _CYAN_("Phase 1") " ------------------------\n");
|
||||
printf("uid.................. %08x\n", uid);
|
||||
printf("nt encrypted......... %08x\n", nt_enc);
|
||||
printf("nt parity err........ %04x\n", nt_par_err);
|
||||
printf("nr encrypted......... %08x\n", nr_enc);
|
||||
printf("ar encrypted......... %08x\n", ar_enc);
|
||||
printf("ar parity err........ %04x\n", ar_par_err);
|
||||
printf("at encrypted......... %08x\n", at_enc);
|
||||
printf("at parity err........ %04x\n", at_par_err);
|
||||
|
||||
if (argc > 9)
|
||||
printf("next cmd enc:\t%08x\n\n", cmd_enc);
|
||||
if (argc > 9) {
|
||||
printf("next encrypted cmd... %s\n", sprint_hex_inrow_ex(enc, enc_len ,0));
|
||||
}
|
||||
|
||||
clock_t t1 = clock();
|
||||
uint16_t nt_par = parity_from_err(nt_enc, nt_par_err);
|
||||
|
@ -361,7 +582,7 @@ int main(int argc, char *argv[]) {
|
|||
thread_count = 2;
|
||||
#endif /* _WIN32 */
|
||||
|
||||
printf("\nBruteforce using %zu threads to find encrypted tagnonce last bytes\n", thread_count);
|
||||
printf("\nBruteforce using " _YELLOW_("%zu") " threads to find encrypted tagnonce last bytes\n", thread_count);
|
||||
|
||||
pthread_t threads[thread_count];
|
||||
|
||||
|
@ -386,6 +607,51 @@ int main(int argc, char *argv[]) {
|
|||
pthread_create(&threads[i + 1], NULL, brute_thread, (void *)b);
|
||||
}
|
||||
|
||||
// wait for threads to terminate:
|
||||
for (int i = 0; i < thread_count; ++i)
|
||||
pthread_join(threads[i], NULL);
|
||||
|
||||
t1 = clock() - t1;
|
||||
printf("execution time %.2f sec\n", (float)t1 / 1000000.0);
|
||||
|
||||
|
||||
if (!global_found && !global_found_candidate) {
|
||||
printf("\nFailed to find a key\n\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (enc_len < 4) {
|
||||
printf("Too few next cmd bytes, skipping phase 2\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
// reset thread signals
|
||||
global_found = 0;
|
||||
global_found_candidate = 0;
|
||||
|
||||
printf("\n----------- " _CYAN_("Phase 2") " ------------------------\n");
|
||||
printf("uid.......... %08x\n", uid);
|
||||
printf("partial key.. %08x\n", (uint32_t)(global_candiate_key & 0xFFFFFFFF));
|
||||
printf("nt enc....... %08x\n", nt_enc);
|
||||
printf("nr enc....... %08x\n", nr_enc);
|
||||
printf("next encrypted cmd: %s\n", sprint_hex_inrow_ex(enc, enc_len ,0));
|
||||
printf("\nStart bruteforce to find upper 16 bits of key\n");
|
||||
fflush(stdout);
|
||||
|
||||
// threads
|
||||
for (int i = 0; i < thread_count; ++i) {
|
||||
struct thread_key_args *b = malloc(sizeof(struct thread_key_args));
|
||||
b->thread = i;
|
||||
b->idx = i;
|
||||
b->uid = uid;
|
||||
b->part_key = (uint32_t)(global_candiate_key & 0xFFFFFFFF);
|
||||
b->nt_enc = nt_enc;
|
||||
b->nr_enc = nr_enc;
|
||||
b->enc_len = enc_len;
|
||||
memcpy(b->enc, enc, enc_len);
|
||||
pthread_create(&threads[i], NULL, brute_key_thread, (void *)b);
|
||||
}
|
||||
|
||||
// wait for threads to terminate:
|
||||
for (int i = 0; i < thread_count; ++i)
|
||||
pthread_join(threads[i], NULL);
|
||||
|
@ -394,10 +660,7 @@ int main(int argc, char *argv[]) {
|
|||
printf("\nFailed to find a key\n\n");
|
||||
}
|
||||
|
||||
t1 = clock() - t1;
|
||||
if (t1 > 0)
|
||||
printf("Execution time: %.0f ticks\n", (float)t1);
|
||||
|
||||
out:
|
||||
// clean up mutex
|
||||
pthread_mutex_destroy(&print_lock);
|
||||
return 0;
|
||||
|
|
315
tools/mf_nonce_brute/mf_trace_brute.c
Normal file
315
tools/mf_nonce_brute/mf_trace_brute.c
Normal 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;
|
||||
}
|
|
@ -102,7 +102,8 @@ def selftests():
|
|||
'pk': "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"},
|
||||
{'name': "DESFire EV2",
|
||||
'samples': ["042A41CAE45380", "B2769F8DDB575AEA2A680ADCA8FFED4FAB81A1E9908E2B82FE0FABB697BBD9B23835C416970E75768F12902ACA491349E94E6589EAF4F508",
|
||||
"045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33"],
|
||||
"045640CAE45380", "D34B53A8C2C100D700DEA1C4C0D0DE4409F3A418CD8D57C4F41F146E42AD9A55F014199ABBF5CA259C7799DB0AE20D5E77D4950AC7E95D33",
|
||||
"040D259A965B80","B158073A7100C88C3726F4299FA58311FC3CB18744686DE3F234928AD74578F5CAD7FCEC1DCB962ECC7CC000B8557B37F45B76DC6573A58F"],
|
||||
'pk': "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"},
|
||||
{'name': "DESFire EV3",
|
||||
'samples': ["04448BD2DB6B80", "5CBB5632795C8F15263FEFB095B51C7B541AFD914A1AE44EF6FB8AF605EDF13DBFEE6C3A2DB372245E671DFE0D42CB1F0D0B8FE67A89D2F6",
|
||||
|
@ -146,6 +147,7 @@ def selftests():
|
|||
for c in curvenames:
|
||||
for h in [None, "md5", "sha1", "sha256", "sha512"]:
|
||||
recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h)
|
||||
|
||||
if (len(recovered) == 1):
|
||||
pk = recovered.pop()
|
||||
pk = binascii.hexlify(pk).decode('utf8')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue