mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
Merge branch 'master' into extend-spiffs-partition-last-page
This commit is contained in:
commit
b1ba5b3ea6
14 changed files with 392 additions and 249 deletions
|
@ -3,13 +3,17 @@ All notable changes to this project will be documented in this file.
|
||||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||||
|
|
||||||
## [unreleased][unreleased]
|
## [unreleased][unreleased]
|
||||||
|
- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva)
|
||||||
|
- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva)
|
||||||
|
- Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001)
|
||||||
|
- Added `hf iclass trbl` to perform tear-off attacks on iClass (@antiklesys)
|
||||||
- Added support for connection to host device in all Docker envs (@doegox)
|
- Added support for connection to host device in all Docker envs (@doegox)
|
||||||
- Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox)
|
- Changed `hf 15 info` to show all type matches and check ST25TVxC signature (@doegox)
|
||||||
- Added initial support for ST25TN and its signature verification (@doegox)
|
- Added initial support for ST25TN and its signature verification (@doegox)
|
||||||
- Changed originality checks handling to refactor code and pk data (@doegox)
|
- Changed originality checks handling to refactor code and pk data (@doegox)
|
||||||
- Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001)
|
- Changed `uniq.yaml` workflow to be case-insensitive (@iceman1001)
|
||||||
- Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva)
|
- Fixed `mem load --mfc` not erasing all SPI flash blocks after extending to 4095 keys (@piotrva)
|
||||||
- Extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva)
|
- Changed extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva)
|
||||||
- Fixed DESFire D40 secure channel crypto (@nvx)
|
- Fixed DESFire D40 secure channel crypto (@nvx)
|
||||||
- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox)
|
- Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox)
|
||||||
- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva)
|
- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva)
|
||||||
|
|
|
@ -440,7 +440,41 @@ static void SendStatus(uint32_t wait) {
|
||||||
ModInfo();
|
ModInfo();
|
||||||
|
|
||||||
#ifdef WITH_FLASH
|
#ifdef WITH_FLASH
|
||||||
Flashmem_print_info();
|
DbpString(_CYAN_("Flash memory dictionary loaded"));
|
||||||
|
uint32_t num = 0;
|
||||||
|
|
||||||
|
if (exists_in_spiffs(MF_KEYS_FILE)) {
|
||||||
|
num = size_in_spiffs(MF_KEYS_FILE) / MF_KEY_LENGTH;
|
||||||
|
} else {
|
||||||
|
num = 0;
|
||||||
|
}
|
||||||
|
if (num > 0) {
|
||||||
|
Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE);
|
||||||
|
} else {
|
||||||
|
Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
|
||||||
|
num = size_in_spiffs(T55XX_KEYS_FILE) / T55XX_KEY_LENGTH;
|
||||||
|
} else {
|
||||||
|
num = 0;
|
||||||
|
}
|
||||||
|
if (num > 0) {
|
||||||
|
Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE);
|
||||||
|
} else {
|
||||||
|
Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exists_in_spiffs(ICLASS_KEYS_FILE)) {
|
||||||
|
num = size_in_spiffs(ICLASS_KEYS_FILE) / ICLASS_KEY_LENGTH;
|
||||||
|
} else {
|
||||||
|
num = 0;
|
||||||
|
}
|
||||||
|
if (num > 0) {
|
||||||
|
Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE);
|
||||||
|
} else {
|
||||||
|
Dbprintf(" iClass.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, ICLASS_KEYS_FILE);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
DbpString("");
|
DbpString("");
|
||||||
reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0);
|
reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0);
|
||||||
|
@ -2748,34 +2782,7 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload->startidx == DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k)) {
|
if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0xC);
|
|
||||||
} else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k)) {
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0x5);
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0x6);
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0x7);
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0x8);
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0x9);
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0xA);
|
|
||||||
} else if (payload->startidx == DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k)) {
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0xB);
|
|
||||||
} else if (payload->startidx == FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k)) {
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
Flash_CheckBusy(BUSY_TIMEOUT);
|
||||||
Flash_WriteEnable();
|
Flash_WriteEnable();
|
||||||
Flash_Erase4k(spi_flash_pages64k - 1, 0xF);
|
Flash_Erase4k(spi_flash_pages64k - 1, 0xF);
|
||||||
|
|
|
@ -37,7 +37,8 @@
|
||||||
#include "protocols.h"
|
#include "protocols.h"
|
||||||
#include "pmflash.h"
|
#include "pmflash.h"
|
||||||
#include "flashmem.h" // persistence on flash
|
#include "flashmem.h" // persistence on flash
|
||||||
#include "appmain.h" // print stack
|
#include "spiffs.h" // spiffs
|
||||||
|
#include "appmain.h" // print stack
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Notes about EM4xxx timings.
|
Notes about EM4xxx timings.
|
||||||
|
@ -324,31 +325,7 @@ void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FlashInit()) {
|
if (SPIFFS_OK == rdv40_spiffs_write(T55XX_CONFIG_FILE, (uint8_t*)&T55xx_Timing, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) {
|
||||||
BigBuf_free();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
uint16_t res = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
|
|
||||||
if (res == 0) {
|
|
||||||
FlashStop();
|
|
||||||
BigBuf_free();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN);
|
|
||||||
|
|
||||||
// delete old configuration
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
Flash_WriteEnable();
|
|
||||||
Flash_Erase4k(3, 0xD);
|
|
||||||
|
|
||||||
// write new
|
|
||||||
res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
|
|
||||||
|
|
||||||
if (res == T55XX_CONFIG_LEN && g_dbglevel > 1) {
|
|
||||||
DbpString("T55XX Config save " _GREEN_("success"));
|
DbpString("T55XX Config save " _GREEN_("success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,15 +340,23 @@ t55xx_configurations_t *getT55xxConfig(void) {
|
||||||
void loadT55xxConfig(void) {
|
void loadT55xxConfig(void) {
|
||||||
#ifdef WITH_FLASH
|
#ifdef WITH_FLASH
|
||||||
|
|
||||||
if (!FlashInit()) {
|
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
|
||||||
|
|
||||||
|
uint32_t size = 0;
|
||||||
|
if (exists_in_spiffs(T55XX_CONFIG_FILE)) {
|
||||||
|
size = size_in_spiffs(T55XX_CONFIG_FILE);
|
||||||
|
}
|
||||||
|
if (size == 0) {
|
||||||
|
Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_CONFIG_FILE);
|
||||||
|
BigBuf_free();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *buf = BigBuf_malloc(T55XX_CONFIG_LEN);
|
if (SPIFFS_OK != rdv40_spiffs_read(T55XX_CONFIG_FILE, buf, T55XX_CONFIG_LEN, RDV40_SPIFFS_SAFETY_SAFE)) {
|
||||||
|
Dbprintf("Spiffs file: %s cannot be read.", T55XX_CONFIG_FILE);
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
BigBuf_free();
|
||||||
uint16_t isok = Flash_ReadDataCont(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN);
|
return;
|
||||||
FlashStop();
|
}
|
||||||
|
|
||||||
// verify read mem is actual data.
|
// verify read mem is actual data.
|
||||||
uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN;
|
uint8_t cntA = T55XX_CONFIG_LEN, cntB = T55XX_CONFIG_LEN;
|
||||||
|
@ -380,6 +365,7 @@ void loadT55xxConfig(void) {
|
||||||
if (buf[i] == 0x00) cntB--;
|
if (buf[i] == 0x00) cntB--;
|
||||||
}
|
}
|
||||||
if (!cntA || !cntB) {
|
if (!cntA || !cntB) {
|
||||||
|
Dbprintf("Spiffs file: %s does not malformed or empty.", T55XX_CONFIG_FILE);
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -387,7 +373,7 @@ void loadT55xxConfig(void) {
|
||||||
if (buf[0] != 0xFF) // if not set for clear
|
if (buf[0] != 0xFF) // if not set for clear
|
||||||
memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN);
|
memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN);
|
||||||
|
|
||||||
if (isok == T55XX_CONFIG_LEN) {
|
if (size == T55XX_CONFIG_LEN) {
|
||||||
if (g_dbglevel > 1) DbpString("T55XX Config load success");
|
if (g_dbglevel > 1) DbpString("T55XX Config load success");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2146,29 +2132,34 @@ void T55xx_ChkPwds(uint8_t flags, bool ledcontrol) {
|
||||||
#ifdef WITH_FLASH
|
#ifdef WITH_FLASH
|
||||||
|
|
||||||
BigBuf_Clear_EM();
|
BigBuf_Clear_EM();
|
||||||
uint16_t isok = 0;
|
uint32_t size = 0;
|
||||||
uint8_t counter[2] = {0x00, 0x00};
|
|
||||||
isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), counter, sizeof(counter));
|
if (exists_in_spiffs(T55XX_KEYS_FILE)) {
|
||||||
if (isok != sizeof(counter))
|
size = size_in_spiffs(T55XX_KEYS_FILE);
|
||||||
|
}
|
||||||
|
if (size == 0) {
|
||||||
|
Dbprintf("Spiffs file: %s does not exists or empty.", T55XX_KEYS_FILE);
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
}
|
||||||
|
|
||||||
pwd_count = (uint16_t)(counter[1] << 8 | counter[0]);
|
pwd_count = size / T55XX_KEY_LENGTH;
|
||||||
if (pwd_count == 0)
|
if (pwd_count == 0)
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
|
||||||
// since flash can report way too many pwds, we need to limit it.
|
// since flash can report way too many pwds, we need to limit it.
|
||||||
// bigbuff EM size is determined by CARD_MEMORY_SIZE
|
// bigbuff EM size is determined by CARD_MEMORY_SIZE
|
||||||
// a password is 4bytes.
|
// a password is 4bytes.
|
||||||
uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * 4);
|
uint16_t pwd_size_available = MIN(CARD_MEMORY_SIZE, pwd_count * T55XX_KEY_LENGTH);
|
||||||
|
|
||||||
// adjust available pwd_count
|
// adjust available pwd_count
|
||||||
pwd_count = pwd_size_available / 4;
|
pwd_count = pwd_size_available / T55XX_KEY_LENGTH;
|
||||||
|
|
||||||
isok = Flash_ReadData(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k) + 2, pwds, pwd_size_available);
|
if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(T55XX_KEYS_FILE, pwds, pwd_size_available, RDV40_SPIFFS_SAFETY_SAFE)) {
|
||||||
if (isok != pwd_size_available)
|
if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u passwords from spiffs file: %s", pwd_count, T55XX_KEYS_FILE);
|
||||||
|
} else {
|
||||||
|
Dbprintf("Spiffs file: %s cannot be read.", T55XX_KEYS_FILE);
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
}
|
||||||
Dbprintf("Password dictionary count " _YELLOW_("%d"), pwd_count);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1900,31 +1900,36 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da
|
||||||
#ifdef WITH_FLASH
|
#ifdef WITH_FLASH
|
||||||
if (use_flashmem) {
|
if (use_flashmem) {
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
uint16_t isok = 0;
|
uint32_t size = 0;
|
||||||
uint8_t size[2] = {0x00, 0x00};
|
if (exists_in_spiffs(MF_KEYS_FILE)) {
|
||||||
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), size, 2);
|
size = size_in_spiffs(MF_KEYS_FILE);
|
||||||
if (isok != 2)
|
}
|
||||||
|
if (size == 0) {
|
||||||
|
Dbprintf("Spiffs file: %s does not exists or empty.", MF_KEYS_FILE);
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
}
|
||||||
|
|
||||||
keyCount = size[1] << 8 | size[0];
|
keyCount = size / MF_KEY_LENGTH;
|
||||||
|
|
||||||
if (keyCount == 0)
|
if (keyCount == 0)
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
|
||||||
// limit size of available for keys in bigbuff
|
// limit size of available for keys in bigbuff
|
||||||
// a key is 6bytes
|
// a key is 6bytes
|
||||||
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * 6);
|
uint16_t key_mem_available = MIN(BigBuf_get_size(), keyCount * MF_KEY_LENGTH);
|
||||||
|
|
||||||
keyCount = key_mem_available / 6;
|
keyCount = key_mem_available / MF_KEY_LENGTH;
|
||||||
|
|
||||||
datain = BigBuf_malloc(key_mem_available);
|
datain = BigBuf_malloc(key_mem_available);
|
||||||
if (datain == NULL)
|
if (datain == NULL)
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
|
||||||
isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k) + 2, datain, key_mem_available);
|
if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, datain, keyCount * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) {
|
||||||
if (isok != key_mem_available)
|
if (g_dbglevel >= DBG_ERROR) Dbprintf("Loaded %u keys from spiffs file: %s", keyCount, MF_KEYS_FILE);
|
||||||
|
} else {
|
||||||
|
Dbprintf("Spiffs file: %s cannot be read.", MF_KEYS_FILE);
|
||||||
goto OUT;
|
goto OUT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include "cmdflashmem.h"
|
#include "cmdflashmem.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
#include "cmdparser.h" // command_t
|
#include "cmdparser.h" // command_t
|
||||||
#include "cliparser.h"
|
#include "cliparser.h"
|
||||||
#include "pmflash.h" // rdv40validation_t
|
#include "pmflash.h" // rdv40validation_t
|
||||||
|
@ -192,7 +193,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
|
||||||
CLIParserInit(&ctx, "mem load",
|
CLIParserInit(&ctx, "mem load",
|
||||||
"Loads binary file into flash memory on device\n"
|
"Loads binary file into flash memory on device\n"
|
||||||
"Warning: mem area to be written must have been wiped first\n"
|
"Warning: mem area to be written must have been wiped first\n"
|
||||||
"( this is already taken care when loading dictionaries )",
|
"( dictionaries are serviced as files in spiffs so no wipe is needed )",
|
||||||
"mem load -f myfile -> upload file myfile values at default offset 0\n"
|
"mem load -f myfile -> upload file myfile values at default offset 0\n"
|
||||||
"mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n"
|
"mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n"
|
||||||
"mem load -f mfc_default_keys -m -> upload MFC keys\n"
|
"mem load -f mfc_default_keys -m -> upload MFC keys\n"
|
||||||
|
@ -217,6 +218,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
|
||||||
bool is_t55xx = arg_get_lit(ctx, 4);
|
bool is_t55xx = arg_get_lit(ctx, 4);
|
||||||
int fnlen = 0;
|
int fnlen = 0;
|
||||||
char filename[FILE_PATH_SIZE] = {0};
|
char filename[FILE_PATH_SIZE] = {0};
|
||||||
|
char spiffsDest[32] = {0};
|
||||||
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
@ -246,57 +248,46 @@ static int CmdFlashMemLoad(const char *Cmd) {
|
||||||
|
|
||||||
switch (d) {
|
switch (d) {
|
||||||
case DICTIONARY_MIFARE:
|
case DICTIONARY_MIFARE:
|
||||||
offset = DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages);
|
keylen = MF_KEY_LENGTH;
|
||||||
keylen = 6;
|
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
|
||||||
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
|
|
||||||
if (res || !keycount) {
|
if (res || !keycount) {
|
||||||
free(data);
|
free(data);
|
||||||
return PM3_EFILE;
|
return PM3_EFILE;
|
||||||
}
|
}
|
||||||
// limited space on flash mem
|
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
|
||||||
if (keycount > DEFAULT_MF_KEYS_MAX) {
|
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
|
||||||
keycount = DEFAULT_MF_KEYS_MAX;
|
free(data);
|
||||||
datalen = keycount * keylen;
|
return PM3_EOVFLOW;
|
||||||
}
|
}
|
||||||
|
strcpy(spiffsDest, MF_KEYS_FILE);
|
||||||
data[0] = (keycount >> 0) & 0xFF;
|
|
||||||
data[1] = (keycount >> 8) & 0xFF;
|
|
||||||
datalen += 2;
|
|
||||||
break;
|
break;
|
||||||
case DICTIONARY_T55XX:
|
case DICTIONARY_T55XX:
|
||||||
offset = DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages);
|
keylen = T55XX_KEY_LENGTH;
|
||||||
keylen = 4;
|
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
|
||||||
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
|
|
||||||
if (res || !keycount) {
|
if (res || !keycount) {
|
||||||
free(data);
|
free(data);
|
||||||
return PM3_EFILE;
|
return PM3_EFILE;
|
||||||
}
|
}
|
||||||
// limited space on flash mem
|
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
|
||||||
if (keycount > DEFAULT_T55XX_KEYS_MAX) {
|
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
|
||||||
keycount = DEFAULT_T55XX_KEYS_MAX;
|
free(data);
|
||||||
datalen = keycount * keylen;
|
return PM3_EOVFLOW;
|
||||||
}
|
}
|
||||||
|
strcpy(spiffsDest, T55XX_KEYS_FILE);
|
||||||
data[0] = (keycount >> 0) & 0xFF;
|
|
||||||
data[1] = (keycount >> 8) & 0xFF;
|
|
||||||
datalen += 2;
|
|
||||||
break;
|
break;
|
||||||
case DICTIONARY_ICLASS:
|
case DICTIONARY_ICLASS:
|
||||||
offset = DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages);
|
keylen = ICLASS_KEY_LENGTH;
|
||||||
res = loadFileDICTIONARY(filename, data + 2, &datalen, keylen, &keycount);
|
res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount);
|
||||||
if (res || !keycount) {
|
if (res || !keycount) {
|
||||||
free(data);
|
free(data);
|
||||||
return PM3_EFILE;
|
return PM3_EFILE;
|
||||||
}
|
}
|
||||||
// limited space on flash mem
|
if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) {
|
||||||
if (keycount > DEFAULT_ICLASS_KEYS_MAX) {
|
PrintAndLogEx(ERR, "error, filesize is larger than available memory");
|
||||||
keycount = DEFAULT_ICLASS_KEYS_MAX;
|
free(data);
|
||||||
datalen = keycount * keylen;
|
return PM3_EOVFLOW;
|
||||||
}
|
}
|
||||||
|
strcpy(spiffsDest, ICLASS_KEYS_FILE);
|
||||||
data[0] = (keycount >> 0) & 0xFF;
|
|
||||||
data[1] = (keycount >> 8) & 0xFF;
|
|
||||||
datalen += 2;
|
|
||||||
break;
|
break;
|
||||||
case DICTIONARY_NONE:
|
case DICTIONARY_NONE:
|
||||||
res = loadFile_safe(filename, ".bin", (void **)&data, &datalen);
|
res = loadFile_safe(filename, ".bin", (void **)&data, &datalen);
|
||||||
|
@ -326,44 +317,57 @@ static int CmdFlashMemLoad(const char *Cmd) {
|
||||||
uint32_t bytes_sent = 0;
|
uint32_t bytes_sent = 0;
|
||||||
uint32_t bytes_remaining = datalen;
|
uint32_t bytes_remaining = datalen;
|
||||||
|
|
||||||
|
// we will treat dictionary files as spiffs files, so we need to handle this here
|
||||||
// fast push mode
|
if (d != DICTIONARY_NONE) {
|
||||||
g_conn.block_after_ACK = true;
|
res = flashmem_spiffs_load(spiffsDest, data, datalen);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
while (bytes_remaining > 0) {
|
PrintAndLogEx(FAILED, "Failed writing passwrods to file %s", spiffsDest);
|
||||||
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
|
||||||
|
|
||||||
clearCommandBuffer();
|
|
||||||
|
|
||||||
flashmem_old_write_t payload = {
|
|
||||||
.startidx = offset + bytes_sent,
|
|
||||||
.len = bytes_in_packet,
|
|
||||||
};
|
|
||||||
memcpy(payload.data, data + bytes_sent, bytes_in_packet);
|
|
||||||
SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload));
|
|
||||||
|
|
||||||
bytes_remaining -= bytes_in_packet;
|
|
||||||
bytes_sent += bytes_in_packet;
|
|
||||||
|
|
||||||
PacketResponseNG resp;
|
|
||||||
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
|
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
|
||||||
g_conn.block_after_ACK = false;
|
|
||||||
free(data);
|
free(data);
|
||||||
return PM3_ETIMEOUT;
|
return res;
|
||||||
|
}
|
||||||
|
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest);
|
||||||
|
SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0);
|
||||||
|
SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0);
|
||||||
|
} else {
|
||||||
|
// fast push mode
|
||||||
|
g_conn.block_after_ACK = true;
|
||||||
|
|
||||||
|
while (bytes_remaining > 0) {
|
||||||
|
uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining);
|
||||||
|
|
||||||
|
clearCommandBuffer();
|
||||||
|
|
||||||
|
flashmem_old_write_t payload = {
|
||||||
|
.startidx = offset + bytes_sent,
|
||||||
|
.len = bytes_in_packet,
|
||||||
|
};
|
||||||
|
memcpy(payload.data, data + bytes_sent, bytes_in_packet);
|
||||||
|
SendCommandNG(CMD_FLASHMEM_WRITE, (uint8_t *)&payload, sizeof(payload));
|
||||||
|
|
||||||
|
bytes_remaining -= bytes_in_packet;
|
||||||
|
bytes_sent += bytes_in_packet;
|
||||||
|
|
||||||
|
PacketResponseNG resp;
|
||||||
|
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
|
||||||
|
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
||||||
|
g_conn.block_after_ACK = false;
|
||||||
|
free(data);
|
||||||
|
return PM3_ETIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
g_conn.block_after_ACK = false;
|
||||||
|
PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent);
|
||||||
|
free(data);
|
||||||
|
return PM3_EFLASH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.status != PM3_SUCCESS) {
|
g_conn.block_after_ACK = false;
|
||||||
g_conn.block_after_ACK = false;
|
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
|
||||||
PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent);
|
|
||||||
free(data);
|
|
||||||
return PM3_EFLASH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_conn.block_after_ACK = false;
|
|
||||||
free(data);
|
free(data);
|
||||||
PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset);
|
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1884,6 +1884,7 @@ static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_sta
|
||||||
printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K");
|
printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K");
|
||||||
printTag("MIFARE DESFire EV3 2K/4K/8K");
|
printTag("MIFARE DESFire EV3 2K/4K/8K");
|
||||||
printTag("MIFARE DESFire Light 640B");
|
printTag("MIFARE DESFire Light 640B");
|
||||||
|
printTag("MIFARE Duox");
|
||||||
type |= MTDESFIRE;
|
type |= MTDESFIRE;
|
||||||
} else {
|
} else {
|
||||||
printTag("MIFARE Plus EV1 2K/4K CL2 in SL3");
|
printTag("MIFARE Plus EV1 2K/4K CL2 in SL3");
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "preferences.h"
|
#include "preferences.h"
|
||||||
#include "generator.h"
|
#include "generator.h"
|
||||||
#include "cmdhf14b.h"
|
#include "cmdhf14b.h"
|
||||||
|
#include "cmdhw.h"
|
||||||
|
|
||||||
|
|
||||||
#define NUM_CSNS 9
|
#define NUM_CSNS 9
|
||||||
|
@ -2927,6 +2928,199 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int CmdHFiClass_TearBlock(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "hf iclass trbl",
|
||||||
|
"Tear off an iCLASS tag block",
|
||||||
|
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150\n"
|
||||||
|
"hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150");
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_str0("k", "key", "<hex>", "Access key as 8 hex bytes"),
|
||||||
|
arg_int0(NULL, "ki", "<dec>", "Key index to select key from memory 'hf iclass managekeys'"),
|
||||||
|
arg_int1(NULL, "blk", "<dec>", "block number"),
|
||||||
|
arg_str1("d", "data", "<hex>", "data to write as 8 hex bytes"),
|
||||||
|
arg_str0("m", "mac", "<hex>", "replay mac data (4 hex bytes)"),
|
||||||
|
arg_lit0(NULL, "credit", "key is assumed to be the credit key"),
|
||||||
|
arg_lit0(NULL, "elite", "elite computations applied to key"),
|
||||||
|
arg_lit0(NULL, "raw", "no computations applied to key"),
|
||||||
|
arg_lit0(NULL, "nr", "replay of NR/MAC"),
|
||||||
|
arg_lit0("v", "verbose", "verbose output"),
|
||||||
|
arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"),
|
||||||
|
arg_int1(NULL, "tdb", "<dec>", "tearoff delay start in ms"),
|
||||||
|
arg_int1(NULL, "tde", "<dec>", "tearoff delay end in ms"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
|
||||||
|
int key_len = 0;
|
||||||
|
uint8_t key[8] = {0};
|
||||||
|
|
||||||
|
CLIGetHexWithReturn(ctx, 1, key, &key_len);
|
||||||
|
|
||||||
|
int key_nr = arg_get_int_def(ctx, 2, -1);
|
||||||
|
|
||||||
|
if (key_len > 0 && key_nr >= 0) {
|
||||||
|
PrintAndLogEx(ERR, "Please specify key or index, not both");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool auth = false;
|
||||||
|
|
||||||
|
if (key_len > 0) {
|
||||||
|
auth = true;
|
||||||
|
if (key_len != 8) {
|
||||||
|
PrintAndLogEx(ERR, "Key is incorrect length");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
} else if (key_nr >= 0) {
|
||||||
|
if (key_nr < ICLASS_KEYS_MAX) {
|
||||||
|
auth = true;
|
||||||
|
memcpy(key, iClass_Key_Table[key_nr], 8);
|
||||||
|
PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8));
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(ERR, "Key number is invalid");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockno = arg_get_int_def(ctx, 3, 0);
|
||||||
|
|
||||||
|
int data_len = 0;
|
||||||
|
uint8_t data[8] = {0};
|
||||||
|
CLIGetHexWithReturn(ctx, 4, data, &data_len);
|
||||||
|
|
||||||
|
if (data_len != 8) {
|
||||||
|
PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols)");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mac_len = 0;
|
||||||
|
uint8_t mac[4] = {0};
|
||||||
|
CLIGetHexWithReturn(ctx, 5, mac, &mac_len);
|
||||||
|
|
||||||
|
if (mac_len) {
|
||||||
|
if (mac_len != 4) {
|
||||||
|
PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)");
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int tearoff_start = arg_get_int_def(ctx, 12, 100);
|
||||||
|
int tearoff_end = arg_get_int_def(ctx, 13, 200);
|
||||||
|
|
||||||
|
if(tearoff_end <= tearoff_start){
|
||||||
|
PrintAndLogEx(ERR, "Tearoff end delay must be bigger than the start delay.");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tearoff_start < 0 || tearoff_end <= 0){
|
||||||
|
PrintAndLogEx(ERR, "Tearoff start/end delays should be bigger than 0.");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool use_credit_key = arg_get_lit(ctx, 6);
|
||||||
|
bool elite = arg_get_lit(ctx, 7);
|
||||||
|
bool rawkey = arg_get_lit(ctx, 8);
|
||||||
|
bool use_replay = arg_get_lit(ctx, 9);
|
||||||
|
bool verbose = arg_get_lit(ctx, 10);
|
||||||
|
bool shallow_mod = arg_get_lit(ctx, 11);
|
||||||
|
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
if ((use_replay + rawkey + elite) > 1) {
|
||||||
|
PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
int isok = 0;
|
||||||
|
tearoff_params_t params;
|
||||||
|
bool read_ok = false;
|
||||||
|
while(tearoff_start < tearoff_end && !read_ok){
|
||||||
|
//perform read here, repeat if failed or 00s
|
||||||
|
|
||||||
|
uint8_t data_read_orig[8] = {0};
|
||||||
|
bool first_read = false;
|
||||||
|
bool reread = false;
|
||||||
|
while(!first_read){
|
||||||
|
int res_orig = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read_orig, false);
|
||||||
|
if (res_orig == PM3_SUCCESS && !reread){
|
||||||
|
if (memcmp(data_read_orig, zeros, 8) == 0){
|
||||||
|
reread = true;
|
||||||
|
}else{
|
||||||
|
first_read = true;
|
||||||
|
reread = false;
|
||||||
|
}
|
||||||
|
} else if (res_orig == PM3_SUCCESS && reread){
|
||||||
|
first_read = true;
|
||||||
|
reread = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params.on = true;
|
||||||
|
params.delay_us = tearoff_start;
|
||||||
|
handle_tearoff(¶ms, false);
|
||||||
|
PrintAndLogEx(INFO, "Tear off delay: "_YELLOW_("%d")" ms", tearoff_start);
|
||||||
|
isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod);
|
||||||
|
switch (isok) {
|
||||||
|
case PM3_SUCCESS:
|
||||||
|
PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno);
|
||||||
|
break;
|
||||||
|
case PM3_ETEAROFF:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PrintAndLogEx(FAILED, "Writing failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//read the data back
|
||||||
|
uint8_t data_read[8] = {0};
|
||||||
|
first_read = false;
|
||||||
|
reread = false;
|
||||||
|
bool decrease = false;
|
||||||
|
while(!first_read){
|
||||||
|
int res = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read, false);
|
||||||
|
if (res == PM3_SUCCESS && !reread){
|
||||||
|
if (memcmp(data_read, zeros, 8) == 0){
|
||||||
|
reread = true;
|
||||||
|
}else{
|
||||||
|
first_read = true;
|
||||||
|
reread = false;
|
||||||
|
}
|
||||||
|
} else if (res == PM3_SUCCESS && reread){
|
||||||
|
first_read = true;
|
||||||
|
reread = false;
|
||||||
|
} else if (res != PM3_SUCCESS){
|
||||||
|
decrease = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decrease && tearoff_start > 0){ //if there was an error reading repeat the tearoff with the same delay
|
||||||
|
tearoff_start--;
|
||||||
|
}
|
||||||
|
bool tear_success = true;
|
||||||
|
for (int i=0; i<PICOPASS_BLOCK_SIZE; i++){
|
||||||
|
if(data[i] != data_read[i]){
|
||||||
|
tear_success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tear_success){ //tearoff succeeded
|
||||||
|
read_ok = true;
|
||||||
|
PrintAndLogEx(SUCCESS, _GREEN_("Tear-off Success!"));
|
||||||
|
PrintAndLogEx(INFO, "Read: %s", sprint_hex(data_read, sizeof(data_read)));
|
||||||
|
}else{ //tearoff did not succeed
|
||||||
|
PrintAndLogEx(FAILED, _RED_("Tear-off Failed!"));
|
||||||
|
tearoff_start++;
|
||||||
|
}
|
||||||
|
PrintAndLogEx(INFO, "---------------");
|
||||||
|
}
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
return isok;
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdHFiClass_loclass(const char *Cmd) {
|
static int CmdHFiClass_loclass(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "hf iclass loclass",
|
CLIParserInit(&ctx, "hf iclass loclass",
|
||||||
|
@ -5332,6 +5526,7 @@ static command_t CommandTable[] = {
|
||||||
{"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"},
|
{"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"},
|
||||||
{"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"},
|
{"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"},
|
||||||
{"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"},
|
{"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"},
|
||||||
|
{"trbl", CmdHFiClass_TearBlock, IfPm3Iclass, "Performs tearoff attack on iClass block"},
|
||||||
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("Recovery") " --------------------"},
|
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("Recovery") " --------------------"},
|
||||||
// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"},
|
// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"},
|
||||||
{"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"},
|
{"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"},
|
||||||
|
|
|
@ -148,6 +148,7 @@ typedef enum {
|
||||||
PLUS_EV2,
|
PLUS_EV2,
|
||||||
NTAG413DNA,
|
NTAG413DNA,
|
||||||
NTAG424,
|
NTAG424,
|
||||||
|
DUOX,
|
||||||
} nxp_cardtype_t;
|
} nxp_cardtype_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -272,6 +273,10 @@ static char *getVersionStr(uint8_t type, uint8_t major, uint8_t minor) {
|
||||||
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV1") " )", major, minor);
|
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV1") " )", major, minor);
|
||||||
else if (type == 0x02 && major == 0x22 && minor == 0x00)
|
else if (type == 0x02 && major == 0x22 && minor == 0x00)
|
||||||
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV2") " )", major, minor);
|
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("Plus EV2") " )", major, minor);
|
||||||
|
else if (type == 0x01 && major == 0xA0 && minor == 0x00)
|
||||||
|
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DUOX") " )", major, minor);
|
||||||
|
else if ((type & 0x08) == 0x08)
|
||||||
|
snprintf(retStr, sizeof(buf), "%x.%x ( " _GREEN_("DESFire Light") " )", major, minor);
|
||||||
else
|
else
|
||||||
snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor);
|
snprintf(retStr, sizeof(buf), "%x.%x ( " _YELLOW_("Unknown") " )", major, minor);
|
||||||
return buf;
|
return buf;
|
||||||
|
@ -338,6 +343,10 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
|
||||||
if (type == 0x01 && major == 0x33 && minor == 0x00)
|
if (type == 0x01 && major == 0x33 && minor == 0x00)
|
||||||
return DESFIRE_EV3;
|
return DESFIRE_EV3;
|
||||||
|
|
||||||
|
// Duox
|
||||||
|
if (type == 0x01 && major == 0xA0 && minor == 0x00)
|
||||||
|
return DUOX;
|
||||||
|
|
||||||
// DESFire Light
|
// DESFire Light
|
||||||
if (type == 0x08 && major == 0x30 && minor == 0x00)
|
if (type == 0x08 && major == 0x30 && minor == 0x00)
|
||||||
return DESFIRE_LIGHT;
|
return DESFIRE_LIGHT;
|
||||||
|
@ -744,6 +753,8 @@ static int CmdHF14ADesInfo(const char *Cmd) {
|
||||||
PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5");
|
PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5");
|
||||||
if (major == 3 && minor == 0)
|
if (major == 3 && minor == 0)
|
||||||
PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?");
|
PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?");
|
||||||
|
if (major == 0xA0 && minor == 0)
|
||||||
|
PrintAndLogEx(INFO, "\tx.x - DUOX, Originality check, proximity check, EAL6++");
|
||||||
|
|
||||||
if (major == 0 && minor == 2)
|
if (major == 0 && minor == 2)
|
||||||
PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, ");
|
PrintAndLogEx(INFO, "\t0.2 - DESFire Light, Originality check, ");
|
||||||
|
@ -761,7 +772,8 @@ static int CmdHF14ADesInfo(const char *Cmd) {
|
||||||
if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_EV2_XL ||
|
if (cardtype == DESFIRE_EV2 || cardtype == DESFIRE_EV2_XL ||
|
||||||
cardtype == DESFIRE_LIGHT ||
|
cardtype == DESFIRE_LIGHT ||
|
||||||
cardtype == DESFIRE_EV3 ||
|
cardtype == DESFIRE_EV3 ||
|
||||||
cardtype == NTAG413DNA) {
|
cardtype == NTAG413DNA ||
|
||||||
|
cardtype == DUOX) {
|
||||||
// Signature originality check
|
// Signature originality check
|
||||||
uint8_t signature[250] = {0}; // must be 56
|
uint8_t signature[250] = {0}; // must be 56
|
||||||
size_t signature_len = 0;
|
size_t signature_len = 0;
|
||||||
|
|
|
@ -449,17 +449,19 @@ static int CmdEM410xSim(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "lf em 410x sim",
|
CLIParserInit(&ctx, "lf em 410x sim",
|
||||||
"Enables simulation of EM 410x card.\n"
|
"Enables simulation of EM 410x card.\n"
|
||||||
"Simulation runs until the button is pressed or another USB command is issued.",
|
"Simulation runs until the button is pressed or another USB command is issued.\n"
|
||||||
|
"Most common readers expects the code to be sent in loop without a break (i.e. --gap 0).\n"
|
||||||
|
"For other, more advanced readers there might be a need to set a non-zero gap value.",
|
||||||
"lf em 410x sim --id 0F0368568B\n"
|
"lf em 410x sim --id 0F0368568B\n"
|
||||||
"lf em 410x sim --id 0F0368568B --clk 32\n"
|
"lf em 410x sim --id 0F0368568B --clk 32\n"
|
||||||
"lf em 410x sim --id 0F0368568B --gap 0"
|
"lf em 410x sim --id 0F0368568B --gap 20"
|
||||||
);
|
);
|
||||||
|
|
||||||
void *argtable[] = {
|
void *argtable[] = {
|
||||||
arg_param_begin,
|
arg_param_begin,
|
||||||
arg_u64_0(NULL, "clk", "<dec>", "<32|64> clock (default 64)"),
|
arg_u64_0(NULL, "clk", "<dec>", "<32|64> clock (default 64)"),
|
||||||
arg_str1(NULL, "id", "<hex>", "EM Tag ID number (5 hex bytes)"),
|
arg_str1(NULL, "id", "<hex>", "EM Tag ID number (5 hex bytes)"),
|
||||||
arg_u64_0(NULL, "gap", "<dec>", "gap (0's) between ID repeats (default 20)"),
|
arg_u64_0(NULL, "gap", "<dec>", "gap (0's) between ID repeats (default 0)"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
};
|
};
|
||||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
@ -467,7 +469,7 @@ static int CmdEM410xSim(const char *Cmd) {
|
||||||
// clock is 64 in EM410x tags
|
// clock is 64 in EM410x tags
|
||||||
int clk = arg_get_u32_def(ctx, 1, 64);
|
int clk = arg_get_u32_def(ctx, 1, 64);
|
||||||
int uid_len = 0;
|
int uid_len = 0;
|
||||||
int gap = arg_get_u32_def(ctx, 3, 20);
|
int gap = arg_get_u32_def(ctx, 3, 0);
|
||||||
uint8_t uid[5] = {0};
|
uint8_t uid[5] = {0};
|
||||||
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
|
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
|
@ -273,7 +273,9 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh
|
||||||
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
|
// max timeout for one chunk of 85keys, 60*3sec = 180seconds
|
||||||
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
|
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
|
||||||
// takes about 97s, still some margin before abort
|
// takes about 97s, still some margin before abort
|
||||||
if (timeout > 180) {
|
// timeout = 180 => ~360s @ Mifare Classic 1k @ ~2300 keys in dict
|
||||||
|
// ~2300 keys @ Mifare Classic 1k => ~620s
|
||||||
|
if (timeout > 60*12) {
|
||||||
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
|
PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting...");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -383,43 +383,6 @@ void Flashmem_print_status(void) {
|
||||||
FlashStop();
|
FlashStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Flashmem_print_info(void) {
|
|
||||||
|
|
||||||
if (!FlashInit()) return;
|
|
||||||
|
|
||||||
DbpString(_CYAN_("Flash memory dictionary loaded"));
|
|
||||||
|
|
||||||
// load dictionary offsets.
|
|
||||||
uint8_t keysum[2];
|
|
||||||
uint16_t num;
|
|
||||||
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
|
|
||||||
if (isok == 2) {
|
|
||||||
num = ((keysum[1] << 8) | keysum[0]);
|
|
||||||
if (num != 0xFFFF && num != 0x0)
|
|
||||||
Dbprintf(" Mifare.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_MF_KEYS_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
|
|
||||||
if (isok == 2) {
|
|
||||||
num = ((keysum[1] << 8) | keysum[0]);
|
|
||||||
if (num != 0xFFFF && num != 0x0)
|
|
||||||
Dbprintf(" T55x7................... "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_T55XX_KEYS_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
Flash_CheckBusy(BUSY_TIMEOUT);
|
|
||||||
isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET_P(spi_flash_pages64k), keysum, 2);
|
|
||||||
if (isok == 2) {
|
|
||||||
num = ((keysum[1] << 8) | keysum[0]);
|
|
||||||
if (num != 0xFFFF && num != 0x0)
|
|
||||||
Dbprintf(" iClass.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_ICLASS_KEYS_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
FlashStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FlashDetect(void) {
|
bool FlashDetect(void) {
|
||||||
flash_device_type_t flash_data = {0};
|
flash_device_type_t flash_data = {0};
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
|
@ -134,7 +134,6 @@ uint16_t Flash_Write(uint32_t address, uint8_t *in, uint16_t len);
|
||||||
uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len);
|
uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len);
|
||||||
uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len);
|
uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len);
|
||||||
void Flashmem_print_status(void);
|
void Flashmem_print_status(void);
|
||||||
void Flashmem_print_info(void);
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t manufacturer_id;
|
uint8_t manufacturer_id;
|
||||||
|
|
|
@ -37,20 +37,20 @@ Therefore a flash address can be interpreted as such:
|
||||||
Page 0:
|
Page 0:
|
||||||
* available for user data
|
* available for user data
|
||||||
* to dump it: `mem dump -f page0_dump -o 0 -l 65536`
|
* to dump it: `mem dump -f page0_dump -o 0 -l 65536`
|
||||||
* to erase it: `mem wipe p 0`
|
* to erase it: `mem wipe -p 0`
|
||||||
|
|
||||||
Page 1:
|
Page 1:
|
||||||
* available for user data
|
* available for user data
|
||||||
* to dump it: `mem dump -f page1_dump -o 65536 -l 65536`
|
* to dump it: `mem dump -f page1_dump -o 65536 -l 65536`
|
||||||
* to erase it: `mem wipe p 1`
|
* to erase it: `mem wipe -p 1`
|
||||||
|
|
||||||
Page 2:
|
Page 2:
|
||||||
* available for user data
|
* available for user data
|
||||||
* to dump it: `mem dump -f page2_dump -o 131072 -l 65536`
|
* to dump it: `mem dump -f page2_dump -o 131072 -l 65536`
|
||||||
* to erase it: `mem wipe p 2`
|
* to erase it: `mem wipe -p 2`
|
||||||
|
|
||||||
Page 3:
|
Page 3:
|
||||||
* used by Proxmark3 RDV4 specific functions: flash signature and keys dictionaries, see below for details
|
* used by Proxmark3 RDV4 specific functions: flash signature, see below for details
|
||||||
* to dump it: `mem dump -f page3_dump -o 196608 -l 65536`
|
* to dump it: `mem dump -f page3_dump -o 196608 -l 65536`
|
||||||
* to erase it:
|
* to erase it:
|
||||||
* **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself!
|
* **Beware** it will erase your flash signature so better to back it up first as you won't be able to regenerate it by yourself!
|
||||||
|
@ -62,22 +62,6 @@ Page 3:
|
||||||
|
|
||||||
Page3 is used as follows by the Proxmark3 RDV4 firmware:
|
Page3 is used as follows by the Proxmark3 RDV4 firmware:
|
||||||
|
|
||||||
* **MF_KEYS**
|
|
||||||
* offset: page 3 sector 5 (0x5) @ 3*0x10000+5*0x1000=0x35000
|
|
||||||
* length: 6 sectors
|
|
||||||
|
|
||||||
* **ICLASS_KEYS**
|
|
||||||
* offset: page 3 sector 11 (0xB) @ 3*0x10000+11*0x1000=0x3B000
|
|
||||||
* length: 1 sector
|
|
||||||
|
|
||||||
* **T55XX_KEYS**
|
|
||||||
* offset: page 3 sector 12 (0xC) @ 3*0x10000+12*0x1000=0x3C000
|
|
||||||
* length: 1 sector
|
|
||||||
|
|
||||||
* **T55XX_CONFIG**
|
|
||||||
* offset: page 3 sector 13 (0xD) @ 3*0x10000+13*0x1000=0x3D000
|
|
||||||
* length: 1 sector (actually only a few bytes are used to store `t55xx_config` structure)
|
|
||||||
|
|
||||||
* **RSA SIGNATURE**, see below for details
|
* **RSA SIGNATURE**, see below for details
|
||||||
* offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015)
|
* offset: page 3 sector 15 (0xF) offset 0xF7F @ 3*0x10000+15*0x1000+0xF7F=0x3FF7F (decimal 262015)
|
||||||
* length: 128 bytes
|
* length: 128 bytes
|
||||||
|
|
|
@ -26,9 +26,6 @@
|
||||||
//
|
//
|
||||||
// 0x3F000 - 1 4kb sector = signature
|
// 0x3F000 - 1 4kb sector = signature
|
||||||
// 0x3E000 - 1 4kb sector = settings
|
// 0x3E000 - 1 4kb sector = settings
|
||||||
// 0x3D000 - 1 4kb sector = default T55XX keys dictionary
|
|
||||||
// 0x3B000 - 1 4kb sector = default ICLASS keys dictionary
|
|
||||||
// 0x35000 - 6 4kb sectors = default MFC keys dictionary
|
|
||||||
//
|
//
|
||||||
#ifndef FLASH_MEM_BLOCK_SIZE
|
#ifndef FLASH_MEM_BLOCK_SIZE
|
||||||
# define FLASH_MEM_BLOCK_SIZE 256
|
# define FLASH_MEM_BLOCK_SIZE 256
|
||||||
|
@ -70,42 +67,19 @@
|
||||||
# define T55XX_CONFIG_LEN sizeof( t55xx_configurations_t )
|
# define T55XX_CONFIG_LEN sizeof( t55xx_configurations_t )
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef T55XX_CONFIG_OFFSET
|
#define T55XX_CONFIG_FILE "cfg_t55xx.bin"
|
||||||
# define T55XX_CONFIG_OFFSET (FLASH_MEM_MAX_4K_SECTOR - 0x2000)
|
|
||||||
#endif
|
|
||||||
#ifndef T55XX_CONFIG_OFFSET_P
|
|
||||||
# define T55XX_CONFIG_OFFSET_P(p64k) (FLASH_MEM_MAX_4K_SECTOR_P(p64k) - 0x2000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Reserved space for T55XX PWD = 4 kb
|
// T55XX PWD stored in spiffs
|
||||||
#ifndef DEFAULT_T55XX_KEYS_OFFSET
|
#define T55XX_KEYS_FILE "dict_t55xx.bin"
|
||||||
# define DEFAULT_T55XX_KEYS_LEN (0x1000)
|
#define T55XX_KEY_LENGTH 4
|
||||||
# define DEFAULT_T55XX_KEYS_OFFSET (T55XX_CONFIG_OFFSET - DEFAULT_T55XX_KEYS_LEN)
|
|
||||||
# define DEFAULT_T55XX_KEYS_MAX ((DEFAULT_T55XX_KEYS_LEN - 2) / 4)
|
|
||||||
#endif
|
|
||||||
#ifndef DEFAULT_T55XX_KEYS_OFFSET_P
|
|
||||||
# define DEFAULT_T55XX_KEYS_OFFSET_P(p64k) (T55XX_CONFIG_OFFSET_P(p64k) - DEFAULT_T55XX_KEYS_LEN)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Reserved space for iClass keys = 4 kb
|
// iClass keys stored in spiffs
|
||||||
#ifndef DEFAULT_ICLASS_KEYS_OFFSET
|
#define ICLASS_KEYS_FILE "dict_iclass.bin"
|
||||||
# define DEFAULT_ICLASS_KEYS_LEN (0x1000)
|
#define ICLASS_KEY_LENGTH 8
|
||||||
# define DEFAULT_ICLASS_KEYS_OFFSET (DEFAULT_T55XX_KEYS_OFFSET - DEFAULT_ICLASS_KEYS_LEN)
|
|
||||||
# define DEFAULT_ICLASS_KEYS_MAX ((DEFAULT_ICLASS_KEYS_LEN - 2) / 8)
|
|
||||||
#endif
|
|
||||||
#ifndef DEFAULT_ICLASS_KEYS_OFFSET_P
|
|
||||||
# define DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) (DEFAULT_T55XX_KEYS_OFFSET_P(p64k) - DEFAULT_ICLASS_KEYS_LEN)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Reserved space for MIFARE Keys = 24 kb
|
// Mifare keys stored in spiffs
|
||||||
#ifndef DEFAULT_MF_KEYS_OFFSET
|
#define MF_KEYS_FILE "dict_mf.bin"
|
||||||
# define DEFAULT_MF_KEYS_LEN (0x6000)
|
#define MF_KEY_LENGTH 6
|
||||||
# define DEFAULT_MF_KEYS_OFFSET (DEFAULT_ICLASS_KEYS_OFFSET - DEFAULT_MF_KEYS_LEN)
|
|
||||||
# define DEFAULT_MF_KEYS_MAX ((DEFAULT_MF_KEYS_LEN - 2) / 6)
|
|
||||||
#endif
|
|
||||||
#ifndef DEFAULT_MF_KEYS_OFFSET_P
|
|
||||||
# define DEFAULT_MF_KEYS_OFFSET_P(p64k) (DEFAULT_ICLASS_KEYS_OFFSET_P(p64k) - DEFAULT_MF_KEYS_LEN)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// RDV40, validation structure to help identifying that client/firmware is talking with RDV40
|
// RDV40, validation structure to help identifying that client/firmware is talking with RDV40
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue