fix: hf iclass restore - now uses NG and better reporting and works :)

This commit is contained in:
iceman1001 2020-10-14 17:41:34 +02:00
commit 53c7e47e75
6 changed files with 143 additions and 175 deletions

View file

@ -302,15 +302,17 @@ static int reader_dump_mode(void) {
Iso15693InitReader();
set_tracing(false);
picopass_hdr *hdr = (picopass_hdr *)card_data;
// select tag.
uint32_t eof_time = 0;
bool res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time);
bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time);
if (res == false) {
switch_off();
continue;
}
picopass_hdr *hdr = (picopass_hdr *)card_data;
// sanity check of CSN.
if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) {
switch_off();
@ -366,7 +368,7 @@ static int reader_dump_mode(void) {
auth.use_credit_key = true;
memcpy(auth.key, aa2_key, sizeof(auth.key));
res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time);
res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time);
if (res) {
// sanity check of CSN.

View file

@ -1553,18 +1553,8 @@ static void PacketReceived(PacketCommandNG *packet) {
iClass_Dump(packet->data.asBytes);
break;
}
case CMD_HF_ICLASS_CLONE: {
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;
}
case CMD_HF_ICLASS_RESTORE: {
iClass_Restore(packet->data.asBytes);
iClass_Restore( (iclass_restore_req_t *)packet->data.asBytes);
break;
}
#endif

View file

@ -1276,7 +1276,7 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t *
* @return false = fail
* true = Got all.
*/
static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time, uint8_t *status) {
static bool select_iclass_tag_ex(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time, uint8_t *status) {
static uint8_t act_all[] = { ICLASS_CMD_ACTALL };
static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 };
@ -1286,8 +1286,6 @@ static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32
uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 };
uint8_t resp[ICLASS_BUFFER_SIZE] = {0};
picopass_hdr *hdr = (picopass_hdr *)card_data;
// Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used
// bit 7: parity.
if (use_credit_key)
@ -1369,6 +1367,8 @@ static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32
*status |= FLAG_ICLASS_CC;
} else {
// on NON_SECURE_PAGEMODE cards, AIA is on block2..
// read App Issuer Area block 2
read_aia[1] = 0x02;
@ -1385,23 +1385,23 @@ static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32
if (status) {
*status |= FLAG_ICLASS_AIA;
memcpy(card_data + (8 * 2), resp, 8);
memcpy(hdr->epurse, resp, sizeof(hdr->epurse));
}
}
return true;
}
bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) {
bool select_iclass_tag(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time) {
uint8_t result = 0;
return select_iclass_tag_ex(card_data, use_credit_key, eof_time, &result);
return select_iclass_tag_ex(hdr, use_credit_key, eof_time, &result);
}
// Reader iClass Anticollission
// turn off afterwards
void ReaderIClass(uint8_t flags) {
uint8_t card_data[6 * 8] = {0xFF};
picopass_hdr hdr = {0};
// uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint8_t resp[ICLASS_BUFFER_SIZE] = {0};
memset(resp, 0xFF, sizeof(resp));
@ -1419,14 +1419,13 @@ void ReaderIClass(uint8_t flags) {
uint8_t result_status = 0;
uint32_t eof_time = 0;
bool status = select_iclass_tag_ex(card_data, use_credit_key, &eof_time, &result_status);
bool status = select_iclass_tag_ex(&hdr, use_credit_key, &eof_time, &result_status);
if (status == false) {
reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0);
reply_mix(CMD_ACK, 0xFF, 0, 0, NULL, 0);
switch_off();
return;
}
// Page mapping for secure mode
// 0 : CSN
// 1 : Configuration
@ -1444,7 +1443,7 @@ void ReaderIClass(uint8_t flags) {
// with 0xFF:s in block 3 and 4.
LED_B_ON();
reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data));
reply_mix(CMD_ACK, result_status, 0, 0, (uint8_t*)&hdr, sizeof(hdr));
//Send back to client, but don't bother if we already sent this -
// only useful if looping in arm (not try_once && not abort_after_read)
@ -1543,7 +1542,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) {
readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK;
// select card / e-purse
uint8_t card_data[6 * 8] = {0};
picopass_hdr hdr = {0};
iclass_premac_t *keys = (iclass_premac_t *)datain;
@ -1557,7 +1556,7 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) {
uint32_t start_time = 0, eof_time = 0;
if (select_iclass_tag(card_data, use_credit_key, &eof_time) == false)
if (select_iclass_tag(&hdr, use_credit_key, &eof_time) == false)
goto out;
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
@ -1634,7 +1633,7 @@ void iClass_ReadBlock(uint8_t *msg) {
// select tag.
uint32_t eof_time = 0;
picopass_hdr hdr = {0};
bool res = select_iclass_tag((uint8_t *)&hdr, payload->use_credit_key, &eof_time);
bool res = select_iclass_tag(&hdr, payload->use_credit_key, &eof_time);
if (res == false) {
if (payload->send_reply) {
response.isOK = res;
@ -1707,7 +1706,7 @@ void iClass_Dump(uint8_t *msg) {
// select tag.
uint32_t eof_time = 0;
picopass_hdr hdr = {0};
bool res = select_iclass_tag((uint8_t *)&hdr, req->use_credit_key, &eof_time);
bool res = select_iclass_tag(&hdr, req->use_credit_key, &eof_time);
if (res == false) {
if (req->send_reply) {
reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0);
@ -1777,10 +1776,12 @@ void iClass_Dump(uint8_t *msg) {
BigBuf_free();
}
static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data) {
static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac) {
// write command: cmd, 1 blockno, 8 data, 4 mac
uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, blockno };
memcpy(write + 2, data, 12); // data + mac
memcpy(write + 2, data, 8);
memcpy(write + 10, mac, 4);
AddCrc(write + 1, 13);
uint8_t resp[10] = {0};
@ -1825,7 +1826,7 @@ void iClass_WriteBlock(uint8_t *msg) {
// select tag.
uint32_t eof_time = 0;
picopass_hdr hdr = {0};
bool res = select_iclass_tag((uint8_t *)&hdr, payload->req.use_credit_key, &eof_time);
bool res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time);
if (res == false) {
goto out;
}
@ -1924,29 +1925,75 @@ out:
reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t));
}
// turn off afterwards
void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) {
}
void iClass_Restore(iclass_restore_req_t *msg) {
void iClass_Restore(uint8_t *msg) {
// sanitation
if (msg == NULL) {
reply_ng(CMD_HF_ICLASS_RESTORE, PM3_ESOFT, NULL, 0);
return;
}
iclass_restore_req_t *cmd = (iclass_restore_req_t *)msg;
// iclass_auth_req_t *req = &cmd->req;
if (msg->item_cnt == 0) {
if (msg->req.send_reply) {
reply_ng(CMD_HF_ICLASS_RESTORE, PM3_ESOFT, NULL, 0);
}
return;
}
LED_A_ON();
uint16_t written = 0;
uint16_t total_blocks = (cmd->end_block - cmd->start_block) + 1;
for (uint8_t b = cmd->start_block; b < total_blocks; b++) {
Iso15693InitReader();
if (iclass_writeblock_ext(b, cmd->data + ((b - cmd->start_block) * 12))) {
Dbprintf("Write block [%02x] successful", b);
written++;
} else {
Dbprintf("Write block [%02x] failed", b);
uint16_t written = 0;
uint32_t eof_time = 0;
picopass_hdr hdr = {0};
// select
bool res = select_iclass_tag(&hdr, msg->req.use_credit_key, &eof_time);
if (res == false) {
goto out;
}
// authenticate
uint8_t mac[4] = {0};
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
// authenticate
if (msg->req.do_auth) {
res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac);
if (res == false) {
goto out;
}
}
// main loop
for (uint8_t i = 0; i < msg->item_cnt; i++) {
iclass_restore_item_t item = msg->blocks[i];
// calc new mac for data, using 1b blockno, 8b data,
uint8_t wb[9] = {0};
wb[0] = item.blockno;
memcpy(wb + 1, item.data, 8);
if (msg->req.use_credit_key)
doMAC_N(wb, sizeof(wb), hdr.key_c, mac);
else
doMAC_N(wb, sizeof(wb), hdr.key_d, mac);
// data + mac
if (iclass_writeblock_ext(item.blockno, item.data, mac)) {
Dbprintf("Write block [%02x] " _GREEN_("successful"), item.blockno);
written++;
} else {
Dbprintf("Write block [%02x] " _RED_("failed"), item.blockno);
}
}
out:
switch_off();
uint8_t isOK = (written == total_blocks) ? 1 : 0;
reply_ng(CMD_HF_ICLASS_CLONE, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t));
if (msg->req.send_reply) {
int isOK = (written == msg->item_cnt) ? PM3_SUCCESS : PM3_ESOFT;
reply_ng(CMD_HF_ICLASS_RESTORE, isOK, NULL, 0);
}
}

View file

@ -21,8 +21,7 @@ void ReaderIClass(uint8_t arg0);
void iClass_WriteBlock(uint8_t *msg);
void iClass_Dump(uint8_t *msg);
void iClass_Restore(uint8_t *msg);
void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data);
void iClass_Restore(iclass_restore_req_t *msg);
int do_iclass_simulation_nonsec(void);
int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf);
@ -36,6 +35,6 @@ bool iclass_auth(iclass_auth_req_t *payload, uint8_t *out);
void iClass_ReadBlock(uint8_t *msg);
bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time);
bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time);
bool select_iclass_tag(picopass_hdr *hdr, bool use_credit_key, uint32_t *eof_time);
bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out);
#endif

