Merge branch 'master' into master

Signed-off-by: Jarek Barwinski <116510448+jareckib@users.noreply.github.com>
This commit is contained in:
Jarek Barwinski 2025-03-20 15:21:41 +00:00 committed by GitHub
commit c5dd239de5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
79 changed files with 6133 additions and 1827 deletions

View file

@ -21,6 +21,7 @@ Have you followed the instructions properly? ie, flashed bootrom seperately fi
**Describe the bug**
A clear and concise description of what the bug is.
Please include text output of the bug happening.
**To Reproduce**
Steps to reproduce the behavior:
@ -40,7 +41,7 @@ If applicable, add screenshots to help explain your problem.
- inside proxmark3 client run the following commands and paste the output here.
- hw version
- hw status
- data tune
- hw tune
**Additional context**
Add any other context about the problem here.

View file

@ -3,6 +3,14 @@ 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 `trace list -t` - shortend the hitag types (@iceman1001)
- Added Be-Tech identification (@iceman1001)
- Added `lf em 410x clone --htu` clone EM410x ID to Hitag µ/8265 (@douniwan5788)
- Added `lf hitag htu` support for Hitag µ/8265 (@douniwan5788)
- Added `hf mfu aesauth` based on existing UL AES support (@doegox)
- Changed `hf mfu sim` deny OTP changes with all zeros (@iceman1001)
- Added missing file in CMakeLists.txt (@iceman1001)
- Changed `lf em 4x70` internals on ARM side; Enabling improved debugging and reliability (@henrygab)
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
- Changed `trace list -t seos` - improved annotation (@iceman1001)
@ -16,7 +24,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001)
- Added sample wiegand format 56bit (@iceman1001)
- Changed Wiegand formats to include number of bits (@iceman1001)
- Fix compilation warning in hitagS (@iceman1001)
- Fixed compilation warning in hitagS (@iceman1001)
- Added new wiegand format H800002 (@jmichelp)
- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001)
- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue)

View file

@ -72,7 +72,7 @@ else
endif
ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS)))
SRC_HITAG = hitag2_crypto.c hitag2.c hitagS.c hitag2_crack.c
SRC_HITAG = hitag2_crypto.c hitag_common.c hitag2.c hitagS.c hitagu.c hitag2_crack.c
APP_CFLAGS += -I../common/hitag2
else
SRC_HITAG =

View file

@ -41,6 +41,7 @@
#include "hitag2.h"
#include "hitag2_crack.h"
#include "hitagS.h"
#include "hitagu.h"
#include "em4x50.h"
#include "em4x70.h"
#include "iclass.h"
@ -1200,7 +1201,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content
hts_simulate((bool)packet->oldarg[0], packet->data.asBytes, true);
hts_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true);
break;
}
case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file
@ -1218,7 +1219,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_LF_HITAGS_UID: {
hts_read_uid(NULL, false, true);
hts_read_uid(NULL, true, true);
break;
}
case CMD_LF_HITAG2_WRITE: {
@ -1232,6 +1233,25 @@ static void PacketReceived(PacketCommandNG *packet) {
memcpy(mem, payload->data, payload->len);
break;
}
case CMD_LF_HITAGU_READ: {
lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes;
htu_read(payload, true);
break;
}
case CMD_LF_HITAGU_WRITE: {
lf_hitag_data_t *payload = (lf_hitag_data_t *)packet->data.asBytes;
htu_write_page(payload, true);
break;
}
case CMD_LF_HITAGU_SIMULATE: {
htu_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true);
break;
}
case CMD_LF_HITAGU_UID: {
htu_read_uid(NULL, true, true);
break;
}
#endif
#ifdef WITH_EM4x50

File diff suppressed because it is too large Load diff

View file

@ -19,12 +19,11 @@
#ifndef EM4x70_H
#define EM4x70_H
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include "../include/em4x70.h"
typedef struct {
uint8_t data[32];
} em4x70_tag_t;
typedef enum {
RISING_EDGE,
FALLING_EDGE

View file

@ -241,8 +241,8 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
// We try 10 times, or if answer was received.
int len = 25;
do {
// end-of-reception response packet data, wait approx. 501μs
// end-of-transmission command packet data, wait approx. 197μs
// end-of-reception response packet data, wait approx. 501µs
// end-of-transmission command packet data, wait approx. 197µs
// polling card
TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0);

View file

@ -320,7 +320,7 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_
// reader/writer
// returns how long it took
static uint32_t hitag_reader_send_bit(int bit) {
static uint32_t hitag2_reader_send_bit(int bit) {
// Binary pulse length modulation (BPLM) is used to encode the data stream
// This means that a transmission of a one takes longer than that of a zero
@ -349,13 +349,13 @@ static uint32_t hitag_reader_send_bit(int bit) {
// reader / writer commands
// frame_len is in number of bits?
static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) {
static uint32_t hitag2_reader_send_frame(const uint8_t *frame, size_t frame_len) {
WDT_HIT();
uint32_t wait = 0;
// Send the content of the frame
for (size_t i = 0; i < frame_len; i++) {
wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
wait += hitag2_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
}
// Send EOF
@ -378,14 +378,14 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
// reader / writer commands
// frame_len is in number of bits?
static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_len) {
static uint32_t hitag2_reader_send_framebits(const uint8_t *frame, size_t frame_len) {
WDT_HIT();
uint32_t wait = 0;
// Send the content of the frame
for (size_t i = 0; i < frame_len; i++) {
wait += hitag_reader_send_bit(frame[i]);
wait += hitag2_reader_send_bit(frame[i]);
}
// Send EOF
@ -1863,7 +1863,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
}
// Transmit the reader frame
command_duration = hitag_reader_send_frame(tx, txlen);
command_duration = hitag2_reader_send_frame(tx, txlen);
response_start = command_start + command_duration;
// Let the antenna and ADC values settle
@ -2214,7 +2214,7 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
}
// Transmit the reader frame
command_duration = hitag_reader_send_frame(tx, txlen);
command_duration = hitag2_reader_send_frame(tx, txlen);
// global write state variable used
// tearoff occurred
@ -2434,9 +2434,9 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start
// Transmit the reader frame
if (send_bits) {
*cmd_duration = hitag_reader_send_framebits(tx, txlen);
*cmd_duration = hitag2_reader_send_framebits(tx, txlen);
} else {
*cmd_duration = hitag_reader_send_frame(tx, txlen);
*cmd_duration = hitag2_reader_send_frame(tx, txlen);
}
*resp_start = (*cmd_start + *cmd_duration);

View file

@ -19,6 +19,7 @@
//-----------------------------------------------------------------------------
#include "hitagS.h"
#include "hitag_common.h"
#include "proxmark3_arm.h"
#include "cmd.h"
@ -33,12 +34,8 @@
#include "lfadc.h"
#include "crc.h"
#include "protocols.h"
#include "hitag.h"
#include "appmain.h" // tearoff_hook()
#define CRC_PRESET 0xFF
#define CRC_POLYNOM 0x1D
static struct hitagS_tag tag = {
.data.pages = {
// Plain mode: | Authentication mode:
@ -58,13 +55,6 @@ static uint8_t page_to_be_written = 0;
static int block_data_left = 0;
static bool enable_page_tearoff = false;
typedef enum modulation {
AC2K = 0,
AC4K,
MC4K,
MC8K
} MOD;
static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1;
static MOD m = AC2K; // used modulation
static uint32_t reader_selected_uid;
@ -72,9 +62,6 @@ static int rotate_uid = 0;
static int sof_bits; // number of start-of-frame bits
static uint8_t pwdh0, pwdl0, pwdl1; // password bytes
static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number
static uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, ~47min
#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
//#define SENDBIT_TEST
@ -98,60 +85,6 @@ datasheet HitagS_V11.pdf bytes in tables printed 3 2 1 0
#define ht2bs_4b(a,b,c,d) (~(((d|c)&(a^b))^(d|a|b)))
#define ht2bs_5c(a,b,c,d,e) (~((((((c^e)|d)&a)^b)&(c^b))^(((d^e)|a)&((d^b)|c))))
// Sam7s has several timers, we will use the source TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK)
// TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, Timer is running at 48MHz/32 = 1500 KHz
// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
// T0 = TIMER_CLOCK3 / 125000 = 12
#define T0 12
#define HITAG_FRAME_LEN 20
// TC0 and TC1 are 16-bit counters and will overflow after 5461 * T0
// Ensure not to set these timings above 5461 (~43ms) when comparing without considering overflow, as they will never reach that value.
#define HITAG_T_STOP 36 /* T_EOF should be > 36 */
#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
#define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */
#define HITAG_T_0 20 /* T[0] should be 18..22 */
#define HITAG_T_1 28 /* T[1] should be 26..30 */
// #define HITAG_T_EOF 40 /* T_EOF should be > 36 */
#define HITAG_T_EOF 80 /* T_EOF should be > 36 */
#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */
#define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */
#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */
#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */
#define HITAG_T_TAG_ONE_HALF_PERIOD 10
#define HITAG_T_TAG_TWO_HALF_PERIOD 25
#define HITAG_T_TAG_THREE_HALF_PERIOD 41
#define HITAG_T_TAG_FOUR_HALF_PERIOD 57
#define HITAG_T_TAG_HALF_PERIOD 16
#define HITAG_T_TAG_FULL_PERIOD 32
#define HITAG_T_TAG_CAPTURE_ONE_HALF 13
#define HITAG_T_TAG_CAPTURE_TWO_HALF 25
#define HITAG_T_TAG_CAPTURE_THREE_HALF 41
#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57
/*
* Implementation of the crc8 calculation from Hitag S
* from http://www.proxmark.org/files/Documents/125%20kHz%20-%20Hitag/HitagS.V11.pdf
*/
static void calc_crc(unsigned char *crc, unsigned char data, unsigned char Bitcount) {
*crc ^= data; // crc = crc (exor) data
do {
if (*crc & 0x80) { // if (MSB-CRC == 1)
*crc <<= 1; // CRC = CRC Bit-shift left
*crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM
} else {
*crc <<= 1; // CRC = CRC Bit-shift left
}
} while (--Bitcount);
}
static void update_tag_max_page(void) {
//check which memorysize this tag has
if (tag.data.s.config.MEMT == 0x00) {
@ -165,261 +98,13 @@ static void update_tag_max_page(void) {
}
}
static void hitag_send_bit(int bit, bool ledcontrol) {
if (ledcontrol) LED_A_ON();
// Reset clock for the next bit
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
switch (m) {
case AC2K: {
if (bit == 0) {
// AC Coding --__
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
} else {
// AC coding -_-_
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 48) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
}
if (ledcontrol) LED_A_OFF();
break;
}
case AC4K: {
if (bit == 0) {
// AC Coding --__
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {};
} else {
// AC coding -_-_
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 24) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
}
if (ledcontrol) LED_A_OFF();
break;
}
case MC4K: {
if (bit == 0) {
// Manchester: Unloaded, then loaded |__--|
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
} else {
// Manchester: Loaded, then unloaded |--__|
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
}
if (ledcontrol) LED_A_OFF();
break;
}
case MC8K: {
if (bit == 0) {
// Manchester: Unloaded, then loaded |__--|
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
} else {
// Manchester: Loaded, then unloaded |--__|
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
}
if (ledcontrol) LED_A_OFF();
break;
}
default: {
break;
}
}
}
static void hitag_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) {
DBG Dbprintf("hitag_send_frame: (%i) %02X %02X %02X %02X", frame_len, frame[0], frame[1], frame[2], frame[3]);
// The beginning of the frame is hidden in some high level; pause until our bits will have an effect
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
HIGH(GPIO_SSC_DOUT);
switch (m) {
case AC4K:
case MC8K: {
while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; //FADV
break;
}
case AC2K:
case MC4K: {
while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; //STD + ADV
break;
}
}
// SOF - send start of frame
for (size_t i = 0; i < sof_bits; i++) {
hitag_send_bit(1, ledcontrol);
}
// Send the content of the frame
for (size_t i = 0; i < frame_len; i++) {
hitag_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol);
}
LOW(GPIO_SSC_DOUT);
}
static void hitag_reader_send_bit(int bit, bool ledcontrol) {
if (ledcontrol) LED_A_ON();
// Reset clock for the next bit
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV != 0) {};
// Binary puls length modulation (BPLM) is used to encode the data stream
// This means that a transmission of a one takes longer than that of a zero
HIGH(GPIO_SSC_DOUT);
#ifdef SENDBIT_TEST
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * 6) {};
LOW(GPIO_SSC_DOUT);
if (bit == 0) {
// Zero bit: |_-|
while (AT91C_BASE_TC0->TC_CV < T0 * 11) {};
} else {
// One bit: |_--|
while (AT91C_BASE_TC0->TC_CV < T0 * 14) {};
}
#else
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
LOW(GPIO_SSC_DOUT);
if (bit == 0) {
// Zero bit: |_-|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {};
} else {
// One bit: |_--|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {};
}
#endif
if (ledcontrol) LED_A_OFF();
}
static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) {
// Send the content of the frame
for (size_t i = 0; i < frame_len; i++) {
hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol);
}
// send EOF
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV != 0) {};
HIGH(GPIO_SSC_DOUT);
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
LOW(GPIO_SSC_DOUT);
}
static void hts_stop_clock(void) {
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
}
static void hts_init_clock(void) {
// Enable Peripheral Clock for
// Timer Counter 0, used to measure exact timing before answering
// Timer Counter 1, used to capture edges of the tag frames
// Timer Counter 2, used to log trace time
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2);
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
// Disable timer during configuration
hts_stop_clock();
// TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
// TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger,
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3)
| AT91C_TC_ABETRG // TIOA is used as an external trigger
| AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge
| AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA
| AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA
// TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
// Enable and reset counters
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
// synchronized startup procedure
// In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right?
while (AT91C_BASE_TC0->TC_CV != 0) {}; // wait until TC0 returned to zero
// reset timestamp
timestamp_high = 0;
}
/*
* to check if the right uid was selected
*/
static int check_select(const uint8_t *rx, uint32_t uid) {
// global var?
concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32);
concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32, false);
reader_selected_uid = BSWAP_32(reader_selected_uid);
if (reader_selected_uid == uid) {
@ -506,7 +191,6 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
DBG DbpString("SELECT");
if ((rx[0] & 0xf8) == HITAGS_SELECT && check_select(rx, BSWAP_32(tag.data.s.uid_le)) == 1) {
DBG DbpString("SELECT match");
//if the right tag was selected
@ -519,13 +203,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
if (protocol_mode != HITAGS_UID_REQ_STD) {
//add crc8
crc = CRC8Hitag1Bits(tx, 32);
*txlen += 8;
crc = CRC_PRESET;
for (int i = 0; i < 4; i++) {
calc_crc(&crc, tx[i], 8);
}
tx[4] = crc;
}
}
@ -546,26 +225,31 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
ht2_hitag2_byte(&state);
}
//send con2, pwdh0, pwdl0, pwdl1 encrypted as a response
tx[0] = ht2_hitag2_byte(&state) ^ tag.data.pages[HITAGS_CONFIG_PADR][2];
tx[1] = ht2_hitag2_byte(&state) ^ tag.data.s.config.pwdh0;
tx[2] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl0;
tx[3] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl1;
// store plaintext first
tx[0] = tag.data.pages[HITAGS_CONFIG_PADR][2];
tx[1] = tag.data.s.config.pwdh0;
tx[2] = tag.data.s.pwdl0;
tx[3] = tag.data.s.pwdl1;
if (protocol_mode != HITAGS_UID_REQ_STD) {
//add crc8
// add crc8
*txlen += 8;
crc = CRC_PRESET;
calc_crc(&crc, tag.data.pages[HITAGS_CONFIG_PADR][2], 8);
calc_crc(&crc, tag.data.s.config.pwdh0, 8);
calc_crc(&crc, tag.data.s.pwdl0, 8);
calc_crc(&crc, tag.data.s.pwdl1, 8);
tx[4] = (crc ^ ht2_hitag2_byte(&state));
crc = CRC8Hitag1Bits(tx, 32);
tx[4] = crc;
}
// then xor with keystream
tx[0] ^= ht2_hitag2_byte(&state);
tx[1] ^= ht2_hitag2_byte(&state);
tx[2] ^= ht2_hitag2_byte(&state);
tx[3] ^= ht2_hitag2_byte(&state);
if (protocol_mode != HITAGS_UID_REQ_STD) {
tx[4] ^= ht2_hitag2_byte(&state);
}
/*
* some readers do not allow to authenticate multiple times in a row with the same tag.
* use this to change the uid between authentications.
if (rotate_uid % 2 == 0) {
tag.data.s.uid_le = 0x44332211;
} else {
@ -621,11 +305,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
if (protocol_mode != HITAGS_UID_REQ_STD) {
//add crc8
crc = CRC8Hitag1Bits(tx, 32);
*txlen += 8;
crc = CRC_PRESET;
for (int i = 0; i < 4; i++) {
calc_crc(&crc, tx[i], 8);
}
tx[4] = crc;
}
@ -643,10 +324,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
if (protocol_mode != HITAGS_UID_REQ_STD) {
//add crc8
crc = CRC_PRESET;
for (int i = 0; i < *txlen / 8; i++) {
calc_crc(&crc, tx[i], 8);
}
crc = CRC8Hitag1Bits(tx, *txlen);
*txlen += 8;
tx[16] = crc;
}
@ -692,26 +370,17 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
/*
* Emulates a Hitag S Tag with the given data from the .hts file
*/
void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
StopTicks();
void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol) {
int overflow = 0;
uint8_t rx[HITAG_FRAME_LEN];
uint8_t rx[HITAG_FRAME_LEN] = {0};
size_t rxlen = 0;
uint8_t tx[HITAG_FRAME_LEN];
size_t txlen = 0;
// Reset the received frame, frame count and timing info
memset(rx, 0x00, sizeof(rx));
// free eventually allocated BigBuf memory
BigBuf_free();
BigBuf_Clear_ext(false);
// Enable tracing
set_tracing(true);
DbpString("Starting Hitag S simulation");
tag.pstate = HT_READY;
@ -738,67 +407,16 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
);
}
// Set up simulator mode, frequency divisor which will drive the FPGA
// and analog mux selection.
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
hitag_setup_fpga(0, threshold, ledcontrol);
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
// Configure output pin that is connected to the FPGA (for modulating)
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
// Disable modulation at default, which means release resistance
LOW(GPIO_SSC_DOUT);
hts_init_clock();
if (ledcontrol) LED_D_ON();
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
uint32_t start_time = 0;
WDT_HIT();
// Receive frame, watch for at most T0*EOF periods
while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) {
// Check if rising edge in modulation is detected
if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) {
// Retrieve the new timing values
int rb = (AT91C_BASE_TC1->TC_RB / T0) + overflow;
overflow = 0;
// Reset timer every frame, we have to capture the last edge for timing
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
if (ledcontrol) LED_B_ON();
// Capture reader cmd start timestamp
if (start_time == 0) {
start_time = TIMESTAMP - HITAG_T_LOW;
}
// Capture reader frame
if (rb >= HITAG_T_STOP) {
if (rxlen != 0) {
//DbpString("weird0?");
}
} else if (rb >= HITAG_T_1_MIN) {
// '1' bit
rx[rxlen / 8] |= 1 << (7 - (rxlen % 8));
rxlen++;
} else if (rb >= HITAG_T_0_MIN) {
// '0' bit
rx[rxlen / 8] |= 0 << (7 - (rxlen % 8));
rxlen++;
} else {
// Ignore weird value, is to small to mean anything
}
}
}
// Receive commands from the reader
hitag_tag_receive_frame(rx, sizeof(rx), &rxlen, &start_time, ledcontrol, &overflow);
// Check if frame was captured
if (rxlen > 0) {
@ -821,7 +439,7 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
if (txlen > 0) {
// Transmit the tag frame
start_time = TIMESTAMP;
hitag_send_frame(tx, txlen, ledcontrol);
hitag_tag_send_frame(tx, txlen, sof_bits, m, ledcontrol);
LogTraceBits(tx, txlen, start_time, TIMESTAMP, false);
}
@ -842,152 +460,13 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
}
hts_stop_clock();
set_tracing(false);
lf_finalize(ledcontrol);
hitag_cleanup(ledcontrol);
// release allocated memory from BigBuff.
BigBuf_free();
DbpString("Sim stopped");
}
static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol) {
// Reset values for receiving frames
memset(rx, 0x00, sizeofrx);
*rxlen = 0;
int lastbit = 1;
bool bSkip = true;
uint32_t errorCount = 0;
bool bStarted = false;
uint16_t next_edge_event = AT91C_TC_LDRBS;
int double_speed = (m == AC4K || m == MC8K) ? 2 : 1;
uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0;
uint8_t edges[160] = {0};
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
// Check if edge in tag modulation is detected
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
// only use AT91C_TC_LDRBS falling edge for now
if (next_edge_event == AT91C_TC_LDRBS) continue;
// Retrieve the new timing values
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0;
edges[rb_i++] = rb;
// Reset timer every frame, we have to capture the last edge for timing
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
if (ledcontrol) LED_B_ON();
// Capture tag response timestamp
if (*rxlen == 0) *resptime = TIMESTAMP;
// Capture tag frame (manchester decoding using only falling edges)
if (bStarted == false) {
if (rb >= HITAG_T_WAIT_RESP) {
bStarted = true;
// We always receive a 'one' first, which has the falling edge after a half period |-_|
rx[0] = 0x80;
(*rxlen)++;
} else {
errorCount++;
}
} else {
// Anticollision Coding
if (m == AC2K || m == AC4K) {
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
// Anticollision Coding example |--__|--__| (00)
lastbit = 0;
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
(*rxlen)++;
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
// Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01)
lastbit = !lastbit;
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
(*rxlen)++;
bSkip = !!lastbit;
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
// Anticollision Coding example |-_-_| (1)
if (bSkip == false) {
lastbit = 1;
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
(*rxlen)++;
}
bSkip = !bSkip;
} else {
// Ignore weird value, is to small to mean anything
errorCount++;
}
} else {
// Manchester coding
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
// Manchester coding example |-_|_-|-_| (101)
rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8));
(*rxlen)++;
rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8));
(*rxlen)++;
h4++;
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
// Manchester coding example |_-|...|_-|-_| (0...01)
rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8));
(*rxlen)++;
// We have to skip this half period at start and add the 'one' the second time
if (bSkip == false) {
rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8));
(*rxlen)++;
}
lastbit = !lastbit;
bSkip = !bSkip;
h3++;
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
// bit is same as last bit
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
(*rxlen)++;
h2++;
} else {
// Ignore weird value, is to small to mean anything
errorCount++;
}
}
}
}
// if we saw over 100 weird values break it probably isn't hitag...
if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) {
break;
}
// We can break this loop if we received the last bit from a frame
// max periods between 2 falling edge
// RTF AC64 |--__|--__| (00) 64 * T0
// RTF MC32 |_-|-_|_-| (010) 48 * T0
if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) {
if ((*rxlen)) {
break;
}
}
}
DBG Dbprintf("RX0 %i:%02X.. err:%i resptime:%i h2:%i h3:%i h4:%i edges:", *rxlen, rx[0], errorCount, *resptime, h2, h3, h4);
DBG Dbhexdump(rb_i, edges, false);
}
static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, bool ledcontrol, bool ac_seq) {
uint32_t start_time;
@ -995,6 +474,9 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
// Disable timer 1 with external trigger to avoid triggers during our own modulation
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
DBG Dbprintf("tx %d bits:", txlen);
DBG Dbhexdump((txlen + 7) / 8, tx, false);
// Wait for HITAG_T_WAIT_SC carrier periods after the last tag bit before transmitting,
// Since the clock counts since the last falling edge, a 'one' means that the
// falling edge occurred halfway the period. with respect to this falling edge,
@ -1005,7 +487,7 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
start_time = TIMESTAMP;
// Transmit the reader frame
hitag_reader_send_frame(tx, txlen, ledcontrol);
hitag_reader_send_frame(tx, txlen, ledcontrol, false);
if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) {
return PM3_ETEAROFF;
@ -1018,7 +500,11 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
hts_set_frame_modulation(protocol_mode, ac_seq);
hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol);
hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, m, sof_bits);
// hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol);
DBG Dbprintf("rx %d bits:", *rxlen);
DBG Dbhexdump((*rxlen + 7) / 8, rx, false);
// Check if frame was captured and store it
if (*rxlen > 0) {
@ -1033,23 +519,8 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
Dbprintf("htS: sizeofrx... %zu", sizeofrx);
DbpString("htS: response_bit:");
Dbhexdump(*rxlen, response_bit, false);
Dbprintf("htS: skipping %d bit SOF", sof_bits);
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
DBG DbpString("htS: Warning, not all bits of SOF are 1");
}
}
// remove first sof_bits bits SOF
for (size_t i = 0; i < (*rxlen + 8) / 8; i++) {
rx[i] <<= sof_bits;
if (i + 1 < (*rxlen + 8) / 8) {
rx[i] |= (rx[i + 1] >> (8 - sof_bits));
}
}
*rxlen -= sof_bits;
LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false);
}
@ -1057,40 +528,18 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
}
static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) {
size_t txlen = 0;
size_t rxlen = 0;
StopTicks();
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
// Clean up trace and prepare it for storing frames
set_tracing(true);
clear_trace();
if (ledcontrol) LED_D_ON();
hts_init_clock();
// Set fpga in edge detect with reader field, we can modulate as reader now
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
// Configure output and enable pin that is connected to the FPGA (for modulating)
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
// Disable modulation at default, which means enable the field
LOW(GPIO_SSC_DOUT);
// Setup FPGA and initialize
hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol);
// UID request standard 00110
// UID request Advanced 1100x
// UID request FAdvanced 11010
size_t txlen = 0;
size_t rxlen = 0;
protocol_mode = packet->mode;
uint8_t cmd = protocol_mode;
txlen = concatbits(tx, txlen, &cmd, 0, 5);
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true);
if (rxlen != 32) {
@ -1105,10 +554,10 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
// select uid
txlen = 0;
cmd = HITAGS_SELECT;
txlen = concatbits(tx, txlen, &cmd, 0, 5);
txlen = concatbits(tx, txlen, rx, 0, 32);
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
txlen = concatbits(tx, txlen, rx, 0, 32, false);
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
@ -1140,8 +589,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
}
txlen = 0;
txlen = concatbits(tx, txlen, rnd, 0, 32);
txlen = concatbits(tx, txlen, auth_ks, 0, 32);
txlen = concatbits(tx, txlen, rnd, 0, 32, false);
txlen = concatbits(tx, txlen, auth_ks, 0, 32, false);
DBG DbpString("Authenticating using key:");
DBG Dbhexdump(6, packet->key, false);
@ -1173,13 +622,13 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
// send write page request
txlen = 0;
cmd = HITAGS_WRITE_PAGE;
txlen = concatbits(tx, txlen, &cmd, 0, 4);
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
uint8_t addr = 64;
txlen = concatbits(tx, txlen, &addr, 0, 8);
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
@ -1189,9 +638,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
}
txlen = 0;
txlen = concatbits(tx, txlen, packet->pwd, 0, 32);
txlen = concatbits(tx, txlen, packet->pwd, 0, 32, false);
crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
@ -1287,11 +736,11 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
//send read request
size_t txlen = 0;
uint8_t cmd = HITAGS_READ_PAGE;
txlen = concatbits(tx, txlen, &cmd, 0, 4);
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
uint8_t addr = page_addr;
txlen = concatbits(tx, txlen, &addr, 0, 8);
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
@ -1356,9 +805,7 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
}
read_end:
hts_stop_clock();
set_tracing(false);
lf_finalize(ledcontrol);
hitag_cleanup(ledcontrol);
reply_reason(CMD_LF_HITAGS_READ, status, reason, (uint8_t *)&card, sizeof(card));
}
@ -1396,13 +843,13 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
txlen = 0;
uint8_t cmd = HITAGS_WRITE_PAGE;
txlen = concatbits(tx, txlen, &cmd, 0, 4);
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
uint8_t addr = payload->page;
txlen = concatbits(tx, txlen, &addr, 0, 8);
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
@ -1430,9 +877,9 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
// }
txlen = 0;
txlen = concatbits(tx, txlen, payload->data, 0, 32);
txlen = concatbits(tx, txlen, payload->data, 0, 32, false);
crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
enable_page_tearoff = g_tearoff_enabled;
@ -1449,41 +896,14 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
}
write_end:
hts_stop_clock();
set_tracing(false);
lf_finalize(ledcontrol);
hitag_cleanup(ledcontrol);
reply_reason(CMD_LF_HITAGS_WRITE, status, reason, NULL, 0);
}
int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
// Setup FPGA and initialize
hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol);
StopTicks();
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
// Clean up trace and prepare it for storing frames
set_tracing(true);
clear_trace();
if (ledcontrol) LED_D_ON();
hts_init_clock();
// Set fpga in edge detect with reader field, we can modulate as reader now
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
// Configure output and enable pin that is connected to the FPGA (for modulating)
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
// Disable modulation at default, which means enable the field
LOW(GPIO_SSC_DOUT);
// UID request standard 00110
// UID request Advanced 1100x
// UID request FAdvanced 11010
protocol_mode = HITAGS_UID_REQ_ADV1;
uint8_t cmd = protocol_mode;
@ -1493,7 +913,7 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
size_t txlen = 0;
uint8_t tx[HITAG_FRAME_LEN] = { 0x00 };
txlen = concatbits(tx, txlen, &cmd, 0, 5);
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_FIRST, ledcontrol, true);
@ -1511,10 +931,10 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
status = PM3_ERFTRANS;
}
hts_stop_clock();
set_tracing(false);
lf_finalize(ledcontrol);
reply_ng(CMD_LF_HITAGS_UID, status, (uint8_t *)tag.data.pages, sizeof(tag.data.pages));
hitag_cleanup(ledcontrol);
if (send_answer) {
reply_ng(CMD_LF_HITAGS_UID, status, (uint8_t *)tag.data.pages, sizeof(tag.data.pages));
}
return status;
}
@ -1576,9 +996,7 @@ void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol
SpinDelay(2);
}
hts_stop_clock();
set_tracing(false);
lf_finalize(ledcontrol);
hitag_cleanup(ledcontrol);
reply_ng(CMD_LF_HITAGS_TEST_TRACES, PM3_SUCCESS, NULL, 0);
return;
}

