From 838d224cd29c7166ad358e4cda910f0a0a0d6c5a Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Sun, 14 Jan 2024 18:22:55 +0100 Subject: [PATCH] added a hf xerox view command, it now also prints some more part info and fixed a bug with 255 != 256 --- CHANGELOG.md | 2 + client/src/cmdhfxerox.c | 246 +++++++++++++++++++++++++++++++++------- 2 files changed, 204 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43d24979f..0e08901b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Changed `hf xerox info` - now prints some part info (@iceman1001) + - Added `hf xerox view` - view dump files of fuji/xerox tags (@iceman1001) - Changed `hf 15 findafi` - improved the params (@iceman1001) - Changed `hf 15 rdbl/rdmulti/dump` - should handle 4 vs 8 bytes block sizes in cards (@iceman1001) - Changed `hf 15 *` - all 15 commands now uses NG packets (@iceman1001) diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c index 88f910dfa..43bbe5008 100644 --- a/client/src/cmdhfxerox.c +++ b/client/src/cmdhfxerox.c @@ -6,14 +6,15 @@ #include "fileutils.h" -#include "cmdparser.h" // command_t +#include "cmdparser.h" // command_t #include "cliparser.h" #include "comms.h" #include "iso14b.h" #include "crc16.h" +#include "commonutil.h" // ARRAYLEN #define TIMEOUT 2000 - +#define XEROX_BLOCK_SIZE 4 #define c2l(c,l) (l = ((unsigned long)(*((c)++))), \ l |= ((unsigned long)(*((c)++))) << 8L, \ @@ -413,7 +414,7 @@ static int findXerox(iso14b_card_select_t *card, bool disconnect) { } static uint8_t info_blocks[] = { 0x15, 0x16, 0x17, 0x18, 0x22 }; -static const char *c_type[] = { "drum", "yellow", "magenta", "cyan", "black" }; +static const char *xerox_c_type[] = { "drum", "yellow", "magenta", "cyan", "black" }; static inline char dec_digit(uint8_t dig) { return (dig <= 9) ? dig + '0' : '?'; @@ -438,6 +439,89 @@ static void gen_pn(const uint8_t *data, char *pn) { pn[12] = 0; } +static void xerox_print_hdr(void) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "block# | data | ascii"); + PrintAndLogEx(INFO, "---------+--------------+----------"); +} + +static void xerox_print(uint8_t *data, uint16_t datalen) { + + uint16_t blockno = datalen / XEROX_BLOCK_SIZE; + + for (int i = 0; i < blockno; i++) { + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s", + i, + i, + sprint_hex(data + (i * XEROX_BLOCK_SIZE), XEROX_BLOCK_SIZE), + sprint_ascii(data + (i * XEROX_BLOCK_SIZE), XEROX_BLOCK_SIZE) + ); + } +} + +static void xerox_print_footer(void) { + PrintAndLogEx(INFO, "---------+--------------+----------"); + PrintAndLogEx(NORMAL, ""); +} + + +// structure and database for uid -> tagtype lookups +typedef struct { + const char *color; + const char *partnumber; + const char *region; + const char *ms; +} xerox_part_t; + +// https://gist.github.com/JeroenSteen/4b45886b8d87fa0530af9b0364e6b277 +static const xerox_part_t xerox_part_mappings[] = { + {"cyan", "006R01532" , "DMO", "sold"}, + {"cyan", "006R01660", "DMO", "sold"}, + {"cyan", "006R01739", "DMO", "sold"}, + {"cyan", "006R01524", "WW", "metered"}, + {"cyan", "006R01528", "NA/ESG", "sold"}, + {"cyan", "006R01656", "NA/ESG", "sold"}, + {"cyan", "006R01735", "NA/ESG", "sold"}, + + {"magenta", "006R01531", "DMO", "sold"}, + {"magenta", "006R01661", "DMO", "sold"}, + {"magenta", "006R01740", "DMO", "sold"}, + {"magenta", "006R01523", "WW", "metered"}, + {"magenta", "006R01527", "NA/ESG", "sold"}, + {"magenta", "006R01657", "NA/ESG", "sold"}, + {"magenta", "006R01736", "NA/ESG", "sold"}, + + {"yellow", "006R01530", "DMO", "sold"}, + {"yellow", "006R01662", "DMO", "sold"}, + {"yellow", "006R01741", "DMO", "sold"}, + {"yellow", "006R01522", "WW", "metered"}, + {"yellow", "006R01526", "NA/ESG", "sold"}, + {"yellow", "006R01658", "NA/ESG", "sold"}, + {"yellow", "006R01737", "NA/ESG", "sold"}, + + {"black", "006R01529", "DMO", "sold"}, + {"black", "006R01659", "DMO", "sold"}, + {"black", "006R01738", "DMO", "sold"}, + {"black", "006R01521", "WW", "metered"}, + {"black", "006R01525", "NA/ESG", "sold"}, + {"black", "006R01655", "NA/ESG", "sold"}, + {"black", "006R01734", "NA/ESG", "sold"}, + {"", "", "", ""} // must be the last entry +}; + +// get a product description based on the UID +// returns description of the best match +static const xerox_part_t *get_xerox_part_info(const char* pn) { + for (int i = 0; i < ARRAYLEN(xerox_part_mappings); ++i) { + if (strcmp(pn, xerox_part_mappings[i].partnumber) == 0) { + return &xerox_part_mappings[i]; + } + } + //No match, return default + return &xerox_part_mappings[ARRAYLEN(xerox_part_mappings) - 1]; +} + int read_xerox_uid(bool loop, bool verbose) { do { @@ -468,7 +552,8 @@ static int CmdHFXeroxReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf xerox reader", - "Act as a 14443B reader to identify a tag", + "Act as a 14443B reader to identify a Fuji Xerox based tag\n" + "ISO/IEC 14443 type B based communications", "hf xerox reader\n" "hf xerox reader -@ \n" ); @@ -494,7 +579,8 @@ static int CmdHFXeroxReader(const char *Cmd) { static int CmdHFXeroxInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf xerox info", - "Tag information for ISO/IEC 14443 type B / XEROX based tags", + "Tag information for Fuji Xerox based tags\n" + "ISO/IEC 14443 type B based communications", "hf xerox info" ); @@ -527,7 +613,7 @@ static int CmdHFXeroxInfo(const char *Cmd) { return PM3_EMALLOC; } - int blocknum = 0; + int blockno = 0; uint8_t data[sizeof(info_blocks) * 4] = {0}; // set up the read command @@ -537,9 +623,9 @@ static int CmdHFXeroxInfo(const char *Cmd) { packet->raw[1] = 0x20; // set command: read mem memcpy(packet->raw + 2, card.uid, 8); // store uid - for (int retry = 0; (retry < 5 && blocknum < sizeof(info_blocks)); retry++) { + for (int retry = 0; (retry < 3 && blockno < sizeof(info_blocks)); retry++) { - packet->raw[10] = info_blocks[blocknum]; + packet->raw[10] = info_blocks[blockno]; PacketResponseNG resp; clearCommandBuffer(); @@ -557,47 +643,56 @@ static int CmdHFXeroxInfo(const char *Cmd) { continue; } - uint8_t *recv = resp.data.asBytes; + uint8_t *d = resp.data.asBytes; - if (check_crc(CRC_14443_B, recv, 7) == false) { + if (check_crc(CRC_14443_B, d, 7) == false) { PrintAndLogEx(FAILED, "crc fail, retrying one more time"); continue; } - if (recv[0] != 2) { - PrintAndLogEx(FAILED, "Tag returned Error %x %x", recv[0], recv[1]); + if (d[0] != 2) { + PrintAndLogEx(FAILED, "Tag returned Error %x %x", d[0], d[1]); break; } - memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + memcpy(data + (blockno * 4), d + 1, 4); retry = 0; - blocknum++; + blockno++; } } switch_off_field(); free(packet); - if (blocknum != sizeof(info_blocks)) { + if (blockno != sizeof(info_blocks)) { PrintAndLogEx(FAILED, "Fuji/Xerox tag read failed"); return PM3_ERFTRANS; } char pn[13]; gen_pn(data, pn); - PrintAndLogEx(SUCCESS, " PartNo : %s", pn); - PrintAndLogEx(SUCCESS, " Date : %02d.%02d.%02d", data[8], data[9], data[10]); - PrintAndLogEx(SUCCESS, " Serial : %d", (data[14] << 16) | (data[13] << 8) | data[12]); - PrintAndLogEx(SUCCESS, " Type : %s", (data[18] <= 4) ? c_type[data[18]] : "Unknown"); + PrintAndLogEx(INFO, "-------- " _CYAN_("tag memory") " ---------"); + PrintAndLogEx(SUCCESS, " PartNo... %s", pn); + PrintAndLogEx(SUCCESS, " Date..... %02d.%02d.%02d", data[8], data[9], data[10]); + PrintAndLogEx(SUCCESS, " Serial... %d", (data[14] << 16) | (data[13] << 8) | data[12]); + PrintAndLogEx(SUCCESS, " Type..... %s", (data[18] <= 4) ? xerox_c_type[data[18]] : "Unknown"); + const xerox_part_t *item = get_xerox_part_info(pn); + if (strlen(item->partnumber) > 0) { + PrintAndLogEx(SUCCESS, "Color..... %s", item->color); + PrintAndLogEx(SUCCESS, "Region.... %s", item->region); + PrintAndLogEx(SUCCESS, "M/s....... %s", item->ms); + } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } static int CmdHFXeroxDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf xerox dump", - "Dump all memory from a Fuji/Xerox tag", + "Dump all memory from a Fuji/Xerox tag\n" + "ISO/IEC 14443 type B based communications", "hf xerox dump\n" ); @@ -605,6 +700,7 @@ static int CmdHFXeroxDump(const char *Cmd) { arg_param_begin, arg_str0("f", "file", "", "filename to save dump to"), arg_lit0("d", "decrypt", "decrypt secret blocks"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -613,6 +709,7 @@ static int CmdHFXeroxDump(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool decrypt = arg_get_lit(ctx, 2); + bool nosave = arg_get_lit(ctx, 3); CLIParserFree(ctx); iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); @@ -631,7 +728,7 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(INFO, "Reading memory from tag UID " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); - int blocknum = 1; // block 0 all zeros + int blockno = 1; // block 0 all zeros uint8_t data[256 * 4] = {0}; // set up the read command @@ -642,10 +739,10 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(INFO, "." NOLF); - for (int retry = 0; (retry < 5 && blocknum < 0x100); retry++) { + for (int retry = 0; (retry < 3 && blockno < 0x100); retry++) { - packet->raw[1] = (blocknum < 12) ? 0x30 : 0x20; // set command: read ext mem or read mem - packet->raw[10] = blocknum & 0xFF; + packet->raw[1] = (blockno < 12) ? 0x30 : 0x20; // set command: read ext mem or read mem + packet->raw[10] = blockno & 0xFF; PacketResponseNG resp; clearCommandBuffer(); @@ -675,10 +772,10 @@ static int CmdHFXeroxDump(const char *Cmd) { break; } - memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + memcpy(data + (blockno * 4), resp.data.asBytes + 1, 4); retry = 0; - blocknum++; + blockno++; PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); @@ -691,8 +788,8 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(NORMAL, ""); - if (blocknum != 0x100) { - PrintAndLogEx(FAILED, "dump failed at block %d", blocknum); + if (blockno != 0x100) { + PrintAndLogEx(FAILED, "dump failed at block " _RED_("%d"), blockno); } if (decrypt) { @@ -731,7 +828,7 @@ static int CmdHFXeroxDump(const char *Cmd) { uint8_t dadr = var_list[n]; - if (dadr + 1 >= blocknum) { + if (dadr + 1 >= blockno) { PrintAndLogEx(INFO, "secret block %02X skipped.", dadr); continue; } @@ -750,6 +847,7 @@ static int CmdHFXeroxDump(const char *Cmd) { for (b = 0, cs = 0; b < sizeof(decr) - 2; b += 2) { cs += decr[b] | (decr[b + 1] << 8); } + cs = ~cs; csd = (decr[7] << 8) | decr[6]; @@ -759,37 +857,97 @@ static int CmdHFXeroxDump(const char *Cmd) { } } - PrintAndLogEx(INFO, "block# | data | ascii"); - PrintAndLogEx(INFO, "---------+--------------+----------"); + xerox_print_hdr(); + xerox_print(data, blockno * XEROX_BLOCK_SIZE); + xerox_print_footer(); - for (int i = 0; i < blocknum; i++) { - PrintAndLogEx(INFO, - "%3d/0x%02X | %s | %s", - i, - i, - sprint_hex(data + (i * 4), 4), - sprint_ascii(data + (i * 4), 4) - ); + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } - PrintAndLogEx(INFO, "---------+--------------+----------"); - PrintAndLogEx(NORMAL, ""); if (0 == filename[0]) { // generate filename from uid char *fptr = filename; PrintAndLogEx(INFO, "Using UID as filename"); fptr += snprintf(fptr, sizeof(filename), "hf-xerox-"); - FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), decrypt ? "-dump-dec" : "-dump", card.uidlen); + FillFileNameByUID(fptr + , SwapEndian64(card.uid, card.uidlen, 8) + , (decrypt) ? "-dump-dec" : "-dump" + , card.uidlen + ); } - pm3_save_dump(filename, data, blocknum * 4, jsf14b_v2); + pm3_save_dump(filename, data, blockno * XEROX_BLOCK_SIZE, jsf14b_v2); + return PM3_SUCCESS; +} + +static int CmdHFXeroxView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf xerox view", + "Print a Fuji/Xerox dump file (bin/eml/json)\n" + "note:\n" + " - command expects the filename to contain a UID\n" + " which is needed to determine card memory type", + "hf xerox view -f hf-xerox-0102030405060708-dump.bin" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + // read dump file + uint8_t *dump = NULL; + size_t bytes_read = (XEROX_BLOCK_SIZE * 0x100); + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (XEROX_BLOCK_SIZE * 0x100)); + if (res != PM3_SUCCESS) { + return res; + } + + uint16_t blockno = bytes_read / XEROX_BLOCK_SIZE; + + if (verbose) { + PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, blockno, blockno); + } + + char pn[13]; + gen_pn(dump, pn); + + PrintAndLogEx(INFO, "-------- " _CYAN_("tag memory") " ---------"); + PrintAndLogEx(SUCCESS, " PartNo... %s", pn); + PrintAndLogEx(SUCCESS, " Date..... %02d.%02d.%02d", dump[8], dump[9], dump[10]); + PrintAndLogEx(SUCCESS, " Serial... %d", (dump[14] << 16) | (dump[13] << 8) | dump[12]); + PrintAndLogEx(SUCCESS, " Type..... %s", (dump[18] <= 4) ? xerox_c_type[dump[18]] : "Unknown"); + + const xerox_part_t *item = get_xerox_part_info(pn); + if (strlen(item->partnumber) > 0) { + PrintAndLogEx(SUCCESS, "Color..... %s", item->color); + PrintAndLogEx(SUCCESS, "Region.... %s", item->region); + PrintAndLogEx(SUCCESS, "M/s....... %s", item->ms); + } + xerox_print_hdr(); + xerox_print(dump, bytes_read); + xerox_print_footer(); + + free(dump); return PM3_SUCCESS; } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Short info on Fuji/Xerox tag"}, - {"reader", CmdHFXeroxReader, IfPm3Iso14443b, "Act like a Fuji/Xerox reader"}, {"dump", CmdHFXeroxDump, IfPm3Iso14443b, "Read all memory pages of an Fuji/Xerox tag, save to file"}, + {"reader", CmdHFXeroxReader, IfPm3Iso14443b, "Act like a Fuji/Xerox reader"}, + {"view", CmdHFXeroxView, AlwaysAvailable, "Display content from tag dump file"}, // {"rdbl", CmdHFXeroxRdBl, IfPm3Iso14443b, "Read Fuji/Xerox block"}, // {"wrbl", CmdHFXeroxWrBl, IfPm3Iso14443b, "Write Fuji/Xerox block"}, {NULL, NULL, NULL, NULL}