From f93c9977ad7bac27fd345af00259e7c9bb8399ea Mon Sep 17 00:00:00 2001 From: Philippe Teuwen Date: Sat, 20 Sep 2025 12:30:49 +0200 Subject: [PATCH] hf 14a info: add ATR fingerprinting --- CHANGELOG.md | 1 + client/atr_scrap_pcsctools.py | 3 ++ client/src/atrs.c | 59 +++++++++++++++++++++++++++++++++++ client/src/atrs.h | 3 ++ client/src/cmdhf14a.c | 14 ++++++++- client/src/cmdsmartcard.c | 58 ---------------------------------- 6 files changed, 79 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2235fb60..0a7dce0cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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] +- Added ATR fingerprinting to `hf 14a info` (@doegox) ## [Phrack.4.20728][2025-09-11] - Change `lf t55xx restore` - now skips writing block0 if its all zeros (@iceman1001) diff --git a/client/atr_scrap_pcsctools.py b/client/atr_scrap_pcsctools.py index 10471e597..bc1cd746a 100755 --- a/client/atr_scrap_pcsctools.py +++ b/client/atr_scrap_pcsctools.py @@ -48,6 +48,7 @@ C_HEADER="""//------------------------------------------------------------------ #define ATRS_H__ #include +#include typedef struct atr_s { const char *bytes; @@ -55,6 +56,8 @@ typedef struct atr_s { } atr_t; const char *getAtrInfo(const char *atr_str); +void atsToEmulatedAtr(uint8_t *ats, uint8_t *atr, int *atrLen); +void atqbToEmulatedAtr(uint8_t *atqb, uint8_t cid, uint8_t *atr, int *atrLen); // atr_t array is expected to be NULL terminated const static atr_t AtrTable[] = { diff --git a/client/src/atrs.c b/client/src/atrs.c index ee7d50711..d401aad71 100644 --- a/client/src/atrs.c +++ b/client/src/atrs.c @@ -67,3 +67,62 @@ const char *getAtrInfo(const char *atr_str) { return AtrTable[ARRAYLEN(AtrTable) - 1].desc; } } + +void atsToEmulatedAtr(uint8_t *ats, uint8_t *atr, int *atrLen) { + uint8_t historicalLen = 0; + uint8_t offset = 2; + + if (ats[0] < 2) { + historicalLen = 0; + } else { + + if ((ats[1] & 64) != 0) { + offset++; + } + if ((ats[1] & 32) != 0) { + offset++; + } + if ((ats[1] & 16) != 0) { + offset++; + } + + if (offset >= ats[0]) { + historicalLen = 0; + } else { + historicalLen = ats[0] - offset; + } + } + + atr[0] = 0x3B; + atr[1] = 0x80 | historicalLen; + atr[2] = 0x80; + atr[3] = 0x01; + + uint8_t tck = atr[1] ^ atr[2] ^ atr[3]; + for (uint8_t i = 0; i < historicalLen; ++i) { + atr[4 + i] = ats[offset + i]; + tck = tck ^ ats[offset + i]; + } + atr[4 + historicalLen] = tck; + + *atrLen = 5 + historicalLen; +} + +void atqbToEmulatedAtr(uint8_t *atqb, uint8_t cid, uint8_t *atr, int *atrLen) { + atr[0] = 0x3B; + atr[1] = 0x80 | 8; + atr[2] = 0x80; + atr[3] = 0x01; + + memcpy(atr + 4, atqb, 7); + atr[11] = cid >> 4; + + uint8_t tck = 0; + for (int i = 1; i < 12; ++i) { + tck = tck ^ atr[i]; + } + atr[12] = tck; + + *atrLen = 13; +} + diff --git a/client/src/atrs.h b/client/src/atrs.h index 832dc00a9..2e9605850 100644 --- a/client/src/atrs.h +++ b/client/src/atrs.h @@ -23,6 +23,7 @@ #define ATRS_H__ #include +#include typedef struct atr_s { const char *bytes; @@ -30,6 +31,8 @@ typedef struct atr_s { } atr_t; const char *getAtrInfo(const char *atr_str); +void atsToEmulatedAtr(uint8_t *ats, uint8_t *atr, int *atrLen); +void atqbToEmulatedAtr(uint8_t *atqb, uint8_t cid, uint8_t *atr, int *atrLen); // atr_t array is expected to be NULL terminated const static atr_t AtrTable[] = { diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index b2b96bcc6..7a664b820 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -2946,7 +2946,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((card.ats[0] > pos) && (card.ats_len >= card.ats[0] + 2)) { uint8_t calen = card.ats[0] - pos; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " ----------------------------"); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " ---------------------------"); if (card.ats[pos] == 0xC1) { PrintAndLogEx(INFO, " %s", sprint_hex(card.ats + pos, calen)); @@ -3024,6 +3024,18 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { , sprint_ascii(card.ats + pos, calen) ); } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------ " _CYAN_("ATR fingerprinting") " ---------------------------"); + uint8_t atr[256] = {0}; + int atrLen = 0; + atsToEmulatedAtr(card.ats, atr, &atrLen); + char *copy = str_dup(getAtrInfo(sprint_hex_inrow(atr, atrLen))); + char *token = strtok(copy, "\n"); + while (token != NULL) { + PrintAndLogEx(INFO, " %s", token); + token = strtok(NULL, "\n"); + } + free(copy); } } } diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 6e1c67ba5..5d01f9a9d 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -1212,64 +1212,6 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { return PM3_SUCCESS; } -static void atsToEmulatedAtr(uint8_t *ats, uint8_t *atr, int *atrLen) { - uint8_t historicalLen = 0; - uint8_t offset = 2; - - if (ats[0] < 2) { - historicalLen = 0; - } else { - - if ((ats[1] & 64) != 0) { - offset++; - } - if ((ats[1] & 32) != 0) { - offset++; - } - if ((ats[1] & 16) != 0) { - offset++; - } - - if (offset >= ats[0]) { - historicalLen = 0; - } else { - historicalLen = ats[0] - offset; - } - } - - atr[0] = 0x3B; - atr[1] = 0x80 | historicalLen; - atr[2] = 0x80; - atr[3] = 0x01; - - uint8_t tck = atr[1] ^ atr[2] ^ atr[3]; - for (uint8_t i = 0; i < historicalLen; ++i) { - atr[4 + i] = ats[offset + i]; - tck = tck ^ ats[offset + i]; - } - atr[4 + historicalLen] = tck; - - *atrLen = 5 + historicalLen; -} - -static void atqbToEmulatedAtr(uint8_t *atqb, uint8_t cid, uint8_t *atr, int *atrLen) { - atr[0] = 0x3B; - atr[1] = 0x80 | 8; - atr[2] = 0x80; - atr[3] = 0x01; - - memcpy(atr + 4, atqb, 7); - atr[11] = cid >> 4; - - uint8_t tck = 0; - for (int i = 1; i < 12; ++i) { - tck = tck ^ atr[i]; - } - atr[12] = tck; - - *atrLen = 13; -} - static int CmdPCSC(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "smart pcsc",