Merge branch 'master' into master

Signed-off-by: Iceman <iceman@iuse.se>
This commit is contained in:
Iceman 2023-04-01 21:34:20 +02:00 committed by GitHub
commit 83cab74f24
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 507 additions and 319 deletions

View file

@ -3,6 +3,9 @@ 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]
- Added iClass credit key to default iClass key table and reorganized key order (@GuruSteve)
- Changed `hf mf value` - ability to use transfer on different block (@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 gdmsetcfg` - Supprt Gen4 GDM write configuration block (@iceman1001)
- Added `hf mf gdmcfg` - Support Gen4 GDM read 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) - Changed magic note to include a section about GDM tags (@iceman1001)
@ -39,9 +42,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 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) - 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) - 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 iClass credit key to default iClass key table and reorganized key order (@GuruSteve) - 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] ## [Nitride.4.16191][2023-01-29]
- Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox)

View file

@ -1584,7 +1584,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break; break;
} }
case CMD_HF_MIFARE_VALUE: { case CMD_HF_MIFARE_VALUE: {
MifareValue(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); MifareValue(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes);
break; break;
} }
case CMD_HF_MIFAREU_WRITEBL: { case CMD_HF_MIFAREU_WRITEBL: {

View file

@ -642,21 +642,28 @@ OUT:
} }
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) { void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) {
// params // params
uint8_t blockNo = arg0; uint8_t blockNo = arg0;
uint8_t keyType = arg1; uint8_t keyType = arg1;
uint8_t transferKeyType = arg2;
uint64_t ui64Key = 0; uint64_t ui64Key = 0;
uint64_t transferUi64Key = 0;
uint8_t blockdata[16] = {0x00}; uint8_t blockdata[16] = {0x00};
ui64Key = bytes_to_num(datain, 6); ui64Key = bytes_to_num(datain, 6);
memcpy(blockdata, datain + 10, 16); memcpy(blockdata, datain + 11, 16);
transferUi64Key = bytes_to_num(datain + 27, 6);
// variables // variables
uint8_t action = datain[9]; uint8_t action = datain[9];
uint8_t transferBlk = datain[10];
bool needAuth = datain[33];
uint8_t isOK = 0; uint8_t isOK = 0;
uint8_t uid[10] = {0x00}; uint8_t uid[10] = {0x00};
uint32_t cuid = 0; uint32_t cuid = 0;
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00};
uint8_t len = 0;
struct Crypto1State mpcs = {0, 0}; struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs; struct Crypto1State *pcs;
pcs = &mpcs; pcs = &mpcs;
@ -686,6 +693,21 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
break; break;
}; };
if (needAuth) {
// transfer to other sector
if (mifare_classic_auth(pcs, cuid, transferBlk, transferKeyType, transferUi64Key, AUTH_NESTED)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Nested auth error");
break;
}
}
// send transfer (commit the change)
len = mifare_sendcmd_short(pcs, 1, MIFARE_CMD_TRANSFER, (transferBlk != 0) ? transferBlk : blockNo, receivedAnswer, NULL, NULL);
if (len != 1 && receivedAnswer[0] != 0x0A) { // 0x0a - ACK
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error in transfer: %02x", receivedAnswer[0]);
break;
}
if (mifare_classic_halt(pcs, cuid)) { if (mifare_classic_halt(pcs, cuid)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error");
break; break;
@ -1154,7 +1176,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
} }
memcpy(prev_enc_nt, receivedAnswer, 4); memcpy(prev_enc_nt, receivedAnswer, 4);
if (prev_counter == 5) { 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; isOK = PM3_ESTATIC_NONCE;
break; break;
} }
@ -1224,6 +1251,9 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
LED_B_ON(); LED_B_ON();
WDT_HIT(); WDT_HIT();
uint32_t prev_enc_nt = 0;
uint8_t prev_counter = 0;
uint16_t unsuccessful_tries = 0; uint16_t unsuccessful_tries = 0;
uint16_t davg = 0; uint16_t davg = 0;
dmax = 0; dmax = 0;
@ -1266,11 +1296,13 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
}; };
// cards with fixed nonce // cards with fixed nonce
/*
if (nt1 == nt2) { if (nt1 == nt2) {
Dbprintf("Nested: %08x vs %08x", nt1, nt2); Dbprintf("Nested: %08x vs %08x", nt1, nt2);
break; 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 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++) { for (i = 101; i < 1200; i++) {
nttmp = prng_successor(nttmp, 1); nttmp = prng_successor(nttmp, 1);
@ -1292,6 +1324,21 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8
isOK = PM3_EFAILED; 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) if (rtr > 1)

View file

@ -26,7 +26,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes);
void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain);
void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain);
void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain);

View file

@ -533,6 +533,8 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo
if (action == 0x01) if (action == 0x01)
command = MIFARE_CMD_DEC; command = MIFARE_CMD_DEC;
if (action == 0x02)
command = MIFARE_CMD_RESTORE;
// Send increment or decrement command // Send increment or decrement command
len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, receivedAnswerPar, NULL); len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, receivedAnswerPar, NULL);
@ -567,13 +569,6 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res);
return 2; return 2;
} }
} else {
// send trnasfer (commit the change)
len = mifare_sendcmd_short(pcs, 1, MIFARE_CMD_TRANSFER, blockNo, receivedAnswer, receivedAnswerPar, NULL);
if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
return 1;
}
} }
return 0; return 0;

View file

@ -38,15 +38,22 @@
#include "wiegand_formats.h" #include "wiegand_formats.h"
#include "wiegand_formatutils.h" #include "wiegand_formatutils.h"
#define MIFARE_4K_MAXBLOCK 256 #define MIFARE_4K_MAXBLOCK 256
#define MIFARE_2K_MAXBLOCK 128 #define MIFARE_2K_MAXBLOCK 128
#define MIFARE_1K_MAXBLOCK 64 #define MIFARE_1K_MAXBLOCK 64
#define MIFARE_MINI_MAXBLOCK 20 #define MIFARE_MINI_MAXBLOCK 20
#define MIFARE_MINI_MAXSECTOR 5 #define MIFARE_4K_MAXSECTOR 40
#define MIFARE_1K_MAXSECTOR 16 #define MIFARE_2K_MAXSECTOR 32
#define MIFARE_2K_MAXSECTOR 32 #define MIFARE_1K_MAXSECTOR 16
#define MIFARE_4K_MAXSECTOR 40 #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); 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++) { for (uint16_t i = 0; i < n; i++) {
if (mfIsSectorTrailer(i)) { if (mfIsSectorTrailer(i)) {
e_sector[mfSectorNum(i)].foundKey[0] = 1; 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)].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); 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; 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) { static int CmdHF14AMfAcl(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mf acl", 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, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_lit0(NULL, "ns", "no save to file"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
@ -836,7 +1029,7 @@ static int CmdHF14AMfDump(const char *Cmd) {
bool m1 = arg_get_lit(ctx, 4); bool m1 = arg_get_lit(ctx, 4);
bool m2 = arg_get_lit(ctx, 5); bool m2 = arg_get_lit(ctx, 5);
bool m4 = arg_get_lit(ctx, 6); bool m4 = arg_get_lit(ctx, 6);
bool nosave = arg_get_lit(ctx, 7);
CLIParserFree(ctx); CLIParserFree(ctx);
uint64_t t1 = msclock(); uint64_t t1 = msclock();
@ -850,244 +1043,68 @@ static int CmdHF14AMfDump(const char *Cmd) {
} }
uint8_t numSectors = MIFARE_1K_MAXSECTOR; uint8_t numSectors = MIFARE_1K_MAXSECTOR;
uint16_t bytes = MIFARE_1K_MAX_BYTES;
if (m0) { if (m0) {
numSectors = MIFARE_MINI_MAXSECTOR; numSectors = MIFARE_MINI_MAXSECTOR;
bytes = MIFARE_MINI_MAX_BYTES;
} else if (m1) { } else if (m1) {
numSectors = MIFARE_1K_MAXSECTOR; numSectors = MIFARE_1K_MAXSECTOR;
bytes = MIFARE_1K_MAX_BYTES;
} else if (m2) { } else if (m2) {
numSectors = MIFARE_2K_MAXSECTOR; numSectors = MIFARE_2K_MAXSECTOR;
} else if (m4) { bytes = MIFARE_2K_MAX_BYTES;
} else if (m4) {
numSectors = MIFARE_4K_MAXSECTOR; numSectors = MIFARE_4K_MAXSECTOR;
bytes = MIFARE_4K_MAX_BYTES;
} else { } else {
PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); PrintAndLogEx(WARNING, "Please specify a MIFARE Type");
return PM3_EINVARG; return PM3_EINVARG;
} }
uint8_t sectorNo, blockNo; // read card
uint8_t keyA[40][6]; iso14a_card_select_t card ;
uint8_t keyB[40][6]; uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t));
uint8_t rights[40][4]; if (mem == NULL) {
uint8_t carddata[256][16]; PrintAndLogEx(ERR, "failed to allocate memory");
return PM3_EMALLOC;
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;
} }
int res = mfc_read_tag(&card, mem, numSectors, keyFilename);
uint64_t select_status = resp.oldarg[0]; if (res != PM3_SUCCESS) {
if (select_status == 0) { free(mem);
PrintAndLogEx(WARNING, "iso14443a card select failed"); return res;
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);
}
}
} }
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); 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) { if (strlen(dataFilename) < 1) {
fptr = GenerateFilename("hf-mf-", "-dump"); char *fptr = GenerateFilename("hf-mf-", "-dump");
if (fptr == NULL) if (fptr == NULL) {
free(mem);
return PM3_ESOFT; return PM3_ESOFT;
}
strcpy(dataFilename, fptr); strcpy(dataFilename, fptr);
free(fptr); free(fptr);
} }
uint16_t bytes = 16 * (mfFirstBlockOfSector(numSectors - 1) + mfNumBlocksPerSector(numSectors - 1)); saveFile(dataFilename, ".bin", mem, bytes);
saveFileEML(dataFilename, mem, bytes, MFBLOCK_SIZE);
saveFile(dataFilename, ".bin", (uint8_t *)carddata, bytes);
saveFileEML(dataFilename, (uint8_t *)carddata, bytes, MFBLOCK_SIZE);
iso14a_mf_extdump_t xdump; iso14a_mf_extdump_t xdump;
xdump.card_info = card; xdump.card_info = card;
xdump.dump = (uint8_t *)carddata; xdump.dump = mem;
xdump.dumplen = bytes; xdump.dumplen = bytes;
saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL);
free(mem);
return PM3_SUCCESS; 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"); PrintAndLogEx(ERR, "Command execute timeout\n");
break; break;
case PM3_EOPABORTED: case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n"); PrintAndLogEx(WARNING, "Button pressed. Aborted\n");
break; break;
case PM3_EFAILED: 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");
@ -1519,6 +1536,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't
case PM3_ESOFT: case PM3_ESOFT:
PrintAndLogEx(FAILED, "No valid key found"); PrintAndLogEx(FAILED, "No valid key found");
break; break;
case PM3_ESTATIC_NONCE:
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
break;
case PM3_SUCCESS: case PM3_SUCCESS:
key64 = bytes_to_num(keyBlock, 6); 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; return PM3_SUCCESS;
default : default :
PrintAndLogEx(ERR, "Unknown error.\n"); PrintAndLogEx(ERR, "Unknown error\n");
} }
return PM3_SUCCESS; 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"); PrintAndLogEx(ERR, "Command execute timeout\n");
break; break;
case PM3_EOPABORTED: case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "button pressed. Aborted.\n"); PrintAndLogEx(WARNING, "button pressed. Aborted\n");
break; break;
case PM3_EFAILED : 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; break;
case PM3_ESOFT: case PM3_ESOFT:
//key not found //key not found
calibrate = false; calibrate = false;
continue; continue;
case PM3_ESTATIC_NONCE:
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
break;
case PM3_SUCCESS: case PM3_SUCCESS:
calibrate = false; calibrate = false;
e_sector[sectorNo].foundKey[trgKeyType] = 1; 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); mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false);
continue; continue;
default : default :
PrintAndLogEx(ERR, "Unknown error.\n"); PrintAndLogEx(ERR, "Unknown error\n");
} }
free(e_sector); free(e_sector);
return PM3_ESOFT; return PM3_ESOFT;
@ -2162,13 +2185,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) {
if (isOK) { if (isOK) {
switch (isOK) { switch (isOK) {
case PM3_ETIMEOUT : case PM3_ETIMEOUT :
PrintAndLogEx(ERR, "Error: No response from Proxmark3.\n"); PrintAndLogEx(ERR, "Error: No response from Proxmark3\n");
break; break;
case PM3_EOPABORTED: case PM3_EOPABORTED:
PrintAndLogEx(WARNING, "Button pressed. Aborted.\n"); PrintAndLogEx(WARNING, "Button pressed. Aborted\n");
break; break;
case PM3_ESTATIC_NONCE: case PM3_ESTATIC_NONCE:
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted.\n"); PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
break; break;
default : default :
break; break;
@ -2824,6 +2847,11 @@ tryNested:
} }
break; break;
} }
case PM3_ESTATIC_NONCE:
PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n");
free(e_sector);
free(fptr);
return isOK;
case PM3_SUCCESS: { case PM3_SUCCESS: {
calibrate = false; calibrate = false;
e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); 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; break;
} }
case PM3_ESTATIC_NONCE: { case PM3_ESTATIC_NONCE: {
PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted.\n"); PrintAndLogEx(ERR, "\nError: Static encrypted nonce detected. Aborted\n");
break; break;
} }
default: { default: {
@ -6892,11 +6920,11 @@ static int CmdHF14AMfView(const char *Cmd) {
} }
uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); 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; block_cnt = MIFARE_MINI_MAXBLOCK;
else if (bytes_read == 2048) else if (bytes_read == MIFARE_2K_MAX_BYTES)
block_cnt = MIFARE_2K_MAXBLOCK; block_cnt = MIFARE_2K_MAXBLOCK;
else if (bytes_read == 4096) else if (bytes_read == MIFARE_4K_MAX_BYTES)
block_cnt = MIFARE_4K_MAXBLOCK; block_cnt = MIFARE_4K_MAXBLOCK;
if (verbose) { if (verbose) {
@ -7774,24 +7802,30 @@ static int CmdHF14AMfValue(const char *Cmd) {
"hf mf value --blk 16 -k FFFFFFFFFFFF --inc 10\n" "hf mf value --blk 16 -k FFFFFFFFFFFF --inc 10\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF -b --dec 10\n" "hf mf value --blk 16 -k FFFFFFFFFFFF -b --dec 10\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF -b --get\n" "hf mf value --blk 16 -k FFFFFFFFFFFF -b --get\n"
"hf mf value --blk 16 -k FFFFFFFFFFFF --res --transfer 30 --tk FFFFFFFFFFFF --> transfer block 16 value to block 30 (even if block can't be incremented by ACL)\n"
"hf mf value --get -d 87D612007829EDFF87D6120011EE11EE\n" "hf mf value --get -d 87D612007829EDFF87D6120011EE11EE\n"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str0("k", "key", "<hex>", "key, 6 hex bytes"), arg_str0("k", "key", "<hex>", "key, 6 hex bytes"),
arg_lit0("a", NULL, "input key type is key A (def)"), arg_lit0("a", NULL, "input key type is key A (def)"),
arg_lit0("b", NULL, "input key type is key B"), arg_lit0("b", NULL, "input key type is key B"),
arg_u64_0(NULL, "inc", "<dec>", "Incremenet value by X (0 - 2147483647)"), arg_u64_0(NULL, "inc", "<dec>", "Increment value by X (0 - 2147483647)"),
arg_u64_0(NULL, "dec", "<dec>", "Dcrement value by X (0 - 2147483647)"), arg_u64_0(NULL, "dec", "<dec>", "Decrement value by X (0 - 2147483647)"),
arg_u64_0(NULL, "set", "<dec>", "Set value to X (-2147483647 - 2147483647)"), arg_u64_0(NULL, "set", "<dec>", "Set value to X (-2147483647 - 2147483647)"),
arg_lit0(NULL, "get", "Get value from block"), arg_u64_0(NULL, "transfer", "<dec>", "Transfer value to other block (after inc/dec/restore)"),
arg_int0(NULL, "blk", "<dec>", "block number"), arg_str0(NULL, "tkey", "<hex>", "transfer key, 6 hex bytes (if transfer is preformed to other sector)"),
arg_str0("d", "data", "<hex>", "block data to extract values from (16 hex bytes)"), arg_lit0(NULL, "ta", "transfer key type is key A (def)"),
arg_param_end arg_lit0(NULL, "tb", "transfer key type is key B"),
arg_lit0(NULL, "get", "Get value from block"),
arg_lit0(NULL, "res", "Restore (copy value to card buffer, should be used with --transfer)"),
arg_int0(NULL, "blk", "<dec>", "block number"),
arg_str0("d", "data", "<hex>", "block data to extract values from (16 hex bytes)"),
arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 8, 1); uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1);
uint8_t keytype = MF_KEY_A; uint8_t keytype = MF_KEY_A;
if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) {
@ -7802,29 +7836,44 @@ static int CmdHF14AMfValue(const char *Cmd) {
keytype = MF_KEY_B;; keytype = MF_KEY_B;;
} }
uint8_t transferkeytype = MF_KEY_A;
if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) {
CLIParserFree(ctx);
PrintAndLogEx(WARNING, "Input key type must be A or B");
return PM3_EINVARG;
} else if (arg_get_lit(ctx, 10)) {
keytype = MF_KEY_B;;
}
int keylen = 0; int keylen = 0;
uint8_t key[6] = {0}; uint8_t key[6] = {0};
CLIGetHexWithReturn(ctx, 1, key, &keylen); CLIGetHexWithReturn(ctx, 1, key, &keylen);
int transferkeylen = 0;
uint8_t transferkey[6] = {0};
CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen);
/* /*
Value /Value Value BLK /BLK BLK /BLK Value /Value Value BLK /BLK BLK /BLK
00000000 FFFFFFFF 00000000 10 EF 10 EF 00000000 FFFFFFFF 00000000 10 EF 10 EF
BLK is used to referece where the backup come from, I suspect its just the current block for the actual value ? BLK is used to reference where the backup come from, I suspect it's just the current block for the actual value ?
increment and decrement are an unsigned value increment and decrement are an unsigned value
set value is a signed value set value is a signed value
We are getting signed and/or bigger values to allow a defult to be set meaning users did not supply that option. We are getting signed and/or bigger values to allow a default to be set meaning users did not supply that option.
*/ */
int64_t incval = (int64_t)arg_get_u64_def(ctx, 4, -1); // Inc by -1 is invalid, so not set. int64_t incval = (int64_t)arg_get_u64_def(ctx, 4, -1); // Inc by -1 is invalid, so not set.
int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Inc by -1 is invalid, so not set. int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Dec by -1 is invalid, so not set.
int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF); // out of bounds (for int32) so not set int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF); // out of bounds (for int32) so not set
bool getval = arg_get_lit(ctx, 7); int64_t trnval = (int64_t)arg_get_u64_def(ctx, 7, -1); // block to transfer to
bool getval = arg_get_lit(ctx, 11);
bool resval = arg_get_lit(ctx, 12);
int dlen = 0; int dlen = 0;
uint8_t data[16] = {0}; uint8_t data[16] = {0};
CLIGetHexWithReturn(ctx, 9, data, &dlen); CLIGetHexWithReturn(ctx, 14, data, &dlen);
CLIParserFree(ctx); CLIParserFree(ctx);
uint8_t action = 3; // 0 Increment, 1 - Decrement, 2 - Set, 3 - Get, 4 - Decode from data uint8_t action = 4; // 0 Increment, 1 - Decrement, 2 - Restore, 3 - Set, 4 - Get, 5 - Decode from data
uint32_t value = 0; uint32_t value = 0;
// Need to check we only have 1 of inc/dec/set and get the value from the selected option // Need to check we only have 1 of inc/dec/set and get the value from the selected option
@ -7852,7 +7901,7 @@ static int CmdHF14AMfValue(const char *Cmd) {
if (setval != 0x7FFFFFFFFFFFFFFF) { if (setval != 0x7FFFFFFFFFFFFFFF) {
optionsprovided++; optionsprovided++;
action = 2; action = 3;
if ((setval < -2147483647) || (setval > 2147483647)) { if ((setval < -2147483647) || (setval > 2147483647)) {
PrintAndLogEx(WARNING, "set value must be between -2147483647 and 2147483647. Got %lli", setval); PrintAndLogEx(WARNING, "set value must be between -2147483647 and 2147483647. Got %lli", setval);
return PM3_EINVARG; return PM3_EINVARG;
@ -7860,9 +7909,19 @@ static int CmdHF14AMfValue(const char *Cmd) {
value = (uint32_t)setval; value = (uint32_t)setval;
} }
if (resval) {
if (trnval == -1) {
PrintAndLogEx(WARNING, "You can't use restore without using transfer");
return PM3_EINVARG;
}
optionsprovided++;
action = 2;
}
if (dlen != 0) { if (dlen != 0) {
optionsprovided++; optionsprovided++;
action = 4; action = 5;
if (dlen != 16) { if (dlen != 16) {
PrintAndLogEx(WARNING, "date length must be 16 hex bytes long, got %d", dlen); PrintAndLogEx(WARNING, "date length must be 16 hex bytes long, got %d", dlen);
return PM3_EINVARG; return PM3_EINVARG;
@ -7874,49 +7933,59 @@ static int CmdHF14AMfValue(const char *Cmd) {
return PM3_EINVARG; return PM3_EINVARG;
} }
// dont want to write value data and break something if (trnval != -1 && action > 2) {
if ((blockno == 0) || (mfIsSectorTrailer(blockno))) { PrintAndLogEx(WARNING, "You can't use transfer without using --inc, --dec or --res");
PrintAndLogEx(WARNING, "invlaid block number, should be a data block ");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (action < 3) { if (trnval != -1 && transferkeylen == 0 && mfSectorNum(trnval) != mfSectorNum(blockno)) {
PrintAndLogEx(WARNING, "Transfer is preformed to other sector, but no key for new sector provided");
return PM3_EINVARG;
}
// don't want to write value data and break something
if ((blockno == 0) || (mfIsSectorTrailer(blockno)) || (trnval == 0) || (trnval != -1 && mfIsSectorTrailer(trnval))) {
PrintAndLogEx(WARNING, "invalid block number, should be a data block");
return PM3_EINVARG;
}
if (action < 4) {
uint8_t isok = true;
if (g_session.pm3_present == false) if (g_session.pm3_present == false)
return PM3_ENOTTY; return PM3_ENOTTY;
// 0 Increment, 1 - Decrement, 2 - Restore, 3 - Set, 4 - Get, 5 - Decode from data
if (action <= 1) { // increment/decrement value if (action <= 2) { // increment/decrement/restore value
uint8_t block[MFBLOCK_SIZE] = {0x00}; uint8_t block[MFBLOCK_SIZE] = {0x00};
memcpy(block, (uint8_t *)&value, 4); memcpy(block, (uint8_t *)&value, 4);
uint8_t cmddata[26]; uint8_t cmddata[34];
memcpy(cmddata, key, sizeof(key)); // Key == 6 data went to 10, so lets offset 9 for inc/dec memcpy(cmddata, key, sizeof(key)); // Key == 6 data went to 10, so lets offset 9 for inc/dec
if (action == 0) if (action == 0)
PrintAndLogEx(INFO, "value increment by : %d", value); PrintAndLogEx(INFO, "Value incremented by : %d", value);
else if (action == 1)
PrintAndLogEx(INFO, "value decrement by : %d", value); PrintAndLogEx(INFO, "Value decremented by : %d", value);
PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); cmddata[9] = action; // 00 if increment, 01 if decrement, 02 if restore
if (trnval != -1) {
cmddata[9] = action; // 00 if increment, 01 if decrement. cmddata[10] = trnval; // transfer to block
memcpy(cmddata + 10, block, sizeof(block)); memcpy(cmddata + 27, transferkey, sizeof(transferkey));
if (mfSectorNum(trnval) != mfSectorNum(blockno))
cmddata[33] = 1; // should send nested auth
PrintAndLogEx(INFO, "Transfer block no %d to block %d", blockno, trnval);
} else {
cmddata[10] = 0;
PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key)));
}
memcpy(cmddata + 11, block, sizeof(block));
clearCommandBuffer(); clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFARE_VALUE, blockno, keytype, 0, cmddata, sizeof(cmddata)); SendCommandMIX(CMD_HF_MIFARE_VALUE, blockno, keytype, transferkeytype, cmddata, sizeof(cmddata));
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(FAILED, "Command execute timeout"); PrintAndLogEx(FAILED, "Command execute timeout");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
isok = resp.oldarg[0] & 0xff;
if (resp.oldarg[0] & 0xFF) {
// all ok so set flag to read current value
getval = true;
PrintAndLogEx(SUCCESS, "Update ( " _GREEN_("success") " )");
} else {
PrintAndLogEx(FAILED, "Update ( " _RED_("failed") " )");
}
} else { // set value } else { // set value
// To set a value block (or setup) we can use the normal mifare classic write block // To set a value block (or setup) we can use the normal mifare classic write block
// So build the command options can call CMD_HF_MIFARE_WRITEBL // So build the command options can call CMD_HF_MIFARE_WRITEBL
@ -7941,17 +8010,14 @@ static int CmdHF14AMfValue(const char *Cmd) {
PrintAndLogEx(FAILED, "Command execute timeout"); PrintAndLogEx(FAILED, "Command execute timeout");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
int status = resp.oldarg[0]; isok = resp.oldarg[0] & 0xff;
if (status) { }
// all ok so set flag to read current value
getval = true; if (isok) {
PrintAndLogEx(SUCCESS, "Update ( " _GREEN_("success") " )"); PrintAndLogEx(SUCCESS, "Update ... : " _GREEN_("success"));
} else if (status == PM3_ETEAROFF) { getval = true; // all ok so set flag to read current value
// all ok so set flag to read current value } else {
getval = true; PrintAndLogEx(FAILED, "Update ... : " _RED_("failed"));
} else {
PrintAndLogEx(FAILED, "Update ( " _RED_("failed") " )");
}
} }
} }
@ -7960,10 +8026,17 @@ static int CmdHF14AMfValue(const char *Cmd) {
int32_t readvalue; int32_t readvalue;
int res = -1; int res = -1;
if (action == 4) { if (action == 5) {
res = PM3_SUCCESS; // alread have data from command line res = PM3_SUCCESS; // already have data from command line
} else { } else {
res = mfReadBlock(blockno, keytype, key, data); if (trnval == -1) {
res = mfReadBlock(blockno, keytype, key, data);
} else {
if (mfSectorNum(trnval) != mfSectorNum(blockno))
res = mfReadBlock(trnval, transferkeytype, transferkey, data);
else
res = mfReadBlock(trnval, keytype, key, data);
}
} }
if (res == PM3_SUCCESS) { if (res == PM3_SUCCESS) {

View file

@ -304,12 +304,12 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si
return PM3_EMALLOC; 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"); FILE *f = fopen(fileName, "wb");
if (!f) { if (!f) {
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", fileName); PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName);
free(fileName); free(fileName);
return PM3_EFILE; return PM3_EFILE;
} }
@ -336,12 +336,12 @@ int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t
int blocks = datalen / blocksize; int blocks = datalen / blocksize;
uint16_t currblock = 1; 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+"); FILE *f = fopen(fileName, "w+");
if (!f) { 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; retval = PM3_EFILE;
goto out; goto out;
} }
@ -770,7 +770,7 @@ int saveFileWAVE(const char *preferredName, const int *data, size_t datalen) {
FILE *wave_file = fopen(fileName, "wb"); FILE *wave_file = fopen(fileName, "wb");
if (!wave_file) { 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; retval = PM3_EFILE;
goto out; goto out;
} }
@ -806,7 +806,7 @@ int saveFilePM3(const char *preferredName, int *data, size_t datalen) {
FILE *f = fopen(fileName, "w"); FILE *f = fopen(fileName, "w");
if (!f) { 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; retval = PM3_EFILE;
goto out; goto out;
} }
@ -939,7 +939,7 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata,
FILE *f = fopen(path, "rb"); FILE *f = fopen(path, "rb");
if (!f) { if (!f) {
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
free(path); free(path);
return PM3_EFILE; return PM3_EFILE;
} }
@ -1050,7 +1050,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) {
FILE *f = fopen(path, "r"); FILE *f = fopen(path, "r");
if (!f) { if (!f) {
PrintAndLogEx(WARNING, "file not found or locked. '" _YELLOW_("%s")"'", path); PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path);
free(path); free(path);
return PM3_EFILE; return PM3_EFILE;
} }
@ -1427,6 +1427,7 @@ int loadFileJSONroot(const char *preferredName, void **proot, bool verbose) {
return retval; 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) { int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint32_t *keycnt) {
// t5577 == 4 bytes // t5577 == 4 bytes
// mifare == 6 bytes // mifare == 6 bytes
@ -1463,7 +1464,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
FILE *f = fopen(path, "r"); FILE *f = fopen(path, "r");
if (!f) { 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; retval = PM3_EFILE;
goto out; goto out;
} }
@ -1566,7 +1567,7 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key
FILE *f = fopen(path, "r"); FILE *f = fopen(path, "r");
if (!f) { 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; retval = PM3_EFILE;
goto out; goto out;
} }
@ -1619,6 +1620,57 @@ out:
return retval; 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 detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) {
mfu_df_e retval = MFU_DF_UNKNOWN; 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) { 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; 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) { int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft, size_t blocksize) {
if (d == NULL || n == 0) { if (d == NULL || n == 0) {
PrintAndLogEx(INFO, "No data to save. Skipping..."); PrintAndLogEx(INFO, "No data to save, skipping...");
return PM3_EINVARG; 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 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 { typedef enum {
MFU_DF_UNKNOWN, 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); 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); int pm3_save_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft, size_t blocksize);
#endif // FILEUTILS_H #endif // FILEUTILS_H

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 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. 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. 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 ## Install Proxmark3 tools