mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 18:48:13 -07:00
Iclass Legacy Raw Key Recovery Function
Based on the work described in Dismantling iClass whitepaper. hf iclass legbrute is tested working hf iclass legrec is partially working: logic of operations and sequence seems to be in order and was tested on simulated data to be effective. The privilege escalation part is still not successful, but the logic should be correct.
This commit is contained in:
parent
0c711ced9e
commit
1832997ccb
7 changed files with 529 additions and 0 deletions
|
@ -15,6 +15,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
|||
- Added MFC Key for swimming pool cards in Reykjavík Iceland (@dandri)
|
||||
- Added key for Orkan keyfobs (@dandri)
|
||||
- Added key for Atlantsolía keyfobs (@dandri)
|
||||
- Added `hf iclass legbrute` this function allows to bruteforce 40/64 k1 bits of an iclass card to recover the raw key used(@antiklesys).
|
||||
- Added `hf iclass legrec` this function allows to recover 24/64 k1 bits of an iclass card (@antiklesys).
|
||||
## [Aurora.4.18589][2024-05-28]
|
||||
- Fixed the pm3 regressiontests for Hitag2Crack (@iceman1001)
|
||||
- Changed `mem spiffs tree` - adapted to bigbuff and show if empty (@iceman1001)
|
||||
|
|
|
@ -2061,6 +2061,10 @@ static void PacketReceived(PacketCommandNG *packet) {
|
|||
iClass_Restore((iclass_restore_req_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_ICLASS_RECOVER: {
|
||||
iClass_Recover((iclass_recover_req_t *)packet->data.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_HF_ICLASS_CREDIT_EPURSE: {
|
||||
iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes);
|
||||
break;
|
||||
|
|
213
armsrc/iclass.c
213
armsrc/iclass.c
|
@ -2152,3 +2152,216 @@ out:
|
|||
reply_ng(CMD_HF_ICLASS_RESTORE, isOK, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_single_key_block_inverted(const uint8_t startingKey[PICOPASS_BLOCK_SIZE], uint32_t index, uint8_t keyBlock[PICOPASS_BLOCK_SIZE]) {
|
||||
uint32_t carry = index;
|
||||
memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE);
|
||||
|
||||
for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) {
|
||||
uint8_t increment_value = carry & 0x07; // Use only the last 3 bits of carry
|
||||
keyBlock[j] = increment_value; // Set the last 3 bits, assuming first 5 bits are always 0
|
||||
|
||||
carry >>= 3; // Shift right by 3 bits for the next byte
|
||||
if (carry == 0) {
|
||||
// If no more carry, break early to avoid unnecessary loops
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Function to convert an unsigned int to binary string
|
||||
void intToBinary(unsigned int num, char *binaryStr, int size) {
|
||||
binaryStr[size] = '\0'; // Null-terminate the string
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
binaryStr[i] = (num % 2) ? '1' : '0';
|
||||
num /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to convert a binary string to hexadecimal
|
||||
uint8_t binaryToHex(char *binaryStr) {
|
||||
return (uint8_t)strtoul(binaryStr, NULL, 2);
|
||||
}
|
||||
|
||||
// Function to convert an unsigned int to an array of hex values
|
||||
void convertToHexArray(unsigned int num, uint8_t *partialKey) {
|
||||
char binaryStr[25]; // 24 bits for binary representation + 1 for null terminator
|
||||
|
||||
// Convert the number to binary string
|
||||
intToBinary(num, binaryStr, 24);
|
||||
|
||||
// Split the binary string into groups of 3 and convert to hex
|
||||
for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) {
|
||||
char group[4];
|
||||
strncpy(group, binaryStr + i * 3, 3);
|
||||
group[3] = '\0'; // Null-terminate the group string
|
||||
partialKey[i] = binaryToHex(group);
|
||||
}
|
||||
}
|
||||
|
||||
void iClass_Recover(iclass_recover_req_t *msg) {
|
||||
|
||||
bool shallow_mod = false;
|
||||
|
||||
LED_A_ON();
|
||||
|
||||
Iso15693InitReader();
|
||||
//Authenticate with AA2 with the standard key to get the AA2 mac
|
||||
//Step0 Card Select Routine
|
||||
|
||||
uint32_t eof_time = 0;
|
||||
picopass_hdr_t hdr = {0};
|
||||
bool res = select_iclass_tag(&hdr, true, &eof_time, shallow_mod);
|
||||
if (res == false) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
//Step1 Authenticate with AA2 using K2
|
||||
|
||||
uint8_t mac2[4] = {0};
|
||||
uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
|
||||
res = authenticate_iclass_tag(&msg->req2, &hdr, &start_time, &eof_time, mac2);
|
||||
if (res == false) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uint8_t div_key2[8] = {0};
|
||||
memcpy(div_key2, hdr.key_c, 8);
|
||||
|
||||
//cycle reader to reset cypher state and be able to authenticate with k1 trace
|
||||
switch_off();
|
||||
Iso15693InitReader();
|
||||
|
||||
//Step0 Card Select Routine
|
||||
|
||||
eof_time = 0;
|
||||
//hdr = {0};
|
||||
res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod);
|
||||
if (res == false) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
//Step1 Authenticate with AA1 using trace
|
||||
|
||||
uint8_t mac1[4] = {0};
|
||||
start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER;
|
||||
res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1);
|
||||
if (res == false) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
//Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1
|
||||
uint8_t blockno = 24;
|
||||
uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00};
|
||||
AddCrc(cmd_read + 1, 1);
|
||||
uint8_t resp[10];
|
||||
|
||||
res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod);
|
||||
|
||||
static uint8_t iClass_Mac_Table[8][8] = { //Reference weak macs table
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xBF, 0x5D, 0x67, 0x7F }, //Expected mac when last 3 bits of each byte are: 000
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x10, 0xED, 0x6F, 0x11 }, //Expected mac when last 3 bits of each byte are: 001
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x53, 0x35, 0x42, 0x0F }, //Expected mac when last 3 bits of each byte are: 010
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xAB, 0x47, 0x4D, 0xA0 }, //Expected mac when last 3 bits of each byte are: 011
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xF6, 0xCF, 0x43, 0x36 }, //Expected mac when last 3 bits of each byte are: 100
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x59, 0x7F, 0x4B, 0x58 }, //Expected mac when last 3 bits of each byte are: 101
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0x1A, 0xA7, 0x66, 0x46 }, //Expected mac when last 3 bits of each byte are: 110
|
||||
{ 0x00, 0x00, 0x00, 0x00, 0xE2, 0xD5, 0x69, 0xE9 } //Expected mac when last 3 bits of each byte are: 111
|
||||
};
|
||||
//Viewing the weak macs table card 24 bits (3x8) in the form of a 24 bit decimal number
|
||||
static uint32_t iClass_Mac_Table_Bit_Values[8] = {0, 2396745, 4793490, 7190235, 9586980, 11983725, 14380470, 16777215};
|
||||
|
||||
uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint32_t index = 1;
|
||||
int bits_found = -1;
|
||||
|
||||
//START LOOP
|
||||
while (bits_found == -1){
|
||||
|
||||
//Step3 Calculate New Key
|
||||
uint8_t GenKeyBlock[PICOPASS_BLOCK_SIZE];
|
||||
uint8_t GenKeyBlock_old[PICOPASS_BLOCK_SIZE];
|
||||
uint8_t XorKeyBlock[PICOPASS_BLOCK_SIZE];
|
||||
generate_single_key_block_inverted(zero_key, index, GenKeyBlock);
|
||||
|
||||
//NOTE BEFORE UPDATING THE KEY WE NEED TO KEEP IN MIND KEYS ARE XORRED
|
||||
//xor the new key against the previously generated key so that we only update the difference
|
||||
if(index != 0){
|
||||
generate_single_key_block_inverted(zero_key, index - 1, GenKeyBlock_old);
|
||||
for (int i = 0; i < 8 ; i++) {
|
||||
XorKeyBlock[i] = GenKeyBlock[i] ^ GenKeyBlock_old[i];
|
||||
}
|
||||
}else{
|
||||
memcpy(XorKeyBlock, GenKeyBlock, PICOPASS_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
//Step4 Calculate New Mac
|
||||
|
||||
bool use_mac = true;
|
||||
uint8_t wb[9] = {0};
|
||||
blockno = 3;
|
||||
wb[0] = blockno;
|
||||
memcpy(wb + 1, XorKeyBlock, 8);
|
||||
|
||||
doMAC_N(wb, sizeof(wb), div_key2, mac2);
|
||||
|
||||
//Step5 Perform Write
|
||||
|
||||
if (iclass_writeblock_ext(blockno, XorKeyBlock, mac2, use_mac, shallow_mod)) {
|
||||
Dbprintf("Write block [%3d/0x%02X] " _GREEN_("successful"), blockno, blockno);
|
||||
} else {
|
||||
Dbprintf("Write block [%3d/0x%02X] " _RED_("failed"), blockno, blockno);
|
||||
goto out;
|
||||
}
|
||||
//Step6 Perform 8 authentication attempts
|
||||
|
||||
for (int i = 0; i < 8 ; ++i) {
|
||||
//need to craft the authentication payload accordingly
|
||||
memcpy(msg->req.key, iClass_Mac_Table[i], 8);
|
||||
res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //the mac here needs to be changed, mac 2 is a compiling placeholder
|
||||
if (res == true) {
|
||||
bits_found = iClass_Mac_Table_Bit_Values[i] ^ index;
|
||||
Dbprintf("Found Card Bits Index: " _GREEN_("[%3d]"), index);
|
||||
Dbprintf("Mac Table Bit Values: " _GREEN_("[%3d]"), iClass_Mac_Table_Bit_Values[i]);
|
||||
Dbprintf("Decimal Value of Partial Key: " _GREEN_("[%3d]"), bits_found);
|
||||
goto restore;
|
||||
}
|
||||
}
|
||||
|
||||
}//end while
|
||||
|
||||
|
||||
restore:
|
||||
;//empty statement for compilation
|
||||
uint8_t partialKey[PICOPASS_BLOCK_SIZE];
|
||||
convertToHexArray(bits_found, partialKey);
|
||||
|
||||
for (int i = 0; i < 8; i++){
|
||||
Dbprintf("Raw Key Partial Bytes: " _GREEN_("[%3d -> 0x%02X]"), i, partialKey);
|
||||
}
|
||||
|
||||
uint8_t resetKey[PICOPASS_BLOCK_SIZE];
|
||||
convertToHexArray(index, resetKey);
|
||||
|
||||
//Calculate reset Mac
|
||||
|
||||
bool use_mac = true;
|
||||
uint8_t wb[9] = {0};
|
||||
blockno = 3;
|
||||
wb[0] = blockno;
|
||||
memcpy(wb + 1, resetKey, 8);
|
||||
|
||||
doMAC_N(wb, sizeof(wb), div_key2, mac2);
|
||||
if (iclass_writeblock_ext(blockno, resetKey, mac2, use_mac, shallow_mod)) {
|
||||
Dbprintf("Restore of Original Key [%3d/0x%02X] " _GREEN_("successful"), blockno, blockno);
|
||||
} else {
|
||||
Dbprintf("Restore of Original Key [%3d/0x%02X] " _RED_("failed"), blockno, blockno);
|
||||
}
|
||||
switch_off();
|
||||
|
||||
|
||||
out:
|
||||
|
||||
switch_off();
|
||||
|
||||
|
||||
}
|
|
@ -70,4 +70,10 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, ui
|
|||
|
||||
uint8_t get_pagemap(const picopass_hdr_t *hdr);
|
||||
void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod);
|
||||
|
||||
void generate_single_key_block_inverted(const uint8_t startingKey[PICOPASS_BLOCK_SIZE], uint32_t index, uint8_t keyBlock[PICOPASS_BLOCK_SIZE]);
|
||||
void intToBinary(unsigned int num, char *binaryStr, int size);
|
||||
uint8_t binaryToHex(char *binaryStr);
|
||||
void convertToHexArray(unsigned int num, uint8_t *partialKey);
|
||||
void iClass_Recover(iclass_recover_req_t *msg);
|
||||
#endif
|
||||
|
|
|
@ -3842,6 +3842,304 @@ void picopass_elite_nextKey(uint8_t* key) {
|
|||
memcpy(key, key_state, 8);
|
||||
}
|
||||
|
||||
static int CmdHFiClassRecover(uint8_t key[8]) {
|
||||
|
||||
uint32_t payload_size = sizeof(iclass_recover_req_t);
|
||||
uint8_t aa2_standard_key[PICOPASS_BLOCK_SIZE] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90};
|
||||
iclass_recover_req_t *payload = calloc(1, payload_size);
|
||||
payload->req.use_raw = true;
|
||||
payload->req.use_elite = false;
|
||||
payload->req.use_credit_key = false;
|
||||
payload->req.use_replay = true;
|
||||
payload->req.send_reply = true;
|
||||
payload->req.do_auth = true;
|
||||
payload->req.shallow_mod = false;
|
||||
payload->req2.use_raw = false;
|
||||
payload->req2.use_elite = false;
|
||||
payload->req2.use_credit_key = true;
|
||||
payload->req2.use_replay = false;
|
||||
payload->req2.send_reply = true;
|
||||
payload->req2.do_auth = true;
|
||||
payload->req2.shallow_mod = false;
|
||||
memcpy(payload->req.key, key, 8);
|
||||
memcpy(payload->req2.key, aa2_standard_key, 8);
|
||||
|
||||
PrintAndLogEx(INFO, "Recover started...");
|
||||
|
||||
PacketResponseNG resp;
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_ICLASS_RECOVER, (uint8_t *)payload, payload_size);
|
||||
|
||||
if (WaitForResponseTimeout(CMD_HF_ICLASS_RECOVER, &resp, 2500) == 0) {
|
||||
PrintAndLogEx(WARNING, "command execute timeout");
|
||||
DropField();
|
||||
free(payload);
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status == PM3_SUCCESS) {
|
||||
PrintAndLogEx(SUCCESS, "iCLASS Recover " _GREEN_("successful"));
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "iCLASS Recover " _RED_("failed"));
|
||||
}
|
||||
|
||||
free(payload);
|
||||
return resp.status;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t start_index;
|
||||
uint32_t keycount;
|
||||
const uint8_t *startingKey;
|
||||
uint8_t (*keyBlock)[PICOPASS_BLOCK_SIZE];
|
||||
} ThreadData;
|
||||
|
||||
void *generate_key_blocks(void *arg) {
|
||||
ThreadData *data = (ThreadData *)arg;
|
||||
uint32_t start_index = data->start_index;
|
||||
uint32_t keycount = data->keycount;
|
||||
const uint8_t *startingKey = data->startingKey;
|
||||
uint8_t (*keyBlock)[PICOPASS_BLOCK_SIZE] = data->keyBlock;
|
||||
|
||||
for (uint32_t i = 0; i < keycount; i++) {
|
||||
uint32_t carry = start_index + i;
|
||||
memcpy(keyBlock[i], startingKey, PICOPASS_BLOCK_SIZE);
|
||||
|
||||
for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) {
|
||||
uint8_t increment_value = (carry & 0x1F) << 3; // Use only the first 5 bits of carry
|
||||
keyBlock[i][j] = (keyBlock[i][j] & 0x07) | increment_value; // Preserve the last three bits
|
||||
|
||||
carry >>= 5; // Shift right by 5 bits for the next byte
|
||||
if (carry == 0) {
|
||||
// If no more carry, break early to avoid unnecessary loops
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void generate_single_key_block_inverted(const uint8_t startingKey[PICOPASS_BLOCK_SIZE], uint32_t index, uint8_t keyBlock[PICOPASS_BLOCK_SIZE]) {
|
||||
uint32_t carry = index;
|
||||
memcpy(keyBlock, startingKey, PICOPASS_BLOCK_SIZE);
|
||||
|
||||
for (int j = PICOPASS_BLOCK_SIZE - 1; j >= 0; j--) {
|
||||
uint8_t increment_value = carry & 0x07; // Use only the last 3 bits of carry
|
||||
keyBlock[j] = increment_value; // Set the last 3 bits, assuming first 5 bits are always 0
|
||||
|
||||
carry >>= 3; // Shift right by 3 bits for the next byte
|
||||
if (carry == 0) {
|
||||
// If no more carry, break early to avoid unnecessary loops
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int CmdHFiClassLegRecLookUp(const char *Cmd) {
|
||||
|
||||
//Standalone Command Start
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf iclass legbrute",
|
||||
"This command take sniffed trace data and partial raw key and bruteforces the remaining 40 bits of the raw key.",
|
||||
"hf iclass legbrute --csn 8D7BD711FEFF12E0 --epurse feffffffffffffff --macs 00000000BD478F76 --pk B4F12AADC5301225"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1(NULL, "csn", "<hex>", "Specify CSN as 8 hex bytes"),
|
||||
arg_str1(NULL, "epurse", "<hex>", "Specify ePurse as 8 hex bytes"),
|
||||
arg_str1(NULL, "macs", "<hex>", "MACs"),
|
||||
arg_str1(NULL, "pk", "<hex>", "Partial Key"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
int csn_len = 0;
|
||||
uint8_t csn[8] = {0};
|
||||
CLIGetHexWithReturn(ctx, 1, csn, &csn_len);
|
||||
|
||||
if (csn_len > 0) {
|
||||
if (csn_len != 8) {
|
||||
PrintAndLogEx(ERR, "CSN is incorrect length");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
int epurse_len = 0;
|
||||
uint8_t epurse[8] = {0};
|
||||
CLIGetHexWithReturn(ctx, 2, epurse, &epurse_len);
|
||||
|
||||
if (epurse_len > 0) {
|
||||
if (epurse_len != 8) {
|
||||
PrintAndLogEx(ERR, "ePurse is incorrect length");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
int macs_len = 0;
|
||||
uint8_t macs[8] = {0};
|
||||
CLIGetHexWithReturn(ctx, 3, macs, &macs_len);
|
||||
|
||||
if (macs_len > 0) {
|
||||
if (macs_len != 8) {
|
||||
PrintAndLogEx(ERR, "MAC is incorrect length");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
int startingkey_len = 0;
|
||||
uint8_t startingKey[8] = {0};
|
||||
CLIGetHexWithReturn(ctx, 4, startingKey, &startingkey_len);
|
||||
|
||||
if (startingkey_len > 0) {
|
||||
if (startingkey_len != 8) {
|
||||
PrintAndLogEx(ERR, "Partial Key is incorrect length");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
CLIParserFree(ctx);
|
||||
//Standalone Command End
|
||||
|
||||
uint8_t CCNR[12];
|
||||
uint8_t MAC_TAG[4] = {0, 0, 0, 0};
|
||||
|
||||
// Copy CCNR and MAC_TAG
|
||||
memcpy(CCNR, epurse, 8);
|
||||
memcpy(CCNR + 8, macs, 4);
|
||||
memcpy(MAC_TAG, macs + 4, 4);
|
||||
|
||||
PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, 8));
|
||||
PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, 8));
|
||||
PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, 8));
|
||||
PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR)));
|
||||
PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG)));
|
||||
PrintAndLogEx(SUCCESS, "Starting Key: %s", sprint_hex(startingKey, 8));
|
||||
|
||||
uint32_t keycount = 1000000;
|
||||
uint32_t keys_per_thread = 200000;
|
||||
uint32_t num_threads = keycount / keys_per_thread;
|
||||
pthread_t threads[num_threads];
|
||||
ThreadData thread_data[num_threads];
|
||||
iclass_prekey_t *prekey = NULL;
|
||||
iclass_prekey_t lookup;
|
||||
iclass_prekey_t *item = NULL;
|
||||
|
||||
memcpy(lookup.mac, MAC_TAG, 4);
|
||||
|
||||
uint32_t block_index = 0;
|
||||
|
||||
while (item == NULL) {
|
||||
for (uint32_t t = 0; t < num_threads; t++) {
|
||||
thread_data[t].start_index = block_index * keycount + t * keys_per_thread;
|
||||
thread_data[t].keycount = keys_per_thread;
|
||||
thread_data[t].startingKey = startingKey;
|
||||
thread_data[t].keyBlock = calloc(keys_per_thread, PICOPASS_BLOCK_SIZE);
|
||||
|
||||
if (thread_data[t].keyBlock == NULL) {
|
||||
PrintAndLogEx(ERROR, "Memory allocation failed for keyBlock in thread %d.", t);
|
||||
for (uint32_t i = 0; i < t; i++) {
|
||||
free(thread_data[i].keyBlock);
|
||||
}
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
pthread_create(&threads[t], NULL, generate_key_blocks, (void *)&thread_data[t]);
|
||||
}
|
||||
|
||||
for (uint32_t t = 0; t < num_threads; t++) {
|
||||
pthread_join(threads[t], NULL);
|
||||
}
|
||||
|
||||
if (prekey == NULL) {
|
||||
prekey = calloc(keycount, sizeof(iclass_prekey_t));
|
||||
} else {
|
||||
prekey = realloc(prekey, (block_index + 1) * keycount * sizeof(iclass_prekey_t));
|
||||
}
|
||||
|
||||
if (prekey == NULL) {
|
||||
PrintAndLogEx(ERROR, "Memory allocation failed for prekey.");
|
||||
for (uint32_t t = 0; t < num_threads; t++) {
|
||||
free(thread_data[t].keyBlock);
|
||||
}
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Generating diversified keys...");
|
||||
for (uint32_t t = 0; t < num_threads; t++) {
|
||||
GenerateMacKeyFrom(csn, CCNR, true, false, (uint8_t *)thread_data[t].keyBlock, keys_per_thread, prekey + (block_index * keycount) + (t * keys_per_thread));
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Sorting...");
|
||||
|
||||
// Sort mac list
|
||||
qsort(prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32);
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT");
|
||||
|
||||
// Binary search
|
||||
item = (iclass_prekey_t *)bsearch(&lookup, prekey, (block_index + 1) * keycount, sizeof(iclass_prekey_t), cmp_uint32);
|
||||
|
||||
for (uint32_t t = 0; t < num_threads; t++) {
|
||||
free(thread_data[t].keyBlock);
|
||||
}
|
||||
|
||||
block_index++;
|
||||
}
|
||||
|
||||
if (item != NULL) {
|
||||
PrintAndLogEx(SUCCESS, "Found valid RAW key " _GREEN_("%s"), sprint_hex(item->key, 8));
|
||||
}
|
||||
|
||||
free(prekey);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int CmdHFiClassLegacyRecover(const char *Cmd) {
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf iclass legrec",
|
||||
"Attempts to recover the diversified key of a specific iClass card. This may take a long time. The Card must remain be on the PM3 antenna during the whole process! This process may brick the card!",
|
||||
"hf iclass legrec --macs 0000000089cb984b"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1(NULL, "macs", "<hex>", "MACs"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
|
||||
int macs_len = 0;
|
||||
uint8_t macs[8] = {0};
|
||||
CLIGetHexWithReturn(ctx, 1, macs, &macs_len);
|
||||
|
||||
if (macs_len > 0) {
|
||||
if (macs_len != 8) {
|
||||
PrintAndLogEx(ERR, "MAC is incorrect length");
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
CmdHFiClassRecover(macs);
|
||||
|
||||
PrintAndLogEx(WARNING, _YELLOW_("If the process completed, you can now run 'hf iclass legrecbrute' with the partial key found."));
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
static int CmdHFiClassLookUp(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf iclass lookup",
|
||||
|
@ -4738,6 +5036,8 @@ static command_t CommandTable[] = {
|
|||
{"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"},
|
||||
{"loclass", CmdHFiClass_loclass, AlwaysAvailable, "Use loclass to perform bruteforce reader attack"},
|
||||
{"lookup", CmdHFiClassLookUp, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"},
|
||||
{"legrec", CmdHFiClassLegacyRecover, IfPm3Iclass, "Attempts to recover the standard key of a legacy card"},
|
||||
{"legbrute", CmdHFiClassLegRecLookUp, AlwaysAvailable, "Bruteforces 40 bits of a partial raw key"},
|
||||
{"-----------", CmdHelp, IfPm3Iclass, "-------------------- " _CYAN_("Simulation") " -------------------"},
|
||||
{"sim", CmdHFiClassSim, IfPm3Iclass, "Simulate iCLASS tag"},
|
||||
{"eload", CmdHFiClassELoad, IfPm3Iclass, "Upload file into emulator memory"},
|
||||
|
|
|
@ -42,4 +42,7 @@ void picopass_elite_reset(void);
|
|||
uint32_t picopass_elite_rng(void);
|
||||
uint32_t picopass_elite_lcg(void);
|
||||
uint8_t picopass_elite_nextByte(void);
|
||||
|
||||
void *generate_key_blocks(void *arg);
|
||||
void generate_single_key_block_inverted(const uint8_t startingKey[PICOPASS_BLOCK_SIZE], uint32_t index, uint8_t keyBlock[PICOPASS_BLOCK_SIZE]);
|
||||
#endif
|
||||
|
|
|
@ -630,6 +630,7 @@ typedef struct {
|
|||
#define CMD_HF_ICLASS_CHKKEYS 0x039A
|
||||
#define CMD_HF_ICLASS_RESTORE 0x039B
|
||||
#define CMD_HF_ICLASS_CREDIT_EPURSE 0x039C
|
||||
#define CMD_HF_ICLASS_RECOVER 0x039D
|
||||
|
||||
|
||||
// For ISO1092 / FeliCa
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue