added a hf xerox view command, it now also prints some more part info and fixed a bug with 255 != 256

This commit is contained in:
iceman1001 2024-01-14 18:22:55 +01:00
commit 838d224cd2
2 changed files with 204 additions and 44 deletions

View file

@ -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)

View file

@ -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", "<fn>", "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", "<fn>", "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}