diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index d3a8932a5b..88e7b91b51 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -357,6 +357,8 @@ static int CmdLFHitagSRead(const char *Cmd) { // access right if (page_addr == HITAGS_UID_PADR) { + PrintAndLogEx(NORMAL, _RED_("RO ")NOLF);\ + } else if (packet.cmd == HTSF_82xx && page_addr > 40) { // using an 82xx (pages>40 are RO) PrintAndLogEx(NORMAL, _RED_("RO ")NOLF); } else if (page_addr == HITAGS_CONFIG_PADR) { if (card->config_page.s.LCON) @@ -454,6 +456,7 @@ static int CmdLFHitagSDump(const char *Cmd) { " - default key 4F4E4D494B52 (ONMIKR)\n\n" " 8268/8310 password mode: \n" " - default password BBDD3399\n", + "lf hitag hts dump --82xx -> use def pwd\n" "lf hitag hts dump --82xx -k BBDD3399 -> pwd mode\n" "lf hitag hts dump --crypto -> use def crypto\n" "lf hitag hts dump -k 4F4E4D494B52 -> crypto mode\n" @@ -534,6 +537,225 @@ static int CmdLFHitagSDump(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLFHitagSRestore(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag hts restore", + "Restore a dump file onto Hitag S tag\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n\n" + " 8268/8310 password mode: \n" + " - default password BBDD3399\n", + "lf hitag hts restore -f myfile --82xx -> use def pwd\n" + "lf hitag hts restore -f myfile --82xx -k BBDD3399 -> pwd mode\n" + "lf hitag hts restore -f myfile --crypto -> use def crypto\n" + "lf hitag hts restore -f myfile -k 4F4E4D494B52 -> crypto mode\n" + "lf hitag hts restore -f myfile --nrar 0102030411223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("8", "82xx", "8268/8310 mode"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), + arg_str0("f", "file", "", "specify file name"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + if (process_hitags_common_args(ctx, &packet) < 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + if (fnlen == 0) { + PrintAndLogEx(ERR, "Must specify a file"); + return PM3_EINVARG; + } + + // read dump file + uint32_t *dump = NULL; + size_t bytes_read = 0; + if (pm3_load_dump(filename, (void **)&dump, &bytes_read, jsfHitag) != PM3_SUCCESS) { + return PM3_EFILE; + } + + // read config to determine memory size and other stuff + packet.page = HITAGS_CONFIG_PADR; + packet.page_count = 1; + + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_READ, (uint8_t *)&packet, sizeof(packet)); + + PacketResponseNG resp; + + if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + free(dump); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + print_error(resp.reason); + free(dump); + return PM3_ESOFT; + } + + lf_hts_read_response_t *config = (lf_hts_read_response_t *)resp.data.asBytes; + hitags_config_t tag_config = config->config_page.s; + + const int hts_mem_sizes[] = {1, 8, 64, 64}; + int mem_size = hts_mem_sizes[tag_config.MEMT] * HITAGS_PAGE_SIZE; + + if (bytes_read != mem_size) { + free(dump); + PrintAndLogEx(FAILED, "Wrong length of dump file. Expected %d bytes, got %zu", mem_size, bytes_read); + return PM3_EFILE; + } + + uint8_t* dump_bytes = (uint8_t*)dump; + bool auth_changed = false; + + for (int page = packet.page_count + 1; page < hts_mem_sizes[tag_config.MEMT]; page++) { // skip config page + + if (packet.cmd == HTSF_82xx && page > 40) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Using " _YELLOW_("82xx") ", Pages " _YELLOW_("41-63") " will be skipped"); + PrintAndLogEx(NORMAL, ""); + break; + } + + size_t offset = page * HITAGS_PAGE_SIZE; + + packet.page = page; + memcpy(packet.data, &dump_bytes[offset], HITAGS_PAGE_SIZE); + + PrintAndLogEx(INPLACE, " Writing page "_YELLOW_("%d")", data: " _GREEN_("%02X %02X %02X %02X"), page, + dump_bytes[offset], + dump_bytes[offset + 1], + dump_bytes[offset + 2], + dump_bytes[offset + 3]); + + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); + + if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + free(dump); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Write failed for page %d", page); + print_error(resp.reason); + free(dump); + return PM3_ESOFT; + } + + switch (page) { + case 2: // auth first page + if (packet.cmd == HTSF_82xx) { + if (memcmp(packet.pwd, &dump_bytes[offset], HITAGS_PAGE_SIZE) == 0) { + break; + } + auth_changed = true; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "Password Changed! Old: " _BACK_BLUE_("%02X %02X %02X %02X") ", New: "_BACK_BLUE_("%02X %02X %02X %02X"), + packet.pwd[0], packet.pwd[1], packet.pwd[2], packet.pwd[3], + dump_bytes[offset], dump_bytes[offset + 1], + dump_bytes[offset + 2], dump_bytes[offset + 3]); + + + memcpy(packet.pwd, &dump_bytes[offset], HITAG_PASSWORD_SIZE); + + + PrintAndLogEx(SUCCESS, "Using new password for subsequent writes"); + } + break; + case 3: // crypto mode + if (packet.cmd == HTSF_KEY) { + + if (memcmp(packet.key, &dump_bytes[offset - HITAGS_PAGE_SIZE], HITAG_CRYPTOKEY_SIZE) == 0) { + break; + } + auth_changed = true; + + memcpy(packet.key, &dump_bytes[offset - HITAGS_PAGE_SIZE], HITAG_CRYPTOKEY_SIZE); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "New key detected: " _BACK_BLUE_("%02X %02X %02X %02X %02X %02X"), + packet.key[0], packet.key[1], packet.key[2], + packet.key[3], packet.key[4], packet.key[5]); + + PrintAndLogEx(SUCCESS, "Using new key for subsequent writes"); + } + break; + } + } + + // restore config page at end + size_t config_offset = HITAGS_PAGE_SIZE * 1; // page 1 + packet.page = HITAGS_CONFIG_PADR; + memcpy(packet.data, &dump_bytes[HITAGS_PAGE_SIZE], HITAGS_PAGE_SIZE); + + + PrintAndLogEx(SUCCESS, "Applying "_YELLOW_("restored config: ") _GREEN_("%02X %02X %02X %02X"), + dump_bytes[config_offset], + dump_bytes[config_offset + 1], + dump_bytes[config_offset + 2], + dump_bytes[config_offset + 3]); + + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); + + if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + free(dump); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Failed to apply config"); + print_error(resp.reason); + free(dump); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Write process completed"); + + if (auth_changed) { + if (packet.cmd == HTSF_82xx) { + PrintAndLogEx(SUCCESS, "New Password: " _BACK_BLUE_("%02X %02X %02X %02X"), + packet.pwd[0], packet.pwd[1], packet.pwd[2], packet.pwd[3]); + } else if (packet.cmd == HTSF_KEY) { + PrintAndLogEx(SUCCESS, "New Key: " _BACK_BLUE_("%02X %02X %02X %02X %02X %02X"), + packet.key[0], packet.key[1], packet.key[2], + packet.key[3], packet.key[4], packet.key[5]); + } + } + + return PM3_SUCCESS; +} + static int CmdLFHitagSWrite(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag hts wrbl", @@ -544,6 +766,7 @@ static int CmdLFHitagSWrite(const char *Cmd) { " 8268/8310 password mode: \n" " - default password BBDD3399\n", " lf hitag hts wrbl -p 6 -d 01020304 -> Hitag S/8211, plain mode\n" + " lf hitag hts wrbl -p 6 -d 01020304 --82xx -> use def pwd\n" " lf hitag hts wrbl -p 6 -d 01020304 --82xx -k BBDD3399 -> 8268/8310, password mode\n" " lf hitag hts wrbl -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag S, challenge mode\n" " lf hitag hts wrbl -p 6 -d 01020304 --crypto -> Hitag S, crypto mode, default key\n" @@ -705,7 +928,8 @@ static command_t CommandTable[] = { {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("General") " ------------------------"}, {"reader", CmdLFHitagSReader, IfPm3Hitag, "Act like a Hitag S reader"}, {"rdbl", CmdLFHitagSRead, IfPm3Hitag, "Read Hitag S page"}, - {"dump", CmdLFHitagSDump, IfPm3Hitag, "Dump Hitag S pages to a file"}, + {"dump", CmdLFHitagSDump, IfPm3Hitag, "Dump Hitag S pages to a file"}, + {"restore", CmdLFHitagSRestore,IfPm3Hitag, "Restore Hitag S memory from dump file"}, {"wrbl", CmdLFHitagSWrite, IfPm3Hitag, "Write Hitag S page"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"}, {"sim", CmdLFHitagSSim, IfPm3Hitag, "Simulate Hitag S transponder"}, diff --git a/doc/commands.json b/doc/commands.json index 0bfc7059df..0bc3bfc4a8 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -9838,6 +9838,27 @@ ], "usage": "lf hitag hts dump [-h8] [--nrar ] [--crypto] [-k ] [-m ] [-f ] [--ns]" }, + "lf hitag hts restore": { + "command": "lf hitag hts restore", + "description": "Restore a dump file onto Hitag S tag Crypto mode: - key format ISK high + ISK low - default key 4F4E4D494B52 (ONMIKR) 8268/8310 password mode: - default password BBDD3399", + "notes": [ + "lf hitag hts restore -f myfile --82xx -k BBDD3399 -> pwd mode", + "lf hitag hts restore -f myfile --crypto -> use def crypto", + "lf hitag hts restore -f myfile -k 4F4E4D494B52 -> crypto mode", + "lf hitag hts restore -f myfile --nrar 0102030411223344" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-8, --82xx 8268/8310 mode", + "--nrar nonce / answer writer, 8 hex bytes", + "--crypto crypto mode", + "-k, --key pwd or key, 4 or 6 hex bytes", + "-m, --mode response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)", + "-f, --file specify file name" + ], + "usage": "lf hitag hts restore [-h8] [--nrar ] [--crypto] [-k ] [-m ] [-f ]" + }, "lf hitag hts help": { "command": "lf hitag hts help", "description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hitags` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", diff --git a/doc/commands.md b/doc/commands.md index 9cd60a66a8..139eb9e3b1 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -1084,6 +1084,7 @@ Check column "offline" for their availability. |`lf hitag hts rdbl `|N |`Read Hitag S page` |`lf hitag hts dump `|N |`Dump Hitag S pages to a file` |`lf hitag hts wrbl `|N |`Write Hitag S page` +|`lf hitag hts restore `|N |`Restore Hitag S memory from a dump file` |`lf hitag hts sim `|N |`Simulate Hitag S transponder`