mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
Merge branch 'master' into mf-supercard
Signed-off-by: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com>
This commit is contained in:
commit
ac5dd0ffa8
8 changed files with 381 additions and 249 deletions
|
@ -4,6 +4,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
|||
|
||||
## [unreleased][unreleased]
|
||||
- Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty)
|
||||
- Change `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001)
|
||||
- Added `hf mf gdmsetcfg` - Supprt Gen4 GDM write configuration block (@iceman1001)
|
||||
- Added `hf mf gdmcfg` - Support Gen4 GDM read configuration block (@iceman1001)
|
||||
- Changed magic note to include a section about GDM tags (@iceman1001)
|
||||
|
@ -40,8 +41,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
|||
- Added Mifare Classic EV1 signature write support to gen4 magic tag lua script (@augustozanellato)
|
||||
- Added XOR key extraction and flag to Guardall G-Prox II (@GuruSteve)
|
||||
- Changed verbiage on `hf iclass info` KeyAccess area to be congruent with AA1 and AA2 areas (@GuruSteve)
|
||||
- Added `hf legic info` command for other sources: `hf legic einfo`, `hf legic view` (@0xdeb)
|
||||
|
||||
- Added `hf legic info` command for other sources (@0xdeb)
|
||||
- Added `hf legic einfo` - views emulator menory (@0xdeb)
|
||||
- Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb)
|
||||
|
||||
## [Nitride.4.16191][2023-01-29]
|
||||
- Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox)
|
||||
|
|
|
@ -1154,7 +1154,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
}
|
||||
memcpy(prev_enc_nt, receivedAnswer, 4);
|
||||
if (prev_counter == 5) {
|
||||
if (g_dbglevel >= DBG_EXTENDED) DbpString("Static encrypted nonce detected, exiting...");
|
||||
if (g_dbglevel >= DBG_EXTENDED) {
|
||||
DbpString("Static encrypted nonce detected, exiting...");
|
||||
uint32_t a = bytes_to_num(prev_enc_nt, 4);
|
||||
uint32_t b = bytes_to_num(receivedAnswer, 4);
|
||||
Dbprintf("( %08x vs %08x )", a, b);
|
||||
}
|
||||
isOK = PM3_ESTATIC_NONCE;
|
||||
break;
|
||||
}
|
||||
|
@ -1224,6 +1229,9 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
|
|||
LED_B_ON();
|
||||
WDT_HIT();
|
||||
|
||||
uint32_t prev_enc_nt = 0;
|
||||
uint8_t prev_counter = 0;
|
||||
|
||||
uint16_t unsuccessful_tries = 0;
|
||||
uint16_t davg = 0;
|
||||
dmax = 0;
|
||||
|
@ -1266,11 +1274,13 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
|
|||
};
|
||||
|
||||
// cards with fixed nonce
|
||||
/*
|
||||
if (nt1 == nt2) {
|
||||
Dbprintf("Nested: %08x vs %08x", nt1, nt2);
|
||||
break;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
uint32_t nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160
|
||||
for (i = 101; i < 1200; i++) {
|
||||
nttmp = prng_successor(nttmp, 1);
|
||||
|
@ -1292,6 +1302,21 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
|
|||
isOK = PM3_EFAILED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (nt1 == nt2) {
|
||||
prev_counter++;
|
||||
}
|
||||
prev_enc_nt = nt2;
|
||||
|
||||
if (prev_counter == 5) {
|
||||
if (g_dbglevel >= DBG_EXTENDED) {
|
||||
DbpString("Static encrypted nonce detected, exiting...");
|
||||
Dbprintf("( %08x vs %08x )", prev_enc_nt, nt2);
|
||||
}
|
||||
isOK = PM3_ESTATIC_NONCE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rtr > 1)
|
||||
|
|
|
@ -139,7 +139,7 @@ int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo,
|
|||
return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL);
|
||||
}
|
||||
int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) {
|
||||
return mifare_classic_authex_2(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL, false);
|
||||
return mifare_classic_authex_2(pcs, uid, blockNo, keyType, ui64Key, isNested, ntptr, timing, false);
|
||||
}
|
||||
int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm) {
|
||||
|
||||
|
|
|
@ -38,15 +38,22 @@
|
|||
#include "wiegand_formats.h"
|
||||
#include "wiegand_formatutils.h"
|
||||
|
||||
#define MIFARE_4K_MAXBLOCK 256
|
||||
#define MIFARE_2K_MAXBLOCK 128
|
||||
#define MIFARE_1K_MAXBLOCK 64
|
||||
#define MIFARE_MINI_MAXBLOCK 20
|
||||
#define MIFARE_4K_MAXBLOCK 256
|
||||
#define MIFARE_2K_MAXBLOCK 128
|
||||
#define MIFARE_1K_MAXBLOCK 64
|
||||
#define MIFARE_MINI_MAXBLOCK 20
|
||||
|
||||
#define MIFARE_MINI_MAXSECTOR 5
|
||||
#define MIFARE_1K_MAXSECTOR 16
|
||||
#define MIFARE_2K_MAXSECTOR 32
|
||||
#define MIFARE_4K_MAXSECTOR 40
|
||||
#define MIFARE_4K_MAXSECTOR 40
|
||||
#define MIFARE_2K_MAXSECTOR 32
|
||||
#define MIFARE_1K_MAXSECTOR 16
|
||||
#define MIFARE_MINI_MAXSECTOR 5
|
||||
|
||||
#define MIFARE_4K_MAX_BYTES 4096
|
||||
#define MIFARE_2K_MAX_BYTES 2048
|
||||
#define MIFARE_1K_MAX_BYTES 1024
|
||||
#define MIFARE_MINI_MAX_BYTES 320
|
||||
|
||||
#define MIFARE_KEY_SIZE 6
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
|
@ -319,9 +326,9 @@ static int mf_print_keys(uint16_t n, uint8_t *d) {
|
|||
for (uint16_t i = 0; i < n; i++) {
|
||||
if (mfIsSectorTrailer(i)) {
|
||||
e_sector[mfSectorNum(i)].foundKey[0] = 1;
|
||||
e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), 6);
|
||||
e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE);
|
||||
e_sector[mfSectorNum(i)].foundKey[1] = 1;
|
||||
e_sector[mfSectorNum(i)].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, 6);
|
||||
e_sector[mfSectorNum(i)].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, MIFARE_KEY_SIZE);
|
||||
}
|
||||
}
|
||||
printKeyTable(sectors, e_sector);
|
||||
|
@ -441,6 +448,191 @@ static int mf_analyse_st_block(uint8_t blockno, uint8_t *block, bool force) {
|
|||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
/* Reads data from tag
|
||||
* @param card: (output) card info
|
||||
* @param carddata: (output) card data
|
||||
* @param numSectors: size of the card
|
||||
* @param keyFileName: filename containing keys or NULL.
|
||||
*/
|
||||
static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t numSectors, char *keyfn){
|
||||
|
||||
// Select card to get UID/UIDLEN/ATQA/SAK information
|
||||
clearCommandBuffer();
|
||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
|
||||
PrintAndLogEx(WARNING, "iso14443a card select timeout");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
uint64_t select_status = resp.oldarg[0];
|
||||
if (select_status == 0) {
|
||||
PrintAndLogEx(WARNING, "iso14443a card select failed");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// store card info
|
||||
memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
|
||||
|
||||
char *fptr = NULL;
|
||||
if (keyfn == NULL || keyfn[0] == '\0') {
|
||||
fptr = GenerateFilename("hf-mf-", "-key.bin");
|
||||
if (fptr == NULL)
|
||||
return PM3_ESOFT;
|
||||
|
||||
keyfn = fptr ;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Using... %s", keyfn);
|
||||
|
||||
size_t alen = 0, blen = 0;
|
||||
uint8_t *keyA, *keyB;
|
||||
if (loadFileBinaryKey(keyfn, "", (void**)&keyA, (void**)&keyB, &alen, &blen) != PM3_SUCCESS) {
|
||||
free(fptr);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Reading sector access bits...");
|
||||
PrintAndLogEx(INFO, "." NOLF);
|
||||
|
||||
uint8_t rights[40][4] = {0};
|
||||
|
||||
mf_readblock_t payload;
|
||||
uint8_t current_key;
|
||||
for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) {
|
||||
current_key = MF_KEY_A;
|
||||
for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) {
|
||||
PrintAndLogEx(NORMAL, "." NOLF);
|
||||
fflush(stdout);
|
||||
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + mfNumBlocksPerSector(sectorNo) - 1;
|
||||
payload.keytype = current_key;
|
||||
|
||||
memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) {
|
||||
|
||||
uint8_t *data = resp.data.asBytes;
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
rights[sectorNo][0] = ((data[7] & 0x10) >> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0
|
||||
rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1
|
||||
rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2
|
||||
rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer
|
||||
break;
|
||||
} else if (tries == (MIFARE_SECTOR_RETRY / 2)) { // after half unsuccessful tries, give key B a go
|
||||
PrintAndLogEx(WARNING, "\ntrying with key B instead...");
|
||||
current_key = MF_KEY_B;
|
||||
PrintAndLogEx(INFO, "." NOLF);
|
||||
} else if (tries == (MIFARE_SECTOR_RETRY - 1)) { // on last try set defaults
|
||||
PrintAndLogEx(FAILED, "\ncould not get access rights for sector %2d. Trying with defaults...", sectorNo);
|
||||
rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00;
|
||||
rights[sectorNo][3] = 0x01;
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "\ncommand execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo);
|
||||
rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00;
|
||||
rights[sectorNo][3] = 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(SUCCESS, "Finished reading sector access bits");
|
||||
PrintAndLogEx(INFO, "Dumping all blocks from card...");
|
||||
|
||||
for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) {
|
||||
for (uint8_t blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) {
|
||||
bool received = false;
|
||||
current_key = MF_KEY_A;
|
||||
uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5;
|
||||
if (rights[sectorNo][data_area] == 0x07) { // no key would work
|
||||
PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d, skipping", sectorNo, blockNo);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) {
|
||||
if (mfIsSectorTrailer(blockNo)) {
|
||||
|
||||
// sector trailer. At least the Access Conditions can always be read with key A.
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
|
||||
payload.keytype = current_key;
|
||||
memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
|
||||
} else {
|
||||
// data block. Check if it can be read with key A or key B
|
||||
if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) {
|
||||
// only key B would work
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
|
||||
payload.keytype = MF_KEY_B;
|
||||
memcpy(payload.key, keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
|
||||
} else {
|
||||
// key A would work
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
|
||||
payload.keytype = current_key;
|
||||
memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
|
||||
}
|
||||
}
|
||||
|
||||
if (received) {
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
// break the re-try loop
|
||||
break;
|
||||
}
|
||||
if ((current_key == MF_KEY_A) && (tries == (MIFARE_SECTOR_RETRY / 2))) {
|
||||
// Half the tries failed with key A. Swap for key B
|
||||
current_key = MF_KEY_B;
|
||||
|
||||
// clear out keyA since it failed.
|
||||
memset(keyA + (sectorNo * MIFARE_KEY_SIZE), 0x00, MIFARE_KEY_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (received) {
|
||||
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
|
||||
uint8_t *data = resp.data.asBytes;
|
||||
|
||||
if (mfIsSectorTrailer(blockNo)) {
|
||||
// sector trailer. Fill in the keys.
|
||||
memcpy(data , keyA + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE);
|
||||
memcpy(data + 10, keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE);
|
||||
}
|
||||
|
||||
memcpy(carddata + (MFBLOCK_SIZE * (mfFirstBlockOfSector(sectorNo) + blockNo)), data, MFBLOCK_SIZE);
|
||||
PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo);
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo);
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(fptr);
|
||||
free(keyA);
|
||||
free(keyB);
|
||||
|
||||
PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks");
|
||||
return PM3_SUCCESS ;
|
||||
}
|
||||
|
||||
static int CmdHF14AMfAcl(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf acl",
|
||||
|
@ -820,6 +1012,7 @@ static int CmdHF14AMfDump(const char *Cmd) {
|
|||
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
|
||||
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
|
||||
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
|
||||
arg_lit0(NULL, "ns", "no save to file"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
@ -836,7 +1029,7 @@ static int CmdHF14AMfDump(const char *Cmd) {
|
|||
bool m1 = arg_get_lit(ctx, 4);
|
||||
bool m2 = arg_get_lit(ctx, 5);
|
||||
bool m4 = arg_get_lit(ctx, 6);
|
||||
|
||||
bool nosave = arg_get_lit(ctx, 7);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
uint64_t t1 = msclock();
|
||||
|
@ -850,244 +1043,68 @@ static int CmdHF14AMfDump(const char *Cmd) {
|
|||
}
|
||||
|
||||
uint8_t numSectors = MIFARE_1K_MAXSECTOR;
|
||||
uint16_t bytes = MIFARE_1K_MAX_BYTES;
|
||||
|
||||
if (m0) {
|
||||
numSectors = MIFARE_MINI_MAXSECTOR;
|
||||
bytes = MIFARE_MINI_MAX_BYTES;
|
||||
} else if (m1) {
|
||||
numSectors = MIFARE_1K_MAXSECTOR;
|
||||
bytes = MIFARE_1K_MAX_BYTES;
|
||||
} else if (m2) {
|
||||
numSectors = MIFARE_2K_MAXSECTOR;
|
||||
} else if (m4) {
|
||||
bytes = MIFARE_2K_MAX_BYTES;
|
||||
} else if (m4) {
|
||||
numSectors = MIFARE_4K_MAXSECTOR;
|
||||
bytes = MIFARE_4K_MAX_BYTES;
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t sectorNo, blockNo;
|
||||
uint8_t keyA[40][6];
|
||||
uint8_t keyB[40][6];
|
||||
uint8_t rights[40][4];
|
||||
uint8_t carddata[256][16];
|
||||
|
||||
FILE *f;
|
||||
PacketResponseNG resp;
|
||||
|
||||
char *fptr;
|
||||
|
||||
// Select card to get UID/UIDLEN/ATQA/SAK information
|
||||
clearCommandBuffer();
|
||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
|
||||
PrintAndLogEx(WARNING, "iso14443a card select timeout");
|
||||
return PM3_ETIMEOUT;
|
||||
// read card
|
||||
iso14a_card_select_t card ;
|
||||
uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t));
|
||||
if (mem == NULL) {
|
||||
PrintAndLogEx(ERR, "failed to allocate memory");
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
uint64_t select_status = resp.oldarg[0];
|
||||
if (select_status == 0) {
|
||||
PrintAndLogEx(WARNING, "iso14443a card select failed");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// store card info
|
||||
iso14a_card_select_t card;
|
||||
memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t));
|
||||
|
||||
if (keyFilename[0] == 0x00) {
|
||||
fptr = GenerateFilename("hf-mf-", "-key.bin");
|
||||
if (fptr == NULL)
|
||||
return PM3_ESOFT;
|
||||
|
||||
strncpy(keyFilename, fptr, sizeof(keyFilename) - 1);
|
||||
free(fptr);
|
||||
}
|
||||
|
||||
if ((f = fopen(keyFilename, "rb")) == NULL) {
|
||||
PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename);
|
||||
|
||||
// Read keys A from file
|
||||
size_t bytes_read;
|
||||
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
|
||||
bytes_read = fread(keyA[sectorNo], 1, MFKEY_SIZE, f);
|
||||
if (bytes_read != MFKEY_SIZE) {
|
||||
PrintAndLogEx(ERR, "File reading error.");
|
||||
fclose(f);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
}
|
||||
|
||||
// Read keys B from file
|
||||
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
|
||||
bytes_read = fread(keyB[sectorNo], 1, MFKEY_SIZE, f);
|
||||
if (bytes_read != MFKEY_SIZE) {
|
||||
PrintAndLogEx(ERR, "File reading error.");
|
||||
fclose(f);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
PrintAndLogEx(INFO, "Reading sector access bits...");
|
||||
PrintAndLogEx(INFO, "." NOLF);
|
||||
|
||||
uint8_t tries;
|
||||
mf_readblock_t payload;
|
||||
uint8_t current_key;
|
||||
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
|
||||
current_key = MF_KEY_A;
|
||||
for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) {
|
||||
PrintAndLogEx(NORMAL, "." NOLF);
|
||||
fflush(stdout);
|
||||
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + mfNumBlocksPerSector(sectorNo) - 1;
|
||||
payload.keytype = current_key;
|
||||
|
||||
memcpy(payload.key, current_key == MF_KEY_A ? keyA[sectorNo] : keyB[sectorNo], sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) {
|
||||
|
||||
uint8_t *data = resp.data.asBytes;
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
rights[sectorNo][0] = ((data[7] & 0x10) >> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0
|
||||
rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1
|
||||
rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2
|
||||
rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer
|
||||
break;
|
||||
} else if (tries == (MIFARE_SECTOR_RETRY / 2)) { // after half unsuccessful tries, give key B a go
|
||||
PrintAndLogEx(WARNING, "\ntrying with key B instead...");
|
||||
current_key = MF_KEY_B;
|
||||
PrintAndLogEx(INFO, "." NOLF);
|
||||
} else if (tries == (MIFARE_SECTOR_RETRY - 1)) { // on last try set defaults
|
||||
PrintAndLogEx(FAILED, "\ncould not get access rights for sector %2d. Trying with defaults...", sectorNo);
|
||||
rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00;
|
||||
rights[sectorNo][3] = 0x01;
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "\ncommand execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo);
|
||||
rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00;
|
||||
rights[sectorNo][3] = 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(SUCCESS, "Finished reading sector access bits");
|
||||
PrintAndLogEx(INFO, "Dumping all blocks from card...");
|
||||
|
||||
for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {
|
||||
for (blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) {
|
||||
bool received = false;
|
||||
current_key = MF_KEY_A;
|
||||
uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5;
|
||||
if (rights[sectorNo][data_area] == 0x07) { // no key would work
|
||||
PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d, skipping", sectorNo, blockNo);
|
||||
continue;
|
||||
}
|
||||
for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) {
|
||||
if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A.
|
||||
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
|
||||
payload.keytype = current_key;
|
||||
memcpy(payload.key, current_key == MF_KEY_A ? keyA[sectorNo] : keyB[sectorNo], sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
|
||||
} else { // data block. Check if it can be read with key A or key B
|
||||
if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work
|
||||
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
|
||||
payload.keytype = MF_KEY_B;
|
||||
memcpy(payload.key, keyB[sectorNo], sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
|
||||
} else { // key A would work
|
||||
|
||||
payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo;
|
||||
payload.keytype = current_key;
|
||||
memcpy(payload.key, current_key == MF_KEY_A ? keyA[sectorNo] : keyB[sectorNo], sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t));
|
||||
received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500);
|
||||
}
|
||||
}
|
||||
if (received) {
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
// break the re-try loop
|
||||
break;
|
||||
}
|
||||
if ((current_key == MF_KEY_A) && (tries == (MIFARE_SECTOR_RETRY / 2))) {
|
||||
// Half the tries failed with key A. Swap for key B
|
||||
current_key = MF_KEY_B;
|
||||
|
||||
// clear out keyA since it failed.
|
||||
memset(keyA[sectorNo], 0x00, sizeof(keyA[sectorNo]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (received) {
|
||||
uint8_t *data = resp.data.asBytes;
|
||||
if (blockNo == mfNumBlocksPerSector(sectorNo) - 1) { // sector trailer. Fill in the keys.
|
||||
data[0] = (keyA[sectorNo][0]);
|
||||
data[1] = (keyA[sectorNo][1]);
|
||||
data[2] = (keyA[sectorNo][2]);
|
||||
data[3] = (keyA[sectorNo][3]);
|
||||
data[4] = (keyA[sectorNo][4]);
|
||||
data[5] = (keyA[sectorNo][5]);
|
||||
|
||||
data[10] = (keyB[sectorNo][0]);
|
||||
data[11] = (keyB[sectorNo][1]);
|
||||
data[12] = (keyB[sectorNo][2]);
|
||||
data[13] = (keyB[sectorNo][3]);
|
||||
data[14] = (keyB[sectorNo][4]);
|
||||
data[15] = (keyB[sectorNo][5]);
|
||||
}
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
memcpy(carddata[mfFirstBlockOfSector(sectorNo) + blockNo], data, 16);
|
||||
PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo);
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo);
|
||||
}
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo);
|
||||
}
|
||||
}
|
||||
int res = mfc_read_tag(&card, mem, numSectors, keyFilename);
|
||||
if (res != PM3_SUCCESS) {
|
||||
free(mem);
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
|
||||
|
||||
PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks");
|
||||
// Skip saving card data to file
|
||||
if (nosave) {
|
||||
PrintAndLogEx(INFO, "Called with no save option");
|
||||
free(mem);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Save to file
|
||||
if (strlen(dataFilename) < 1) {
|
||||
fptr = GenerateFilename("hf-mf-", "-dump");
|
||||
if (fptr == NULL)
|
||||
char *fptr = GenerateFilename("hf-mf-", "-dump");
|
||||
if (fptr == NULL) {
|
||||
free(mem);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
strcpy(dataFilename, fptr);
|
||||
free(fptr);
|
||||
}
|
||||
|
||||
uint16_t bytes = 16 * (mfFirstBlockOfSector(numSectors - 1) + mfNumBlocksPerSector(numSectors - 1));
|
||||
|
||||
saveFile(dataFilename, ".bin", (uint8_t *)carddata, bytes);
|
||||
saveFileEML(dataFilename, (uint8_t *)carddata, bytes, MFBLOCK_SIZE);
|
||||
saveFile(dataFilename, ".bin", mem, bytes);
|
||||
saveFileEML(dataFilename, mem, bytes, MFBLOCK_SIZE);
|
||||
|
||||
iso14a_mf_extdump_t xdump;
|
||||
xdump.card_info = card;
|
||||
xdump.dump = (uint8_t *)carddata;
|
||||
xdump.dump = mem;
|
||||
xdump.dumplen = bytes;
|
||||
saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL);
|
||||
free(mem);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -1511,7 +1528,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
|
|||
PrintAndLogEx(ERR, "Command execute timeout\n");
|
||||
break;
|
||||
case PM3_EOPABORTED:
|
||||
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n");
|
||||
PrintAndLogEx(WARNING, "Button pressed. Aborted\n");
|
||||
break;
|
||||
case PM3_EFAILED:
|
||||
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n");
|
||||
|
@ -1519,6 +1536,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
|
|||
case PM3_ESOFT:
|
||||
PrintAndLogEx(FAILED, "No valid key found");
|
||||
break;
|
||||
case PM3_ESTATIC_NONCE:
|
||||
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
|
||||
break;
|
||||
case PM3_SUCCESS:
|
||||
key64 = bytes_to_num(keyBlock, 6);
|
||||
|
||||
|
@ -1543,7 +1563,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
|
|||
}
|
||||
return PM3_SUCCESS;
|
||||
default :
|
||||
PrintAndLogEx(ERR, "Unknown error.\n");
|
||||
PrintAndLogEx(ERR, "Unknown error\n");
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
|
||||
|
@ -1591,15 +1611,18 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
|
|||
PrintAndLogEx(ERR, "Command execute timeout\n");
|
||||
break;
|
||||
case PM3_EOPABORTED:
|
||||
PrintAndLogEx(WARNING, "button pressed. Aborted.\n");
|
||||
PrintAndLogEx(WARNING, "button pressed. Aborted\n");
|
||||
break;
|
||||
case PM3_EFAILED :
|
||||
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n");
|
||||
PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable)\n");
|
||||
break;
|
||||
case PM3_ESOFT:
|
||||
//key not found
|
||||
calibrate = false;
|
||||
continue;
|
||||
case PM3_ESTATIC_NONCE:
|
||||
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
|
||||
break;
|
||||
case PM3_SUCCESS:
|
||||
calibrate = false;
|
||||
e_sector[sectorNo].foundKey[trgKeyType] = 1;
|
||||
|
@ -1608,7 +1631,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
|
|||
mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false);
|
||||
continue;
|
||||
default :
|
||||
PrintAndLogEx(ERR, "Unknown error.\n");
|
||||
PrintAndLogEx(ERR, "Unknown error\n");
|
||||
}
|
||||
free(e_sector);
|
||||
return PM3_ESOFT;
|
||||
|
@ -2162,13 +2185,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) {
|
|||
if (isOK) {
|
||||
switch (isOK) {
|
||||
case PM3_ETIMEOUT :
|
||||
PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n");
|
||||
PrintAndLogEx(ERR, "Error: No response from Proxmark3\n");
|
||||
break;
|
||||
case PM3_EOPABORTED:
|
||||
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n");
|
||||
PrintAndLogEx(WARNING, "Button pressed. Aborted\n");
|
||||
break;
|
||||
case PM3_ESTATIC_NONCE:
|
||||
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted.\n");
|
||||
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
|
||||
break;
|
||||
default :
|
||||
break;
|
||||
|
@ -2824,6 +2847,11 @@ tryNested:
|
|||
}
|
||||
break;
|
||||
}
|
||||
case PM3_ESTATIC_NONCE:
|
||||
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
|
||||
free(e_sector);
|
||||
free(fptr);
|
||||
return isOK;
|
||||
case PM3_SUCCESS: {
|
||||
calibrate = false;
|
||||
e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6);
|
||||
|
@ -2861,7 +2889,7 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack
|
|||
break;
|
||||
}
|
||||
case PM3_ESTATIC_NONCE: {
|
||||
PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted.\n");
|
||||
PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted\n");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -6989,11 +7017,11 @@ static int CmdHF14AMfView(const char *Cmd) {
|
|||
}
|
||||
|
||||
uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE));
|
||||
if (bytes_read == 320)
|
||||
if (bytes_read == MIFARE_MINI_MAX_BYTES)
|
||||
block_cnt = MIFARE_MINI_MAXBLOCK;
|
||||
else if (bytes_read == 2048)
|
||||
else if (bytes_read == MIFARE_2K_MAX_BYTES)
|
||||
block_cnt = MIFARE_2K_MAXBLOCK;
|
||||
else if (bytes_read == 4096)
|
||||
else if (bytes_read == MIFARE_4K_MAX_BYTES)
|
||||
block_cnt = MIFARE_4K_MAXBLOCK;
|
||||
|
||||
if (verbose) {
|
||||
|
|
|
@ -304,12 +304,12 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si
|
|||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
/* We should have a valid filename now, e.g. dumpdata-3.bin */
|
||||
// We should have a valid filename now, e.g. dumpdata-3.bin
|
||||
|
||||
/*Opening file for writing in binary mode*/
|
||||
// Opening file for writing in binary mode
|
||||
FILE *f = fopen(fileName, "wb");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName);
|
||||
free(fileName);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
@ -336,12 +336,12 @@ int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t
|
|||
int blocks = datalen / blocksize;
|
||||
uint16_t currblock = 1;
|
||||
|
||||
/* We should have a valid filename now, e.g. dumpdata-3.bin */
|
||||
// We should have a valid filename now, e.g. dumpdata-3.bin
|
||||
|
||||
/*Opening file for writing in text mode*/
|
||||
// Opening file for writing in text mode
|
||||
FILE *f = fopen(fileName, "w+");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName);
|
||||
retval = PM3_EFILE;
|
||||
goto out;
|
||||
}
|
||||
|
@ -770,7 +770,7 @@ int saveFileWAVE(const char *preferredName, const int *data, size_t datalen) {
|
|||
|
||||
FILE *wave_file = fopen(fileName, "wb");
|
||||
if (!wave_file) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. "_YELLOW_("'%s'"), fileName);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName);
|
||||
retval = PM3_EFILE;
|
||||
goto out;
|
||||
}
|
||||
|
@ -806,7 +806,7 @@ int saveFilePM3(const char *preferredName, int *data, size_t datalen) {
|
|||
|
||||
FILE *f = fopen(fileName, "w");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. "_YELLOW_("'%s'"), fileName);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName);
|
||||
retval = PM3_EFILE;
|
||||
goto out;
|
||||
}
|
||||
|
@ -939,7 +939,7 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata,
|
|||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
|
||||
free(path);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
@ -1050,7 +1050,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) {
|
|||
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
|
||||
free(path);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
@ -1427,6 +1427,7 @@ int loadFileJSONroot(const char *preferredName, void **proot, bool verbose) {
|
|||
return retval;
|
||||
}
|
||||
|
||||
// iceman: todo - move all unsafe functions like this from client source.
|
||||
int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint32_t *keycnt) {
|
||||
// t5577 == 4 bytes
|
||||
// mifare == 6 bytes
|
||||
|
@ -1463,7 +1464,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
|
|||
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
|
||||
retval = PM3_EFILE;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1566,7 +1567,7 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
|
|||
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
|
||||
retval = PM3_EFILE;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1619,6 +1620,57 @@ out:
|
|||
return retval;
|
||||
}
|
||||
|
||||
int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen) {
|
||||
|
||||
char *path;
|
||||
int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
|
||||
free(path);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
free(path);
|
||||
|
||||
// get filesize in order to malloc memory
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (fsize <= 0) {
|
||||
PrintAndLogEx(FAILED, "error, when getting filesize");
|
||||
fclose(f);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
// Half is KEY A, half is KEY B
|
||||
fsize /= 2;
|
||||
|
||||
*keya = calloc(fsize, sizeof(uint8_t));
|
||||
if (*keya == NULL) {
|
||||
PrintAndLogEx(FAILED, "error, cannot allocate memory");
|
||||
fclose(f);
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
*alen = fread(*keya, 1, fsize, f);
|
||||
|
||||
*keyb = calloc(fsize, sizeof(uint8_t));
|
||||
if (*keyb == NULL) {
|
||||
PrintAndLogEx(FAILED, "error, cannot allocate memory");
|
||||
fclose(f);
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
*blen = fread(*keyb, 1, fsize, f);
|
||||
fclose(f);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) {
|
||||
|
||||
mfu_df_e retval = MFU_DF_UNKNOWN;
|
||||
|
@ -2058,7 +2110,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl
|
|||
}
|
||||
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", fn);
|
||||
PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fn);
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
|
@ -2068,7 +2120,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl
|
|||
int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft, size_t blocksize) {
|
||||
|
||||
if (d == NULL || n == 0) {
|
||||
PrintAndLogEx(INFO, "No data to save. Skipping...");
|
||||
PrintAndLogEx(INFO, "No data to save, skipping...");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
|
|
|
@ -236,6 +236,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
|
|||
*/
|
||||
int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint32_t *keycnt);
|
||||
|
||||
int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen);
|
||||
|
||||
typedef enum {
|
||||
MFU_DF_UNKNOWN,
|
||||
|
@ -276,5 +277,20 @@ DumpFileType_t getfiletype(const char *filename);
|
|||
*/
|
||||
int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen);
|
||||
|
||||
|
||||
/** STUB
|
||||
* @brief Utility function to save data to three file files (BIN/EML/JSON).
|
||||
* It also tries to save according to user preferences set dump folder paths.
|
||||
* E.g. dumpdata.bin
|
||||
* E.g. dumpdata.eml
|
||||
* E.g. dumpdata.json
|
||||
|
||||
* @param fn
|
||||
* @param d The binary data to write to the file
|
||||
* @param n the length of the data
|
||||
* @param jsft json format type for the different memory cards (MFC, MFUL, LEGIC, 14B, 15, ICLASS etc)
|
||||
* @param blocksize
|
||||
* @return PM3_SUCCESS if OK
|
||||
*/
|
||||
int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft, size_t blocksize);
|
||||
#endif // FILEUTILS_H
|
||||
|
|
|
@ -575,6 +575,13 @@ Read config:
|
|||
2. send 0xE000, will return the configuration bytes.
|
||||
`results: 850000000000000000005A5A00000008`
|
||||
|
||||
|
||||
Mapping of configuration bytes so far:
|
||||
```
|
||||
850000000000000000005A5A00000008
|
||||
^^ --> SAK
|
||||
```
|
||||
|
||||
Write config:
|
||||
1. sending custom auth with all zeros key
|
||||
2. send 0xE100
|
||||
|
|
|
@ -54,6 +54,8 @@ Then you are missing Rosetta 2 and need to install it: `/usr/sbin/softwareupdate
|
|||
Homebrew has changed their prefix to differentiate between native Apple Silicon and Intel compiled binaries. The Makefile attempts to account for this but please note that
|
||||
whichever terminal or application you're using must be running under Architecture "Apple" as seen by Activity Monitor as all child processes inherit the Rosetta 2 environment of their parent. You can check which architecture you're currently running under with a `uname -m` in your terminal.
|
||||
|
||||
The fastest option is to run the brew command with the `arch -arm64` prefix i.e. `arch -arm64 brew install --HEAD --with-blueshark proxmark3`. This doesn't require running the whole terminal in Rosetta 2.
|
||||
|
||||
Visual Studio Code still runs under Rosetta 2 and if you're developing for proxmark3 on an Apple Silicon Mac you might want to consider running the Insiders build which has support for running natively on Apple Silicon.
|
||||
|
||||
## Install Proxmark3 tools
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue