mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-12-28 19:31:19 +08:00
ADD: 'hf mf fastchk' - new command, improved check keys functionality. It uses a bunch of techniques to get a speedup.
Using a dictionary file with 421keys, Current implementation of checkkeys takes 300 sec. This implementation of checkkeys takes 250 sec. I implemented it as a separate command so it will be easier to compare between the old and new checkkeys. Its also doing much on deviceside, which is a step to much funnier standalone modes :))
This commit is contained in:
parent
b4a03581c2
commit
2ca0ea8cb4
6 changed files with 693 additions and 12 deletions
|
@ -26,9 +26,6 @@
|
|||
#include "LCD.h"
|
||||
#endif
|
||||
|
||||
// Global var to determine if device is in standalone mode or not.
|
||||
static int InStandAloneMode = 0;
|
||||
|
||||
//=============================================================================
|
||||
// A buffer where we can queue things up to be sent through the FPGA, for
|
||||
// any purpose (fake tag, as reader, whatever). We go MSB first, since that
|
||||
|
@ -364,7 +361,6 @@ void SendStatus(void) {
|
|||
// Show some leds in a pattern to identify StandAlone mod is running
|
||||
void StandAloneMode(void) {
|
||||
|
||||
InStandAloneMode = 1;
|
||||
DbpString("Stand-alone mode! No PC necessary.");
|
||||
// Oooh pretty -- notify user we're in elite samy mode now
|
||||
LED(LED_RED, 200);
|
||||
|
@ -403,7 +399,9 @@ void printStandAloneModes(void) {
|
|||
#endif
|
||||
|
||||
DbpString("Running ");
|
||||
Dbprintf(" Are we running standalone | %s", (InStandAloneMode)? "Yes" : "No");
|
||||
//Dbprintf(" Is Device attached to USB| %s", USB_ATTACHED() ? "Yes" : "No");
|
||||
//Dbprintf(" Is USB_reconnect value | %d", GetUSBreconnect() );
|
||||
//Dbprintf(" Is USB_configured value | %d", GetUSBconfigured() );
|
||||
|
||||
//.. add your own standalone detection based on with compiler directive you are used.
|
||||
// don't "reuse" the already taken ones, this will make things easier when trying to detect the different modes
|
||||
|
@ -818,9 +816,14 @@ void UsbPacketReceived(uint8_t *packet, int len) {
|
|||
case CMD_MIFARE_NESTED:
|
||||
MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
|
||||
break;
|
||||
case CMD_MIFARE_CHKKEYS:
|
||||
case CMD_MIFARE_CHKKEYS: {
|
||||
MifareChkKeys(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_MIFARE_CHKKEYS_FAST: {
|
||||
MifareChkKeys_fast(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_SIMULATE_MIFARE_CARD:
|
||||
Mifare1ksim(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
|
||||
break;
|
||||
|
@ -1127,8 +1130,6 @@ void __attribute__((noreturn)) AppMain(void) {
|
|||
common_area.flags.osimage_present = 1;
|
||||
|
||||
LEDsoff();
|
||||
|
||||
// list with standalone refs.
|
||||
|
||||
// Init USB device
|
||||
usb_enable();
|
||||
|
@ -1158,7 +1159,7 @@ void __attribute__((noreturn)) AppMain(void) {
|
|||
|
||||
byte_t rx[sizeof(UsbCommand)];
|
||||
size_t rx_len;
|
||||
|
||||
|
||||
for(;;) {
|
||||
if ( usb_poll_validate_length() ) {
|
||||
rx_len = usb_read(rx, sizeof(UsbCommand));
|
||||
|
@ -1184,7 +1185,9 @@ void __attribute__((noreturn)) AppMain(void) {
|
|||
RunMod();
|
||||
#endif
|
||||
// when here, we are no longer in standalone mode.
|
||||
InStandAloneMode = 0;
|
||||
// reseting the variables which keeps track of usb re-attached/configured
|
||||
//SetUSBreconnect(0);
|
||||
//SetUSBconfigured(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,6 +144,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
|
|||
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
|
||||
void MifareAcquireNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
|
||||
void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
|
||||
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
|
||||
void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
|
||||
void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
|
||||
void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
|
||||
|
|
|
@ -1957,7 +1957,61 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_card, uint32_
|
|||
//set default timeout based on ATS
|
||||
iso14a_set_ATS_timeout(resp);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) {
|
||||
uint8_t wupa[] = { ISO14443A_CMD_WUPA }; // 0x26 - ISO14443A_CMD_REQA 0x52 - ISO14443A_CMD_WUPA
|
||||
uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x20 };
|
||||
uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
|
||||
uint8_t resp[5] = {0}; // theoretically. A usual RATS will be much smaller
|
||||
uint8_t resp_par[1] = {0};
|
||||
uint8_t uid_resp[4] = {0};
|
||||
|
||||
uint8_t sak = 0x04; // cascade uid
|
||||
int cascade_level = 0;
|
||||
|
||||
// Broadcast for a card, WUPA (0x52) will force response from all cards in the field
|
||||
ReaderTransmitBitsPar(wupa, 7, NULL, NULL);
|
||||
|
||||
// Receive the ATQA
|
||||
if(!ReaderReceive(resp, resp_par)) return 0;
|
||||
|
||||
// OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in
|
||||
// which case we need to make a cascade 2 request and select - this is a long UID
|
||||
// While the UID is not complete, the 3nd bit (from the right) is set in the SAK.
|
||||
for(; sak & 0x04; cascade_level++) {
|
||||
// SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97)
|
||||
sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2;
|
||||
|
||||
if (cascade_level < num_cascades - 1) {
|
||||
uid_resp[0] = 0x88;
|
||||
memcpy(uid_resp+1, uid_ptr+cascade_level*3, 3);
|
||||
} else {
|
||||
memcpy(uid_resp, uid_ptr+cascade_level*3, 4);
|
||||
}
|
||||
|
||||
// Construct SELECT UID command
|
||||
//sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC)
|
||||
memcpy(sel_uid+2, uid_resp, 4); // the UID received during anticollision, or the provided UID
|
||||
sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC
|
||||
AppendCrc14443a(sel_uid, 7); // calculate and add CRC
|
||||
ReaderTransmit(sel_uid, sizeof(sel_uid), NULL);
|
||||
|
||||
// Receive the SAK
|
||||
if (!ReaderReceive(resp, resp_par)) return 0;
|
||||
|
||||
sak = resp[0];
|
||||
|
||||
// Test if more parts of the uid are coming
|
||||
if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) {
|
||||
// Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of:
|
||||
// http://www.nxp.com/documents/application_note/AN10927.pdf
|
||||
uid_resp[0] = uid_resp[1];
|
||||
uid_resp[1] = uid_resp[2];
|
||||
uid_resp[2] = uid_resp[3];
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par);
|
|||
extern void iso14443a_setup(uint8_t fpga_minor_mode);
|
||||
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data);
|
||||
extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats);
|
||||
extern int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades);
|
||||
extern void iso14a_set_trigger(bool enable);
|
||||
|
||||
int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen);
|
||||
|
|
|
@ -773,7 +773,8 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
|
|||
}
|
||||
have_uid = true;
|
||||
} else { // no need for anticollision. We can directly select the card
|
||||
if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) {
|
||||
if (!iso14443a_fast_select_card(uid, cascade_levels)) {
|
||||
//if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) {
|
||||
if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)");
|
||||
continue;
|
||||
}
|
||||
|
@ -1053,6 +1054,315 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat
|
|||
// MIFARE check keys. key count up to 85.
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
typedef struct sector_t {
|
||||
uint8_t keyA[6];
|
||||
uint8_t keyB[6];
|
||||
} sector_t;
|
||||
|
||||
typedef struct chk_t {
|
||||
uint64_t key;
|
||||
uint32_t cuid;
|
||||
uint8_t cl;
|
||||
uint8_t block;
|
||||
uint8_t keyType;
|
||||
uint8_t *uid;
|
||||
struct Crypto1State *pcs;
|
||||
} chk_t;
|
||||
|
||||
|
||||
// wait for the card to become ready again
|
||||
// assume: fast reset of card
|
||||
/*void chk_timeout(void){
|
||||
uint8_t dummy_answer = 0;
|
||||
ReaderTransmit(&dummy_answer, 1, NULL);
|
||||
uint32_t timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT;
|
||||
while(GetCountSspClk() < timeout);
|
||||
}
|
||||
*/
|
||||
|
||||
#ifndef CHK_TIMEOUT
|
||||
# define CHK_TIMEOUT() {\
|
||||
uint8_t dummy_answer = 0; \
|
||||
ReaderTransmit(&dummy_answer, 1, NULL); \
|
||||
uint32_t timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; \
|
||||
while(GetCountSspClk() < timeout); \
|
||||
}
|
||||
#endif
|
||||
|
||||
// checks one key.
|
||||
// fast select, tries 10times to select
|
||||
//
|
||||
// return:
|
||||
// 2 = failed to select.
|
||||
// 1 = wrong key
|
||||
// 0 = correct key
|
||||
uint8_t chkKey( struct chk_t *c ) {
|
||||
|
||||
uint8_t i = 0, res = 2;
|
||||
while( i<10 ) {
|
||||
// this part is from Piwi's faster nonce collecting part in Hardnested.
|
||||
// assume: fast select
|
||||
if(!iso14443a_fast_select_card(c->uid, c->cl)) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL);
|
||||
CHK_TIMEOUT();
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) {
|
||||
|
||||
if(!iso14443a_fast_select_card(c->uid, c->cl))
|
||||
return 2;
|
||||
|
||||
if ( mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL) )
|
||||
return 1;
|
||||
|
||||
uint8_t data[16] = {0x00};
|
||||
uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data);
|
||||
|
||||
CHK_TIMEOUT();
|
||||
|
||||
// successful read
|
||||
if ( !res ) {
|
||||
// data was something else than zeros.
|
||||
if ( memcmp(data+10, "\x00\x00\x00\x00\x00\x00", 6) != 0) {
|
||||
memcpy(keyb, data+10, 6);
|
||||
res = 0;
|
||||
} else {
|
||||
res = 3;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
|
||||
|
||||
// keep track of how many sectors on card.
|
||||
for (uint8_t s = 0; s < *sectorcnt; ++s) {
|
||||
|
||||
// skip already found A keys
|
||||
if( !found[(s*2)] ) {
|
||||
|
||||
c->block = FirstBlockOfSector( s );
|
||||
uint8_t status = chkKey( c );
|
||||
if ( status == 0 ) {
|
||||
num_to_bytes(c->key, 6, k_sector[s].keyA);
|
||||
found[(s*2)] = 1;
|
||||
++*foundkeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
|
||||
|
||||
// keep track of how many sectors on card.
|
||||
for (uint8_t s = 0; s < *sectorcnt; ++s) {
|
||||
|
||||
// skip already found B keys
|
||||
if( !found[(s*2)+1] ) {
|
||||
|
||||
c->block = FirstBlockOfSector( s );
|
||||
uint8_t status = chkKey( c );
|
||||
if ( status == 0 ) {
|
||||
num_to_bytes(c->key, 6, k_sector[s].keyB);
|
||||
found[(s*2)+1] = 1;
|
||||
++*foundkeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop all A keys,
|
||||
// when A is found but not B, try to read B.
|
||||
void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
|
||||
|
||||
// read Block B, if A is found.
|
||||
for (uint8_t s = 0; s < *sectorcnt; ++s) {
|
||||
c->block = (FirstBlockOfSector( s ) + NumBlocksPerSector( s ) - 1);
|
||||
// A but not B
|
||||
if ( found[(s*2)] && !found[(s*2)+1] ){
|
||||
c->key = bytes_to_num(k_sector[s].keyA, 6);
|
||||
uint8_t status = chkKey_readb(c, k_sector[s].keyB);
|
||||
if ( status == 0 ){
|
||||
found[(s*2)+1] = 1;
|
||||
++*foundkeys;
|
||||
// try quick find all B?
|
||||
// assume: keys comes in groups. Find one B, test against all B.
|
||||
c->key = bytes_to_num( k_sector[s].keyB, 6);
|
||||
c->block = 1;
|
||||
chkKey_scanB(c, k_sector, found, sectorcnt, foundkeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// get Chunks of keys, to test authentication against card.
|
||||
// arg0 = antal sectorer
|
||||
// arg0 = first time
|
||||
// arg1 = clear trace
|
||||
// arg2 = antal nycklar i keychunk
|
||||
// datain = keys as array
|
||||
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) {
|
||||
|
||||
// save old debuglevel, and tempory turn off dbg printing. speedissues.
|
||||
int OLD_MF_DBGLEVEL = MF_DBGLEVEL;
|
||||
MF_DBGLEVEL = MF_DBG_NONE;
|
||||
|
||||
// first call or
|
||||
uint8_t sectorcnt = arg0 & 0xFF; // 16;
|
||||
uint8_t firstchunk = (arg0 >> 8) & 0xF;
|
||||
uint8_t lastchunk = (arg0 >> 12) & 0xF;
|
||||
uint8_t keyCount = arg2 & 0xFF;
|
||||
uint8_t status = 0;
|
||||
|
||||
struct Crypto1State mpcs = {0, 0};
|
||||
struct Crypto1State *pcs;
|
||||
pcs = &mpcs;
|
||||
|
||||
struct chk_t chk_data;
|
||||
|
||||
uint8_t allkeys = sectorcnt << 1;
|
||||
|
||||
static uint32_t cuid = 0;
|
||||
static uint8_t cascade_levels = 0;
|
||||
static uint8_t foundkeys = 0;
|
||||
static sector_t k_sector[80];
|
||||
static uint8_t found[80];
|
||||
static uint8_t *uid;
|
||||
|
||||
if (uid == NULL || firstchunk) {
|
||||
uid = BigBuf_malloc(10);
|
||||
if (uid == NULL ) {
|
||||
Dbprintf("ChkKeys: uid malloc failed");
|
||||
goto OUT;
|
||||
}
|
||||
}
|
||||
|
||||
LEDsoff();
|
||||
LED_A_ON();
|
||||
|
||||
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
|
||||
|
||||
if ( firstchunk ) {
|
||||
|
||||
clear_trace();
|
||||
set_tracing(false);
|
||||
|
||||
memset(k_sector, 0x00, 480+10);
|
||||
memset(found, 0x00, 80);
|
||||
foundkeys = 0;
|
||||
|
||||
iso14a_card_select_t card_info;
|
||||
if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) {
|
||||
Dbprintf("ChkKeys: Can't select card (ALL)");
|
||||
goto OUT;
|
||||
}
|
||||
switch (card_info.uidlen) {
|
||||
case 4 : cascade_levels = 1; break;
|
||||
case 7 : cascade_levels = 2; break;
|
||||
case 10: cascade_levels = 3; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
CHK_TIMEOUT();
|
||||
}
|
||||
|
||||
// set check struct.
|
||||
chk_data.uid = uid;
|
||||
chk_data.cuid = cuid;
|
||||
chk_data.cl = cascade_levels;
|
||||
chk_data.pcs = pcs;
|
||||
|
||||
// Keychunk loop
|
||||
for (uint8_t i = 0; i < keyCount; ++i) {
|
||||
|
||||
// Allow button press / usb cmd to interrupt device
|
||||
if (BUTTON_PRESS() && !usb_poll_validate_length()) break;
|
||||
|
||||
WDT_HIT();
|
||||
|
||||
// new key
|
||||
chk_data.key = bytes_to_num(datain + i * 6, 6);
|
||||
|
||||
// Sector main loop
|
||||
// keep track of how many sectors on card.
|
||||
for (uint8_t s = 0; s < sectorcnt; ++s) {
|
||||
|
||||
// assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector
|
||||
chk_data.block = FirstBlockOfSector( s );
|
||||
|
||||
// skip already found A keys
|
||||
if( !found[(s*2)] ) {
|
||||
chk_data.keyType = 0;
|
||||
status = chkKey( &chk_data);
|
||||
if ( status == 0 ) {
|
||||
memcpy(k_sector[s].keyA, datain + i * 6, 6);
|
||||
found[(s*2)] = 1;
|
||||
++foundkeys;
|
||||
|
||||
chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
||||
}
|
||||
}
|
||||
|
||||
// skip already found B keys
|
||||
if( !found[(s*2)+1] ) {
|
||||
chk_data.keyType = 1;
|
||||
status = chkKey( &chk_data);
|
||||
if ( status == 0 ) {
|
||||
memcpy(k_sector[s].keyB, datain + i * 6, 6);
|
||||
found[(s*2)+1] = 1;
|
||||
++foundkeys;
|
||||
|
||||
chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys);
|
||||
}
|
||||
}
|
||||
} // end loop sectors
|
||||
|
||||
// read Block B, if A is found.
|
||||
chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys);
|
||||
|
||||
// is all keys found?
|
||||
if ( foundkeys == allkeys )
|
||||
break;
|
||||
} // end loop keys
|
||||
|
||||
OUT:
|
||||
// restore debug level
|
||||
MF_DBGLEVEL = OLD_MF_DBGLEVEL;
|
||||
LEDsoff();
|
||||
|
||||
// All keys found, send to client, or last keychunk from client
|
||||
if (foundkeys==allkeys || lastchunk ) {
|
||||
|
||||
uint64_t foo = 0;
|
||||
uint16_t bar = 0;
|
||||
for (uint8_t m = 0; m < 64; ++m)
|
||||
foo |= (found[m] << m);
|
||||
for (uint8_t m=64; m < sizeof(found); ++m)
|
||||
bar |= (found[m] << (m-64));
|
||||
|
||||
uint8_t *tmp = BigBuf_malloc(480+10);
|
||||
memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t) );
|
||||
num_to_bytes(foo, 8, tmp+480);
|
||||
tmp[488] = bar & 0xFF;
|
||||
tmp[489] = bar >> 8 & 0xFF;
|
||||
cmd_send(CMD_ACK, foundkeys, 0, 0, tmp, 480+10);
|
||||
|
||||
set_tracing(false);
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
crypto1_destroy(pcs);
|
||||
} else {
|
||||
// partial/none keys found
|
||||
cmd_send(CMD_ACK, foundkeys, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) {
|
||||
|
||||
uint8_t blockNo = arg0 & 0xFF;
|
||||
|
|
314
client/cmdhfmf.c
314
client/cmdhfmf.c
|
@ -128,6 +128,24 @@ int usage_hf14_chk(void){
|
|||
PrintAndLog(" hf mf chk *1 ? d -- target all blocks, all keys, 1K, write to file");
|
||||
return 0;
|
||||
}
|
||||
int usage_hf14_chk_fast(void){
|
||||
PrintAndLog("Usage: hf mf fastchk <card memory> [t|d] [<key (12 hex symbols)>] [<dic (*.dic)>]");
|
||||
PrintAndLog("options:");
|
||||
PrintAndLog(" h this help");
|
||||
PrintAndLog(" <cardmem> all sectors based on card memory, other values then below defaults to 1k");
|
||||
PrintAndLog(" 0 - MINI(320 bytes)");
|
||||
PrintAndLog(" 1 - 1K");
|
||||
PrintAndLog(" 2 - 2K");
|
||||
PrintAndLog(" 4 - 4K");
|
||||
PrintAndLog(" d write keys to binary file");
|
||||
PrintAndLog(" t write keys to emulator memory\n");
|
||||
PrintAndLog(" ");
|
||||
PrintAndLog("samples:");
|
||||
PrintAndLog(" hf mf chk 1 1234567890ab keys.dic -- target 1K using key 1234567890ab, using dictionary file");
|
||||
PrintAndLog(" hf mf chk 1 t -- target 1K, write to emulator mem");
|
||||
PrintAndLog(" hf mf chk 1 d -- target 1K, write to file");
|
||||
return 0;
|
||||
}
|
||||
int usage_hf14_keybrute(void){
|
||||
PrintAndLog("J_Run's 2nd phase of multiple sector nested authentication key recovery");
|
||||
PrintAndLog("You have a known 4 last bytes of a key recovered with mf_nonce_brute tool.");
|
||||
|
@ -1147,6 +1165,272 @@ int CmdHF14AMfNestedHard(const char *Cmd) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int randInRange(int min, int max)
|
||||
{
|
||||
return min + (int) (rand() / (double) (RAND_MAX + 1) * (max - min + 1));
|
||||
}
|
||||
|
||||
//Fisher–Yates shuffle
|
||||
void shuffle( uint8_t *array, uint16_t len) {
|
||||
uint8_t tmp[6];
|
||||
uint16_t x;
|
||||
time_t t;
|
||||
srand((unsigned) time(&t));
|
||||
while (len) {
|
||||
//x = randInRange(0, len) * (len-- *6) | 0; // 0 = i < n
|
||||
x = randInRange(0, (len -= 6) ) | 0; // 0 = i < n
|
||||
x %= 6;
|
||||
memcpy(tmp, array + x, 6);
|
||||
memcpy(array + x, array + len, 6);
|
||||
memcpy(array + len, tmp, 6);
|
||||
}
|
||||
}
|
||||
|
||||
int CmdHF14AMfChk_fast(const char *Cmd) {
|
||||
|
||||
if (strlen(Cmd)<2) return usage_hf14_chk_fast();
|
||||
|
||||
FILE * f;
|
||||
char filename[FILE_PATH_SIZE]={0};
|
||||
char buf[13];
|
||||
uint8_t *keyBlock = NULL, *p;
|
||||
uint16_t stKeyBlock = 20;
|
||||
int i, keycnt = 0;
|
||||
int transferToEml = 0, createDumpFile = 0;
|
||||
char ctmp = 0x00;
|
||||
uint8_t SectorsCnt = 1;
|
||||
|
||||
uint64_t foo = 0, bar = 0;
|
||||
icesector_t *e_sector = NULL;
|
||||
|
||||
keyBlock = calloc(stKeyBlock, 6);
|
||||
if (keyBlock == NULL) return 1;
|
||||
|
||||
uint64_t defaultKeys[] = {
|
||||
0xffffffffffff, // Default key (first key used by program if no user defined key)
|
||||
0x000000000000, // Blank key
|
||||
0xa0a1a2a3a4a5, // NFCForum MAD key
|
||||
0xb0b1b2b3b4b5,
|
||||
0xaabbccddeeff,
|
||||
0x4d3a99c351dd,
|
||||
0x1a982c7e459a,
|
||||
0xd3f7d3f7d3f7,
|
||||
0x714c5c886e97,
|
||||
0x587ee5f9350f,
|
||||
0xa0478cc39091,
|
||||
0x533cb6c723f6,
|
||||
0x8fd0a4f256e9
|
||||
};
|
||||
int defaultKeysSize = sizeof(defaultKeys) / sizeof(uint64_t);
|
||||
|
||||
for (int defaultKeyCounter = 0; defaultKeyCounter < defaultKeysSize; defaultKeyCounter++)
|
||||
num_to_bytes(defaultKeys[defaultKeyCounter], 6, (uint8_t*)(keyBlock + defaultKeyCounter * 6));
|
||||
|
||||
// sectors
|
||||
switch(param_getchar(Cmd, 0)) {
|
||||
case '0': SectorsCnt = 5; break;
|
||||
case '1': SectorsCnt = 16; break;
|
||||
case '2': SectorsCnt = 32; break;
|
||||
case '4': SectorsCnt = 40; break;
|
||||
default: SectorsCnt = 16;
|
||||
}
|
||||
|
||||
ctmp = param_getchar(Cmd, 1);
|
||||
if (ctmp == 't' || ctmp == 'T') transferToEml = 1;
|
||||
else if (ctmp == 'd' || ctmp == 'D') createDumpFile = 1;
|
||||
|
||||
for (i = transferToEml || createDumpFile; param_getchar(Cmd, 1 + i); i++) {
|
||||
if (!param_gethex(Cmd, 1 + i, keyBlock + 6 * keycnt, 12)) {
|
||||
if ( stKeyBlock - keycnt < 2) {
|
||||
p = realloc(keyBlock, 6*(stKeyBlock+=10));
|
||||
if (!p) {
|
||||
PrintAndLog("Cannot allocate memory for Keys");
|
||||
free(keyBlock);
|
||||
return 2;
|
||||
}
|
||||
keyBlock = p;
|
||||
}
|
||||
PrintAndLog("key[%2d] %02x%02x%02x%02x%02x%02x", keycnt,
|
||||
(keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2],
|
||||
(keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6);
|
||||
keycnt++;
|
||||
} else {
|
||||
// May be a dic file
|
||||
if ( param_getstr(Cmd, 1 + i,filename) >= FILE_PATH_SIZE ) {
|
||||
PrintAndLog("File name too long");
|
||||
free(keyBlock);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if ( (f = fopen( filename , "r")) ) {
|
||||
while( fgets(buf, sizeof(buf), f) ){
|
||||
if (strlen(buf) < 12 || buf[11] == '\n')
|
||||
continue;
|
||||
|
||||
while (fgetc(f) != '\n' && !feof(f)) ; //goto next line
|
||||
|
||||
if( buf[0]=='#' ) continue; //The line start with # is comment, skip
|
||||
|
||||
if (!isxdigit(buf[0])){
|
||||
PrintAndLog("File content error. '%s' must include 12 HEX symbols",buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
buf[12] = 0;
|
||||
if ( stKeyBlock - keycnt < 2) {
|
||||
p = realloc(keyBlock, 6*(stKeyBlock += 64));
|
||||
if (!p) {
|
||||
PrintAndLog("Cannot allocate memory for defKeys");
|
||||
free(keyBlock);
|
||||
fclose(f);
|
||||
return 2;
|
||||
}
|
||||
keyBlock = p;
|
||||
}
|
||||
int pos = 6 * keycnt;
|
||||
memset(keyBlock + pos, 0, 6);
|
||||
num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + pos);
|
||||
//PrintAndLog("check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + pos, 6) );
|
||||
keycnt++;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
}
|
||||
fclose(f);
|
||||
PrintAndLog("Loaded %2d keys from %s", keycnt, filename);
|
||||
} else {
|
||||
PrintAndLog("File: %s: not found or locked.", filename);
|
||||
free(keyBlock);
|
||||
return 1;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keycnt == 0) {
|
||||
PrintAndLog("No key specified, trying default keys");
|
||||
for (;keycnt < defaultKeysSize; keycnt++)
|
||||
PrintAndLog("key[%2d] %02x%02x%02x%02x%02x%02x", keycnt,
|
||||
(keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2],
|
||||
(keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6);
|
||||
}
|
||||
|
||||
// initialize storage for found keys
|
||||
e_sector = calloc(SectorsCnt, sizeof(icesector_t));
|
||||
if (e_sector == NULL) {
|
||||
free(keyBlock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// empty e_sector
|
||||
for(int i = 0; i < SectorsCnt; ++i){
|
||||
memset(e_sector[i].keyA, 0xFF, 6);
|
||||
memset(e_sector[i].keyB, 0xFF, 6);
|
||||
}
|
||||
|
||||
uint32_t chunksize = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt;
|
||||
bool firstChunk = true, lastChunk = false;
|
||||
uint32_t timeout = 0;
|
||||
|
||||
// time
|
||||
uint64_t t1 = msclock();
|
||||
|
||||
// main keychunk loop
|
||||
for (uint32_t i = 0; i < keycnt; i += chunksize) {
|
||||
|
||||
uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i;
|
||||
|
||||
// last chunk?
|
||||
if ( size == keycnt - i)
|
||||
lastChunk = true;
|
||||
|
||||
// send keychunk
|
||||
UsbCommand c = {CMD_MIFARE_CHKKEYS_FAST, { (SectorsCnt | (firstChunk << 8) | (lastChunk << 12) ), 0, size}};
|
||||
|
||||
memcpy(c.d.asBytes, keyBlock + i * 6, 6 * size);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
|
||||
if ( firstChunk ) firstChunk = false;
|
||||
|
||||
uint64_t t2 = msclock();
|
||||
while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) {
|
||||
timeout++;
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
// max timeout for one chunk of 85keys, 60*2sec = 120seconds
|
||||
// s70 with 40*2 keys to check, 80*85 = 6800 auth.
|
||||
// takes about 97s, still some margin before abort
|
||||
if (timeout > 60) {
|
||||
PrintAndLog("\nNo response from Proxmark. Aborting...");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t curr_keys = resp.arg[0];
|
||||
foo = bytes_to_num(resp.d.asBytes+480, 8);
|
||||
bar = bytes_to_num(resp.d.asBytes+488, 2);
|
||||
|
||||
// reset
|
||||
timeout = 0;
|
||||
|
||||
t2 = msclock() - t2;
|
||||
PrintAndLog("\n[-] Chunk: %.1fs | found %d/%d keys", t2, (float)(t2/1000.0), curr_keys, (SectorsCnt<<1));
|
||||
|
||||
// all keys?
|
||||
if ( curr_keys == SectorsCnt*2 || lastChunk ) {
|
||||
memcpy(e_sector, resp.d.asBytes, SectorsCnt * sizeof(icesector_t) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
t1 = msclock() - t1;
|
||||
PrintAndLog("[+] Time in checkkeys (fast): %.1fs\n", (float)(t1/1000.0));
|
||||
|
||||
//print keys
|
||||
printKeyTable_fast( SectorsCnt, e_sector, bar, foo );
|
||||
|
||||
if (transferToEml) {
|
||||
uint8_t block[16] = {0x00};
|
||||
for (uint8_t i = 0; i < SectorsCnt; ++i ) {
|
||||
mfEmlGetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1);
|
||||
/*
|
||||
if (e_sector[i].foundKey[0])
|
||||
memcpy(block, e_sector[i].keyA, 6);
|
||||
if (e_sector[i].foundKey[1])
|
||||
memcpy(block+10, e_sector[i].keyB, 6);
|
||||
mfEmlSetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1);
|
||||
*/
|
||||
}
|
||||
PrintAndLog("Found keys have been transferred to the emulator memory");
|
||||
}
|
||||
|
||||
if (createDumpFile) {
|
||||
FILE *fkeys = fopen("dumpkeys.bin","wb");
|
||||
if (fkeys == NULL) {
|
||||
PrintAndLog("Could not create file dumpkeys.bin");
|
||||
free(keyBlock);
|
||||
free(e_sector);
|
||||
return 1;
|
||||
}
|
||||
PrintAndLog("Printing keys to binary file dumpkeys.bin...");
|
||||
|
||||
for( i=0; i<SectorsCnt; i++)
|
||||
fwrite (e_sector[i].keyA, 1, 6, fkeys);
|
||||
|
||||
for(i=0; i<SectorsCnt; i++)
|
||||
fwrite (e_sector[i].keyB, 1, 6, fkeys );
|
||||
|
||||
fclose(fkeys);
|
||||
PrintAndLog("Found keys have been dumped to file dumpkeys.bin. 0xffffffffffff has been inserted for unknown keys.");
|
||||
}
|
||||
|
||||
free(keyBlock);
|
||||
free(e_sector);
|
||||
PrintAndLog("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdHF14AMfChk(const char *Cmd) {
|
||||
|
||||
if (strlen(Cmd)<3) return usage_hf14_chk();
|
||||
|
@ -1585,6 +1869,7 @@ int CmdHF14AMfSniff(const char *Cmd){
|
|||
bool wantSaveToEmlFile = false;
|
||||
|
||||
//var
|
||||
int tmpchar;
|
||||
int res = 0;
|
||||
int len = 0;
|
||||
int blockLen = 0;
|
||||
|
@ -1627,7 +1912,8 @@ int CmdHF14AMfSniff(const char *Cmd){
|
|||
while (true) {
|
||||
printf("."); fflush(stdout);
|
||||
if (ukbhit()) {
|
||||
int gc = getchar(); (void)gc;
|
||||
tmpchar = getchar();
|
||||
(void)tmpchar;
|
||||
printf("\naborted via keyboard!\n");
|
||||
break;
|
||||
}
|
||||
|
@ -1774,6 +2060,31 @@ int CmdHF14AMfKeyBrute(const char *Cmd) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void printKeyTable_fast( uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo ){
|
||||
|
||||
uint8_t arr[80];
|
||||
for (uint8_t i = 0; i < 64; ++i) {
|
||||
arr[i] = (foo >> i) & 0x1;
|
||||
}
|
||||
for (uint8_t i = 0; i < 16; ++i) {
|
||||
arr[i+64] = (bar >> i) & 0x1;
|
||||
}
|
||||
|
||||
PrintAndLog("|---|----------------|---|----------------|---|");
|
||||
PrintAndLog("|sec|key A |res|key B |res|");
|
||||
PrintAndLog("|---|----------------|---|----------------|---|");
|
||||
for (uint8_t i = 0; i < sectorscnt; ++i) {
|
||||
PrintAndLog("|%03d| %012" PRIx64 " | %d | %012" PRIx64 " | %d |"
|
||||
, i
|
||||
, bytes_to_num(e_sector[i].keyA, 6)
|
||||
, arr[i*2]
|
||||
, bytes_to_num(e_sector[i].keyB, 6)
|
||||
, arr[(i*2)+1]
|
||||
);
|
||||
}
|
||||
PrintAndLog("|---|----------------|---|----------------|---|");
|
||||
}
|
||||
|
||||
void printKeyTable( uint8_t sectorscnt, sector_t *e_sector ){
|
||||
PrintAndLog("|---|----------------|---|----------------|---|");
|
||||
PrintAndLog("|sec|key A |res|key B |res|");
|
||||
|
@ -2611,6 +2922,7 @@ static command_t CommandTable[] = {
|
|||
{"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"},
|
||||
{"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"},
|
||||
{"chk", CmdHF14AMfChk, 0, "Check keys"},
|
||||
{"fchk", CmdHF14AMfChk_fast, 0, "Check keys fast, targets all keys on card"},
|
||||
{"mifare", CmdHF14AMifare, 0, "Darkside attack. read parity error messages."},
|
||||
{"nested", CmdHF14AMfNested, 0, "Nested attack. Test nested authentication"},
|
||||
{"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"},
|
||||
|
|
Loading…
Reference in a new issue