added lf hitag view command to view dump files

This commit is contained in:
iceman1001 2024-01-30 10:35:30 +01:00
commit 53a5087d67
3 changed files with 225 additions and 134 deletions

View file

@ -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 `lf hitag view` - now supports viewing of dump files (@iceman1001)
- Changed `lf hitag dump --ns` - now support nosave flag (@iceman1001)
- Added `hf xerox rdbl` - read block of a Fuji/Xerox tag (@iceman1001)
- Added a xerox trace file, thanks @jeroenSteen, (@iceman1001)
@ -20,8 +21,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Changed `hw readmem` to allow larger reads, write to file and better hex viewer (@martian01010)
- Added `CMD_READ_MEM_DOWNLOAD` and `CMD_READ_MEM_DOWNLOADED` to osimage and bootloader (@martian01010)
- Changed `hf mfu info` - now correctly identifies NTAG I2C tags (@icemann1001)
- Changed `hf 14b dump/view` - now supports `z` flag for dense output (@iceman1001)
- Changed `hf xerox dump/view` - now supports `z` flag for dense output (@iceman1001)
- Changed `hf 14b dump/view` - now supports `-z` flag for dense output (@iceman1001)
- Changed `hf xerox dump/view` - now supports `-z` flag for dense output (@iceman1001)
- Changed `hf mfu dump/view/eview` - now supports `-z` flag for dense output (@iceman1001)
- Added `data crypto` to encrypt and decrypt data in the proxmark client using built-in methods (@team-orangeBlue)
- Changed `hf tune` and `lf tune` - Added an option to show statistical data after tuning (@wh201906)
@ -44,9 +45,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- 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)
- Changed `hf 15 raw` - now supports "-k" keep field on and "-s" select (@iceman1001)
- Changed `hf 15 raw` - now supports `-k` keep field on and `-s` select (@iceman1001)
- Changed `prefs set client.debug` - now also toggles the client side APDU logging (@iceman1001)
- Changed `hf 14b sriwrbl` - now supports --force to override block checks. Thanks @gentilkiwi for the idea! (@iceman1001)
- Changed `hf 14b sriwrbl` - now supports `--force` to override block checks. Thanks @gentilkiwi for the idea! (@iceman1001)
- Changed `hf 14b sriwrbl` - now tries to verify the write (@iceman1001)
- Changed `hf 14b apdu` - now supports tearoff (@iceman1001)
- Fixed `hf 14b raw` - fixed a potential write out of bounds. (@iceman1001)

View file

@ -332,8 +332,7 @@ static int CmdLFHitagSim(const char *Cmd) {
return PM3_SUCCESS;
}
static void printHitag2PaxtonDowngrade(const uint8_t *data) {
static void print_hitag2_paxton(const uint8_t *data) {
uint64_t bytes = 0;
uint64_t num = 0;
@ -359,116 +358,150 @@ static void printHitag2PaxtonDowngrade(const uint8_t *data) {
}
}
PrintAndLogEx(INFO, "-------- " _CYAN_("Possible de-scramble patterns") " ---------");
PrintAndLogEx(SUCCESS, "Paxton id: %lu | 0x%lx", paxton_id, paxton_id);
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Possible de-scramble patterns") " -------------");
PrintAndLogEx(SUCCESS, "Paxton id... %" PRIu64 " | 0x%" PRIx64, paxton_id, paxton_id);
}
static void printHitag2Configuration(uint8_t config) {
static void print_hitag2_configuration(uint32_t uid, uint8_t config) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%08X"), uid);
PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), getHitagTypeStr(uid));
char msg[100];
memset(msg, 0, sizeof(msg));
char bits[9];
char *bs = bits;
for (uint8_t i = 0 ; i < 8 ; i++) {
snprintf(bs, sizeof(bits) - i, "%1d", (config >> (7 - i)) & 1);
bs++;
}
PrintAndLogEx(INFO, "\n\nHitag2 tag information ");
PrintAndLogEx(INFO, "------------------------------------");
uint8_t bits[8 + 1] = {0};
num_to_bytebits(config, 8, bits);
const char *bs = sprint_bytebits_bin(bits, 8);
//configuration byte
PrintAndLogEx(SUCCESS, "Config byte : 0x%02X [ %s ]", config, bits);
PrintAndLogEx(SUCCESS, "");
PrintAndLogEx(SUCCESS, "Config byte... 0x%02X", config);
PrintAndLogEx(SUCCESS, " %s", bs);
// encoding
strcat(msg, "Encoding : ");
if (config & 0x1) {
strcat(msg + strlen(msg), _YELLOW_("Biphase"));
} else {
strcat(msg + strlen(msg), _YELLOW_("Manchester"));
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 0, 4, "RFU"));
if (config & 0x8) {
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_YELLOW, bs, 8, 4, 1, "Crypto mode"));
} else {
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 4, 1, "Password mode"));
}
PrintAndLogEx(SUCCESS, "%s", msg);
memset(msg, 0, sizeof(msg));
// version
strcat(msg, "Coding in HITAG 2 operation: %s");
uint8_t foo = (config & 0x6) >> 1;
uint8_t foo = ((config & 0x6) >> 1);
switch (foo) {
case 0:
PrintAndLogEx(SUCCESS, "Version : public mode B, Coding: biphase");
PrintAndLogEx(SUCCESS, msg, (config & 0x1) ? "biphase" : "manchester");
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 5, 2, "Public mode B, Coding: biphase"));
break;
case 1:
PrintAndLogEx(SUCCESS, "Version : public mode A, Coding: manchester");
PrintAndLogEx(SUCCESS, msg, (config & 0x1) ? "biphase" : "manchester");
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 5, 2, "Public mode A, Coding: manchester"));
break;
case 2:
PrintAndLogEx(SUCCESS, "Version : public mode C, Coding: biphase");
PrintAndLogEx(SUCCESS, msg, (config & 0x1) ? "biphase" : "manchester");
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 5, 2, "Public mode C, Coding: biphase"));
break;
case 3:
PrintAndLogEx(SUCCESS, "Version : Hitag2");
PrintAndLogEx(SUCCESS, msg, (config & 0x1) ? "biphase" : "manchester");
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 5, 2, "Hitag2"));
break;
}
memset(msg, 0, sizeof(msg));
// mode
strcat(msg, "Tag is in : ");
if (config & 0x8) {
strcat(msg + strlen(msg), _YELLOW_("Crypto mode"));
} else {
strcat(msg + strlen(msg), _YELLOW_("Password mode"));
// encoding
if (config & 0x01) {
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 7, 1, "Biphase"));
} else {
PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 7, 1, "Manchester"));
}
PrintAndLogEx(SUCCESS, "%s", msg);
memset(msg, 0, sizeof(msg));
// page access
strcat(msg, "Page 6,7 : ");
if (config & 0x10) {
strcat(msg + strlen(msg), "read only");
} else {
strcat(msg + strlen(msg), _GREEN_("RW"));
}
PrintAndLogEx(SUCCESS, "%s", msg);
memset(msg, 0, sizeof(msg));
}
// page access
strcat(msg, "Page 4,5 : ");
if (config & 0x20) {
strcat(msg + strlen(msg), "read only");
} else {
strcat(msg + strlen(msg), _GREEN_("RW"));
}
PrintAndLogEx(SUCCESS, "%s", msg);
memset(msg, 0, sizeof(msg));
const char* annotation[] = {
"UID", "Pwd", "Key/Pwd", "Config",
"User", "User", "User", "User",
"User", "User", "User", "User"
};
// OTP
strcat(msg, "Page 3 : ");
if (config & 0x40) {
strcat(msg + strlen(msg), "read only. Configuration byte and password tag " _RED_("FIXED / IRREVERSIBLE"));
} else {
strcat(msg + strlen(msg), _GREEN_("RW"));
}
PrintAndLogEx(SUCCESS, "%s", msg);
memset(msg, 0, sizeof(msg));
static void print_hitag2_blocks(uint8_t *d, uint16_t n) {
// OTP
if (config & 0x80) {
strcat(msg, "Page 1 : " _RED_("locked") "\n");
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "-----------------------------------------------");
PrintAndLogEx(INFO, "block# | data | ascii | lck | Info");
PrintAndLogEx(INFO, "---------+-------------+-------+-----+---------");
strcat(msg + strlen(msg), "Page 2 : ");
if (config & 0x8) {
strcat(msg + strlen(msg), _RED_("locked"));
} else {
strcat(msg + strlen(msg), "read only");
uint8_t config = d[HITAG2_CONFIG_OFFSET];
uint8_t blocks = (n / HITAG_BLOCK_SIZE);
for (uint8_t i = 0; i < blocks; ++i) {
char lckstr[20] = {0};
sprintf(lckstr, " ");
switch (i) {
case 0:
sprintf(lckstr, "%s", _RED_("L "));
break;
case 1:
if (config & 0x80) {
sprintf(lckstr, "%s", _RED_("L "));
} else {
sprintf(lckstr, "%s", _GREEN_("RW"));
}
break;
case 2:
if (config & 0x80) {
if (config & 0x8) {
sprintf(lckstr, "%s", _RED_("L "));
} else {
sprintf(lckstr, "%s", _RED_("R "));
}
} else {
sprintf(lckstr, "%s", _GREEN_("RW"));
}
break;
case 3:
// OTP Page 3.
if (config & 0x40) {
sprintf(lckstr, "%s", _RED_("R "));
//. Configuration byte and password tag " _RED_("FIXED / IRREVERSIBLE"));
} else {
sprintf(lckstr, "%s", _GREEN_("RW"));
}
break;
case 4:
case 5:
if (config & 0x20) {
sprintf(lckstr, "%s", _RED_("R "));
} else {
sprintf(lckstr, "%s", _GREEN_("RW"));
}
break;
case 6:
case 7:
if (config & 0x10) {
sprintf(lckstr, "%s", _RED_("R "));
} else {
sprintf(lckstr, "%s", _GREEN_("RW"));
}
break;
default:
break;
}
} else {
strcat(msg, "Page 1,2 : " _GREEN_("RW"));
PrintAndLogEx(INFO, "%3d/0x%02X | %s| %s | %s | %s"
, i
, i
, sprint_hex(d + (i * HITAG_BLOCK_SIZE), HITAG_BLOCK_SIZE)
, sprint_ascii(d + (i * HITAG_BLOCK_SIZE), HITAG_BLOCK_SIZE)
, lckstr
, annotation[i]
);
}
PrintAndLogEx(SUCCESS, "%s", msg);
PrintAndLogEx(INFO, "------------------------------------");
PrintAndLogEx(INFO, "---------+-------------+-------+-----+---------");
PrintAndLogEx(INFO, " L = Locked, "_GREEN_("RW") " = Read Write, R = Read Only");
PrintAndLogEx(INFO, " FI = Fixed / Irreversible");
PrintAndLogEx(INFO, "-----------------------------------------------");
}
static bool getHitag2Uid(uint32_t *uid) {
@ -477,7 +510,7 @@ static bool getHitag2Uid(uint32_t *uid) {
clearCommandBuffer();
SendCommandMIX(CMD_LF_HITAG_READER, RHT2F_UID_ONLY, 0, 0, &htd, sizeof(htd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
return false;
}
@ -487,8 +520,9 @@ static bool getHitag2Uid(uint32_t *uid) {
return false;
}
if (uid)
*uid = bytes_to_num(resp.data.asBytes, 4);
if (uid) {
*uid = bytes_to_num(resp.data.asBytes, HITAG_UID_SIZE);
}
return true;
}
@ -509,24 +543,18 @@ static int CmdLFHitagInfo(const char *Cmd) {
// read UID
uint32_t uid = 0;
if (getHitag2Uid(&uid) == false)
if (getHitag2Uid(&uid) == false) {
return PM3_ESOFT;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%08X"), uid);
PrintAndLogEx(SUCCESS, " TYPE: " _GREEN_("%s"), getHitagTypeStr(uid));
}
// how to determine Hitag types?
// read block3, get configuration byte.
// common configurations.
// printHitag2Configuration(0x06);
//printHitag2Configuration( 0x0E );
//printHitag2Configuration( 0x02 );
//printHitag2Configuration( 0x00 );
//printHitag2Configuration( 0x04 );
PrintAndLogEx(INFO, "-------------------------------------------------------------");
print_hitag2_configuration( uid, 0x06 );
// print_hitag2_configuration( uid, 0x0E );
// print_hitag2_configuration( uid, 0x02 );
// print_hitag2_configuration( uid, 0x00 );
// print_hitag2_configuration( uid, 0x04 );
return PM3_SUCCESS;
}
@ -700,12 +728,11 @@ static int CmdLFHitagReader(const char *Cmd) {
return PM3_ESOFT;
}
uint32_t id = bytes_to_num(resp.data.asBytes, 4);
uint8_t *data = resp.data.asBytes;
PrintAndLogEx(SUCCESS, " UID: " _YELLOW_("%08x"), id);
printHitag2Configuration(data[4 * 3]);
print_hex_break(data, 48, 4);
printHitag2PaxtonDowngrade(data);
uint32_t uid = bytes_to_num(data, HITAG_UID_SIZE);
print_hitag2_configuration(uid, data[HITAG_BLOCK_SIZE * 3]);
print_hex_break(data, HITAG2_MAX_BYTE_SIZE, HITAG_BLOCK_SIZE);
print_hitag2_paxton(data);
return PM3_SUCCESS;
}
@ -773,7 +800,6 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) {
return PM3_SUCCESS;
}
static int CmdLFHitagWriter(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag wrbl",
@ -985,6 +1011,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
arg_lit0(NULL, "crypto", "crypto mode"),
arg_str0("k", "key", "<hex>", "key, 4 or 6 hex bytes"),
arg_str0("f", "file", "<fn>", "specify file name"),
arg_lit0(NULL, "ns", "no save to file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -1006,7 +1033,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
bool use_nrar = nalen > 0;
bool use_crypto = arg_get_lit(ctx, 3);
uint8_t key[6];
uint8_t key[HITAG_CRYPTOKEY_SIZE];
int keylen = 0;
res = CLIParamHexToBuf(arg_get_str(ctx, 4), key, sizeof(key), &keylen);
if (res != 0) {
@ -1018,6 +1045,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool nosave = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
// sanity checks
@ -1036,19 +1064,21 @@ static int CmdLFHitag2Dump(const char *Cmd) {
}
// complete options
if (keylen == 4) {
if (keylen == HITAG_PASSWORD_SIZE) {
use_pwd = true;
}
if (keylen == 6) {
if (keylen == HITAG_CRYPTOKEY_SIZE) {
use_crypto = true;
}
// Set default key / pwd
if ((keylen == 0) && use_pwd) {
memcpy(key, "MIKR", 4);
keylen = 4;
memcpy(key, "MIKR", HITAG_PASSWORD_SIZE);
keylen = HITAG_PASSWORD_SIZE;
}
if ((keylen == 0) && use_crypto) {
memcpy(key, "ONMIKR", 6);
keylen = 6;
memcpy(key, "ONMIKR", HITAG_CRYPTOKEY_SIZE);
keylen = HITAG_CRYPTOKEY_SIZE;
}
// check coherence
@ -1081,11 +1111,11 @@ static int CmdLFHitag2Dump(const char *Cmd) {
if (use_ht2 && use_pwd) {
htf = RHT2F_PASSWORD;
memcpy(htd.pwd.password, key, sizeof(htd.pwd.password));
PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Password mode");
PrintAndLogEx(INFO, "Authenticating to Hitag2 in Password mode");
} else if (use_ht2 && use_crypto) {
htf = RHT2F_CRYPTO;
memcpy(htd.crypto.key, key, sizeof(htd.crypto.key));
PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Crypto mode");
PrintAndLogEx(INFO, "Authenticating to Hitag2 in Crypto mode");
} else {
PrintAndLogEx(WARNING, "Sorry, not yet implemented");
return PM3_ENOTIMPL;
@ -1103,34 +1133,77 @@ static int CmdLFHitag2Dump(const char *Cmd) {
PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed");
return PM3_ESOFT;
}
uint8_t *data = resp.data.asBytes;
if (data == NULL)
return PM3_ESOFT;
// block3, 1 byte
uint32_t uid = bytes_to_num(data, HITAG_UID_SIZE);
print_hitag2_configuration(uid, data[HITAG_BLOCK_SIZE * 3]);
print_hitag2_blocks(data, HITAG2_MAX_BYTE_SIZE);
print_hitag2_paxton(data);
uint32_t id = bytes_to_num(resp.data.asBytes, 4);
PrintAndLogEx(SUCCESS, " UID: " _YELLOW_("%08x"), id);
if (nosave) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Called with no save option");
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;
}
if (fnlen < 1) {
char *fptr = filename;
fptr += snprintf(filename, sizeof(filename), "lf-hitag-");
FillFileNameByUID(fptr, data, "-dump", 4);
FillFileNameByUID(fptr, data, "-dump", HITAG_UID_SIZE);
}
// block3, 1 byte
printHitag2Configuration(data[4 * 3]);
// print data
print_hex_break(data, 48, 4);
printHitag2PaxtonDowngrade(data);
PrintAndLogEx(SUCCESS, "Dumping tag memory...");
pm3_save_dump(filename, data, 48, jsfHitag);
pm3_save_dump(filename, data, HITAG2_MAX_BYTE_SIZE, jsfHitag);
return PM3_SUCCESS;
}
static int CmdLFHitagView(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag view",
"Print a HITAG dump file (bin/eml/json)",
"lf hitag view -f lf-hitag-01020304-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 = 0;
int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, HITAG2_MAX_BYTE_SIZE);
if (res != PM3_SUCCESS) {
return res;
}
if (bytes_read < HITAG2_MAX_BYTE_SIZE) {
PrintAndLogEx(ERR, "Error, dump file is too small");
free(dump);
return PM3_ESOFT;
}
if (verbose) {
// block3, 1 byte
uint8_t config = dump[HITAG_BLOCK_SIZE * 3];
uint32_t uid = bytes_to_num(dump, HITAG_UID_SIZE);
print_hitag2_configuration(uid, config);
print_hitag2_paxton(dump);
}
print_hitag2_blocks(dump, HITAG2_MAX_BYTE_SIZE);
free(dump);
return PM3_SUCCESS;
}
// Annotate HITAG protocol
void annotateHitag1(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
@ -1197,6 +1270,7 @@ static command_t CommandTable[] = {
{"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"},
{"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag 2 tag"},
{"read", CmdLFHitagReader, IfPm3Hitag, "Read Hitag memory"},
{"view", CmdLFHitagView, AlwaysAvailable, "Display content from tag dump file"},
{"wrbl", CmdLFHitagWriter, IfPm3Hitag, "Write a block (page) in Hitag memory"},
{"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"},
{"cc", CmdLFHitagSCheckChallenges, IfPm3Hitag, "Hitag S: test all provided challenges"},
@ -1223,6 +1297,8 @@ int readHitagUid(void) {
}
uint8_t hitag1_CRC_check(uint8_t *d, uint32_t nbit) {
if (nbit < 9) return 2;
if (nbit < 9) {
return 2;
}
return (CRC8Hitag1Bits(d, nbit) == 0);
}

View file

@ -21,6 +21,20 @@
#include "common.h"
#define HITAG_CRYPTOKEY_SIZE 6
#define HITAG_PASSWORD_SIZE 4
#define HITAG_UID_SIZE 4
#define HITAG_BLOCK_SIZE 4
#define HITAG2_MAX_BYTE_SIZE 48
// need to see which limits these cards has
#define HITAG1_MAX_BYTE_SIZE 64
#define HITAGS_MAX_BYTE_SIZE 64
#define HITAGU_MAX_BYTE_SIZE 64
#define HITAG2_CONFIG_BLOCK 3
#define HITAG2_CONFIG_OFFSET (HITAG_BLOCK_SIZE * HITAG2_CONFIG_BLOCK)
int CmdLFHitag(const char *Cmd);
int readHitagUid(void);