mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-07-05 20:41:34 -07:00
Added lf hitag htu
support for Hitag µ/8265
This commit is contained in:
parent
ff1289c03d
commit
4bde83b89d
15 changed files with 1822 additions and 19 deletions
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
897
armsrc/hitagu.c
Normal file
897
armsrc/hitagu.c
Normal 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
30
armsrc/hitagu.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -660,6 +660,7 @@ SRCS = mifare/aiddesfire.c \
|
|||
cmdlfhid.c \
|
||||
cmdlfhitag.c \
|
||||
cmdlfhitaghts.c \
|
||||
cmdlfhitagu.c \
|
||||
cmdlfidteck.c \
|
||||
cmdlfindala.c \
|
||||
cmdlfio.c \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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...");
|
||||
|
|
|
@ -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
687
client/src/cmdlfhitagu.c
Normal 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
32
client/src/cmdlfhitagu.h
Normal 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__
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
102
include/hitag.h
102
include/hitag.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue