Merge branch 'master' into mf-supercard

Signed-off-by: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com>
This commit is contained in:
AloneLiberty 2023-04-01 16:42:02 +00:00 committed by GitHub
commit ac5dd0ffa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 381 additions and 249 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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) {

View file

@ -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) {

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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