Merge pull request #554 from quantum-x/master-UL-4K-DirectWrite-OneTimeWrite
Adding extended Magic Card support
This commit is contained in:
commit
f8b28523d7
2 changed files with 252 additions and 83 deletions
|
@ -10,6 +10,7 @@
|
|||
* See AUTHORS file for a more comprehensive list of contributors.
|
||||
* Additional contributors of this file:
|
||||
* Copyright (C) 2011-2013 Adam Laurie
|
||||
* Copyright (C) 2018-2019 Danielle Bruneo
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
@ -70,7 +71,9 @@ static bool bForceKeyFile;
|
|||
static bool bTolerateFailures;
|
||||
static bool bFormatCard;
|
||||
static bool magic2 = false;
|
||||
static bool magic3 = false;
|
||||
static bool unlocked = false;
|
||||
static bool bForceSizeMismatch;
|
||||
static uint8_t uiBlocks;
|
||||
static uint8_t keys[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
|
@ -208,10 +211,18 @@ authenticate(uint32_t uiBlock)
|
|||
// Try to authenticate for the current sector
|
||||
if (nfc_initiator_mifare_cmd(pnd, mc, uiBlock, &mp))
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if (magic3) {
|
||||
//If it's a One Time Write card, we're gonna authenticate with the default keys
|
||||
memcpy(mp.mpa.abtKey, default_key, sizeof(default_key));
|
||||
|
||||
|
||||
// Try to authenticate for the current sector
|
||||
if (nfc_initiator_mifare_cmd(pnd, mc, uiBlock, &mp)) {
|
||||
return true;
|
||||
}
|
||||
// If formatting or not using key file, try to guess the right key
|
||||
if (bFormatCard || !bUseKeyFile) {
|
||||
} else if (bFormatCard || !bUseKeyFile) {
|
||||
for (size_t key_index = 0; key_index < num_keys; key_index++) {
|
||||
memcpy(mp.mpa.abtKey, keys + (key_index * 6), 6);
|
||||
if (nfc_initiator_mifare_cmd(pnd, mc, uiBlock, &mp)) {
|
||||
|
@ -351,12 +362,19 @@ read_card(int read_unlocked)
|
|||
if (nfc_initiator_mifare_cmd(pnd, MC_READ, iBlock, &mp)) {
|
||||
if (read_unlocked) {
|
||||
memcpy(mtDump.amb[iBlock].mbd.abtData, mp.mpd.abtData, sizeof(mtDump.amb[iBlock].mbd.abtData));
|
||||
} else {
|
||||
//If we're using a One Time Write ('Magic 3') Badge - we'll use default keys + ACL
|
||||
if (magic3) {
|
||||
memcpy(mtDump.amb[iBlock].mbt.abtKeyA, default_key, sizeof(default_key));
|
||||
memcpy(mtDump.amb[iBlock].mbt.abtAccessBits, mp.mpt.abtAccessBits, sizeof(mtDump.amb[iBlock].mbt.abtAccessBits));
|
||||
memcpy(mtDump.amb[iBlock].mbt.abtKeyB, default_key, sizeof(default_key));
|
||||
} else {
|
||||
// Copy the keys over from our key dump and store the retrieved access bits
|
||||
memcpy(mtDump.amb[iBlock].mbt.abtKeyA, mtKeys.amb[iBlock].mbt.abtKeyA, sizeof(mtDump.amb[iBlock].mbt.abtKeyA));
|
||||
memcpy(mtDump.amb[iBlock].mbt.abtAccessBits, mp.mpt.abtAccessBits, sizeof(mtDump.amb[iBlock].mbt.abtAccessBits));
|
||||
memcpy(mtDump.amb[iBlock].mbt.abtKeyB, mtKeys.amb[iBlock].mbt.abtKeyB, sizeof(mtDump.amb[iBlock].mbt.abtKeyB));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("!\nfailed to read trailer block 0x%02x\n", iBlock);
|
||||
bFailure = true;
|
||||
|
@ -392,6 +410,7 @@ write_card(int write_block_zero)
|
|||
bool bFailure = false;
|
||||
uint32_t uiWriteBlocks = 0;
|
||||
|
||||
//Determine if we have to unlock the card
|
||||
if (write_block_zero) {
|
||||
//If the user is attempting an unlocked write, but has a direct-write type magic card, they don't
|
||||
//need to use the W mode. We'll trigger a warning and let them proceed.
|
||||
|
@ -407,8 +426,8 @@ write_card(int write_block_zero)
|
|||
}
|
||||
|
||||
printf("Writing %d blocks |", uiBlocks + 1);
|
||||
// Write the card from begin to end;
|
||||
for (uiBlock = 0; uiBlock <= uiBlocks; uiBlock++) {
|
||||
// Completely write the card, end to start, but skipping block 0
|
||||
for (uiBlock = 4; uiBlock <= uiBlocks; uiBlock++) {
|
||||
// Authenticate everytime we reach the first sector of a new block
|
||||
if (is_first_block(uiBlock)) {
|
||||
if (bFailure) {
|
||||
|
@ -423,7 +442,11 @@ write_card(int write_block_zero)
|
|||
fflush(stdout);
|
||||
|
||||
// Try to authenticate for the current sector
|
||||
if (!write_block_zero && !authenticate(uiBlock) && !bTolerateFailures) {
|
||||
// If we are are writing to a chinese magic card, we've already unlocked
|
||||
// If we're writing to a One Time Write card, we need to authenticate
|
||||
// If we're writing something else, we'll need to authenticate
|
||||
if ((write_block_zero && magic3) || !write_block_zero) {
|
||||
if (!authenticate(uiBlock) && !bTolerateFailures) {
|
||||
printf("!\nError: authentication failed for block %02x\n", uiBlock);
|
||||
return false;
|
||||
}
|
||||
|
@ -452,6 +475,74 @@ write_card(int write_block_zero)
|
|||
if (uiBlock == 0 && !write_block_zero && !magic2)
|
||||
continue;
|
||||
|
||||
// Make sure a earlier write did not fail
|
||||
if (!bFailure) {
|
||||
// Try to write the data block
|
||||
if (bFormatCard && uiBlock)
|
||||
|
||||
memset(mp.mpd.abtData, 0x00, sizeof(mp.mpd.abtData));
|
||||
else
|
||||
memcpy(mp.mpd.abtData, mtDump.amb[uiBlock].mbd.abtData, sizeof(mp.mpd.abtData));
|
||||
// do not write a block 0 with incorrect BCC - card will be made invalid!
|
||||
if (uiBlock == 0) {
|
||||
if ((mp.mpd.abtData[0] ^ mp.mpd.abtData[1] ^ mp.mpd.abtData[2] ^ mp.mpd.abtData[3] ^ mp.mpd.abtData[4]) != 0x00 && !magic2) {
|
||||
printf("!\nError: incorrect BCC in MFD file!\n");
|
||||
printf("Expecting BCC=%02X\n", mp.mpd.abtData[0] ^ mp.mpd.abtData[1] ^ mp.mpd.abtData[2] ^ mp.mpd.abtData[3]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, uiBlock, &mp)) {
|
||||
bFailure = true;
|
||||
printf("Failure to write to data block %i\n", uiBlock);
|
||||
}
|
||||
|
||||
} else {
|
||||
printf("Failure during write process.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Show if the write went well for each block
|
||||
print_success_or_failure(bFailure, &uiWriteBlocks);
|
||||
if ((! bTolerateFailures) && bFailure)
|
||||
return false;
|
||||
}
|
||||
|
||||
//Write Block 0 if necessary
|
||||
if (write_block_zero || magic2 || magic3) {
|
||||
for (uiBlock = 0; uiBlock < 4; uiBlock++) {
|
||||
|
||||
// The first block 0x00 is read only, skip this
|
||||
if (uiBlock == 0) {
|
||||
//If the card is not magic, we're gonna skip over
|
||||
if (write_block_zero || magic2 || magic3) {
|
||||
//NOP
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_first_block(uiBlock)) {
|
||||
if (bFailure) {
|
||||
// When a failure occured we need to redo the anti-collision
|
||||
if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
|
||||
printf("!\nError: tag was removed\n");
|
||||
return false;
|
||||
}
|
||||
bFailure = false;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
// Try to authenticate for the current sector
|
||||
// If we are are writing to a chinese magic card, we've already unlocked
|
||||
// If we're writing to a One Time Write, we need to authenticate
|
||||
// If we're writing something else, we'll need to authenticate
|
||||
if ((write_block_zero && magic3) || !write_block_zero) {
|
||||
if (!authenticate(uiBlock) && !bTolerateFailures) {
|
||||
printf("!\nError: authentication failed for block %02x\n", uiBlock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure a earlier write did not fail
|
||||
if (!bFailure) {
|
||||
|
@ -468,15 +559,24 @@ write_card(int write_block_zero)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, uiBlock, &mp))
|
||||
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, uiBlock, &mp)) {
|
||||
bFailure = true;
|
||||
printf("Failure to write to data block %i\n", uiBlock);
|
||||
}
|
||||
|
||||
} else {
|
||||
printf("Failure during write process.\n");
|
||||
}
|
||||
|
||||
// Show if the write went well for each block
|
||||
print_success_or_failure(bFailure, &uiWriteBlocks);
|
||||
if ((!bTolerateFailures) && bFailure)
|
||||
if ((! bTolerateFailures) && bFailure)
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printf("|\n");
|
||||
printf("Done, %d of %d blocks written.\n", uiWriteBlocks, uiBlocks + 1);
|
||||
fflush(stdout);
|
||||
|
@ -505,6 +605,7 @@ print_usage(const char *pcProgramName)
|
|||
printf(" <dump.mfd> - MiFare Dump (MFD) used to write (card to MFD) or (MFD to card)\n");
|
||||
printf(" <keys.mfd> - MiFare Dump (MFD) that contain the keys (optional)\n");
|
||||
printf(" f - Force using the keyfile even if UID does not match (optional)\n");
|
||||
|
||||
printf("Examples: \n\n");
|
||||
printf(" Read card to file, using key A:\n\n");
|
||||
printf(" %s r a u mycard.mfd\n\n", pcProgramName);
|
||||
|
@ -519,6 +620,54 @@ print_usage(const char *pcProgramName)
|
|||
printf(" %s r a U01ab23cd mycard.mfd\n\n", pcProgramName);
|
||||
}
|
||||
|
||||
|
||||
bool is_directwrite(){
|
||||
printf("Checking if Badge is DirectWrite...\n");
|
||||
|
||||
// Set default keys
|
||||
memcpy(mtDump.amb[0].mbt.abtKeyA, default_key, sizeof(default_key));
|
||||
memcpy(mtDump.amb[0].mbt.abtAccessBits, default_acl, sizeof(mp.mpt.abtAccessBits));
|
||||
memcpy(mtDump.amb[0].mbt.abtKeyB, default_key, sizeof(default_key));
|
||||
|
||||
// Temporarly override bUseKeyFile
|
||||
bool orig_bUseKeyFile=bUseKeyFile;
|
||||
bUseKeyFile=false;
|
||||
// Try to authenticate for the current sector
|
||||
if (!authenticate(0)) {
|
||||
printf("!\nError: authentication failed for block 0x%02x\n", 0);
|
||||
bUseKeyFile=orig_bUseKeyFile;
|
||||
return false;
|
||||
}
|
||||
// restore bUseKeyFile
|
||||
bUseKeyFile=orig_bUseKeyFile;
|
||||
|
||||
// Try to read block 0
|
||||
uint8_t original_b0[16];
|
||||
if (nfc_initiator_mifare_cmd(pnd, MC_READ, 0, &mp)) {
|
||||
memcpy(original_b0, mp.mpd.abtData, sizeof(mp.mpd.abtData));
|
||||
printf(" Original Block 0: ");
|
||||
for(int i=0;i<16;i++){
|
||||
printf("%02x", original_b0[i]);
|
||||
}
|
||||
printf("\n");
|
||||
printf(" Original UID: %02x%02x%02x%02x\n",
|
||||
original_b0[0], original_b0[1], original_b0[2], original_b0[3]);
|
||||
} else {
|
||||
printf("!\nError: unable to read block 0x%02x\n", 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf(" Attempt to write Block 0 ...\n");
|
||||
memcpy(mp.mpd.abtData, original_b0, sizeof(original_b0));
|
||||
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, 0, &mp)) {
|
||||
printf("Failure to write to data block %i\n", 0);
|
||||
return false;
|
||||
}
|
||||
printf(" Block 0 written successfully\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char *argv[])
|
||||
{
|
||||
|
@ -697,6 +846,26 @@ main(int argc, const char *argv[])
|
|||
}
|
||||
printf("Guessing size: seems to be a %lu-byte card\n", (uiBlocks + 1) * sizeof(mifare_classic_block));
|
||||
|
||||
//If size is 4k check for direct-write card
|
||||
if (uiBlocks == 0xff) {
|
||||
if (is_directwrite()){
|
||||
printf("Card is DirectWrite\n");
|
||||
magic3=true;
|
||||
unlock=0;
|
||||
} else {
|
||||
printf("Card is not DirectWrite\n");
|
||||
}
|
||||
}
|
||||
|
||||
//Check to see if we have a One Time Write badge (magic3)
|
||||
if (pbtUID[0] == 0xaa && pbtUID[1] == 0x55 &&
|
||||
pbtUID[2] == 0xc3 && pbtUID[3] == 0x96) {
|
||||
printf("Card appears to be a One Time Write Card..\n");
|
||||
magic3 = true;
|
||||
unlock = 0;
|
||||
}
|
||||
|
||||
|
||||
if (bUseKeyFile) {
|
||||
FILE *pfKeys = fopen(argv[5], "rb");
|
||||
if (pfKeys == NULL) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* See AUTHORS file for a more comprehensive list of contributors.
|
||||
* Additional contributors of this file:
|
||||
* Copyright (C) 2013-2018 Adam Laurie
|
||||
* Copyright (C) 2018-2019 Daniele Bruneo
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
|
@ -290,50 +291,48 @@ unlock_card(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool check_magic()
|
||||
{
|
||||
bool bFailure = false;
|
||||
int uid_data;
|
||||
static bool check_magic() {
|
||||
// Firstly try to directly read and re-write the first three pages
|
||||
// if this fail try to unlock with chinese magic backdoor
|
||||
|
||||
for (uint32_t page = 0; page <= 1; page++) {
|
||||
// Show if the readout went well
|
||||
if (bFailure) {
|
||||
// When a failure occured we need to redo the anti-collision
|
||||
if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0) {
|
||||
ERR("tag was removed");
|
||||
return false;
|
||||
}
|
||||
bFailure = false;
|
||||
}
|
||||
|
||||
uid_data = 0x00000000;
|
||||
|
||||
memcpy(mp.mpd.abtData, &uid_data, sizeof uid_data);
|
||||
memset(mp.mpd.abtData + 4, 0, 12);
|
||||
|
||||
//Force the write without checking for errors - otherwise the writes to the sector 0 seem to complain
|
||||
nfc_initiator_mifare_cmd(pnd, MC_WRITE, page, &mp);
|
||||
}
|
||||
|
||||
//Check that the ID is now set to 0x000000000000
|
||||
bool directWrite = true;
|
||||
// Try to read pages 0, 1, 2
|
||||
uint8_t original_b0[12];
|
||||
printf("Checking if UL badge is DirectWrite...\n");
|
||||
if (nfc_initiator_mifare_cmd(pnd, MC_READ, 0, &mp)) {
|
||||
//printf("%u", mp.mpd.abtData);
|
||||
bool result = true;
|
||||
for (int i = 0; i <= 7; i++) {
|
||||
if (mp.mpd.abtData[i] != 0x00) result = false;
|
||||
memcpy(original_b0, mp.mpd.abtData, 12);
|
||||
printf(" Original Block 0 (Pages 0-2): ");
|
||||
for(int i=0;i<12;i++){
|
||||
printf("%02x", original_b0[i]);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return true;
|
||||
printf("\n");
|
||||
printf(" Original UID: %02x%02x%02x%02x%02x%02x%02x\n",
|
||||
original_b0[0], original_b0[1], original_b0[2], original_b0[4], original_b0[5], original_b0[6], original_b0[7]);
|
||||
} else {
|
||||
printf("!\nError: unable to read block 0x%02x\n", 0);
|
||||
directWrite = false;
|
||||
}
|
||||
|
||||
printf(" Attempt to write Block 0 (pages 0-2) ...\n");
|
||||
for (uint32_t page = 0; page <= 2; page++) {
|
||||
printf(" Writing Page %i:", page);
|
||||
memcpy(mp.mpd.abtData, original_b0 + page*4, 4);
|
||||
for(int i=0;i<4;i++){
|
||||
printf(" %02x", mp.mpd.abtData[i]);
|
||||
}
|
||||
|
||||
//Initially check if we can unlock via the MF method
|
||||
if (unlock_card()) {
|
||||
printf("\n");
|
||||
if (!nfc_initiator_mifare_cmd(pnd, MC_WRITE, page, &mp)) {
|
||||
printf(" Failure writing Page %i\n", page);
|
||||
directWrite = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(directWrite){
|
||||
printf(" Block 0 written successfully\n");
|
||||
printf("Card is DirectWrite\n");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
printf("Card is not DirectWrite\n");
|
||||
return unlock_card();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -383,9 +382,9 @@ write_card(bool write_otp, bool write_lock, bool write_dyn_lock, bool write_uid)
|
|||
write_uid = ((buffer[0] == 'y') || (buffer[0] == 'Y'));
|
||||
}
|
||||
|
||||
printf("Writing %d pages |", uiBlocks);
|
||||
/* We may need to skip 2 first pages. */
|
||||
if (!write_uid) {
|
||||
printf("Writing %d pages |", uiBlocks);
|
||||
printf("ss");
|
||||
uiSkippedPages = 2;
|
||||
} else {
|
||||
|
@ -393,6 +392,7 @@ write_card(bool write_otp, bool write_lock, bool write_dyn_lock, bool write_uid)
|
|||
printf("\nUnable to unlock card - are you sure the card is magic?\n");
|
||||
return false;
|
||||
}
|
||||
printf("Writing %d pages |", uiBlocks);
|
||||
}
|
||||
|
||||
for (uint32_t page = uiSkippedPages; page < uiBlocks; page++) {
|
||||
|
@ -527,7 +527,7 @@ main(int argc, const char *argv[])
|
|||
bool bFilename = false;
|
||||
FILE *pfDump;
|
||||
|
||||
if (argc < 3) {
|
||||
if (argc == 0) {
|
||||
print_usage(argv);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
@ -580,7 +580,7 @@ main(int argc, const char *argv[])
|
|||
}
|
||||
}
|
||||
}
|
||||
if (! bFilename) {
|
||||
if (iAction != 3 && !bFilename) {
|
||||
ERR("Please supply a Mifare Dump filename");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
@ -650,7 +650,7 @@ main(int argc, const char *argv[])
|
|||
if (get_ev1_version()) {
|
||||
if (!bPWD)
|
||||
printf("WARNING: Tag is EV1 or NTAG - PASSWORD may be required\n");
|
||||
if (abtRx[6] == 0x0b) {
|
||||
if (abtRx[6] == 0x0b || abtRx[6] == 0x00) {
|
||||
printf("EV1 type: MF0UL11 (48 bytes)\n");
|
||||
uiBlocks = 20; // total number of 4 byte 'pages'
|
||||
iDumpSize = uiBlocks * 4;
|
||||
|
|
Loading…
Reference in a new issue