View file

@ -1431,13 +1431,6 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) {
return PM3_SUCCESS;
}
static void calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_t *MAC) {
uint8_t wb[9];
wb[0] = blockno;
memcpy(wb + 1, data, 8);
doMAC_N(wb, sizeof(wb), div_key, MAC);
}
static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) {
uint8_t flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE);
@ -1475,47 +1468,6 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose) {
return true;
}
static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool rawkey, bool verbose) {
iclass_auth_req_t payload = {
.use_raw = rawkey,
.use_elite = elite,
.use_credit_key = use_credit_key
};
memcpy(payload.key, KEY, 8);
SendCommandNG(CMD_HF_ICLASS_AUTH, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
clearCommandBuffer();
if (WaitForResponseTimeout(CMD_HF_ICLASS_AUTH, &resp, 2000) == 0) {
if (verbose) PrintAndLogEx(WARNING, "Command execute timeout");
return false;
}
if (resp.status != PM3_SUCCESS) {
if (verbose) PrintAndLogEx(ERR, "failed to communicate with card");
return false;
}
iclass_readblock_resp_t *packet = (iclass_readblock_resp_t *)resp.data.asBytes;
if (packet->isOK == 0) {
if (verbose) PrintAndLogEx(FAILED, "authentication error");
return false;
}
if (div_key)
memcpy(div_key, packet->div_key, sizeof(packet->div_key));
if (MAC)
memcpy(MAC, packet->mac, sizeof(packet->mac));
if (verbose)
PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8));
return true;
}
static int CmdHFiClassDump(const char *Cmd) {
uint8_t KEY[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@ -2026,11 +1978,6 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) {
return isok;
}
/*
static int CmdHFiClassClone(const char *Cmd) {
return PM3_SUCCESS;
}
*/
static int CmdHFiClassRestore(const char *Cmd) {
char filename[FILE_PATH_SIZE] = { 0x00 };
char tempStr[50] = {0};
@ -2082,7 +2029,7 @@ static int CmdHFiClassRestore(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));
PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%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");
@ -2119,14 +2066,19 @@ static int CmdHFiClassRestore(const char *Cmd) {
if (errors || cmdp < 8) return usage_hf_iclass_restore();
if (rawkey + elite > 1) {
PrintAndLogEx(FAILED, "Can not use both 'e', 'r'");
return PM3_EINVARG;
}
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);
uint32_t payload_size = sizeof(iclass_restore_req_t) + (sizeof(iclass_restore_item_t) * (endblock - startblock + 1));
if (total_bytes > PM3_CMD_DATA_SIZE - 2) {
if (payload_size > PM3_CMD_DATA_SIZE) {
PrintAndLogEx(NORMAL, "Trying to write too many blocks at once. Max: %d", PM3_CMD_DATA_SIZE / 8);
return PM3_EINVARG;
}
@ -2144,93 +2096,68 @@ static int CmdHFiClassRestore(const char *Cmd) {
return PM3_EFILE;
}
if (bytes_read < sizeof(iclass_block_t) * (endblock - startblock + 1)) {
PrintAndLogEx(ERR, "file wrong size");
if (bytes_read < ((endblock - startblock + 1) * 8 )) {
PrintAndLogEx(ERR, "file is smaller than your suggested block range ( " _RED_("0x%02x..0x%02x")" )",
startblock, endblock
);
free(dump);
return PM3_EFILE;
}
iclass_restore_req_t *payload = calloc(1, payload_size);
payload->req.use_raw = rawkey,
payload->req.use_elite = elite,
payload->req.use_credit_key = use_credit_key,
payload->req.use_replay = false,
payload->req.blockno = startblock,
payload->req.send_reply = true,
payload->req.do_auth = true,
memcpy(payload->req.key, KEY, 8);
payload->item_cnt = (endblock - startblock + 1);
// 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
iclass_block_t tag_data[PM3_CMD_DATA_SIZE / 12];
memcpy(tag_data, dump + startblock * 8, sizeof(iclass_block_t) * (endblock - startblock + 1));
for (uint8_t i = 0; i < payload->item_cnt; i++) {
payload->blocks[i].blockno = startblock + i;
memcpy(payload->blocks[i].data, dump + (startblock * 8) + (i * 8) , sizeof(payload->blocks[i].data));
}
free(dump);
uint8_t MAC[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int i;
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[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
// memcpy(pointer + 8,mac,sizoof(mac) 4 bytes;
// next one
uint8_t *ptr = data + (i - startblock) * 12;
memcpy(ptr, &(tag_data[i - startblock].d[0]), 8);
memcpy(ptr + 8, MAC, 4);
}
if (verbose) {
PrintAndLogEx(INFO, "------+--------------------------+-------------");
PrintAndLogEx(INFO, "block | data | mac");
PrintAndLogEx(INFO, "------+--------------------------+-------------");
uint8_t p[12];
for (i = 0; i <= endblock - startblock; i++) {
memcpy(p, data + (i * 12), 12);
char *s = calloc(70, sizeof(uint8_t));
snprintf(s, 70, "| %s ", sprint_hex(p, 8));
snprintf(s + strlen(s), 70 - strlen(s), "| %s", sprint_hex(p + 8, 4));
PrintAndLogEx(NORMAL, " %02X %s", i + startblock, s);
free(s);
PrintAndLogEx(INFO, "Preparing to restore block range 0x02x..0x%02x", startblock, endblock);
PrintAndLogEx(INFO, "------+----------------------");
PrintAndLogEx(INFO, "block | data");
PrintAndLogEx(INFO, "------+----------------------");
for (uint8_t i = 0; i < payload->item_cnt; i++) {
iclass_restore_item_t item = payload->blocks[i];
PrintAndLogEx(INFO, " %02X | %s", item.blockno, sprint_hex_inrow(item.data, sizeof(item.data)));
}
}
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);
PrintAndLogEx(INFO, "restore started...");
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_HF_ICLASS_CLONE, (uint8_t *)&payload, total_bytes + 2);
SendCommandNG(CMD_HF_ICLASS_RESTORE, (uint8_t *)payload, payload_size);
if (WaitForResponseTimeout(CMD_HF_ICLASS_CLONE, &resp, 2000) == 0) {
if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == 0) {
PrintAndLogEx(WARNING, "command execute timeout");
DropField();
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS) {
if (resp.data.asBytes[0] == 1)
PrintAndLogEx(SUCCESS, "Restore successful");
else
PrintAndLogEx(WARNING, "Restore failed");
PrintAndLogEx(SUCCESS, "iCLASS restore " _GREEN_("successful"));
PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass rdbl ") "` to verify data on card");
} else {
PrintAndLogEx(WARNING, "iCLASS restore " _RED_("failed"));
}
return resp.status;
}

View file

@ -334,11 +334,15 @@ typedef struct {
} PACKED iclass_writeblock_req_t;
// iCLASS dump data structure
typedef struct {
uint8_t blockno;
uint8_t data[8];
} PACKED iclass_restore_item_t;
typedef struct {
iclass_auth_req_t req;
uint8_t start_block;
uint8_t end_block;
uint8_t data[];
uint8_t item_cnt;
iclass_restore_item_t blocks[];
} PACKED iclass_restore_req_t;
@ -351,7 +355,7 @@ typedef struct {
uint8_t mem_config; //[13]
uint8_t eas; //[14]
uint8_t fuses; //[15]
} picopass_conf_block_t;
} PACKED picopass_conf_block_t;
// iCLASS secure mode memory mapping
typedef struct {
@ -361,14 +365,14 @@ typedef struct {
uint8_t key_d[8];
uint8_t key_c[8];
uint8_t app_issuer_area[8];
} picopass_hdr;
} PACKED picopass_hdr;
// iCLASS non-secure mode memory mapping
typedef struct {
uint8_t csn[8];
picopass_conf_block_t conf;
uint8_t app_issuer_area[8];
} picopass_ns_hdr;
} PACKED picopass_ns_hdr;
// For the bootloader
@ -560,7 +564,6 @@ typedef struct {
// iCLASS / Picopass
#define CMD_HF_ICLASS_READCHECK 0x038F
#define CMD_HF_ICLASS_CLONE 0x0390
#define CMD_HF_ICLASS_DUMP 0x0391
#define CMD_HF_ICLASS_SNIFF 0x0392
#define CMD_HF_ICLASS_SIMULATE 0x0393