From 6d699d66bc92e881714fb8cdf1b9cb44f0805f0c Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Thu, 30 Jun 2022 22:50:52 +0200 Subject: [PATCH] adding support to read fuji/xerox tags, thanks to user Horror --- CHANGELOG.md | 3 + armsrc/iso14443b.c | 282 ++++++++++-- armsrc/iso14443b.h | 1 + client/Makefile | 1 + client/src/cmdhf.c | 2 + client/src/cmdhf14b.c | 22 +- client/src/cmdhfxerox.c | 744 ++++++++++++++++++++++++++++++++ client/src/cmdhfxerox.h | 12 + client/src/pm3line_vocabulory.h | 5 + doc/commands.json | 48 ++- doc/commands.md | 11 + include/iso14b.h | 1 + 12 files changed, 1083 insertions(+), 49 deletions(-) create mode 100644 client/src/cmdhfxerox.c create mode 100644 client/src/cmdhfxerox.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6a799e9..9aa7ff298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ 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 14b raw` - now supports selecting Fuji/Xerox tag (@horror) + - Added `hf xerox dump` - dump a Fuji/Xerox tag (@horror) + - Added `hf xerox info` - read Fuji/Xerox tag (@horror) - Fixed `hf 14a reader --ecp` to work consistently (@kormax) - Change `trace list -t 14a` to annotate ECP frames of all valid V1 and V2 formats (@kormax) - Changed `hf mf staticnested` - significant speedups by using two encrypted nonces (@xianglin1998) diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 655ed367e..0372189a5 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1381,11 +1381,11 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t } if (Demod.len > 0) { - uint32_t sof_time = *eof_time - - (Demod.len * (8 + 2)) // time for byte transfers - - (10) // time for TR1 - - (10 + 2) // time for SOF transfer - - (10); // time for EOF transfer + uint32_t sof_time = *eof_time - ETU_TO_SSP( + (Demod.len * (8 + 2)) // time for byte transfers +// + (10) // time for TR1 + + (10 + 2) // time for SOF transfer + + (10)); // time for EOF transfer LogTrace(Demod.output, Demod.len, sof_time, *eof_time, NULL, false); } return Demod.len; @@ -1435,9 +1435,28 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { } WDT_HIT(); } + + // transmit remaining bits. we need one-sample granularity now + + volatile uint8_t data = ts->buf[ts->max], last_bits = ts->bit; + + for (uint8_t i = 0; i < last_bits; i++) { + volatile uint16_t send_word = (data & 0x80) ? 0x0000 : 0xFFFF; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + + data <<= 1; + } + WDT_HIT(); + + LED_B_OFF(); - *start_time += DELAY_ARM_TO_TAG; +// *start_time += DELAY_ARM_TO_TAG; // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) {}; @@ -1447,7 +1466,7 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { // Code a layer 2 command (string of octets, including CRC) into ToSend[], // so that it is ready to transmit to the tag using TransmitFor14443b(). //----------------------------------------------------------------------------- -static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { +static void CodeIso14443bAsReader(const uint8_t *cmd, int len, bool framing) { /* * QUESTION: how long is a 1 or 0 in pulses in the xcorr_848 mode? * 1 "stuffbit" = 1ETU (9us) @@ -1460,14 +1479,18 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { int i; tosend_reset(); - // Send SOF - // 10-11 ETUs of ZERO - for (i = 0; i < 10; i++) { - tosend_stuffbit(0); + // add framing enable flag. xerox chips use unframed commands during anticollision + + if (framing) { + // Send SOF + // 10-11 ETUs of ZERO + for (i = 0; i < 10; i++) { + tosend_stuffbit(0); + } + // 2-3 ETUs of ONE + tosend_stuffbit(1); + tosend_stuffbit(1); } - // 2-3 ETUs of ONE - tosend_stuffbit(1); - tosend_stuffbit(1); // Sending cmd, LSB // from here we add BITS @@ -1493,28 +1516,36 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { // FOR PICC it ranges 0-19us == 0 - 2 ETU } - // Send EOF - // 10-11 ETUs of ZERO - for (i = 0; i < 10; i++) { - tosend_stuffbit(0); + if (framing) { + // Send EOF + // 10-11 ETUs of ZERO + for (i = 0; i < 10; i++) { + tosend_stuffbit(0); + } } - - int pad = (10 + 2 + (len * 10) + 10) & 0x7; - for (i = 0; i < 16 - pad; ++i) - tosend_stuffbit(1); - + // we can't use padding now + /* + int pad = (10 + 2 + (len * 10) + 10) & 0x7; + for (i = 0; i < 16 - pad; ++i) + tosend_stuffbit(1); + */ } /* * Convenience function to encode, transmit and trace iso 14443b comms */ -static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time) { +static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { tosend_t *ts = get_tosend(); - CodeIso14443bAsReader(cmd, len); + CodeIso14443bAsReader(cmd, len, framing); TransmitFor14443b_AsReader(start_time); if (g_trigger) LED_A_ON(); - *eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10; + +// eof_time in ssp clocks, but bits was added here! +// *eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10; + + *eof_time = *start_time + ETU_TO_SSP(8 * ts->max); + LogTrace(cmd, len, *start_time, *eof_time, NULL, true); } @@ -1545,7 +1576,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void // send uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time); + CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int len = Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time); @@ -1578,7 +1609,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void AddCrc14B(data_bytes, len - 2); // transmit S-Block - CodeAndTransmit14443bAsReader(data_bytes, len, &start_time, &eof_time); + CodeAndTransmit14443bAsReader(data_bytes, len, &start_time, &eof_time, true); // retrieve the result again (with increased timeout) eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; @@ -1637,7 +1668,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(cmdINIT, sizeof(cmdINIT), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmdINIT, sizeof(cmdINIT), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); @@ -1657,7 +1688,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { } start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(cmdMSBUID, sizeof(cmdMSBUID), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmdMSBUID, sizeof(cmdMSBUID), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); @@ -1675,7 +1706,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { } start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(cmdLSBUID, sizeof(cmdLSBUID), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmdLSBUID, sizeof(cmdLSBUID), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); @@ -1706,7 +1737,7 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r_init, sizeof(r_init), iso14b_timeout, &eof_time); @@ -1728,7 +1759,7 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 2); start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r_select, sizeof(r_select), iso14b_timeout, &eof_time); @@ -1752,7 +1783,7 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 1); start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(select_srx, 3, &start_time, &eof_time); // Only first three bytes for this one + CodeAndTransmit14443bAsReader(select_srx, 3, &start_time, &eof_time, true); // Only first three bytes for this one eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r_papid, sizeof(r_papid), iso14b_timeout, &eof_time); @@ -1772,6 +1803,174 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { return 0; } + +// Xerox tag connect function: wup, anticoll, attrib, password +// the original chips require all commands in this sequence + +// 0: OK, 1: select fail, 2: attrib fail, 3: crc fail, 4: password fail +int iso14443b_select_xrx_card(iso14b_card_select_t *card) { +// AFI + static const uint8_t x_wup1[] = { 0x0D, 0x37, 0x21, 0x92, 0xf2 }; + static const uint8_t x_wup2[] = { 0x5D, 0x37, 0x21, 0x71, 0x71 }; + uint8_t slot_mark[1]; + + uint8_t x_atqb[24] = {0x0}; // ATQB len = 18 + + uint32_t start_time = 0; + uint32_t eof_time = 0; + + iso14b_set_timeout(24); // wait for carrier + + // wup1 + CodeAndTransmit14443bAsReader(x_wup1, sizeof(x_wup1), &start_time, &eof_time, true); + + start_time = eof_time + US_TO_SSP(9000); // 9ms before next cmd + + // wup2 + CodeAndTransmit14443bAsReader(x_wup2, sizeof(x_wup2), &start_time, &eof_time, true); + + uint64_t uid = 0; + int retlen; + + for (int uid_pos = 0; uid_pos < 64; uid_pos += 2) { + int slot; + + for (slot = 0; slot < 4; slot++) { + start_time = eof_time + ETU_TO_SSP(30); //(24); // next slot after 24 ETU + + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + + if (retlen > 0) { + FpgaDisableTracing(); + + Dbprintf("unexpected data %d", retlen); + Dbprintf("crc %s", check_crc(CRC_14443_B, x_atqb, retlen) ? "OK" : "BAD"); + return 1; + } + + // tx unframed slot-marker + + if (Demod.posCount) { // no rx, but subcarrier burst detected + uid |= (uint64_t)slot << uid_pos; + + slot_mark[0] = 0xB1 + (slot << 1); // ack slot + CodeAndTransmit14443bAsReader(slot_mark, sizeof(slot_mark), &start_time, &eof_time, false); + break; + } else { // no subcarrier burst + slot_mark[0] = 0xA1 + (slot << 1); // nak slot + CodeAndTransmit14443bAsReader(slot_mark, sizeof(slot_mark), &start_time, &eof_time, false); + } + } + + if (4 == slot) { + FpgaDisableTracing(); + + DbpString("no answer to anticollision"); + return 1; + } + } + + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + + Dbprintf("anticollision uid %llx", uid); + + // ATQB too short? + if (retlen < 18) { + return 1; + } + + // VALIDATE CRC + if (check_crc(CRC_14443_B, x_atqb, 18) == false) { // use fixed len because unstable EOF catch + return 3; + } + + if (x_atqb[0] != 0x50) { +// DbpString("aqtb bad"); + return 1; + } + + if (card) { + card->uidlen = 8; + memcpy(card->uid, x_atqb + 1, 8); + memcpy(card->atqb, x_atqb + 9, 7); + } + +// DbpString("aqtb ok"); + + // send ATTRIB command + + uint8_t txbuf[18]; + + txbuf[1] = 0x1d; + memcpy(txbuf + 2, &uid, 8); + txbuf[10] = 0; + txbuf[11] = 0xF; + txbuf[12] = 1; + txbuf[13] = 0xF; + + AddCrc14B(txbuf + 1, 13); + + start_time = eof_time + ISO14B_TR2; + CodeAndTransmit14443bAsReader(txbuf + 1, 15, &start_time, &eof_time, true); + + eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + FpgaDisableTracing(); + + if (retlen < 3) { +// DbpString("attrib failed"); + return 2; + } + + if (check_crc(CRC_14443_B, x_atqb, 3) == false) { + return 3; + } + + if (x_atqb[0] != 0) { +// DbpString("attrib failed"); + return 2; + } + +// DbpString("attrib ok"); + + // apply PASSWORD command + + txbuf[0] = 2; + txbuf[1] = 0x38; + // uid from previous command used + txbuf[10] = 3; + txbuf[11] = 0x4e; + txbuf[12] = 0x4b; + txbuf[13] = 0x53; + txbuf[14] = 0x4F; + + AddCrc14B(txbuf, 15); + + start_time = eof_time + ISO14B_TR2; + CodeAndTransmit14443bAsReader(txbuf, 17, &start_time, &eof_time, true); + + eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; + retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + + if (retlen < 4) { +// DbpString("passwd failed"); + return 4; + } + + if (check_crc(CRC_14443_B, x_atqb, 4) == false) { + return 3; + } + + if (x_atqb[0] != 2 || x_atqb[1] != 0) { +// DbpString("passwd failed"); + return 4; + } + +// DbpString("passwd ok"); + + return 0; +} + /* Perform the ISO 14443 B Card Selection procedure * Currently does NOT do any collision handling. * It expects 0-1 cards in the device's range. @@ -1794,7 +1993,7 @@ int iso14443b_select_card(iso14b_card_select_t *card) { // first, wake up the tag uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(wupb, sizeof(wupb), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(wupb, sizeof(wupb), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r_pupid, sizeof(r_pupid), iso14b_timeout, &eof_time); @@ -1823,7 +2022,7 @@ int iso14443b_select_card(iso14b_card_select_t *card) { attrib[7] = r_pupid[10] & 0x0F; AddCrc14B(attrib, 9); start_time = eof_time + ISO14B_TR2; - CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; retlen = Get14443bAnswerFromTag(r_attrib, sizeof(r_attrib), iso14b_timeout, &eof_time); @@ -1916,7 +2115,7 @@ static int read_srx_block(uint8_t blocknr, uint8_t *block) { uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time); + CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; int retlen = Get14443bAnswerFromTag(r_block, sizeof(r_block), iso14b_timeout, &eof_time); @@ -2223,6 +2422,13 @@ void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p) { if (status > 0) goto out; } + if ((p->flags & ISO14B_SELECT_XRX) == ISO14B_SELECT_XRX) { + status = iso14443b_select_xrx_card(&card); + reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, (uint8_t *)&card, sendlen); + // 0: OK, 1: select fail, 2: attrib fail, 3: crc fail, 4: password fail + if (status != 0) goto out; + } + if ((p->flags & ISO14B_APDU) == ISO14B_APDU) { uint8_t res; status = iso14443b_apdu(p->raw, p->rawlen, (p->flags & ISO14B_SEND_CHAINING), buf, sizeof(buf), &res); @@ -2239,7 +2445,7 @@ void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p) { } uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(p->raw, p->rawlen, &start_time, &eof_time); + CodeAndTransmit14443bAsReader(p->raw, p->rawlen, &start_time, &eof_time, true); if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred FpgaDisableTracing(); diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 3dcbceaae..2579e6f1c 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -39,6 +39,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void int iso14443b_select_card(iso14b_card_select_t *card); int iso14443b_select_card_srx(iso14b_card_select_t *card); +int iso14443b_select_xrx_card(iso14b_card_select_t *card); void SimulateIso14443bTag(uint8_t *pupi); void AcquireRawAdcSamplesIso14443b(uint32_t parameter); diff --git a/client/Makefile b/client/Makefile index d4893b341..3f57cd1bd 100644 --- a/client/Makefile +++ b/client/Makefile @@ -578,6 +578,7 @@ SRCS = mifare/aiddesfire.c \ cmdhftopaz.c \ cmdhftexkom.c \ cmdhfwaveshare.c \ + cmdhfxerox.c \ cmdhw.c \ cmdlf.c \ cmdlfawid.c \ diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 9ed214eac..31979a5cd 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -48,6 +48,7 @@ #include "cmdhfst25ta.h" // ST25TA #include "cmdhfwaveshare.h" // Waveshare #include "cmdhftexkom.h" // Texkom +#include "cmdhfxerox.h" // Xerox #include "cmdtrace.h" // trace list #include "ui.h" #include "proxgui.h" @@ -437,6 +438,7 @@ static command_t CommandTable[] = { {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"texkom", CmdHFTexkom, AlwaysAvailable, "{ Texkom RFIDs... }"}, + {"xerox", CmdHFXerox, AlwaysAvailable, "{ Fuji/Xerox cartridge RFIDs... }"}, {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index d981727b4..5a1193cd0 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -283,6 +283,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { arg_lit0("s", "std", "activate field, use ISO14B select"), arg_lit0(NULL, "sr", "activate field, use SRx ST select"), arg_lit0(NULL, "cts", "activate field, use ASK C-ticket select"), + arg_lit0(NULL, "xrx", "activate field, use Fuji/Xerox select"), arg_lit0("c", "crc", "calculate and append CRC"), arg_lit0("r", NULL, "do not read response from card"), arg_int0("t", "timeout", "", "timeout in ms"), @@ -296,10 +297,11 @@ static int CmdHF14BCmdRaw(const char *Cmd) { bool select_std = arg_get_lit(ctx, 2); bool select_sr = arg_get_lit(ctx, 3); bool select_cts = arg_get_lit(ctx, 4); - bool add_crc = arg_get_lit(ctx, 5); - bool read_reply = (arg_get_lit(ctx, 6) == false); - int user_timeout = arg_get_int_def(ctx, 7, -1); - bool verbose = arg_get_lit(ctx, 8); + bool select_xrx = arg_get_lit(ctx, 5); + bool add_crc = arg_get_lit(ctx, 6); + bool read_reply = (arg_get_lit(ctx, 7) == false); + int user_timeout = arg_get_int_def(ctx, 8, -1); + bool verbose = arg_get_lit(ctx, 9); uint32_t flags = ISO14B_CONNECT; if (add_crc) { @@ -318,11 +320,15 @@ static int CmdHF14BCmdRaw(const char *Cmd) { flags |= (ISO14B_SELECT_CTS | ISO14B_CLEARTRACE); if (verbose) PrintAndLogEx(INFO, "using ASK/C-ticket select"); + } else if (select_xrx) { + flags |= (ISO14B_SELECT_XRX | ISO14B_CLEARTRACE); + if (verbose) + PrintAndLogEx(INFO, "using Fuji/Xerox select"); } uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; int datalen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 9), data, sizeof(data), &datalen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 10), data, sizeof(data), &datalen); if (res && verbose) { PrintAndLogEx(INFO, "called with no raw bytes"); } @@ -395,6 +401,12 @@ static int CmdHF14BCmdRaw(const char *Cmd) { PrintAndLogEx(SUCCESS, "Got response for ASK/C-ticket select"); } + if (select_xrx) { + success = wait_cmd_14b(verbose, true, user_timeout); + if (verbose && success) + PrintAndLogEx(SUCCESS, "Got response for Fuji/Xerox select"); + } + // get back response from the raw bytes you sent. if (success && datalen > 0) { wait_cmd_14b(true, false, user_timeout); diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c new file mode 100644 index 000000000..0041de368 --- /dev/null +++ b/client/src/cmdhfxerox.c @@ -0,0 +1,744 @@ +//----------------------------------------------------------------------------- +// High frequency Xerox commands (ISO14443B) +//----------------------------------------------------------------------------- + +#include "cmdhfxerox.h" + +#include "fileutils.h" + +#include "cmdparser.h" // command_t +#include "cliparser.h" +#include "comms.h" +#include "iso14b.h" +#include "crc16.h" + +#define TIMEOUT 2000 + + +#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<<24L) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c))))<<24L; \ + case 7: l2|=((unsigned long)(*(--(c))))<<16L; \ + case 6: l2|=((unsigned long)(*(--(c))))<< 8L; \ + case 5: l2|=((unsigned long)(*(--(c)))); \ + case 4: l1 =((unsigned long)(*(--(c))))<<24L; \ + case 3: l1|=((unsigned long)(*(--(c))))<<16L; \ + case 2: l1|=((unsigned long)(*(--(c))))<< 8L; \ + case 1: l1|=((unsigned long)(*(--(c)))); \ + } \ + } + +#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>24L)&0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \ + } \ + } + +/* NOTE - c is not incremented as per n2l */ +#define n2ln(c,l1,l2,n) { \ + c+=n; \ + l1=l2=0; \ + switch (n) { \ + case 8: l2 =((unsigned long)(*(--(c)))) ; \ + case 7: l2|=((unsigned long)(*(--(c))))<< 8; \ + case 6: l2|=((unsigned long)(*(--(c))))<<16; \ + case 5: l2|=((unsigned long)(*(--(c))))<<24; \ + case 4: l1 =((unsigned long)(*(--(c)))) ; \ + case 3: l1|=((unsigned long)(*(--(c))))<< 8; \ + case 2: l1|=((unsigned long)(*(--(c))))<<16; \ + case 1: l1|=((unsigned long)(*(--(c))))<<24; \ + } \ + } + +/* NOTE - c is not incremented as per l2n */ +#define l2nn(l1,l2,c,n) { \ + c+=n; \ + switch (n) { \ + case 8: *(--(c))=(unsigned char)(((l2) )&0xff); \ + case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \ + case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \ + case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \ + case 4: *(--(c))=(unsigned char)(((l1) )&0xff); \ + case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \ + case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \ + case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \ + } \ + } + +#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \ + l|=((unsigned long)(*((c)++)))<<16L, \ + l|=((unsigned long)(*((c)++)))<< 8L, \ + l|=((unsigned long)(*((c)++)))) + +#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \ + *((c)++)=(unsigned char)(((l)>>16L)&0xff), \ + *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \ + *((c)++)=(unsigned char)(((l) )&0xff)) + +#define C_RC2(n) \ + t=(x0+(x1& ~x3)+(x2&x3)+ *(p0++))&0xffff; \ + x0=(t<<1)|(t>>15); \ + t=(x1+(x2& ~x0)+(x3&x0)+ *(p0++))&0xffff; \ + x1=(t<<2)|(t>>14); \ + t=(x2+(x3& ~x1)+(x0&x1)+ *(p0++))&0xffff; \ + x2=(t<<3)|(t>>13); \ + t=(x3+(x0& ~x2)+(x1&x2)+ *(p0++))&0xffff; \ + x3=(t<<5)|(t>>11); + +#define RC2_ENCRYPT 1 +#define RC2_DECRYPT 0 + +typedef unsigned int RC2_INT; + +typedef struct rc2_key_st { + RC2_INT data[64]; +} RC2_KEY; + +static const unsigned char key_table[256] = { + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, + 0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, + 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5, + 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, + 0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, + 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f, + 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, + 0xbc, 0x94, 0x43, 0x03, 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, + 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x08, 0xe8, 0xea, 0xde, + 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, + 0x04, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, + 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85, + 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, + 0x67, 0x6c, 0xba, 0xc9, 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, + 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0x0d, 0x38, 0x34, 0x1b, + 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, + 0xfe, 0x7f, 0xc1, 0xad, +}; + +static const unsigned char var_list[] = {0x1c, 0x1e, 0x20, 0x26, 0x28, 0x2a, 0x2c, 0x2e}; + + +static int CmdHelp(const char *Cmd); +void RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits); +void RC2_encrypt(unsigned long *d, RC2_KEY *key); +void RC2_decrypt(unsigned long *d, RC2_KEY *key); +void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, RC2_KEY *ks, unsigned char *iv, int encrypt); + + +void RC2_set_key(RC2_KEY *key, int len, const unsigned char *data, int bits) { + int i, j; + unsigned char *k; + RC2_INT *ki; + unsigned int c, d; + + k = (unsigned char *) & (key->data[0]); + *k = 0; /* for if there is a zero length key */ + + if (len > 128) + len = 128; + if (bits <= 0) + bits = 1024; + if (bits > 1024) + bits = 1024; + + for (i = 0; i < len; i++) + k[i] = data[i]; + + /* expand table */ + d = k[len - 1]; + j = 0; + for (i = len; i < 128; i++, j++) { + d = key_table[(k[j] + d) & 0xff]; + k[i] = d; + } + + /* hmm.... key reduction to 'bits' bits */ + + j = (bits + 7) >> 3; + i = 128 - j; + c = (0xff >> (-bits & 0x07)); + + d = key_table[k[i] & c]; + k[i] = d; + while (i--) { + d = key_table[k[i + j] ^ d]; + k[i] = d; + } + + /* copy from bytes into RC2_INT's */ + ki = &(key->data[63]); + for (i = 127; i >= 0; i -= 2) + * (ki--) = ((k[i] << 8) | k[i - 1]) & 0xffff; +} + +void RC2_encrypt(unsigned long *d, RC2_KEY *key) { + int i, n; + register RC2_INT *p0, *p1; + register RC2_INT x0, x1, x2, x3, t; + unsigned long l; + + l = d[0]; + x0 = (RC2_INT)l & 0xffff; + x1 = (RC2_INT)(l >> 16L); + l = d[1]; + x2 = (RC2_INT)l & 0xffff; + x3 = (RC2_INT)(l >> 16L); + + n = 3; + i = 5; + + p0 = p1 = &(key->data[0]); + for (;;) { + t = (x0 + (x1 & ~x3) + (x2 & x3) + * (p0++)) & 0xffff; + x0 = (t << 1) | (t >> 15); + t = (x1 + (x2 & ~x0) + (x3 & x0) + * (p0++)) & 0xffff; + x1 = (t << 2) | (t >> 14); + t = (x2 + (x3 & ~x1) + (x0 & x1) + * (p0++)) & 0xffff; + x2 = (t << 3) | (t >> 13); + t = (x3 + (x0 & ~x2) + (x1 & x2) + * (p0++)) & 0xffff; + x3 = (t << 5) | (t >> 11); + + if (--i == 0) { + if (--n == 0) break; + i = (n == 2) ? 6 : 5; + + x0 += p1[x3 & 0x3f]; + x1 += p1[x0 & 0x3f]; + x2 += p1[x1 & 0x3f]; + x3 += p1[x2 & 0x3f]; + } + } + + d[0] = (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L); + d[1] = (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L); +} + +void RC2_decrypt(unsigned long *d, RC2_KEY *key) { + int i, n; + register RC2_INT *p0, *p1; + register RC2_INT x0, x1, x2, x3, t; + unsigned long l; + + l = d[0]; + x0 = (RC2_INT)l & 0xffff; + x1 = (RC2_INT)(l >> 16L); + l = d[1]; + x2 = (RC2_INT)l & 0xffff; + x3 = (RC2_INT)(l >> 16L); + + n = 3; + i = 5; + + p0 = &(key->data[63]); + p1 = &(key->data[0]); + for (;;) { + t = ((x3 << 11) | (x3 >> 5)) & 0xffff; + x3 = (t - (x0 & ~x2) - (x1 & x2) - * (p0--)) & 0xffff; + t = ((x2 << 13) | (x2 >> 3)) & 0xffff; + x2 = (t - (x3 & ~x1) - (x0 & x1) - * (p0--)) & 0xffff; + t = ((x1 << 14) | (x1 >> 2)) & 0xffff; + x1 = (t - (x2 & ~x0) - (x3 & x0) - * (p0--)) & 0xffff; + t = ((x0 << 15) | (x0 >> 1)) & 0xffff; + x0 = (t - (x1 & ~x3) - (x2 & x3) - * (p0--)) & 0xffff; + + if (--i == 0) { + if (--n == 0) break; + i = (n == 2) ? 6 : 5; + + x3 = (x3 - p1[x2 & 0x3f]) & 0xffff; + x2 = (x2 - p1[x1 & 0x3f]) & 0xffff; + x1 = (x1 - p1[x0 & 0x3f]) & 0xffff; + x0 = (x0 - p1[x3 & 0x3f]) & 0xffff; + } + } + + d[0] = (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L); + d[1] = (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L); +} + +void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + RC2_KEY *ks, unsigned char *iv, int encrypt) { + register unsigned long tin0, tin1; + register unsigned long tout0, tout1, xor0, xor1; + register long l = length; + unsigned long tin[2]; + + if (encrypt) { + c2l(iv, tout0); + c2l(iv, tout1); + iv -= 8; + for (l -= 8; l >= 0; l -= 8) { + c2l(in, tin0); + c2l(in, tin1); + tin0 ^= tout0; + tin1 ^= tout1; + tin[0] = tin0; + tin[1] = tin1; + RC2_encrypt(tin, ks); + tout0 = tin[0]; + l2c(tout0, out); + tout1 = tin[1]; + l2c(tout1, out); + } + if (l != -8) { + c2ln(in, tin0, tin1, l + 8); + tin0 ^= tout0; + tin1 ^= tout1; + tin[0] = tin0; + tin[1] = tin1; + RC2_encrypt(tin, ks); + tout0 = tin[0]; + l2c(tout0, out); + tout1 = tin[1]; + l2c(tout1, out); + } + l2c(tout0, iv); + l2c(tout1, iv); + } else { + c2l(iv, xor0); + c2l(iv, xor1); + iv -= 8; + for (l -= 8; l >= 0; l -= 8) { + c2l(in, tin0); + tin[0] = tin0; + c2l(in, tin1); + tin[1] = tin1; + RC2_decrypt(tin, ks); + tout0 = tin[0] ^ xor0; + tout1 = tin[1] ^ xor1; + l2c(tout0, out); + l2c(tout1, out); + xor0 = tin0; + xor1 = tin1; + } + if (l != -8) { + c2l(in, tin0); + tin[0] = tin0; + c2l(in, tin1); + tin[1] = tin1; + RC2_decrypt(tin, ks); + tout0 = tin[0] ^ xor0; + tout1 = tin[1] ^ xor1; + l2cn(tout0, tout1, out, l + 8); + xor0 = tin0; + xor1 = tin1; + } + l2c(xor0, iv); + l2c(xor1, iv); + } + tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0; + tin[0] = tin[1] = 0; +} + +static int switch_off_field(void) { + SetISODEPState(ISODEP_INACTIVE); + iso14b_raw_cmd_t packet = { + .flags = ISO14B_DISCONNECT, + .timeout = 0, + .rawlen = 0, + }; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); + return PM3_SUCCESS; +} + +static int findXerox(iso14b_card_select_t *card, bool disconnect) { + + if (card == NULL) + return false; + + int8_t retry = 3; + while (retry--) { + + iso14b_raw_cmd_t packet = { + .flags = (ISO14B_CONNECT | ISO14B_SELECT_XRX | (disconnect ? ISO14B_DISCONNECT : 0)), + .timeout = 0, + .rawlen = 0, + }; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + /* + PrintAndLogEx(INFO, "%X %X %X %X %X %I64X %I64X %I64X %X %X %X %c", + resp.cmd, resp.length, resp.magic, resp.status, resp.crc, resp.oldarg[0], resp.oldarg[1], resp.oldarg[2], + resp.data.asBytes[0], resp.data.asBytes[1], resp.data.asBytes[2], resp.ng ? 't' : 'f'); + */ + if (resp.oldarg[0] == 0) { + memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + } + return resp.oldarg[0]; + } + } // retry + +// switch_off_field(); + PrintAndLogEx(FAILED, "command execution timeout"); + return -1; +} + +static uint8_t info_blocks[] = {0x15, 0x16, 0x17, 0x18, 0x22}; +static const char *c_type[] = {"drum", "yellow", "magenta", "cyan", "black"}; + +static inline char dec_digit(uint8_t dig) { return (dig <= 9) ? dig + '0' : '?'; } + +static void gen_pn(uint8_t *data, char *pn) { + pn[0] = dec_digit(data[0] >> 4); + pn[1] = dec_digit(data[0] & 0xF); + pn[2] = dec_digit(data[1] >> 4); + + char sym = ((data[1] & 0xF) << 4) | (data[2] >> 4); + pn[3] = (sym >= 'A' && sym <= 'Z') ? sym : '?'; + + pn[4] = dec_digit(data[2] & 0xF); + pn[5] = dec_digit(data[3] >> 4); + pn[6] = dec_digit(data[3] & 0xF); + pn[7] = dec_digit(data[4] >> 4); + pn[8] = dec_digit(data[4] & 0xF); + pn[9] = '-'; + pn[10] = dec_digit(data[5] >> 4); + pn[11] = dec_digit(data[5] & 0xF); + pn[12] = 0; +} + +static int CmdHFXeroxInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf xerox info", + "Act as a reader.", + "hf xerox info"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + iso14b_card_select_t card; + int status = findXerox(&card, false); + if (status != 0) { + switch_off_field(); + if (verbose) PrintAndLogEx(FAILED, "Fuji/Xerox tag select failed"); + return PM3_ERFTRANS; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); + if (packet == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + int blocknum = 0; + uint8_t data[sizeof(info_blocks) * 4] = {0}; + + // set up the read command + packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); + packet->rawlen = 11; + packet->raw[0] = 0x02; + 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++) { + + packet->raw[10] = info_blocks[blocknum]; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + /* + PrintAndLogEx(INFO, "%X %X %X %X %X %I64X %I64X %I64X %X %X %X %c", + resp.cmd, resp.length, resp.magic, resp.status, resp.crc, resp.oldarg[0], resp.oldarg[1], resp.oldarg[2], + resp.data.asBytes[0], resp.data.asBytes[1], resp.data.asBytes[2], resp.ng ? 't' : 'f'); + */ + if (/*resp.status != 0 ||*/ resp.length < 7) { // 14b raw command send data_len instead of status + PrintAndLogEx(FAILED, "retrying one more time"); + continue; + } + + uint8_t *recv = resp.data.asBytes; + + if (check_crc(CRC_14443_B, recv, 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]); + break; + } + + memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + + retry = 0; + blocknum++; + } + } + + switch_off_field(); + free(packet); + + if (blocknum != 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"); + + return PM3_SUCCESS; +} + +static int CmdHFXeroxDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf xerox dump", + "Dump all memory from a Fuji/Xerox tag", + "hf xerox dump\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename to save dump to"), + arg_lit0("d", "decrypt", "decrypt secret blocks"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; + 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); + + CLIParserFree(ctx); + + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); + if (packet == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + iso14b_card_select_t card; + int status = findXerox(&card, false); // remain RF on + if (status != 0) { + switch_off_field(); + return PM3_ERFTRANS; + } + + PrintAndLogEx(INFO, "Reading memory from tag UID " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + + int blocknum = 1; // block 0 all zeros + uint8_t data[256 * 4] = {0}; + + // set up the read command + packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); + packet->rawlen = 11; + packet->raw[0] = 0x02; + memcpy(packet->raw + 2, card.uid, 8); // store uid + + PrintAndLogEx(INFO, "." NOLF); + + for (int retry = 0; (retry < 5 && blocknum < 0x100); retry++) { + + packet->raw[1] = (blocknum < 12) ? 0x30 : 0x20; // set command: read ext mem or read mem + packet->raw[10] = blocknum & 0xFF; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + /* + PrintAndLogEx(INFO, "%X %X %X %X %X %I64X %I64X %I64X %X %X %X %c", + resp.cmd, resp.length, resp.magic, resp.status, resp.crc, resp.oldarg[0], resp.oldarg[1], resp.oldarg[2], + resp.data.asBytes[0], resp.data.asBytes[1], resp.data.asBytes[2], resp.ng ? 't' : 'f'); + */ + if (/*resp.status != 0 ||*/ resp.length < 7) { // 14b raw command send data_len instead of status + PrintAndLogEx(FAILED, "retrying one more time"); + continue; + } + + uint8_t *recv = resp.data.asBytes; + + if (check_crc(CRC_14443_B, recv, 7) == false) { + PrintAndLogEx(FAILED, "crc fail, retrying one more time"); + continue; + } + + if (recv[0] != 2) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Tag returned Error %x %x", recv[0], recv[1]); + break; + } + + memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + + retry = 0; + blocknum++; + + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); +// PrintAndLogEx(INPLACE, "blk %3d", blocknum); + } + } + + switch_off_field(); + + free(packet); + + PrintAndLogEx(NORMAL, ""); + + if (blocknum != 0x100) + PrintAndLogEx(FAILED, "dump failed at block %d", blocknum); + + if (decrypt) { + PrintAndLogEx(INFO, "Decrypting secret blocks..."); + + RC2_KEY exp_key; + uint8_t k1[8], iv[8], k2[8], decr[8]; + + k1[0] = data[8]; + k1[1] = data[5]; + k1[2] = data[6]; + k1[3] = data[7]; + k1[4] = data[0x18 * 4 + 0]; + k1[5] = data[0x18 * 4 + 1]; + k1[6] = data[0x22 * 4 + 0]; + k1[7] = 0; + + RC2_set_key(&exp_key, 8, k1, 64); + + memset(iv, 0, sizeof(iv)); + iv[0] = k1[6]; + iv[1] = k1[7]; + iv[2] = 1; + + RC2_cbc_encrypt(k1, k2, 8, &exp_key, iv, RC2_ENCRYPT); + + memcpy(k1, k2, sizeof(k1)); + + k1[2] = k2[3] ^ data[0x22 * 4 + 0]; + k1[3] = k2[4] ^ data[0x22 * 4 + 1]; // first_key[7]; + k1[5] = k2[1] ^ 0x01; // 01 = crypto method? rfid[23][2] + + RC2_set_key(&exp_key, 8, k1, 64); + + for (int n = 0; n < sizeof(var_list); n++) { + + uint8_t dadr = var_list[n]; + + if (dadr + 1 >= blocknum) { + PrintAndLogEx(INFO, "secret block %02X skipped.", dadr); + continue; + } + + memset(iv, 0, sizeof(iv)); + iv[0] = dadr; + + RC2_cbc_encrypt(&data[dadr * 4], decr, 8, &exp_key, iv, RC2_DECRYPT); + + memcpy(&data[dadr * 4], decr, 8); + + int b; + uint16_t cs, csd; + + // calc checksum + 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]; + + if (cs != csd) { + PrintAndLogEx(FAILED, "secret block %02X checksum failed.", dadr); + } + } + } + + PrintAndLogEx(INFO, "block# | data | ascii"); + PrintAndLogEx(INFO, "---------+--------------+----------"); + + 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) + ); + } + PrintAndLogEx(INFO, "---------+--------------+----------"); + PrintAndLogEx(NORMAL, ""); + + if (0 == filename[0]) { // generate filename from uid + /* + PrintAndLogEx(INFO, "Using UID as filename"); + + sprintf(filename, "hf-xerox-%02X%02X%02X%02X%02X%02X%02X%02X-dump%s", + card.uid[7],card.uid[6],card.uid[5],card.uid[4],card.uid[3],card.uid[2],card.uid[1],card.uid[0], + decrypt ? "-dec" : ""); + */ + 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); + } + + size_t datalen = blocknum * 4; + saveFile(filename, ".bin", data, datalen); +// saveFileEML(filename, data, datalen, 4); +// saveFileJSON(filename, jsf15, data, datalen, NULL); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Short info on Fuji/Xerox tag"}, + {"dump", CmdHFXeroxDump, IfPm3Iso14443b, "Read all memory pages of an Fuji/Xerox tag, save to file"}, +// {"rdbl", CmdHFXeroxRdBl, IfPm3Iso14443b, "Read Fuji/Xerox block"}, +// {"wrbl", CmdHFXeroxWrBl, IfPm3Iso14443b, "Write Fuji/Xerox block"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFXerox(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhfxerox.h b/client/src/cmdhfxerox.h new file mode 100644 index 000000000..68b9906c8 --- /dev/null +++ b/client/src/cmdhfxerox.h @@ -0,0 +1,12 @@ +//----------------------------------------------------------------------------- +// High frequency Xerox commands (ISO14443B) +//----------------------------------------------------------------------------- + +#ifndef CMDHFXEROX_H__ +#define CMDHFXEROX_H__ + +#include "common.h" + +int CmdHFXerox(const char *Cmd); + +#endif diff --git a/client/src/pm3line_vocabulory.h b/client/src/pm3line_vocabulory.h index 4e4c3948e..c8619f164 100644 --- a/client/src/pm3line_vocabulory.h +++ b/client/src/pm3line_vocabulory.h @@ -425,6 +425,11 @@ const static vocabulory_t vocabulory[] = { { 0, "hf topaz sim" }, { 0, "hf topaz sniff" }, { 0, "hf topaz raw" }, + { 1, "hf texkom help" }, + { 0, "hf texkom reader" }, + { 1, "hf xerox help" }, + { 0, "hf xerox info" }, + { 0, "hf xerox dump" }, { 1, "hf waveshare help" }, { 0, "hf waveshare loadbmp" }, { 1, "hw help" }, diff --git a/doc/commands.json b/doc/commands.json index db5be2717..5fb476e59 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -1365,13 +1365,14 @@ "-s, --std activate field, use ISO14B select", "--sr activate field, use SRx ST select", "--cts activate field, use ASK C-ticket select", + "--xrx activate field, use Fuji/Xerox select", "-c, --crc calculate and append CRC", "-r do not read response from card", "-t, --timeout timeout in ms", "-v, --verbose verbose", "-d, --data data, bytes to send" ], - "usage": "hf 14b raw [-hkscrv] [--sr] [--cts] [-t ] [-d ]" + "usage": "hf 14b raw [-hkscrv] [--sr] [--cts] [--xrx] [-t ] [-d ]" }, "hf 14b rdbl": { "command": "hf 14b rdbl", @@ -2661,7 +2662,7 @@ }, "hf help": { "command": "hf help", - "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } gallagher { Gallagher DESFire RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } jooki { Jooki RFIDs... } iclass { ICLASS RFIDs... } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } texkom { Texkom RFIDs... } waveshare { Waveshare NFC ePaper... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags", + "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } gallagher { Gallagher DESFire RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } jooki { Jooki RFIDs... } iclass { ICLASS RFIDs... } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } texkom { Texkom RFIDs... } xerox { Fuji/Xerox cartridge RFIDs... } waveshare { Waveshare NFC ePaper... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags", "notes": [], "offline": true, "options": [], @@ -6518,6 +6519,41 @@ ], "usage": "hf waveshare loadbmp [-hs] -m -f " }, + "hf xerox dump": { + "command": "hf xerox dump", + "description": "Dump all memory from a Fuji/Xerox tag", + "notes": [ + "hf xerox dump" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file filename to save dump to", + "-d, --decrypt decrypt secret blocks" + ], + "usage": "hf xerox dump [-hd] [-f ]" + }, + "hf xerox help": { + "command": "hf xerox help", + "description": "help This help", + "notes": [], + "offline": true, + "options": [], + "usage": "" + }, + "hf xerox info": { + "command": "hf xerox info", + "description": "Act as a reader.", + "notes": [ + "hf xerox info" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output" + ], + "usage": "hf xerox info [-hv]" + }, "hints": { "command": "hints", "description": "Turn on/off hints", @@ -6549,8 +6585,8 @@ "command": "hw connect", "description": "Connects to a Proxmark3 device via specified serial port. Baudrate here is only for physical UART or UART-BT, NOT for USB-CDC or blue shark add-on", "notes": [ - "hw connect -p /dev/ttyacm0", - "hw connect -p /dev/ttyacm0 -b 115200" + "hw connect -p /dev/ttyACM0", + "hw connect -p /dev/ttyACM0 -b 115200" ], "offline": true, "options": [ @@ -11019,8 +11055,8 @@ } }, "metadata": { - "commands_extracted": 695, + "commands_extracted": 698, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2022-06-28T08:43:20" + "extracted_on": "2022-06-30T19:19:33" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index b9689c5c2..fe57dfbdb 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -656,6 +656,17 @@ Check column "offline" for their availability. |`hf texkom reader `|N |`Act like a Texkom reader` +### hf xerox + + { Fuji/Xerox cartridge RFIDs... } + +|command |offline |description +|------- |------- |----------- +|`hf xerox help `|Y |`This help` +|`hf xerox info `|N |`Short info on Fuji/Xerox tag` +|`hf xerox dump `|N |`Read all memory pages of an Fuji/Xerox tag, save to file` + + ### hf waveshare { Waveshare NFC ePaper... } diff --git a/include/iso14b.h b/include/iso14b.h index 432baac0c..38862950b 100644 --- a/include/iso14b.h +++ b/include/iso14b.h @@ -47,6 +47,7 @@ typedef enum ISO14B_COMMAND { ISO14B_SEND_CHAINING = (1 << 9), ISO14B_SELECT_CTS = (1 << 10), ISO14B_CLEARTRACE = (1 << 11), + ISO14B_SELECT_XRX = (1 << 12), } iso14b_command_t; typedef struct {