View file

@ -24,7 +24,7 @@
#include "common.h"
#include "hitag.h"
void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol);
void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol);
void hts_read(const lf_hitag_data_t *payload, bool ledcontrol);
void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol);
void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol);

539
armsrc/hitag_common.c Normal file
View file

@ -0,0 +1,539 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Hitag shared functionality
//-----------------------------------------------------------------------------
#include "hitag_common.h"
#include "proxmark3_arm.h"
#include "cmd.h"
#include "BigBuf.h"
#include "fpgaloader.h"
#include "ticks.h"
#include "dbprint.h"
#include "util.h"
#include "string.h"
#include "commonutil.h"
#include "hitag2/hitag2_crypto.h"
#include "lfadc.h"
#include "crc.h"
#include "protocols.h"
#include "appmain.h" // tearoff_hook()
uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, combined with TC2 counter for ~47min timing
static void hitag_stop_clock(void) {
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
}
static void hitag_init_clock(void) {
// Enable Peripheral Clock for
// Timer Counter 0, used to measure exact timing before answering
// Timer Counter 1, used to capture edges of the tag frames
// Timer Counter 2, used to log trace time
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2);
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
// Disable timer during configuration
hitag_stop_clock();
// TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
// TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger,
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3)
| AT91C_TC_ABETRG // TIOA is used as an external trigger
| AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge
| AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA
| AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA
// TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
// Enable and reset counters
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
AT91C_BASE_TCB->TCB_BCR = 1;
// synchronized startup procedure
// In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right?
while (AT91C_BASE_TC0->TC_CV != 0) {
}; // wait until TC0 returned to zero
// reset timestamp
timestamp_high = 0;
}
// Initialize FPGA and timer for Hitag operations
void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol) {
StopTicks();
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
// Clean up trace and prepare it for storing frames
set_tracing(true);
clear_trace();
if (ledcontrol) LED_D_ON();
hitag_init_clock();
// Set fpga in edge detect with/without reader field, we can modulate as reader/tag now
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | conf);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
if (threshold != 127) FpgaSendCommand(FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, threshold);
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
// Configure output and enable pin that is connected to the FPGA (for modulating)
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
// Disable modulation at default, which means enable the field
LOW(GPIO_SSC_DOUT);
}
// Clean up and finalize Hitag operations
void hitag_cleanup(bool ledcontrol) {
hitag_stop_clock();
set_tracing(false);
lf_finalize(ledcontrol);
}
// Reader functions
static void hitag_reader_send_bit(int bit, bool ledcontrol) {
// Reset clock for the next bit
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV != 0) {};
if (ledcontrol) LED_A_ON();
// Binary puls length modulation (BPLM) is used to encode the data stream
// This means that a transmission of a one takes longer than that of a zero
HIGH(GPIO_SSC_DOUT);
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
LOW(GPIO_SSC_DOUT);
if (bit == 0) {
// Zero bit: |_-|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {};
} else {
// One bit: |_--|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {};
}
if (ledcontrol) LED_A_OFF();
}
void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof) {
// Send SOF (Start of Frame) for Hitag µ if requested
if (send_sof) {
hitag_reader_send_bit(0, ledcontrol);
// Reset clock for the code violation
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV != 0) {};
if (ledcontrol) LED_A_ON();
// SOF is HIGH for HITAG_T_LOW
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
// Then LOW for HITAG_T_CODE_VIOLATION
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_CODE_VIOLATION) {};
if (ledcontrol) LED_A_OFF();
}
// Send the content of the frame
for (size_t i = 0; i < frame_len; i++) {
hitag_reader_send_bit(TEST_BIT_MSB(frame, i), ledcontrol);
}
// Send EOF
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV != 0) {};
HIGH(GPIO_SSC_DOUT);
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
LOW(GPIO_SSC_DOUT);
}
void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol,
MOD modulation, int sof_bits) {
// Reset values for receiving frames
memset(rx, 0x00, sizeofrx);
*rxlen = 0;
int lastbit = 1;
bool bSkip = true;
uint32_t errorCount = 0;
bool bStarted = false;
uint16_t next_edge_event = AT91C_TC_LDRBS;
int double_speed = (modulation == AC4K || modulation == MC8K) ? 2 : 1;
uint32_t rb_i = 0;
uint8_t edges[160] = {0};
// Skip SOF bits
bool sof_received = false;
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
// Check if edge in tag modulation is detected
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
// only use AT91C_TC_LDRBS falling edge for now
if (next_edge_event == AT91C_TC_LDRBS) continue;
// Retrieve the new timing values
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0;
edges[rb_i++] = rb;
// Reset timer every frame, we have to capture the last edge for timing
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
if (ledcontrol) LED_B_INV();
// Capture tag frame (manchester decoding using only falling edges)
if (bStarted == false) {
if (rb >= HITAG_T_WAIT_RESP) {
bStarted = true;
// Capture tag response timestamp
*resptime = TIMESTAMP;
// We always receive a 'one' first, which has the falling edge after a half period |-_|
rx[0] = 0x80;
*rxlen = 1;
} else {
errorCount++;
}
} else {
// Handle different modulation types
if (modulation == AC2K || modulation == AC4K) {
// Anticollision Coding
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
// Anticollision Coding example |--__|--__| (00)
lastbit = 0;
// CLEAR_BIT_MSB(rx, *rxlen);
(*rxlen)++;
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
// Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01)
lastbit = !lastbit;
if (lastbit) SET_BIT_MSB(rx, *rxlen);
(*rxlen)++;
bSkip = !!lastbit;
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
// Anticollision Coding example |-_-_| (1)
if (bSkip == false) {
lastbit = 1;
SET_BIT_MSB(rx, *rxlen);
(*rxlen)++;
}
bSkip = !bSkip;
} else {
// Ignore weird value, is to small to mean anything
errorCount++;
}
} else {
// Manchester coding (MC4K, MC8K)
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
// Manchester coding example |-_|_-|-_| (101)
// CLEAR_BIT_MSB(rx, *rxlen);
(*rxlen)++;
SET_BIT_MSB(rx, *rxlen);
(*rxlen)++;
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
// Manchester coding example |_-|...|_-|-_| (0...01)
// CLEAR_BIT_MSB(rx, *rxlen);
(*rxlen)++;
// We have to skip this half period at start and add the 'one' the second time
if (bSkip == false) {
SET_BIT_MSB(rx, *rxlen);
(*rxlen)++;
}
lastbit = !lastbit;
bSkip = !bSkip;
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
// bit is same as last bit
if (lastbit) SET_BIT_MSB(rx, *rxlen);
(*rxlen)++;
} else {
// Ignore weird value, is to small to mean anything
errorCount++;
}
}
// Handle SOF bits
if (sof_received == false && *rxlen >= sof_bits) {
// Check if SOF is valid (all bits should be 1)
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
if (sof_bits == 4) {
sof_bits = 3;
// Hitag µ is LSB first 0b110
if ((rx[0] & 0xE0) != 0xC0) {
DBG Dbprintf("Warning, SOF is invalid rx[0]: 0x%02X", rx[0]);
}
} else {
DBG DbpString("Warning, not all bits of SOF are 1");
}
}
*rxlen -= sof_bits;
uint8_t tmp = rx[0];
rx[0] = 0x00;
for (size_t i = 0; i < *rxlen; i++) {
if (TEST_BIT_MSB(&tmp, sof_bits + i)) SET_BIT_MSB(rx, i);
}
// DBG Dbprintf("after sof_bits rxlen: %d rx[0]: 0x%02X", *rxlen, rx[0]);
sof_received = true;
}
}
}
// if we saw over 100 weird values break it probably isn't hitag...
if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) {
break;
}
// We can break this loop if we received the last bit from a frame
// max periods between 2 falling edge
// RTF AC64 |--__|--__| (00) 64 * T0
// RTF MC32 |_-|-_|_-| (010) 48 * T0
if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) {
if (bStarted) {
break;
}
}
}
DBG {
Dbprintf("RX %i:%02X.. resptime:%i edges:", *rxlen, rx[0], *resptime);
Dbhexdump(rb_i, edges, false);
}
}
// Tag functions - depends on modulation type
static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) {
// Reset clock for the next bit
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
if (ledcontrol) LED_A_ON();
switch (modulation) {
case AC2K: {
if (bit == 0) {
// AC Coding --__
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
} else {
// AC coding -_-_
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 48) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
}
break;
}
case AC4K: {
if (bit == 0) {
// AC Coding --__
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {};
} else {
// AC coding -_-_
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 24) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
}
break;
}
case MC4K: {
if (bit == 0) {
// Manchester: Unloaded, then loaded |__--|
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
} else {
// Manchester: Loaded, then unloaded |--__|
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
}
break;
}
case MC8K: {
if (bit == 0) {
// Manchester: Unloaded, then loaded |__--|
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
} else {
// Manchester: Loaded, then unloaded |--__|
HIGH(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
LOW(GPIO_SSC_DOUT);
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
}
break;
}
}
if (ledcontrol) LED_A_OFF();
}
void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow) {
uint16_t next_edge_event = AT91C_TC_LDRBS;
uint8_t edges[160] = {0};
uint32_t rb_i = 0;
// Receive frame, watch for at most T0*EOF periods
while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) {
// Check if edge in modulation is detected
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
// only use AT91C_TC_LDRBS falling edge for now
if (next_edge_event == AT91C_TC_LDRBS) continue;
// Retrieve the new timing values
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0 + *overflow;
*overflow = 0;
edges[rb_i++] = rb;
if (ledcontrol) LED_B_INV();
// Capture reader cmd start timestamp
if (*start_time == 0) {
*start_time = TIMESTAMP - HITAG_T_LOW;
}
// Capture reader frame
if (rb >= HITAG_T_STOP) {
// Hitag µ SOF
if (*rxlen != 0 && *rxlen != 1) {
// DBG DbpString("weird0?");
break;
}
*rxlen = 0;
} else if (rb >= HITAG_T_1_MIN) {
// '1' bit
SET_BIT_MSB(rx, *rxlen);
(*rxlen)++;
} else if (rb >= HITAG_T_0_MIN) {
// '0' bit
// CLEAR_BIT_MSB(rx, *rxlen);
(*rxlen)++;
} else {
// Ignore weird value, is too small to mean anything
}
}
}
if (ledcontrol) LED_B_OFF();
DBG if (rb_i) {
Dbprintf("RX %i bits.. start_time:%i edges:", *rxlen, *start_time);
Dbhexdump(rb_i, edges, false);
}
}
void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol) {
// The beginning of the frame is hidden in some high level; pause until our bits will have an effect
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
HIGH(GPIO_SSC_DOUT);
switch (modulation) {
case AC4K:
case MC8K: {
while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; // FADV
break;
}
case AC2K:
case MC4K: {
while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; // STD + ADV
break;
}
}
// SOF - send start of frame
for (size_t i = 0; i < sof_bits; i++) {
if (sof_bits == 4 && i == 3) {
// Hitag µ SOF is 110
hitag_tag_send_bit(0, modulation, ledcontrol);
break;
} else
hitag_tag_send_bit(1, modulation, ledcontrol);
}
// Send the content of the frame
for (size_t i = 0; i < frame_len; i++) {
hitag_tag_send_bit(TEST_BIT_MSB(frame, i), modulation, ledcontrol);
}
LOW(GPIO_SSC_DOUT);
}

57
armsrc/hitag_common.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef HITAG_COMMON_H
#define HITAG_COMMON_H
#include "hitag.h"
// Sam7s has several timers, we will use the source TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK)
// TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, Timer is running at 48MHz/32 = 1500 KHz
// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
// T0 = TIMER_CLOCK3 / 125000 = 12
#define T0 12
#define HITAG_FRAME_LEN 20
// TC0 and TC1 are 16-bit counters and will overflow after 5461 * T0
// Ensure not to set these timings above 5461 (~43ms) when comparing without considering overflow, as they will never reach that value.
#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */
#define HITAG_T_0 20 /* T[0] should be 18..22 */
#define HITAG_T_1 28 /* T[1] should be 26..32 */
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
#define HITAG_T_1_MIN 25 /* T[1] should be 26..32 */
#define HITAG_T_STOP 36 /* T_EOF should be > 36 */
#define HITAG_T_CODE_VIOLATION 36 /* Hitag µ TFcv should be 34..38 */
#define HITAG_T_EOF 80 /* T_EOF should be > 36 */
#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */
#define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */
#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */
#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */
#define HITAG_T_TAG_ONE_HALF_PERIOD 10
#define HITAG_T_TAG_TWO_HALF_PERIOD 25
#define HITAG_T_TAG_THREE_HALF_PERIOD 41
#define HITAG_T_TAG_FOUR_HALF_PERIOD 57
#define HITAG_T_TAG_HALF_PERIOD 16
#define HITAG_T_TAG_FULL_PERIOD 32
#define HITAG_T_TAG_CAPTURE_ONE_HALF 13
#define HITAG_T_TAG_CAPTURE_TWO_HALF 25
#define HITAG_T_TAG_CAPTURE_THREE_HALF 41
#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57
extern uint16_t timestamp_high;
#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
// Common hitag functions
void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol);
void hitag_cleanup(bool ledcontrol);
void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof);
void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, MOD modulation,
int sof_bits);
void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow);
void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol);
#endif

897
armsrc/hitagu.c Normal file
View file

