mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-14 10:37:23 -07:00
annotation of the hitag2 protocol now properly identifies different parts without shifting of whole hex arrays. Took the idea from RFIdler (@adamLLaurie) where he treats it like a binary string instead. It works. Problem: We use whole bytes in our logging protocol and Hitag2 uses 5, 10, 32, 64 bits commands. START_AUTH is 11000, which as a MSB aligned byte is 0xC0. Now we shift it down to LSB centered and 11000 becomes 0x18. This reduces all issues with handing the rest of the array. \n\nTake note that our protocol uses whole bytes. I had to print out number of actually captured bits in the trace log now. Otherwise 65 or 71 bits would not look any different but will not work when used with other tools. This also means we abuse the logging protocol by using the parity byte arry to store number of "left over bits" in the par[0] position.
This commit is contained in:
parent
529d57a181
commit
c66e781a9c
4 changed files with 155 additions and 62 deletions
|
@ -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]
|
||||
- Fixed `lf hitag list` - improved HITAG2 protocol annotation (@iceman1001)
|
||||
|
||||
## [Zenith.4.18340][2024-03-20]
|
||||
- Changed `hf mf info` - some detections (@iceman1001)
|
||||
|
|
|
@ -17,15 +17,19 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
#include "cmdlfhitag.h"
|
||||
#include <ctype.h>
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "comms.h"
|
||||
#include "cmdtrace.h"
|
||||
#include "commonutil.h"
|
||||
#include "hitag.h"
|
||||
#include "fileutils.h" // savefile
|
||||
#include "protocols.h" // defines
|
||||
#include "fileutils.h" // savefile
|
||||
#include "protocols.h" // defines
|
||||
#include "cliparser.h"
|
||||
#include "crc.h"
|
||||
#include "graph.h" // MAX_GRAPH_TRACE_LEN
|
||||
#include "lfdemod.h"
|
||||
#include "cmddata.h" // setDemodBuff
|
||||
#include "pm3_cmd.h" // return codes
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
|
@ -375,54 +379,119 @@ static void print_hitag2_blocks(uint8_t *d, uint16_t n) {
|
|||
void annotateHitag1(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
|
||||
}
|
||||
|
||||
void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
|
||||
|
||||
// iceman: live decrypt of trace?
|
||||
if (is_response) {
|
||||
|
||||
|
||||
uint8_t cmdbits = (cmd[0] & 0xC0) >> 6;
|
||||
static struct {
|
||||
enum {
|
||||
STATE_HALT,
|
||||
STATE_START_AUTH,
|
||||
STATE_AUTH,
|
||||
STATE_ENCRYPTED,
|
||||
} state;
|
||||
} _ht2state;
|
||||
|
||||
|
||||
void annotateHitag2_init(void) {
|
||||
_ht2state.state = STATE_HALT;
|
||||
}
|
||||
|
||||
void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response) {
|
||||
|
||||
// I think its better to handle this log bytes as a long array of bits instead.
|
||||
// 1100 0
|
||||
// 1100 0001 1100 0000 00
|
||||
if (cmdsize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *binstr = (char *)calloc((cmdsize * 8) + 1, sizeof(uint8_t));
|
||||
if (binstr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
bytes_2_binstr(binstr, cmd, cmdsize);
|
||||
|
||||
size_t bn = strlen(binstr);
|
||||
if (bits) {
|
||||
if (cmdsize == 1) {
|
||||
if (cmdbits == HITAG2_START_AUTH) {
|
||||
snprintf(exp, size, "START AUTH");
|
||||
return;
|
||||
}
|
||||
if (cmdbits == HITAG2_HALT) {
|
||||
snprintf(exp, size, "HALT");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdsize == 3) {
|
||||
if (cmdbits == HITAG2_START_AUTH) {
|
||||
// C 1 C 0
|
||||
// 1100 0 00 1 1100 000
|
||||
uint8_t page = (cmd[0] & 0x38) >> 3;
|
||||
uint8_t inv_page = ((cmd[0] & 0x1) << 2) | ((cmd[1] & 0xC0) >> 6);
|
||||
snprintf(exp, size, "READ page(%x) %x", page, inv_page);
|
||||
return;
|
||||
}
|
||||
if (cmdbits == HITAG2_WRITE_PAGE) {
|
||||
uint8_t page = (cmd[0] & 0x38) >> 3;
|
||||
uint8_t inv_page = ((cmd[0] & 0x1) << 2) | ((cmd[1] & 0xC0) >> 6);
|
||||
snprintf(exp, size, "WRITE page(%x) %x", page, inv_page);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmdsize == 9) {
|
||||
snprintf(exp, size, "Nr Ar Is response");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
if (cmdsize == 9) {
|
||||
snprintf(exp, size, "Nr Ar");
|
||||
return;
|
||||
bn = bits;
|
||||
} else if (cmdsize > 1) {
|
||||
bn = ((cmdsize - 1) * 8) + bits;
|
||||
}
|
||||
}
|
||||
|
||||
// 11000 AUTH only one with 5 bits. cmdsize 1
|
||||
switch(bn) {
|
||||
case 5: {
|
||||
_ht2state.state = STATE_HALT;
|
||||
if(memcmp(binstr, HITAG2_START_AUTH, 5) == 0) {
|
||||
snprintf(exp, size, "START AUTH");
|
||||
_ht2state.state = STATE_START_AUTH;
|
||||
} else {
|
||||
snprintf(exp, size, "?");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
|
||||
if (_ht2state.state == STATE_ENCRYPTED) {
|
||||
snprintf(exp, size, "ENC CMD");
|
||||
break;
|
||||
}
|
||||
|
||||
if(memcmp(binstr, HITAG2_HALT, 2) == 0) {
|
||||
snprintf(exp, size, "HALT");
|
||||
_ht2state.state = STATE_HALT;
|
||||
break;
|
||||
}
|
||||
if(memcmp(binstr, HITAG2_READ_PAGE, 2) == 0) {
|
||||
snprintf(exp, size, "READ_PAGE (%u)", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if(memcmp(binstr, HITAG2_READ_PAGE_INVERTED, 2) == 0) {
|
||||
snprintf(exp, size, "READ_PAGE_INVERTED (%u)", 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if(memcmp(binstr, HITAG2_WRITE_PAGE, 2) == 0) {
|
||||
snprintf(exp, size, "WRITE_PAGE ()");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 32: { // password or data
|
||||
if (_ht2state.state == STATE_START_AUTH) {
|
||||
if (is_response) {
|
||||
snprintf(exp, size, "UID");
|
||||
} else {
|
||||
snprintf(exp, size, "PWD: " _GREEN_("0x%02X%02X%02X%02X"), cmd[0], cmd[1], cmd[2], cmd[3]);
|
||||
_ht2state.state = STATE_AUTH;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (_ht2state.state == STATE_AUTH) {
|
||||
snprintf(exp, size, "DATA");
|
||||
} else{
|
||||
snprintf(exp, size, "?");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 64: { // crypto handshake
|
||||
snprintf(exp, size, "AUTH: Nr Ar");
|
||||
_ht2state.state = STATE_ENCRYPTED;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
snprintf(exp, size, "?");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(binstr);
|
||||
}
|
||||
|
||||
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
|
||||
|
|
|
@ -605,7 +605,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t partialbytebuff = 0;
|
||||
// uint8_t partialbytebuff = 0;
|
||||
uint8_t offset = 0;
|
||||
for (int j = 0; j < data_len && (j / TRACE_MAX_HEX_BYTES) < TRACE_MAX_HEX_BYTES; j++) {
|
||||
uint8_t parityBits = parityBytes[j >> 3];
|
||||
|
@ -639,18 +639,35 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
|
||||
}
|
||||
|
||||
} else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS)) && (parityBytes[0] > 0)) {
|
||||
// handle partial bytes
|
||||
} else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS))) {
|
||||
|
||||
// handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES
|
||||
// This part prints the number of bits in the trace entry for hitag.
|
||||
uint8_t nbits = parityBytes[0];
|
||||
if (j == 0) {
|
||||
partialbytebuff = frame[0] << nbits;
|
||||
snprintf(line[0], 120, "%02x(%i) ", frame[0] >> (8 - nbits), nbits);
|
||||
offset = 2;
|
||||
|
||||
// only apply this to lesser than one byte
|
||||
if (data_len == 1) {
|
||||
|
||||
if (nbits == 5) {
|
||||
snprintf(line[0], 120, "%2u: %02x ", nbits, frame[0] >> (8 - nbits));
|
||||
} else {
|
||||
snprintf(line[0], 120, "%2u: %02x ", nbits, frame[0] >> (8 - nbits));
|
||||
}
|
||||
|
||||
} else {
|
||||
if (nbits == 0) {
|
||||
snprintf(line[0], 120, "%2u: %02x ", (data_len * 8), frame[0]);
|
||||
} else {
|
||||
snprintf(line[0], 120, "%2u: %02x ", ((data_len - 1) * 8) + nbits, frame[0]);
|
||||
}
|
||||
}
|
||||
offset = 4;
|
||||
|
||||
} else {
|
||||
uint8_t byte = partialbytebuff | (frame[j] >> (8 - nbits));
|
||||
partialbytebuff = frame[j] << nbits;
|
||||
snprintf(line[j / 18] + ((j % 18) * 4) + offset, 120, "%02x ", byte);
|
||||
snprintf(line[j / 18] + ((j % 18) * 4) + offset, 120, "%02x ", frame[j]);
|
||||
}
|
||||
|
||||
} else {
|
||||
snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x ", frame[j]);
|
||||
}
|
||||
|
@ -776,7 +793,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
|||
annotateHitag1(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
||||
break;
|
||||
case PROTO_HITAG2:
|
||||
annotateHitag2(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
||||
annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse);
|
||||
break;
|
||||
case PROTO_HITAGS:
|
||||
annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
||||
|
@ -1379,8 +1396,9 @@ int CmdTraceList(const char *Cmd) {
|
|||
if (protocol == ISO_7816_4)
|
||||
PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings N/A");
|
||||
|
||||
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS)
|
||||
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) {
|
||||
PrintAndLogEx(INFO, _YELLOW_("Hitag1 / Hitag2 / HitagS") " - Timings in ETU (8us)");
|
||||
}
|
||||
|
||||
if (protocol == FELICA) {
|
||||
if (use_us)
|
||||
|
@ -1427,8 +1445,14 @@ int CmdTraceList(const char *Cmd) {
|
|||
PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------");
|
||||
|
||||
// clean authentication data used with the mifare classic decrypt fct
|
||||
if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS)
|
||||
if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) {
|
||||
ClearAuthData();
|
||||
}
|
||||
|
||||
// reset hitag state machine
|
||||
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) {
|
||||
annotateHitag2_init();
|
||||
}
|
||||
|
||||
uint32_t previous_EOT = 0;
|
||||
uint32_t *prev_EOT = NULL;
|
||||
|
|
|
@ -908,12 +908,11 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
|||
#define HITAG1_HALT 0x70 // left 4 bits only, followed by 8 bits (dummy) page and 8 bits CRC
|
||||
|
||||
// HITAG2 commands
|
||||
#define HITAG2_START_AUTH 0x3 // left 5 bits only
|
||||
#define HITAG2_HALT 0x0 // left 5 bits only
|
||||
|
||||
#define HITAG2_READ_PAGE 0x3 // page number in bits 5 to 3, page number inverted in bit 0 and following 2 bits
|
||||
#define HITAG2_READ_PAGE_INVERTED 0x1 // page number in bits 5 to 3, page number inverted in bit 0 and following 2 bits
|
||||
#define HITAG2_WRITE_PAGE 0x2 // page number in bits 5 to 3, page number
|
||||
#define HITAG2_START_AUTH "11000" // get UID and/or start the authentication process
|
||||
#define HITAG2_READ_PAGE "11" // read page after auth
|
||||
#define HITAG2_READ_PAGE_INVERTED "01" // as read page but all bits inverted
|
||||
#define HITAG2_WRITE_PAGE "10" // write page after auth
|
||||
#define HITAG2_HALT "00" // silence currently authenticated tag
|
||||
|
||||
|
||||
// HITAG S commands
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue