Merge pull request #2795 from douniwan5788/hitagµ

Hitagµ
This commit is contained in:
Iceman 2025-03-19 12:44:06 +01:00 committed by GitHub
commit 8350dbd6b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1974 additions and 69 deletions

View file

@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added `lf 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)

View file

@ -72,7 +72,7 @@ else
endif
ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS)))
SRC_HITAG = hitag2_crypto.c hitag_common.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"
@ -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

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);

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 -1; // 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 -2; // 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 -2; // 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 -3; // 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 = BSWAP_48(*(uint64_t*)&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] = -1;
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] = -11;
} 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 = -1;
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

@ -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

@ -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

@ -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

@ -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...");

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
@ -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;
@ -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;
}
@ -748,10 +760,78 @@ static int CmdEM410xClone(const char *Cmd) {
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;

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);
@ -2458,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"},

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

@ -0,0 +1,687 @@
//-----------------------------------------------------------------------------
// 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) {
exp_len += snprintf(exp + exp_len, size - exp_len, " PEXT");
}
if (flag & HITAGU_FLAG_INV) {
exp_len += snprintf(exp + exp_len, size - exp_len, " INV");
if (flag & HITAGU_FLAG_RFU) {
exp_len += snprintf(exp + exp_len, size - exp_len, " RFU");
}
if (flag & HITAGU_FLAG_NOS) {
exp_len += snprintf(exp + exp_len, size - exp_len, " NOS");
}
} else {
if (flag & HITAGU_FLAG_SEL) {
exp_len += snprintf(exp + exp_len, size - exp_len, " SEL");
}
if (flag & HITAGU_FLAG_ADR) {
exp_len += snprintf(exp + exp_len, size - exp_len, " ADR");
has_uid = true;
}
}
if (flag & 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;
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];
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];
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;
concatbits(&block_addr, 0, cmd, 5 + 6, 8, false);
uint8_t block_count;
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;
concatbits(&block_addr, 0, cmd, 5 + 6, 8, false);
uint8_t block_data[4];
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) {
switch (reason) {
case 0:
PrintAndLogEx(FAILED, "No data");
break;
case -2:
PrintAndLogEx(FAILED, "UID Request failed!");
break;
case -3:
PrintAndLogEx(FAILED, "Select UID failed!");
break;
case -4:
PrintAndLogEx(FAILED, "No write access on block. Not authorized?");
break;
case -5:
PrintAndLogEx(FAILED, "Write failed! Wrong password?");
break;
case -6:
PrintAndLogEx(FAILED, "Error, " _YELLOW_("AUT=1") " This tag is configured in Authentication Mode");
break;
case -7:
PrintAndLogEx(FAILED, "Error, unknown function");
break;
case -8:
PrintAndLogEx(FAILED, "Authenticate failed!");
break;
case -9:
PrintAndLogEx(FAILED, "No write access on block");
break;
case -10:
PrintAndLogEx(FAILED, "Write to block failed!");
break;
case -11:
PrintAndLogEx(FAILED, "Read block failed!");
break;
default:
// PM3_REASON_UNKNOWN
PrintAndLogEx(FAILED, "Error - Hitag µ failed");
}
}
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 Invalid. Maximum is 255.");
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 %d, count %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-hitagu-");
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 UID
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", "hitagu"); }
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

@ -29,6 +29,7 @@
#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..
@ -586,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;
}
@ -625,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
@ -647,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) {
@ -803,6 +809,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case PROTO_HITAGS:
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);
break;
@ -1314,6 +1323,7 @@ int CmdTraceList(const char *Cmd) {
"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 hitagu -> 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"
@ -1381,6 +1391,7 @@ int CmdTraceList(const char *Cmd) {
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, "hitagu") == 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;
@ -1470,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) {
@ -1552,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

@ -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

@ -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

@ -13,7 +13,7 @@
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Hitag 2, Hitag S
// Hitag 2, Hitag S, Hitag µ
//-----------------------------------------------------------------------------
@ -39,6 +39,26 @@
#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 HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE)
@ -58,18 +78,24 @@ typedef enum {
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;
//---------------------------------------------------------
@ -150,6 +176,57 @@ 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;
@ -170,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 {
@ -185,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

@ -282,7 +282,7 @@ typedef struct {
typedef struct {
// 64KB SRAM -> 524288 bits(max sample num) < 2^30
uint32_t samples :
uint32_t samples :
LF_SAMPLES_BITS;
bool realtime : 1;
bool verbose : 1;
@ -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