@ -0,0 +1,897 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Low frequency HITAG µ (micro) functions
#include "hitagu.h"
#include "hitag_common.h"
#include "BigBuf.h"
#include "appmain.h" // tearoff_hook()
#include "cmd.h"
#include "commonutil.h"
#include "crc16.h"
#include "dbprint.h"
#include "fpgaloader.h"
#include "hitag2/hitag2_crypto.h"
#include "lfadc.h"
#include "protocols.h"
#include "proxmark3_arm.h"
#include "string.h"
#include "ticks.h"
#include "util.h"
// Hitag µ specific definitions
#define HTU_SOF_BITS 4 // Start of frame bits is always 3 for Hitag µ (110) plus 1 bit error flag
MOD M = MC4K; // Modulation type
// Structure to hold the state of the Hitag µ tag
static struct hitagU_tag tag = {
.pstate = HT_READY, // Initial state is ready
.max_page = HITAGU_MAX_PAGE_STANDARD, // Default to standard version
.icr = 0, // Default ICR value
};
// Macros for managing authentication state
#define IS_AUTHENTICATED() (tag.pstate == HT_AUTHENTICATE)
#define SET_AUTHENTICATED() (tag.pstate = HT_AUTHENTICATE)
#define RESET_AUTHENTICATION() (tag.pstate = HT_READY)
/*
* Update the maximum page number based on the tag's ICR (IC Revision)
*/
static void update_tag_max_page_by_icr(void) {
// Set max_page based on ICR value
switch (tag.icr) {
case HITAGU_ICR_STANDARD:
tag.max_page = HITAGU_MAX_PAGE_STANDARD;
DBG Dbprintf("Detected standard Hitag µ (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page);
break;
case HITAGU_ICR_ADVANCED:
tag.max_page = HITAGU_MAX_PAGE_ADVANCED;
DBG Dbprintf("Detected Hitag µ advanced (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page);
break;
case HITAGU_ICR_ADVANCED_PLUS:
tag.max_page = HITAGU_MAX_PAGE_ADVANCED_PLUS;
DBG Dbprintf("Detected Hitag µ advanced+ (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page);
break;
case HITAGU_ICR_8265:
tag.max_page = HITAGU_MAX_PAGE_8265;
DBG Dbprintf("Detected Hitag µ 8265 (ICR=0x%02X), max page: 0x%02X", tag.icr, tag.max_page);
break;
default:
// Unknown ICR, use standard size as fallback
tag.max_page = HITAGU_MAX_PAGE_STANDARD;
DBG Dbprintf("Unknown Hitag µ ICR: 0x%02X, defaulting to max page: 0x%02X", tag.icr, tag.max_page);
break;
}
}
/*
* Update the maximum page number based on the tag's memory configuration
* This function checks both ICR and additional pattern-based detection
*/
static void update_tag_max_page(void) {
// First try to determine max_page from ICR
update_tag_max_page_by_icr();
// Additional tag type detection can be added here
}
/*
* Handles all commands from a reader for Hitag µ
* Processes flags and commands, generates appropriate responses
*/
static void htu_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) {
// Initialize response
*txlen = 0;
if (rxlen < 5) {
return; // Command too short
}
// Extract flags (5 bits) and command (6 bits if present)
uint8_t flags = (rx[0] >> 3) & 0x1F;
uint8_t command = 0;
if (rxlen >= 11) {
// Extract 6-bit command if present
command = ((rx[0] & 0x07) << 3) | ((rx[1] >> 5) & 0x07);
}
// Check flags
bool inv_flag = (flags & HITAGU_FLAG_INV);
bool crct_flag = (flags & HITAGU_FLAG_CRCT);
// Handle based on flags and command
if (inv_flag) {
// Inventory mode - respond with UID (48 bits)
*txlen = concatbits(tx, *txlen, tag.uid, 0, HITAGU_UID_SIZE * 8, true);
} else if (command == HITAGU_CMD_LOGIN) {
// Login command
if (rxlen >= 43) { // 5+6+32 bits = 43 bits minimum
// Extract password - 32 bits after command
uint32_t password = 0;
for (int i = 0; i < 4; i++) {
int startBit = 11 + i * 8; // 5+6 bits of command + i*8
uint8_t b = 0;
for (int j = 0; j < 8; j++) {
int bitPos = startBit + j;
int pos = bitPos / 8;
int shift = 7 - (bitPos % 8);
b |= ((rx[pos] >> shift) & 0x01) << (7 - j);
}
password |= (b << (24 - i * 8));
}
// Check password
if (password == ((tag.password[0] << 24) | (tag.password[1] << 16) | (tag.password[2] << 8) | tag.password[3])) {
// Set authentication state
SET_AUTHENTICATED();
// Send success response
uint8_t resp_byte = 0x01; // Success code
*txlen = concatbits(tx, *txlen, &resp_byte, 0, 8, true);
} else {
// Authentication failed
RESET_AUTHENTICATION();
// Send failure response
uint8_t resp_byte = 0x00; // Failure code
*txlen = concatbits(tx, *txlen, &resp_byte, 0, 8, true);
}
}
} else if (command == HITAGU_CMD_SELECT) {
// Select command
if (rxlen >= 59) { // 5+6+48 bits = 59 bits minimum (48-bit UID)
// Extract UID to select - next 48 bits
uint8_t sel_uid[6] = {0};
for (int i = 0; i < 6; i++) {
int startBit = 11 + i * 8; // 5+6 bits of command + i*8
uint8_t b = 0;
for (int j = 0; j < 8; j++) {
int bitPos = startBit + j;
int pos = bitPos / 8;
int shift = 7 - (bitPos % 8);
b |= ((rx[pos] >> shift) & 0x01) << (7 - j);
}
sel_uid[i] = b;
}
// Check if UID matches
if (memcmp(sel_uid, tag.uid, 6) == 0) {
// Selected - send response with select data
uint8_t resp_data[4] = {0xCA, 0x24, 0x00, 0x00}; // Standard select response
*txlen = concatbits(tx, *txlen, resp_data, 0, 32, true);
} else {
// UID mismatch - no response
*txlen = 0;
}
}
} else if (command == HITAGU_CMD_READ_MULTIPLE_BLOCK) {
// Read command
if (rxlen >= 19) { // 5+6+8 bits = 19 bits minimum
// Extract page address - 8 bits after command
uint8_t page = 0;
for (int i = 0; i < 8; i++) {
int bitPos = 11 + i; // 5+6 bits of command + i
int pos = bitPos / 8;
int shift = 7 - (bitPos % 8);
page |= ((rx[pos] >> shift) & 0x01) << (7 - i);
}
// Extract number of blocks to read if ADR flag is set
uint8_t read_len = 1; // Default to 1 page
if ((flags & HITAGU_FLAG_ADR) && rxlen >= 27) {
for (int i = 0; i < 8; i++) {
int bitPos = 19 + i;
int pos = bitPos / 8;
int shift = 7 - (bitPos % 8);
if (pos < (rxlen + 7) / 8) {
read_len |= ((rx[pos] >> shift) & 0x01) << (7 - i);
}
}
}
// Security check: does this page require authentication?
bool needs_auth = false;
// Check if page is password-protected (e.g., config or password page)
if (page == HITAGU_PASSWORD_PADR) {
needs_auth = true;
}
// Check authentication for protected pages
if (needs_auth && !IS_AUTHENTICATED()) {
// Not authenticated, cannot read protected pages
DBG Dbprintf("Page %d requires authentication", page);
// Mark as unauthorized access
*txlen = 0; // No response
} else {
// Map page address (some pages may be aliased)
uint8_t real_page = page;
if (page >= 64 && tag.max_page <= 64) {
real_page = page & 0x3F; // Pages above 64 map to 0-63
}
// Read requested number of pages
for (int i = 0; i < read_len && i < 16; i++) { // Limit to 16 pages max
uint8_t curr_page = (real_page + i) % tag.max_page;
// Special pages
if (curr_page == HITAGU_CONFIG_PADR) {
// Config page
*txlen = concatbits(tx, *txlen, (uint8_t *)&tag.config, 0, 32, true);
} else if (curr_page == HITAGU_PASSWORD_PADR) {
// Password page - only return if authenticated
if (IS_AUTHENTICATED()) {
*txlen = concatbits(tx, *txlen, tag.password, 0, 32, true);
} else {
// Return zeros if not authenticated
uint8_t zeros[4] = {0};
*txlen = concatbits(tx, *txlen, zeros, 0, 32, true);
}
} else {
// Regular page
*txlen = concatbits(tx, *txlen, tag.data.pages[curr_page], 0, 32, true);
}
}
}
}
} else if (command == HITAGU_CMD_WRITE_SINGLE_BLOCK) {
// Write command
if (rxlen >= 51) { // 5+6+8+32 bits = 51 bits minimum
// Check if authenticated
if (!IS_AUTHENTICATED()) {
DBG Dbprintf("WRITE failed: not authenticated");
*txlen = 0; // No response
} else {
// Extract page address - 8 bits after command
uint8_t page = 0;
for (int i = 0; i < 8; i++) {
int bitPos = 11 + i; // 5+6 bits of command + i
int pos = bitPos / 8;
int shift = 7 - (bitPos % 8);
page |= ((rx[pos] >> shift) & 0x01) << (7 - i);
}
// Extract data - 32 bits after page address
uint8_t data[4] = {0};
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++) {
int bitPos = 19 + i * 8 + j;
int pos = bitPos / 8;
int shift = 7 - (bitPos % 8);
if (pos < (rxlen + 7) / 8) {
data[i] |= ((rx[pos] >> shift) & 0x01) << (7 - j);
}
}
}
// Map page address
uint8_t real_page = page;
if (page >= 64 && tag.max_page <= 64) {
real_page = page & 0x3F; // Pages above 64 map to 0-63
}
// Special pages
if (real_page == HITAGU_CONFIG_PADR) {
// Write config
memcpy(&tag.config, data, 4);
DBG Dbprintf("WRITE CONFIG: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]);
} else if (real_page == HITAGU_PASSWORD_PADR) {
// Write password
memcpy(tag.password, data, 4);
DBG Dbprintf("WRITE PASSWORD: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]);
} else if (real_page < tag.max_page) {
// Regular page
memcpy(tag.data.pages[real_page], data, 4);
DBG Dbprintf("WRITE PAGE %02X: %02X %02X %02X %02X", real_page, data[0], data[1], data[2], data[3]);
}
// Send success acknowledgment
uint8_t ack = 0x01; // Success acknowledgment
*txlen = concatbits(tx, *txlen, &ack, 0, 8, true);
}
}
} else if (command == HITAGU_CMD_SYSINFO) {
// System info command
// Prepare system info response with ICR field
uint8_t info[8] = {0};
// First byte: Error flag (0) + 7 reserved bits
info[0] = 0x00;
// Additional bytes: System Memory Block Data
// MSN (Manufacturer Serial Number) - example values
info[1] = 0x12;
info[2] = 0x34;
// MFC (Manufacturer Code) - example value
info[3] = 0x04; // NXP
// ICR (IC Revision)
info[4] = tag.icr;
// Reserved bytes
info[5] = 0x00;
info[6] = 0x00;
info[7] = 0x00;
// Add the system info data to the response
*txlen = concatbits(tx, *txlen, info, 0, 64, true);
} else if (flags == HITAGU_CMD_STAY_QUIET) {
// Quiet command - no response needed
RESET_AUTHENTICATION();
*txlen = 0;
} else {
// Unknown command
DBG Dbprintf("Unknown command or flags: flags=%02X, cmd=%02X", flags, command);
*txlen = 0; // No response
}
// Add CRC if requested and there is response data
if (crct_flag && *txlen > 0) {
// Calculate CRC-16/XMODEM directly from tx
uint16_t crc = Crc16(tx, *txlen, 0, CRC16_POLY_CCITT, false, true);
// Append CRC-16 (16 bits)
*txlen = concatbits(tx, *txlen, (uint8_t *)&crc, 0, 16, true);
}
}
/*
* Simulates a Hitag µ Tag with the given data
*/
void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol) {
uint8_t rx[HITAG_FRAME_LEN] = {0};
size_t rxlen = 0;
uint8_t tx[HITAG_FRAME_LEN];
size_t txlen = 0;
// Free any allocated BigBuf memory
BigBuf_free();
BigBuf_Clear_ext(false);
DbpString("Starting Hitag µ simulation");
// Reset tag state
memset(&tag, 0, sizeof(tag));
tag.max_page = 64; // Default maximum page
RESET_AUTHENTICATION();
// Read tag data into memory if supplied
if (tag_mem_supplied) {
DbpString("Loading Hitag µ memory...");
// First 6 bytes are the UID (48 bits)
memcpy(tag.uid, data, 6);
// Rest is page data
memcpy(tag.data.pages, data + 6, sizeof(tag.data.pages));
}
// Update max_page based on configuration
update_tag_max_page();
// Debug output of tag data
DBG Dbprintf("UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]);
for (int i = 0; i <= tag.max_page; i++) {
DBG Dbprintf("Page[%2d]: %02X %02X %02X %02X", i, tag.data.pages[i][0], tag.data.pages[i][1],
tag.data.pages[i][2], tag.data.pages[i][3]);
}
hitag_setup_fpga(0, threshold, ledcontrol);
int overflow = 0;
// Simulation main loop
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
uint32_t start_time = 0;
WDT_HIT();
// Receive commands from the reader
hitag_tag_receive_frame(rx, sizeof(rx), &rxlen, &start_time, ledcontrol, &overflow);
// Check if frame was captured and store it
if (rxlen > 0) {
LogTraceBits(rx, rxlen, start_time, TIMESTAMP, true);
// Disable timer 1 with external trigger to avoid triggers during our own modulation
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
// Prepare tag response (tx)
memset(tx, 0x00, sizeof(tx));
txlen = 0;
// Process received reader command
htu_handle_reader_command(rx, rxlen, tx, &txlen);
// Wait for HITAG_T_WAIT_RESP carrier periods after the last reader bit,
// not that since the clock counts since the rising edge, but T_Wait1 is
// with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low)
// periods. The gap time T_Low varies (4..10). All timer values are in
// terms of T0 units
while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) {
};
// Send and store the tag answer (if there is any)
if (txlen > 0) {
// Transmit the tag frame
start_time = TIMESTAMP;
hitag_tag_send_frame(tx, txlen, HTU_SOF_BITS, MC4K, ledcontrol);
LogTraceBits(tx, txlen, start_time, TIMESTAMP, false);
}
// Enable and reset external trigger in timer for capturing future frames
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Reset the received frame and response timing info
memset(rx, 0x00, sizeof(rx));
}
// Reset the frame length
rxlen = 0;
// Save the timer overflow, will be 0 when frame was received
overflow += (AT91C_BASE_TC1->TC_CV / T0);
// Reset the timer to restart while-loop that receives frames
AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG;
}
hitag_cleanup(ledcontrol);
// Release allocated memory from BigBuf
BigBuf_free();
DbpString("Simulation stopped");
}
/*
* Send command to reader and receive answer from tag
*/
static int htu_reader_send_receive(uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen,
uint32_t t_wait, bool ledcontrol, uint8_t modulation, uint8_t sof_bits) {
// Reset the received frame
memset(rx, 0x00, sizeofrx);
// Disable timer 1 with external trigger to avoid triggers during our own modulation
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
DBG Dbprintf("tx %d bits:", txlen);
DBG Dbhexdump((txlen + 7) / 8, tx, false);
// Wait until we can send the command
while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) {
};
// Set up tracing
uint32_t start_time = TIMESTAMP;
// Send the command - Hitag µ always requires SOF
hitag_reader_send_frame(tx, txlen, ledcontrol, true);
// if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) {
// return PM3_ETEAROFF;
// }
LogTraceBits(tx, txlen, start_time, TIMESTAMP, true);
// Enable and reset external trigger in timer for capturing future frames
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Capture response - SOF is automatically stripped by hitag_reader_receive_frame
hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, modulation, sof_bits);
LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false);
DBG Dbprintf("rx %d bits:", *rxlen);
DBG Dbhexdump((*rxlen + 7) / 8, rx, false);
// TODO: check Error flag
return PM3_SUCCESS;
}
/*
* Selects a tag using the READ UID, GET SYSTEM INFORMATION, and LOGIN commands
*/
static int htu_select_tag(const lf_hitag_data_t *payload, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx,
int t_wait, bool ledcontrol) {
// Initialize response
size_t txlen = 0;
size_t rxlen = 0;
// Setup FPGA and initialize
hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol);
// Prepare common flags and command variables
uint8_t flags = HITAGU_FLAG_CRCT; // Set appropriate flags for all commands
uint8_t command;
// uint8_t parameter;
// 1. Send READ UID command
command = HITAGU_CMD_READ_UID;
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
txlen = concatbits(tx, txlen, &command, 0, 6, true);
// Append CRC-16 (16 bits)
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// lf cmdread -d 64 -z 96 -o 160 -e W2400 -e S224 -e E336 -s 4096 -c W0S00100010000
// Send the READ UID command and receive the response
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, MC4K, HTU_SOF_BITS);
// Check if the response is valid
if (rxlen < 1 + 48 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
DBG Dbprintf("Read UID command failed! %i", rxlen);
return -2; // Read UID failed
}
// Process the UID from the response
concatbits(tag.uid, 0, rx, 1, 48, false);
// 2. Send GET SYSTEM INFORMATION command
command = HITAGU_CMD_SYSINFO;
txlen = 0; // Reset txlen for the new command
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
txlen = concatbits(tx, txlen, &command, 0, 6, true);
// Append CRC-16 (16 bits)
crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// Send the GET SYSTEM INFORMATION command and receive the response
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
concatbits(&tag.icr, 0, rx, 9, 8, false);
// Check if the response is valid
if (rxlen < 1 + 16 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
// 8265 bug? sometile lost Data field first bit
DBG Dbprintf("Get System Information command failed! %i", rxlen);
return -3; // Get System Information failed
}
// 3. Read config block
command = HITAGU_CMD_READ_MULTIPLE_BLOCK;
txlen = 0; // Reset txlen for the new command
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
txlen = concatbits(tx, txlen, &command, 0, 6, true);
// Add block address
txlen = concatbits(tx, txlen, (uint8_t *)&"\xFF", 0, 8, true);
// Add number of blocks, 0 means 1 block
txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true);
// Append CRC-16 (16 bits)
crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// Send read command and receive response
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
// Check if the response is valid
if (rxlen < 1 + 32 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
DBG Dbprintf("Read config block command failed! %i", rxlen);
return -3; // Read config block failed
}
// Process the config block from the response
concatbits(tag.config.asBytes, 0, rx, 1, 32, false);
reverse_arraybytes(tag.config.asBytes, HITAGU_BLOCK_SIZE);
// 4. Send LOGIN command if necessary
if (payload && (payload->cmd == HTUF_82xx || payload->cmd == HTUF_PASSWORD)) {
command = HITAGU_CMD_LOGIN; // Set command for login
txlen = 0; // Reset txlen for the new command
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
txlen = concatbits(tx, txlen, &command, 0, 6, true);
txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false);
// Append CRC-16 (16 bits)
crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// Send the LOGIN command and receive the response
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
// Check if login succeeded
if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
DBG Dbprintf("Login command failed! %i", rxlen);
return -4; // Login failed
} else {
DBG DbpString("Login successful");
}
// flags |= HITAGU_FLAG_ADR;
// command = HITAGU_CMD_LOGIN; // Set command for login
// txlen = 0; // Reset txlen for the new command
// txlen = concatbits(tx, txlen, &flags, 0, 5, true);
// txlen = concatbits(tx, txlen, &command, 0, 6, true);
// txlen = concatbits(tx, txlen, payload->uid, 0, HITAGU_UID_SIZE * 8, false);
// txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false);
// // Append CRC-16 (16 bits)
// crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
// txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// // Send the LOGIN command and receive the response
// htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
// // Check if login succeeded
// if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
// DBG Dbprintf("Login command failed! %i", rxlen);
// return -3; // Login failed
// } else {
// DbpString("Login successful");
// }
}
// If all commands are successful, update the tag's state
update_tag_max_page(); // Update max_page based on the new configuration
return 0; // Selection successful
}
/*
* Reads the UID of a Hitag µ tag using the INVENTORY command
*/
int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer) {
// Initialize response
uint8_t rx[HITAG_FRAME_LEN] = {0x00};
uint8_t tx[HITAG_FRAME_LEN] = {0x00};
int status = PM3_SUCCESS;
// Use htu_select_tag to select the card and retrieve UID
int reason = htu_select_tag(NULL, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
if (reason != 0) {
DBG DbpString("Error: htu_read_uid Failed to select tag");
status = PM3_ERFTRANS;
goto exit;
}
DBG Dbprintf("HitagU UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]);
if (uid) {
*uid = MemBeToUint6byte(tag.uid);
}
exit:
hitag_cleanup(ledcontrol);
if (send_answer) {
// Send UID
reply_reason(CMD_LF_HITAGU_UID, status, reason, tag.uid, sizeof(tag.uid));
}
// Reset authentication state
RESET_AUTHENTICATION();
return status;
}
/*
* Reads a Hitag µ tag
*/
void htu_read(const lf_hitag_data_t *payload, bool ledcontrol) {
size_t rxlen = 0;
uint8_t rx[HITAG_FRAME_LEN] = {0x00};
size_t txlen = 0;
uint8_t tx[HITAG_FRAME_LEN] = {0x00};
int status = PM3_SUCCESS;
// DBG {
// DbpString("htu_read");
// Dbprintf("payload->page: %d", payload->page);
// Dbprintf("payload->page_count: %d", payload->page_count);
// Dbprintf("payload->cmd: %d", payload->cmd);
// Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2],
// payload->uid[3], payload->uid[4], payload->uid[5]);
// Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2],
// payload->key[3], payload->key[4], payload->key[5]);
// Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]);
// }
// Use htu_select_tag to select the card and retrieve UID
int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
if (reason != 0) {
DbpString("Error: htu_read Failed to select tag");
status = PM3_ERFTRANS;
goto exit;
}
lf_htu_read_response_t card = {
.icr = tag.icr,
};
memcpy(card.uid, tag.uid, HITAGU_UID_SIZE);
memcpy(card.config_page.asBytes, tag.config.asBytes, HITAGU_BLOCK_SIZE);
uint8_t page_count_index = payload->page_count - 1;
if (payload->page_count == 0) {
page_count_index = tag.max_page - payload->page - 1;
}
// Step 2: Process the actual command
// Add flags (5 bits)
uint8_t flags = HITAGU_FLAG_CRCT;
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
// Read command format: <flags> <command> [UID] <block address> <number of blocks> [CRC-16]
// Add command (6 bits)
uint8_t command = HITAGU_CMD_READ_MULTIPLE_BLOCK;
txlen = concatbits(tx, txlen, &command, 0, 6, true);
// The 8265 chip has known issues when reading multiple blocks:
// - page_count = 1: Works correctly
// - page_count >= 2: Data field is left shifted by 1 bit
// - page_count = 2 or page+page_count exceeds valid page: Data field has an extra '1' bit
// - page_count = 3,4: Data field last bit is always '1'
// - page_count = 5: CRC is not appended
// - page_count >= 6: May cause next command to have no response
// Workaround: Read one block at a time
if (payload->mode == 0 /**for debug */
&& (payload->cmd == HTUF_82xx || tag.icr == HITAGU_ICR_8265 || !memcmp(tag.uid, "\x00\x00\x00\x00\x00\x00", 6))) {
uint8_t page_addr;
for (int i = 0; i <= page_count_index; i++) {
page_addr = payload->page + i;
txlen = 5 + 6; // restore txlen for the new command
txlen = concatbits(tx, txlen, &page_addr, 0, 8, true);
// Add number of blocks, 0 means 1 block
txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true);
// Append CRC-16 (16 bits)
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// Send read command and receive response
htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
DBG Dbprintf("Error: response CRC invalid");
card.pages_reason[i] = -6;
continue;
}
// Check response
if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) {
DbpString("Error: invalid response received after read command");
card.pages_reason[i] = -7;
} else {
DBG Dbprintf("Read successful, response: %d bits", rxlen);
// todo: For certain pages, update our cached data
card.pages_reason[i] = 1;
concatbits(card.pages[i], 0, rx, 1, HITAGU_BLOCK_SIZE * 8, false);
}
}
} else {
txlen = concatbits(tx, txlen, &payload->page, 0, 8, true);
// Add number of blocks, 0 means 1 block
txlen = concatbits(tx, txlen, &page_count_index, 0, 8, true);
// Append CRC-16 (16 bits)
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
// Send read command and receive response
htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
DBG Dbprintf("Error: response CRC invalid");
status = PM3_ERFTRANS;
goto exit;
}
// Check response
if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) {
DbpString("Error: invalid response received after read command");
status = PM3_ERFTRANS;
} else {
DBG Dbprintf("Read successful, response: %d bits", rxlen);
// todo: For certain pages, update our cached data
concatbits((uint8_t *)card.pages, 0, rx, 1, rxlen - 1 - 16, false);
for (int i = 0; i < (rxlen - 1 - 16) / (HITAGU_BLOCK_SIZE * 8); i++) {
card.pages_reason[i] = 1;
}
}
}
exit:
hitag_cleanup(ledcontrol);
// Send status to client
reply_reason(CMD_LF_HITAGU_READ, status, reason, (uint8_t *)&card, sizeof(card));
}
/*
* Writes a page to a Hitag µ tag
*/
void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
size_t rxlen = 0;
uint8_t rx[HITAG_FRAME_LEN] = {0x00};
size_t txlen = 0;
uint8_t tx[HITAG_FRAME_LEN] = {0x00};
int status = PM3_SUCCESS;
// DBG {
// DbpString("htu_write_page");
// Dbprintf("payload->page: %d", payload->page);
// Dbprintf("payload->data: %02X%02X%02X%02X", payload->data[0], payload->data[1], payload->data[2], payload->data[3]);
// Dbprintf("payload->cmd: %d", payload->cmd);
// Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2], payload->uid[3], payload->uid[4], payload->uid[5]);
// Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2], payload->key[3], payload->key[4], payload->key[5]);
// Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]);
// Dbprintf("payload->mode: %d", payload->mode);
// }
int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
if (reason != 0) {
status = PM3_ERFTRANS;
goto exit;
}
// Step 2: Send write command
uint8_t flags = HITAGU_FLAG_CRCT;
// Add flags (5 bits) for write operation
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
// Add write command (6 bits)
uint8_t command = HITAGU_CMD_WRITE_SINGLE_BLOCK;
txlen = concatbits(tx, txlen, &command, 0, 6, true);
// Add page address (8 bits)
txlen = concatbits(tx, txlen, &payload->page, 0, 8, true);
// Add data to write (32 bits)
txlen = concatbits(tx, txlen, payload->data, 0, 32, false);
// Append CRC-16 (16 bits)
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
DBG Dbprintf("Writing to page 0x%02X", payload->page);
// Send write command and receive response
htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
// Check response
if (payload->cmd == HTUF_82xx && rxlen == 0) {
// 8265 bug? no response on successful write command
reason = 0;
status = PM3_ENODATA;
} else if (rxlen != 1 + 16) {
DbpString("Error: htu_write_page No valid response received after write command");
reason = -5;
status = PM3_ERFTRANS;
} else {
DBG Dbprintf("Write successful, response: %d bits", rxlen);
}
exit:
hitag_cleanup(ledcontrol);
reply_reason(CMD_LF_HITAGU_WRITE, status, reason, NULL, 0);
}

30
armsrc/hitagu.h Normal file
View file

@ -0,0 +1,30 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Hitag µ functions
//-----------------------------------------------------------------------------
#ifndef _HITAGU_H_
#define _HITAGU_H_
#include "common.h"
#include "hitag.h"
void htu_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol);
void htu_read(const lf_hitag_data_t *payload, bool ledcontrol);
void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol);
int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer);
#endif

View file

@ -1726,24 +1726,48 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
}
p_response = NULL;
} else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE
p_response = NULL;
// cmd + block + 4 bytes data + 2 bytes crc
if (CheckCrc14A(receivedCmd, len)) {
uint8_t block = receivedCmd[1];
// sanity checks
if (block > pages) {
// send NACK 0x0 == invalid argument
// send NACK 0x0, invalid argument
EmSend4bit(CARD_NACK_IV);
} else {
// first blocks of emu are header
emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4);
// send ACK
EmSend4bit(CARD_ACK);
goto jump;
}
// OTP sanity check
if (block == 0x03) {
uint8_t orig[4] = {0};
emlGet(orig, 12 + MFU_DUMP_PREFIX_LENGTH, 4);
bool risky = false;
for (int i = 0; i < 4; i++) {
risky |= orig[i] & ~receivedCmd[2 + i];
}
if (risky) {
EmSend4bit(CARD_NACK_IV);
goto jump;
}
}
// first blocks of emu are header
emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4);
// send ACK
EmSend4bit(CARD_ACK);
} else {
// send NACK 0x1 == crc/parity error
EmSend4bit(CARD_NACK_PA);
}
p_response = NULL;
goto jump;
} else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) {
// cmd + block + 2 bytes crc
if (CheckCrc14A(receivedCmd, len)) {
@ -1990,6 +2014,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
// Count number of other messages after a halt
// if (order != ORDER_WUPA && lastorder == ORDER_HALTED) { happened2++; }
jump:
cmdsRecvd++;
@ -3101,49 +3126,50 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u
size_t len = ReaderReceive(data, data_len, parity_array);
uint8_t *data_bytes = (uint8_t *) data;
if (!len) {
if (len == 0) {
BigBuf_free();
return 0; // DATA LINK ERROR
} else {
// S-Block WTX
while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
uint32_t save_iso14a_timeout = iso14a_get_timeout();
// temporarily increase timeout
iso14a_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT));
// Transmit WTX back
// byte1 - WTXM [1..59]. command FWT=FWT*WTXM
data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b
// now need to fix CRC.
AddCrc14A(data_bytes, len - 2);
// transmit S-Block
ReaderTransmit(data_bytes, len, NULL);
// retrieve the result again (with increased timeout)
len = ReaderReceive(data, data_len, parity_array);
data_bytes = data;
// restore timeout
iso14a_set_timeout(save_iso14a_timeout);
}
}
// if we received an I- or R(ACK)-Block with a block number equal to the
// current block number, toggle the current block number
if (len >= 3 // PCB+CRC = 3 bytes
&& ((data_bytes[0] & 0xC0) == 0 // I-Block
|| (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0
&& (data_bytes[0] & 0x01) == iso14_pcb_blocknum) { // equal block numbers
iso14_pcb_blocknum ^= 1;
}
// if we received I-block with chaining we need to send ACK and receive another block of data
if (res) {
*res = data_bytes[0];
}
// S-Block WTX
while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
uint32_t save_iso14a_timeout = iso14a_get_timeout();
// temporarily increase timeout
iso14a_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT));
// Transmit WTX back
// byte1 - WTXM [1..59]. command FWT=FWT*WTXM
data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b
// now need to fix CRC.
AddCrc14A(data_bytes, len - 2);
// transmit S-Block
ReaderTransmit(data_bytes, len, NULL);
// retrieve the result again (with increased timeout)
data_bytes[0] = 0x00;
len = ReaderReceive(data, data_len, parity_array);
data_bytes = data;
// restore timeout
iso14a_set_timeout(save_iso14a_timeout);
}
// crc check
if (len >= 3 && !CheckCrc14A(data_bytes, len)) {
BigBuf_free();
return -1;
}
// if we received an I- or R(ACK)-Block with a block number equal to the
// current block number, toggle the current block number
if (len >= 3 // PCB+CRC = 3 bytes
&& ((data_bytes[0] & 0xC0) == 0 // I-Block
|| (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0
&& (data_bytes[0] & 0x01) == iso14_pcb_blocknum) { // equal block numbers
iso14_pcb_blocknum ^= 1;
}
// if we received I-block with chaining we need to send ACK and receive another block of data
if (res) {
*res = data_bytes[0];
}
// crc check
if (len >= 3 && !CheckCrc14A(data_bytes, len)) {
BigBuf_free();
return -1;
}
if (len) {
@ -3226,7 +3252,7 @@ void ReaderIso14443a(PacketCommandNG *c) {
}
if ((param & ISO14A_APDU) == ISO14A_APDU) {
uint8_t res;
uint8_t res = 0;
arg0 = iso14_apdu(
cmd,
len,

View file

@ -64,11 +64,11 @@ SAM7S has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS
TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz
New timer implementation in ticks.c, which is used in LFOPS.c
1 μs = 1.5 ticks
1 fc = 8 μs = 12 ticks
1 µs = 1.5 ticks
1 fc = 8 µs = 12 ticks
Terms you find in different datasheets and how they match.
1 Cycle = 8 microseconds (μs) == 1 field clock (fc)
1 Cycle = 8 microseconds (µs) == 1 field clock (fc)
Note about HITAG timing
Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
@ -80,7 +80,7 @@ Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per sec
==========================================================================================================
ATA5577 Downlink Protocol Timings.
Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz)
Note: All absolute times assume TC = 1 / fC = 8 µs (fC = 125 kHz)
Note: These timings are from the datasheet and doesn't map the best to the features of the RVD4 LF antenna.
RDV4 LF antenna has high voltage and the drop of power when turning off the rf field takes about 1-2 TC longer.

View file

@ -294,13 +294,13 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) {
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT, NULL, 0);
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
return;
};
if (!mifare_ultra_aes_auth(keyno, keybytes)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed");
reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT, NULL, 0);
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
return;
}

View file

@ -483,7 +483,7 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
// send & receive
len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL);
if (len != 19) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
return 0;
}
@ -492,7 +492,7 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(random_b), IV, resp + 1, random_b);
if (memcmp(random_b, random_a, 16) != 0) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("failed authentication");
if (g_dbglevel >= DBG_INFO) Dbprintf("failed authentication");
return 0;
}

View file

@ -87,15 +87,15 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
samplePosLastEdge = 0;
block_done = 0;
bitPos = 0;
lastClockDuration=0;
lastClockDuration = 0;
for (sample = 1 ; sample < g_GraphTraceLen-4; sample++) {
// condition is searching for the next edge, in the expected diretion.
for (sample = 1 ; sample < g_GraphTraceLen - 4; sample++) {
// condition is searching for the next edge, in the expected diretion.
//todo: without flouz
dest[sample] = (uint8_t)(dest[sample-1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter
dest[sample] = (uint8_t)(dest[sample - 1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter
if ( ((dest[sample] + THRESHOLD) < dest[sample-1] && expectedNextEdge == FALLING ) ||
((dest[sample] - THRESHOLD) > dest[sample-1] && expectedNextEdge == RISING )) {
if (((dest[sample] + THRESHOLD) < dest[sample - 1] && expectedNextEdge == FALLING) ||
((dest[sample] - THRESHOLD) > dest[sample - 1] && expectedNextEdge == RISING)) {
//okay, next falling/rising edge found
expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge
@ -104,7 +104,7 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
lastClockDuration = samplePosCurrentEdge - samplePosLastEdge;
samplePosLastEdge = sample;
// Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge);
// Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge);
// Switch depending on lastClockDuration length:
// 16T0
@ -121,7 +121,7 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
block_done = 1;
}
// 32TO
// 32TO
} else if (ABS(lastClockDuration - _32T0) < TOLERANCE) {
// if the clock before also was 16T0, it is a PMC!
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
@ -134,26 +134,26 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
samplePosLastEdge = sample;
block_done = 1;
// if no pmc, then its a normal bit.
// Check if its the second time, the edge changed if yes, then the bit is 0
// if no pmc, then its a normal bit.
// Check if its the second time, the edge changed if yes, then the bit is 0
} else if (half_switch == 1) {
bits[bitPos] = 0;
// reset the edge counter to 0
half_switch = 0;
bitPos++;
// if it is the first time the edge changed. No bit value will be set here, bit if the
// edge changes again, it will be. see case above.
// if it is the first time the edge changed. No bit value will be set here, bit if the
// edge changes again, it will be. see case above.
} else
half_switch++;
// 64T0
// 64T0
} else if (ABS(lastClockDuration - _64T0) < TOLERANCE) {
// this means, bit here is 1
bits[bitPos] = 1;
bitPos++;
// Error
// Error
} else {
// some Error. maybe check tolerances.
// likeley to happen in the first block.
@ -193,8 +193,8 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
half_switch = 0;
}
}else {
// Dbprintf("%d, %d", sample, dest[sample]);
} else {
// Dbprintf("%d, %d", sample, dest[sample]);
}
// one block only holds 16byte (=128 bit) and then comes the PMC. so if more bit are found than 129, there must be an issue and PMC has not been identfied...
@ -403,28 +403,28 @@ void ReadPCF7931(bool ledcontrol) {
end:
/*
Dbprintf("-----------------------------------------");
Dbprintf("Memory content:");
Dbprintf("-----------------------------------------");
for (i = 0; i < maxBlocks; ++i) {
if (memory_blocks[i][ALLOC])
print_result("Block", memory_blocks[i], 16);
else
Dbprintf("<missing block %d>", i);
}
Dbprintf("-----------------------------------------");
/*
Dbprintf("-----------------------------------------");
Dbprintf("Memory content:");
Dbprintf("-----------------------------------------");
for (i = 0; i < maxBlocks; ++i) {
if (memory_blocks[i][ALLOC])
print_result("Block", memory_blocks[i], 16);
else
Dbprintf("<missing block %d>", i);
}
Dbprintf("-----------------------------------------");
if (found_blocks < maxBlocks) {
Dbprintf("-----------------------------------------");
Dbprintf("Blocks with unknown position:");
Dbprintf("-----------------------------------------");
for (i = 0; i < single_blocks_cnt; ++i)
print_result("Block", single_blocks[i], 16);
if (found_blocks < maxBlocks) {
Dbprintf("-----------------------------------------");
Dbprintf("Blocks with unknown position:");
Dbprintf("-----------------------------------------");
for (i = 0; i < single_blocks_cnt; ++i)
print_result("Block", single_blocks[i], 16);
Dbprintf("-----------------------------------------");
}
*/
Dbprintf("-----------------------------------------");
}
*/
reply_mix(CMD_ACK, 0, 0, 0, 0, 0);
}
@ -434,7 +434,7 @@ static void RealWritePCF7931(
uint16_t init_delay,
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
uint8_t address, uint8_t byte, uint8_t data,
bool ledcontrol){
bool ledcontrol) {
uint32_t tab[1024] = {0}; // data times frame
uint32_t u = 0;

View file

@ -19,7 +19,7 @@
#include "common.h"
typedef enum{
typedef enum {
FALLING,
RISING
} EdgeType;

View file

@ -111,6 +111,12 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) {
AT91C_BASE_SSC->SSC_THR = resp[i++];
FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
(void)b;
}
if (BUTTON_PRESS()) break;
}
// Ensure that the FPGA Delay Queue is empty

View file

@ -380,6 +380,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdlfhid.c
${PM3_ROOT}/client/src/cmdlfhitag.c
${PM3_ROOT}/client/src/cmdlfhitaghts.c
${PM3_ROOT}/client/src/cmdlfhitagu.c
${PM3_ROOT}/client/src/cmdlfidteck.c
${PM3_ROOT}/client/src/cmdlfindala.c
${PM3_ROOT}/client/src/cmdlfio.c

View file

@ -660,6 +660,7 @@ SRCS = mifare/aiddesfire.c \
cmdlfhid.c \
cmdlfhitag.c \
cmdlfhitaghts.c \
cmdlfhitagu.c \
cmdlfidteck.c \
cmdlfindala.c \
cmdlfio.c \

View file

@ -1,5 +1,4 @@
#-----------------------------------------------------------------------------
# Copyright (C) Jonathan Westhues, Mar 2006
# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
#
# This program is free software: you can redistribute it and/or modify
@ -381,6 +380,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdlfhid.c
${PM3_ROOT}/client/src/cmdlfhitag.c
${PM3_ROOT}/client/src/cmdlfhitaghts.c
${PM3_ROOT}/client/src/cmdlfhitagu.c
${PM3_ROOT}/client/src/cmdlfidteck.c
${PM3_ROOT}/client/src/cmdlfindala.c
${PM3_ROOT}/client/src/cmdlfio.c
@ -414,6 +414,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/fileutils.c
${PM3_ROOT}/client/src/flash.c
${PM3_ROOT}/client/src/graph.c
${PM3_ROOT}/client/src/hidsio.c
${PM3_ROOT}/client/src/iso4217.c
${PM3_ROOT}/client/src/jansson_path.c
${PM3_ROOT}/client/src/lua_bitlib.c

View file

@ -1,462 +0,0 @@
local getopt = require('getopt')
local utils = require('utils')
local ac = require('ansicolors')
local os = require('os')
local dash = string.rep('--', 32)
local dir = os.getenv('HOME') .. '/.proxmark3/logs/'
local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+"))
local log_file_path = dir .. "Paxton_log.txt"
local nam = ""
local pm3 = require('pm3')
p = pm3.pm3()
local command = core.console
command('clear')
author = ' Author: jareckib - 30.01.2025'
tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102'
version = ' version v1.19'
desc = [[
The script automates the copying of Paxton fobs read - write.
It also allows manual input of data for blocks 4-7.
The third option is reading data stored in the log file and create new fob.
Additionally, the script calculates the ID for downgrading Paxton to EM4102.
]]
usage = [[
script run paxton_clone
]]
arguments = [[
script run paxton_clone -h : this help
]]
local debug = true
local function dbg(args)
if not DEBUG then return end
if type(args) == 'table' then
local i = 1
while args[i] do
dbg(args[i])
i = i+1
end
else
print('###', args)
end
end
local function help()
print()
print(author)
print(tutorial)
print(version)
print(desc)
print(ac.cyan..' Usage'..ac.reset)
print(usage)
print(ac.cyan..' Arguments'..ac.reset)
print(arguments)
end
local function read_log_file(logfile)
local file = io.open(logfile, "r")
if not file then
error(" Could not open the file")
end
local content = file:read("*all")
file:close()
return content
end
local function parse_blocks(result)
local blocks = {}
for line in result:gmatch("[^\r\n]+") do
local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)")
if block_num and block_data then
block_num = tonumber(block_num)
block_data = block_data:gsub("%s+", "")
blocks[block_num] = block_data
end
end
return blocks
end
local function hex_to_bin(hex_string)
local bin_string = ""
local hex_to_bin_map = {
['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011",
['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111",
['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011",
['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111"
}
for i = 1, #hex_string do
bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)]
end
return bin_string
end
local function remove_last_two_bits(binary_str)
return binary_str:sub(1, #binary_str - 2)
end
local function split_into_5bit_chunks(binary_str)
local chunks = {}
for i = 1, #binary_str, 5 do
table.insert(chunks, binary_str:sub(i, i + 4))
end
return chunks
end
local function remove_parity_bit(chunks)
local no_parity_chunks = {}
for _, chunk in ipairs(chunks) do
if #chunk == 5 then
table.insert(no_parity_chunks, chunk:sub(2))
end
end
return no_parity_chunks
end
local function convert_to_hex(chunks)
local hex_values = {}
for _, chunk in ipairs(chunks) do
if #chunk > 0 then
table.insert(hex_values, string.format("%X", tonumber(chunk, 2)))
end
end
return hex_values
end
local function convert_to_decimal(chunks)
local decimal_values = {}
for _, chunk in ipairs(chunks) do
table.insert(decimal_values, tonumber(chunk, 2))
end
return decimal_values
end
local function find_until_before_f(hex_values)
local result = {}
for _, value in ipairs(hex_values) do
if value == 'F' then
break
end
table.insert(result, value)
end
return result
end
local function process_block(block)
local binary_str = hex_to_bin(block)
binary_str = remove_last_two_bits(binary_str)
local chunks = split_into_5bit_chunks(binary_str)
local no_parity_chunks = remove_parity_bit(chunks)
return no_parity_chunks
end
local function calculate_id_net(blocks)
local all_hex_values = {}
for _, block in ipairs(blocks) do
local hex_values = convert_to_hex(process_block(block))
for _, hex in ipairs(hex_values) do
table.insert(all_hex_values, hex)
end
end
local selected_hex_values = find_until_before_f(all_hex_values)
if #selected_hex_values == 0 then
error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5')
end
local combined_hex = table.concat(selected_hex_values)
if not combined_hex:match("^%x+$") then
error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5')
end
local decimal_id = tonumber(combined_hex)
local stripped_hex_id = string.format("%X", decimal_id)
local padded_hex_id = string.format("%010X", decimal_id)
return decimal_id, padded_hex_id
end
local function calculate_id_switch(blocks)
local all_decimal_values = {}
for _, block in ipairs(blocks) do
local decimal_values = convert_to_decimal(process_block(block))
for _, dec in ipairs(decimal_values) do
table.insert(all_decimal_values, dec)
end
end
if #all_decimal_values < 15 then
error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7')
end
local id_positions = {9, 11, 13, 15, 2, 4, 6, 8}
local id_numbers = {}
for _, pos in ipairs(id_positions) do
table.insert(id_numbers, all_decimal_values[pos])
end
local decimal_id = tonumber(table.concat(id_numbers))
local padded_hex_id = string.format("%010X", decimal_id)
return decimal_id, padded_hex_id
end
local function name_exists_in_log(name)
local file = io.open(log_file_path, "r")
if not file then
return false
end
local pattern = "^Name:%s*" .. name .. "%s*$"
for line in file:lines() do
if line:match(pattern) then
file:close()
return true
end
end
file:close()
return false
end
local function log_result(blocks, em410_id, name)
local log_file = io.open(log_file_path, "a")
if log_file then
log_file:write("Name: " .. name .. "\n")
log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n")
for i = 4, 7 do
log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil"))
end
log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil"))
log_file:write('--------------------------\n')
log_file:close()
print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset)
else
print(" Failed to open log file for writing.")
end
end
local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
while true do
io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset)
local choice = io.read()
if choice == "1" then
io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
io.read()
print(dash)
command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846")
command("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846")
command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846")
command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846")
elseif choice == "2" then
io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
io.read()
p:console("lf em 410x clone --id " .. padded_hex_id)
print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset)
else
print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset)
goto ask_again
end
while true do
print(dash)
io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset)
local another = io.read()
if another:lower() == "n" then
if was_option_3 then
print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist")
return
end
print()
print(ac.green .. " Saving Paxton_log file..." .. ac.reset)
while true do
io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow)
name = io.read()
io.write(ac.reset..'')
if name == nil or name:match("^%s*$") then
print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.')
else
if name_exists_in_log(name) then
print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.')
else
break
end
end
end
log_result(blocks, padded_hex_id, name)
print(ac.green .. " Log saved successfully!" .. ac.reset)
local file = io.open(logfile, "w+")
file:write("")
file:close()
return
elseif another:lower() == "y" then
goto ask_again
else
print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset)
end
end
::ask_again::
end
end
local function is_valid_hex(input)
return #input == 8 and input:match("^[0-9A-Fa-f]+$")
end
local function main(args)
while true do
for o, a in getopt.getopt(args, 'h') do
if o == 'h' then return help() end
end
command('clear')
print(dash)
print(ac.green .. ' Select option: ' .. ac.reset)
print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy')
print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7')
print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data")
print(dash)
while true do
io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset)
input_option = io.read()
if input_option == "1" or input_option == "2" or input_option == "3" then
break
else
print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset)
end
end
local was_option_3 = false
if input_option == "1" then
local show_place_message = true
while true do
if show_place_message then
io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..')
end
io.read()
print(dash)
p:console('lf hitag read --ht2 -k BDF5E846')
if not logfile then
error(" No files in this directory")
end
local result = read_log_file(logfile)
local blocks = parse_blocks(result)
local empty_block = false
for i = 4, 7 do
if not blocks[i] then
empty_block = true
break
end
end
if empty_block then
io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..')
show_place_message = false
else
print(' Readed blocks:')
print()
for i = 4, 7 do
if blocks[i] then
print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset))
end
end
local decimal_id, padded_hex_id
if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then
print(dash)
print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset)
decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]})
else
print(dash)
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
end
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
print(dash)
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
break
end
end
elseif input_option == "2" then
local blocks = {}
for i = 4, 7 do
while true do
io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow)
local input = io.read()
input = input:upper()
if is_valid_hex(input) then
blocks[i] = input
break
else
print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).')
end
end
end
local decimal_id, padded_hex_id
if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then
print(ac.reset.. dash)
print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset)
decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]})
else
print(ac.reset.. dash)
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
end
print(dash)
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
print(dash)
if not padded_hex_id then
print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided')
return
end
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
break
elseif input_option == "3" then
was_option_3 = true
local retries = 3
while retries > 0 do
io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow)
local user_input = io.read()
io.write(ac.reset..'')
if user_input == nil or user_input:match("^%s*$") then
print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!')
end
local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$"
local file = io.open(log_file_path, "r")
if not file then
print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.')
return
end
local lines = {}
for line in file:lines() do
table.insert(lines, line)
end
file:close()
local found = false
for i = 1, #lines do
if lines[i]:match(name_clean) then
nam = user_input
local blocks = {
[4] = lines[i + 2]:match("Block 4: (.+)"),
[5] = lines[i + 3]:match("Block 5: (.+)"),
[6] = lines[i + 4]:match("Block 6: (.+)"),
[7] = lines[i + 5]:match("Block 7: (.+)")
}
local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)")
print(dash)
print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset)
for j = 4, 7 do
print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset))
end
print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset)
print(dash)
local decimal_id, padded_hex_id = em4102_id, em4102_id
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam)
found = true
break
end
end
if not found then
retries = retries - 1
else
break
end
end
if retries == 0 then
print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset)
end
end
print(dash)
print(' Exiting script Lua...')
return
end
end
main(args)

View file

@ -11,6 +11,7 @@ p = pm3.pm3()
command('clear')
author = ' Author: jareckib - 12.03.2025'
version = ' version v1.05'
desc = [[
This simple script stores 1, 2 or 3 different EM4102 on a single T5577.
There is an option to enter the number engraved on the fob in decimal form.
@ -61,8 +62,6 @@ local function timer(n)
end
end
local function reset_log_file()
local file = io.open(logfile, "w+")
file:write("")

View file

@ -1866,7 +1866,7 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_conf
PacketResponseNG resp;
if (GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &resp, 10000, true) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -349,7 +349,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
g_conn.block_after_ACK = false;
free(data);
return PM3_ETIMEOUT;
@ -473,7 +473,7 @@ static int CmdFlashMemWipe(const char *Cmd) {
SendCommandMIX(CMD_FLASHMEM_WIPE, page, initialwipe, 0, NULL, 0);
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 10000)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -69,7 +69,7 @@ int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen
uint8_t retry = 3;
while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
retry--;
if (retry == 0) {
ret_val = PM3_ETIMEOUT;
@ -96,7 +96,7 @@ int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *dest
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)fn, fnlen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -401,7 +401,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) {
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -83,7 +83,7 @@ int CmdHFSearch(const char *Cmd) {
int res = PM3_ESOFT;
uint8_t success[20] = {0};
uint8_t success[COUNT_OF_PROTOCOLS] = {0};
PROMPT_CLEARLINE;
PrintAndLogEx(INPLACE, " Searching for ThinFilm tag...");
@ -526,7 +526,7 @@ int handle_hf_plot(bool show_plot) {
PacketResponseNG resp;
if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &resp, 4000, true) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -498,7 +498,7 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -806,7 +806,7 @@ static int CmdHF14ACUIDs(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1206,9 +1206,9 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen
}
}
uint16_t cmdc = 0;
uint16_t cmdc = (ISO14A_APDU | ISO14A_NO_DISCONNECT);
if (chainingin) {
cmdc = ISO14A_SEND_CHAINING;
cmdc |= ISO14A_SEND_CHAINING;
}
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
@ -1216,9 +1216,9 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen
// here length PM3_CMD_DATA_SIZE=512
// timeout must be authomatically set by "get ATS"
if (datain) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF);
SendCommandMIX(CMD_HF_ISO14443A_READER, cmdc, (datainlen & 0x1FF), 0, datain, (datainlen & 0x1FF));
} else {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0);
SendCommandMIX(CMD_HF_ISO14443A_READER, cmdc, 0, 0, NULL, 0);
}
PacketResponseNG resp;
@ -1670,7 +1670,7 @@ static int waitCmd(bool i_select, uint32_t timeout, bool verbose) {
}
} else {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
return PM3_SUCCESS;
@ -2264,7 +2264,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
DropField();
return PM3_ETIMEOUT;
}
@ -2324,7 +2324,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
clearCommandBuffer();
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats));
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -1182,7 +1182,7 @@ static void hf15EmlClear(void) {
SendCommandNG(CMD_HF_ISO15693_EML_CLEAR, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_ISO15693_EML_CLEAR, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
}
}

View file

@ -264,7 +264,7 @@ static const char *felica_model_name(uint8_t rom_type, uint8_t ic_type) {
*/
static bool waitCmdFelica(bool iSelect, PacketResponseNG *resp, bool verbose) {
if (WaitForResponseTimeout(CMD_ACK, resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return false;
}
@ -2068,7 +2068,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) {
return PM3_EOPABORTED;
}
if (timeout > 10) {
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.");
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply");
DropField();
return PM3_ETIMEOUT;
}

View file

@ -922,7 +922,7 @@ static int CmdHFiClassSim(const char *Cmd) {
return PM3_EOPABORTED;
}
if (tries > 20) {
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.");
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply");
return PM3_ETIMEOUT;
}
}
@ -973,7 +973,7 @@ static int CmdHFiClassSim(const char *Cmd) {
return PM3_EOPABORTED;
}
if (tries > 20) {
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.");
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply");
return PM3_ETIMEOUT;
}
}
@ -1186,7 +1186,7 @@ static int CmdHFiClassELoad(const char *Cmd) {
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1482,7 +1482,8 @@ static void iclass_decode_credentials(uint8_t *data) {
char *pbin = binstr;
while (strlen(pbin) && *(++pbin) == '0');
PrintAndLogEx(SUCCESS, "Binary... %zu - " _GREEN_("%s"), strlen(pbin), pbin);
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s") " ( %zu )", pbin, strlen(pbin));
PrintAndLogEx(NORMAL, "");
decode_wiegand(top, mid, bot, 0);
}

View file

@ -1829,8 +1829,8 @@ void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is
uint8_t ks = cmd[pos + 3];
if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) {
snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks);
return;
}
return;
}
if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) {

View file

@ -132,7 +132,7 @@ static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply.");
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -2923,6 +2923,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
noValidKeyFound:
PrintAndLogEx(FAILED, "No usable key was found!");
PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf autopwn -f mfc_default_keys")"` i.e. the Randy special");
free(keyBlock);
free(e_sector);
free(fptr);
@ -4712,7 +4713,7 @@ int CmdHF14AMfELoad(const char *Cmd) {
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -6278,7 +6279,8 @@ static int CmdHF14AMfMAD(const char *Cmd) {
if (sector > -1) {
// decode it
PrintAndLogEx(INFO, "");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------");
PrintAndLogEx(INFO, _CYAN_("HID PACS detected"));
uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0};
@ -6301,7 +6303,6 @@ static int CmdHF14AMfMAD(const char *Cmd) {
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
PrintAndLogEx(INFO, "Wiegand decode");
decode_wiegand(top, mid, bot, 0);
}
}

View file

@ -1446,7 +1446,7 @@ static int CmdHF14aDesChk(const char *Cmd) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -1655,7 +1655,7 @@ static int CmdHFMFPChk(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -315,7 +315,7 @@ int ul_read_uid(uint8_t *uid) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
iso14a_card_select_t card;
@ -3892,6 +3892,69 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
return PM3_SUCCESS;
}
//-------------------------------------------------------------------------------
// Ultralight AES Methods
//-------------------------------------------------------------------------------
// Ultralight AES Authentication
//
static int CmdHF14AMfUAESAuth(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf mfu aesauth",
"Tests AES key on Mifare Ultralight AES tags.\n"
"If no key is specified, null key will be tried.\n"
"Key index 0: DataProtKey (default)\n"
"Key index 1: UIDRetrKey\n"
"Key index 2: OriginalityKey\n",
"hf mfu aesauth\n"
"hf mfu aesauth --key <32 bytes> --index <0..2>"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "key", "<hex>", "AES key (32 hex bytes)"),
arg_int0("i", "index", "<0..2>", "Key index, default: 0"),
arg_lit0("k", NULL, "Keep field on (only if a key is provided)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int ak_len = 0;
uint8_t authentication_key[32] = {0};
uint8_t *authKeyPtr = authentication_key;
CLIGetHexWithReturn(ctx, 1, authentication_key, &ak_len);
int key_index = arg_get_int_def(ctx, 2, 0);
bool keep_field_on = arg_get_lit(ctx, 3);
CLIParserFree(ctx);
if (ak_len == 0) {
// default to null key
ak_len = 32;
}
if (ak_len != 32) {
PrintAndLogEx(WARNING, "Invalid key length");
return PM3_EINVARG;
}
if (key_index < 0 || key_index > 2) {
PrintAndLogEx(WARNING, "Invalid key index");
return PM3_EINVARG;
}
int result = ulaes_requestAuthentication(authKeyPtr, key_index, !keep_field_on);
const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" };
if (result == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )",
key_type[key_index], sprint_hex_inrow(authKeyPtr, ak_len));
} else {
PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )",
key_type[key_index]);
}
return result;
}
/**
A test function to validate that the polarssl-function works the same
was as the openssl-implementation.
@ -4168,7 +4231,7 @@ static int CmdHF14AMfUKeyGen(const char *Cmd) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -5915,6 +5978,7 @@ static command_t CommandTable[] = {
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"},
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"},
{"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"},
{"aesauth", CmdHF14AMfUAESAuth, IfPm3Iso14443a, "Ultralight-AES - Authentication"},
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
{"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"},

View file

@ -126,7 +126,7 @@ int infoThinFilm(bool verbose) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -54,7 +54,7 @@ static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, len, 0, cmd, len);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply.");
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -41,6 +41,7 @@
#include "cmdlfhid.h" // for hid menu
#include "cmdlfhitag.h" // for hitag menu
#include "cmdlfhitaghts.h" // for hitag S sub commands
#include "cmdlfhitagu.h" // for hitag µ sub commands
#include "cmdlfidteck.h" // for idteck menu
#include "cmdlfio.h" // for ioprox menu
#include "cmdlfcotag.h" // for COTAG menu
@ -455,7 +456,7 @@ int CmdLFCommandRead(const char *Cmd) {
getSamples(samples, false);
ret = PM3_SUCCESS;
} else {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1602,6 +1603,14 @@ static bool check_chiptype(bool getDeviceData) {
retval = true;
goto out;
}
// Hitag µ
if (read_htu_uid() == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("Hitag µ / 8265"));
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf hitag htu`") " commands");
retval = true;
goto out;
}
}

