fix: 'hf iclass clone' - last block write fails, see (https://github.com/RfidResearchGroup/proxmark3/issues/183)

chg: 'hf iclass clone' - now tries ten times,  behavies better, obey verbose nicer, drop field constant. Uses NG.
chg: 'hf iclass dump' - now tries ten times,
in general nice outputs..
This commit is contained in:
iceman1001 2019-09-08 23:19:06 +02:00
commit 9a2a0116e4
3 changed files with 145 additions and 87 deletions

View file

@ -1271,7 +1271,13 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_ICLASS_CLONE: {
iClass_Clone(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes);
struct p {
uint8_t startblock;
uint8_t endblock;
uint8_t data[];
} PACKED;
struct p *payload = (struct p *)packet->data.asBytes;
iClass_Clone(payload->startblock, payload->endblock, payload->data);
break;
}
#endif

View file

@ -2520,23 +2520,19 @@ void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) {
int total_block = (endblock - startblock) + 1;
for (i = 0; i < total_block; i++) {
// block number
if (iClass_WriteBlock_ext(i + startblock, data + (i * 12))) {
Dbprintf("Write block [%02x] successful", i + startblock);
if (iClass_WriteBlock_ext(startblock + i, data + (i * 12))) {
Dbprintf("Write block [%02x] successful", startblock + i);
written++;
} else {
if (iClass_WriteBlock_ext(i + startblock, data + (i * 12))) {
Dbprintf("Write block [%02x] successful", i + startblock);
written++;
} else {
Dbprintf("Write block [%02x] failed", i + startblock);
}
Dbprintf("Write block [%02x] failed", startblock + i);
}
}
if (written == total_block)
DbpString("Clone complete");
else
DbpString("Clone incomplete");
reply_mix(CMD_ACK, 1, 0, 0, 0, 0);
switch_off();
uint8_t isOK = 0;
if (written == total_block)
isOK = 1;
reply_ng(CMD_HF_ICLASS_CLONE, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t));
}

View file

@ -1118,6 +1118,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
cmdp += 2;
break;
case 'e':
PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo"));
elite = true;
cmdp++;
break;
@ -1149,6 +1150,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
cmdp += 2;
break;
case 'r':
PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode"));
rawkey = true;
cmdp++;
break;
@ -1180,7 +1182,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) {
PrintAndLogEx(WARNING, "command execute timeout");
DropField();
return 0;
return PM3_ESOFT;
}
DropField();
@ -1189,7 +1191,8 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
if (readStatus == 0) {
PrintAndLogEx(FAILED, "no tag found");
return 0;
DropField();
return PM3_ESOFT;
}
if (readStatus & (FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_CC)) {
@ -1202,14 +1205,16 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
}
// authenticate debit key and get div_key - later store in dump block 3
if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) {
//try twice - for some reason it sometimes fails the first time...
PrintAndLogEx(SUCCESS, "retry to select card");
if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) {
PrintAndLogEx(WARNING, "failed authenticating with debit key");
DropField();
return 0;
}
int numberAuthRetries = ICLASS_AUTH_RETRY;
do {
if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose))
break;
} while (numberAuthRetries--);
if ( numberAuthRetries <= 0) {
PrintAndLogEx(WARNING, "failed authenticating with debit key");
DropField();
return PM3_ESOFT;
}
// begin dump
@ -1233,7 +1238,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
uint8_t isOK = resp.oldarg[0] & 0xff;
if (!isOK && !blocksRead) {
PrintAndLogEx(WARNING, "read block failed");
return 0;
return PM3_ESOFt;
}
uint32_t startindex = resp.oldarg[2];
@ -1245,7 +1250,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
// response ok - now get bigbuf content of the dump
if (!GetFromDevice(BIG_BUF, tag_data + (blockno * 8), blocksRead * 8, startindex, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "command execution time out");
return 0;
return PM3_ETIMEOUT;
}
size_t gotBytes = blocksRead * 8 + blockno * 8;
@ -1254,16 +1259,22 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
if (have_credit_key) {
//turn off hf field before authenticating with different key
DropField();
memset(MAC, 0, 4);
// AA2 authenticate credit key and git c_div_key - later store in dump block 4
if (!select_and_auth(CreditKEY, MAC, c_div_key, true, elite, rawkey, verbose)) {
//try twice - for some reason it sometimes fails the first time...
if (!select_and_auth(CreditKEY, MAC, c_div_key, true, elite, rawkey, verbose)) {
PrintAndLogEx(WARNING, "failed authenticating with credit key");
DropField();
return 0;
}
numberAuthRetries = ICLASS_AUTH_RETRY;
do {
if (select_and_auth(CreditKEY, MAC, c_div_key, true, elite, rawkey, verbose))
break;
} while (numberAuthRetries--);
if ( numberAuthRetries <= 0) {
PrintAndLogEx(WARNING, "failed authenticating with credit key");
DropField();
return PM3_ESOFT;
}
// do we still need to read more block? (aa2 enabled?)
if (maxBlk > blockno + numblks + 1) {
// setup dump and start
@ -1271,13 +1282,13 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
SendCommandMIX(CMD_HF_ICLASS_DUMP, blockno + blocksRead, maxBlk - (blockno + blocksRead), 0, NULL, 0);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) {
PrintAndLogEx(WARNING, "command execute timeout 2");
return 0;
return PM3_ETIMEOUT;
}
isOK = resp.oldarg[0] & 0xff;
blocksRead = resp.oldarg[1];
if (!isOK && !blocksRead) {
PrintAndLogEx(WARNING, "read block failed 2");
return 0;
return PM3_ESOFT;
}
startindex = resp.oldarg[2];
@ -1288,7 +1299,7 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
// get dumped data from bigbuf
if (!GetFromDevice(BIG_BUF, tag_data + gotBytes, blocksRead * 8, startindex, NULL, 0, NULL, 2500, false)) {
PrintAndLogEx(WARNING, "command execution time out");
return 0;
return PM3_ETIMEOUT;
}
gotBytes += blocksRead * 8;
@ -1298,16 +1309,20 @@ static int CmdHFiClassReader_Dump(const char *Cmd) {
DropField();
// add diversified keys to dump
if (have_debit_key) memcpy(tag_data + (3 * 8), div_key, 8);
if (have_credit_key) memcpy(tag_data + (4 * 8), c_div_key, 8);
if (have_debit_key)
memcpy(tag_data + (3 * 8), div_key, 8);
if (have_credit_key)
memcpy(tag_data + (4 * 8), c_div_key, 8);
// print the dump
PrintAndLogEx(NORMAL, "------+--+-------------------------+\n");
PrintAndLogEx(NORMAL, "CSN |00| %s|\n", sprint_hex(tag_data, 8));
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "------+--+-------------------------+");
PrintAndLogEx(NORMAL, "CSN |00| %s|", sprint_hex(tag_data, 8));
printIclassDumpContents(tag_data, 1, (gotBytes / 8), gotBytes);
if (filename[0] == 0) {
//Use the first block (CSN) for filename
strcat(filename, "hf-iclass-");
FillFileNameByUID(filename, tag_data, "-data", 8);
@ -1469,6 +1484,7 @@ static int CmdHFiClassCloneTag(const char *Cmd) {
uint8_t startblock = 0;
uint8_t endblock = 0;
uint8_t dataLen = 0;
bool got_startblk = false, got_endblk = false;
bool use_credit_key = false;
bool elite = false;
bool rawkey = false;
@ -1480,17 +1496,17 @@ static int CmdHFiClassCloneTag(const char *Cmd) {
case 'h':
return usage_hf_iclass_clone();
case 'b':
if (param_gethex(Cmd, cmdp + 1, &startblock, 2)) {
PrintAndLogEx(WARNING, "start block No must include 2 HEX symbols\n");
errors = true;
}
startblock = param_get8ex(Cmd, cmdp + 1, 07, 16);
got_startblk = true;
cmdp += 2;
break;
case 'c':
PrintAndLogEx(SUCCESS, "Using " _YELLOW_("CREDIT"));
use_credit_key = true;
cmdp++;
break;
case 'e':
PrintAndLogEx(SUCCESS, "Using " _YELLOW_("elite algo"));
elite = true;
cmdp++;
break;
@ -1509,6 +1525,7 @@ static int CmdHFiClassCloneTag(const char *Cmd) {
} else if (dataLen == 1) {
keyNbr = param_get8(Cmd, cmdp + 1);
if (keyNbr < ICLASS_KEYS_MAX) {
PrintAndLogEx(SUCCESS, "Using key[%d] %s", keyNbr, sprint_hex(iClass_Key_Table[keyNbr], 8 ));
memcpy(KEY, iClass_Key_Table[keyNbr], 8);
} else {
PrintAndLogEx(WARNING, "\nERROR: Credit KeyNbr is invalid\n");
@ -1521,13 +1538,12 @@ static int CmdHFiClassCloneTag(const char *Cmd) {
cmdp += 2;
break;
case 'l':
if (param_gethex(Cmd, cmdp + 1, &endblock, 2)) {
PrintAndLogEx(WARNING, "start Block No must include 2 HEX symbols\n");
errors = true;
}
endblock = param_get8ex(Cmd, cmdp + 1, 07, 16);
got_endblk = true;
cmdp += 2;
break;
case 'r':
PrintAndLogEx(SUCCESS, "Using " _YELLOW_("raw mode"));
rawkey = true;
cmdp++;
break;
@ -1541,53 +1557,66 @@ static int CmdHFiClassCloneTag(const char *Cmd) {
break;
}
}
if ( got_endblk == false || got_startblk == false)
errors = true;
if (errors || cmdp < 8) return usage_hf_iclass_clone();
FILE *f;
iclass_block_t tag_data[PM3_CMD_DATA_SIZE / 12];
if ((endblock - startblock + 1) * 12 > PM3_CMD_DATA_SIZE) {
PrintAndLogEx(NORMAL, "Trying to write too many blocks at once. Max: %d", PM3_CMD_DATA_SIZE / 8);
if (startblock < 5) {
PrintAndLogEx(WARNING, "you cannot write key blocks this way. yet... make your start block > 4");
return PM3_EINVARG;
}
int total_bytes = (((endblock - startblock) + 1) * 12);
if (total_bytes > PM3_CMD_DATA_SIZE - 2) {
PrintAndLogEx(NORMAL, "Trying to write too many blocks at once. Max: %d", PM3_CMD_DATA_SIZE / 8);
return PM3_EINVARG;
}
// file handling and reading
f = fopen(filename, "rb");
FILE *f = fopen(filename, "rb");
if (!f) {
PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename);
return PM3_EFILE;
}
if (startblock < 5) {
PrintAndLogEx(WARNING, "you cannot write key blocks this way. yet... make your start block > 4");
fclose(f);
return 0;
}
// now read data from the file from block 6 --- 19
// ok we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data
// then copy to usbcommand->asbytes; the max is 32 - 6 = 24 block 12 bytes each block 288 bytes then we can only accept to clone 21 blocks at the time,
// else we have to create a share memory
iclass_block_t tag_data[PM3_CMD_DATA_SIZE / 12];
// read data from file from block 6 --- 19
// we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data
// then copy to usbcommand->asbytes;
// max is 32 - 6 = 28 block. 28 x 12 bytes gives 336 bytes
int i;
fseek(f, startblock * 8, SEEK_SET);
size_t bytes_read = fread(tag_data, sizeof(iclass_block_t), endblock - startblock + 1, f);
fclose(f);
if (bytes_read == 0) {
PrintAndLogEx(ERR, "file reading error.");
fclose(f);
return 2;
return PM3_EFILE;
}
fclose(f);
uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose)) {
return 0;
int numberAuthRetries = ICLASS_AUTH_RETRY;
do {
if (select_and_auth(KEY, MAC, div_key, use_credit_key, elite, rawkey, verbose))
break;
} while (numberAuthRetries--);
if ( numberAuthRetries <= 0 ) {
PrintAndLogEx(ERR, "failed to authenticate");
DropField();
return PM3_ESOFT;
}
uint8_t data[(endblock - startblock) * 12];
uint8_t data[total_bytes];
// calculate all mac for every the block we will write
for (i = startblock; i <= endblock; i++) {
Calc_wb_mac(i, tag_data[i - startblock].d, div_key, MAC);
// usb command d start pointer = d + (i - 6) * 12
// memcpy(pointer,tag_data[i - 6],8) 8 bytes
@ -1597,22 +1626,49 @@ static int CmdHFiClassCloneTag(const char *Cmd) {
memcpy(ptr, &(tag_data[i - startblock].d[0]), 8);
memcpy(ptr + 8, MAC, 4);
}
uint8_t p[12];
for (i = 0; i <= endblock - startblock; i++) {
memcpy(p, data + (i * 12), 12);
PrintAndLogEx(NORMAL, "Block |%02x|", i + startblock);
PrintAndLogEx(NORMAL, " %02x%02x%02x%02x%02x%02x%02x%02x |", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
PrintAndLogEx(NORMAL, " MAC |%02x%02x%02x%02x|\n", p[8], p[9], p[10], p[11]);
if ( verbose ) {
PrintAndLogEx(NORMAL,"------+--------------------------+-------------");
PrintAndLogEx(NORMAL,"block | data | mac");
PrintAndLogEx(NORMAL,"------+--------------------------+-------------");
uint8_t p[12];
for (i = 0; i <= endblock - startblock; i++) {
memcpy(p, data + (i * 12), 12);
char *s = calloc(70, sizeof(uint8_t));
sprintf(s, "| %s ", sprint_hex(p, 8));
sprintf(s + strlen(s), "| %s", sprint_hex(p + 8, 4));
PrintAndLogEx(NORMAL, " %02X %s", i + startblock, s );
free(s);
}
}
struct p {
uint8_t startblock;
uint8_t endblock;
uint8_t data[PM3_CMD_DATA_SIZE - 2];
} PACKED payload;
payload.startblock = startblock;
payload.endblock = endblock;
memcpy(payload.data, data, total_bytes);
PacketResponseNG resp;
clearCommandBuffer();
SendCommandOLD(CMD_HF_ICLASS_CLONE, startblock, endblock, 0, data, (endblock - startblock) * 12);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) {
SendCommandNG(CMD_HF_ICLASS_CLONE, (uint8_t*)&payload, total_bytes + 2 );
if (WaitForResponseTimeout(CMD_HF_ICLASS_CLONE, &resp, 4500) == 0) {
PrintAndLogEx(WARNING, "command execute timeout");
return 0;
DropField();
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
if (resp.status == PM3_SUCCESS) {
if ( resp.data.asBytes[0] == 1 )
PrintAndLogEx(SUCCESS, "Clone successful");
else
PrintAndLogEx(WARNING, "Clone failed");
}
return resp.status;
}
static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool rawkey, bool verbose, bool auth) {
@ -1809,13 +1865,13 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e
//PrintAndLog ("startblock: %d, endblock: %d, filesize: %d, maxmemcount: %d, filemaxblock: %d",startblock, endblock,filesize, maxmemcount, filemaxblock);
int i = startblock;
PrintAndLogEx(NORMAL, "------+--+-------------------------+\n");
PrintAndLogEx(NORMAL, "------+--+-------------------------+");
while (i <= endblock) {
uint8_t *blk = iclass_dump + (i * 8);
PrintAndLogEx(NORMAL, " |%02X| %s\n", i, sprint_hex_ascii(blk, 8));
PrintAndLogEx(NORMAL, " |%02X| %s", i, sprint_hex_ascii(blk, 8));
i++;
}
PrintAndLogEx(NORMAL, "------+--+-------------------------+\n");
PrintAndLogEx(NORMAL, "------+--+-------------------------+");
}
static int CmdHFiClassReadTagFile(const char *Cmd) {