View file

@ -624,11 +624,12 @@ static int CmdEM410xSpoof(const char *Cmd) {
static int CmdEM410xClone(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x clone",
"clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469 or Hitag S/8211/8268/8310 tag.",
"clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469, Hitag S/8211/8268/8310 or Hitag µ/8265 tag.",
"lf em 410x clone --id 0F0368568B -> encode for T55x7 tag\n"
"lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag\n"
"lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469\n"
"lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310"
"lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310\n"
"lf em 410x clone --id 0F0368568B --htu -> encode for Hitag µ/8265 tag"
);
void *argtable[] = {
@ -638,6 +639,7 @@ static int CmdEM410xClone(const char *Cmd) {
arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"),
arg_lit0(NULL, "hts", "optional - specify writing to Hitag S/8211/8268/8310 tag"),
arg_lit0(NULL, "htu", "optional - specify writing to Hitag µ/8265 tag"),
arg_lit0(NULL, "electra", "optional - add Electra blocks to tag"),
arg_param_end
};
@ -651,23 +653,23 @@ static int CmdEM410xClone(const char *Cmd) {
bool q5 = arg_get_lit(ctx, 3);
bool em = arg_get_lit(ctx, 4);
bool hts = arg_get_lit(ctx, 5);
bool add_electra = arg_get_lit(ctx, 6);
bool htu = arg_get_lit(ctx, 6);
bool add_electra = arg_get_lit(ctx, 7);
CLIParserFree(ctx);
if (q5 + em + hts > 1) {
if (q5 + em + hts + htu > 1) {
PrintAndLogEx(FAILED, "Only specify one tag Type");
return PM3_EINVARG;
}
if (hts) {
if (IfPm3Hitag() == false) {
PrintAndLogEx(FAILED, "Device not compiled to support Hitag");
return PM3_EINVARG;
}
if (clk == 40) {
PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64"));
return PM3_EINVARG;
}
if ((hts || htu) && IfPm3Hitag() == false) {
PrintAndLogEx(FAILED, "Device not compiled to support Hitag");
return PM3_EINVARG;
}
if ((hts || htu) && clk == 40) {
PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64"));
return PM3_EINVARG;
}
// Allowed clock rates: 16, 32, 40 and 64
@ -678,9 +680,9 @@ static int CmdEM410xClone(const char *Cmd) {
uint64_t id = bytes_to_num(uid, uid_len);
PrintAndLogEx(SUCCESS, "Preparing to clone EM4102 to " _YELLOW_("%s") " tag with EM Tag ID " _GREEN_("%010" PRIX64) " (RF/%d)",
q5 ? "Q5/T5555" : (em ? "EM4305/4469" : (hts ? "Hitag S/82xx" : "T55x7")), id, clk);
q5 ? "Q5/T5555" : (em ? "EM4305/4469" : (hts ? "Hitag S/82xx" : (htu ? "Hitag µ/82xx" : "T55x7"))), id, clk);
uint8_t data[HITAG_BLOCK_SIZE * 2] = {0xFF, 0x80}; // EM410X_HEADER 9 bits of one
uint8_t data[8] = {0xFF, 0x80}; // EM410X_HEADER 9 bits of one
uint32_t databits = 9;
uint8_t c_parity = 0;
@ -688,11 +690,11 @@ static int CmdEM410xClone(const char *Cmd) {
uint8_t r_parity = 0;
uint8_t nibble = id >> i & 0xF;
databits = concatbits(data, databits, &nibble, 4, 4);
databits = concatbits(data, databits, &nibble, 4, 4, false);
for (size_t j = 0; j < 4; j++) {
r_parity ^= nibble >> j & 1;
}
databits = concatbits(data, databits, &r_parity, 7, 1);
databits = concatbits(data, databits, &r_parity, 7, 1, false);
c_parity ^= nibble;
}
data[7] |= c_parity << 1;
@ -706,35 +708,45 @@ static int CmdEM410xClone(const char *Cmd) {
lf_hitag_data_t packet;
memset(&packet, 0, sizeof(packet));
for (size_t steps = 0; steps < 3; steps++) {
switch (steps) {
case 0:
packet.data[0] = 0xCA; //compatiable for 82xx, no impact on Hitag S
// clk -> TTFDR1 TTFDR0
// 32 -> 0x00 4 kBit/s
// 16 -> 0x10 8 kBit/s
// 64 -> 0x20 2 kBit/s
packet.data[1] = 0x04;
for (size_t step = 0; step < 3; step++) {
switch (step) {
case 0: {
hitags_config_t config = {0};
config.MEMT = 0x02; // compatiable for 82xx, no impact on Hitag S
config.TTFM = 0x01; // 0 = "Block 0, Block 1, Block 2, Block 3", 1 = "Block 0, Block 1"
config.TTFC = 0x00; // Manchester
config.auth = 0x00; // Plain
//compatiable for 82xx, no impact on Hitag S
config.RES1 = 0x01;
config.RES4 = 0x01;
config.RES5 = 0x01;
switch (clk) {
case 64:
// 2 kBit/s
config.TTFDR = 0x02;
break;
case 32:
// 4 kBit/s
config.TTFDR = 0x00;
break;
case 16:
packet.data[1] |= 0x10;
break;
case 64:
packet.data[1] |= 0x20;
// 8 kBit/s
config.TTFDR = 0x01;
break;
}
packet.data[2] = 0;
packet.data[3] = 0; //TODO: keep PWDH0?
//TODO: keep other fields?
memcpy(packet.data, &config, sizeof(config));
// PrintAndLogEx(INFO, "packet.data: %s", sprint_hex(packet.data, sizeof(packet.data)));
packet.page = 1;
break;
}
case 1:
memcpy(packet.data, &data[HITAG_BLOCK_SIZE * 0], HITAG_BLOCK_SIZE);
memcpy(packet.data, &data[HITAGS_PAGE_SIZE * 0], HITAGS_PAGE_SIZE);
packet.page = 4;
break;
case 2:
memcpy(packet.data, &data[HITAG_BLOCK_SIZE * 1], HITAG_BLOCK_SIZE);
memcpy(packet.data, &data[HITAGS_PAGE_SIZE * 1], HITAGS_PAGE_SIZE);
packet.page = 5;
break;
}
@ -744,14 +756,82 @@ static int CmdEM410xClone(const char *Cmd) {
packet.mode = HITAGS_UID_REQ_FADV;
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Something went wrong");
PrintAndLogEx(WARNING, "Something went wrong in step %zu", step);
return resp.status;
}
}
} else if (htu) {
lf_hitag_data_t packet;
memset(&packet, 0, sizeof(packet));
// Use password auth with default password
packet.cmd = HTUF_82xx;
memcpy(packet.pwd, "\x00\x00\x00\x00", HITAG_PASSWORD_SIZE);
// memcpy(packet.pwd, "\x9A\xC4\x99\x9C", HITAGU_BLOCK_SIZE);
for (size_t step = 0; step < 3; step++) {
switch (step) {
case 0: {
// Configure datarate based on clock
// clk -> datarate
// 64 -> 0x00 2 kBit/s
// 32 -> 0x01 4 kBit/s
// 16 -> 0x10 8 kBit/s
hitagu82xx_config_t config = {0};
config.datarate_override = 0x00; // no datarate override
config.encoding = 0x00; // Manchester
config.ttf_mode = 0x01; // 01 = "Block 0, Block 1"
config.ttf = 0x01; // enable TTF
switch (clk) {
case 64:
break;
case 32:
config.datarate = 0x01;
break;
case 16:
config.datarate = 0x02;
break;
}
packet.data[0] = reflect8(*(uint8_t *)&config);
packet.page = HITAGU_CONFIG_PADR; // Config block
break;
}
case 1:
memcpy(packet.data, &data[HITAGU_BLOCK_SIZE * 0], HITAGU_BLOCK_SIZE);
packet.page = 0; // Start writing EM410x data
break;
case 2:
memcpy(packet.data, &data[HITAGU_BLOCK_SIZE * 1], HITAGU_BLOCK_SIZE);
packet.page = 1; // Continue with second block
break;
}
SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_ENODATA && resp.status != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Something went wrong in step %zu, retrying... Press " _GREEN_("<Enter>") " to exit", step);
// 8265 Often fails during continuous command execution, need to retry
if (kbd_enter_pressed()) {
PrintAndLogEx(INFO, "Button pressed, user aborted");
return PM3_EOPABORTED;
}
step--;
continue;
}
//TODO: fix this
resp.status = PM3_SUCCESS;
}
} else {
struct {
bool Q5;
@ -771,7 +851,7 @@ static int CmdEM410xClone(const char *Cmd) {
SendCommandNG(CMD_LF_EM410X_CLONE, (uint8_t *)&payload, sizeof(payload));
if (WaitForResponseTimeout(CMD_LF_EM410X_CLONE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
}

View file

@ -469,7 +469,7 @@ static int em4x05_login_ext(uint32_t pwd) {
SendCommandNG(CMD_LF_EM4X_LOGIN, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_EM4X_LOGIN, &resp, 10000) == false) {
PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply.");
PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -496,7 +496,7 @@ int em4x05_read_word_ext(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t *wor
SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000) == false) {
PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply.");
PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1839,7 +1839,7 @@ int CmdEM4x05Brute(const char *Cmd) {
SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) {
PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply.");
PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply");
return PM3_ETIMEOUT;
}
PrintAndLogEx(INFO, "Bruteforce is running on device side, press button to interrupt");

View file

@ -341,7 +341,7 @@ static int CmdEM4x50Login(const char *Cmd) {
PacketResponseNG resp;
SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password));
if (WaitForResponseTimeout(CMD_LF_EM4X50_LOGIN, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -631,7 +631,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, EM4X50_TIMEOUT_CMD) == false) {
PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply.");
PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -740,7 +740,7 @@ static int CmdEM4x50Info(const char *Cmd) {
SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -962,7 +962,7 @@ static int CmdEM4x50Write(const char *Cmd) {
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1033,7 +1033,7 @@ static int CmdEM4x50WritePwd(const char *Cmd) {
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1117,7 +1117,7 @@ static int CmdEM4x50Wipe(const char *Cmd) {
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1213,7 +1213,7 @@ static int CmdEM4x50Restore(const char *Cmd) {
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -34,9 +34,15 @@
// TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first
// common header field that includes the command itself (to improve debugging / validation).
typedef struct _em4x70_tag_info_t {
/// <summary>
/// The full data on an em4x70 the tag.
/// The full data on an em4170 tag.
/// For V4070 tags:
/// * UM2 does not exist on the tag
/// * Pin does not exist on the tag
/// * UM1 (including the lock bits) might be one-time programmable (OTP)
///
/// [31] == Block 15 MSB == UM2₆₃..UM2₅₆
/// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
/// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
@ -168,6 +174,7 @@ typedef struct _em4x70_cmd_input_calculate_t {
ID48LIB_KEY key;
ID48LIB_NONCE rn;
} em4x70_cmd_input_calculate_t;
typedef struct _em4x70_cmd_output_calculate_t {
ID48LIB_FRN frn;
ID48LIB_GRN grn;
@ -471,7 +478,7 @@ static int CmdEM4x70Info(const char *Cmd) {
int result = get_em4x70_info(&opts, &info);
if (result == PM3_ETIMEOUT) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
} else if (result == PM3_SUCCESS) {
em4x70_print_info_result(&info);
} else {
@ -523,7 +530,7 @@ static int CmdEM4x70Write(const char *Cmd) {
int result = writeblock_em4x70(&opts, &info);
if (result == PM3_ETIMEOUT) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
} else if (result == PM3_SUCCESS) {
em4x70_print_info_result(&info);
} else {
@ -657,7 +664,7 @@ static int CmdEM4x70Unlock(const char *Cmd) {
int result = unlock_em4x70(&opts, &info);
if (result == PM3_ETIMEOUT) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
} else if (result == PM3_SUCCESS) {
em4x70_print_info_result(&info);
} else {
@ -719,7 +726,7 @@ static int CmdEM4x70Auth(const char *Cmd) {
if (PM3_SUCCESS == result) {
PrintAndLogEx(INFO, "Tag Auth Response: %02X %02X %02X", data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]);
} else if (PM3_ETIMEOUT == result) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
} else {
PrintAndLogEx(FAILED, "TAG Authentication ( " _RED_("fail") " )");
}
@ -761,7 +768,7 @@ static int CmdEM4x70SetPIN(const char *Cmd) {
int result = setpin_em4x70(&opts, &info);
if (result == PM3_ETIMEOUT) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
} else if (result == PM3_SUCCESS) {
em4x70_print_info_result(&info);
PrintAndLogEx(INFO, "Writing new PIN ( " _GREEN_("ok") " )");
@ -805,7 +812,7 @@ static int CmdEM4x70SetKey(const char *Cmd) {
int result = setkey_em4x70(&opts);
if (PM3_ETIMEOUT == result) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
} else if (PM3_SUCCESS != result) {
PrintAndLogEx(FAILED, "Writing new key " _RED_("fail"));
@ -850,7 +857,7 @@ static int CmdEM4x70SetKey(const char *Cmd) {
result = verify_auth_em4x70(&opts_v);
if (PM3_ETIMEOUT == result) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return result;
} else if (PM3_SUCCESS != result) {
PrintAndLogEx(FAILED, "Authenticating with new key ( " _RED_("fail") " )");
@ -1197,7 +1204,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
result = auth_em4x70(&opts_auth, &tag_grn);
if (PM3_ETIMEOUT == result) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return result;
} else if (PM3_SUCCESS != result) {
PrintAndLogEx(FAILED, "Authenticating with provided values ( " _RED_("fail") " )");
@ -1241,7 +1248,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
result = writeblock_em4x70(&opt_write_zeros, &tag_info);
if (PM3_ETIMEOUT == result) {
PrintAndLogEx(FAILED, "Timeout while waiting for reply.");
PrintAndLogEx(FAILED, "timeout while waiting for reply");
PrintAndLogEx(HINT, "Block %d data may have been overwritten. Manually restart at step %d", block, step);
return result;
} else if (PM3_SUCCESS != result) {
@ -1266,7 +1273,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
result = brute_em4x70(&opts_brute, &brute);
if (PM3_ETIMEOUT == result) {
PrintAndLogEx(FAILED, "Timeout while waiting for reply.");
PrintAndLogEx(FAILED, "timeout while waiting for reply");
PrintAndLogEx(HINT, "Block %d data was overwritten. Manually restart at step %d", block, step);
return result;
} else if (PM3_SUCCESS != result) {
@ -1305,7 +1312,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
result = writeblock_em4x70(&opt_write_zeros, &tag_info);
if (PM3_ETIMEOUT == result) {
PrintAndLogEx(FAILED, "Timeout while waiting for reply.");
PrintAndLogEx(FAILED, "timeout while waiting for reply");
PrintAndLogEx(HINT, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]);
return result;
} else if (PM3_SUCCESS != result) {
@ -1571,7 +1578,7 @@ bool detect_4x70_block(void) {
int result = get_em4x70_info(&opts, &info);
if (result == PM3_ETIMEOUT) { // consider removing this output?
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
}
return result == PM3_SUCCESS;
}

View file

@ -473,7 +473,7 @@ static int CmdHIDClone(const char *Cmd) {
SendCommandNG(CMD_LF_HID_CLONE, (uint8_t *)&payload, sizeof(payload));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HID_CLONE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -16,6 +16,8 @@
// Low frequency Hitag support
//-----------------------------------------------------------------------------
#include "cmdlfhitag.h"
#include "cmdlfhitaghts.h"
#include "cmdlfhitagu.h"
#include <ctype.h>
#include "cmdparser.h" // command_t
#include "comms.h"
@ -31,7 +33,6 @@
#include "pm3_cmd.h" // return codes
#include "hitag2/hitag2_crypto.h"
#include "util_posix.h" // msclock
#include "cmdlfhitaghts.h"
static int CmdHelp(const char *Cmd);
@ -79,7 +80,7 @@ static size_t nbytes(size_t nbits) {
*/
static int CmdLFHitagList(const char *Cmd) {
return CmdTraceListAlias(Cmd, "lf hitag", "hitag2");
return CmdTraceListAlias(Cmd, "lf hitag", "ht2");
/*
uint8_t *got = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
if (!got) {
@ -519,7 +520,7 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;
}
@ -793,9 +794,6 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize,
free(binstr);
}
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
}
static const char *identify_transponder_hitag2(uint32_t uid) {
switch (uid) {
@ -827,12 +825,12 @@ static bool ht2_get_uid(uint32_t *uid) {
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return false;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID");
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag 2 UID");
return false;
}
@ -1054,7 +1052,7 @@ static int CmdLFHitagRd(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;
}
@ -1143,7 +1141,7 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) {
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
@ -1305,7 +1303,7 @@ static int CmdLFHitagWriter(const char *Cmd) {
SendCommandNG(CMD_LF_HITAG2_WRITE, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAG2_WRITE, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -1528,7 +1526,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
if (attempt == 0) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ESOFT;
}
@ -1546,7 +1544,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 5000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
@ -2256,7 +2254,7 @@ static int CmdLFHitag2Crack2(const char *Cmd) {
if (attempt == 0) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ESOFT;
}
@ -2461,6 +2459,7 @@ static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"},
{"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"},
{"htu", CmdLFHitagU, AlwaysAvailable, "{ Hitag µ/8265 operations }"},
{"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"},
{"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"},
{"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag 2 reader"},

View file

@ -31,7 +31,6 @@ int ht2_read_uid(void);
int ht2_read_paxton(void);
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, uint8_t bits, bool is_response, const uint64_t *keys, uint32_t keycount, bool isdecrypted);
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response);
void annotateHitag2_init(void);
bool hitag2_get_plain(uint8_t *plain, uint8_t *plen);

View file

@ -36,6 +36,84 @@
static int CmdHelp(const char *Cmd);
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t nbits, bool is_response) {
size_t exp_len = 0;
uint8_t command = 0;
if (is_response) {
// Handle responses
if (nbits == 32) {
exp_len = snprintf(exp, size, "UID: [%02X%02X%02X%02X]", cmd[0], cmd[1], cmd[2], cmd[3]);
} else if (nbits == 40) {
exp_len = snprintf(exp, size, "Data");
}
} else if (nbits >= 5) {
concatbits(&command, 0, cmd, 0, 5, false);
if (nbits == 5) {
concatbits(&command, 0, cmd, 0, 5, false);
switch (command) {
case HITAGS_UID_REQ_STD:
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Standard 00110)");
break;
case HITAGS_UID_REQ_ADV1:
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Advanced 11000)");
break;
case HITAGS_UID_REQ_ADV2:
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Advanced 11001)");
break;
case HITAGS_UID_REQ_FADV:
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Fast Advanced 11010)");
break;
}
} else if (nbits == 4 + 8 + 8) {
concatbits(&command, 0, cmd, 0, 4, false);
if (command == HITAGS_READ_PAGE) {
exp_len += snprintf(exp + exp_len, size - exp_len, "READ");
} else if (command == HITAGS_WRITE_PAGE) {
exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE");
} else if (command == HITAGS_READ_BLOCK) {
exp_len += snprintf(exp + exp_len, size - exp_len, "READ_BLOCK");
} else if (command == HITAGS_WRITE_BLOCK) {
exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE_BLOCK");
} else if (command == HITAGS_QUIET) {
exp_len += snprintf(exp + exp_len, size - exp_len, "QUIET");
}
// Hitag 1 commands
else if (command == HITAG1_RDCPAGE) {
exp_len += snprintf(exp + exp_len, size - exp_len, "RDCPAGE");
} else if (command == HITAG1_RDCBLK) {
exp_len += snprintf(exp + exp_len, size - exp_len, "RDCBLK");
} else if (command == HITAG1_WRCPAGE) {
exp_len += snprintf(exp + exp_len, size - exp_len, "WRCPAGE");
} else if (command == HITAG1_WRCBLK) {
exp_len += snprintf(exp + exp_len, size - exp_len, "WRCBLK");
} else {
exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown (%02X)", command);
}
uint8_t page = 0;
concatbits(&page, 0, cmd, 5, 8, false);
exp_len += snprintf(exp + exp_len, size - exp_len, " Page: %d", page);
} else if (nbits == 32 + 8) {
concatbits(&command, 0, cmd, 0, 5, false);
exp_len += snprintf(exp + exp_len, size - exp_len, "Data");
} else if (nbits == 5 + 32 + 8 || nbits == 5 + 32 + 1 + 8) {
concatbits(&command, 0, cmd, 0, 5, false);
if (command == HITAGS_SELECT) {
uint8_t uid[4] = {0};
concatbits(uid, 0, cmd, 5, 32, false);
exp_len = snprintf(exp, size, "SELECT UID: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]);
}
}
} else {
exp_len = snprintf(exp, size, "Invalid command (too short)");
}
}
static const char *hts_get_type_str(uint32_t uid) {
// source 1: https://www.scorpio-lk.com/downloads/Tango/HITAG_Classification.pdf
// IDE Mark
@ -83,12 +161,12 @@ static bool hts_get_uid(uint32_t *uid) {
SendCommandNG(CMD_LF_HITAGS_UID, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_UID, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return false;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID");
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag S UID");
return false;
}
@ -253,10 +331,38 @@ static void print_error(int8_t reason) {
break;
default:
// PM3_REASON_UNKNOWN
PrintAndLogEx(DEBUG, "DEBUG: Error - Hitag S failed");
PrintAndLogEx(FAILED, "Error - Hitag S failed");
}
}
static void hitags_config_print(hitags_config_t config) {
PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"),
(const char *[]) {"Hitag S 32", "Hitag S 256", "Hitag S 2048", "Unknown Hitag S/8211"}[config.MEMT]);
PrintAndLogEx(INFO, " Authenticaion.... %s", config.auth ? _YELLOW_("Yes") : "No");
PrintAndLogEx(INFO, " TTF coding....... %s",
config.RES3 ? "FSK 0=RF/10 1=RF/8" : (const char *[]) {"Manchester", "Biphase"}[config.TTFC]);
PrintAndLogEx(INFO, " TTF data rate.... %s",
(const char *[]) {"4 kBit", "8 kBit", "2 kBit", "2 kBit and Pigeon Race Standard"}[config.TTFDR]);
PrintAndLogEx(INFO, " TTF mode......... %s",
(const char *[]) {
"TTF Mode disabled (= RTF Mode)",
"Page 4, Page 5",
"Page 4, Page 5, Page 6, Page 7",
"Page 4",
"TTF Mode disabled (= RTF Mode)",
"Page 4, Page 5, Page 6",
"Page 4, Page 5, Page 6, Page 7, Page 8",
"Page 4, Page 5, Page 6, Page 7, Page 8, Page 9, Page 10, Page 11",
}[config.RES0 << 2 | config.TTFM]);
PrintAndLogEx(INFO, " Config locked.... %s", config.LCON ? _RED_("Yes") : _GREEN_("No"));
PrintAndLogEx(INFO, " Key/PWD locked... %s", config.LKP ? _RED_("Yes") : _GREEN_("No"));
}
static int CmdLFHitagSRead(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag hts rdbl",
@ -314,7 +420,7 @@ static int CmdLFHitagSRead(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;
}
@ -501,7 +607,7 @@ static int CmdLFHitagSDump(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -603,7 +709,7 @@ static int CmdLFHitagSRestore(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
free(dump);
return PM3_ETIMEOUT;
}
@ -654,7 +760,7 @@ static int CmdLFHitagSRestore(const char *Cmd) {
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
free(dump);
return PM3_ETIMEOUT;
}
@ -727,7 +833,7 @@ static int CmdLFHitagSRestore(const char *Cmd) {
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
free(dump);
return PM3_ETIMEOUT;
}
@ -812,7 +918,7 @@ static int CmdLFHitagSWrite(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
@ -868,59 +974,28 @@ static int CmdLFHitagSSim(const char *Cmd) {
"Simulate Hitag S transponder\n"
"You need to `lf hitag hts eload` first",
"lf hitag hts sim\n"
"lf hitag hts sim --82xx");
"lf hitag hts sim --82xx\n"
"lf hitag hts sim -t 30 -> set threshold to 30");
void *argtable[] = {
arg_param_begin,
arg_lit0("8", "82xx", "simulate 8268/8310"),
arg_int0("t", "threshold", "<dec>", "set edge detect threshold (def: 127)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
// bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet
int threshold = arg_get_int_def(ctx, 2, 127);
CLIParserFree(ctx);
clearCommandBuffer();
SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, 0, 0, NULL, 0);
SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, threshold, 0, NULL, 0);
return PM3_SUCCESS;
}
static int CmdLFHitagSList(const char *Cmd) {
return CmdTraceListAlias(Cmd, "lf hitag hts", "hitags");
}
void hitags_config_print(hitags_config_t config) {
PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"),
(const char *[]) {
"Hitag S 32", "Hitag S 256", "Hitag S 2048",
"Unknown Hitag S/8211"
}[config.MEMT]);
PrintAndLogEx(INFO, " Authenticaion.... %s", config.auth ? _YELLOW_("Yes") : "No");
PrintAndLogEx(INFO, " TTF coding....... %s",
config.RES3 ? "FSK 0=RF/10 1=RF/8" : (const char *[]) {"Manchester", "Biphase"}[config.TTFC]);
PrintAndLogEx(INFO, " TTF data rate.... %s",
(const char *[]) {
"4 kBit", "8 kBit", "2 kBit",
"2 kBit and Pigeon Race Standard"
}[config.TTFDR]);
PrintAndLogEx(INFO, " TTF mode......... %s",
(const char *[]) {
"TTF Mode disabled (= RTF Mode)",
"Page 4, Page 5",
"Page 4, Page 5, Page 6, Page 7",
"Page 4",
"TTF Mode disabled (= RTF Mode)",
"Page 4, Page 5, Page 6",
"Page 4, Page 5, Page 6, Page 7, Page 8",
"Page 4, Page 5, Page 6, Page 7, Page 8, Page 9, Page 10, Page 11",
}[config.RES0 << 2 | config.TTFM]);
PrintAndLogEx(INFO, " Config locked.... %s", config.LCON ? _RED_("Yes") : _GREEN_("No"));
PrintAndLogEx(INFO, " Key/PWD locked... %s", config.LKP ? _RED_("Yes") : _GREEN_("No"));
return CmdTraceListAlias(Cmd, "lf hitag hts", "hts");
}
static command_t CommandTable[] = {

View file

@ -22,9 +22,10 @@
#include "common.h"
#include "hitag.h"
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t nbits, bool is_response);
int CmdLFHitagS(const char *Cmd);
int read_hts_uid(void);
void hitags_config_print(hitags_config_t config);
#endif //CMDLFHITAGS_H__

731
client/src/cmdlfhitagu.c Normal file
View file

@ -0,0 +1,731 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Low frequency Hitag µ support
//-----------------------------------------------------------------------------
#include "cmdlfhitagu.h"
#include "cliparser.h"
#include "cmddata.h" // setDemodBuff
#include "cmdparser.h" // command_t
#include "cmdtrace.h"
#include "commonutil.h"
#include "comms.h"
#include "crc16.h"
#include "fileutils.h" // savefile
#include "graph.h" // MAX_GRAPH_TRACE_LEN
#include "hitag.h"
#include "hitag2/hitag2_crypto.h"
#include "lfdemod.h"
#include "pm3_cmd.h" // return codes
#include "protocols.h" // defines
#include "util_posix.h" // msclock
#include <ctype.h>
static int CmdHelp(const char *Cmd);
uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit) {
if (nbit < 9) {
return 2;
}
return (Crc16(d, nbit, 0, CRC16_POLY_CCITT, false, false) == 0);
}
void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
if (is_response) {
} else {
uint8_t flag = reflect8(cmd[0]) & 0x1F;
uint8_t command = ((reflect8(cmd[0]) >> 5) & 0x07) | ((reflect8(cmd[1]) & 0x07) << 3);
bool has_uid = false;
size_t exp_len = snprintf(exp, size, "Flg:");
if ((flag & HITAGU_FLAG_PEXT) == HITAGU_FLAG_PEXT) {
exp_len += snprintf(exp + exp_len, size - exp_len, " PEXT");
}
if ((flag & HITAGU_FLAG_INV) == HITAGU_FLAG_INV) {
exp_len += snprintf(exp + exp_len, size - exp_len, " INV");
if ((flag & HITAGU_FLAG_RFU) == HITAGU_FLAG_RFU) {
exp_len += snprintf(exp + exp_len, size - exp_len, " RFU");
}
if ((flag & HITAGU_FLAG_NOS) == HITAGU_FLAG_NOS) {
exp_len += snprintf(exp + exp_len, size - exp_len, " NOS");
}
} else {
if ((flag & HITAGU_FLAG_SEL) == HITAGU_FLAG_SEL) {
exp_len += snprintf(exp + exp_len, size - exp_len, " SEL");
}
if ((flag & HITAGU_FLAG_ADR) == HITAGU_FLAG_ADR) {
exp_len += snprintf(exp + exp_len, size - exp_len, " ADR");
has_uid = true;
}
}
if ((flag & HITAGU_FLAG_CRCT) == HITAGU_FLAG_CRCT) {
exp_len += snprintf(exp + exp_len, size - exp_len, " CRCT");
}
exp_len += snprintf(exp + exp_len, size - exp_len, "|Cmd: ");
switch (command) {
case HITAGU_CMD_LOGIN: {
bool has_mfc = false;
if (cmdsize == (6 + (has_uid * HITAGU_UID_SIZE)) || cmdsize == (8 + (has_uid * HITAGU_UID_SIZE))) {
exp_len += snprintf(exp + exp_len, size - exp_len, "8265 LOGIN");
} else if (cmdsize == (7 + (has_uid * HITAGU_UID_SIZE)) || cmdsize == (9 + (has_uid * HITAGU_UID_SIZE))) {
uint8_t mfc = 0;
concatbits(&mfc, 0, cmd, 5 + 6 + 8 + 32, 8, false);
exp_len += snprintf(exp + exp_len, size - exp_len, "LOGIN mfc:%02x ", mfc);
has_mfc = true;
}
if (has_uid) {
uint8_t uid[HITAGU_UID_SIZE] = {0};
concatbits(uid, 0, cmd, 5 + 6 + has_mfc * 8 + 32, HITAGU_UID_SIZE * 8, false);
exp_len += snprintf(exp + exp_len, size - exp_len, " uid:%s", sprint_hex_inrow(uid, HITAGU_UID_SIZE));
}
uint8_t password[HITAG_PASSWORD_SIZE] = {0};
concatbits(password, 0, cmd, 5 + 6 + has_mfc * 8 + has_uid * HITAGU_UID_SIZE * 8, HITAG_PASSWORD_SIZE * 8, false);
exp_len += snprintf(exp + exp_len, size - exp_len, " pwd:%s", sprint_hex_inrow(password, HITAG_PASSWORD_SIZE));
break;
}
case HITAGU_CMD_INVENTORY: {
exp_len += snprintf(exp + exp_len, size - exp_len, "INVENTORY");
break;
}
case HITAGU_CMD_READ_MULTIPLE_BLOCK: {
uint8_t block_addr = 0;
concatbits(&block_addr, 0, cmd, 5 + 6, 8, false);
uint8_t block_count = 0;
concatbits(&block_count, 0, cmd, 5 + 6 + 8, 8, false);
exp_len += snprintf(exp + exp_len, size - exp_len, "READ MULTIPLE BLOCK start:%d num:%d"
, reflect8(block_addr)
, reflect8(block_count)
);
break;
}
case HITAGU_CMD_WRITE_SINGLE_BLOCK: {
uint8_t block_addr = 0;
concatbits(&block_addr, 0, cmd, 5 + 6, 8, false);
uint8_t block_data[4] = {0};
concatbits(block_data, 0, cmd, 5 + 6 + 8, 32, false);
exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE SINGLE BLOCK start:%d data:[%s]"
, reflect8(block_addr)
, sprint_hex_inrow(block_data, 4)
);
break;
}
case HITAGU_CMD_SELECT: {
exp_len += snprintf(exp + exp_len, size - exp_len, "SELECT");
break;
}
case HITAGU_CMD_SYSINFO: {
exp_len += snprintf(exp + exp_len, size - exp_len, "GET SYSTEM INFORMATION");
break;
}
case HITAGU_CMD_READ_UID: {
exp_len += snprintf(exp + exp_len, size - exp_len, "READ UID");
break;
}
case HITAGU_CMD_STAY_QUIET: {
exp_len += snprintf(exp + exp_len, size - exp_len, "STAY QUIET");
break;
}
default: {
exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown 0x%02X", command);
break;
}
}
}
}
static bool htu_get_uid(uint64_t *uid) {
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGU_UID, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGU_UID, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return false;
}
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag µ UID");
return false;
}
if (uid) {
*uid = bytes_to_num(resp.data.asBytes, HITAGU_UID_SIZE);
}
return true;
}
int read_htu_uid(void) {
uint64_t uid = 0;
if (htu_get_uid(&uid) == false) {
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%012llX"), uid);
// PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), htu_get_type_str(uid));
return PM3_SUCCESS;
}
static int process_hitagu_common_args(CLIParserContext *ctx, lf_hitag_data_t *const packet) {
bool use_82xx = arg_get_lit(ctx, 1);
bool use_password = false;
uint8_t key[HITAG_PASSWORD_SIZE];
int key_len = 0;
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, HITAG_PASSWORD_SIZE, &key_len);
if (res != 0) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (key_len != 0 && key_len != HITAG_PASSWORD_SIZE) {
PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", key_len);
return PM3_EINVARG;
}
// complete options
if (key_len == 0 && use_82xx) {
memcpy(key, "\x00\x00\x00\x00", 4);
key_len = 4;
} else if (key_len != 0) {
use_password = true;
}
memset(packet, 0, sizeof(*packet));
if (use_82xx) {
packet->cmd = HTUF_82xx;
memcpy(packet->pwd, key, sizeof(packet->pwd));
PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in 82xx mode");
} else if (use_password) {
packet->cmd = HTUF_PASSWORD;
memcpy(packet->pwd, key, sizeof(packet->pwd));
PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in password mode");
} else {
packet->cmd = HTUF_PLAIN;
memcpy(packet->pwd, key, sizeof(packet->pwd));
PrintAndLogEx(INFO, "Access " _YELLOW_("Hitag µ") " in Plain mode");
}
return PM3_SUCCESS;
}
static void print_error(int8_t reason) {
//todo: USE ENUM OR DEFINES
switch (reason) {
case 0: {
PrintAndLogEx(INFO, "No data");
break;
}
case -2: {
PrintAndLogEx(FAILED, "READ UID failed!");
break;
}
case -3: {
PrintAndLogEx(FAILED, "Get System Information / Config failed!");
break;
}
case -4: {
PrintAndLogEx(FAILED, "Login failed! Wrong password?");
break;
}
case -5: {
PrintAndLogEx(FAILED, "No write access on block. Not authorized?");
break;
}
case -6: {
PrintAndLogEx(FAILED, "Response CRC invalid!");
break;
}
case -7: {
PrintAndLogEx(FAILED, "Read block failed!");
break;
}
default: {
// PM3_REASON_UNKNOWN
PrintAndLogEx(FAILED, "Error - Hitag µ failed");
break;
}
}
}
static void hitagu_config_print(hitagu_config_t config) {
PrintAndLogEx(INFO, " Data Rate......... %s", (const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "Reserved"}[config.datarate]);
PrintAndLogEx(INFO, " Encoding.......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester"));
PrintAndLogEx(INFO, " Password Protect W Bit 0-127(block 0-3) %s", config.pwdW0_127 ? _RED_("Yes") : _GREEN_("No"));
PrintAndLogEx(INFO, " Password Protect W Bit 128-511(block 4-15) %s", config.pwdW128_511 ? _RED_("Yes") : _GREEN_("No"));
PrintAndLogEx(INFO, " Password Protect W Bit 512-Max(block 16-Max) %s", config.pwdW512_max ? _RED_("Yes") : _GREEN_("No"));
PrintAndLogEx(INFO, " Password Protect RW Bit 512-Max(block 16-Max) %s", config.pwdRW512_max ? _RED_("Yes") : _GREEN_("No"));
}
static void hitagu8265_config_print(hitagu82xx_config_t config) {
PrintAndLogEx(INFO, " Config Byte0: %s", sprint_hex((uint8_t *)&config, sizeof(config))); // for debug
// Check if datarate_override is set
if (config.datarate_override) {
PrintAndLogEx(INFO, " Data Rate........ %s", _YELLOW_("2 kbit/s"));
} else {
PrintAndLogEx(INFO, " Data Rate........ %s",
(const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "2 kbit/s"}[config.datarate]);
}
PrintAndLogEx(INFO, " Rate Override.... %s", config.datarate_override ? _RED_("Yes") : _GREEN_("No"));
PrintAndLogEx(INFO, " Encoding......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester"));
PrintAndLogEx(INFO, " TTF mode ........ %s",
(const char *[]) {
"Block 0, Block 1, Block 2, Block 3",
"Block 0, Block 1",
"Block 0, Block 1, Block 2, Block 3",
"Block 0, Block 1, Block 2, Block 3",
}[config.ttf_mode]);
PrintAndLogEx(INFO, " TTF.............. %s", config.ttf ? _GREEN_("Enabled") : _RED_("Disabled"));
}
static int CmdLFHitagURead(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag htu rdbl",
"Read Hitag µ memory.\n\n"
" 82xx password mode: \n"
" - default password 00000000\n",
" lf hitag htu rdbl -p 1 -> Hitag µ, plain mode\n"
" lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass\n"
" lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode\n");
void *argtable[] = {arg_param_begin,
arg_lit0("8", "82xx", "82xx mode"),
arg_str0("k", "key", "<hex>", "pwd, 4 hex bytes"),
arg_int0("p", "page", "<dec>", "block address to read from (def: 0)"),
arg_int0("c", "count", "<dec>", "how many blocks to read. '0' reads all blocks (def: 1)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
lf_hitag_data_t packet;
if (process_hitagu_common_args(ctx, &packet) < 0) {
return PM3_EINVARG;
}
uint32_t page = arg_get_int_def(ctx, 3, 0);
if (page >= HITAGU_MAX_BLOCKS) {
PrintAndLogEx(WARNING, "Block address out-of-range. Max is 255, got %u", page);
return PM3_EINVARG;
}
uint32_t count = arg_get_int_def(ctx, 4, 1);
if (count > HITAGU_MAX_BLOCKS) {
PrintAndLogEx(WARNING, "No more than %d blocks can be read at once", HITAGU_MAX_BLOCKS);
return PM3_EINVARG;
}
CLIParserFree(ctx);
packet.page = page;
packet.page_count = count;
// packet.mode = 1; // for debug
PrintAndLogEx(INFO, "Read Hitag µ memory block " _YELLOW_("%d") ", count " _YELLOW_("%d"), page, count);
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
print_error(resp.reason);
return PM3_ESOFT;
}
lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
int user_blocks;
uint8_t icr = card->icr;
if (icr == HITAGU_ICR_STANDARD) {
user_blocks = HITAGU_MAX_PAGE_STANDARD;
PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else if (icr == HITAGU_ICR_ADVANCED) {
user_blocks = HITAGU_MAX_PAGE_ADVANCED;
PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else if (icr == HITAGU_ICR_ADVANCED_PLUS) {
user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS;
PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else if (icr == HITAGU_ICR_8265) {
user_blocks = HITAGU_MAX_PAGE_8265;
PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else {
user_blocks = HITAGU_MAX_PAGE_STANDARD;
PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr);
}
if (packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) {
hitagu8265_config_print(card->config_page.s82xx);
} else {
hitagu_config_print(card->config_page.s);
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ---------------------------");
PrintAndLogEx(INFO, " # | 00 01 02 03 | ascii | perm | info");
PrintAndLogEx(INFO, "----+-------------+-------+------+------");
if (count == 0) {
count = (user_blocks > page) ? (user_blocks - page) : HITAGU_MAX_BLOCKS;
}
for (int i = 0; i < count; ++i) {
int page_addr = page + i;
if (page_addr >= HITAGU_MAX_BLOCKS) {
break;
}
if (card->pages_reason[i] > 0) {
PrintAndLogEx(SUCCESS, "% 3u | %s | " NOLF, page_addr, sprint_hex_ascii(card->pages[i], HITAGU_BLOCK_SIZE));
// access right
// 82xx
if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr != HITAGU_PASSWORD_PADR) {
PrintAndLogEx(NORMAL, _YELLOW_("RO ") NOLF);
} else if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr == HITAGU_PASSWORD_PADR) {
PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF);
// Hitag µ
} else if (page_addr < HITAGU_MAX_PAGE_STANDARD) {
if (card->config_page.s.pwdW0_127) {
PrintAndLogEx(NORMAL, _RED_("RO ") NOLF);
} else {
PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF);
}
} else if (HITAGU_MAX_PAGE_STANDARD <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED) {
if (card->config_page.s.pwdW128_511) {
PrintAndLogEx(NORMAL, _RED_("RO ") NOLF);
} else {
PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF);
}
} else if (HITAGU_MAX_PAGE_ADVANCED <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED_PLUS) {
if (card->config_page.s.pwdRW512_max) {
PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF);
} else {
if (card->config_page.s.pwdW512_max) {
PrintAndLogEx(NORMAL, _RED_("RO ") NOLF);
} else {
PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF);
}
}
} else {
PrintAndLogEx(NORMAL, _YELLOW_("UNK ") NOLF);
}
PrintAndLogEx(NORMAL, " | " NOLF);
// info
if (page_addr == HITAGU_PASSWORD_PADR) {
PrintAndLogEx(NORMAL, "Password");
} else if (page_addr == HITAGU_CONFIG_PADR) {
PrintAndLogEx(NORMAL, "Config");
} else {
PrintAndLogEx(NORMAL, "Data");
}
} else {
PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr);
print_error(card->pages_reason[i]);
}
}
PrintAndLogEx(INFO, "----+-------------+-------+------+------");
PrintAndLogEx(INFO, " " _YELLOW_("RO") " = Read without password, write with password");
PrintAndLogEx(INFO, " " _GREEN_("R/W") " = Read and write without password");
PrintAndLogEx(INFO, " " _RED_("R/WP") " = Read and write with password");
PrintAndLogEx(INFO, "----------------------------------------");
return PM3_SUCCESS;
}
static int CmdLFHitagUDump(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag htu dump",
"Read all Hitag µ memory and save to file\n"
" 82xx password mode: \n"
" - default password 00000000\n",
"lf hitag htu dump --82xx -> use def pwd\n"
"lf hitag htu dump --82xx -k 9AC4999C -> pwd mode\n");
void *argtable[] = {arg_param_begin,
arg_lit0("8", "82xx", "82xx mode"),
arg_str0("k", "key", "<hex>", "pwd, 4 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);
lf_hitag_data_t packet;
memset(&packet, 0, sizeof(packet));
if (process_hitagu_common_args(ctx, &packet) < 0) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool nosave = arg_get_lit(ctx, 4);
CLIParserFree(ctx);
// read all pages
packet.page = 0;
packet.page_count = 0;
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 5000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
if (resp.status != PM3_SUCCESS) {
print_error(resp.reason);
return PM3_ESOFT;
}
lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes;
int user_blocks;
uint8_t icr = card->icr;
if (icr == HITAGU_ICR_STANDARD) {
user_blocks = HITAGU_MAX_PAGE_STANDARD;
PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else if (icr == HITAGU_ICR_ADVANCED) {
user_blocks = HITAGU_MAX_PAGE_ADVANCED;
PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else if (icr == HITAGU_ICR_ADVANCED_PLUS) {
user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS;
PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else if (icr == HITAGU_ICR_8265) {
user_blocks = HITAGU_MAX_PAGE_8265;
PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
} else {
user_blocks = HITAGU_MAX_PAGE_STANDARD;
PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr);
}
int mem_size = (user_blocks + 2) * HITAGU_BLOCK_SIZE;
hitagu_config_t config = card->config_page.s;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
hitagu_config_print(config);
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-htu-");
FillFileNameByUID(fptr, card->uid, "-dump", HITAGU_UID_SIZE);
}
pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag);
return PM3_SUCCESS;
}
static int CmdLFHitagUWrite(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag htu wrbl",
"Write a block in Hitag µ memory.\n"
" 82xx password mode: \n"
" - default password 00000000\n",
" lf hitag htu wrbl -p 6 -d 01020304 -> Hitag µ, plain mode\n"
" lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd\n"
" lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode\n");
void *argtable[] = {arg_param_begin,
arg_lit0("8", "82xx", "82xx mode"),
arg_str0("k", "key", "<hex>", "pwd, 4 hex bytes"),
arg_int1("p", "page", "<dec>", "block address to write to"),
arg_str1("d", "data", "<hex>", "data, 4 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
lf_hitag_data_t packet;
if (process_hitagu_common_args(ctx, &packet) < 0) return PM3_EINVARG;
int page = arg_get_int_def(ctx, 3, 0);
uint8_t data[HITAGU_BLOCK_SIZE];
int data_len = 0;
int res = CLIParamHexToBuf(arg_get_str(ctx, 4), data, HITAGU_BLOCK_SIZE, &data_len);
if (res != 0) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
CLIParserFree(ctx);
packet.page = page;
memcpy(packet.data, data, sizeof(packet.data));
clearCommandBuffer();
SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet));
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}
if (resp.status == PM3_ETEAROFF) {
PrintAndLogEx(INFO, "Writing tear off triggered");
return PM3_SUCCESS;
}
if (resp.status != PM3_SUCCESS) {
print_error(resp.reason);
return resp.status;
}
PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
return PM3_SUCCESS;
}
static int CmdLFHitagUReader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag htu reader", "Act as a Hitag µ reader. Look for Hitag µ tags until Enter or the pm3 button is pressed\n",
"lf hitag htu reader\n"
"lf hitag htu reader -@ -> Continuous mode");
void *argtable[] = {
arg_param_begin, arg_lit0("@", NULL, "continuous reader mode"), arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool cm = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
if (cm) {
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
}
do {
read_htu_uid();
} while (cm && kbd_enter_pressed() == false);
return PM3_SUCCESS;
}
static int CmdLFHitagUSim(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf hitag htu sim",
"Simulate Hitag µ transponder\n"
"You need to `lf hitag htu eload` first",
"lf hitag htu sim\n"
"lf hitag htu sim --82xx\n"
"lf hitag htu sim -t 30 -> set threshold to 30");
void *argtable[] = {
arg_param_begin,
arg_lit0("8", "82xx", "simulate 82xx"),
arg_int0("t", "threshold", "<dec>", "set edge detect threshold (def: 127)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
// bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet
int threshold = arg_get_int_def(ctx, 2, 127);
CLIParserFree(ctx);
clearCommandBuffer();
SendCommandMIX(CMD_LF_HITAGU_SIMULATE, false, threshold, 0, NULL, 0);
return PM3_SUCCESS;
}
static int CmdLFHitagUList(const char *Cmd) { return CmdTraceListAlias(Cmd, "lf hitag htu", "htu"); }
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdLFHitagUList, AlwaysAvailable, "List Hitag µ trace history"},
{"-----------", CmdHelp, IfPm3Hitag, "----------- " _CYAN_("General") " -----------"},
{"reader", CmdLFHitagUReader, IfPm3Hitag, "Act like a Hitag µ reader"},
{"rdbl", CmdLFHitagURead, IfPm3Hitag, "Read Hitag µ block"},
{"dump", CmdLFHitagUDump, IfPm3Hitag, "Dump Hitag µ blocks to a file"},
{"wrbl", CmdLFHitagUWrite, IfPm3Hitag, "Write Hitag µ block"},
{"-----------", CmdHelp, IfPm3Hitag, "----------- " _CYAN_("Simulation") " -----------"},
{"sim", CmdLFHitagUSim, IfPm3Hitag, "Simulate Hitag µ transponder"},
{NULL, NULL, 0, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdLFHitagU(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}

32
client/src/cmdlfhitagu.h Normal file
View file

@ -0,0 +1,32 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Low frequency Hitag µ support
//-----------------------------------------------------------------------------
#ifndef CMDLFHITAGU_H__
#define CMDLFHITAGU_H__
#include "common.h"
#include "hitag.h"
uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit);
void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response);
int CmdLFHitagU(const char *Cmd);
int read_htu_uid(void);
#endif //CMDLFHITAGU_H__

View file

@ -1551,6 +1551,7 @@ bool testKnownConfigBlock(uint32_t block0) {
case T55X7_JABLOTRON_CONFIG_BLOCK:
case T55X7_PYRONIX_CONFIG_BLOCK:
case T55X7_TEXECOM_CONFIG_BLOCK:
case T55X7_BETECH_CONFIG_BLOCK:
return true;
}
return false;
@ -2302,12 +2303,15 @@ static void printT5x7KnownBlock0(uint32_t b0) {
case T55X7_TEXECOM_CONFIG_BLOCK:
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Texecom ");
break;
case T55X7_BETECH_CONFIG_BLOCK:
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Be-Tech ");
break;
default:
break;
}
if (strlen(s) > 0) {
PrintAndLogEx(SUCCESS, "Config block match : " _YELLOW_("%s"), s);
PrintAndLogEx(SUCCESS, "Config block match... " _YELLOW_("%s"), s);
}
}

View file

@ -44,6 +44,7 @@
#define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ...
#define T55X7_PYRONIX_CONFIG_BLOCK 0x00088C40 // ASK, Manchester, data rate 32, 2 data blocks
#define T55X7_TEXECOM_CONFIG_BLOCK 0x001C8020 // ASK, Manchester, data rate 128, 1 data block
#define T55X7_BETECH_CONFIG_BLOCK 0x001480E0 // ASK, Manchester, data rate 64, 7 data block
// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag
// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo,

View file

@ -658,7 +658,7 @@ static int CmdSmartUpgrade(const char *Cmd) {
clearCommandBuffer();
SendCommandNG(CMD_SMART_UPLOAD, (uint8_t *)&upload, sizeof(upload));
if (!WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
free(firmware);
return PM3_ETIMEOUT;
}
@ -690,7 +690,7 @@ static int CmdSmartUpgrade(const char *Cmd) {
free(firmware);
SendCommandNG(CMD_SMART_UPGRADE, (uint8_t *)&payload, sizeof(payload));
if (!WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -28,6 +28,8 @@
#include "comms.h" // for sending cmds to device. GetFromBigBuf
#include "fileutils.h" // for saveFile
#include "cmdlfhitag.h" // annotate hitag
#include "cmdlfhitaghts.h" // annotate hitags
#include "cmdlfhitagu.h" // annotate hitagu
#include "pm3_cmd.h" // tracelog_hdr_t
#include "cliparser.h" // args..
@ -585,8 +587,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case PROTO_HITAG1:
case PROTO_HITAGS:
crcStatus = hitag1_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8));
case PROTO_CRYPTORF:
break;
case PROTO_HITAGU:
crcStatus = hitagu_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8));
break;
case PROTO_HITAG2:
case PROTO_CRYPTORF:
default:
break;
}
@ -624,6 +630,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
&& protocol != PROTO_HITAG1
&& protocol != PROTO_HITAG2
&& protocol != PROTO_HITAGS
&& protocol != PROTO_HITAGU
&& protocol != THINFILM
&& protocol != FELICA
&& protocol != LTO
@ -646,7 +653,7 @@ 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))) {
} else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS) || (protocol == PROTO_HITAGU))) {
if (j == 0) {
@ -800,7 +807,10 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse, mfDicKeys, mfDicKeysCount, false);
break;
case PROTO_HITAGS:
annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
annotateHitagS(explanation, sizeof(explanation), frame, (data_len * 8) - ((8 - parityBytes[0]) % 8), hdr->isResponse);
break;
case PROTO_HITAGU:
annotateHitagU(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
break;
case ICLASS:
annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
@ -1102,7 +1112,7 @@ static int download_trace(void) {
// Query for the size of the trace, downloading PM3_CMD_DATA_SIZE
PacketResponseNG resp;
if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 4000, true)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
free(gs_trace);
gs_trace = NULL;
return PM3_ETIMEOUT;
@ -1310,9 +1320,10 @@ int CmdTraceList(const char *Cmd) {
"trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") "\n\n"
"trace list -t des -> interpret as " _YELLOW_("MIFARE DESFire") "\n"
"trace list -t felica -> interpret as " _YELLOW_("ISO18092 / FeliCa") "\n"
"trace list -t hitag1 -> interpret as " _YELLOW_("Hitag 1") "\n"
"trace list -t hitag2 -> interpret as " _YELLOW_("Hitag 2") "\n"
"trace list -t hitags -> interpret as " _YELLOW_("Hitag S") "\n"
"trace list -t ht1 -> interpret as " _YELLOW_("Hitag 1") "\n"
"trace list -t ht2 -> interpret as " _YELLOW_("Hitag 2") "\n"
"trace list -t hts -> interpret as " _YELLOW_("Hitag S") "\n"
"trace list -t htu -> interpret as " _YELLOW_("Hitag µ") "\n"
"trace list -t iclass -> interpret as " _YELLOW_("iCLASS") "\n"
"trace list -t legic -> interpret as " _YELLOW_("LEGIC") "\n"
"trace list -t lto -> interpret as " _YELLOW_("LTO-CM") "\n"
@ -1377,9 +1388,10 @@ int CmdTraceList(const char *Cmd) {
else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF;
else if (strcmp(type, "des") == 0) protocol = MFDES;
else if (strcmp(type, "felica") == 0) protocol = FELICA;
else if (strcmp(type, "hitag1") == 0) protocol = PROTO_HITAG1;
else if (strcmp(type, "hitag2") == 0) protocol = PROTO_HITAG2;
else if (strcmp(type, "hitags") == 0) protocol = PROTO_HITAGS;
else if (strcmp(type, "ht1") == 0) protocol = PROTO_HITAG1;
else if (strcmp(type, "ht2") == 0) protocol = PROTO_HITAG2;
else if (strcmp(type, "hts") == 0) protocol = PROTO_HITAGS;
else if (strcmp(type, "htu") == 0) protocol = PROTO_HITAGU;
else if (strcmp(type, "iclass") == 0) protocol = ICLASS;
else if (strcmp(type, "legic") == 0) protocol = LEGIC;
else if (strcmp(type, "lto") == 0) protocol = LTO;
@ -1469,8 +1481,8 @@ 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) {
PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S") " - Timings in ETU (8us)");
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) {
PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S / Hitag µ") " - Timings in ETU (8us)");
}
if (protocol == PROTO_FMCOS20) {
@ -1551,7 +1563,7 @@ int CmdTraceList(const char *Cmd) {
}
// reset hitag state machine
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) {
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) {
annotateHitag2_init();
}

View file

@ -94,7 +94,7 @@ typedef enum {
const char *nxp_cluster_to_text(uint8_t cluster) {
switch (cluster) {
case CL_ADMIN:
return "card administration";
return "Card administration";
case CL_MISC1:
case CL_MISC2:
case CL_MISC3:
@ -102,40 +102,40 @@ const char *nxp_cluster_to_text(uint8_t cluster) {
case CL_MISC5:
case CL_MISC6:
case CL_MISC7:
return "miscellaneous applications";
return "Miscellaneous applications";
case CL_AIRLINES:
return "airlines";
return "Airlines";
case CL_FERRY:
return "ferry traffic";
return "Ferry traffic";
case CL_RAIL:
return "railway services";
return "Railway services";
case CL_MISC:
return "miscellaneous applications";
return "Miscellaneous applications";
case CL_TRANSPORT:
return "transport";
return "Transport";
case CL_SECURITY:
return "security solutions";
return "Security solutions";
case CL_CITYTRAFFIC:
return "city traffic";
return "City traffic";
case CL_CZECH_RAIL:
return "Czech Railways";
case CL_BUS:
return "bus services";
return "Bus services";
case CL_MMT:
return "multi modal transit";
return "Multi modal transit";
case CL_TAXI:
return "taxi";
return "Taxi";
case CL_TOLL:
return "road toll";
return "Road toll";
case CL_GENERIC_TRANS:
return "generic transport";
return "Generic transport";
case CL_COMPANY_SERVICES:
return "company services";
return "Company services";
case CL_CITYCARD:
return "city card services";
return "City card services";
case CL_ACCESS_CONTROL_1:
case CL_ACCESS_CONTROL_2:
return "access control & security";
return "Access control & security";
case CL_VIGIK:
return "VIGIK";
case CL_NED_DEFENCE:
@ -145,63 +145,63 @@ const char *nxp_cluster_to_text(uint8_t cluster) {
case CL_EU:
return "European Union Institutions";
case CL_SKI_TICKET:
return "ski ticketing";
return "Ski ticketing";
case CL_SOAA:
return "SOAA standard for offline access standard";
case CL_ACCESS2:
return "access control & security";
return "Access control & security";
case CL_FOOD:
return "food";
return "Food";
case CL_NONFOOD:
return "non-food trade";
return "Non-food trade";
case CL_HOTEL:
return "hotel";
return "Hotel";
case CL_LOYALTY:
return "loyalty";
return "Loyalty";
case CL_AIRPORT:
return "airport services";
return "Airport services";
case CL_CAR_RENTAL:
return "car rental";
return "Car rental";
case CL_NED_GOV:
return "Dutch government";
case CL_ADMIN2:
return "administration services";
return "Administration services";
case CL_PURSE:
return "electronic purse";
return "Electronic purse";
case CL_TV:
return "television";
return "Television";
case CL_CRUISESHIP:
return "cruise ship";
return "Cruise ship";
case CL_IOPTA:
return "IOPTA";
case CL_METERING:
return "metering";
return "Metering";
case CL_TELEPHONE:
return "telephone";
return "Telephone";
case CL_HEALTH:
return "health services";
return "Health services";
case CL_WAREHOUSE:
return "warehouse";
return "Warehouse";
case CL_BANKING:
return "banking";
return "Banking";
case CL_ENTERTAIN:
return "entertainment & sports";
return "Entertainment & sports";
case CL_PARKING:
return "car parking";
return "Car parking";
case CL_FLEET:
return "fleet management";
return "Fleet management";
case CL_FUEL:
return "fuel, gasoline";
return "Fuel, gasoline";
case CL_INFO:
return "info services";
return "Info services";
case CL_PRESS:
return "press";
return "Press";
case CL_NFC:
return "NFC Forum";
case CL_COMPUTER:
return "computer";
return "Computer";
case CL_MAIL:
return "mail";
return "Mail";
case CL_AMISC:
case CL_AMISC1:
case CL_AMISC2:
@ -210,11 +210,11 @@ const char *nxp_cluster_to_text(uint8_t cluster) {
case CL_AMISC5:
case CL_AMISC6:
case CL_AMISC7:
return "miscellaneous applications";
return "Miscellaneous applications";
default:
break;
}
return "reserved";
return "Reserved";
}
static json_t *df_known_aids = NULL;

View file

@ -3073,7 +3073,7 @@ int DesfireGetCardUID(DesfireContext_t *ctx) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
PrintAndLogEx(WARNING, "timeout while waiting for reply");
return PM3_ETIMEOUT;
}

View file

@ -430,6 +430,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "hf mfu otptear" },
{ 0, "hf mfu cauth" },
{ 0, "hf mfu setpwd" },
{ 0, "hf mfu aesauth" },
{ 0, "hf mfu dump" },
{ 0, "hf mfu incr" },
{ 0, "hf mfu info" },
@ -690,6 +691,13 @@ const static vocabulary_t vocabulary[] = {
{ 0, "lf hitag hts restore" },
{ 0, "lf hitag hts wrbl" },
{ 0, "lf hitag hts sim" },
{ 1, "lf hitag htu help" },
{ 1, "lf hitag htu list" },
{ 0, "lf hitag htu reader" },
{ 0, "lf hitag htu rdbl" },
{ 0, "lf hitag htu dump" },
{ 0, "lf hitag htu wrbl" },
{ 0, "lf hitag htu sim" },
{ 1, "lf idteck help" },
{ 1, "lf idteck demod" },
{ 0, "lf idteck reader" },

View file

@ -186,6 +186,7 @@ static void fill_grabber(const char *string) {
g_grabbed_output.ptr = tmp;
g_grabbed_output.size += MAX_PRINT_BUFFER;
}
int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
if (len < 0 || len > MAX_PRINT_BUFFER) {
// We leave current g_grabbed_output_len untouched
@ -196,24 +197,34 @@ static void fill_grabber(const char *string) {
}
void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) {
char buff[2000] = "Options:\n";
char format[2000] = "";
size_t counts[2] = {0, 0};
for (size_t i = 0; i < size; i++)
for (size_t j = 0 ; j < 2 ; j++)
for (size_t i = 0; i < size; i++) {
for (size_t j = 0 ; j < 2 ; j++) {
if (counts[j] < strlen(str[i][j])) {
counts[j] = strlen(str[i][j]);
}
}
}
for (size_t i = 0; i < size; i++) {
for (size_t j = 0; j < 2; j++) {
if (j == 0)
if (j == 0) {
snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
else
} else {
snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]);
}
snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]);
}
if (i < size - 1)
if (i < size - 1) {
strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1);
}
}
PrintAndLogEx(NORMAL, "%s", buff);
}
@ -223,12 +234,14 @@ static uint8_t PrintAndLogEx_spinidx = 0;
void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
// skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
if (g_debugMode == 0 && level == DEBUG)
if (g_debugMode == 0 && level == DEBUG) {
return;
}
// skip HINT messages if client has hints turned off i.e. 'HINT 0'
if (g_session.show_hints == false && level == HINT)
if (g_session.show_hints == false && level == HINT) {
return;
}
char prefix[40] = {0};
char buffer[MAX_PRINT_BUFFER] = {0};
@ -242,17 +255,19 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
};
switch (level) {
case ERR:
if (g_session.emoji_mode == EMO_EMOJI)
if (g_session.emoji_mode == EMO_EMOJI) {
strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
else
} else {
strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
}
stream = stderr;
break;
case FAILED:
if (g_session.emoji_mode == EMO_EMOJI)
if (g_session.emoji_mode == EMO_EMOJI) {
strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
else
} else {
strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
}
break;
case DEBUG:
strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
@ -264,10 +279,11 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
break;
case WARNING:
if (g_session.emoji_mode == EMO_EMOJI)
if (g_session.emoji_mode == EMO_EMOJI) {
strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
else
} else {
strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
}
break;
case INFO:
strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1);
@ -276,13 +292,15 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
if (g_session.emoji_mode == EMO_EMOJI) {
strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
PrintAndLogEx_spinidx++;
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji))
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji)) {
PrintAndLogEx_spinidx = 0;
}
} else {
strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
PrintAndLogEx_spinidx++;
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner))
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner)) {
PrintAndLogEx_spinidx = 0;
}
}
break;
case NORMAL:
@ -306,8 +324,9 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
const char delim[2] = "\n";
// line starts with newline
if (buffer[0] == '\n')
if (buffer[0] == '\n') {
fPrintAndLog(stream, "");
}
token = strtok_r(buffer, delim, &tmp_ptr);
@ -315,16 +334,21 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
size_t size = strlen(buffer2);
if (strlen(token))
if (strlen(token)) {
snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
else
} else {
snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
}
token = strtok_r(NULL, delim, &tmp_ptr);
}
fPrintAndLog(stream, "%s", buffer2);
} else {
snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
if (level == INPLACE) {
// ignore INPLACE if rest of output is grabbed
if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
@ -354,6 +378,7 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
if (logging && g_session.incognito) {
logging = 0;
}
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
char *my_logfile_path = NULL;
char filename[40];
@ -361,11 +386,15 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
time_t now = time(NULL);
timenow = gmtime(&now);
strftime(filename, sizeof(filename), PROXLOG, timenow);
if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
printf(_YELLOW_("[-]") " Logging disabled!\n");
my_logfile_path = NULL;
logging = 0;
} else {
logfile = fopen(my_logfile_path, "a");
if (logfile == NULL) {
printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path);
@ -411,13 +440,16 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
linefeed = false;
buffer[strlen(buffer) - 1] = 0;
}
bool filter_ansi = !g_session.supports_colors;
memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi);
if (g_printAndLog & PRINTANDLOG_PRINT) {
if ((g_printAndLog & PRINTANDLOG_PRINT) == PRINTANDLOG_PRINT) {
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode);
fprintf(stream, "%s", buffer3);
if (linefeed)
if (linefeed) {
fprintf(stream, "\n");
}
}
#ifdef RL_STATE_READCMD
@ -433,33 +465,44 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
(g_printAndLog & PRINTANDLOG_GRAB)) {
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
if (filter_ansi == false) {
memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
}
}
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
if (filter_ansi) {
fprintf(logfile, "%s", buffer3);
} else {
fprintf(logfile, "%s", buffer);
}
if (linefeed)
if (linefeed) {
fprintf(logfile, "\n");
}
fflush(logfile);
}
if (g_printAndLog & PRINTANDLOG_GRAB) {
if (filter_ansi) {
fill_grabber(buffer3);
} else {
fill_grabber(buffer);
}
if (linefeed)
if (linefeed) {
fill_grabber("\n");
}
}
if (flushAfterWrite)
if (flushAfterWrite) {
fflush(stdout);
}
//release lock
pthread_mutex_unlock(&g_print_lock);
@ -478,9 +521,10 @@ void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n) {
uint8_t *rsrc = (uint8_t *)src;
uint16_t si = 0;
for (size_t i = 0; i < n; i++) {
if ((rsrc[i] == '\001') || (rsrc[i] == '\002'))
if ((rsrc[i] == '\001') || (rsrc[i] == '\002')) {
// skip readline special markers
continue;
}
rdest[si++] = rsrc[i];
}
}

View file

@ -433,11 +433,21 @@ void xor(uint8_t *dest, const uint8_t *src, size_t n) {
}
}
void lsl(uint8_t *data, size_t len) {
for (size_t n = 0; n < len - 1; n++) {
data[n] = (data[n] << 1) | (data[n + 1] >> 7);
// left shift an array of length one bit
void lsl(uint8_t *d, size_t n) {
for (size_t i = 0; i < n - 1; i++) {
d[i] = (d[i] << 1) | (d[i + 1] >> 7);
}
d[n - 1] <<= 1;
}
void lslx(uint8_t *d, size_t n, uint8_t shifts) {
for (uint8_t i = 0; i < shifts; i++) {
for (size_t j = 0; j < n - 1; j++) {
d[j] = (d[j] << 1) | (d[j + 1] >> 7);
}
d[n - 1] <<= 1;
}
data[len - 1] <<= 1;
}
@ -555,7 +565,12 @@ void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len) {
}
// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible.
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits) {
/**
* @brief Concatenate bits from src to dest, bitstream is stored MSB first
* which means that the dest_offset=0 is the MSB of the dest[0]
*
*/
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb) {
int i, end, step;
// overlap
@ -571,8 +586,8 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of
for (; i != end; i += step) {
// equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i]
CLEAR_BIT(dest, dest_offset + i);
if (TEST_BIT(src, src_offset + i)) SET_BIT(dest, dest_offset + i);
CLEAR_BIT_MSB(dest, dest_offset + i);
if (src_lsb ? TEST_BIT_LSB(src, src_offset + i) : TEST_BIT_MSB(src, src_offset + i)) SET_BIT_MSB(dest, dest_offset + i);
}
return dest_offset + nbits;

View file

@ -132,7 +132,9 @@ void rol(uint8_t *data, const size_t n);
void ror(uint8_t *data, const size_t n);
void xor(uint8_t *dest, const uint8_t *src, size_t n);
void lsl(uint8_t *data, size_t len);
void lsl(uint8_t *d, size_t n);
void lslx(uint8_t *d, size_t n, uint8_t shifts);
uint32_t le24toh(const uint8_t data[3]);
void htole24(uint32_t val, uint8_t data[3]);
@ -150,7 +152,7 @@ bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n);
void reverse_arraybytes(uint8_t *arr, size_t len);
void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len);
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits);
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb);
int char2int(char c);
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
#endif

View file

@ -142,12 +142,17 @@ uint16_t update_crc16(uint16_t crc, uint8_t c) {
}
// two ways. msb or lsb loop.
uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) {
if (length == 0)
uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) {
if (bitlength == 0)
return (~remainder);
for (uint32_t i = 0; i < length; ++i) {
uint8_t c = d[i];
uint8_t offset = 8 - (bitlength % 8);
// front padding with 0s won't change the CRC result
uint8_t prebits = 0;
for (uint32_t i = 0; i < (bitlength + 7) / 8; ++i) {
uint8_t c = prebits | d[i] >> offset;
prebits = d[i] << (8 - offset);
if (refin) c = reflect8(c);
// xor in at msb

View file

@ -47,7 +47,7 @@ typedef enum {
uint16_t update_crc16_ex(uint16_t crc, uint8_t c, uint16_t polynomial);
uint16_t update_crc16(uint16_t crc, uint8_t c);
uint16_t Crc16(uint8_t const *d, size_t length, uint16_t remainder, uint16_t polynomial, bool refin, bool refout);
uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout);
uint16_t Crc16ex(CrcType_t ct, const uint8_t *d, size_t n);
void compute_crc(CrcType_t ct, const uint8_t *d, size_t n, uint8_t *first, uint8_t *second);

View file

@ -5047,7 +5047,7 @@
"-v, --verbose verbose output",
"-f, --file <fn> Specify a filename for dump file",
"--emu from emulator memory",
"--start <dec> index of block to start writing (default 0)",
"--start <dec> index of block to start writing (def 0)",
"--end <dec> index of block to end writing (default last block)"
],
"usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p <hex>] [-f <fn>] [--emu] [--start <dec>] [--end <dec>]"
@ -7062,6 +7062,22 @@
],
"usage": "hf mfp wrp [-hv] -a <hex> [-d <hex>]"
},
"hf mfu aesauth": {
"command": "hf mfu aesauth",
"description": "Tests AES key on Mifare Ultralight AES tags. If no key is specified, null key will be tried. Key index 0: DataProtKey (default) Key index 1: UIDRetrKey Key index 2: OriginalityKey",
"notes": [
"hf mfu aesauth",
"hf mfu aesauth --key <32 bytes> --index <0..2>"
],
"offline": false,
"options": [
"-h, --help This help",
"--key <hex> AES key (32 hex bytes)",
"-i, --index <0..2> Key index, default: 0",
"-k Keep field on (only if a key is provided)"
],
"usage": "hf mfu aesauth [-hk] [--key <hex>] [-i <0..2>]"
},
"hf mfu amiibo": {
"command": "hf mfu amiibo",
"description": "Tries to read all memory from amiibo tag and decrypt it",
@ -8825,12 +8841,13 @@
},
"lf em 410x clone": {
"command": "lf em 410x clone",
"description": "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469 or Hitag S/8211/8268/8310 tag.",
"description": "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469, Hitag S/8211/8268/8310 or Hitag \u00b5/8265 tag.",
"notes": [
"lf em 410x clone --id 0F0368568B -> encode for T55x7 tag",
"lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag",
"lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469",
"lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310"
"lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310",
"lf em 410x clone --id 0F0368568B --htu -> encode for Hitag \u00b5/8265 tag"
],
"offline": false,
"options": [
@ -8840,9 +8857,10 @@
"--q5 optional - specify writing to Q5/T5555 tag",
"--em optional - specify writing to EM4305/4469 tag",
"--hts optional - specify writing to Hitag S/8211/8268/8310 tag",
"--htu optional - specify writing to Hitag \u00b5/8265 tag",
"--electra optional - add Electra blocks to tag"
],
"usage": "lf em 410x clone [-h] [--clk <dec>] --id <hex> [--q5] [--em] [--hts] [--electra]"
"usage": "lf em 410x clone [-h] [--clk <dec>] --id <hex> [--q5] [--em] [--hts] [--htu] [--electra]"
},
"lf em 410x reader": {
"command": "lf em 410x reader",
@ -9959,7 +9977,7 @@
},
"lf hitag help": {
"command": "lf hitag help",
"description": "help This help list List Hitag trace history hts { Hitag S/8211 operations } test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t hitag2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"description": "help This help list List Hitag trace history hts { Hitag S/8211 operations } htu { Hitag \u00b5/8265 operations } test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t ht2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [
"lf hitag list --frame -> show frame delay times",
"lf hitag list -1 -> use trace buffer"
@ -10003,7 +10021,7 @@
},
"lf hitag hts help": {
"command": "lf hitag hts help",
"description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hitags` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hts` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [
"lf hitag hts list --frame -> show frame delay times",
"lf hitag hts list -1 -> use trace buffer"
@ -10086,14 +10104,16 @@
"description": "Simulate Hitag S transponder You need to `lf hitag hts eload` first",
"notes": [
"lf hitag hts sim",
"lf hitag hts sim --82xx"
"lf hitag hts sim --82xx",
"lf hitag hts sim -t 30 -> set threshold to 30"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx simulate 8268/8310"
"-8, --82xx simulate 8268/8310",
"-t, --threshold <dec> set edge detect threshold (def: 127)"
],
"usage": "lf hitag hts sim [-h8]"
"usage": "lf hitag hts sim [-h8] [-t <dec>]"
},
"lf hitag hts wrbl": {
"command": "lf hitag hts wrbl",
@ -10119,6 +10139,110 @@
],
"usage": "lf hitag hts wrbl [-h8] [--nrar <hex>] [--crypto] [-k <hex>] [-m <dec>] -p <dec> -d <hex>"
},
"lf hitag htu dump": {
"command": "lf hitag htu dump",
"description": "Read all Hitag \u00b5 memory and save to file 82xx password mode: - default password 00000000",
"notes": [
"lf hitag htu dump --82xx -> use def pwd",
"lf hitag htu dump --82xx -k 9AC4999C -> pwd mode"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx 82xx mode",
"-k, --key <hex> pwd, 4 hex bytes",
"-f, --file <fn> specify file name",
"--ns no save to file"
],
"usage": "lf hitag htu dump [-h8] [-k <hex>] [-f <fn>] [--ns]"
},
"lf hitag htu help": {
"command": "lf hitag htu help",
"description": "help This help list List Hitag \u00b5 trace history --------------------------------------------------------------------------------------- lf hitag htu list available offline: yes Alias of `trace list -t htu` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
"notes": [
"lf hitag htu list --frame -> show frame delay times",
"lf hitag htu list -1 -> use trace buffer"
],
"offline": true,
"options": [
"-h, --help This help",
"-1, --buffer use data from trace buffer",
"--frame show frame delay times",
"-c mark CRC bytes",
"-r show relative times (gap and duration)",
"-u display times in microseconds instead of clock cycles",
"-x show hexdump to convert to pcap(ng)",
"or to import into Wireshark using encapsulation type \"ISO 14443\"",
"-f, --file <fn> filename of dictionary"
],
"usage": "lf hitag htu list [-h1crux] [--frame] [-f <fn>]"
},
"lf hitag htu rdbl": {
"command": "lf hitag htu rdbl",
"description": "Read Hitag \u00b5 memory. 82xx password mode: - default password 00000000",
"notes": [
"lf hitag htu rdbl -p 1 -> Hitag \u00b5, plain mode",
"lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass",
"lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx 82xx mode",
"-k, --key <hex> pwd, 4 hex bytes",
"-p, --page <dec> block address to read from (def: 0)",
"-c, --count <dec> how many blocks to read. '0' reads all blocks (def: 1)"
],
"usage": "lf hitag htu rdbl [-h8] [-k <hex>] [-p <dec>] [-c <dec>]"
},
"lf hitag htu reader": {
"command": "lf hitag htu reader",
"description": "Act as a Hitag \u00b5 reader. Look for Hitag \u00b5 tags until Enter or the pm3 button is pressed",
"notes": [
"lf hitag htu reader",
"lf hitag htu reader -@ -> Continuous mode"
],
"offline": false,
"options": [
"-h, --help This help",
"-@ continuous reader mode"
],
"usage": "lf hitag htu reader [-h@]"
},
"lf hitag htu sim": {
"command": "lf hitag htu sim",
"description": "Simulate Hitag \u00b5 transponder You need to `lf hitag htu eload` first",
"notes": [
"lf hitag htu sim",
"lf hitag htu sim --82xx",
"lf hitag htu sim -t 30 -> set threshold to 30"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx simulate 82xx",
"-t, --threshold <dec> set edge detect threshold (def: 127)"
],
"usage": "lf hitag htu sim [-h8] [-t <dec>]"
},
"lf hitag htu wrbl": {
"command": "lf hitag htu wrbl",
"description": "Write a block in Hitag \u00b5 memory. 82xx password mode: - default password 00000000",
"notes": [
"lf hitag htu wrbl -p 6 -d 01020304 -> Hitag \u00b5, plain mode",
"lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd",
"lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode"
],
"offline": false,
"options": [
"-h, --help This help",
"-8, --82xx 82xx mode",
"-k, --key <hex> pwd, 4 hex bytes",
"-p, --page <dec> block address to write to",
"-d, --data <hex> data, 4 hex bytes"
],
"usage": "lf hitag htu wrbl [-h8] [-k <hex>] -p <dec> -d <hex>"
},
"lf hitag info": {
"command": "lf hitag info",
"description": "Hitag 2 tag information",
@ -10992,8 +11116,8 @@
"-r, --reset Reset configuration to default values",
"-p, --pwd <hex> Password, 7bytes, LSB-order",
"-d, --delay <dec> Tag initialization delay (in us)",
"--lw <dec> offset, low pulses width (in us)",
"--lp <dec> offset, low pulses position (in us)"
"--lw <dec> offset, low pulses width (in us), optional!",
"--lp <dec> offset, low pulses position (in us), optional!"
],
"usage": "lf pcf7931 config [-hr] [-p <hex>] [-d <dec>] [--lw <dec>] [--lp <dec>]"
},
@ -13012,9 +13136,10 @@
"",
"trace list -t des -> interpret as MIFARE DESFire",
"trace list -t felica -> interpret as ISO18092 / FeliCa",
"trace list -t hitag1 -> interpret as Hitag 1",
"trace list -t hitag2 -> interpret as Hitag 2",
"trace list -t hitags -> interpret as Hitag S",
"trace list -t ht1 -> interpret as Hitag 1",
"trace list -t ht2 -> interpret as Hitag 2",
"trace list -t hts -> interpret as Hitag S",
"trace list -t htu -> interpret as Hitag \u00b5",
"trace list -t iclass -> interpret as iCLASS",
"trace list -t legic -> interpret as LEGIC",
"trace list -t lto -> interpret as LTO-CM",
@ -13230,8 +13355,8 @@
}
},
"metadata": {
"commands_extracted": 760,
"commands_extracted": 767,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2025-03-12T15:46:33"
"extracted_on": "2025-03-19T20:08:46"
}
}

View file

@ -617,6 +617,7 @@ Check column "offline" for their availability.
|`hf mfu otptear `|N |`Tear-off test on OTP bits`
|`hf mfu cauth `|N |`Ultralight-C - Authentication`
|`hf mfu setpwd `|N |`Ultralight-C - Set 3DES key`
|`hf mfu aesauth `|N |`Ultralight-AES - Authentication`
|`hf mfu dump `|N |`Dump MIFARE Ultralight family tag to binary file`
|`hf mfu incr `|N |`Increments Ev1/NTAG counter`
|`hf mfu info `|N |`Tag information`
@ -1096,6 +1097,21 @@ Check column "offline" for their availability.
|`lf hitag hts sim `|N |`Simulate Hitag S transponder`
### lf hitag htu
{ Hitag µ/8265 operations }
|command |offline |description
|------- |------- |-----------
|`lf hitag htu help `|Y |`This help`
|`lf hitag htu list `|Y |`List Hitag µ trace history`
|`lf hitag htu reader `|N |`Act like a Hitag µ reader`
|`lf hitag htu rdbl `|N |`Read Hitag µ block`
|`lf hitag htu dump `|N |`Dump Hitag µ blocks to a file`
|`lf hitag htu wrbl `|N |`Write Hitag µ block`
|`lf hitag htu sim `|N |`Simulate Hitag µ transponder`
### lf idteck
{ Idteck RFIDs... }

View file

@ -155,14 +155,20 @@ This is the cheapest and most common ID82xx chip available. It is usually sold a
#### Characteristics
* Chip is likely a cut down version of Hitag μ (micro) clone
* Chip is likely a cut down version of Hitag µ (micro) clone
* UID `00 00 00 00 00 00`
* Password protection (4b), usually "00000000"(default) or "9AC4999C"(FURUI)
* CON0
* bit 0-1 -> data rate 00... 2kbit/s 01... 4kbit/s 10... 8kbit/s 11... 2kbit/s
* bit 2 when set, fixed to MC 2kbit/s
* bit 3-6 reversed? all blocks always read without password and write with password
* bit 7 -> enable TTF
* Config block 0xFF
* Byte0
* bit 0-1 : Data Rate. 00 -> 2kbit/s, 01 -> 4kbit/s, 10 -> 8kbit/s, 11 -> 2kbit/s
* bit 2 : 1 -> fixed to 2kbit/s
* bit 3 : 0 -> Manchester, 1 -> Bi-phase
* bit 4 : TTF blocks. 0 -> "Block 0, Block 1, Block 2, Block 3", 1 -> "Block 0, Block 1"
* bit 5-6 : reversed? all blocks always read without password and write with password
* bit 7 : 1 -> enable TTF
* Byte1 only bit 0 changable
* Byte2 fixed 0x00
* Byte3 only higher nibble changable
* Currently unimplemented in proxmark3 client
* Other names:
* ID8210 (CN)

View file

@ -0,0 +1,147 @@
# arbitrary lf em commands
Goals:
1. Improved logging of `lf em` commands and responses
2. Greater certainty in command sequences
3. Easier testing of new commands
## Methodology
This is documenting the actual commands used by existing code. Phases include:
* Document the existing command sequences
* Document the existing logging APIs
* Define small set of timing-sensitive functions as abstractions
* Implement the abstractions
* Add logging
The goal is to improve logging and debugging, and allow easily testing new LF commands.
## EM4x70 (aka ID48, aka Megamos)
Only six command sequences currently used:
#define EM4X70_COMMAND_ID 0x01
#define EM4X70_COMMAND_UM1 0x02
#define EM4X70_COMMAND_AUTH 0x03
#define EM4X70_COMMAND_PIN 0x04
#define EM4X70_COMMAND_WRITE 0x05
#define EM4X70_COMMAND_UM2 0x07
### ID Command
Wait for `LIW` (listen window), and start transmission at next `LIW`:
source | bits | comment
----------|---------|---------
tag | LIW | listen window sync
reader | `0b00` | RM
reader | `0b001` | CMD
reader | `0b1` | command parity bit
tag | HEADER | HEADER (0b1111'1111'1111'0000)
tag | 32-bits | ID (D31..D0)
tag | LIW | tag reverts to be ready for next command
### UM1 Command
source | bits | comment
----------|---------|---------
tag | LIW | listen window
reader | `0b00` | RM
reader | `0b010` | CMD
reader | `0b1` | command parity bit
tag | 16-bits | HEADER
tag | 32-bits | UM1 data
tag | LIW | tag reverts to be ready for next command
### UM2 Command
source | bits | comment
----------|---------|---------
tag | LIW | listen window
reader | `0b00` | RM
reader | `0b111` | CMD
reader | `0b1` | command parity bit
tag | 16-bits | HEADER
tag | 64-bits | UM2 data
tag | LIW | tag reverts to be ready for next command
### Auth Command
source | bits | comment
----------|---------|---------
tag | LIW | listen window
reader | `0b00` | RM
reader | `0b011` | CMD
reader | `0b0` | command parity bit
reader | 56-bits | RN
reader | 7-bits | Tdiv == 0b0000000 (always zero)
reader | 28-bits | f(RN)
tag | 16-bits | HEADER
tag | 20-bits | g(RN)
tag | LIW | tag reverts to be ready for next command
### Write Word
source | bits | comment
----------|---------|---------
tag | LIW | listen window
reader | `0b00` | RM
reader | `0b101` | CMD
reader | `0b0` | command parity bit
reader | 4-bits | address/block to write
reader | 1-bit | address/block parity bit
reader | 25-bits | 5x5 data w/ row and column parity
tag | ACK | Wait (TWA) for ACK ... time to wait before searching for ACK
tag | ACK | Wait (WEE) for ACK ... time to wait before searching for ACK
tag | LIW | tag reverts to be ready for next command
### PIN Command
source | bits | comment
----------|---------|---------
tag | LIW | listen window
reader | `0b00` | RM
reader | `0b100` | CMD
reader | `0b1` | command parity bit
reader | 32-bits | ID of the tag
reader | 32-bits | PIN
tag | ACK | Wait (TWALB) for ACK ... time to wait before searching for ACK
tag | HEADER | DELAYED (TWEE) header ... time to wait before searching for header
tag | 32-bits | ID of the tag
tag | LIW | tag reverts to be ready for next command
### Abstraction required
Possible items to abstract:
* bits to send: quantity of bits to be sent + storage containing those bits
* bits to receive: expected bits to receive + storage to receive those bits
* LIW: special-case handling to synchronize next command
* ACK: special-case handling to wait for ACK
* HEADER: special-case handling to wait for HEADER
* DELAY: ticks to delay before processing next item
Special handling required for:
* `HEADER` --> 12-bits of zero, 4-bits of one. Consider a timeout: if tag disappears, no pulse found, while sometimes expect long time before HEADER appears (as in SEND_PIN). Read of header may miss the first few bits during transition, so need to special-case handling of this detection.
* `LIW` --> Timing-sensitive, syncs reader with tag ... reader must send during 32 period where chip's modulator is ON.
* `ACK` --> This is currently a time-to-delay.
Should this be a maximum time to wait for ACK?
Currently, could sit waiting for long time
if no tag present, as `check_ack()` has no timeout.
```C
WaitTicks(EM4X70_T_TAG_TWA);
if (check_ack())
WaitTicks(EM4X70_T_TAG_WEE);
if (check_ack())
return PM3_SUCCESS;
```

File diff suppressed because it is too large Load diff

View file

@ -202,10 +202,15 @@ extern bool g_tearoff_enabled;
#endif
// bit stream operations
#define TEST_BIT(data, i) (*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1
#define SET_BIT(data, i) *((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8)))
#define CLEAR_BIT(data, i) *((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8)))
#define FLIP_BIT(data, i) *((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8)))
#define TEST_BIT_MSB(data, i) ((*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1)
#define SET_BIT_MSB(data, i) (*((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8))))
#define CLEAR_BIT_MSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8))))
#define FLIP_BIT_MSB(data, i) (*((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8))))
#define TEST_BIT_LSB(data, i) ((*((data) + ((i) / 8)) >> ((i) % 8)) & 1)
#define SET_BIT_LSB(data, i) (*((data) + ((i) / 8)) |= (1 << ((i) % 8)))
#define CLEAR_BIT_LSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << ((i) % 8)))
#define FLIP_BIT_LSB(data, i) (*((data) + ((i) / 8)) ^= (1 << ((i) % 8)))
// time for decompressing and loading the image to the FPGA
#define FPGA_LOAD_WAIT_TIME (1500)

View file

@ -21,6 +21,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#define EM4X70_NUM_BLOCKS 16
@ -28,24 +29,36 @@
#define EM4X70_PIN_WORD_LOWER 10
#define EM4X70_PIN_WORD_UPPER 11
/// @brief Command transport structure for EM4x70 commands.
/// @details
/// This structure is used to transport data from the PC
/// to the proxmark3, and contain all data needed for
/// a given `lf em 4x70 ...` command to be processed
/// on the proxmark3.
/// The only requirement is that this structure remain
/// smaller than the NG buffer size (256 bytes).
typedef struct {
// ISSUE: `bool` type does not have a standard-defined size.
// therefore, compatibility between architectures /
// compilers is not guaranteed.
// ISSUE: C99 has no _Static_assert() ... was added in C11
// TODO: add _Static_assert(sizeof(bool)==1);
// TODO: add _Static_assert(sizeof(em4x70_data_t)==36);
bool parity;
// Used for writing address
uint8_t address;
// ISSUE: Presumes target is little-endian
// BUGBUG: Non-portable ... presumes stored in little-endian form!
uint16_t word;
// PIN to unlock
// BUGBUG: Non-portable ... presumes stored in little-endian form!
uint32_t pin;
// Used for authentication
//
// IoT safe subset of C++ would be helpful here,
// to support variable-bit-length integer types
// as integral integer types.
//
// Even C23 would work for this (GCC14+, Clang15+):
// _BitInt(56) rnd;
// _BitInt(28) frnd;
// _BitInt(20) grnd;
uint8_t frnd[4];
uint8_t grnd[3];
uint8_t rnd[7];
@ -54,9 +67,20 @@ typedef struct {
uint8_t crypt_key[12];
// used for bruteforce the partial key
// ISSUE: Presumes target is little-endian
// BUGBUG: Non-portable ... presumes stored in little-endian form!
uint16_t start_key;
} em4x70_data_t;
//_Static_assert(sizeof(em4x70_data_t) == 36);
// ISSUE: `bool` type does not have a standard-defined size.
// therefore, compatibility between architectures /
// compilers is not guaranteed.
// TODO: verify alignof(bool) == 1
//_Static_assert(sizeof(bool) == 1, "bool size mismatch");
typedef union {
uint8_t data[32];
} em4x70_tag_t;
//_Static_assert(sizeof(em4x70_tag_t) == 32, "em4x70_tag_t size mismatch");
#endif /* EM4X70_H__ */

View file

@ -13,7 +13,7 @@
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Hitag 2, Hitag S
// Hitag 2, Hitag S, Hitag µ
//-----------------------------------------------------------------------------
@ -39,30 +39,63 @@
#define HITAGS_UID_PADR 0
#define HITAGS_CONFIG_PADR 1
// Add Hitag µ specific definitions
#define HITAGU_UID_SIZE 6
#define HITAGU_BLOCK_SIZE HITAG_BLOCK_SIZE
#define HITAGU_MAX_BLOCKS 0x100
#define HITAGU_MAX_BYTE_SIZE (HITAGU_MAX_BLOCKS * HITAGU_BLOCK_SIZE)
#define HITAGU_CONFIG_PADR 0xFF
#define HITAGU_PASSWORD_PADR 0xFE
// Hitag µ IC Revision (ICR) values
#define HITAGU_ICR_STANDARD 0x10 // Standard Hitag µ
#define HITAGU_ICR_ADVANCED 0x20 // Hitag µ advanced
#define HITAGU_ICR_ADVANCED_PLUS 0x30 // Hitag µ advanced+
#define HITAGU_ICR_8265 0x80 // 8265
// Hitag µ memory sizes based on ICR
#define HITAGU_MAX_PAGE_STANDARD 0x04 // 4 blocks (0x00-0x03) for standard Hitag µ
#define HITAGU_MAX_PAGE_ADVANCED 0x10 // 16 blocks (0x00-0x0F) for Hitag µ advanced
#define HITAGU_MAX_PAGE_ADVANCED_PLUS 0x37 // 56 blocks (0x00-0x36) for Hitag µ advanced+
#define HITAGU_MAX_PAGE_8265 0x0F // 15 blocks (0x00-0x0E) for 8265
// need to see which limits these cards has
#define HITAG1_MAX_BYTE_SIZE 64
#define HITAGU_MAX_BYTE_SIZE 64
#define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE)
#define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE)
#define HITAG2_CONFIG_BLOCK 3
// Modulation types - used by shared code
typedef enum modulation {
AC2K = 0, // Amplitude modulation 2000 bits/s
AC4K, // Amplitude modulation 4000 bits/s
MC4K, // Manchester modulation 4000 bits/s
MC8K // Manchester modulation 8000 bits/s
} MOD;
typedef enum {
HTSF_PLAIN,
HTSF_82xx,
HTSF_CHALLENGE,
HTSF_KEY,
HTS_LAST_CMD = HTSF_KEY,
HTS_LAST_CMD = HTSF_KEY,
HT1F_PLAIN,
HT1F_AUTHENTICATE,
HT1_LAST_CMD = HT1F_AUTHENTICATE,
HT1_LAST_CMD = HT1F_AUTHENTICATE,
HT2F_PASSWORD,
HT2F_AUTHENTICATE,
HT2F_CRYPTO,
HT2F_TEST_AUTH_ATTEMPTS,
HT2F_UID_ONLY,
HT2_LAST_CMD = HT2F_UID_ONLY,
HT2_LAST_CMD = HT2F_UID_ONLY,
// Add Hitag µ commands
HTUF_PLAIN,
HTUF_82xx,
HTUF_PASSWORD,
HTU_LAST_CMD = HTUF_PASSWORD,
} PACKED hitag_function;
//---------------------------------------------------------
@ -125,7 +158,7 @@ struct hitagS_tag {
int max_page;
union {
uint8_t pages[64][4];
uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE];
struct {
// page 0
uint32_t uid_le;
@ -143,11 +176,62 @@ struct hitagS_tag {
} PACKED;
// Configuration byte 0 bit definitions
#define HITAGU_BYTE0_DATARATE_MASK 0x03 // Bits 0-1: data rate
#define HITAGU_BYTE0_DATARATE_2K 0x00 // 00 = 2kbit/s
#define HITAGU_BYTE0_DATARATE_4K 0x01 // 01 = 4kbit/s
#define HITAGU_BYTE0_DATARATE_8K 0x02 // 10 = 8kbit/s
#define HITAGU_BYTE0_ENCODING_MASK 0x04 // Bit 2: encoding
#define HITAGU_BYTE0_ENCODING_MANCHESTER 0x00 // 0 = Manchester
#define HITAGU_BYTE0_ENCODING_BIPHASE 0x01 // 1 = Biphase
// Hitag µ configuration structure
typedef struct {
// byte0
uint8_t datarate: 2;
uint8_t encoding: 1;
uint8_t pwdW0_127: 1;
uint8_t pwdW128_511: 1;
uint8_t pwdW512_max: 1;
uint8_t pwdRW512_max: 1;
} PACKED hitagu_config_t;
typedef struct {
// byte0
uint8_t datarate : 2; // 00 = 2kbit/s, 01 = 4kbit/s, 10 = 8kbit/s, 11 = 2kbit/s
uint8_t datarate_override : 1; // 0 = datarate, 1 = 2kbit/s
uint8_t encoding : 1; // 0 = Manchester, 1 = Biphase
uint8_t reserved : 1;
uint8_t ttf_mode : 2; // 00/10/11 = "Block 0, Block 1, Block 2, Block 3", 01 = "Block 0, Block 1"
uint8_t ttf : 1;
} PACKED hitagu82xx_config_t;
// Hitag µ tag structure
struct hitagU_tag {
PSTATE pstate; // protocol-state
TSATE tstate; // tag-state
int max_page;
uint8_t uid[HITAGU_UID_SIZE];
union {
uint8_t asBytes[HITAGU_BLOCK_SIZE];
hitagu_config_t s;
hitagu82xx_config_t s82xx;
} config;
uint8_t password[HITAG_PASSWORD_SIZE];
uint8_t icr; // IC Revision value - determines memory size
union {
uint8_t pages[HITAGU_MAX_BLOCKS][HITAGU_BLOCK_SIZE];
} data;
} PACKED;
typedef struct {
hitag_function cmd;
uint8_t page;
uint8_t page_count;
uint8_t data[HITAGS_PAGE_SIZE];
uint8_t data[HITAG_BLOCK_SIZE];
uint8_t NrAr[HITAG_NRAR_SIZE];
// unaligned access to key as uint64_t will abort.
// todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place?
@ -163,6 +247,9 @@ typedef struct {
// Hitag S section
uint8_t mode;
// Hitag µ section
uint8_t uid[HITAGU_UID_SIZE];
} PACKED lf_hitag_data_t;
typedef struct {
@ -178,4 +265,18 @@ typedef struct {
int8_t pages_reason[HITAGS_MAX_PAGES];
uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE];
} PACKED lf_hts_read_response_t;
// Hitag µ read response structure
typedef struct {
union {
uint8_t asBytes[HITAGU_BLOCK_SIZE];
hitagu_config_t s;
hitagu82xx_config_t s82xx;
} config_page;
uint8_t uid[HITAGU_UID_SIZE];
uint8_t icr; // IC Revision value for memory size detection
int8_t pages_reason[HITAGU_MAX_PAGE_ADVANCED_PLUS];
uint8_t pages[HITAGU_MAX_PAGE_ADVANCED_PLUS][HITAGU_BLOCK_SIZE];
} PACKED lf_htu_read_response_t;
#endif

View file

@ -602,6 +602,12 @@ typedef struct {
#define CMD_LF_HITAGS_WRITE 0x0375
#define CMD_LF_HITAGS_UID 0x037A
// For Hitag µ
#define CMD_LF_HITAGU_READ 0x037B
#define CMD_LF_HITAGU_WRITE 0x037C
#define CMD_LF_HITAGU_SIMULATE 0x037D
#define CMD_LF_HITAGU_UID 0x037E
#define CMD_LF_HITAG_ELOAD 0x0376
#define CMD_HF_ISO14443A_ANTIFUZZ 0x0380

View file

@ -455,12 +455,14 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define LTO 12
#define PROTO_HITAG2 13
#define PROTO_HITAGS 14
#define PROTO_CRYPTORF 15
#define SEOS 16
#define PROTO_MFPLUS 17
#define PROTO_TEXKOM 18
#define PROTO_XEROX 19
#define PROTO_FMCOS20 20
#define PROTO_HITAGU 15
#define PROTO_CRYPTORF 16
#define SEOS 17
#define PROTO_MFPLUS 18
#define PROTO_TEXKOM 19
#define PROTO_XEROX 20
#define PROTO_FMCOS20 21
#define COUNT_OF_PROTOCOLS 22
// Picopass fuses
#define FUSE_FPERS 0x80
@ -948,6 +950,25 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define HITAGS_WRITE_BLOCK 0x90 // 1001 WRITE BLOCK
#define HITAGS_QUIET 0x70 // 0111 QUIET
// Hitag µ flags
#define HITAGU_FLAG_PEXT 0x01 // 0b00001 - Protocol EXTension flag
#define HITAGU_FLAG_INV 0x02 // 0b00010 - INVentory flag
#define HITAGU_FLAG_CRCT 0x04 // 0b00100 - CRC Transponder flag
#define HITAGU_FLAG_SEL 0x08 // 0b01000 - SELect flag (when INV=0)
#define HITAGU_FLAG_ADR 0x10 // 0b10000 - ADdRess flag (when INV=0)
#define HITAGU_FLAG_RFU 0x08 // 0b01000 - Reserved For Use flag (when INV=1, always 0)
#define HITAGU_FLAG_NOS 0x10 // 0b10000 - Number Of Slots flag (when INV=1)
// Hitag µ commands (6-bit)
#define HITAGU_CMD_LOGIN 0x28 // 0b101000 - Login command
#define HITAGU_CMD_INVENTORY 0x00 // 0b000000 - Inventory command
#define HITAGU_CMD_READ_MULTIPLE_BLOCK 0x12 // 0b010010 - Read multiple block command
#define HITAGU_CMD_WRITE_SINGLE_BLOCK 0x14 // 0b010100 - Write single block command
#define HITAGU_CMD_SELECT 0x18 // 0b011000 - Select command
#define HITAGU_CMD_SYSINFO 0x17 // 0b010111 - Get system information command
#define HITAGU_CMD_READ_UID 0x02 // 0b000010 - Read UID command
#define HITAGU_CMD_STAY_QUIET 0x01 // 0b000001 - Stay quiet command
// LTO-CM commands
#define LTO_REQ_STANDARD 0x45
#define LTO_REQ_ALL 0x4A