mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-22 06:13:51 -07:00
Merge branch 'master' into master
Signed-off-by: Jarek Barwinski <116510448+jareckib@users.noreply.github.com>
This commit is contained in:
commit
c5dd239de5
79 changed files with 6133 additions and 1827 deletions
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -21,6 +21,7 @@ Have you followed the instructions properly? ie, flashed bootrom seperately fi
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
|
Please include text output of the bug happening.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
@ -40,7 +41,7 @@ If applicable, add screenshots to help explain your problem.
|
||||||
- inside proxmark3 client run the following commands and paste the output here.
|
- inside proxmark3 client run the following commands and paste the output here.
|
||||||
- hw version
|
- hw version
|
||||||
- hw status
|
- hw status
|
||||||
- data tune
|
- hw tune
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file.
|
||||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
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]
|
## [unreleased][unreleased]
|
||||||
|
- Changed `trace list -t` - shortend the hitag types (@iceman1001)
|
||||||
|
- Added Be-Tech identification (@iceman1001)
|
||||||
|
- Added `lf em 410x clone --htu` clone EM410x ID to Hitag µ/8265 (@douniwan5788)
|
||||||
|
- Added `lf hitag htu` support for Hitag µ/8265 (@douniwan5788)
|
||||||
|
- Added `hf mfu aesauth` based on existing UL AES support (@doegox)
|
||||||
|
- Changed `hf mfu sim` deny OTP changes with all zeros (@iceman1001)
|
||||||
|
- Added missing file in CMakeLists.txt (@iceman1001)
|
||||||
|
- Changed `lf em 4x70` internals on ARM side; Enabling improved debugging and reliability (@henrygab)
|
||||||
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
|
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
|
||||||
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
|
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
|
||||||
- Changed `trace list -t seos` - improved annotation (@iceman1001)
|
- Changed `trace list -t seos` - improved annotation (@iceman1001)
|
||||||
|
@ -16,7 +24,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||||
- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001)
|
- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001)
|
||||||
- Added sample wiegand format 56bit (@iceman1001)
|
- Added sample wiegand format 56bit (@iceman1001)
|
||||||
- Changed Wiegand formats to include number of bits (@iceman1001)
|
- Changed Wiegand formats to include number of bits (@iceman1001)
|
||||||
- Fix compilation warning in hitagS (@iceman1001)
|
- Fixed compilation warning in hitagS (@iceman1001)
|
||||||
- Added new wiegand format H800002 (@jmichelp)
|
- Added new wiegand format H800002 (@jmichelp)
|
||||||
- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001)
|
- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001)
|
||||||
- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue)
|
- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue)
|
||||||
|
|
|
@ -72,7 +72,7 @@ else
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS)))
|
ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS)))
|
||||||
SRC_HITAG = hitag2_crypto.c hitag2.c hitagS.c hitag2_crack.c
|
SRC_HITAG = hitag2_crypto.c hitag_common.c hitag2.c hitagS.c hitagu.c hitag2_crack.c
|
||||||
APP_CFLAGS += -I../common/hitag2
|
APP_CFLAGS += -I../common/hitag2
|
||||||
else
|
else
|
||||||
SRC_HITAG =
|
SRC_HITAG =
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "hitag2.h"
|
#include "hitag2.h"
|
||||||
#include "hitag2_crack.h"
|
#include "hitag2_crack.h"
|
||||||
#include "hitagS.h"
|
#include "hitagS.h"
|
||||||
|
#include "hitagu.h"
|
||||||
#include "em4x50.h"
|
#include "em4x50.h"
|
||||||
#include "em4x70.h"
|
#include "em4x70.h"
|
||||||
#include "iclass.h"
|
#include "iclass.h"
|
||||||
|
@ -1200,7 +1201,7 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content
|
case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content
|
||||||
hts_simulate((bool)packet->oldarg[0], packet->data.asBytes, true);
|
hts_simulate((bool)packet->oldarg[0], packet->oldarg[1], packet->data.asBytes, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file
|
case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file
|
||||||
|
@ -1218,7 +1219,7 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_LF_HITAGS_UID: {
|
case CMD_LF_HITAGS_UID: {
|
||||||
hts_read_uid(NULL, false, true);
|
hts_read_uid(NULL, true, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CMD_LF_HITAG2_WRITE: {
|
case CMD_LF_HITAG2_WRITE: {
|
||||||
|
@ -1232,6 +1233,25 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||||
memcpy(mem, payload->data, payload->len);
|
memcpy(mem, payload->data, payload->len);
|
||||||
break;
|
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
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_EM4x50
|
#ifdef WITH_EM4x50
|
||||||
|
|
1209
armsrc/em4x70.c
1209
armsrc/em4x70.c
File diff suppressed because it is too large
Load diff
|
@ -19,12 +19,11 @@
|
||||||
#ifndef EM4x70_H
|
#ifndef EM4x70_H
|
||||||
#define EM4x70_H
|
#define EM4x70_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "../include/em4x70.h"
|
#include "../include/em4x70.h"
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint8_t data[32];
|
|
||||||
} em4x70_tag_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RISING_EDGE,
|
RISING_EDGE,
|
||||||
FALLING_EDGE
|
FALLING_EDGE
|
||||||
|
|
|
@ -241,8 +241,8 @@ static uint8_t felica_select_card(felica_card_select_t *card) {
|
||||||
// We try 10 times, or if answer was received.
|
// We try 10 times, or if answer was received.
|
||||||
int len = 25;
|
int len = 25;
|
||||||
do {
|
do {
|
||||||
// end-of-reception response packet data, wait approx. 501μs
|
// end-of-reception response packet data, wait approx. 501µs
|
||||||
// end-of-transmission command packet data, wait approx. 197μs
|
// end-of-transmission command packet data, wait approx. 197µs
|
||||||
// polling card
|
// polling card
|
||||||
TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0);
|
TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0);
|
||||||
|
|
||||||
|
|
|
@ -320,7 +320,7 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_
|
||||||
|
|
||||||
// reader/writer
|
// reader/writer
|
||||||
// returns how long it took
|
// returns how long it took
|
||||||
static uint32_t hitag_reader_send_bit(int bit) {
|
static uint32_t hitag2_reader_send_bit(int bit) {
|
||||||
// Binary pulse length modulation (BPLM) is used to encode the data stream
|
// Binary pulse length modulation (BPLM) is used to encode the data stream
|
||||||
// This means that a transmission of a one takes longer than that of a zero
|
// This means that a transmission of a one takes longer than that of a zero
|
||||||
|
|
||||||
|
@ -349,13 +349,13 @@ static uint32_t hitag_reader_send_bit(int bit) {
|
||||||
|
|
||||||
// reader / writer commands
|
// reader / writer commands
|
||||||
// frame_len is in number of bits?
|
// frame_len is in number of bits?
|
||||||
static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) {
|
static uint32_t hitag2_reader_send_frame(const uint8_t *frame, size_t frame_len) {
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
|
|
||||||
uint32_t wait = 0;
|
uint32_t wait = 0;
|
||||||
// Send the content of the frame
|
// Send the content of the frame
|
||||||
for (size_t i = 0; i < frame_len; i++) {
|
for (size_t i = 0; i < frame_len; i++) {
|
||||||
wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
|
wait += hitag2_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send EOF
|
// Send EOF
|
||||||
|
@ -378,14 +378,14 @@ static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len)
|
||||||
|
|
||||||
// reader / writer commands
|
// reader / writer commands
|
||||||
// frame_len is in number of bits?
|
// frame_len is in number of bits?
|
||||||
static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_len) {
|
static uint32_t hitag2_reader_send_framebits(const uint8_t *frame, size_t frame_len) {
|
||||||
|
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
|
|
||||||
uint32_t wait = 0;
|
uint32_t wait = 0;
|
||||||
// Send the content of the frame
|
// Send the content of the frame
|
||||||
for (size_t i = 0; i < frame_len; i++) {
|
for (size_t i = 0; i < frame_len; i++) {
|
||||||
wait += hitag_reader_send_bit(frame[i]);
|
wait += hitag2_reader_send_bit(frame[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send EOF
|
// Send EOF
|
||||||
|
@ -1863,7 +1863,7 @@ void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
command_duration = hitag_reader_send_frame(tx, txlen);
|
command_duration = hitag2_reader_send_frame(tx, txlen);
|
||||||
response_start = command_start + command_duration;
|
response_start = command_start + command_duration;
|
||||||
|
|
||||||
// Let the antenna and ADC values settle
|
// Let the antenna and ADC values settle
|
||||||
|
@ -2214,7 +2214,7 @@ void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
command_duration = hitag_reader_send_frame(tx, txlen);
|
command_duration = hitag2_reader_send_frame(tx, txlen);
|
||||||
|
|
||||||
// global write state variable used
|
// global write state variable used
|
||||||
// tearoff occurred
|
// tearoff occurred
|
||||||
|
@ -2434,9 +2434,9 @@ static void ht2_send(bool turn_on, uint32_t *cmd_start
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
if (send_bits) {
|
if (send_bits) {
|
||||||
*cmd_duration = hitag_reader_send_framebits(tx, txlen);
|
*cmd_duration = hitag2_reader_send_framebits(tx, txlen);
|
||||||
} else {
|
} else {
|
||||||
*cmd_duration = hitag_reader_send_frame(tx, txlen);
|
*cmd_duration = hitag2_reader_send_frame(tx, txlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
*resp_start = (*cmd_start + *cmd_duration);
|
*resp_start = (*cmd_start + *cmd_duration);
|
||||||
|
|
722
armsrc/hitagS.c
722
armsrc/hitagS.c
|
@ -19,6 +19,7 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
#include "hitagS.h"
|
#include "hitagS.h"
|
||||||
|
#include "hitag_common.h"
|
||||||
|
|
||||||
#include "proxmark3_arm.h"
|
#include "proxmark3_arm.h"
|
||||||
#include "cmd.h"
|
#include "cmd.h"
|
||||||
|
@ -33,12 +34,8 @@
|
||||||
#include "lfadc.h"
|
#include "lfadc.h"
|
||||||
#include "crc.h"
|
#include "crc.h"
|
||||||
#include "protocols.h"
|
#include "protocols.h"
|
||||||
#include "hitag.h"
|
|
||||||
#include "appmain.h" // tearoff_hook()
|
#include "appmain.h" // tearoff_hook()
|
||||||
|
|
||||||
#define CRC_PRESET 0xFF
|
|
||||||
#define CRC_POLYNOM 0x1D
|
|
||||||
|
|
||||||
static struct hitagS_tag tag = {
|
static struct hitagS_tag tag = {
|
||||||
.data.pages = {
|
.data.pages = {
|
||||||
// Plain mode: | Authentication mode:
|
// Plain mode: | Authentication mode:
|
||||||
|
@ -58,13 +55,6 @@ static uint8_t page_to_be_written = 0;
|
||||||
static int block_data_left = 0;
|
static int block_data_left = 0;
|
||||||
static bool enable_page_tearoff = false;
|
static bool enable_page_tearoff = false;
|
||||||
|
|
||||||
typedef enum modulation {
|
|
||||||
AC2K = 0,
|
|
||||||
AC4K,
|
|
||||||
MC4K,
|
|
||||||
MC8K
|
|
||||||
} MOD;
|
|
||||||
|
|
||||||
static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1;
|
static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1;
|
||||||
static MOD m = AC2K; // used modulation
|
static MOD m = AC2K; // used modulation
|
||||||
static uint32_t reader_selected_uid;
|
static uint32_t reader_selected_uid;
|
||||||
|
@ -72,9 +62,6 @@ static int rotate_uid = 0;
|
||||||
static int sof_bits; // number of start-of-frame bits
|
static int sof_bits; // number of start-of-frame bits
|
||||||
static uint8_t pwdh0, pwdl0, pwdl1; // password bytes
|
static uint8_t pwdh0, pwdl0, pwdl1; // password bytes
|
||||||
static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number
|
static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number
|
||||||
static uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, ~47min
|
|
||||||
|
|
||||||
#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
|
|
||||||
|
|
||||||
//#define SENDBIT_TEST
|
//#define SENDBIT_TEST
|
||||||
|
|
||||||
|
@ -98,60 +85,6 @@ datasheet HitagS_V11.pdf bytes in tables printed 3 2 1 0
|
||||||
#define ht2bs_4b(a,b,c,d) (~(((d|c)&(a^b))^(d|a|b)))
|
#define ht2bs_4b(a,b,c,d) (~(((d|c)&(a^b))^(d|a|b)))
|
||||||
#define ht2bs_5c(a,b,c,d,e) (~((((((c^e)|d)&a)^b)&(c^b))^(((d^e)|a)&((d^b)|c))))
|
#define ht2bs_5c(a,b,c,d,e) (~((((((c^e)|d)&a)^b)&(c^b))^(((d^e)|a)&((d^b)|c))))
|
||||||
|
|
||||||
// Sam7s has several timers, we will use the source TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK)
|
|
||||||
// TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, Timer is running at 48MHz/32 = 1500 KHz
|
|
||||||
// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
|
|
||||||
// T0 = TIMER_CLOCK3 / 125000 = 12
|
|
||||||
|
|
||||||
#define T0 12
|
|
||||||
|
|
||||||
#define HITAG_FRAME_LEN 20
|
|
||||||
|
|
||||||
// TC0 and TC1 are 16-bit counters and will overflow after 5461 * T0
|
|
||||||
// Ensure not to set these timings above 5461 (~43ms) when comparing without considering overflow, as they will never reach that value.
|
|
||||||
|
|
||||||
#define HITAG_T_STOP 36 /* T_EOF should be > 36 */
|
|
||||||
#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */
|
|
||||||
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
|
|
||||||
#define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */
|
|
||||||
#define HITAG_T_0 20 /* T[0] should be 18..22 */
|
|
||||||
#define HITAG_T_1 28 /* T[1] should be 26..30 */
|
|
||||||
// #define HITAG_T_EOF 40 /* T_EOF should be > 36 */
|
|
||||||
#define HITAG_T_EOF 80 /* T_EOF should be > 36 */
|
|
||||||
#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */
|
|
||||||
#define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */
|
|
||||||
#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */
|
|
||||||
#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */
|
|
||||||
|
|
||||||
#define HITAG_T_TAG_ONE_HALF_PERIOD 10
|
|
||||||
#define HITAG_T_TAG_TWO_HALF_PERIOD 25
|
|
||||||
#define HITAG_T_TAG_THREE_HALF_PERIOD 41
|
|
||||||
#define HITAG_T_TAG_FOUR_HALF_PERIOD 57
|
|
||||||
|
|
||||||
#define HITAG_T_TAG_HALF_PERIOD 16
|
|
||||||
#define HITAG_T_TAG_FULL_PERIOD 32
|
|
||||||
|
|
||||||
#define HITAG_T_TAG_CAPTURE_ONE_HALF 13
|
|
||||||
#define HITAG_T_TAG_CAPTURE_TWO_HALF 25
|
|
||||||
#define HITAG_T_TAG_CAPTURE_THREE_HALF 41
|
|
||||||
#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Implementation of the crc8 calculation from Hitag S
|
|
||||||
* from http://www.proxmark.org/files/Documents/125%20kHz%20-%20Hitag/HitagS.V11.pdf
|
|
||||||
*/
|
|
||||||
static void calc_crc(unsigned char *crc, unsigned char data, unsigned char Bitcount) {
|
|
||||||
*crc ^= data; // crc = crc (exor) data
|
|
||||||
do {
|
|
||||||
if (*crc & 0x80) { // if (MSB-CRC == 1)
|
|
||||||
*crc <<= 1; // CRC = CRC Bit-shift left
|
|
||||||
*crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM
|
|
||||||
} else {
|
|
||||||
*crc <<= 1; // CRC = CRC Bit-shift left
|
|
||||||
}
|
|
||||||
} while (--Bitcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void update_tag_max_page(void) {
|
static void update_tag_max_page(void) {
|
||||||
//check which memorysize this tag has
|
//check which memorysize this tag has
|
||||||
if (tag.data.s.config.MEMT == 0x00) {
|
if (tag.data.s.config.MEMT == 0x00) {
|
||||||
|
@ -165,261 +98,13 @@ static void update_tag_max_page(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hitag_send_bit(int bit, bool ledcontrol) {
|
|
||||||
|
|
||||||
if (ledcontrol) LED_A_ON();
|
|
||||||
// Reset clock for the next bit
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
||||||
|
|
||||||
switch (m) {
|
|
||||||
case AC2K: {
|
|
||||||
if (bit == 0) {
|
|
||||||
// AC Coding --__
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// AC coding -_-_
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
||||||
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 48) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
|
|
||||||
|
|
||||||
}
|
|
||||||
if (ledcontrol) LED_A_OFF();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AC4K: {
|
|
||||||
if (bit == 0) {
|
|
||||||
// AC Coding --__
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// AC coding -_-_
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
||||||
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 24) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
||||||
}
|
|
||||||
if (ledcontrol) LED_A_OFF();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MC4K: {
|
|
||||||
if (bit == 0) {
|
|
||||||
// Manchester: Unloaded, then loaded |__--|
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
||||||
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Manchester: Loaded, then unloaded |--__|
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
|
||||||
|
|
||||||
}
|
|
||||||
if (ledcontrol) LED_A_OFF();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MC8K: {
|
|
||||||
if (bit == 0) {
|
|
||||||
// Manchester: Unloaded, then loaded |__--|
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
|
||||||
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Manchester: Loaded, then unloaded |--__|
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
|
||||||
|
|
||||||
}
|
|
||||||
if (ledcontrol) LED_A_OFF();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hitag_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) {
|
|
||||||
|
|
||||||
DBG Dbprintf("hitag_send_frame: (%i) %02X %02X %02X %02X", frame_len, frame[0], frame[1], frame[2], frame[3]);
|
|
||||||
|
|
||||||
// The beginning of the frame is hidden in some high level; pause until our bits will have an effect
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
switch (m) {
|
|
||||||
case AC4K:
|
|
||||||
case MC8K: {
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; //FADV
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case AC2K:
|
|
||||||
case MC4K: {
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; //STD + ADV
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SOF - send start of frame
|
|
||||||
for (size_t i = 0; i < sof_bits; i++) {
|
|
||||||
hitag_send_bit(1, ledcontrol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the content of the frame
|
|
||||||
for (size_t i = 0; i < frame_len; i++) {
|
|
||||||
hitag_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol);
|
|
||||||
}
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hitag_reader_send_bit(int bit, bool ledcontrol) {
|
|
||||||
|
|
||||||
if (ledcontrol) LED_A_ON();
|
|
||||||
// Reset clock for the next bit
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
||||||
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
|
||||||
|
|
||||||
// Binary puls length modulation (BPLM) is used to encode the data stream
|
|
||||||
// This means that a transmission of a one takes longer than that of a zero
|
|
||||||
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
#ifdef SENDBIT_TEST
|
|
||||||
// Wait for 4-10 times the carrier period
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 6) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
if (bit == 0) {
|
|
||||||
// Zero bit: |_-|
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 11) {};
|
|
||||||
} else {
|
|
||||||
// One bit: |_--|
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * 14) {};
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// Wait for 4-10 times the carrier period
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
if (bit == 0) {
|
|
||||||
// Zero bit: |_-|
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {};
|
|
||||||
} else {
|
|
||||||
// One bit: |_--|
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ledcontrol) LED_A_OFF();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) {
|
|
||||||
// Send the content of the frame
|
|
||||||
for (size_t i = 0; i < frame_len; i++) {
|
|
||||||
hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol);
|
|
||||||
}
|
|
||||||
// send EOF
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
||||||
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
|
||||||
|
|
||||||
HIGH(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
// Wait for 4-10 times the carrier period
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
|
||||||
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hts_stop_clock(void) {
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
|
|
||||||
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
|
|
||||||
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hts_init_clock(void) {
|
|
||||||
// Enable Peripheral Clock for
|
|
||||||
// Timer Counter 0, used to measure exact timing before answering
|
|
||||||
// Timer Counter 1, used to capture edges of the tag frames
|
|
||||||
// Timer Counter 2, used to log trace time
|
|
||||||
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2);
|
|
||||||
|
|
||||||
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
|
|
||||||
|
|
||||||
// Disable timer during configuration
|
|
||||||
hts_stop_clock();
|
|
||||||
|
|
||||||
// TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
|
|
||||||
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
|
|
||||||
|
|
||||||
// TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger,
|
|
||||||
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3)
|
|
||||||
| AT91C_TC_ABETRG // TIOA is used as an external trigger
|
|
||||||
| AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge
|
|
||||||
| AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA
|
|
||||||
| AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA
|
|
||||||
|
|
||||||
// TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
|
|
||||||
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
|
|
||||||
|
|
||||||
// Enable and reset counters
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
||||||
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
||||||
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
||||||
|
|
||||||
// Assert a sync signal. This sets all timers to 0 on next active clock edge
|
|
||||||
AT91C_BASE_TCB->TCB_BCR = 1;
|
|
||||||
|
|
||||||
// synchronized startup procedure
|
|
||||||
// In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right?
|
|
||||||
while (AT91C_BASE_TC0->TC_CV != 0) {}; // wait until TC0 returned to zero
|
|
||||||
|
|
||||||
// reset timestamp
|
|
||||||
timestamp_high = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* to check if the right uid was selected
|
* to check if the right uid was selected
|
||||||
*/
|
*/
|
||||||
static int check_select(const uint8_t *rx, uint32_t uid) {
|
static int check_select(const uint8_t *rx, uint32_t uid) {
|
||||||
|
|
||||||
// global var?
|
// global var?
|
||||||
concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32);
|
concatbits((uint8_t *)&reader_selected_uid, 0, rx, 5, 32, false);
|
||||||
reader_selected_uid = BSWAP_32(reader_selected_uid);
|
reader_selected_uid = BSWAP_32(reader_selected_uid);
|
||||||
|
|
||||||
if (reader_selected_uid == uid) {
|
if (reader_selected_uid == uid) {
|
||||||
|
@ -506,7 +191,6 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
|
||||||
DBG DbpString("SELECT");
|
DBG DbpString("SELECT");
|
||||||
|
|
||||||
if ((rx[0] & 0xf8) == HITAGS_SELECT && check_select(rx, BSWAP_32(tag.data.s.uid_le)) == 1) {
|
if ((rx[0] & 0xf8) == HITAGS_SELECT && check_select(rx, BSWAP_32(tag.data.s.uid_le)) == 1) {
|
||||||
|
|
||||||
DBG DbpString("SELECT match");
|
DBG DbpString("SELECT match");
|
||||||
|
|
||||||
//if the right tag was selected
|
//if the right tag was selected
|
||||||
|
@ -519,13 +203,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
|
||||||
|
|
||||||
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
||||||
//add crc8
|
//add crc8
|
||||||
|
crc = CRC8Hitag1Bits(tx, 32);
|
||||||
*txlen += 8;
|
*txlen += 8;
|
||||||
crc = CRC_PRESET;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
calc_crc(&crc, tx[i], 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
tx[4] = crc;
|
tx[4] = crc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,26 +225,31 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
|
||||||
ht2_hitag2_byte(&state);
|
ht2_hitag2_byte(&state);
|
||||||
}
|
}
|
||||||
|
|
||||||
//send con2, pwdh0, pwdl0, pwdl1 encrypted as a response
|
// store plaintext first
|
||||||
tx[0] = ht2_hitag2_byte(&state) ^ tag.data.pages[HITAGS_CONFIG_PADR][2];
|
tx[0] = tag.data.pages[HITAGS_CONFIG_PADR][2];
|
||||||
tx[1] = ht2_hitag2_byte(&state) ^ tag.data.s.config.pwdh0;
|
tx[1] = tag.data.s.config.pwdh0;
|
||||||
tx[2] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl0;
|
tx[2] = tag.data.s.pwdl0;
|
||||||
tx[3] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl1;
|
tx[3] = tag.data.s.pwdl1;
|
||||||
|
|
||||||
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
||||||
// add crc8
|
// add crc8
|
||||||
*txlen += 8;
|
*txlen += 8;
|
||||||
crc = CRC_PRESET;
|
crc = CRC8Hitag1Bits(tx, 32);
|
||||||
calc_crc(&crc, tag.data.pages[HITAGS_CONFIG_PADR][2], 8);
|
tx[4] = crc;
|
||||||
calc_crc(&crc, tag.data.s.config.pwdh0, 8);
|
|
||||||
calc_crc(&crc, tag.data.s.pwdl0, 8);
|
|
||||||
calc_crc(&crc, tag.data.s.pwdl1, 8);
|
|
||||||
tx[4] = (crc ^ ht2_hitag2_byte(&state));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// then xor with keystream
|
||||||
|
tx[0] ^= ht2_hitag2_byte(&state);
|
||||||
|
tx[1] ^= ht2_hitag2_byte(&state);
|
||||||
|
tx[2] ^= ht2_hitag2_byte(&state);
|
||||||
|
tx[3] ^= ht2_hitag2_byte(&state);
|
||||||
|
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
||||||
|
tx[4] ^= ht2_hitag2_byte(&state);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* some readers do not allow to authenticate multiple times in a row with the same tag.
|
* some readers do not allow to authenticate multiple times in a row with the same tag.
|
||||||
* use this to change the uid between authentications.
|
* use this to change the uid between authentications.
|
||||||
|
|
||||||
if (rotate_uid % 2 == 0) {
|
if (rotate_uid % 2 == 0) {
|
||||||
tag.data.s.uid_le = 0x44332211;
|
tag.data.s.uid_le = 0x44332211;
|
||||||
} else {
|
} else {
|
||||||
|
@ -621,11 +305,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
|
||||||
|
|
||||||
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
||||||
//add crc8
|
//add crc8
|
||||||
|
crc = CRC8Hitag1Bits(tx, 32);
|
||||||
*txlen += 8;
|
*txlen += 8;
|
||||||
crc = CRC_PRESET;
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
calc_crc(&crc, tx[i], 8);
|
|
||||||
}
|
|
||||||
tx[4] = crc;
|
tx[4] = crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,10 +324,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
|
||||||
|
|
||||||
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
if (protocol_mode != HITAGS_UID_REQ_STD) {
|
||||||
//add crc8
|
//add crc8
|
||||||
crc = CRC_PRESET;
|
crc = CRC8Hitag1Bits(tx, *txlen);
|
||||||
for (int i = 0; i < *txlen / 8; i++) {
|
|
||||||
calc_crc(&crc, tx[i], 8);
|
|
||||||
}
|
|
||||||
*txlen += 8;
|
*txlen += 8;
|
||||||
tx[16] = crc;
|
tx[16] = crc;
|
||||||
}
|
}
|
||||||
|
@ -692,26 +370,17 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
|
||||||
/*
|
/*
|
||||||
* Emulates a Hitag S Tag with the given data from the .hts file
|
* Emulates a Hitag S Tag with the given data from the .hts file
|
||||||
*/
|
*/
|
||||||
void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
|
void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol) {
|
||||||
|
|
||||||
StopTicks();
|
|
||||||
|
|
||||||
int overflow = 0;
|
int overflow = 0;
|
||||||
uint8_t rx[HITAG_FRAME_LEN];
|
uint8_t rx[HITAG_FRAME_LEN] = {0};
|
||||||
size_t rxlen = 0;
|
size_t rxlen = 0;
|
||||||
uint8_t tx[HITAG_FRAME_LEN];
|
uint8_t tx[HITAG_FRAME_LEN];
|
||||||
size_t txlen = 0;
|
size_t txlen = 0;
|
||||||
|
|
||||||
// Reset the received frame, frame count and timing info
|
|
||||||
memset(rx, 0x00, sizeof(rx));
|
|
||||||
|
|
||||||
// free eventually allocated BigBuf memory
|
// free eventually allocated BigBuf memory
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
BigBuf_Clear_ext(false);
|
BigBuf_Clear_ext(false);
|
||||||
|
|
||||||
// Enable tracing
|
|
||||||
set_tracing(true);
|
|
||||||
|
|
||||||
DbpString("Starting Hitag S simulation");
|
DbpString("Starting Hitag S simulation");
|
||||||
|
|
||||||
tag.pstate = HT_READY;
|
tag.pstate = HT_READY;
|
||||||
|
@ -738,67 +407,16 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hitag_setup_fpga(0, threshold, ledcontrol);
|
||||||
// Set up simulator mode, frequency divisor which will drive the FPGA
|
|
||||||
// and analog mux selection.
|
|
||||||
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
||||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
|
||||||
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
|
|
||||||
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
|
|
||||||
|
|
||||||
// Configure output pin that is connected to the FPGA (for modulating)
|
|
||||||
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
|
|
||||||
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
|
|
||||||
|
|
||||||
// Disable modulation at default, which means release resistance
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
hts_init_clock();
|
|
||||||
|
|
||||||
if (ledcontrol) LED_D_ON();
|
|
||||||
|
|
||||||
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
|
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
|
||||||
uint32_t start_time = 0;
|
uint32_t start_time = 0;
|
||||||
|
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
|
|
||||||
// Receive frame, watch for at most T0*EOF periods
|
// Receive commands from the reader
|
||||||
while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) {
|
hitag_tag_receive_frame(rx, sizeof(rx), &rxlen, &start_time, ledcontrol, &overflow);
|
||||||
|
|
||||||
// Check if rising edge in modulation is detected
|
|
||||||
if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) {
|
|
||||||
|
|
||||||
// Retrieve the new timing values
|
|
||||||
int rb = (AT91C_BASE_TC1->TC_RB / T0) + overflow;
|
|
||||||
overflow = 0;
|
|
||||||
|
|
||||||
// Reset timer every frame, we have to capture the last edge for timing
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
|
||||||
|
|
||||||
if (ledcontrol) LED_B_ON();
|
|
||||||
// Capture reader cmd start timestamp
|
|
||||||
if (start_time == 0) {
|
|
||||||
start_time = TIMESTAMP - HITAG_T_LOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Capture reader frame
|
|
||||||
if (rb >= HITAG_T_STOP) {
|
|
||||||
if (rxlen != 0) {
|
|
||||||
//DbpString("weird0?");
|
|
||||||
}
|
|
||||||
} else if (rb >= HITAG_T_1_MIN) {
|
|
||||||
// '1' bit
|
|
||||||
rx[rxlen / 8] |= 1 << (7 - (rxlen % 8));
|
|
||||||
rxlen++;
|
|
||||||
} else if (rb >= HITAG_T_0_MIN) {
|
|
||||||
// '0' bit
|
|
||||||
rx[rxlen / 8] |= 0 << (7 - (rxlen % 8));
|
|
||||||
rxlen++;
|
|
||||||
} else {
|
|
||||||
// Ignore weird value, is to small to mean anything
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if frame was captured
|
// Check if frame was captured
|
||||||
if (rxlen > 0) {
|
if (rxlen > 0) {
|
||||||
|
@ -821,7 +439,7 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
|
||||||
if (txlen > 0) {
|
if (txlen > 0) {
|
||||||
// Transmit the tag frame
|
// Transmit the tag frame
|
||||||
start_time = TIMESTAMP;
|
start_time = TIMESTAMP;
|
||||||
hitag_send_frame(tx, txlen, ledcontrol);
|
hitag_tag_send_frame(tx, txlen, sof_bits, m, ledcontrol);
|
||||||
LogTraceBits(tx, txlen, start_time, TIMESTAMP, false);
|
LogTraceBits(tx, txlen, start_time, TIMESTAMP, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -842,152 +460,13 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hts_stop_clock();
|
hitag_cleanup(ledcontrol);
|
||||||
set_tracing(false);
|
|
||||||
lf_finalize(ledcontrol);
|
|
||||||
// release allocated memory from BigBuff.
|
// release allocated memory from BigBuff.
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
|
|
||||||
DbpString("Sim stopped");
|
DbpString("Sim stopped");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol) {
|
|
||||||
|
|
||||||
// Reset values for receiving frames
|
|
||||||
memset(rx, 0x00, sizeofrx);
|
|
||||||
*rxlen = 0;
|
|
||||||
|
|
||||||
int lastbit = 1;
|
|
||||||
bool bSkip = true;
|
|
||||||
uint32_t errorCount = 0;
|
|
||||||
bool bStarted = false;
|
|
||||||
uint16_t next_edge_event = AT91C_TC_LDRBS;
|
|
||||||
int double_speed = (m == AC4K || m == MC8K) ? 2 : 1;
|
|
||||||
|
|
||||||
uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0;
|
|
||||||
uint8_t edges[160] = {0};
|
|
||||||
|
|
||||||
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
|
|
||||||
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
|
|
||||||
|
|
||||||
// Check if edge in tag modulation is detected
|
|
||||||
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
|
|
||||||
|
|
||||||
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
|
|
||||||
|
|
||||||
// only use AT91C_TC_LDRBS falling edge for now
|
|
||||||
if (next_edge_event == AT91C_TC_LDRBS) continue;
|
|
||||||
|
|
||||||
// Retrieve the new timing values
|
|
||||||
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0;
|
|
||||||
edges[rb_i++] = rb;
|
|
||||||
// Reset timer every frame, we have to capture the last edge for timing
|
|
||||||
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
|
||||||
|
|
||||||
if (ledcontrol) LED_B_ON();
|
|
||||||
|
|
||||||
// Capture tag response timestamp
|
|
||||||
if (*rxlen == 0) *resptime = TIMESTAMP;
|
|
||||||
|
|
||||||
// Capture tag frame (manchester decoding using only falling edges)
|
|
||||||
if (bStarted == false) {
|
|
||||||
|
|
||||||
if (rb >= HITAG_T_WAIT_RESP) {
|
|
||||||
bStarted = true;
|
|
||||||
|
|
||||||
// We always receive a 'one' first, which has the falling edge after a half period |-_|
|
|
||||||
rx[0] = 0x80;
|
|
||||||
(*rxlen)++;
|
|
||||||
} else {
|
|
||||||
errorCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Anticollision Coding
|
|
||||||
if (m == AC2K || m == AC4K) {
|
|
||||||
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
|
|
||||||
// Anticollision Coding example |--__|--__| (00)
|
|
||||||
lastbit = 0;
|
|
||||||
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
|
|
||||||
// Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01)
|
|
||||||
lastbit = !lastbit;
|
|
||||||
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
|
|
||||||
bSkip = !!lastbit;
|
|
||||||
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
|
|
||||||
// Anticollision Coding example |-_-_| (1)
|
|
||||||
if (bSkip == false) {
|
|
||||||
lastbit = 1;
|
|
||||||
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bSkip = !bSkip;
|
|
||||||
} else {
|
|
||||||
// Ignore weird value, is to small to mean anything
|
|
||||||
errorCount++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Manchester coding
|
|
||||||
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
|
|
||||||
// Manchester coding example |-_|_-|-_| (101)
|
|
||||||
rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
|
|
||||||
rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
h4++;
|
|
||||||
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
|
|
||||||
// Manchester coding example |_-|...|_-|-_| (0...01)
|
|
||||||
rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
|
|
||||||
// We have to skip this half period at start and add the 'one' the second time
|
|
||||||
if (bSkip == false) {
|
|
||||||
rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastbit = !lastbit;
|
|
||||||
bSkip = !bSkip;
|
|
||||||
h3++;
|
|
||||||
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
|
|
||||||
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
|
|
||||||
// bit is same as last bit
|
|
||||||
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
|
|
||||||
(*rxlen)++;
|
|
||||||
h2++;
|
|
||||||
} else {
|
|
||||||
// Ignore weird value, is to small to mean anything
|
|
||||||
errorCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we saw over 100 weird values break it probably isn't hitag...
|
|
||||||
if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can break this loop if we received the last bit from a frame
|
|
||||||
// max periods between 2 falling edge
|
|
||||||
// RTF AC64 |--__|--__| (00) 64 * T0
|
|
||||||
// RTF MC32 |_-|-_|_-| (010) 48 * T0
|
|
||||||
if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) {
|
|
||||||
if ((*rxlen)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DBG Dbprintf("RX0 %i:%02X.. err:%i resptime:%i h2:%i h3:%i h4:%i edges:", *rxlen, rx[0], errorCount, *resptime, h2, h3, h4);
|
|
||||||
DBG Dbhexdump(rb_i, edges, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, bool ledcontrol, bool ac_seq) {
|
static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, bool ledcontrol, bool ac_seq) {
|
||||||
uint32_t start_time;
|
uint32_t start_time;
|
||||||
|
|
||||||
|
@ -995,6 +474,9 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
|
||||||
// Disable timer 1 with external trigger to avoid triggers during our own modulation
|
// Disable timer 1 with external trigger to avoid triggers during our own modulation
|
||||||
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
|
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
|
||||||
|
|
||||||
|
DBG Dbprintf("tx %d bits:", txlen);
|
||||||
|
DBG Dbhexdump((txlen + 7) / 8, tx, false);
|
||||||
|
|
||||||
// Wait for HITAG_T_WAIT_SC carrier periods after the last tag bit before transmitting,
|
// Wait for HITAG_T_WAIT_SC carrier periods after the last tag bit before transmitting,
|
||||||
// Since the clock counts since the last falling edge, a 'one' means that the
|
// Since the clock counts since the last falling edge, a 'one' means that the
|
||||||
// falling edge occurred halfway the period. with respect to this falling edge,
|
// falling edge occurred halfway the period. with respect to this falling edge,
|
||||||
|
@ -1005,7 +487,7 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
|
||||||
start_time = TIMESTAMP;
|
start_time = TIMESTAMP;
|
||||||
|
|
||||||
// Transmit the reader frame
|
// Transmit the reader frame
|
||||||
hitag_reader_send_frame(tx, txlen, ledcontrol);
|
hitag_reader_send_frame(tx, txlen, ledcontrol, false);
|
||||||
|
|
||||||
if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) {
|
if (enable_page_tearoff && tearoff_hook() == PM3_ETEAROFF) {
|
||||||
return PM3_ETEAROFF;
|
return PM3_ETEAROFF;
|
||||||
|
@ -1018,7 +500,11 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
|
||||||
|
|
||||||
hts_set_frame_modulation(protocol_mode, ac_seq);
|
hts_set_frame_modulation(protocol_mode, ac_seq);
|
||||||
|
|
||||||
hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol);
|
hitag_reader_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol, m, sof_bits);
|
||||||
|
// hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol);
|
||||||
|
|
||||||
|
DBG Dbprintf("rx %d bits:", *rxlen);
|
||||||
|
DBG Dbhexdump((*rxlen + 7) / 8, rx, false);
|
||||||
|
|
||||||
// Check if frame was captured and store it
|
// Check if frame was captured and store it
|
||||||
if (*rxlen > 0) {
|
if (*rxlen > 0) {
|
||||||
|
@ -1033,22 +519,7 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
|
||||||
Dbprintf("htS: sizeofrx... %zu", sizeofrx);
|
Dbprintf("htS: sizeofrx... %zu", sizeofrx);
|
||||||
DbpString("htS: response_bit:");
|
DbpString("htS: response_bit:");
|
||||||
Dbhexdump(*rxlen, response_bit, false);
|
Dbhexdump(*rxlen, response_bit, false);
|
||||||
Dbprintf("htS: skipping %d bit SOF", sof_bits);
|
|
||||||
|
|
||||||
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
|
|
||||||
DBG DbpString("htS: Warning, not all bits of SOF are 1");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// remove first sof_bits bits SOF
|
|
||||||
for (size_t i = 0; i < (*rxlen + 8) / 8; i++) {
|
|
||||||
rx[i] <<= sof_bits;
|
|
||||||
if (i + 1 < (*rxlen + 8) / 8) {
|
|
||||||
rx[i] |= (rx[i + 1] >> (8 - sof_bits));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*rxlen -= sof_bits;
|
|
||||||
|
|
||||||
LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false);
|
LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false);
|
||||||
}
|
}
|
||||||
|
@ -1057,40 +528,18 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) {
|
static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) {
|
||||||
|
size_t txlen = 0;
|
||||||
|
size_t rxlen = 0;
|
||||||
|
|
||||||
StopTicks();
|
// Setup FPGA and initialize
|
||||||
|
hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol);
|
||||||
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
||||||
|
|
||||||
// Clean up trace and prepare it for storing frames
|
|
||||||
set_tracing(true);
|
|
||||||
clear_trace();
|
|
||||||
|
|
||||||
if (ledcontrol) LED_D_ON();
|
|
||||||
|
|
||||||
hts_init_clock();
|
|
||||||
|
|
||||||
// Set fpga in edge detect with reader field, we can modulate as reader now
|
|
||||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD);
|
|
||||||
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
|
|
||||||
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
|
|
||||||
|
|
||||||
// Configure output and enable pin that is connected to the FPGA (for modulating)
|
|
||||||
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
|
|
||||||
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
|
|
||||||
|
|
||||||
// Disable modulation at default, which means enable the field
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
// UID request standard 00110
|
// UID request standard 00110
|
||||||
// UID request Advanced 1100x
|
// UID request Advanced 1100x
|
||||||
// UID request FAdvanced 11010
|
// UID request FAdvanced 11010
|
||||||
size_t txlen = 0;
|
|
||||||
size_t rxlen = 0;
|
|
||||||
|
|
||||||
protocol_mode = packet->mode;
|
protocol_mode = packet->mode;
|
||||||
uint8_t cmd = protocol_mode;
|
uint8_t cmd = protocol_mode;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true);
|
||||||
|
|
||||||
if (rxlen != 32) {
|
if (rxlen != 32) {
|
||||||
|
@ -1105,10 +554,10 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
// select uid
|
// select uid
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
cmd = HITAGS_SELECT;
|
cmd = HITAGS_SELECT;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||||
txlen = concatbits(tx, txlen, rx, 0, 32);
|
txlen = concatbits(tx, txlen, rx, 0, 32, false);
|
||||||
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1140,8 +589,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
}
|
}
|
||||||
|
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
txlen = concatbits(tx, txlen, rnd, 0, 32);
|
txlen = concatbits(tx, txlen, rnd, 0, 32, false);
|
||||||
txlen = concatbits(tx, txlen, auth_ks, 0, 32);
|
txlen = concatbits(tx, txlen, auth_ks, 0, 32, false);
|
||||||
|
|
||||||
DBG DbpString("Authenticating using key:");
|
DBG DbpString("Authenticating using key:");
|
||||||
DBG Dbhexdump(6, packet->key, false);
|
DBG Dbhexdump(6, packet->key, false);
|
||||||
|
@ -1173,13 +622,13 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
// send write page request
|
// send write page request
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
cmd = HITAGS_WRITE_PAGE;
|
cmd = HITAGS_WRITE_PAGE;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||||
|
|
||||||
uint8_t addr = 64;
|
uint8_t addr = 64;
|
||||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||||
|
|
||||||
crc = CRC8Hitag1Bits(tx, txlen);
|
crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1189,9 +638,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
|
||||||
}
|
}
|
||||||
|
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
txlen = concatbits(tx, txlen, packet->pwd, 0, 32);
|
txlen = concatbits(tx, txlen, packet->pwd, 0, 32, false);
|
||||||
crc = CRC8Hitag1Bits(tx, txlen);
|
crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1287,11 +736,11 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
//send read request
|
//send read request
|
||||||
size_t txlen = 0;
|
size_t txlen = 0;
|
||||||
uint8_t cmd = HITAGS_READ_PAGE;
|
uint8_t cmd = HITAGS_READ_PAGE;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||||
uint8_t addr = page_addr;
|
uint8_t addr = page_addr;
|
||||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||||
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1356,9 +805,7 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
read_end:
|
read_end:
|
||||||
hts_stop_clock();
|
hitag_cleanup(ledcontrol);
|
||||||
set_tracing(false);
|
|
||||||
lf_finalize(ledcontrol);
|
|
||||||
reply_reason(CMD_LF_HITAGS_READ, status, reason, (uint8_t *)&card, sizeof(card));
|
reply_reason(CMD_LF_HITAGS_READ, status, reason, (uint8_t *)&card, sizeof(card));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1396,13 +843,13 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
|
|
||||||
uint8_t cmd = HITAGS_WRITE_PAGE;
|
uint8_t cmd = HITAGS_WRITE_PAGE;
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 4);
|
txlen = concatbits(tx, txlen, &cmd, 0, 4, false);
|
||||||
|
|
||||||
uint8_t addr = payload->page;
|
uint8_t addr = payload->page;
|
||||||
txlen = concatbits(tx, txlen, &addr, 0, 8);
|
txlen = concatbits(tx, txlen, &addr, 0, 8, false);
|
||||||
|
|
||||||
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
|
||||||
|
|
||||||
|
@ -1430,9 +877,9 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
txlen = 0;
|
txlen = 0;
|
||||||
txlen = concatbits(tx, txlen, payload->data, 0, 32);
|
txlen = concatbits(tx, txlen, payload->data, 0, 32, false);
|
||||||
crc = CRC8Hitag1Bits(tx, txlen);
|
crc = CRC8Hitag1Bits(tx, txlen);
|
||||||
txlen = concatbits(tx, txlen, &crc, 0, 8);
|
txlen = concatbits(tx, txlen, &crc, 0, 8, false);
|
||||||
|
|
||||||
enable_page_tearoff = g_tearoff_enabled;
|
enable_page_tearoff = g_tearoff_enabled;
|
||||||
|
|
||||||
|
@ -1449,41 +896,14 @@ void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
}
|
}
|
||||||
|
|
||||||
write_end:
|
write_end:
|
||||||
hts_stop_clock();
|
hitag_cleanup(ledcontrol);
|
||||||
set_tracing(false);
|
|
||||||
lf_finalize(ledcontrol);
|
|
||||||
reply_reason(CMD_LF_HITAGS_WRITE, status, reason, NULL, 0);
|
reply_reason(CMD_LF_HITAGS_WRITE, status, reason, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
|
int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
|
||||||
|
// Setup FPGA and initialize
|
||||||
|
hitag_setup_fpga(FPGA_LF_EDGE_DETECT_READER_FIELD, 127, ledcontrol);
|
||||||
|
|
||||||
StopTicks();
|
|
||||||
|
|
||||||
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
|
||||||
|
|
||||||
// Clean up trace and prepare it for storing frames
|
|
||||||
set_tracing(true);
|
|
||||||
clear_trace();
|
|
||||||
|
|
||||||
if (ledcontrol) LED_D_ON();
|
|
||||||
|
|
||||||
hts_init_clock();
|
|
||||||
|
|
||||||
// Set fpga in edge detect with reader field, we can modulate as reader now
|
|
||||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD);
|
|
||||||
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
|
|
||||||
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
|
|
||||||
|
|
||||||
// Configure output and enable pin that is connected to the FPGA (for modulating)
|
|
||||||
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
|
|
||||||
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
|
|
||||||
|
|
||||||
// Disable modulation at default, which means enable the field
|
|
||||||
LOW(GPIO_SSC_DOUT);
|
|
||||||
|
|
||||||
// UID request standard 00110
|
|
||||||
// UID request Advanced 1100x
|
|
||||||
// UID request FAdvanced 11010
|
|
||||||
protocol_mode = HITAGS_UID_REQ_ADV1;
|
protocol_mode = HITAGS_UID_REQ_ADV1;
|
||||||
uint8_t cmd = protocol_mode;
|
uint8_t cmd = protocol_mode;
|
||||||
|
|
||||||
|
@ -1493,7 +913,7 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
|
||||||
size_t txlen = 0;
|
size_t txlen = 0;
|
||||||
uint8_t tx[HITAG_FRAME_LEN] = { 0x00 };
|
uint8_t tx[HITAG_FRAME_LEN] = { 0x00 };
|
||||||
|
|
||||||
txlen = concatbits(tx, txlen, &cmd, 0, 5);
|
txlen = concatbits(tx, txlen, &cmd, 0, 5, false);
|
||||||
|
|
||||||
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_FIRST, ledcontrol, true);
|
hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_FIRST, ledcontrol, true);
|
||||||
|
|
||||||
|
@ -1511,10 +931,10 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) {
|
||||||
status = PM3_ERFTRANS;
|
status = PM3_ERFTRANS;
|
||||||
}
|
}
|
||||||
|
|
||||||
hts_stop_clock();
|
hitag_cleanup(ledcontrol);
|
||||||
set_tracing(false);
|
if (send_answer) {
|
||||||
lf_finalize(ledcontrol);
|
|
||||||
reply_ng(CMD_LF_HITAGS_UID, status, (uint8_t *)tag.data.pages, sizeof(tag.data.pages));
|
reply_ng(CMD_LF_HITAGS_UID, status, (uint8_t *)tag.data.pages, sizeof(tag.data.pages));
|
||||||
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,9 +996,7 @@ void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol
|
||||||
SpinDelay(2);
|
SpinDelay(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
hts_stop_clock();
|
hitag_cleanup(ledcontrol);
|
||||||
set_tracing(false);
|
|
||||||
lf_finalize(ledcontrol);
|
|
||||||
reply_ng(CMD_LF_HITAGS_TEST_TRACES, PM3_SUCCESS, NULL, 0);
|
reply_ng(CMD_LF_HITAGS_TEST_TRACES, PM3_SUCCESS, NULL, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "hitag.h"
|
#include "hitag.h"
|
||||||
|
|
||||||
void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol);
|
void hts_simulate(bool tag_mem_supplied, int8_t threshold, const uint8_t *data, bool ledcontrol);
|
||||||
void hts_read(const lf_hitag_data_t *payload, bool ledcontrol);
|
void hts_read(const lf_hitag_data_t *payload, bool ledcontrol);
|
||||||
void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol);
|
void hts_write_page(const lf_hitag_data_t *payload, bool ledcontrol);
|
||||||
void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol);
|
void hts_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol);
|
||||||
|
|
539
armsrc/hitag_common.c
Normal file
539
armsrc/hitag_common.c
Normal file
|
@ -0,0 +1,539 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for the text of the license.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Hitag shared functionality
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#include "hitag_common.h"
|
||||||
|
|
||||||
|
#include "proxmark3_arm.h"
|
||||||
|
#include "cmd.h"
|
||||||
|
#include "BigBuf.h"
|
||||||
|
#include "fpgaloader.h"
|
||||||
|
#include "ticks.h"
|
||||||
|
#include "dbprint.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "commonutil.h"
|
||||||
|
#include "hitag2/hitag2_crypto.h"
|
||||||
|
#include "lfadc.h"
|
||||||
|
#include "crc.h"
|
||||||
|
#include "protocols.h"
|
||||||
|
#include "appmain.h" // tearoff_hook()
|
||||||
|
|
||||||
|
uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, combined with TC2 counter for ~47min timing
|
||||||
|
|
||||||
|
static void hitag_stop_clock(void) {
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
|
||||||
|
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
|
||||||
|
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hitag_init_clock(void) {
|
||||||
|
// Enable Peripheral Clock for
|
||||||
|
// Timer Counter 0, used to measure exact timing before answering
|
||||||
|
// Timer Counter 1, used to capture edges of the tag frames
|
||||||
|
// Timer Counter 2, used to log trace time
|
||||||
|
AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2);
|
||||||
|
|
||||||
|
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
|
||||||
|
|
||||||
|
// Disable timer during configuration
|
||||||
|
hitag_stop_clock();
|
||||||
|
|
||||||
|
// TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
|
||||||
|
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
|
||||||
|
|
||||||
|
// TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger,
|
||||||
|
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3)
|
||||||
|
| AT91C_TC_ABETRG // TIOA is used as an external trigger
|
||||||
|
| AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge
|
||||||
|
| AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA
|
||||||
|
| AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA
|
||||||
|
|
||||||
|
// TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers
|
||||||
|
AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
|
||||||
|
|
||||||
|
// Enable and reset counters
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
||||||
|
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
||||||
|
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
||||||
|
|
||||||
|
// Assert a sync signal. This sets all timers to 0 on next active clock edge
|
||||||
|
AT91C_BASE_TCB->TCB_BCR = 1;
|
||||||
|
|
||||||
|
// synchronized startup procedure
|
||||||
|
// In theory, with MCK/32, we shouldn't be waiting longer than 32 instruction statements, right?
|
||||||
|
while (AT91C_BASE_TC0->TC_CV != 0) {
|
||||||
|
}; // wait until TC0 returned to zero
|
||||||
|
|
||||||
|
// reset timestamp
|
||||||
|
timestamp_high = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize FPGA and timer for Hitag operations
|
||||||
|
void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol) {
|
||||||
|
StopTicks();
|
||||||
|
|
||||||
|
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
|
||||||
|
|
||||||
|
// Clean up trace and prepare it for storing frames
|
||||||
|
set_tracing(true);
|
||||||
|
clear_trace();
|
||||||
|
|
||||||
|
if (ledcontrol) LED_D_ON();
|
||||||
|
|
||||||
|
hitag_init_clock();
|
||||||
|
|
||||||
|
// Set fpga in edge detect with/without reader field, we can modulate as reader/tag now
|
||||||
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | conf);
|
||||||
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
|
||||||
|
if (threshold != 127) FpgaSendCommand(FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, threshold);
|
||||||
|
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
|
||||||
|
|
||||||
|
// Configure output and enable pin that is connected to the FPGA (for modulating)
|
||||||
|
AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT;
|
||||||
|
AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT;
|
||||||
|
|
||||||
|
// Disable modulation at default, which means enable the field
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up and finalize Hitag operations
|
||||||
|
void hitag_cleanup(bool ledcontrol) {
|
||||||
|
hitag_stop_clock();
|
||||||
|
set_tracing(false);
|
||||||
|
lf_finalize(ledcontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader functions
|
||||||
|
static void hitag_reader_send_bit(int bit, bool ledcontrol) {
|
||||||
|
// Reset clock for the next bit
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
||||||
|
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
||||||
|
|
||||||
|
if (ledcontrol) LED_A_ON();
|
||||||
|
|
||||||
|
// Binary puls length modulation (BPLM) is used to encode the data stream
|
||||||
|
// This means that a transmission of a one takes longer than that of a zero
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
|
||||||
|
// Wait for 4-10 times the carrier period
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
|
||||||
|
if (bit == 0) {
|
||||||
|
// Zero bit: |_-|
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {};
|
||||||
|
} else {
|
||||||
|
// One bit: |_--|
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ledcontrol) LED_A_OFF();
|
||||||
|
}
|
||||||
|
|
||||||
|
void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof) {
|
||||||
|
// Send SOF (Start of Frame) for Hitag µ if requested
|
||||||
|
if (send_sof) {
|
||||||
|
hitag_reader_send_bit(0, ledcontrol);
|
||||||
|
|
||||||
|
// Reset clock for the code violation
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
||||||
|
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
||||||
|
|
||||||
|
if (ledcontrol) LED_A_ON();
|
||||||
|
|
||||||
|
// SOF is HIGH for HITAG_T_LOW
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
||||||
|
|
||||||
|
// Then LOW for HITAG_T_CODE_VIOLATION
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_CODE_VIOLATION) {};
|
||||||
|
|
||||||
|
if (ledcontrol) LED_A_OFF();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the content of the frame
|
||||||
|
for (size_t i = 0; i < frame_len; i++) {
|
||||||
|
hitag_reader_send_bit(TEST_BIT_MSB(frame, i), ledcontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send EOF
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
||||||
|
while (AT91C_BASE_TC0->TC_CV != 0) {};
|
||||||
|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
|
||||||
|
// Wait for 4-10 times the carrier period
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol,
|
||||||
|
MOD modulation, int sof_bits) {
|
||||||
|
// Reset values for receiving frames
|
||||||
|
memset(rx, 0x00, sizeofrx);
|
||||||
|
*rxlen = 0;
|
||||||
|
|
||||||
|
int lastbit = 1;
|
||||||
|
bool bSkip = true;
|
||||||
|
uint32_t errorCount = 0;
|
||||||
|
bool bStarted = false;
|
||||||
|
uint16_t next_edge_event = AT91C_TC_LDRBS;
|
||||||
|
int double_speed = (modulation == AC4K || modulation == MC8K) ? 2 : 1;
|
||||||
|
|
||||||
|
uint32_t rb_i = 0;
|
||||||
|
uint8_t edges[160] = {0};
|
||||||
|
|
||||||
|
// Skip SOF bits
|
||||||
|
bool sof_received = false;
|
||||||
|
|
||||||
|
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
|
||||||
|
// Check if edge in tag modulation is detected
|
||||||
|
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
|
||||||
|
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
|
||||||
|
|
||||||
|
// only use AT91C_TC_LDRBS falling edge for now
|
||||||
|
if (next_edge_event == AT91C_TC_LDRBS) continue;
|
||||||
|
|
||||||
|
// Retrieve the new timing values
|
||||||
|
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0;
|
||||||
|
edges[rb_i++] = rb;
|
||||||
|
|
||||||
|
// Reset timer every frame, we have to capture the last edge for timing
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
||||||
|
|
||||||
|
if (ledcontrol) LED_B_INV();
|
||||||
|
|
||||||
|
// Capture tag frame (manchester decoding using only falling edges)
|
||||||
|
if (bStarted == false) {
|
||||||
|
if (rb >= HITAG_T_WAIT_RESP) {
|
||||||
|
bStarted = true;
|
||||||
|
|
||||||
|
// Capture tag response timestamp
|
||||||
|
*resptime = TIMESTAMP;
|
||||||
|
|
||||||
|
// We always receive a 'one' first, which has the falling edge after a half period |-_|
|
||||||
|
rx[0] = 0x80;
|
||||||
|
*rxlen = 1;
|
||||||
|
} else {
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle different modulation types
|
||||||
|
if (modulation == AC2K || modulation == AC4K) {
|
||||||
|
// Anticollision Coding
|
||||||
|
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
|
||||||
|
// Anticollision Coding example |--__|--__| (00)
|
||||||
|
lastbit = 0;
|
||||||
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
|
||||||
|
// Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01)
|
||||||
|
lastbit = !lastbit;
|
||||||
|
if (lastbit) SET_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
|
||||||
|
bSkip = !!lastbit;
|
||||||
|
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
|
||||||
|
// Anticollision Coding example |-_-_| (1)
|
||||||
|
if (bSkip == false) {
|
||||||
|
lastbit = 1;
|
||||||
|
SET_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bSkip = !bSkip;
|
||||||
|
} else {
|
||||||
|
// Ignore weird value, is to small to mean anything
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Manchester coding (MC4K, MC8K)
|
||||||
|
if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) {
|
||||||
|
// Manchester coding example |-_|_-|-_| (101)
|
||||||
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
|
||||||
|
SET_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
} else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) {
|
||||||
|
// Manchester coding example |_-|...|_-|-_| (0...01)
|
||||||
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
|
||||||
|
// We have to skip this half period at start and add the 'one' the second time
|
||||||
|
if (bSkip == false) {
|
||||||
|
SET_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastbit = !lastbit;
|
||||||
|
bSkip = !bSkip;
|
||||||
|
} else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) {
|
||||||
|
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
|
||||||
|
// bit is same as last bit
|
||||||
|
if (lastbit) SET_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
} else {
|
||||||
|
// Ignore weird value, is to small to mean anything
|
||||||
|
errorCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle SOF bits
|
||||||
|
if (sof_received == false && *rxlen >= sof_bits) {
|
||||||
|
// Check if SOF is valid (all bits should be 1)
|
||||||
|
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
|
||||||
|
if (sof_bits == 4) {
|
||||||
|
sof_bits = 3;
|
||||||
|
// Hitag µ is LSB first 0b110
|
||||||
|
if ((rx[0] & 0xE0) != 0xC0) {
|
||||||
|
DBG Dbprintf("Warning, SOF is invalid rx[0]: 0x%02X", rx[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DBG DbpString("Warning, not all bits of SOF are 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*rxlen -= sof_bits;
|
||||||
|
uint8_t tmp = rx[0];
|
||||||
|
rx[0] = 0x00;
|
||||||
|
for (size_t i = 0; i < *rxlen; i++) {
|
||||||
|
if (TEST_BIT_MSB(&tmp, sof_bits + i)) SET_BIT_MSB(rx, i);
|
||||||
|
}
|
||||||
|
// DBG Dbprintf("after sof_bits rxlen: %d rx[0]: 0x%02X", *rxlen, rx[0]);
|
||||||
|
sof_received = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we saw over 100 weird values break it probably isn't hitag...
|
||||||
|
if (errorCount > 100 || (*rxlen) / 8 >= sizeofrx) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can break this loop if we received the last bit from a frame
|
||||||
|
// max periods between 2 falling edge
|
||||||
|
// RTF AC64 |--__|--__| (00) 64 * T0
|
||||||
|
// RTF MC32 |_-|-_|_-| (010) 48 * T0
|
||||||
|
if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) {
|
||||||
|
if (bStarted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG {
|
||||||
|
Dbprintf("RX %i:%02X.. resptime:%i edges:", *rxlen, rx[0], *resptime);
|
||||||
|
Dbhexdump(rb_i, edges, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag functions - depends on modulation type
|
||||||
|
static void hitag_tag_send_bit(int bit, MOD modulation, bool ledcontrol) {
|
||||||
|
// Reset clock for the next bit
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
||||||
|
|
||||||
|
if (ledcontrol) LED_A_ON();
|
||||||
|
|
||||||
|
switch (modulation) {
|
||||||
|
case AC2K: {
|
||||||
|
if (bit == 0) {
|
||||||
|
// AC Coding --__
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
|
||||||
|
} else {
|
||||||
|
// AC coding -_-_
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
||||||
|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 48) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 64) {};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AC4K: {
|
||||||
|
if (bit == 0) {
|
||||||
|
// AC Coding --__
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {};
|
||||||
|
} else {
|
||||||
|
// AC coding -_-_
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
||||||
|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 24) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MC4K: {
|
||||||
|
if (bit == 0) {
|
||||||
|
// Manchester: Unloaded, then loaded |__--|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
||||||
|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
||||||
|
} else {
|
||||||
|
// Manchester: Loaded, then unloaded |--__|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 32) {};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MC8K: {
|
||||||
|
if (bit == 0) {
|
||||||
|
// Manchester: Unloaded, then loaded |__--|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
||||||
|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
||||||
|
} else {
|
||||||
|
// Manchester: Loaded, then unloaded |--__|
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 8) {};
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 16) {};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ledcontrol) LED_A_OFF();
|
||||||
|
}
|
||||||
|
|
||||||
|
void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow) {
|
||||||
|
uint16_t next_edge_event = AT91C_TC_LDRBS;
|
||||||
|
uint8_t edges[160] = {0};
|
||||||
|
uint32_t rb_i = 0;
|
||||||
|
|
||||||
|
// Receive frame, watch for at most T0*EOF periods
|
||||||
|
while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) {
|
||||||
|
|
||||||
|
// Check if edge in modulation is detected
|
||||||
|
if (AT91C_BASE_TC1->TC_SR & next_edge_event) {
|
||||||
|
next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS);
|
||||||
|
|
||||||
|
// only use AT91C_TC_LDRBS falling edge for now
|
||||||
|
if (next_edge_event == AT91C_TC_LDRBS) continue;
|
||||||
|
|
||||||
|
// Retrieve the new timing values
|
||||||
|
uint32_t rb = AT91C_BASE_TC1->TC_RB / T0 + *overflow;
|
||||||
|
*overflow = 0;
|
||||||
|
|
||||||
|
edges[rb_i++] = rb;
|
||||||
|
|
||||||
|
if (ledcontrol) LED_B_INV();
|
||||||
|
|
||||||
|
// Capture reader cmd start timestamp
|
||||||
|
if (*start_time == 0) {
|
||||||
|
*start_time = TIMESTAMP - HITAG_T_LOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture reader frame
|
||||||
|
if (rb >= HITAG_T_STOP) {
|
||||||
|
// Hitag µ SOF
|
||||||
|
if (*rxlen != 0 && *rxlen != 1) {
|
||||||
|
// DBG DbpString("weird0?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*rxlen = 0;
|
||||||
|
} else if (rb >= HITAG_T_1_MIN) {
|
||||||
|
// '1' bit
|
||||||
|
SET_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
} else if (rb >= HITAG_T_0_MIN) {
|
||||||
|
// '0' bit
|
||||||
|
// CLEAR_BIT_MSB(rx, *rxlen);
|
||||||
|
(*rxlen)++;
|
||||||
|
} else {
|
||||||
|
// Ignore weird value, is too small to mean anything
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ledcontrol) LED_B_OFF();
|
||||||
|
|
||||||
|
DBG if (rb_i) {
|
||||||
|
Dbprintf("RX %i bits.. start_time:%i edges:", *rxlen, *start_time);
|
||||||
|
Dbhexdump(rb_i, edges, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol) {
|
||||||
|
// The beginning of the frame is hidden in some high level; pause until our bits will have an effect
|
||||||
|
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
|
||||||
|
HIGH(GPIO_SSC_DOUT);
|
||||||
|
|
||||||
|
switch (modulation) {
|
||||||
|
case AC4K:
|
||||||
|
case MC8K: {
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; // FADV
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AC2K:
|
||||||
|
case MC4K: {
|
||||||
|
while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; // STD + ADV
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SOF - send start of frame
|
||||||
|
for (size_t i = 0; i < sof_bits; i++) {
|
||||||
|
if (sof_bits == 4 && i == 3) {
|
||||||
|
// Hitag µ SOF is 110
|
||||||
|
hitag_tag_send_bit(0, modulation, ledcontrol);
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
hitag_tag_send_bit(1, modulation, ledcontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the content of the frame
|
||||||
|
for (size_t i = 0; i < frame_len; i++) {
|
||||||
|
hitag_tag_send_bit(TEST_BIT_MSB(frame, i), modulation, ledcontrol);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOW(GPIO_SSC_DOUT);
|
||||||
|
}
|
57
armsrc/hitag_common.h
Normal file
57
armsrc/hitag_common.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef HITAG_COMMON_H
|
||||||
|
#define HITAG_COMMON_H
|
||||||
|
|
||||||
|
#include "hitag.h"
|
||||||
|
|
||||||
|
// Sam7s has several timers, we will use the source TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK)
|
||||||
|
// TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, Timer is running at 48MHz/32 = 1500 KHz
|
||||||
|
// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
|
||||||
|
// T0 = TIMER_CLOCK3 / 125000 = 12
|
||||||
|
|
||||||
|
#define T0 12
|
||||||
|
|
||||||
|
#define HITAG_FRAME_LEN 20
|
||||||
|
|
||||||
|
// TC0 and TC1 are 16-bit counters and will overflow after 5461 * T0
|
||||||
|
// Ensure not to set these timings above 5461 (~43ms) when comparing without considering overflow, as they will never reach that value.
|
||||||
|
|
||||||
|
#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */
|
||||||
|
#define HITAG_T_0 20 /* T[0] should be 18..22 */
|
||||||
|
#define HITAG_T_1 28 /* T[1] should be 26..32 */
|
||||||
|
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
|
||||||
|
#define HITAG_T_1_MIN 25 /* T[1] should be 26..32 */
|
||||||
|
#define HITAG_T_STOP 36 /* T_EOF should be > 36 */
|
||||||
|
#define HITAG_T_CODE_VIOLATION 36 /* Hitag µ TFcv should be 34..38 */
|
||||||
|
#define HITAG_T_EOF 80 /* T_EOF should be > 36 */
|
||||||
|
|
||||||
|
#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */
|
||||||
|
#define HITAG_T_WAIT_SC 200 /* T_wsc should be 90..5000 */
|
||||||
|
#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */
|
||||||
|
#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */
|
||||||
|
|
||||||
|
#define HITAG_T_TAG_ONE_HALF_PERIOD 10
|
||||||
|
#define HITAG_T_TAG_TWO_HALF_PERIOD 25
|
||||||
|
#define HITAG_T_TAG_THREE_HALF_PERIOD 41
|
||||||
|
#define HITAG_T_TAG_FOUR_HALF_PERIOD 57
|
||||||
|
|
||||||
|
#define HITAG_T_TAG_HALF_PERIOD 16
|
||||||
|
#define HITAG_T_TAG_FULL_PERIOD 32
|
||||||
|
|
||||||
|
#define HITAG_T_TAG_CAPTURE_ONE_HALF 13
|
||||||
|
#define HITAG_T_TAG_CAPTURE_TWO_HALF 25
|
||||||
|
#define HITAG_T_TAG_CAPTURE_THREE_HALF 41
|
||||||
|
#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57
|
||||||
|
|
||||||
|
extern uint16_t timestamp_high;
|
||||||
|
#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
|
||||||
|
|
||||||
|
// Common hitag functions
|
||||||
|
void hitag_setup_fpga(uint16_t conf, uint8_t threshold, bool ledcontrol);
|
||||||
|
void hitag_cleanup(bool ledcontrol);
|
||||||
|
void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol, bool send_sof);
|
||||||
|
void hitag_reader_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *resptime, bool ledcontrol, MOD modulation,
|
||||||
|
int sof_bits);
|
||||||
|
void hitag_tag_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint32_t *start_time, bool ledcontrol, int *overflow);
|
||||||
|
void hitag_tag_send_frame(const uint8_t *frame, size_t frame_len, int sof_bits, MOD modulation, bool ledcontrol);
|
||||||
|
|
||||||
|
#endif
|
897
armsrc/hitagu.c
Normal file
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 -2; // Read UID failed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the UID from the response
|
||||||
|
concatbits(tag.uid, 0, rx, 1, 48, false);
|
||||||
|
|
||||||
|
// 2. Send GET SYSTEM INFORMATION command
|
||||||
|
command = HITAGU_CMD_SYSINFO;
|
||||||
|
txlen = 0; // Reset txlen for the new command
|
||||||
|
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
|
||||||
|
txlen = concatbits(tx, txlen, &command, 0, 6, true);
|
||||||
|
|
||||||
|
// Append CRC-16 (16 bits)
|
||||||
|
crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
// Send the GET SYSTEM INFORMATION command and receive the response
|
||||||
|
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
concatbits(&tag.icr, 0, rx, 9, 8, false);
|
||||||
|
|
||||||
|
// Check if the response is valid
|
||||||
|
if (rxlen < 1 + 16 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
|
||||||
|
// 8265 bug? sometile lost Data field first bit
|
||||||
|
DBG Dbprintf("Get System Information command failed! %i", rxlen);
|
||||||
|
return -3; // Get System Information failed
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Read config block
|
||||||
|
command = HITAGU_CMD_READ_MULTIPLE_BLOCK;
|
||||||
|
txlen = 0; // Reset txlen for the new command
|
||||||
|
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
|
||||||
|
txlen = concatbits(tx, txlen, &command, 0, 6, true);
|
||||||
|
|
||||||
|
// Add block address
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&"\xFF", 0, 8, true);
|
||||||
|
|
||||||
|
// Add number of blocks, 0 means 1 block
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true);
|
||||||
|
|
||||||
|
// Append CRC-16 (16 bits)
|
||||||
|
crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
// Send read command and receive response
|
||||||
|
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
// Check if the response is valid
|
||||||
|
if (rxlen < 1 + 32 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
|
||||||
|
DBG Dbprintf("Read config block command failed! %i", rxlen);
|
||||||
|
return -3; // Read config block failed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the config block from the response
|
||||||
|
concatbits(tag.config.asBytes, 0, rx, 1, 32, false);
|
||||||
|
reverse_arraybytes(tag.config.asBytes, HITAGU_BLOCK_SIZE);
|
||||||
|
|
||||||
|
// 4. Send LOGIN command if necessary
|
||||||
|
if (payload && (payload->cmd == HTUF_82xx || payload->cmd == HTUF_PASSWORD)) {
|
||||||
|
command = HITAGU_CMD_LOGIN; // Set command for login
|
||||||
|
txlen = 0; // Reset txlen for the new command
|
||||||
|
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
|
||||||
|
txlen = concatbits(tx, txlen, &command, 0, 6, true);
|
||||||
|
|
||||||
|
txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false);
|
||||||
|
|
||||||
|
// Append CRC-16 (16 bits)
|
||||||
|
crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
// Send the LOGIN command and receive the response
|
||||||
|
htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
// Check if login succeeded
|
||||||
|
if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
|
||||||
|
DBG Dbprintf("Login command failed! %i", rxlen);
|
||||||
|
return -4; // Login failed
|
||||||
|
} else {
|
||||||
|
DBG DbpString("Login successful");
|
||||||
|
}
|
||||||
|
|
||||||
|
// flags |= HITAGU_FLAG_ADR;
|
||||||
|
// command = HITAGU_CMD_LOGIN; // Set command for login
|
||||||
|
// txlen = 0; // Reset txlen for the new command
|
||||||
|
// txlen = concatbits(tx, txlen, &flags, 0, 5, true);
|
||||||
|
// txlen = concatbits(tx, txlen, &command, 0, 6, true);
|
||||||
|
|
||||||
|
// txlen = concatbits(tx, txlen, payload->uid, 0, HITAGU_UID_SIZE * 8, false);
|
||||||
|
// txlen = concatbits(tx, txlen, payload->pwd, 0, HITAG_PASSWORD_SIZE * 8, false);
|
||||||
|
|
||||||
|
// // Append CRC-16 (16 bits)
|
||||||
|
// crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
// txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
// // Send the LOGIN command and receive the response
|
||||||
|
// htu_reader_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
// // Check if login succeeded
|
||||||
|
// if (rxlen < 1 + 16 || Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
|
||||||
|
// DBG Dbprintf("Login command failed! %i", rxlen);
|
||||||
|
// return -3; // Login failed
|
||||||
|
// } else {
|
||||||
|
// DbpString("Login successful");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all commands are successful, update the tag's state
|
||||||
|
update_tag_max_page(); // Update max_page based on the new configuration
|
||||||
|
|
||||||
|
return 0; // Selection successful
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads the UID of a Hitag µ tag using the INVENTORY command
|
||||||
|
*/
|
||||||
|
int htu_read_uid(uint64_t *uid, bool ledcontrol, bool send_answer) {
|
||||||
|
// Initialize response
|
||||||
|
uint8_t rx[HITAG_FRAME_LEN] = {0x00};
|
||||||
|
uint8_t tx[HITAG_FRAME_LEN] = {0x00};
|
||||||
|
int status = PM3_SUCCESS;
|
||||||
|
|
||||||
|
// Use htu_select_tag to select the card and retrieve UID
|
||||||
|
int reason = htu_select_tag(NULL, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
|
||||||
|
if (reason != 0) {
|
||||||
|
DBG DbpString("Error: htu_read_uid Failed to select tag");
|
||||||
|
status = PM3_ERFTRANS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG Dbprintf("HitagU UID: %02X%02X%02X%02X%02X%02X", tag.uid[0], tag.uid[1], tag.uid[2], tag.uid[3], tag.uid[4], tag.uid[5]);
|
||||||
|
|
||||||
|
if (uid) {
|
||||||
|
*uid = MemBeToUint6byte(tag.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
hitag_cleanup(ledcontrol);
|
||||||
|
|
||||||
|
if (send_answer) {
|
||||||
|
// Send UID
|
||||||
|
reply_reason(CMD_LF_HITAGU_UID, status, reason, tag.uid, sizeof(tag.uid));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset authentication state
|
||||||
|
RESET_AUTHENTICATION();
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads a Hitag µ tag
|
||||||
|
*/
|
||||||
|
void htu_read(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
|
size_t rxlen = 0;
|
||||||
|
uint8_t rx[HITAG_FRAME_LEN] = {0x00};
|
||||||
|
size_t txlen = 0;
|
||||||
|
uint8_t tx[HITAG_FRAME_LEN] = {0x00};
|
||||||
|
int status = PM3_SUCCESS;
|
||||||
|
|
||||||
|
// DBG {
|
||||||
|
// DbpString("htu_read");
|
||||||
|
// Dbprintf("payload->page: %d", payload->page);
|
||||||
|
// Dbprintf("payload->page_count: %d", payload->page_count);
|
||||||
|
// Dbprintf("payload->cmd: %d", payload->cmd);
|
||||||
|
// Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2],
|
||||||
|
// payload->uid[3], payload->uid[4], payload->uid[5]);
|
||||||
|
// Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2],
|
||||||
|
// payload->key[3], payload->key[4], payload->key[5]);
|
||||||
|
// Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Use htu_select_tag to select the card and retrieve UID
|
||||||
|
int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
|
||||||
|
if (reason != 0) {
|
||||||
|
DbpString("Error: htu_read Failed to select tag");
|
||||||
|
status = PM3_ERFTRANS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
lf_htu_read_response_t card = {
|
||||||
|
.icr = tag.icr,
|
||||||
|
};
|
||||||
|
memcpy(card.uid, tag.uid, HITAGU_UID_SIZE);
|
||||||
|
memcpy(card.config_page.asBytes, tag.config.asBytes, HITAGU_BLOCK_SIZE);
|
||||||
|
|
||||||
|
uint8_t page_count_index = payload->page_count - 1;
|
||||||
|
|
||||||
|
if (payload->page_count == 0) {
|
||||||
|
page_count_index = tag.max_page - payload->page - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Process the actual command
|
||||||
|
// Add flags (5 bits)
|
||||||
|
uint8_t flags = HITAGU_FLAG_CRCT;
|
||||||
|
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
|
||||||
|
|
||||||
|
// Read command format: <flags> <command> [UID] <block address> <number of blocks> [CRC-16]
|
||||||
|
|
||||||
|
// Add command (6 bits)
|
||||||
|
uint8_t command = HITAGU_CMD_READ_MULTIPLE_BLOCK;
|
||||||
|
txlen = concatbits(tx, txlen, &command, 0, 6, true);
|
||||||
|
|
||||||
|
// The 8265 chip has known issues when reading multiple blocks:
|
||||||
|
// - page_count = 1: Works correctly
|
||||||
|
// - page_count >= 2: Data field is left shifted by 1 bit
|
||||||
|
// - page_count = 2 or page+page_count exceeds valid page: Data field has an extra '1' bit
|
||||||
|
// - page_count = 3,4: Data field last bit is always '1'
|
||||||
|
// - page_count = 5: CRC is not appended
|
||||||
|
// - page_count >= 6: May cause next command to have no response
|
||||||
|
// Workaround: Read one block at a time
|
||||||
|
if (payload->mode == 0 /**for debug */
|
||||||
|
&& (payload->cmd == HTUF_82xx || tag.icr == HITAGU_ICR_8265 || !memcmp(tag.uid, "\x00\x00\x00\x00\x00\x00", 6))) {
|
||||||
|
|
||||||
|
uint8_t page_addr;
|
||||||
|
for (int i = 0; i <= page_count_index; i++) {
|
||||||
|
page_addr = payload->page + i;
|
||||||
|
|
||||||
|
txlen = 5 + 6; // restore txlen for the new command
|
||||||
|
txlen = concatbits(tx, txlen, &page_addr, 0, 8, true);
|
||||||
|
|
||||||
|
// Add number of blocks, 0 means 1 block
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&"\x00", 0, 8, true);
|
||||||
|
|
||||||
|
// Append CRC-16 (16 bits)
|
||||||
|
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
// Send read command and receive response
|
||||||
|
htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
|
||||||
|
DBG Dbprintf("Error: response CRC invalid");
|
||||||
|
card.pages_reason[i] = -6;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check response
|
||||||
|
if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) {
|
||||||
|
DbpString("Error: invalid response received after read command");
|
||||||
|
card.pages_reason[i] = -7;
|
||||||
|
} else {
|
||||||
|
DBG Dbprintf("Read successful, response: %d bits", rxlen);
|
||||||
|
// todo: For certain pages, update our cached data
|
||||||
|
card.pages_reason[i] = 1;
|
||||||
|
concatbits(card.pages[i], 0, rx, 1, HITAGU_BLOCK_SIZE * 8, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
txlen = concatbits(tx, txlen, &payload->page, 0, 8, true);
|
||||||
|
|
||||||
|
// Add number of blocks, 0 means 1 block
|
||||||
|
txlen = concatbits(tx, txlen, &page_count_index, 0, 8, true);
|
||||||
|
|
||||||
|
// Append CRC-16 (16 bits)
|
||||||
|
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
// Send read command and receive response
|
||||||
|
htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
if (flags & HITAGU_FLAG_CRCT && Crc16(rx, rxlen, 0, CRC16_POLY_CCITT, false, false) != 0) {
|
||||||
|
DBG Dbprintf("Error: response CRC invalid");
|
||||||
|
status = PM3_ERFTRANS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check response
|
||||||
|
if (rxlen < 1 + HITAGU_BLOCK_SIZE * 8 + (flags & HITAGU_FLAG_CRCT ? 16 : 0)) {
|
||||||
|
DbpString("Error: invalid response received after read command");
|
||||||
|
status = PM3_ERFTRANS;
|
||||||
|
} else {
|
||||||
|
DBG Dbprintf("Read successful, response: %d bits", rxlen);
|
||||||
|
// todo: For certain pages, update our cached data
|
||||||
|
concatbits((uint8_t *)card.pages, 0, rx, 1, rxlen - 1 - 16, false);
|
||||||
|
for (int i = 0; i < (rxlen - 1 - 16) / (HITAGU_BLOCK_SIZE * 8); i++) {
|
||||||
|
card.pages_reason[i] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
hitag_cleanup(ledcontrol);
|
||||||
|
// Send status to client
|
||||||
|
reply_reason(CMD_LF_HITAGU_READ, status, reason, (uint8_t *)&card, sizeof(card));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes a page to a Hitag µ tag
|
||||||
|
*/
|
||||||
|
void htu_write_page(const lf_hitag_data_t *payload, bool ledcontrol) {
|
||||||
|
size_t rxlen = 0;
|
||||||
|
uint8_t rx[HITAG_FRAME_LEN] = {0x00};
|
||||||
|
size_t txlen = 0;
|
||||||
|
uint8_t tx[HITAG_FRAME_LEN] = {0x00};
|
||||||
|
int status = PM3_SUCCESS;
|
||||||
|
|
||||||
|
// DBG {
|
||||||
|
// DbpString("htu_write_page");
|
||||||
|
// Dbprintf("payload->page: %d", payload->page);
|
||||||
|
// Dbprintf("payload->data: %02X%02X%02X%02X", payload->data[0], payload->data[1], payload->data[2], payload->data[3]);
|
||||||
|
// Dbprintf("payload->cmd: %d", payload->cmd);
|
||||||
|
// Dbprintf("payload->uid: %02X%02X%02X%02X%02X%02X", payload->uid[0], payload->uid[1], payload->uid[2], payload->uid[3], payload->uid[4], payload->uid[5]);
|
||||||
|
// Dbprintf("payload->key: %02X%02X%02X%02X%02X%02X", payload->key[0], payload->key[1], payload->key[2], payload->key[3], payload->key[4], payload->key[5]);
|
||||||
|
// Dbprintf("payload->pwd: %02X%02X%02X%02X", payload->pwd[0], payload->pwd[1], payload->pwd[2], payload->pwd[3]);
|
||||||
|
// Dbprintf("payload->mode: %d", payload->mode);
|
||||||
|
// }
|
||||||
|
|
||||||
|
int reason = htu_select_tag(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
|
||||||
|
if (reason != 0) {
|
||||||
|
status = PM3_ERFTRANS;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Send write command
|
||||||
|
uint8_t flags = HITAGU_FLAG_CRCT;
|
||||||
|
|
||||||
|
// Add flags (5 bits) for write operation
|
||||||
|
txlen = concatbits(tx, txlen, &flags, 0, 5, true);
|
||||||
|
|
||||||
|
// Add write command (6 bits)
|
||||||
|
uint8_t command = HITAGU_CMD_WRITE_SINGLE_BLOCK;
|
||||||
|
txlen = concatbits(tx, txlen, &command, 0, 6, true);
|
||||||
|
|
||||||
|
// Add page address (8 bits)
|
||||||
|
txlen = concatbits(tx, txlen, &payload->page, 0, 8, true);
|
||||||
|
|
||||||
|
// Add data to write (32 bits)
|
||||||
|
txlen = concatbits(tx, txlen, payload->data, 0, 32, false);
|
||||||
|
|
||||||
|
// Append CRC-16 (16 bits)
|
||||||
|
uint16_t crc = Crc16(tx, txlen, 0, CRC16_POLY_CCITT, false, true);
|
||||||
|
txlen = concatbits(tx, txlen, (uint8_t *)&crc, 0, 16, true);
|
||||||
|
|
||||||
|
DBG Dbprintf("Writing to page 0x%02X", payload->page);
|
||||||
|
|
||||||
|
// Send write command and receive response
|
||||||
|
htu_reader_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, MC4K, HTU_SOF_BITS);
|
||||||
|
|
||||||
|
// Check response
|
||||||
|
if (payload->cmd == HTUF_82xx && rxlen == 0) {
|
||||||
|
// 8265 bug? no response on successful write command
|
||||||
|
reason = 0;
|
||||||
|
status = PM3_ENODATA;
|
||||||
|
} else if (rxlen != 1 + 16) {
|
||||||
|
DbpString("Error: htu_write_page No valid response received after write command");
|
||||||
|
reason = -5;
|
||||||
|
status = PM3_ERFTRANS;
|
||||||
|
} else {
|
||||||
|
DBG Dbprintf("Write successful, response: %d bits", rxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
hitag_cleanup(ledcontrol);
|
||||||
|
reply_reason(CMD_LF_HITAGU_WRITE, status, reason, NULL, 0);
|
||||||
|
}
|
30
armsrc/hitagu.h
Normal file
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
|
|
@ -1726,24 +1726,48 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
|
||||||
}
|
}
|
||||||
p_response = NULL;
|
p_response = NULL;
|
||||||
} else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE
|
} else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE
|
||||||
|
|
||||||
|
p_response = NULL;
|
||||||
|
|
||||||
// cmd + block + 4 bytes data + 2 bytes crc
|
// cmd + block + 4 bytes data + 2 bytes crc
|
||||||
if (CheckCrc14A(receivedCmd, len)) {
|
if (CheckCrc14A(receivedCmd, len)) {
|
||||||
|
|
||||||
uint8_t block = receivedCmd[1];
|
uint8_t block = receivedCmd[1];
|
||||||
|
|
||||||
|
// sanity checks
|
||||||
if (block > pages) {
|
if (block > pages) {
|
||||||
// send NACK 0x0 == invalid argument
|
// send NACK 0x0, invalid argument
|
||||||
EmSend4bit(CARD_NACK_IV);
|
EmSend4bit(CARD_NACK_IV);
|
||||||
} else {
|
goto jump;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OTP sanity check
|
||||||
|
if (block == 0x03) {
|
||||||
|
|
||||||
|
uint8_t orig[4] = {0};
|
||||||
|
emlGet(orig, 12 + MFU_DUMP_PREFIX_LENGTH, 4);
|
||||||
|
|
||||||
|
bool risky = false;
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
risky |= orig[i] & ~receivedCmd[2 + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (risky) {
|
||||||
|
EmSend4bit(CARD_NACK_IV);
|
||||||
|
goto jump;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// first blocks of emu are header
|
// first blocks of emu are header
|
||||||
emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4);
|
emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4);
|
||||||
// send ACK
|
// send ACK
|
||||||
EmSend4bit(CARD_ACK);
|
EmSend4bit(CARD_ACK);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// send NACK 0x1 == crc/parity error
|
// send NACK 0x1 == crc/parity error
|
||||||
EmSend4bit(CARD_NACK_PA);
|
EmSend4bit(CARD_NACK_PA);
|
||||||
}
|
}
|
||||||
p_response = NULL;
|
goto jump;
|
||||||
} else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) {
|
} else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) {
|
||||||
// cmd + block + 2 bytes crc
|
// cmd + block + 2 bytes crc
|
||||||
if (CheckCrc14A(receivedCmd, len)) {
|
if (CheckCrc14A(receivedCmd, len)) {
|
||||||
|
@ -1990,6 +2014,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin
|
||||||
|
|
||||||
// Count number of other messages after a halt
|
// Count number of other messages after a halt
|
||||||
// if (order != ORDER_WUPA && lastorder == ORDER_HALTED) { happened2++; }
|
// if (order != ORDER_WUPA && lastorder == ORDER_HALTED) { happened2++; }
|
||||||
|
jump:
|
||||||
|
|
||||||
cmdsRecvd++;
|
cmdsRecvd++;
|
||||||
|
|
||||||
|
@ -3101,10 +3126,12 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u
|
||||||
size_t len = ReaderReceive(data, data_len, parity_array);
|
size_t len = ReaderReceive(data, data_len, parity_array);
|
||||||
uint8_t *data_bytes = (uint8_t *) data;
|
uint8_t *data_bytes = (uint8_t *) data;
|
||||||
|
|
||||||
if (!len) {
|
if (len == 0) {
|
||||||
BigBuf_free();
|
BigBuf_free();
|
||||||
return 0; // DATA LINK ERROR
|
return 0; // DATA LINK ERROR
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
|
||||||
// S-Block WTX
|
// S-Block WTX
|
||||||
while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
|
while (len && ((data_bytes[0] & 0xF2) == 0xF2)) {
|
||||||
uint32_t save_iso14a_timeout = iso14a_get_timeout();
|
uint32_t save_iso14a_timeout = iso14a_get_timeout();
|
||||||
|
@ -3118,6 +3145,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u
|
||||||
// transmit S-Block
|
// transmit S-Block
|
||||||
ReaderTransmit(data_bytes, len, NULL);
|
ReaderTransmit(data_bytes, len, NULL);
|
||||||
// retrieve the result again (with increased timeout)
|
// retrieve the result again (with increased timeout)
|
||||||
|
data_bytes[0] = 0x00;
|
||||||
len = ReaderReceive(data, data_len, parity_array);
|
len = ReaderReceive(data, data_len, parity_array);
|
||||||
data_bytes = data;
|
data_bytes = data;
|
||||||
// restore timeout
|
// restore timeout
|
||||||
|
@ -3144,8 +3172,6 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len) {
|
if (len) {
|
||||||
// cut frame byte
|
// cut frame byte
|
||||||
len -= 1;
|
len -= 1;
|
||||||
|
@ -3226,7 +3252,7 @@ void ReaderIso14443a(PacketCommandNG *c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((param & ISO14A_APDU) == ISO14A_APDU) {
|
if ((param & ISO14A_APDU) == ISO14A_APDU) {
|
||||||
uint8_t res;
|
uint8_t res = 0;
|
||||||
arg0 = iso14_apdu(
|
arg0 = iso14_apdu(
|
||||||
cmd,
|
cmd,
|
||||||
len,
|
len,
|
||||||
|
|
|
@ -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
|
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
|
New timer implementation in ticks.c, which is used in LFOPS.c
|
||||||
1 μs = 1.5 ticks
|
1 µs = 1.5 ticks
|
||||||
1 fc = 8 μs = 12 ticks
|
1 fc = 8 µs = 12 ticks
|
||||||
|
|
||||||
Terms you find in different datasheets and how they match.
|
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
|
Note about HITAG timing
|
||||||
Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
|
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.
|
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.
|
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.
|
RDV4 LF antenna has high voltage and the drop of power when turning off the rf field takes about 1-2 TC longer.
|
||||||
|
|
|
@ -294,13 +294,13 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) {
|
||||||
|
|
||||||
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
|
if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) {
|
||||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
|
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
|
||||||
reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT, NULL, 0);
|
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!mifare_ultra_aes_auth(keyno, keybytes)) {
|
if (!mifare_ultra_aes_auth(keyno, keybytes)) {
|
||||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed");
|
if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed");
|
||||||
reply_ng(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT, NULL, 0);
|
OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -483,7 +483,7 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
|
||||||
// send & receive
|
// send & receive
|
||||||
len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL);
|
len = mifare_sendcmd(MIFARE_ULAES_AUTH_2, enc_rnd_ab, sizeof(enc_rnd_ab), resp, sizeof(resp), respPar, NULL);
|
||||||
if (len != 19) {
|
if (len != 19) {
|
||||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
|
if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +492,7 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) {
|
||||||
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(random_b), IV, resp + 1, random_b);
|
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(random_b), IV, resp + 1, random_b);
|
||||||
|
|
||||||
if (memcmp(random_b, random_a, 16) != 0) {
|
if (memcmp(random_b, random_a, 16) != 0) {
|
||||||
if (g_dbglevel >= DBG_ERROR) Dbprintf("failed authentication");
|
if (g_dbglevel >= DBG_INFO) Dbprintf("failed authentication");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,12 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) {
|
||||||
AT91C_BASE_SSC->SSC_THR = resp[i++];
|
AT91C_BASE_SSC->SSC_THR = resp[i++];
|
||||||
FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
|
FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
||||||
|
b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR);
|
||||||
|
(void)b;
|
||||||
|
}
|
||||||
|
if (BUTTON_PRESS()) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the FPGA Delay Queue is empty
|
// Ensure that the FPGA Delay Queue is empty
|
||||||
|
|
|
@ -380,6 +380,7 @@ set (TARGET_SOURCES
|
||||||
${PM3_ROOT}/client/src/cmdlfhid.c
|
${PM3_ROOT}/client/src/cmdlfhid.c
|
||||||
${PM3_ROOT}/client/src/cmdlfhitag.c
|
${PM3_ROOT}/client/src/cmdlfhitag.c
|
||||||
${PM3_ROOT}/client/src/cmdlfhitaghts.c
|
${PM3_ROOT}/client/src/cmdlfhitaghts.c
|
||||||
|
${PM3_ROOT}/client/src/cmdlfhitagu.c
|
||||||
${PM3_ROOT}/client/src/cmdlfidteck.c
|
${PM3_ROOT}/client/src/cmdlfidteck.c
|
||||||
${PM3_ROOT}/client/src/cmdlfindala.c
|
${PM3_ROOT}/client/src/cmdlfindala.c
|
||||||
${PM3_ROOT}/client/src/cmdlfio.c
|
${PM3_ROOT}/client/src/cmdlfio.c
|
||||||
|
|
|
@ -660,6 +660,7 @@ SRCS = mifare/aiddesfire.c \
|
||||||
cmdlfhid.c \
|
cmdlfhid.c \
|
||||||
cmdlfhitag.c \
|
cmdlfhitag.c \
|
||||||
cmdlfhitaghts.c \
|
cmdlfhitaghts.c \
|
||||||
|
cmdlfhitagu.c \
|
||||||
cmdlfidteck.c \
|
cmdlfidteck.c \
|
||||||
cmdlfindala.c \
|
cmdlfindala.c \
|
||||||
cmdlfio.c \
|
cmdlfio.c \
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
# Copyright (C) Jonathan Westhues, Mar 2006
|
|
||||||
# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -381,6 +380,7 @@ set (TARGET_SOURCES
|
||||||
${PM3_ROOT}/client/src/cmdlfhid.c
|
${PM3_ROOT}/client/src/cmdlfhid.c
|
||||||
${PM3_ROOT}/client/src/cmdlfhitag.c
|
${PM3_ROOT}/client/src/cmdlfhitag.c
|
||||||
${PM3_ROOT}/client/src/cmdlfhitaghts.c
|
${PM3_ROOT}/client/src/cmdlfhitaghts.c
|
||||||
|
${PM3_ROOT}/client/src/cmdlfhitagu.c
|
||||||
${PM3_ROOT}/client/src/cmdlfidteck.c
|
${PM3_ROOT}/client/src/cmdlfidteck.c
|
||||||
${PM3_ROOT}/client/src/cmdlfindala.c
|
${PM3_ROOT}/client/src/cmdlfindala.c
|
||||||
${PM3_ROOT}/client/src/cmdlfio.c
|
${PM3_ROOT}/client/src/cmdlfio.c
|
||||||
|
@ -414,6 +414,7 @@ set (TARGET_SOURCES
|
||||||
${PM3_ROOT}/client/src/fileutils.c
|
${PM3_ROOT}/client/src/fileutils.c
|
||||||
${PM3_ROOT}/client/src/flash.c
|
${PM3_ROOT}/client/src/flash.c
|
||||||
${PM3_ROOT}/client/src/graph.c
|
${PM3_ROOT}/client/src/graph.c
|
||||||
|
${PM3_ROOT}/client/src/hidsio.c
|
||||||
${PM3_ROOT}/client/src/iso4217.c
|
${PM3_ROOT}/client/src/iso4217.c
|
||||||
${PM3_ROOT}/client/src/jansson_path.c
|
${PM3_ROOT}/client/src/jansson_path.c
|
||||||
${PM3_ROOT}/client/src/lua_bitlib.c
|
${PM3_ROOT}/client/src/lua_bitlib.c
|
||||||
|
|
|
@ -1,462 +0,0 @@
|
||||||
local getopt = require('getopt')
|
|
||||||
local utils = require('utils')
|
|
||||||
local ac = require('ansicolors')
|
|
||||||
local os = require('os')
|
|
||||||
local dash = string.rep('--', 32)
|
|
||||||
local dir = os.getenv('HOME') .. '/.proxmark3/logs/'
|
|
||||||
local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+"))
|
|
||||||
local log_file_path = dir .. "Paxton_log.txt"
|
|
||||||
local nam = ""
|
|
||||||
local pm3 = require('pm3')
|
|
||||||
p = pm3.pm3()
|
|
||||||
local command = core.console
|
|
||||||
command('clear')
|
|
||||||
|
|
||||||
author = ' Author: jareckib - 30.01.2025'
|
|
||||||
tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102'
|
|
||||||
version = ' version v1.19'
|
|
||||||
desc = [[
|
|
||||||
The script automates the copying of Paxton fobs read - write.
|
|
||||||
It also allows manual input of data for blocks 4-7.
|
|
||||||
The third option is reading data stored in the log file and create new fob.
|
|
||||||
Additionally, the script calculates the ID for downgrading Paxton to EM4102.
|
|
||||||
|
|
||||||
]]
|
|
||||||
usage = [[
|
|
||||||
script run paxton_clone
|
|
||||||
]]
|
|
||||||
arguments = [[
|
|
||||||
script run paxton_clone -h : this help
|
|
||||||
]]
|
|
||||||
|
|
||||||
local debug = true
|
|
||||||
|
|
||||||
local function dbg(args)
|
|
||||||
if not DEBUG then return end
|
|
||||||
if type(args) == 'table' then
|
|
||||||
local i = 1
|
|
||||||
while args[i] do
|
|
||||||
dbg(args[i])
|
|
||||||
i = i+1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
print('###', args)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function help()
|
|
||||||
print()
|
|
||||||
print(author)
|
|
||||||
print(tutorial)
|
|
||||||
print(version)
|
|
||||||
print(desc)
|
|
||||||
print(ac.cyan..' Usage'..ac.reset)
|
|
||||||
print(usage)
|
|
||||||
print(ac.cyan..' Arguments'..ac.reset)
|
|
||||||
print(arguments)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function read_log_file(logfile)
|
|
||||||
local file = io.open(logfile, "r")
|
|
||||||
if not file then
|
|
||||||
error(" Could not open the file")
|
|
||||||
end
|
|
||||||
local content = file:read("*all")
|
|
||||||
file:close()
|
|
||||||
return content
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parse_blocks(result)
|
|
||||||
local blocks = {}
|
|
||||||
for line in result:gmatch("[^\r\n]+") do
|
|
||||||
local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)")
|
|
||||||
if block_num and block_data then
|
|
||||||
block_num = tonumber(block_num)
|
|
||||||
block_data = block_data:gsub("%s+", "")
|
|
||||||
blocks[block_num] = block_data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return blocks
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hex_to_bin(hex_string)
|
|
||||||
local bin_string = ""
|
|
||||||
local hex_to_bin_map = {
|
|
||||||
['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011",
|
|
||||||
['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111",
|
|
||||||
['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011",
|
|
||||||
['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111"
|
|
||||||
}
|
|
||||||
for i = 1, #hex_string do
|
|
||||||
bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)]
|
|
||||||
end
|
|
||||||
return bin_string
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_last_two_bits(binary_str)
|
|
||||||
return binary_str:sub(1, #binary_str - 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function split_into_5bit_chunks(binary_str)
|
|
||||||
local chunks = {}
|
|
||||||
for i = 1, #binary_str, 5 do
|
|
||||||
table.insert(chunks, binary_str:sub(i, i + 4))
|
|
||||||
end
|
|
||||||
return chunks
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_parity_bit(chunks)
|
|
||||||
local no_parity_chunks = {}
|
|
||||||
for _, chunk in ipairs(chunks) do
|
|
||||||
if #chunk == 5 then
|
|
||||||
table.insert(no_parity_chunks, chunk:sub(2))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return no_parity_chunks
|
|
||||||
end
|
|
||||||
|
|
||||||
local function convert_to_hex(chunks)
|
|
||||||
local hex_values = {}
|
|
||||||
for _, chunk in ipairs(chunks) do
|
|
||||||
if #chunk > 0 then
|
|
||||||
table.insert(hex_values, string.format("%X", tonumber(chunk, 2)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return hex_values
|
|
||||||
end
|
|
||||||
|
|
||||||
local function convert_to_decimal(chunks)
|
|
||||||
local decimal_values = {}
|
|
||||||
for _, chunk in ipairs(chunks) do
|
|
||||||
table.insert(decimal_values, tonumber(chunk, 2))
|
|
||||||
end
|
|
||||||
return decimal_values
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_until_before_f(hex_values)
|
|
||||||
local result = {}
|
|
||||||
for _, value in ipairs(hex_values) do
|
|
||||||
if value == 'F' then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
table.insert(result, value)
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
local function process_block(block)
|
|
||||||
local binary_str = hex_to_bin(block)
|
|
||||||
binary_str = remove_last_two_bits(binary_str)
|
|
||||||
local chunks = split_into_5bit_chunks(binary_str)
|
|
||||||
local no_parity_chunks = remove_parity_bit(chunks)
|
|
||||||
return no_parity_chunks
|
|
||||||
end
|
|
||||||
|
|
||||||
local function calculate_id_net(blocks)
|
|
||||||
local all_hex_values = {}
|
|
||||||
for _, block in ipairs(blocks) do
|
|
||||||
local hex_values = convert_to_hex(process_block(block))
|
|
||||||
for _, hex in ipairs(hex_values) do
|
|
||||||
table.insert(all_hex_values, hex)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local selected_hex_values = find_until_before_f(all_hex_values)
|
|
||||||
if #selected_hex_values == 0 then
|
|
||||||
error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5')
|
|
||||||
end
|
|
||||||
local combined_hex = table.concat(selected_hex_values)
|
|
||||||
if not combined_hex:match("^%x+$") then
|
|
||||||
error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5')
|
|
||||||
end
|
|
||||||
local decimal_id = tonumber(combined_hex)
|
|
||||||
local stripped_hex_id = string.format("%X", decimal_id)
|
|
||||||
local padded_hex_id = string.format("%010X", decimal_id)
|
|
||||||
return decimal_id, padded_hex_id
|
|
||||||
end
|
|
||||||
|
|
||||||
local function calculate_id_switch(blocks)
|
|
||||||
local all_decimal_values = {}
|
|
||||||
for _, block in ipairs(blocks) do
|
|
||||||
local decimal_values = convert_to_decimal(process_block(block))
|
|
||||||
for _, dec in ipairs(decimal_values) do
|
|
||||||
table.insert(all_decimal_values, dec)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #all_decimal_values < 15 then
|
|
||||||
error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7')
|
|
||||||
end
|
|
||||||
local id_positions = {9, 11, 13, 15, 2, 4, 6, 8}
|
|
||||||
local id_numbers = {}
|
|
||||||
for _, pos in ipairs(id_positions) do
|
|
||||||
table.insert(id_numbers, all_decimal_values[pos])
|
|
||||||
end
|
|
||||||
local decimal_id = tonumber(table.concat(id_numbers))
|
|
||||||
local padded_hex_id = string.format("%010X", decimal_id)
|
|
||||||
return decimal_id, padded_hex_id
|
|
||||||
end
|
|
||||||
|
|
||||||
local function name_exists_in_log(name)
|
|
||||||
local file = io.open(log_file_path, "r")
|
|
||||||
if not file then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local pattern = "^Name:%s*" .. name .. "%s*$"
|
|
||||||
for line in file:lines() do
|
|
||||||
if line:match(pattern) then
|
|
||||||
file:close()
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
file:close()
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function log_result(blocks, em410_id, name)
|
|
||||||
local log_file = io.open(log_file_path, "a")
|
|
||||||
if log_file then
|
|
||||||
log_file:write("Name: " .. name .. "\n")
|
|
||||||
log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n")
|
|
||||||
for i = 4, 7 do
|
|
||||||
log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil"))
|
|
||||||
end
|
|
||||||
log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil"))
|
|
||||||
log_file:write('--------------------------\n')
|
|
||||||
log_file:close()
|
|
||||||
print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset)
|
|
||||||
else
|
|
||||||
print(" Failed to open log file for writing.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
|
||||||
while true do
|
|
||||||
io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset)
|
|
||||||
local choice = io.read()
|
|
||||||
if choice == "1" then
|
|
||||||
io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
|
||||||
io.read()
|
|
||||||
print(dash)
|
|
||||||
command("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846")
|
|
||||||
command("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846")
|
|
||||||
command("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846")
|
|
||||||
command("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846")
|
|
||||||
elseif choice == "2" then
|
|
||||||
io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..")
|
|
||||||
io.read()
|
|
||||||
p:console("lf em 410x clone --id " .. padded_hex_id)
|
|
||||||
print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset)
|
|
||||||
else
|
|
||||||
print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset)
|
|
||||||
goto ask_again
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
print(dash)
|
|
||||||
io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset)
|
|
||||||
local another = io.read()
|
|
||||||
if another:lower() == "n" then
|
|
||||||
if was_option_3 then
|
|
||||||
print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
print()
|
|
||||||
print(ac.green .. " Saving Paxton_log file..." .. ac.reset)
|
|
||||||
while true do
|
|
||||||
io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow)
|
|
||||||
name = io.read()
|
|
||||||
io.write(ac.reset..'')
|
|
||||||
if name == nil or name:match("^%s*$") then
|
|
||||||
print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.')
|
|
||||||
else
|
|
||||||
if name_exists_in_log(name) then
|
|
||||||
print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.')
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
log_result(blocks, padded_hex_id, name)
|
|
||||||
print(ac.green .. " Log saved successfully!" .. ac.reset)
|
|
||||||
local file = io.open(logfile, "w+")
|
|
||||||
file:write("")
|
|
||||||
file:close()
|
|
||||||
return
|
|
||||||
elseif another:lower() == "y" then
|
|
||||||
goto ask_again
|
|
||||||
else
|
|
||||||
print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
::ask_again::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_valid_hex(input)
|
|
||||||
return #input == 8 and input:match("^[0-9A-Fa-f]+$")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function main(args)
|
|
||||||
while true do
|
|
||||||
for o, a in getopt.getopt(args, 'h') do
|
|
||||||
if o == 'h' then return help() end
|
|
||||||
end
|
|
||||||
command('clear')
|
|
||||||
print(dash)
|
|
||||||
print(ac.green .. ' Select option: ' .. ac.reset)
|
|
||||||
print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy')
|
|
||||||
print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7')
|
|
||||||
print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data")
|
|
||||||
print(dash)
|
|
||||||
while true do
|
|
||||||
io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset)
|
|
||||||
input_option = io.read()
|
|
||||||
if input_option == "1" or input_option == "2" or input_option == "3" then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local was_option_3 = false
|
|
||||||
if input_option == "1" then
|
|
||||||
local show_place_message = true
|
|
||||||
while true do
|
|
||||||
if show_place_message then
|
|
||||||
io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..')
|
|
||||||
end
|
|
||||||
io.read()
|
|
||||||
print(dash)
|
|
||||||
p:console('lf hitag read --ht2 -k BDF5E846')
|
|
||||||
if not logfile then
|
|
||||||
error(" No files in this directory")
|
|
||||||
end
|
|
||||||
local result = read_log_file(logfile)
|
|
||||||
local blocks = parse_blocks(result)
|
|
||||||
local empty_block = false
|
|
||||||
for i = 4, 7 do
|
|
||||||
if not blocks[i] then
|
|
||||||
empty_block = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if empty_block then
|
|
||||||
io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..')
|
|
||||||
show_place_message = false
|
|
||||||
else
|
|
||||||
print(' Readed blocks:')
|
|
||||||
print()
|
|
||||||
for i = 4, 7 do
|
|
||||||
if blocks[i] then
|
|
||||||
print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local decimal_id, padded_hex_id
|
|
||||||
if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then
|
|
||||||
print(dash)
|
|
||||||
print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset)
|
|
||||||
decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]})
|
|
||||||
else
|
|
||||||
print(dash)
|
|
||||||
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
|
|
||||||
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
|
|
||||||
end
|
|
||||||
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
|
|
||||||
print(dash)
|
|
||||||
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif input_option == "2" then
|
|
||||||
local blocks = {}
|
|
||||||
for i = 4, 7 do
|
|
||||||
while true do
|
|
||||||
io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow)
|
|
||||||
local input = io.read()
|
|
||||||
input = input:upper()
|
|
||||||
if is_valid_hex(input) then
|
|
||||||
blocks[i] = input
|
|
||||||
break
|
|
||||||
else
|
|
||||||
print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local decimal_id, padded_hex_id
|
|
||||||
if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then
|
|
||||||
print(ac.reset.. dash)
|
|
||||||
print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset)
|
|
||||||
decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]})
|
|
||||||
else
|
|
||||||
print(ac.reset.. dash)
|
|
||||||
print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset)
|
|
||||||
decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]})
|
|
||||||
end
|
|
||||||
print(dash)
|
|
||||||
print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset))
|
|
||||||
print(dash)
|
|
||||||
if not padded_hex_id then
|
|
||||||
print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3)
|
|
||||||
break
|
|
||||||
elseif input_option == "3" then
|
|
||||||
was_option_3 = true
|
|
||||||
local retries = 3
|
|
||||||
while retries > 0 do
|
|
||||||
io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow)
|
|
||||||
local user_input = io.read()
|
|
||||||
io.write(ac.reset..'')
|
|
||||||
if user_input == nil or user_input:match("^%s*$") then
|
|
||||||
print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!')
|
|
||||||
end
|
|
||||||
local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$"
|
|
||||||
local file = io.open(log_file_path, "r")
|
|
||||||
if not file then
|
|
||||||
print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local lines = {}
|
|
||||||
for line in file:lines() do
|
|
||||||
table.insert(lines, line)
|
|
||||||
end
|
|
||||||
file:close()
|
|
||||||
local found = false
|
|
||||||
for i = 1, #lines do
|
|
||||||
if lines[i]:match(name_clean) then
|
|
||||||
nam = user_input
|
|
||||||
local blocks = {
|
|
||||||
[4] = lines[i + 2]:match("Block 4: (.+)"),
|
|
||||||
[5] = lines[i + 3]:match("Block 5: (.+)"),
|
|
||||||
[6] = lines[i + 4]:match("Block 6: (.+)"),
|
|
||||||
[7] = lines[i + 5]:match("Block 7: (.+)")
|
|
||||||
}
|
|
||||||
local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)")
|
|
||||||
print(dash)
|
|
||||||
print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset)
|
|
||||||
for j = 4, 7 do
|
|
||||||
print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset))
|
|
||||||
end
|
|
||||||
print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset)
|
|
||||||
print(dash)
|
|
||||||
local decimal_id, padded_hex_id = em4102_id, em4102_id
|
|
||||||
handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam)
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not found then
|
|
||||||
retries = retries - 1
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if retries == 0 then
|
|
||||||
print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print(dash)
|
|
||||||
print(' Exiting script Lua...')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
main(args)
|
|
|
@ -11,6 +11,7 @@ p = pm3.pm3()
|
||||||
command('clear')
|
command('clear')
|
||||||
author = ' Author: jareckib - 12.03.2025'
|
author = ' Author: jareckib - 12.03.2025'
|
||||||
version = ' version v1.05'
|
version = ' version v1.05'
|
||||||
|
|
||||||
desc = [[
|
desc = [[
|
||||||
This simple script stores 1, 2 or 3 different EM4102 on a single T5577.
|
This simple script stores 1, 2 or 3 different EM4102 on a single T5577.
|
||||||
There is an option to enter the number engraved on the fob in decimal form.
|
There is an option to enter the number engraved on the fob in decimal form.
|
||||||
|
@ -61,8 +62,6 @@ local function timer(n)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local function reset_log_file()
|
local function reset_log_file()
|
||||||
local file = io.open(logfile, "w+")
|
local file = io.open(logfile, "w+")
|
||||||
file:write("")
|
file:write("")
|
||||||
|
|
|
@ -1866,7 +1866,7 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_conf
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &resp, 10000, true) == false) {
|
if (GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &resp, 10000, true) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ static int CmdFlashMemLoad(const char *Cmd) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_FLASHMEM_WRITE, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
g_conn.block_after_ACK = false;
|
g_conn.block_after_ACK = false;
|
||||||
free(data);
|
free(data);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
|
@ -473,7 +473,7 @@ static int CmdFlashMemWipe(const char *Cmd) {
|
||||||
SendCommandMIX(CMD_FLASHMEM_WIPE, page, initialwipe, 0, NULL, 0);
|
SendCommandMIX(CMD_FLASHMEM_WIPE, page, initialwipe, 0, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 10000)) {
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 10000)) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ int flashmem_spiffs_load(const char *destfn, const uint8_t *data, size_t datalen
|
||||||
|
|
||||||
uint8_t retry = 3;
|
uint8_t retry = 3;
|
||||||
while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) {
|
while (WaitForResponseTimeout(CMD_SPIFFS_WRITE, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
retry--;
|
retry--;
|
||||||
if (retry == 0) {
|
if (retry == 0) {
|
||||||
ret_val = PM3_ETIMEOUT;
|
ret_val = PM3_ETIMEOUT;
|
||||||
|
@ -96,7 +96,7 @@ int flashmem_spiffs_download(char *fn, uint8_t fnlen, void **pdest, size_t *dest
|
||||||
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)fn, fnlen);
|
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)fn, fnlen);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) {
|
||||||
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen);
|
SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)src, slen);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ int CmdHFSearch(const char *Cmd) {
|
||||||
|
|
||||||
int res = PM3_ESOFT;
|
int res = PM3_ESOFT;
|
||||||
|
|
||||||
uint8_t success[20] = {0};
|
uint8_t success[COUNT_OF_PROTOCOLS] = {0};
|
||||||
|
|
||||||
PROMPT_CLEARLINE;
|
PROMPT_CLEARLINE;
|
||||||
PrintAndLogEx(INPLACE, " Searching for ThinFilm tag...");
|
PrintAndLogEx(INPLACE, " Searching for ThinFilm tag...");
|
||||||
|
@ -526,7 +526,7 @@ int handle_hf_plot(bool show_plot) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &resp, 4000, true) == false) {
|
if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &resp, 4000, true) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -498,7 +498,7 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -806,7 +806,7 @@ static int CmdHF14ACUIDs(const char *Cmd) {
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
|
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1206,9 +1206,9 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t cmdc = 0;
|
uint16_t cmdc = (ISO14A_APDU | ISO14A_NO_DISCONNECT);
|
||||||
if (chainingin) {
|
if (chainingin) {
|
||||||
cmdc = ISO14A_SEND_CHAINING;
|
cmdc |= ISO14A_SEND_CHAINING;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
|
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
|
||||||
|
@ -1216,9 +1216,9 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen
|
||||||
// here length PM3_CMD_DATA_SIZE=512
|
// here length PM3_CMD_DATA_SIZE=512
|
||||||
// timeout must be authomatically set by "get ATS"
|
// timeout must be authomatically set by "get ATS"
|
||||||
if (datain) {
|
if (datain) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, cmdc, (datainlen & 0x1FF), 0, datain, (datainlen & 0x1FF));
|
||||||
} else {
|
} else {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, cmdc, 0, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
|
@ -1670,7 +1670,7 @@ static int waitCmd(bool i_select, uint32_t timeout, bool verbose) {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
|
@ -2264,7 +2264,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0);
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
DropField();
|
DropField();
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -2324,7 +2324,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats));
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats));
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1182,7 +1182,7 @@ static void hf15EmlClear(void) {
|
||||||
SendCommandNG(CMD_HF_ISO15693_EML_CLEAR, NULL, 0);
|
SendCommandNG(CMD_HF_ISO15693_EML_CLEAR, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_HF_ISO15693_EML_CLEAR, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_HF_ISO15693_EML_CLEAR, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,7 +264,7 @@ static const char *felica_model_name(uint8_t rom_type, uint8_t ic_type) {
|
||||||
*/
|
*/
|
||||||
static bool waitCmdFelica(bool iSelect, PacketResponseNG *resp, bool verbose) {
|
static bool waitCmdFelica(bool iSelect, PacketResponseNG *resp, bool verbose) {
|
||||||
if (WaitForResponseTimeout(CMD_ACK, resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2068,7 +2068,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) {
|
||||||
return PM3_EOPABORTED;
|
return PM3_EOPABORTED;
|
||||||
}
|
}
|
||||||
if (timeout > 10) {
|
if (timeout > 10) {
|
||||||
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply");
|
||||||
DropField();
|
DropField();
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -922,7 +922,7 @@ static int CmdHFiClassSim(const char *Cmd) {
|
||||||
return PM3_EOPABORTED;
|
return PM3_EOPABORTED;
|
||||||
}
|
}
|
||||||
if (tries > 20) {
|
if (tries > 20) {
|
||||||
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -973,7 +973,7 @@ static int CmdHFiClassSim(const char *Cmd) {
|
||||||
return PM3_EOPABORTED;
|
return PM3_EOPABORTED;
|
||||||
}
|
}
|
||||||
if (tries > 20) {
|
if (tries > 20) {
|
||||||
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "\ntimeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1186,7 +1186,7 @@ static int CmdHFiClassELoad(const char *Cmd) {
|
||||||
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
|
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1482,7 +1482,8 @@ static void iclass_decode_credentials(uint8_t *data) {
|
||||||
char *pbin = binstr;
|
char *pbin = binstr;
|
||||||
while (strlen(pbin) && *(++pbin) == '0');
|
while (strlen(pbin) && *(++pbin) == '0');
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Binary... %zu - " _GREEN_("%s"), strlen(pbin), pbin);
|
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s") " ( %zu )", pbin, strlen(pbin));
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
decode_wiegand(top, mid, bot, 0);
|
decode_wiegand(top, mid, bot, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1829,9 +1829,9 @@ void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is
|
||||||
uint8_t ks = cmd[pos + 3];
|
uint8_t ks = cmd[pos + 3];
|
||||||
if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) {
|
if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) {
|
||||||
snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks);
|
snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) {
|
if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) {
|
||||||
uint8_t ks = cmd[pos + 3];
|
uint8_t ks = cmd[pos + 3];
|
||||||
|
|
|
@ -132,7 +132,7 @@ static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
|
|
||||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
|
||||||
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2923,6 +2923,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
|
||||||
|
|
||||||
noValidKeyFound:
|
noValidKeyFound:
|
||||||
PrintAndLogEx(FAILED, "No usable key was found!");
|
PrintAndLogEx(FAILED, "No usable key was found!");
|
||||||
|
PrintAndLogEx(HINT, "try `" _YELLOW_("hf mf autopwn -f mfc_default_keys")"` i.e. the Randy special");
|
||||||
free(keyBlock);
|
free(keyBlock);
|
||||||
free(e_sector);
|
free(e_sector);
|
||||||
free(fptr);
|
free(fptr);
|
||||||
|
@ -4712,7 +4713,7 @@ int CmdHF14AMfELoad(const char *Cmd) {
|
||||||
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
|
SendCommandNG(CMD_SPIFFS_ELOAD, (uint8_t *)filename, fnlen);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_SPIFFS_ELOAD, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6278,7 +6279,8 @@ static int CmdHF14AMfMAD(const char *Cmd) {
|
||||||
if (sector > -1) {
|
if (sector > -1) {
|
||||||
|
|
||||||
// decode it
|
// decode it
|
||||||
PrintAndLogEx(INFO, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------");
|
||||||
PrintAndLogEx(INFO, _CYAN_("HID PACS detected"));
|
PrintAndLogEx(INFO, _CYAN_("HID PACS detected"));
|
||||||
|
|
||||||
uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0};
|
uint8_t pacs_sector[MFBLOCK_SIZE * 3] = {0};
|
||||||
|
@ -6301,7 +6303,6 @@ static int CmdHF14AMfMAD(const char *Cmd) {
|
||||||
|
|
||||||
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
|
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "Wiegand decode");
|
|
||||||
decode_wiegand(top, mid, bot, 0);
|
decode_wiegand(top, mid, bot, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1446,7 +1446,7 @@ static int CmdHF14aDesChk(const char *Cmd) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1655,7 +1655,7 @@ static int CmdHFMFPChk(const char *Cmd) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -315,7 +315,7 @@ int ul_read_uid(uint8_t *uid) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
iso14a_card_select_t card;
|
iso14a_card_select_t card;
|
||||||
|
@ -3892,6 +3892,69 @@ static int CmdHF14AMfUCAuth(const char *Cmd) {
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------
|
||||||
|
// Ultralight AES Methods
|
||||||
|
//-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Ultralight AES Authentication
|
||||||
|
//
|
||||||
|
static int CmdHF14AMfUAESAuth(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "hf mfu aesauth",
|
||||||
|
"Tests AES key on Mifare Ultralight AES tags.\n"
|
||||||
|
"If no key is specified, null key will be tried.\n"
|
||||||
|
"Key index 0: DataProtKey (default)\n"
|
||||||
|
"Key index 1: UIDRetrKey\n"
|
||||||
|
"Key index 2: OriginalityKey\n",
|
||||||
|
"hf mfu aesauth\n"
|
||||||
|
"hf mfu aesauth --key <32 bytes> --index <0..2>"
|
||||||
|
);
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_str0(NULL, "key", "<hex>", "AES key (32 hex bytes)"),
|
||||||
|
arg_int0("i", "index", "<0..2>", "Key index, default: 0"),
|
||||||
|
arg_lit0("k", NULL, "Keep field on (only if a key is provided)"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
|
int ak_len = 0;
|
||||||
|
uint8_t authentication_key[32] = {0};
|
||||||
|
uint8_t *authKeyPtr = authentication_key;
|
||||||
|
CLIGetHexWithReturn(ctx, 1, authentication_key, &ak_len);
|
||||||
|
int key_index = arg_get_int_def(ctx, 2, 0);
|
||||||
|
bool keep_field_on = arg_get_lit(ctx, 3);
|
||||||
|
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
if (ak_len == 0) {
|
||||||
|
// default to null key
|
||||||
|
ak_len = 32;
|
||||||
|
}
|
||||||
|
if (ak_len != 32) {
|
||||||
|
PrintAndLogEx(WARNING, "Invalid key length");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_index < 0 || key_index > 2) {
|
||||||
|
PrintAndLogEx(WARNING, "Invalid key index");
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = ulaes_requestAuthentication(authKeyPtr, key_index, !keep_field_on);
|
||||||
|
|
||||||
|
const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" };
|
||||||
|
if (result == PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )",
|
||||||
|
key_type[key_index], sprint_hex_inrow(authKeyPtr, ak_len));
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )",
|
||||||
|
key_type[key_index]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A test function to validate that the polarssl-function works the same
|
A test function to validate that the polarssl-function works the same
|
||||||
was as the openssl-implementation.
|
was as the openssl-implementation.
|
||||||
|
@ -4168,7 +4231,7 @@ static int CmdHF14AMfUKeyGen(const char *Cmd) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5915,6 +5978,7 @@ static command_t CommandTable[] = {
|
||||||
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"},
|
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"},
|
||||||
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"},
|
{"cauth", CmdHF14AMfUCAuth, IfPm3Iso14443a, "Ultralight-C - Authentication"},
|
||||||
{"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"},
|
{"setpwd", CmdHF14AMfUCSetPwd, IfPm3Iso14443a, "Ultralight-C - Set 3DES key"},
|
||||||
|
{"aesauth", CmdHF14AMfUAESAuth, IfPm3Iso14443a, "Ultralight-AES - Authentication"},
|
||||||
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
|
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
|
||||||
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
|
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
|
||||||
{"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"},
|
{"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"},
|
||||||
|
|
|
@ -126,7 +126,7 @@ int infoThinFilm(bool verbose) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500) == false) {
|
if (WaitForResponseTimeout(CMD_HF_THINFILM_READ, &resp, 1500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, len, 0, cmd, len);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE | ISO14A_NO_RATS, len, 0, cmd, len);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
|
||||||
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "cmdlfhid.h" // for hid menu
|
#include "cmdlfhid.h" // for hid menu
|
||||||
#include "cmdlfhitag.h" // for hitag menu
|
#include "cmdlfhitag.h" // for hitag menu
|
||||||
#include "cmdlfhitaghts.h" // for hitag S sub commands
|
#include "cmdlfhitaghts.h" // for hitag S sub commands
|
||||||
|
#include "cmdlfhitagu.h" // for hitag µ sub commands
|
||||||
#include "cmdlfidteck.h" // for idteck menu
|
#include "cmdlfidteck.h" // for idteck menu
|
||||||
#include "cmdlfio.h" // for ioprox menu
|
#include "cmdlfio.h" // for ioprox menu
|
||||||
#include "cmdlfcotag.h" // for COTAG menu
|
#include "cmdlfcotag.h" // for COTAG menu
|
||||||
|
@ -455,7 +456,7 @@ int CmdLFCommandRead(const char *Cmd) {
|
||||||
getSamples(samples, false);
|
getSamples(samples, false);
|
||||||
ret = PM3_SUCCESS;
|
ret = PM3_SUCCESS;
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1602,6 +1603,14 @@ static bool check_chiptype(bool getDeviceData) {
|
||||||
retval = true;
|
retval = true;
|
||||||
goto out;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -624,11 +624,12 @@ static int CmdEM410xSpoof(const char *Cmd) {
|
||||||
static int CmdEM410xClone(const char *Cmd) {
|
static int CmdEM410xClone(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "lf em 410x clone",
|
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 -> encode for T55x7 tag\n"
|
||||||
"lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 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 --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[] = {
|
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, "q5", "optional - specify writing to Q5/T5555 tag"),
|
||||||
arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 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, "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_lit0(NULL, "electra", "optional - add Electra blocks to tag"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
};
|
};
|
||||||
|
@ -651,24 +653,24 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
bool q5 = arg_get_lit(ctx, 3);
|
bool q5 = arg_get_lit(ctx, 3);
|
||||||
bool em = arg_get_lit(ctx, 4);
|
bool em = arg_get_lit(ctx, 4);
|
||||||
bool hts = arg_get_lit(ctx, 5);
|
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);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
if (q5 + em + hts > 1) {
|
if (q5 + em + hts + htu > 1) {
|
||||||
PrintAndLogEx(FAILED, "Only specify one tag Type");
|
PrintAndLogEx(FAILED, "Only specify one tag Type");
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hts) {
|
if ((hts || htu) && IfPm3Hitag() == false) {
|
||||||
if (IfPm3Hitag() == false) {
|
|
||||||
PrintAndLogEx(FAILED, "Device not compiled to support Hitag");
|
PrintAndLogEx(FAILED, "Device not compiled to support Hitag");
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
if (clk == 40) {
|
|
||||||
|
if ((hts || htu) && clk == 40) {
|
||||||
PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64"));
|
PrintAndLogEx(FAILED, "supported clock rates for Hitag are " _YELLOW_("16, 32, 64"));
|
||||||
return PM3_EINVARG;
|
return PM3_EINVARG;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Allowed clock rates: 16, 32, 40 and 64
|
// Allowed clock rates: 16, 32, 40 and 64
|
||||||
if ((clk != 16) && (clk != 32) && (clk != 64) && (clk != 40)) {
|
if ((clk != 16) && (clk != 32) && (clk != 64) && (clk != 40)) {
|
||||||
|
@ -678,9 +680,9 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
|
|
||||||
uint64_t id = bytes_to_num(uid, uid_len);
|
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)",
|
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;
|
uint32_t databits = 9;
|
||||||
uint8_t c_parity = 0;
|
uint8_t c_parity = 0;
|
||||||
|
|
||||||
|
@ -688,11 +690,11 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
uint8_t r_parity = 0;
|
uint8_t r_parity = 0;
|
||||||
uint8_t nibble = id >> i & 0xF;
|
uint8_t nibble = id >> i & 0xF;
|
||||||
|
|
||||||
databits = concatbits(data, databits, &nibble, 4, 4);
|
databits = concatbits(data, databits, &nibble, 4, 4, false);
|
||||||
for (size_t j = 0; j < 4; j++) {
|
for (size_t j = 0; j < 4; j++) {
|
||||||
r_parity ^= nibble >> j & 1;
|
r_parity ^= nibble >> j & 1;
|
||||||
}
|
}
|
||||||
databits = concatbits(data, databits, &r_parity, 7, 1);
|
databits = concatbits(data, databits, &r_parity, 7, 1, false);
|
||||||
c_parity ^= nibble;
|
c_parity ^= nibble;
|
||||||
}
|
}
|
||||||
data[7] |= c_parity << 1;
|
data[7] |= c_parity << 1;
|
||||||
|
@ -706,35 +708,45 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
lf_hitag_data_t packet;
|
lf_hitag_data_t packet;
|
||||||
memset(&packet, 0, sizeof(packet));
|
memset(&packet, 0, sizeof(packet));
|
||||||
|
|
||||||
for (size_t steps = 0; steps < 3; steps++) {
|
for (size_t step = 0; step < 3; step++) {
|
||||||
switch (steps) {
|
switch (step) {
|
||||||
case 0:
|
case 0: {
|
||||||
packet.data[0] = 0xCA; //compatiable for 82xx, no impact on Hitag S
|
hitags_config_t config = {0};
|
||||||
// clk -> TTFDR1 TTFDR0
|
config.MEMT = 0x02; // compatiable for 82xx, no impact on Hitag S
|
||||||
// 32 -> 0x00 4 kBit/s
|
config.TTFM = 0x01; // 0 = "Block 0, Block 1, Block 2, Block 3", 1 = "Block 0, Block 1"
|
||||||
// 16 -> 0x10 8 kBit/s
|
config.TTFC = 0x00; // Manchester
|
||||||
// 64 -> 0x20 2 kBit/s
|
config.auth = 0x00; // Plain
|
||||||
packet.data[1] = 0x04;
|
|
||||||
|
//compatiable for 82xx, no impact on Hitag S
|
||||||
|
config.RES1 = 0x01;
|
||||||
|
config.RES4 = 0x01;
|
||||||
|
config.RES5 = 0x01;
|
||||||
switch (clk) {
|
switch (clk) {
|
||||||
|
case 64:
|
||||||
|
// 2 kBit/s
|
||||||
|
config.TTFDR = 0x02;
|
||||||
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
|
// 4 kBit/s
|
||||||
|
config.TTFDR = 0x00;
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
packet.data[1] |= 0x10;
|
// 8 kBit/s
|
||||||
break;
|
config.TTFDR = 0x01;
|
||||||
case 64:
|
|
||||||
packet.data[1] |= 0x20;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
packet.data[2] = 0;
|
//TODO: keep other fields?
|
||||||
packet.data[3] = 0; //TODO: keep PWDH0?
|
memcpy(packet.data, &config, sizeof(config));
|
||||||
|
// PrintAndLogEx(INFO, "packet.data: %s", sprint_hex(packet.data, sizeof(packet.data)));
|
||||||
packet.page = 1;
|
packet.page = 1;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 1:
|
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;
|
packet.page = 4;
|
||||||
break;
|
break;
|
||||||
case 2:
|
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;
|
packet.page = 5;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -744,14 +756,82 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
packet.mode = HITAGS_UID_REQ_FADV;
|
packet.mode = HITAGS_UID_REQ_FADV;
|
||||||
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
if (resp.status != PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(WARNING, "Something went wrong");
|
PrintAndLogEx(WARNING, "Something went wrong in step %zu", step);
|
||||||
return resp.status;
|
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 {
|
} else {
|
||||||
struct {
|
struct {
|
||||||
bool Q5;
|
bool Q5;
|
||||||
|
@ -771,7 +851,7 @@ static int CmdEM410xClone(const char *Cmd) {
|
||||||
|
|
||||||
SendCommandNG(CMD_LF_EM410X_CLONE, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_LF_EM410X_CLONE, (uint8_t *)&payload, sizeof(payload));
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM410X_CLONE, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM410X_CLONE, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -469,7 +469,7 @@ static int em4x05_login_ext(uint32_t pwd) {
|
||||||
SendCommandNG(CMD_LF_EM4X_LOGIN, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_LF_EM4X_LOGIN, (uint8_t *)&payload, sizeof(payload));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X_LOGIN, &resp, 10000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X_LOGIN, &resp, 10000) == false) {
|
||||||
PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "(em4x05_login_ext) timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,7 +496,7 @@ int em4x05_read_word_ext(uint8_t addr, uint32_t pwd, bool use_pwd, uint32_t *wor
|
||||||
SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_LF_EM4X_READWORD, (uint8_t *)&payload, sizeof(payload));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X_READWORD, &resp, 10000) == false) {
|
||||||
PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "(em4x05_read_word_ext) timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1839,7 +1839,7 @@ int CmdEM4x05Brute(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) {
|
||||||
PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
PrintAndLogEx(INFO, "Bruteforce is running on device side, press button to interrupt");
|
PrintAndLogEx(INFO, "Bruteforce is running on device side, press button to interrupt");
|
||||||
|
|
|
@ -341,7 +341,7 @@ static int CmdEM4x50Login(const char *Cmd) {
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password));
|
SendCommandNG(CMD_LF_EM4X50_LOGIN, (uint8_t *)&password, sizeof(password));
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_LOGIN, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_LOGIN, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,7 +631,7 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
|
||||||
SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata));
|
SendCommandNG(CMD_LF_EM4X50_READ, (uint8_t *)&edata, sizeof(edata));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_READ, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
||||||
PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "(em4x50) timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,7 +740,7 @@ static int CmdEM4x50Info(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
|
SendCommandNG(CMD_LF_EM4X50_INFO, (uint8_t *)&etd, sizeof(etd));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_INFO, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -962,7 +962,7 @@ static int CmdEM4x50Write(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
|
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,7 +1033,7 @@ static int CmdEM4x50WritePwd(const char *Cmd) {
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
|
SendCommandNG(CMD_LF_EM4X50_WRITEPWD, (uint8_t *)&etd, sizeof(etd));
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITEPWD, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1117,7 +1117,7 @@ static int CmdEM4x50Wipe(const char *Cmd) {
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
|
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1213,7 +1213,7 @@ static int CmdEM4x50Restore(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
|
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
|
||||||
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
if (WaitForResponseTimeout(CMD_LF_EM4X50_WRITE, &resp, EM4X50_TIMEOUT_CMD) == false) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,9 +34,15 @@
|
||||||
|
|
||||||
// TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first
|
// TODO: Optional: use those unique structures in a union, call it em4x70_data_t, but add a first
|
||||||
// common header field that includes the command itself (to improve debugging / validation).
|
// common header field that includes the command itself (to improve debugging / validation).
|
||||||
|
|
||||||
typedef struct _em4x70_tag_info_t {
|
typedef struct _em4x70_tag_info_t {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The full data on an em4x70 the tag.
|
/// The full data on an em4170 tag.
|
||||||
|
/// For V4070 tags:
|
||||||
|
/// * UM2 does not exist on the tag
|
||||||
|
/// * Pin does not exist on the tag
|
||||||
|
/// * UM1 (including the lock bits) might be one-time programmable (OTP)
|
||||||
|
///
|
||||||
/// [31] == Block 15 MSB == UM2₆₃..UM2₅₆
|
/// [31] == Block 15 MSB == UM2₆₃..UM2₅₆
|
||||||
/// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
|
/// [30] == Block 15 LSB == UM2₅₅..UM2₄₈
|
||||||
/// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
|
/// [29] == Block 14 MSB == UM2₄₇..UM2₄₀
|
||||||
|
@ -168,6 +174,7 @@ typedef struct _em4x70_cmd_input_calculate_t {
|
||||||
ID48LIB_KEY key;
|
ID48LIB_KEY key;
|
||||||
ID48LIB_NONCE rn;
|
ID48LIB_NONCE rn;
|
||||||
} em4x70_cmd_input_calculate_t;
|
} em4x70_cmd_input_calculate_t;
|
||||||
|
|
||||||
typedef struct _em4x70_cmd_output_calculate_t {
|
typedef struct _em4x70_cmd_output_calculate_t {
|
||||||
ID48LIB_FRN frn;
|
ID48LIB_FRN frn;
|
||||||
ID48LIB_GRN grn;
|
ID48LIB_GRN grn;
|
||||||
|
@ -471,7 +478,7 @@ static int CmdEM4x70Info(const char *Cmd) {
|
||||||
int result = get_em4x70_info(&opts, &info);
|
int result = get_em4x70_info(&opts, &info);
|
||||||
|
|
||||||
if (result == PM3_ETIMEOUT) {
|
if (result == PM3_ETIMEOUT) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
} else if (result == PM3_SUCCESS) {
|
} else if (result == PM3_SUCCESS) {
|
||||||
em4x70_print_info_result(&info);
|
em4x70_print_info_result(&info);
|
||||||
} else {
|
} else {
|
||||||
|
@ -523,7 +530,7 @@ static int CmdEM4x70Write(const char *Cmd) {
|
||||||
int result = writeblock_em4x70(&opts, &info);
|
int result = writeblock_em4x70(&opts, &info);
|
||||||
|
|
||||||
if (result == PM3_ETIMEOUT) {
|
if (result == PM3_ETIMEOUT) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
} else if (result == PM3_SUCCESS) {
|
} else if (result == PM3_SUCCESS) {
|
||||||
em4x70_print_info_result(&info);
|
em4x70_print_info_result(&info);
|
||||||
} else {
|
} else {
|
||||||
|
@ -657,7 +664,7 @@ static int CmdEM4x70Unlock(const char *Cmd) {
|
||||||
int result = unlock_em4x70(&opts, &info);
|
int result = unlock_em4x70(&opts, &info);
|
||||||
|
|
||||||
if (result == PM3_ETIMEOUT) {
|
if (result == PM3_ETIMEOUT) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
} else if (result == PM3_SUCCESS) {
|
} else if (result == PM3_SUCCESS) {
|
||||||
em4x70_print_info_result(&info);
|
em4x70_print_info_result(&info);
|
||||||
} else {
|
} else {
|
||||||
|
@ -719,7 +726,7 @@ static int CmdEM4x70Auth(const char *Cmd) {
|
||||||
if (PM3_SUCCESS == result) {
|
if (PM3_SUCCESS == result) {
|
||||||
PrintAndLogEx(INFO, "Tag Auth Response: %02X %02X %02X", data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]);
|
PrintAndLogEx(INFO, "Tag Auth Response: %02X %02X %02X", data.grn.grn[0], data.grn.grn[1], data.grn.grn[2]);
|
||||||
} else if (PM3_ETIMEOUT == result) {
|
} else if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(FAILED, "TAG Authentication ( " _RED_("fail") " )");
|
PrintAndLogEx(FAILED, "TAG Authentication ( " _RED_("fail") " )");
|
||||||
}
|
}
|
||||||
|
@ -761,7 +768,7 @@ static int CmdEM4x70SetPIN(const char *Cmd) {
|
||||||
int result = setpin_em4x70(&opts, &info);
|
int result = setpin_em4x70(&opts, &info);
|
||||||
|
|
||||||
if (result == PM3_ETIMEOUT) {
|
if (result == PM3_ETIMEOUT) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
} else if (result == PM3_SUCCESS) {
|
} else if (result == PM3_SUCCESS) {
|
||||||
em4x70_print_info_result(&info);
|
em4x70_print_info_result(&info);
|
||||||
PrintAndLogEx(INFO, "Writing new PIN ( " _GREEN_("ok") " )");
|
PrintAndLogEx(INFO, "Writing new PIN ( " _GREEN_("ok") " )");
|
||||||
|
@ -805,7 +812,7 @@ static int CmdEM4x70SetKey(const char *Cmd) {
|
||||||
int result = setkey_em4x70(&opts);
|
int result = setkey_em4x70(&opts);
|
||||||
|
|
||||||
if (PM3_ETIMEOUT == result) {
|
if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
} else if (PM3_SUCCESS != result) {
|
} else if (PM3_SUCCESS != result) {
|
||||||
PrintAndLogEx(FAILED, "Writing new key " _RED_("fail"));
|
PrintAndLogEx(FAILED, "Writing new key " _RED_("fail"));
|
||||||
|
@ -850,7 +857,7 @@ static int CmdEM4x70SetKey(const char *Cmd) {
|
||||||
result = verify_auth_em4x70(&opts_v);
|
result = verify_auth_em4x70(&opts_v);
|
||||||
|
|
||||||
if (PM3_ETIMEOUT == result) {
|
if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return result;
|
return result;
|
||||||
} else if (PM3_SUCCESS != result) {
|
} else if (PM3_SUCCESS != result) {
|
||||||
PrintAndLogEx(FAILED, "Authenticating with new key ( " _RED_("fail") " )");
|
PrintAndLogEx(FAILED, "Authenticating with new key ( " _RED_("fail") " )");
|
||||||
|
@ -1197,7 +1204,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
|
||||||
result = auth_em4x70(&opts_auth, &tag_grn);
|
result = auth_em4x70(&opts_auth, &tag_grn);
|
||||||
|
|
||||||
if (PM3_ETIMEOUT == result) {
|
if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return result;
|
return result;
|
||||||
} else if (PM3_SUCCESS != result) {
|
} else if (PM3_SUCCESS != result) {
|
||||||
PrintAndLogEx(FAILED, "Authenticating with provided values ( " _RED_("fail") " )");
|
PrintAndLogEx(FAILED, "Authenticating with provided values ( " _RED_("fail") " )");
|
||||||
|
@ -1241,7 +1248,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
|
||||||
result = writeblock_em4x70(&opt_write_zeros, &tag_info);
|
result = writeblock_em4x70(&opt_write_zeros, &tag_info);
|
||||||
|
|
||||||
if (PM3_ETIMEOUT == result) {
|
if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(FAILED, "Timeout while waiting for reply.");
|
PrintAndLogEx(FAILED, "timeout while waiting for reply");
|
||||||
PrintAndLogEx(HINT, "Block %d data may have been overwritten. Manually restart at step %d", block, step);
|
PrintAndLogEx(HINT, "Block %d data may have been overwritten. Manually restart at step %d", block, step);
|
||||||
return result;
|
return result;
|
||||||
} else if (PM3_SUCCESS != result) {
|
} else if (PM3_SUCCESS != result) {
|
||||||
|
@ -1266,7 +1273,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
|
||||||
result = brute_em4x70(&opts_brute, &brute);
|
result = brute_em4x70(&opts_brute, &brute);
|
||||||
|
|
||||||
if (PM3_ETIMEOUT == result) {
|
if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(FAILED, "Timeout while waiting for reply.");
|
PrintAndLogEx(FAILED, "timeout while waiting for reply");
|
||||||
PrintAndLogEx(HINT, "Block %d data was overwritten. Manually restart at step %d", block, step);
|
PrintAndLogEx(HINT, "Block %d data was overwritten. Manually restart at step %d", block, step);
|
||||||
return result;
|
return result;
|
||||||
} else if (PM3_SUCCESS != result) {
|
} else if (PM3_SUCCESS != result) {
|
||||||
|
@ -1305,7 +1312,7 @@ static int CmdEM4x70AutoRecover(const char *Cmd) {
|
||||||
result = writeblock_em4x70(&opt_write_zeros, &tag_info);
|
result = writeblock_em4x70(&opt_write_zeros, &tag_info);
|
||||||
|
|
||||||
if (PM3_ETIMEOUT == result) {
|
if (PM3_ETIMEOUT == result) {
|
||||||
PrintAndLogEx(FAILED, "Timeout while waiting for reply.");
|
PrintAndLogEx(FAILED, "timeout while waiting for reply");
|
||||||
PrintAndLogEx(HINT, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]);
|
PrintAndLogEx(HINT, "Block %d data (" _GREEN_("%02X%02X") ") may need to be rewritten", block, brute.partial_key[0], brute.partial_key[1]);
|
||||||
return result;
|
return result;
|
||||||
} else if (PM3_SUCCESS != result) {
|
} else if (PM3_SUCCESS != result) {
|
||||||
|
@ -1571,7 +1578,7 @@ bool detect_4x70_block(void) {
|
||||||
int result = get_em4x70_info(&opts, &info);
|
int result = get_em4x70_info(&opts, &info);
|
||||||
|
|
||||||
if (result == PM3_ETIMEOUT) { // consider removing this output?
|
if (result == PM3_ETIMEOUT) { // consider removing this output?
|
||||||
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
}
|
}
|
||||||
return result == PM3_SUCCESS;
|
return result == PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -473,7 +473,7 @@ static int CmdHIDClone(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_HID_CLONE, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_LF_HID_CLONE, (uint8_t *)&payload, sizeof(payload));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HID_CLONE, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HID_CLONE, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
// Low frequency Hitag support
|
// Low frequency Hitag support
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include "cmdlfhitag.h"
|
#include "cmdlfhitag.h"
|
||||||
|
#include "cmdlfhitaghts.h"
|
||||||
|
#include "cmdlfhitagu.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "cmdparser.h" // command_t
|
#include "cmdparser.h" // command_t
|
||||||
#include "comms.h"
|
#include "comms.h"
|
||||||
|
@ -31,7 +33,6 @@
|
||||||
#include "pm3_cmd.h" // return codes
|
#include "pm3_cmd.h" // return codes
|
||||||
#include "hitag2/hitag2_crypto.h"
|
#include "hitag2/hitag2_crypto.h"
|
||||||
#include "util_posix.h" // msclock
|
#include "util_posix.h" // msclock
|
||||||
#include "cmdlfhitaghts.h"
|
|
||||||
|
|
||||||
static int CmdHelp(const char *Cmd);
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
|
@ -79,7 +80,7 @@ static size_t nbytes(size_t nbits) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int CmdLFHitagList(const char *Cmd) {
|
static int CmdLFHitagList(const char *Cmd) {
|
||||||
return CmdTraceListAlias(Cmd, "lf hitag", "hitag2");
|
return CmdTraceListAlias(Cmd, "lf hitag", "ht2");
|
||||||
/*
|
/*
|
||||||
uint8_t *got = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
|
uint8_t *got = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
|
||||||
if (!got) {
|
if (!got) {
|
||||||
|
@ -519,7 +520,7 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl
|
||||||
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 4000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 4000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -793,9 +794,6 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize,
|
||||||
free(binstr);
|
free(binstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *identify_transponder_hitag2(uint32_t uid) {
|
static const char *identify_transponder_hitag2(uint32_t uid) {
|
||||||
|
|
||||||
switch (uid) {
|
switch (uid) {
|
||||||
|
@ -827,12 +825,12 @@ static bool ht2_get_uid(uint32_t *uid) {
|
||||||
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 1500) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 1500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.status != PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID");
|
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag 2 UID");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1054,7 +1052,7 @@ static int CmdLFHitagRd(const char *Cmd) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -1143,7 +1141,7 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
if (resp.status != PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
@ -1305,7 +1303,7 @@ static int CmdLFHitagWriter(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_HITAG2_WRITE, (uint8_t *)&packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAG2_WRITE, (uint8_t *)&packet, sizeof(packet));
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAG2_WRITE, &resp, 4000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAG2_WRITE, &resp, 4000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1528,7 +1526,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
|
||||||
|
|
||||||
if (attempt == 0) {
|
if (attempt == 0) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1546,7 +1544,7 @@ static int CmdLFHitag2Dump(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet));
|
||||||
|
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 5000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 5000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
if (resp.status != PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
@ -2256,7 +2254,7 @@ static int CmdLFHitag2Crack2(const char *Cmd) {
|
||||||
|
|
||||||
if (attempt == 0) {
|
if (attempt == 0) {
|
||||||
PrintAndLogEx(NORMAL, "");
|
PrintAndLogEx(NORMAL, "");
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ESOFT;
|
return PM3_ESOFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2461,6 +2459,7 @@ static command_t CommandTable[] = {
|
||||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||||
{"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"},
|
{"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"},
|
||||||
{"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"},
|
{"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"},
|
||||||
|
{"htu", CmdLFHitagU, AlwaysAvailable, "{ Hitag µ/8265 operations }"},
|
||||||
{"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"},
|
{"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"},
|
||||||
{"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"},
|
{"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"},
|
||||||
{"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag 2 reader"},
|
{"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag 2 reader"},
|
||||||
|
|
|
@ -31,7 +31,6 @@ int ht2_read_uid(void);
|
||||||
int ht2_read_paxton(void);
|
int ht2_read_paxton(void);
|
||||||
void annotateHitag1(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response);
|
void annotateHitag1(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response);
|
||||||
void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response, const uint64_t *keys, uint32_t keycount, bool isdecrypted);
|
void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response, const uint64_t *keys, uint32_t keycount, bool isdecrypted);
|
||||||
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response);
|
|
||||||
|
|
||||||
void annotateHitag2_init(void);
|
void annotateHitag2_init(void);
|
||||||
bool hitag2_get_plain(uint8_t *plain, uint8_t *plen);
|
bool hitag2_get_plain(uint8_t *plain, uint8_t *plen);
|
||||||
|
|
|
@ -36,6 +36,84 @@
|
||||||
|
|
||||||
static int CmdHelp(const char *Cmd);
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
|
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t nbits, bool is_response) {
|
||||||
|
size_t exp_len = 0;
|
||||||
|
uint8_t command = 0;
|
||||||
|
|
||||||
|
if (is_response) {
|
||||||
|
// Handle responses
|
||||||
|
if (nbits == 32) {
|
||||||
|
exp_len = snprintf(exp, size, "UID: [%02X%02X%02X%02X]", cmd[0], cmd[1], cmd[2], cmd[3]);
|
||||||
|
} else if (nbits == 40) {
|
||||||
|
exp_len = snprintf(exp, size, "Data");
|
||||||
|
}
|
||||||
|
} else if (nbits >= 5) {
|
||||||
|
concatbits(&command, 0, cmd, 0, 5, false);
|
||||||
|
|
||||||
|
if (nbits == 5) {
|
||||||
|
concatbits(&command, 0, cmd, 0, 5, false);
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case HITAGS_UID_REQ_STD:
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Standard 00110)");
|
||||||
|
break;
|
||||||
|
case HITAGS_UID_REQ_ADV1:
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Advanced 11000)");
|
||||||
|
break;
|
||||||
|
case HITAGS_UID_REQ_ADV2:
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Advanced 11001)");
|
||||||
|
break;
|
||||||
|
case HITAGS_UID_REQ_FADV:
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "UID Request (Fast Advanced 11010)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (nbits == 4 + 8 + 8) {
|
||||||
|
concatbits(&command, 0, cmd, 0, 4, false);
|
||||||
|
|
||||||
|
if (command == HITAGS_READ_PAGE) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "READ");
|
||||||
|
} else if (command == HITAGS_WRITE_PAGE) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE");
|
||||||
|
} else if (command == HITAGS_READ_BLOCK) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "READ_BLOCK");
|
||||||
|
} else if (command == HITAGS_WRITE_BLOCK) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE_BLOCK");
|
||||||
|
} else if (command == HITAGS_QUIET) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "QUIET");
|
||||||
|
}
|
||||||
|
// Hitag 1 commands
|
||||||
|
else if (command == HITAG1_RDCPAGE) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "RDCPAGE");
|
||||||
|
} else if (command == HITAG1_RDCBLK) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "RDCBLK");
|
||||||
|
} else if (command == HITAG1_WRCPAGE) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "WRCPAGE");
|
||||||
|
} else if (command == HITAG1_WRCBLK) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "WRCBLK");
|
||||||
|
} else {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown (%02X)", command);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t page = 0;
|
||||||
|
concatbits(&page, 0, cmd, 5, 8, false);
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " Page: %d", page);
|
||||||
|
} else if (nbits == 32 + 8) {
|
||||||
|
concatbits(&command, 0, cmd, 0, 5, false);
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "Data");
|
||||||
|
} else if (nbits == 5 + 32 + 8 || nbits == 5 + 32 + 1 + 8) {
|
||||||
|
concatbits(&command, 0, cmd, 0, 5, false);
|
||||||
|
|
||||||
|
if (command == HITAGS_SELECT) {
|
||||||
|
uint8_t uid[4] = {0};
|
||||||
|
concatbits(uid, 0, cmd, 5, 32, false);
|
||||||
|
exp_len = snprintf(exp, size, "SELECT UID: %02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exp_len = snprintf(exp, size, "Invalid command (too short)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const char *hts_get_type_str(uint32_t uid) {
|
static const char *hts_get_type_str(uint32_t uid) {
|
||||||
// source 1: https://www.scorpio-lk.com/downloads/Tango/HITAG_Classification.pdf
|
// source 1: https://www.scorpio-lk.com/downloads/Tango/HITAG_Classification.pdf
|
||||||
// IDE Mark
|
// IDE Mark
|
||||||
|
@ -83,12 +161,12 @@ static bool hts_get_uid(uint32_t *uid) {
|
||||||
SendCommandNG(CMD_LF_HITAGS_UID, NULL, 0);
|
SendCommandNG(CMD_LF_HITAGS_UID, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_UID, &resp, 1500) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_UID, &resp, 1500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.status != PM3_SUCCESS) {
|
if (resp.status != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID");
|
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag S UID");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,10 +331,38 @@ static void print_error(int8_t reason) {
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// PM3_REASON_UNKNOWN
|
// PM3_REASON_UNKNOWN
|
||||||
PrintAndLogEx(DEBUG, "DEBUG: Error - Hitag S failed");
|
PrintAndLogEx(FAILED, "Error - Hitag S failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hitags_config_print(hitags_config_t config) {
|
||||||
|
PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"),
|
||||||
|
(const char *[]) {"Hitag S 32", "Hitag S 256", "Hitag S 2048", "Unknown Hitag S/8211"}[config.MEMT]);
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, " Authenticaion.... %s", config.auth ? _YELLOW_("Yes") : "No");
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, " TTF coding....... %s",
|
||||||
|
config.RES3 ? "FSK 0=RF/10 1=RF/8" : (const char *[]) {"Manchester", "Biphase"}[config.TTFC]);
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, " TTF data rate.... %s",
|
||||||
|
(const char *[]) {"4 kBit", "8 kBit", "2 kBit", "2 kBit and Pigeon Race Standard"}[config.TTFDR]);
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, " TTF mode......... %s",
|
||||||
|
(const char *[]) {
|
||||||
|
"TTF Mode disabled (= RTF Mode)",
|
||||||
|
"Page 4, Page 5",
|
||||||
|
"Page 4, Page 5, Page 6, Page 7",
|
||||||
|
"Page 4",
|
||||||
|
"TTF Mode disabled (= RTF Mode)",
|
||||||
|
"Page 4, Page 5, Page 6",
|
||||||
|
"Page 4, Page 5, Page 6, Page 7, Page 8",
|
||||||
|
"Page 4, Page 5, Page 6, Page 7, Page 8, Page 9, Page 10, Page 11",
|
||||||
|
}[config.RES0 << 2 | config.TTFM]);
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, " Config locked.... %s", config.LCON ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
PrintAndLogEx(INFO, " Key/PWD locked... %s", config.LKP ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
}
|
||||||
|
|
||||||
static int CmdLFHitagSRead(const char *Cmd) {
|
static int CmdLFHitagSRead(const char *Cmd) {
|
||||||
CLIParserContext *ctx;
|
CLIParserContext *ctx;
|
||||||
CLIParserInit(&ctx, "lf hitag hts rdbl",
|
CLIParserInit(&ctx, "lf hitag hts rdbl",
|
||||||
|
@ -314,7 +420,7 @@ static int CmdLFHitagSRead(const char *Cmd) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -501,7 +607,7 @@ static int CmdLFHitagSDump(const char *Cmd) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 5000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,7 +709,7 @@ static int CmdLFHitagSRestore(const char *Cmd) {
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
|
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_READ, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
free(dump);
|
free(dump);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -654,7 +760,7 @@ static int CmdLFHitagSRestore(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
||||||
|
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
free(dump);
|
free(dump);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -727,7 +833,7 @@ static int CmdLFHitagSRestore(const char *Cmd) {
|
||||||
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet));
|
||||||
|
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 2000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
free(dump);
|
free(dump);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -812,7 +918,7 @@ static int CmdLFHitagSWrite(const char *Cmd) {
|
||||||
|
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
|
if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,59 +974,28 @@ static int CmdLFHitagSSim(const char *Cmd) {
|
||||||
"Simulate Hitag S transponder\n"
|
"Simulate Hitag S transponder\n"
|
||||||
"You need to `lf hitag hts eload` first",
|
"You need to `lf hitag hts eload` first",
|
||||||
"lf hitag hts sim\n"
|
"lf hitag hts sim\n"
|
||||||
"lf hitag hts sim --82xx");
|
"lf hitag hts sim --82xx\n"
|
||||||
|
"lf hitag hts sim -t 30 -> set threshold to 30");
|
||||||
|
|
||||||
void *argtable[] = {
|
void *argtable[] = {
|
||||||
arg_param_begin,
|
arg_param_begin,
|
||||||
arg_lit0("8", "82xx", "simulate 8268/8310"),
|
arg_lit0("8", "82xx", "simulate 8268/8310"),
|
||||||
|
arg_int0("t", "threshold", "<dec>", "set edge detect threshold (def: 127)"),
|
||||||
arg_param_end
|
arg_param_end
|
||||||
};
|
};
|
||||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
// bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet
|
// bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet
|
||||||
|
int threshold = arg_get_int_def(ctx, 2, 127);
|
||||||
CLIParserFree(ctx);
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, threshold, 0, NULL, 0);
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CmdLFHitagSList(const char *Cmd) {
|
static int CmdLFHitagSList(const char *Cmd) {
|
||||||
return CmdTraceListAlias(Cmd, "lf hitag hts", "hitags");
|
return CmdTraceListAlias(Cmd, "lf hitag hts", "hts");
|
||||||
}
|
|
||||||
|
|
||||||
void hitags_config_print(hitags_config_t config) {
|
|
||||||
PrintAndLogEx(INFO, " Memory type...... " _GREEN_("%s"),
|
|
||||||
(const char *[]) {
|
|
||||||
"Hitag S 32", "Hitag S 256", "Hitag S 2048",
|
|
||||||
"Unknown Hitag S/8211"
|
|
||||||
}[config.MEMT]);
|
|
||||||
|
|
||||||
PrintAndLogEx(INFO, " Authenticaion.... %s", config.auth ? _YELLOW_("Yes") : "No");
|
|
||||||
|
|
||||||
PrintAndLogEx(INFO, " TTF coding....... %s",
|
|
||||||
config.RES3 ? "FSK 0=RF/10 1=RF/8" : (const char *[]) {"Manchester", "Biphase"}[config.TTFC]);
|
|
||||||
|
|
||||||
PrintAndLogEx(INFO, " TTF data rate.... %s",
|
|
||||||
(const char *[]) {
|
|
||||||
"4 kBit", "8 kBit", "2 kBit",
|
|
||||||
"2 kBit and Pigeon Race Standard"
|
|
||||||
}[config.TTFDR]);
|
|
||||||
|
|
||||||
PrintAndLogEx(INFO, " TTF mode......... %s",
|
|
||||||
(const char *[]) {
|
|
||||||
"TTF Mode disabled (= RTF Mode)",
|
|
||||||
"Page 4, Page 5",
|
|
||||||
"Page 4, Page 5, Page 6, Page 7",
|
|
||||||
"Page 4",
|
|
||||||
"TTF Mode disabled (= RTF Mode)",
|
|
||||||
"Page 4, Page 5, Page 6",
|
|
||||||
"Page 4, Page 5, Page 6, Page 7, Page 8",
|
|
||||||
"Page 4, Page 5, Page 6, Page 7, Page 8, Page 9, Page 10, Page 11",
|
|
||||||
}[config.RES0 << 2 | config.TTFM]);
|
|
||||||
|
|
||||||
PrintAndLogEx(INFO, " Config locked.... %s", config.LCON ? _RED_("Yes") : _GREEN_("No"));
|
|
||||||
PrintAndLogEx(INFO, " Key/PWD locked... %s", config.LKP ? _RED_("Yes") : _GREEN_("No"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static command_t CommandTable[] = {
|
static command_t CommandTable[] = {
|
||||||
|
|
|
@ -22,9 +22,10 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "hitag.h"
|
#include "hitag.h"
|
||||||
|
|
||||||
|
void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t nbits, bool is_response);
|
||||||
|
|
||||||
int CmdLFHitagS(const char *Cmd);
|
int CmdLFHitagS(const char *Cmd);
|
||||||
|
|
||||||
int read_hts_uid(void);
|
int read_hts_uid(void);
|
||||||
void hitags_config_print(hitags_config_t config);
|
|
||||||
|
|
||||||
#endif //CMDLFHITAGS_H__
|
#endif //CMDLFHITAGS_H__
|
||||||
|
|
731
client/src/cmdlfhitagu.c
Normal file
731
client/src/cmdlfhitagu.c
Normal file
|
@ -0,0 +1,731 @@
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for the text of the license.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Low frequency Hitag µ support
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#include "cmdlfhitagu.h"
|
||||||
|
|
||||||
|
#include "cliparser.h"
|
||||||
|
#include "cmddata.h" // setDemodBuff
|
||||||
|
#include "cmdparser.h" // command_t
|
||||||
|
#include "cmdtrace.h"
|
||||||
|
#include "commonutil.h"
|
||||||
|
#include "comms.h"
|
||||||
|
#include "crc16.h"
|
||||||
|
#include "fileutils.h" // savefile
|
||||||
|
#include "graph.h" // MAX_GRAPH_TRACE_LEN
|
||||||
|
#include "hitag.h"
|
||||||
|
#include "hitag2/hitag2_crypto.h"
|
||||||
|
#include "lfdemod.h"
|
||||||
|
#include "pm3_cmd.h" // return codes
|
||||||
|
#include "protocols.h" // defines
|
||||||
|
#include "util_posix.h" // msclock
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
static int CmdHelp(const char *Cmd);
|
||||||
|
|
||||||
|
uint8_t hitagu_CRC_check(uint8_t *d, uint32_t nbit) {
|
||||||
|
if (nbit < 9) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Crc16(d, nbit, 0, CRC16_POLY_CCITT, false, false) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void annotateHitagU(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) {
|
||||||
|
|
||||||
|
if (is_response) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
uint8_t flag = reflect8(cmd[0]) & 0x1F;
|
||||||
|
uint8_t command = ((reflect8(cmd[0]) >> 5) & 0x07) | ((reflect8(cmd[1]) & 0x07) << 3);
|
||||||
|
bool has_uid = false;
|
||||||
|
|
||||||
|
size_t exp_len = snprintf(exp, size, "Flg:");
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_PEXT) == HITAGU_FLAG_PEXT) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " PEXT");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_INV) == HITAGU_FLAG_INV) {
|
||||||
|
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " INV");
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_RFU) == HITAGU_FLAG_RFU) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " RFU");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_NOS) == HITAGU_FLAG_NOS) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " NOS");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_SEL) == HITAGU_FLAG_SEL) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " SEL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_ADR) == HITAGU_FLAG_ADR) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " ADR");
|
||||||
|
has_uid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flag & HITAGU_FLAG_CRCT) == HITAGU_FLAG_CRCT) {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " CRCT");
|
||||||
|
}
|
||||||
|
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "|Cmd: ");
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case HITAGU_CMD_LOGIN: {
|
||||||
|
|
||||||
|
bool has_mfc = false;
|
||||||
|
|
||||||
|
if (cmdsize == (6 + (has_uid * HITAGU_UID_SIZE)) || cmdsize == (8 + (has_uid * HITAGU_UID_SIZE))) {
|
||||||
|
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "8265 LOGIN");
|
||||||
|
|
||||||
|
} else if (cmdsize == (7 + (has_uid * HITAGU_UID_SIZE)) || cmdsize == (9 + (has_uid * HITAGU_UID_SIZE))) {
|
||||||
|
|
||||||
|
uint8_t mfc = 0;
|
||||||
|
concatbits(&mfc, 0, cmd, 5 + 6 + 8 + 32, 8, false);
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "LOGIN mfc:%02x ", mfc);
|
||||||
|
has_mfc = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_uid) {
|
||||||
|
uint8_t uid[HITAGU_UID_SIZE] = {0};
|
||||||
|
concatbits(uid, 0, cmd, 5 + 6 + has_mfc * 8 + 32, HITAGU_UID_SIZE * 8, false);
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " uid:%s", sprint_hex_inrow(uid, HITAGU_UID_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t password[HITAG_PASSWORD_SIZE] = {0};
|
||||||
|
concatbits(password, 0, cmd, 5 + 6 + has_mfc * 8 + has_uid * HITAGU_UID_SIZE * 8, HITAG_PASSWORD_SIZE * 8, false);
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, " pwd:%s", sprint_hex_inrow(password, HITAG_PASSWORD_SIZE));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_INVENTORY: {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "INVENTORY");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_READ_MULTIPLE_BLOCK: {
|
||||||
|
uint8_t block_addr = 0;
|
||||||
|
concatbits(&block_addr, 0, cmd, 5 + 6, 8, false);
|
||||||
|
|
||||||
|
uint8_t block_count = 0;
|
||||||
|
concatbits(&block_count, 0, cmd, 5 + 6 + 8, 8, false);
|
||||||
|
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "READ MULTIPLE BLOCK start:%d num:%d"
|
||||||
|
, reflect8(block_addr)
|
||||||
|
, reflect8(block_count)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_WRITE_SINGLE_BLOCK: {
|
||||||
|
uint8_t block_addr = 0;
|
||||||
|
concatbits(&block_addr, 0, cmd, 5 + 6, 8, false);
|
||||||
|
|
||||||
|
uint8_t block_data[4] = {0};
|
||||||
|
concatbits(block_data, 0, cmd, 5 + 6 + 8, 32, false);
|
||||||
|
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "WRITE SINGLE BLOCK start:%d data:[%s]"
|
||||||
|
, reflect8(block_addr)
|
||||||
|
, sprint_hex_inrow(block_data, 4)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_SELECT: {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "SELECT");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_SYSINFO: {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "GET SYSTEM INFORMATION");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_READ_UID: {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "READ UID");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HITAGU_CMD_STAY_QUIET: {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "STAY QUIET");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
exp_len += snprintf(exp + exp_len, size - exp_len, "Unknown 0x%02X", command);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool htu_get_uid(uint64_t *uid) {
|
||||||
|
clearCommandBuffer();
|
||||||
|
SendCommandNG(CMD_LF_HITAGU_UID, NULL, 0);
|
||||||
|
PacketResponseNG resp;
|
||||||
|
if (WaitForResponseTimeout(CMD_LF_HITAGU_UID, &resp, 1500) == false) {
|
||||||
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting Hitag µ UID");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uid) {
|
||||||
|
*uid = bytes_to_num(resp.data.asBytes, HITAGU_UID_SIZE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_htu_uid(void) {
|
||||||
|
uint64_t uid = 0;
|
||||||
|
if (htu_get_uid(&uid) == false) {
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, "UID.... " _GREEN_("%012llX"), uid);
|
||||||
|
// PrintAndLogEx(SUCCESS, "TYPE... " _GREEN_("%s"), htu_get_type_str(uid));
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_hitagu_common_args(CLIParserContext *ctx, lf_hitag_data_t *const packet) {
|
||||||
|
bool use_82xx = arg_get_lit(ctx, 1);
|
||||||
|
bool use_password = false;
|
||||||
|
uint8_t key[HITAG_PASSWORD_SIZE];
|
||||||
|
int key_len = 0;
|
||||||
|
|
||||||
|
int res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, HITAG_PASSWORD_SIZE, &key_len);
|
||||||
|
if (res != 0) {
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key_len != 0 && key_len != HITAG_PASSWORD_SIZE) {
|
||||||
|
PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", key_len);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
// complete options
|
||||||
|
if (key_len == 0 && use_82xx) {
|
||||||
|
memcpy(key, "\x00\x00\x00\x00", 4);
|
||||||
|
key_len = 4;
|
||||||
|
} else if (key_len != 0) {
|
||||||
|
use_password = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(packet, 0, sizeof(*packet));
|
||||||
|
|
||||||
|
if (use_82xx) {
|
||||||
|
packet->cmd = HTUF_82xx;
|
||||||
|
memcpy(packet->pwd, key, sizeof(packet->pwd));
|
||||||
|
PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in 82xx mode");
|
||||||
|
|
||||||
|
} else if (use_password) {
|
||||||
|
packet->cmd = HTUF_PASSWORD;
|
||||||
|
memcpy(packet->pwd, key, sizeof(packet->pwd));
|
||||||
|
PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag µ") " in password mode");
|
||||||
|
|
||||||
|
} else {
|
||||||
|
packet->cmd = HTUF_PLAIN;
|
||||||
|
memcpy(packet->pwd, key, sizeof(packet->pwd));
|
||||||
|
PrintAndLogEx(INFO, "Access " _YELLOW_("Hitag µ") " in Plain mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_error(int8_t reason) {
|
||||||
|
|
||||||
|
//todo: USE ENUM OR DEFINES
|
||||||
|
switch (reason) {
|
||||||
|
case 0: {
|
||||||
|
PrintAndLogEx(INFO, "No data");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -2: {
|
||||||
|
PrintAndLogEx(FAILED, "READ UID failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -3: {
|
||||||
|
PrintAndLogEx(FAILED, "Get System Information / Config failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -4: {
|
||||||
|
PrintAndLogEx(FAILED, "Login failed! Wrong password?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -5: {
|
||||||
|
PrintAndLogEx(FAILED, "No write access on block. Not authorized?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -6: {
|
||||||
|
PrintAndLogEx(FAILED, "Response CRC invalid!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case -7: {
|
||||||
|
PrintAndLogEx(FAILED, "Read block failed!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// PM3_REASON_UNKNOWN
|
||||||
|
PrintAndLogEx(FAILED, "Error - Hitag µ failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hitagu_config_print(hitagu_config_t config) {
|
||||||
|
PrintAndLogEx(INFO, " Data Rate......... %s", (const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "Reserved"}[config.datarate]);
|
||||||
|
PrintAndLogEx(INFO, " Encoding.......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester"));
|
||||||
|
PrintAndLogEx(INFO, " Password Protect W Bit 0-127(block 0-3) %s", config.pwdW0_127 ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
PrintAndLogEx(INFO, " Password Protect W Bit 128-511(block 4-15) %s", config.pwdW128_511 ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
PrintAndLogEx(INFO, " Password Protect W Bit 512-Max(block 16-Max) %s", config.pwdW512_max ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
PrintAndLogEx(INFO, " Password Protect RW Bit 512-Max(block 16-Max) %s", config.pwdRW512_max ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hitagu8265_config_print(hitagu82xx_config_t config) {
|
||||||
|
PrintAndLogEx(INFO, " Config Byte0: %s", sprint_hex((uint8_t *)&config, sizeof(config))); // for debug
|
||||||
|
// Check if datarate_override is set
|
||||||
|
if (config.datarate_override) {
|
||||||
|
PrintAndLogEx(INFO, " Data Rate........ %s", _YELLOW_("2 kbit/s"));
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, " Data Rate........ %s",
|
||||||
|
(const char *[]) {"2 kbit/s", "4 kbit/s", "8 kbit/s", "2 kbit/s"}[config.datarate]);
|
||||||
|
}
|
||||||
|
PrintAndLogEx(INFO, " Rate Override.... %s", config.datarate_override ? _RED_("Yes") : _GREEN_("No"));
|
||||||
|
PrintAndLogEx(INFO, " Encoding......... %s", config.encoding ? _YELLOW_("Bi-phase") : _YELLOW_("Manchester"));
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, " TTF mode ........ %s",
|
||||||
|
(const char *[]) {
|
||||||
|
"Block 0, Block 1, Block 2, Block 3",
|
||||||
|
"Block 0, Block 1",
|
||||||
|
"Block 0, Block 1, Block 2, Block 3",
|
||||||
|
"Block 0, Block 1, Block 2, Block 3",
|
||||||
|
}[config.ttf_mode]);
|
||||||
|
PrintAndLogEx(INFO, " TTF.............. %s", config.ttf ? _GREEN_("Enabled") : _RED_("Disabled"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdLFHitagURead(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "lf hitag htu rdbl",
|
||||||
|
"Read Hitag µ memory.\n\n"
|
||||||
|
" 82xx password mode: \n"
|
||||||
|
" - default password 00000000\n",
|
||||||
|
" lf hitag htu rdbl -p 1 -> Hitag µ, plain mode\n"
|
||||||
|
" lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass\n"
|
||||||
|
" lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode\n");
|
||||||
|
|
||||||
|
void *argtable[] = {arg_param_begin,
|
||||||
|
arg_lit0("8", "82xx", "82xx mode"),
|
||||||
|
arg_str0("k", "key", "<hex>", "pwd, 4 hex bytes"),
|
||||||
|
arg_int0("p", "page", "<dec>", "block address to read from (def: 0)"),
|
||||||
|
arg_int0("c", "count", "<dec>", "how many blocks to read. '0' reads all blocks (def: 1)"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
|
lf_hitag_data_t packet;
|
||||||
|
|
||||||
|
if (process_hitagu_common_args(ctx, &packet) < 0) {
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t page = arg_get_int_def(ctx, 3, 0);
|
||||||
|
|
||||||
|
if (page >= HITAGU_MAX_BLOCKS) {
|
||||||
|
PrintAndLogEx(WARNING, "Block address out-of-range. Max is 255, got %u", page);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t count = arg_get_int_def(ctx, 4, 1);
|
||||||
|
|
||||||
|
if (count > HITAGU_MAX_BLOCKS) {
|
||||||
|
PrintAndLogEx(WARNING, "No more than %d blocks can be read at once", HITAGU_MAX_BLOCKS);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
packet.page = page;
|
||||||
|
packet.page_count = count;
|
||||||
|
// packet.mode = 1; // for debug
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, "Read Hitag µ memory block " _YELLOW_("%d") ", count " _YELLOW_("%d"), page, count);
|
||||||
|
|
||||||
|
clearCommandBuffer();
|
||||||
|
SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet));
|
||||||
|
|
||||||
|
PacketResponseNG resp;
|
||||||
|
if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 2000) == false) {
|
||||||
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
|
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||||
|
return PM3_ETIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
print_error(resp.reason);
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes;
|
||||||
|
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
|
||||||
|
|
||||||
|
int user_blocks;
|
||||||
|
uint8_t icr = card->icr;
|
||||||
|
|
||||||
|
if (icr == HITAGU_ICR_STANDARD) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_STANDARD;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else if (icr == HITAGU_ICR_ADVANCED) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_ADVANCED;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else if (icr == HITAGU_ICR_ADVANCED_PLUS) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else if (icr == HITAGU_ICR_8265) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_8265;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_STANDARD;
|
||||||
|
PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) {
|
||||||
|
hitagu8265_config_print(card->config_page.s82xx);
|
||||||
|
} else {
|
||||||
|
hitagu_config_print(card->config_page.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Data") " ---------------------------");
|
||||||
|
PrintAndLogEx(INFO, " # | 00 01 02 03 | ascii | perm | info");
|
||||||
|
PrintAndLogEx(INFO, "----+-------------+-------+------+------");
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
count = (user_blocks > page) ? (user_blocks - page) : HITAGU_MAX_BLOCKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
|
||||||
|
int page_addr = page + i;
|
||||||
|
if (page_addr >= HITAGU_MAX_BLOCKS) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card->pages_reason[i] > 0) {
|
||||||
|
PrintAndLogEx(SUCCESS, "% 3u | %s | " NOLF, page_addr, sprint_hex_ascii(card->pages[i], HITAGU_BLOCK_SIZE));
|
||||||
|
|
||||||
|
// access right
|
||||||
|
// 82xx
|
||||||
|
if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr != HITAGU_PASSWORD_PADR) {
|
||||||
|
PrintAndLogEx(NORMAL, _YELLOW_("RO ") NOLF);
|
||||||
|
} else if ((packet.cmd == HTUF_82xx || icr == HITAGU_ICR_8265) && page_addr == HITAGU_PASSWORD_PADR) {
|
||||||
|
PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF);
|
||||||
|
// Hitag µ
|
||||||
|
} else if (page_addr < HITAGU_MAX_PAGE_STANDARD) {
|
||||||
|
|
||||||
|
if (card->config_page.s.pwdW0_127) {
|
||||||
|
PrintAndLogEx(NORMAL, _RED_("RO ") NOLF);
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (HITAGU_MAX_PAGE_STANDARD <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED) {
|
||||||
|
|
||||||
|
if (card->config_page.s.pwdW128_511) {
|
||||||
|
PrintAndLogEx(NORMAL, _RED_("RO ") NOLF);
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (HITAGU_MAX_PAGE_ADVANCED <= page_addr && page_addr < HITAGU_MAX_PAGE_ADVANCED_PLUS) {
|
||||||
|
|
||||||
|
if (card->config_page.s.pwdRW512_max) {
|
||||||
|
PrintAndLogEx(NORMAL, _RED_("R/WP") NOLF);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (card->config_page.s.pwdW512_max) {
|
||||||
|
PrintAndLogEx(NORMAL, _RED_("RO ") NOLF);
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(NORMAL, _GREEN_("RW ") NOLF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(NORMAL, _YELLOW_("UNK ") NOLF);
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(NORMAL, " | " NOLF);
|
||||||
|
|
||||||
|
// info
|
||||||
|
if (page_addr == HITAGU_PASSWORD_PADR) {
|
||||||
|
PrintAndLogEx(NORMAL, "Password");
|
||||||
|
} else if (page_addr == HITAGU_CONFIG_PADR) {
|
||||||
|
PrintAndLogEx(NORMAL, "Config");
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(NORMAL, "Data");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr);
|
||||||
|
print_error(card->pages_reason[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(INFO, "----+-------------+-------+------+------");
|
||||||
|
PrintAndLogEx(INFO, " " _YELLOW_("RO") " = Read without password, write with password");
|
||||||
|
PrintAndLogEx(INFO, " " _GREEN_("R/W") " = Read and write without password");
|
||||||
|
PrintAndLogEx(INFO, " " _RED_("R/WP") " = Read and write with password");
|
||||||
|
PrintAndLogEx(INFO, "----------------------------------------");
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdLFHitagUDump(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "lf hitag htu dump",
|
||||||
|
"Read all Hitag µ memory and save to file\n"
|
||||||
|
" 82xx password mode: \n"
|
||||||
|
" - default password 00000000\n",
|
||||||
|
"lf hitag htu dump --82xx -> use def pwd\n"
|
||||||
|
"lf hitag htu dump --82xx -k 9AC4999C -> pwd mode\n");
|
||||||
|
|
||||||
|
void *argtable[] = {arg_param_begin,
|
||||||
|
arg_lit0("8", "82xx", "82xx mode"),
|
||||||
|
arg_str0("k", "key", "<hex>", "pwd, 4 hex bytes"),
|
||||||
|
arg_str0("f", "file", "<fn>", "specify file name"),
|
||||||
|
arg_lit0(NULL, "ns", "no save to file"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
|
||||||
|
lf_hitag_data_t packet;
|
||||||
|
memset(&packet, 0, sizeof(packet));
|
||||||
|
|
||||||
|
if (process_hitagu_common_args(ctx, &packet) < 0) {
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fnlen = 0;
|
||||||
|
char filename[FILE_PATH_SIZE] = {0};
|
||||||
|
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||||
|
|
||||||
|
bool nosave = arg_get_lit(ctx, 4);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
// read all pages
|
||||||
|
packet.page = 0;
|
||||||
|
packet.page_count = 0;
|
||||||
|
|
||||||
|
clearCommandBuffer();
|
||||||
|
SendCommandNG(CMD_LF_HITAGU_READ, (uint8_t *)&packet, sizeof(packet));
|
||||||
|
|
||||||
|
PacketResponseNG resp;
|
||||||
|
if (WaitForResponseTimeout(CMD_LF_HITAGU_READ, &resp, 5000) == false) {
|
||||||
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
|
return PM3_ETIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
print_error(resp.reason);
|
||||||
|
return PM3_ESOFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
lf_htu_read_response_t *card = (lf_htu_read_response_t *)resp.data.asBytes;
|
||||||
|
|
||||||
|
int user_blocks;
|
||||||
|
uint8_t icr = card->icr;
|
||||||
|
|
||||||
|
if (icr == HITAGU_ICR_STANDARD) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_STANDARD;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ Standard (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else if (icr == HITAGU_ICR_ADVANCED) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_ADVANCED;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ Advanced (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else if (icr == HITAGU_ICR_ADVANCED_PLUS) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_ADVANCED_PLUS;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ Advanced+ (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else if (icr == HITAGU_ICR_8265) {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_8265;
|
||||||
|
PrintAndLogEx(INFO, "Hitag µ 8265 (ICR=0x%02X), user blocks: 0x%02X", icr, user_blocks);
|
||||||
|
} else {
|
||||||
|
user_blocks = HITAGU_MAX_PAGE_STANDARD;
|
||||||
|
PrintAndLogEx(INFO, "Unknown ICR (0x%02X)", icr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mem_size = (user_blocks + 2) * HITAGU_BLOCK_SIZE;
|
||||||
|
|
||||||
|
hitagu_config_t config = card->config_page.s;
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
|
||||||
|
hitagu_config_print(config);
|
||||||
|
|
||||||
|
if (nosave) {
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
PrintAndLogEx(INFO, "Called with no save option");
|
||||||
|
PrintAndLogEx(NORMAL, "");
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fnlen < 1) {
|
||||||
|
char *fptr = filename;
|
||||||
|
fptr += snprintf(filename, sizeof(filename), "lf-htu-");
|
||||||
|
FillFileNameByUID(fptr, card->uid, "-dump", HITAGU_UID_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pm3_save_dump(filename, (uint8_t *)card->pages, mem_size, jsfHitag);
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdLFHitagUWrite(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "lf hitag htu wrbl",
|
||||||
|
"Write a block in Hitag µ memory.\n"
|
||||||
|
" 82xx password mode: \n"
|
||||||
|
" - default password 00000000\n",
|
||||||
|
" lf hitag htu wrbl -p 6 -d 01020304 -> Hitag µ, plain mode\n"
|
||||||
|
" lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd\n"
|
||||||
|
" lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode\n");
|
||||||
|
|
||||||
|
void *argtable[] = {arg_param_begin,
|
||||||
|
arg_lit0("8", "82xx", "82xx mode"),
|
||||||
|
arg_str0("k", "key", "<hex>", "pwd, 4 hex bytes"),
|
||||||
|
arg_int1("p", "page", "<dec>", "block address to write to"),
|
||||||
|
arg_str1("d", "data", "<hex>", "data, 4 hex bytes"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||||
|
|
||||||
|
lf_hitag_data_t packet;
|
||||||
|
|
||||||
|
if (process_hitagu_common_args(ctx, &packet) < 0) return PM3_EINVARG;
|
||||||
|
|
||||||
|
int page = arg_get_int_def(ctx, 3, 0);
|
||||||
|
|
||||||
|
uint8_t data[HITAGU_BLOCK_SIZE];
|
||||||
|
int data_len = 0;
|
||||||
|
|
||||||
|
int res = CLIParamHexToBuf(arg_get_str(ctx, 4), data, HITAGU_BLOCK_SIZE, &data_len);
|
||||||
|
if (res != 0) {
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
return PM3_EINVARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
packet.page = page;
|
||||||
|
memcpy(packet.data, data, sizeof(packet.data));
|
||||||
|
|
||||||
|
clearCommandBuffer();
|
||||||
|
SendCommandNG(CMD_LF_HITAGU_WRITE, (uint8_t *)&packet, sizeof(packet));
|
||||||
|
|
||||||
|
PacketResponseNG resp;
|
||||||
|
if (WaitForResponseTimeout(CMD_LF_HITAGU_WRITE, &resp, 4000) == false) {
|
||||||
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
|
return PM3_ETIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status == PM3_ETEAROFF) {
|
||||||
|
PrintAndLogEx(INFO, "Writing tear off triggered");
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp.status != PM3_SUCCESS) {
|
||||||
|
print_error(resp.reason);
|
||||||
|
return resp.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )");
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdLFHitagUReader(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "lf hitag htu reader", "Act as a Hitag µ reader. Look for Hitag µ tags until Enter or the pm3 button is pressed\n",
|
||||||
|
"lf hitag htu reader\n"
|
||||||
|
"lf hitag htu reader -@ -> Continuous mode");
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin, arg_lit0("@", NULL, "continuous reader mode"), arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
bool cm = arg_get_lit(ctx, 1);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
if (cm) {
|
||||||
|
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
read_htu_uid();
|
||||||
|
} while (cm && kbd_enter_pressed() == false);
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdLFHitagUSim(const char *Cmd) {
|
||||||
|
CLIParserContext *ctx;
|
||||||
|
CLIParserInit(&ctx, "lf hitag htu sim",
|
||||||
|
"Simulate Hitag µ transponder\n"
|
||||||
|
"You need to `lf hitag htu eload` first",
|
||||||
|
"lf hitag htu sim\n"
|
||||||
|
"lf hitag htu sim --82xx\n"
|
||||||
|
"lf hitag htu sim -t 30 -> set threshold to 30");
|
||||||
|
|
||||||
|
void *argtable[] = {
|
||||||
|
arg_param_begin,
|
||||||
|
arg_lit0("8", "82xx", "simulate 82xx"),
|
||||||
|
arg_int0("t", "threshold", "<dec>", "set edge detect threshold (def: 127)"),
|
||||||
|
arg_param_end
|
||||||
|
};
|
||||||
|
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||||
|
|
||||||
|
// bool use_82xx = arg_get_lit(ctx, 1); // not implemented yet
|
||||||
|
int threshold = arg_get_int_def(ctx, 2, 127);
|
||||||
|
CLIParserFree(ctx);
|
||||||
|
|
||||||
|
clearCommandBuffer();
|
||||||
|
SendCommandMIX(CMD_LF_HITAGU_SIMULATE, false, threshold, 0, NULL, 0);
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CmdLFHitagUList(const char *Cmd) { return CmdTraceListAlias(Cmd, "lf hitag htu", "htu"); }
|
||||||
|
|
||||||
|
static command_t CommandTable[] = {
|
||||||
|
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||||
|
{"list", CmdLFHitagUList, AlwaysAvailable, "List Hitag µ trace history"},
|
||||||
|
{"-----------", CmdHelp, IfPm3Hitag, "----------- " _CYAN_("General") " -----------"},
|
||||||
|
{"reader", CmdLFHitagUReader, IfPm3Hitag, "Act like a Hitag µ reader"},
|
||||||
|
{"rdbl", CmdLFHitagURead, IfPm3Hitag, "Read Hitag µ block"},
|
||||||
|
{"dump", CmdLFHitagUDump, IfPm3Hitag, "Dump Hitag µ blocks to a file"},
|
||||||
|
{"wrbl", CmdLFHitagUWrite, IfPm3Hitag, "Write Hitag µ block"},
|
||||||
|
{"-----------", CmdHelp, IfPm3Hitag, "----------- " _CYAN_("Simulation") " -----------"},
|
||||||
|
{"sim", CmdLFHitagUSim, IfPm3Hitag, "Simulate Hitag µ transponder"},
|
||||||
|
{NULL, NULL, 0, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int CmdHelp(const char *Cmd) {
|
||||||
|
(void)Cmd; // Cmd is not used so far
|
||||||
|
CmdsHelp(CommandTable);
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CmdLFHitagU(const char *Cmd) {
|
||||||
|
clearCommandBuffer();
|
||||||
|
return CmdsParse(CommandTable, Cmd);
|
||||||
|
}
|
32
client/src/cmdlfhitagu.h
Normal file
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__
|
|
@ -1551,6 +1551,7 @@ bool testKnownConfigBlock(uint32_t block0) {
|
||||||
case T55X7_JABLOTRON_CONFIG_BLOCK:
|
case T55X7_JABLOTRON_CONFIG_BLOCK:
|
||||||
case T55X7_PYRONIX_CONFIG_BLOCK:
|
case T55X7_PYRONIX_CONFIG_BLOCK:
|
||||||
case T55X7_TEXECOM_CONFIG_BLOCK:
|
case T55X7_TEXECOM_CONFIG_BLOCK:
|
||||||
|
case T55X7_BETECH_CONFIG_BLOCK:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -2302,12 +2303,15 @@ static void printT5x7KnownBlock0(uint32_t b0) {
|
||||||
case T55X7_TEXECOM_CONFIG_BLOCK:
|
case T55X7_TEXECOM_CONFIG_BLOCK:
|
||||||
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Texecom ");
|
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Texecom ");
|
||||||
break;
|
break;
|
||||||
|
case T55X7_BETECH_CONFIG_BLOCK:
|
||||||
|
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Be-Tech ");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(s) > 0) {
|
if (strlen(s) > 0) {
|
||||||
PrintAndLogEx(SUCCESS, "Config block match : " _YELLOW_("%s"), s);
|
PrintAndLogEx(SUCCESS, "Config block match... " _YELLOW_("%s"), s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@
|
||||||
#define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ...
|
#define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ...
|
||||||
#define T55X7_PYRONIX_CONFIG_BLOCK 0x00088C40 // ASK, Manchester, data rate 32, 2 data blocks
|
#define T55X7_PYRONIX_CONFIG_BLOCK 0x00088C40 // ASK, Manchester, data rate 32, 2 data blocks
|
||||||
#define T55X7_TEXECOM_CONFIG_BLOCK 0x001C8020 // ASK, Manchester, data rate 128, 1 data block
|
#define T55X7_TEXECOM_CONFIG_BLOCK 0x001C8020 // ASK, Manchester, data rate 128, 1 data block
|
||||||
|
#define T55X7_BETECH_CONFIG_BLOCK 0x001480E0 // ASK, Manchester, data rate 64, 7 data block
|
||||||
|
|
||||||
// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag
|
// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag
|
||||||
// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo,
|
// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo,
|
||||||
|
|
|
@ -658,7 +658,7 @@ static int CmdSmartUpgrade(const char *Cmd) {
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandNG(CMD_SMART_UPLOAD, (uint8_t *)&upload, sizeof(upload));
|
SendCommandNG(CMD_SMART_UPLOAD, (uint8_t *)&upload, sizeof(upload));
|
||||||
if (!WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000)) {
|
if (!WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000)) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
free(firmware);
|
free(firmware);
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -690,7 +690,7 @@ static int CmdSmartUpgrade(const char *Cmd) {
|
||||||
free(firmware);
|
free(firmware);
|
||||||
SendCommandNG(CMD_SMART_UPGRADE, (uint8_t *)&payload, sizeof(payload));
|
SendCommandNG(CMD_SMART_UPGRADE, (uint8_t *)&payload, sizeof(payload));
|
||||||
if (!WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500)) {
|
if (!WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500)) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include "comms.h" // for sending cmds to device. GetFromBigBuf
|
#include "comms.h" // for sending cmds to device. GetFromBigBuf
|
||||||
#include "fileutils.h" // for saveFile
|
#include "fileutils.h" // for saveFile
|
||||||
#include "cmdlfhitag.h" // annotate hitag
|
#include "cmdlfhitag.h" // annotate hitag
|
||||||
|
#include "cmdlfhitaghts.h" // annotate hitags
|
||||||
|
#include "cmdlfhitagu.h" // annotate hitagu
|
||||||
#include "pm3_cmd.h" // tracelog_hdr_t
|
#include "pm3_cmd.h" // tracelog_hdr_t
|
||||||
#include "cliparser.h" // args..
|
#include "cliparser.h" // args..
|
||||||
|
|
||||||
|
@ -585,8 +587,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
||||||
case PROTO_HITAG1:
|
case PROTO_HITAG1:
|
||||||
case PROTO_HITAGS:
|
case PROTO_HITAGS:
|
||||||
crcStatus = hitag1_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8));
|
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_HITAG2:
|
||||||
|
case PROTO_CRYPTORF:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -624,6 +630,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
||||||
&& protocol != PROTO_HITAG1
|
&& protocol != PROTO_HITAG1
|
||||||
&& protocol != PROTO_HITAG2
|
&& protocol != PROTO_HITAG2
|
||||||
&& protocol != PROTO_HITAGS
|
&& protocol != PROTO_HITAGS
|
||||||
|
&& protocol != PROTO_HITAGU
|
||||||
&& protocol != THINFILM
|
&& protocol != THINFILM
|
||||||
&& protocol != FELICA
|
&& protocol != FELICA
|
||||||
&& protocol != LTO
|
&& protocol != LTO
|
||||||
|
@ -646,7 +653,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
||||||
snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02X! ", frame[j]);
|
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) {
|
if (j == 0) {
|
||||||
|
|
||||||
|
@ -800,7 +807,10 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
||||||
annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse, mfDicKeys, mfDicKeysCount, false);
|
annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse, mfDicKeys, mfDicKeysCount, false);
|
||||||
break;
|
break;
|
||||||
case PROTO_HITAGS:
|
case PROTO_HITAGS:
|
||||||
annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
annotateHitagS(explanation, sizeof(explanation), frame, (data_len * 8) - ((8 - parityBytes[0]) % 8), hdr->isResponse);
|
||||||
|
break;
|
||||||
|
case PROTO_HITAGU:
|
||||||
|
annotateHitagU(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
||||||
break;
|
break;
|
||||||
case ICLASS:
|
case ICLASS:
|
||||||
annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
|
||||||
|
@ -1102,7 +1112,7 @@ static int download_trace(void) {
|
||||||
// Query for the size of the trace, downloading PM3_CMD_DATA_SIZE
|
// Query for the size of the trace, downloading PM3_CMD_DATA_SIZE
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 4000, true)) {
|
if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 4000, true)) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
free(gs_trace);
|
free(gs_trace);
|
||||||
gs_trace = NULL;
|
gs_trace = NULL;
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
|
@ -1310,9 +1320,10 @@ int CmdTraceList(const char *Cmd) {
|
||||||
"trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") "\n\n"
|
"trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") "\n\n"
|
||||||
"trace list -t des -> interpret as " _YELLOW_("MIFARE DESFire") "\n"
|
"trace list -t des -> interpret as " _YELLOW_("MIFARE DESFire") "\n"
|
||||||
"trace list -t felica -> interpret as " _YELLOW_("ISO18092 / FeliCa") "\n"
|
"trace list -t felica -> interpret as " _YELLOW_("ISO18092 / FeliCa") "\n"
|
||||||
"trace list -t hitag1 -> interpret as " _YELLOW_("Hitag 1") "\n"
|
"trace list -t ht1 -> interpret as " _YELLOW_("Hitag 1") "\n"
|
||||||
"trace list -t hitag2 -> interpret as " _YELLOW_("Hitag 2") "\n"
|
"trace list -t ht2 -> interpret as " _YELLOW_("Hitag 2") "\n"
|
||||||
"trace list -t hitags -> interpret as " _YELLOW_("Hitag S") "\n"
|
"trace list -t hts -> interpret as " _YELLOW_("Hitag S") "\n"
|
||||||
|
"trace list -t htu -> interpret as " _YELLOW_("Hitag µ") "\n"
|
||||||
"trace list -t iclass -> interpret as " _YELLOW_("iCLASS") "\n"
|
"trace list -t iclass -> interpret as " _YELLOW_("iCLASS") "\n"
|
||||||
"trace list -t legic -> interpret as " _YELLOW_("LEGIC") "\n"
|
"trace list -t legic -> interpret as " _YELLOW_("LEGIC") "\n"
|
||||||
"trace list -t lto -> interpret as " _YELLOW_("LTO-CM") "\n"
|
"trace list -t lto -> interpret as " _YELLOW_("LTO-CM") "\n"
|
||||||
|
@ -1377,9 +1388,10 @@ int CmdTraceList(const char *Cmd) {
|
||||||
else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF;
|
else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF;
|
||||||
else if (strcmp(type, "des") == 0) protocol = MFDES;
|
else if (strcmp(type, "des") == 0) protocol = MFDES;
|
||||||
else if (strcmp(type, "felica") == 0) protocol = FELICA;
|
else if (strcmp(type, "felica") == 0) protocol = FELICA;
|
||||||
else if (strcmp(type, "hitag1") == 0) protocol = PROTO_HITAG1;
|
else if (strcmp(type, "ht1") == 0) protocol = PROTO_HITAG1;
|
||||||
else if (strcmp(type, "hitag2") == 0) protocol = PROTO_HITAG2;
|
else if (strcmp(type, "ht2") == 0) protocol = PROTO_HITAG2;
|
||||||
else if (strcmp(type, "hitags") == 0) protocol = PROTO_HITAGS;
|
else if (strcmp(type, "hts") == 0) protocol = PROTO_HITAGS;
|
||||||
|
else if (strcmp(type, "htu") == 0) protocol = PROTO_HITAGU;
|
||||||
else if (strcmp(type, "iclass") == 0) protocol = ICLASS;
|
else if (strcmp(type, "iclass") == 0) protocol = ICLASS;
|
||||||
else if (strcmp(type, "legic") == 0) protocol = LEGIC;
|
else if (strcmp(type, "legic") == 0) protocol = LEGIC;
|
||||||
else if (strcmp(type, "lto") == 0) protocol = LTO;
|
else if (strcmp(type, "lto") == 0) protocol = LTO;
|
||||||
|
@ -1469,8 +1481,8 @@ int CmdTraceList(const char *Cmd) {
|
||||||
if (protocol == ISO_7816_4)
|
if (protocol == ISO_7816_4)
|
||||||
PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings n/a");
|
PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings n/a");
|
||||||
|
|
||||||
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) {
|
if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS || protocol == PROTO_HITAGU) {
|
||||||
PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S") " - Timings in ETU (8us)");
|
PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S / Hitag µ") " - Timings in ETU (8us)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (protocol == PROTO_FMCOS20) {
|
if (protocol == PROTO_FMCOS20) {
|
||||||
|
@ -1551,7 +1563,7 @@ int CmdTraceList(const char *Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset hitag state machine
|
// 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();
|
annotateHitag2_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ typedef enum {
|
||||||
const char *nxp_cluster_to_text(uint8_t cluster) {
|
const char *nxp_cluster_to_text(uint8_t cluster) {
|
||||||
switch (cluster) {
|
switch (cluster) {
|
||||||
case CL_ADMIN:
|
case CL_ADMIN:
|
||||||
return "card administration";
|
return "Card administration";
|
||||||
case CL_MISC1:
|
case CL_MISC1:
|
||||||
case CL_MISC2:
|
case CL_MISC2:
|
||||||
case CL_MISC3:
|
case CL_MISC3:
|
||||||
|
@ -102,40 +102,40 @@ const char *nxp_cluster_to_text(uint8_t cluster) {
|
||||||
case CL_MISC5:
|
case CL_MISC5:
|
||||||
case CL_MISC6:
|
case CL_MISC6:
|
||||||
case CL_MISC7:
|
case CL_MISC7:
|
||||||
return "miscellaneous applications";
|
return "Miscellaneous applications";
|
||||||
case CL_AIRLINES:
|
case CL_AIRLINES:
|
||||||
return "airlines";
|
return "Airlines";
|
||||||
case CL_FERRY:
|
case CL_FERRY:
|
||||||
return "ferry traffic";
|
return "Ferry traffic";
|
||||||
case CL_RAIL:
|
case CL_RAIL:
|
||||||
return "railway services";
|
return "Railway services";
|
||||||
case CL_MISC:
|
case CL_MISC:
|
||||||
return "miscellaneous applications";
|
return "Miscellaneous applications";
|
||||||
case CL_TRANSPORT:
|
case CL_TRANSPORT:
|
||||||
return "transport";
|
return "Transport";
|
||||||
case CL_SECURITY:
|
case CL_SECURITY:
|
||||||
return "security solutions";
|
return "Security solutions";
|
||||||
case CL_CITYTRAFFIC:
|
case CL_CITYTRAFFIC:
|
||||||
return "city traffic";
|
return "City traffic";
|
||||||
case CL_CZECH_RAIL:
|
case CL_CZECH_RAIL:
|
||||||
return "Czech Railways";
|
return "Czech Railways";
|
||||||
case CL_BUS:
|
case CL_BUS:
|
||||||
return "bus services";
|
return "Bus services";
|
||||||
case CL_MMT:
|
case CL_MMT:
|
||||||
return "multi modal transit";
|
return "Multi modal transit";
|
||||||
case CL_TAXI:
|
case CL_TAXI:
|
||||||
return "taxi";
|
return "Taxi";
|
||||||
case CL_TOLL:
|
case CL_TOLL:
|
||||||
return "road toll";
|
return "Road toll";
|
||||||
case CL_GENERIC_TRANS:
|
case CL_GENERIC_TRANS:
|
||||||
return "generic transport";
|
return "Generic transport";
|
||||||
case CL_COMPANY_SERVICES:
|
case CL_COMPANY_SERVICES:
|
||||||
return "company services";
|
return "Company services";
|
||||||
case CL_CITYCARD:
|
case CL_CITYCARD:
|
||||||
return "city card services";
|
return "City card services";
|
||||||
case CL_ACCESS_CONTROL_1:
|
case CL_ACCESS_CONTROL_1:
|
||||||
case CL_ACCESS_CONTROL_2:
|
case CL_ACCESS_CONTROL_2:
|
||||||
return "access control & security";
|
return "Access control & security";
|
||||||
case CL_VIGIK:
|
case CL_VIGIK:
|
||||||
return "VIGIK";
|
return "VIGIK";
|
||||||
case CL_NED_DEFENCE:
|
case CL_NED_DEFENCE:
|
||||||
|
@ -145,63 +145,63 @@ const char *nxp_cluster_to_text(uint8_t cluster) {
|
||||||
case CL_EU:
|
case CL_EU:
|
||||||
return "European Union Institutions";
|
return "European Union Institutions";
|
||||||
case CL_SKI_TICKET:
|
case CL_SKI_TICKET:
|
||||||
return "ski ticketing";
|
return "Ski ticketing";
|
||||||
case CL_SOAA:
|
case CL_SOAA:
|
||||||
return "SOAA standard for offline access standard";
|
return "SOAA standard for offline access standard";
|
||||||
case CL_ACCESS2:
|
case CL_ACCESS2:
|
||||||
return "access control & security";
|
return "Access control & security";
|
||||||
case CL_FOOD:
|
case CL_FOOD:
|
||||||
return "food";
|
return "Food";
|
||||||
case CL_NONFOOD:
|
case CL_NONFOOD:
|
||||||
return "non-food trade";
|
return "Non-food trade";
|
||||||
case CL_HOTEL:
|
case CL_HOTEL:
|
||||||
return "hotel";
|
return "Hotel";
|
||||||
case CL_LOYALTY:
|
case CL_LOYALTY:
|
||||||
return "loyalty";
|
return "Loyalty";
|
||||||
case CL_AIRPORT:
|
case CL_AIRPORT:
|
||||||
return "airport services";
|
return "Airport services";
|
||||||
case CL_CAR_RENTAL:
|
case CL_CAR_RENTAL:
|
||||||
return "car rental";
|
return "Car rental";
|
||||||
case CL_NED_GOV:
|
case CL_NED_GOV:
|
||||||
return "Dutch government";
|
return "Dutch government";
|
||||||
case CL_ADMIN2:
|
case CL_ADMIN2:
|
||||||
return "administration services";
|
return "Administration services";
|
||||||
case CL_PURSE:
|
case CL_PURSE:
|
||||||
return "electronic purse";
|
return "Electronic purse";
|
||||||
case CL_TV:
|
case CL_TV:
|
||||||
return "television";
|
return "Television";
|
||||||
case CL_CRUISESHIP:
|
case CL_CRUISESHIP:
|
||||||
return "cruise ship";
|
return "Cruise ship";
|
||||||
case CL_IOPTA:
|
case CL_IOPTA:
|
||||||
return "IOPTA";
|
return "IOPTA";
|
||||||
case CL_METERING:
|
case CL_METERING:
|
||||||
return "metering";
|
return "Metering";
|
||||||
case CL_TELEPHONE:
|
case CL_TELEPHONE:
|
||||||
return "telephone";
|
return "Telephone";
|
||||||
case CL_HEALTH:
|
case CL_HEALTH:
|
||||||
return "health services";
|
return "Health services";
|
||||||
case CL_WAREHOUSE:
|
case CL_WAREHOUSE:
|
||||||
return "warehouse";
|
return "Warehouse";
|
||||||
case CL_BANKING:
|
case CL_BANKING:
|
||||||
return "banking";
|
return "Banking";
|
||||||
case CL_ENTERTAIN:
|
case CL_ENTERTAIN:
|
||||||
return "entertainment & sports";
|
return "Entertainment & sports";
|
||||||
case CL_PARKING:
|
case CL_PARKING:
|
||||||
return "car parking";
|
return "Car parking";
|
||||||
case CL_FLEET:
|
case CL_FLEET:
|
||||||
return "fleet management";
|
return "Fleet management";
|
||||||
case CL_FUEL:
|
case CL_FUEL:
|
||||||
return "fuel, gasoline";
|
return "Fuel, gasoline";
|
||||||
case CL_INFO:
|
case CL_INFO:
|
||||||
return "info services";
|
return "Info services";
|
||||||
case CL_PRESS:
|
case CL_PRESS:
|
||||||
return "press";
|
return "Press";
|
||||||
case CL_NFC:
|
case CL_NFC:
|
||||||
return "NFC Forum";
|
return "NFC Forum";
|
||||||
case CL_COMPUTER:
|
case CL_COMPUTER:
|
||||||
return "computer";
|
return "Computer";
|
||||||
case CL_MAIL:
|
case CL_MAIL:
|
||||||
return "mail";
|
return "Mail";
|
||||||
case CL_AMISC:
|
case CL_AMISC:
|
||||||
case CL_AMISC1:
|
case CL_AMISC1:
|
||||||
case CL_AMISC2:
|
case CL_AMISC2:
|
||||||
|
@ -210,11 +210,11 @@ const char *nxp_cluster_to_text(uint8_t cluster) {
|
||||||
case CL_AMISC5:
|
case CL_AMISC5:
|
||||||
case CL_AMISC6:
|
case CL_AMISC6:
|
||||||
case CL_AMISC7:
|
case CL_AMISC7:
|
||||||
return "miscellaneous applications";
|
return "Miscellaneous applications";
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return "reserved";
|
return "Reserved";
|
||||||
}
|
}
|
||||||
|
|
||||||
static json_t *df_known_aids = NULL;
|
static json_t *df_known_aids = NULL;
|
||||||
|
|
|
@ -3073,7 +3073,7 @@ int DesfireGetCardUID(DesfireContext_t *ctx) {
|
||||||
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0);
|
||||||
PacketResponseNG resp;
|
PacketResponseNG resp;
|
||||||
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
|
||||||
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
|
PrintAndLogEx(WARNING, "timeout while waiting for reply");
|
||||||
return PM3_ETIMEOUT;
|
return PM3_ETIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -430,6 +430,7 @@ const static vocabulary_t vocabulary[] = {
|
||||||
{ 0, "hf mfu otptear" },
|
{ 0, "hf mfu otptear" },
|
||||||
{ 0, "hf mfu cauth" },
|
{ 0, "hf mfu cauth" },
|
||||||
{ 0, "hf mfu setpwd" },
|
{ 0, "hf mfu setpwd" },
|
||||||
|
{ 0, "hf mfu aesauth" },
|
||||||
{ 0, "hf mfu dump" },
|
{ 0, "hf mfu dump" },
|
||||||
{ 0, "hf mfu incr" },
|
{ 0, "hf mfu incr" },
|
||||||
{ 0, "hf mfu info" },
|
{ 0, "hf mfu info" },
|
||||||
|
@ -690,6 +691,13 @@ const static vocabulary_t vocabulary[] = {
|
||||||
{ 0, "lf hitag hts restore" },
|
{ 0, "lf hitag hts restore" },
|
||||||
{ 0, "lf hitag hts wrbl" },
|
{ 0, "lf hitag hts wrbl" },
|
||||||
{ 0, "lf hitag hts sim" },
|
{ 0, "lf hitag hts sim" },
|
||||||
|
{ 1, "lf hitag htu help" },
|
||||||
|
{ 1, "lf hitag htu list" },
|
||||||
|
{ 0, "lf hitag htu reader" },
|
||||||
|
{ 0, "lf hitag htu rdbl" },
|
||||||
|
{ 0, "lf hitag htu dump" },
|
||||||
|
{ 0, "lf hitag htu wrbl" },
|
||||||
|
{ 0, "lf hitag htu sim" },
|
||||||
{ 1, "lf idteck help" },
|
{ 1, "lf idteck help" },
|
||||||
{ 1, "lf idteck demod" },
|
{ 1, "lf idteck demod" },
|
||||||
{ 0, "lf idteck reader" },
|
{ 0, "lf idteck reader" },
|
||||||
|
|
|
@ -186,6 +186,7 @@ static void fill_grabber(const char *string) {
|
||||||
g_grabbed_output.ptr = tmp;
|
g_grabbed_output.ptr = tmp;
|
||||||
g_grabbed_output.size += MAX_PRINT_BUFFER;
|
g_grabbed_output.size += MAX_PRINT_BUFFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
|
int len = snprintf(g_grabbed_output.ptr + g_grabbed_output.idx, MAX_PRINT_BUFFER, "%s", string);
|
||||||
if (len < 0 || len > MAX_PRINT_BUFFER) {
|
if (len < 0 || len > MAX_PRINT_BUFFER) {
|
||||||
// We leave current g_grabbed_output_len untouched
|
// We leave current g_grabbed_output_len untouched
|
||||||
|
@ -196,25 +197,35 @@ static void fill_grabber(const char *string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) {
|
void PrintAndLogOptions(const char *str[][2], size_t size, size_t space) {
|
||||||
|
|
||||||
char buff[2000] = "Options:\n";
|
char buff[2000] = "Options:\n";
|
||||||
char format[2000] = "";
|
char format[2000] = "";
|
||||||
size_t counts[2] = {0, 0};
|
size_t counts[2] = {0, 0};
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
for (size_t j = 0 ; j < 2 ; j++)
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
for (size_t j = 0 ; j < 2 ; j++) {
|
||||||
if (counts[j] < strlen(str[i][j])) {
|
if (counts[j] < strlen(str[i][j])) {
|
||||||
counts[j] = strlen(str[i][j]);
|
counts[j] = strlen(str[i][j]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
|
||||||
for (size_t j = 0; j < 2; j++) {
|
for (size_t j = 0; j < 2; j++) {
|
||||||
if (j == 0)
|
if (j == 0) {
|
||||||
snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
|
snprintf(format, sizeof(format), "%%%zus%%%zus", space, counts[j]);
|
||||||
else
|
} else {
|
||||||
snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]);
|
snprintf(format, sizeof(format), "%%%zus%%-%zus", space, counts[j]);
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]);
|
snprintf(buff + strlen(buff), sizeof(buff) - strlen(buff), format, " ", str[i][j]);
|
||||||
}
|
}
|
||||||
if (i < size - 1)
|
|
||||||
|
if (i < size - 1) {
|
||||||
strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1);
|
strncat(buff, "\n", sizeof(buff) - strlen(buff) - 1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
PrintAndLogEx(NORMAL, "%s", buff);
|
PrintAndLogEx(NORMAL, "%s", buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,12 +234,14 @@ static uint8_t PrintAndLogEx_spinidx = 0;
|
||||||
void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
|
|
||||||
// skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
|
// skip debug messages if client debugging is turned off i.e. 'DATA SETDEBUG -0'
|
||||||
if (g_debugMode == 0 && level == DEBUG)
|
if (g_debugMode == 0 && level == DEBUG) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// skip HINT messages if client has hints turned off i.e. 'HINT 0'
|
// skip HINT messages if client has hints turned off i.e. 'HINT 0'
|
||||||
if (g_session.show_hints == false && level == HINT)
|
if (g_session.show_hints == false && level == HINT) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char prefix[40] = {0};
|
char prefix[40] = {0};
|
||||||
char buffer[MAX_PRINT_BUFFER] = {0};
|
char buffer[MAX_PRINT_BUFFER] = {0};
|
||||||
|
@ -242,17 +255,19 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
};
|
};
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case ERR:
|
case ERR:
|
||||||
if (g_session.emoji_mode == EMO_EMOJI)
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("!!") "] :rotating_light: ", sizeof(prefix) - 1);
|
||||||
else
|
} else {
|
||||||
strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("!!") "] ", sizeof(prefix) - 1);
|
||||||
|
}
|
||||||
stream = stderr;
|
stream = stderr;
|
||||||
break;
|
break;
|
||||||
case FAILED:
|
case FAILED:
|
||||||
if (g_session.emoji_mode == EMO_EMOJI)
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("-") "] :no_entry: ", sizeof(prefix) - 1);
|
||||||
else
|
} else {
|
||||||
strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _RED_("-") "] ", sizeof(prefix) - 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DEBUG:
|
case DEBUG:
|
||||||
strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _BLUE_("#") "] ", sizeof(prefix) - 1);
|
||||||
|
@ -264,10 +279,11 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _GREEN_("+") "] ", sizeof(prefix) - 1);
|
||||||
break;
|
break;
|
||||||
case WARNING:
|
case WARNING:
|
||||||
if (g_session.emoji_mode == EMO_EMOJI)
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _CYAN_("!") "] :warning: ", sizeof(prefix) - 1);
|
||||||
else
|
} else {
|
||||||
strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _CYAN_("!") "] ", sizeof(prefix) - 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case INFO:
|
case INFO:
|
||||||
strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1);
|
strncpy(prefix, "[" _YELLOW_("=") "] ", sizeof(prefix) - 1);
|
||||||
|
@ -276,14 +292,16 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
if (g_session.emoji_mode == EMO_EMOJI) {
|
if (g_session.emoji_mode == EMO_EMOJI) {
|
||||||
strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
strncpy(prefix, spinner_emoji[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
||||||
PrintAndLogEx_spinidx++;
|
PrintAndLogEx_spinidx++;
|
||||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji))
|
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner_emoji)) {
|
||||||
PrintAndLogEx_spinidx = 0;
|
PrintAndLogEx_spinidx = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
strncpy(prefix, spinner[PrintAndLogEx_spinidx], sizeof(prefix) - 1);
|
||||||
PrintAndLogEx_spinidx++;
|
PrintAndLogEx_spinidx++;
|
||||||
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner))
|
if (PrintAndLogEx_spinidx >= ARRAYLEN(spinner)) {
|
||||||
PrintAndLogEx_spinidx = 0;
|
PrintAndLogEx_spinidx = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case NORMAL:
|
case NORMAL:
|
||||||
// no prefixes for normal
|
// no prefixes for normal
|
||||||
|
@ -306,8 +324,9 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
const char delim[2] = "\n";
|
const char delim[2] = "\n";
|
||||||
|
|
||||||
// line starts with newline
|
// line starts with newline
|
||||||
if (buffer[0] == '\n')
|
if (buffer[0] == '\n') {
|
||||||
fPrintAndLog(stream, "");
|
fPrintAndLog(stream, "");
|
||||||
|
}
|
||||||
|
|
||||||
token = strtok_r(buffer, delim, &tmp_ptr);
|
token = strtok_r(buffer, delim, &tmp_ptr);
|
||||||
|
|
||||||
|
@ -315,16 +334,21 @@ void PrintAndLogEx(logLevel_t level, const char *fmt, ...) {
|
||||||
|
|
||||||
size_t size = strlen(buffer2);
|
size_t size = strlen(buffer2);
|
||||||
|
|
||||||
if (strlen(token))
|
if (strlen(token)) {
|
||||||
snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
|
snprintf(buffer2 + size, sizeof(buffer2) - size, "%s%s\n", prefix, token);
|
||||||
else
|
} else {
|
||||||
snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
|
snprintf(buffer2 + size, sizeof(buffer2) - size, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
token = strtok_r(NULL, delim, &tmp_ptr);
|
token = strtok_r(NULL, delim, &tmp_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fPrintAndLog(stream, "%s", buffer2);
|
fPrintAndLog(stream, "%s", buffer2);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
|
snprintf(buffer2, sizeof(buffer2), "%s%s", prefix, buffer);
|
||||||
|
|
||||||
if (level == INPLACE) {
|
if (level == INPLACE) {
|
||||||
// ignore INPLACE if rest of output is grabbed
|
// ignore INPLACE if rest of output is grabbed
|
||||||
if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
|
if (!(g_printAndLog & PRINTANDLOG_GRAB)) {
|
||||||
|
@ -354,6 +378,7 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
if (logging && g_session.incognito) {
|
if (logging && g_session.incognito) {
|
||||||
logging = 0;
|
logging = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
|
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && !logfile) {
|
||||||
char *my_logfile_path = NULL;
|
char *my_logfile_path = NULL;
|
||||||
char filename[40];
|
char filename[40];
|
||||||
|
@ -361,11 +386,15 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
timenow = gmtime(&now);
|
timenow = gmtime(&now);
|
||||||
strftime(filename, sizeof(filename), PROXLOG, timenow);
|
strftime(filename, sizeof(filename), PROXLOG, timenow);
|
||||||
|
|
||||||
if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
|
if (searchHomeFilePath(&my_logfile_path, LOGS_SUBDIR, filename, true) != PM3_SUCCESS) {
|
||||||
|
|
||||||
printf(_YELLOW_("[-]") " Logging disabled!\n");
|
printf(_YELLOW_("[-]") " Logging disabled!\n");
|
||||||
my_logfile_path = NULL;
|
my_logfile_path = NULL;
|
||||||
logging = 0;
|
logging = 0;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
logfile = fopen(my_logfile_path, "a");
|
logfile = fopen(my_logfile_path, "a");
|
||||||
if (logfile == NULL) {
|
if (logfile == NULL) {
|
||||||
printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path);
|
printf(_YELLOW_("[-]") " Can't open logfile %s, logging disabled!\n", my_logfile_path);
|
||||||
|
@ -411,14 +440,17 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
linefeed = false;
|
linefeed = false;
|
||||||
buffer[strlen(buffer) - 1] = 0;
|
buffer[strlen(buffer) - 1] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool filter_ansi = !g_session.supports_colors;
|
bool filter_ansi = !g_session.supports_colors;
|
||||||
memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi);
|
memcpy_filter_ansi(buffer2, buffer, sizeof(buffer), filter_ansi);
|
||||||
if (g_printAndLog & PRINTANDLOG_PRINT) {
|
|
||||||
|
if ((g_printAndLog & PRINTANDLOG_PRINT) == PRINTANDLOG_PRINT) {
|
||||||
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode);
|
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), g_session.emoji_mode);
|
||||||
fprintf(stream, "%s", buffer3);
|
fprintf(stream, "%s", buffer3);
|
||||||
if (linefeed)
|
if (linefeed) {
|
||||||
fprintf(stream, "\n");
|
fprintf(stream, "\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef RL_STATE_READCMD
|
#ifdef RL_STATE_READCMD
|
||||||
// We are using GNU readline. libedit (OSX) doesn't support this flag.
|
// We are using GNU readline. libedit (OSX) doesn't support this flag.
|
||||||
|
@ -433,33 +465,44 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) {
|
||||||
|
|
||||||
if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
|
if (((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) ||
|
||||||
(g_printAndLog & PRINTANDLOG_GRAB)) {
|
(g_printAndLog & PRINTANDLOG_GRAB)) {
|
||||||
|
|
||||||
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
|
memcpy_filter_emoji(buffer3, buffer2, sizeof(buffer2), EMO_ALTTEXT);
|
||||||
|
|
||||||
if (filter_ansi == false) {
|
if (filter_ansi == false) {
|
||||||
memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
|
memcpy_filter_ansi(buffer, buffer3, sizeof(buffer3), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
|
if ((g_printAndLog & PRINTANDLOG_LOG) && logging && logfile) {
|
||||||
|
|
||||||
if (filter_ansi) {
|
if (filter_ansi) {
|
||||||
fprintf(logfile, "%s", buffer3);
|
fprintf(logfile, "%s", buffer3);
|
||||||
} else {
|
} else {
|
||||||
fprintf(logfile, "%s", buffer);
|
fprintf(logfile, "%s", buffer);
|
||||||
}
|
}
|
||||||
if (linefeed)
|
|
||||||
|
if (linefeed) {
|
||||||
fprintf(logfile, "\n");
|
fprintf(logfile, "\n");
|
||||||
|
}
|
||||||
fflush(logfile);
|
fflush(logfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_printAndLog & PRINTANDLOG_GRAB) {
|
if (g_printAndLog & PRINTANDLOG_GRAB) {
|
||||||
|
|
||||||
if (filter_ansi) {
|
if (filter_ansi) {
|
||||||
fill_grabber(buffer3);
|
fill_grabber(buffer3);
|
||||||
} else {
|
} else {
|
||||||
fill_grabber(buffer);
|
fill_grabber(buffer);
|
||||||
}
|
}
|
||||||
if (linefeed)
|
|
||||||
|
if (linefeed) {
|
||||||
fill_grabber("\n");
|
fill_grabber("\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (flushAfterWrite)
|
if (flushAfterWrite) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
//release lock
|
//release lock
|
||||||
pthread_mutex_unlock(&g_print_lock);
|
pthread_mutex_unlock(&g_print_lock);
|
||||||
|
@ -478,9 +521,10 @@ void memcpy_filter_rlmarkers(void *dest, const void *src, size_t n) {
|
||||||
uint8_t *rsrc = (uint8_t *)src;
|
uint8_t *rsrc = (uint8_t *)src;
|
||||||
uint16_t si = 0;
|
uint16_t si = 0;
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
if ((rsrc[i] == '\001') || (rsrc[i] == '\002'))
|
if ((rsrc[i] == '\001') || (rsrc[i] == '\002')) {
|
||||||
// skip readline special markers
|
// skip readline special markers
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
rdest[si++] = rsrc[i];
|
rdest[si++] = rsrc[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,11 +433,21 @@ void xor(uint8_t *dest, const uint8_t *src, size_t n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void lsl(uint8_t *data, size_t len) {
|
// left shift an array of length one bit
|
||||||
for (size_t n = 0; n < len - 1; n++) {
|
void lsl(uint8_t *d, size_t n) {
|
||||||
data[n] = (data[n] << 1) | (data[n + 1] >> 7);
|
for (size_t i = 0; i < n - 1; i++) {
|
||||||
|
d[i] = (d[i] << 1) | (d[i + 1] >> 7);
|
||||||
|
}
|
||||||
|
d[n - 1] <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lslx(uint8_t *d, size_t n, uint8_t shifts) {
|
||||||
|
for (uint8_t i = 0; i < shifts; i++) {
|
||||||
|
for (size_t j = 0; j < n - 1; j++) {
|
||||||
|
d[j] = (d[j] << 1) | (d[j + 1] >> 7);
|
||||||
|
}
|
||||||
|
d[n - 1] <<= 1;
|
||||||
}
|
}
|
||||||
data[len - 1] <<= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -555,7 +565,12 @@ void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible.
|
// TODO: Boost performance by copying in chunks of 1, 2, or 4 bytes when feasible.
|
||||||
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits) {
|
/**
|
||||||
|
* @brief Concatenate bits from src to dest, bitstream is stored MSB first
|
||||||
|
* which means that the dest_offset=0 is the MSB of the dest[0]
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb) {
|
||||||
int i, end, step;
|
int i, end, step;
|
||||||
|
|
||||||
// overlap
|
// overlap
|
||||||
|
@ -571,8 +586,8 @@ size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_of
|
||||||
|
|
||||||
for (; i != end; i += step) {
|
for (; i != end; i += step) {
|
||||||
// equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i]
|
// equiv of dest_bits[dest_offset + i] = src_bits[src_offset + i]
|
||||||
CLEAR_BIT(dest, dest_offset + i);
|
CLEAR_BIT_MSB(dest, dest_offset + i);
|
||||||
if (TEST_BIT(src, src_offset + i)) SET_BIT(dest, dest_offset + i);
|
if (src_lsb ? TEST_BIT_LSB(src, src_offset + i) : TEST_BIT_MSB(src, src_offset + i)) SET_BIT_MSB(dest, dest_offset + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dest_offset + nbits;
|
return dest_offset + nbits;
|
||||||
|
|
|
@ -132,7 +132,9 @@ void rol(uint8_t *data, const size_t n);
|
||||||
void ror(uint8_t *data, const size_t n);
|
void ror(uint8_t *data, const size_t n);
|
||||||
void xor(uint8_t *dest, const uint8_t *src, size_t n);
|
void xor(uint8_t *dest, const uint8_t *src, size_t n);
|
||||||
|
|
||||||
void lsl(uint8_t *data, size_t len);
|
void lsl(uint8_t *d, size_t n);
|
||||||
|
void lslx(uint8_t *d, size_t n, uint8_t shifts);
|
||||||
|
|
||||||
uint32_t le24toh(const uint8_t data[3]);
|
uint32_t le24toh(const uint8_t data[3]);
|
||||||
void htole24(uint32_t val, uint8_t data[3]);
|
void htole24(uint32_t val, uint8_t data[3]);
|
||||||
|
|
||||||
|
@ -150,7 +152,7 @@ bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n);
|
||||||
void reverse_arraybytes(uint8_t *arr, size_t len);
|
void reverse_arraybytes(uint8_t *arr, size_t len);
|
||||||
void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len);
|
void reverse_arraybytes_copy(uint8_t *arr, uint8_t *dest, size_t len);
|
||||||
|
|
||||||
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits);
|
size_t concatbits(uint8_t *dest, int dest_offset, const uint8_t *src, int src_offset, size_t nbits, bool src_lsb);
|
||||||
int char2int(char c);
|
int char2int(char c);
|
||||||
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
|
int hexstr2ByteArr(const char *hexstr, unsigned char *array, size_t asize);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -142,12 +142,17 @@ uint16_t update_crc16(uint16_t crc, uint8_t c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// two ways. msb or lsb loop.
|
// 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) {
|
uint16_t Crc16(uint8_t const *d, size_t bitlength, uint16_t remainder, uint16_t polynomial, bool refin, bool refout) {
|
||||||
if (length == 0)
|
if (bitlength == 0)
|
||||||
return (~remainder);
|
return (~remainder);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < length; ++i) {
|
uint8_t offset = 8 - (bitlength % 8);
|
||||||
uint8_t c = d[i];
|
// 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);
|
if (refin) c = reflect8(c);
|
||||||
|
|
||||||
// xor in at msb
|
// xor in at msb
|
||||||
|
|
|
@ -47,7 +47,7 @@ typedef enum {
|
||||||
|
|
||||||
uint16_t update_crc16_ex(uint16_t crc, uint8_t c, uint16_t polynomial);
|
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 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);
|
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);
|
void compute_crc(CrcType_t ct, const uint8_t *d, size_t n, uint8_t *first, uint8_t *second);
|
||||||
|
|
|
@ -5047,7 +5047,7 @@
|
||||||
"-v, --verbose verbose output",
|
"-v, --verbose verbose output",
|
||||||
"-f, --file <fn> Specify a filename for dump file",
|
"-f, --file <fn> Specify a filename for dump file",
|
||||||
"--emu from emulator memory",
|
"--emu from emulator memory",
|
||||||
"--start <dec> index of block to start writing (default 0)",
|
"--start <dec> index of block to start writing (def 0)",
|
||||||
"--end <dec> index of block to end writing (default last block)"
|
"--end <dec> index of block to end writing (default last block)"
|
||||||
],
|
],
|
||||||
"usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p <hex>] [-f <fn>] [--emu] [--start <dec>] [--end <dec>]"
|
"usage": "hf mf gload [-hv] [--mini] [--1k] [--1k+] [--2k] [--4k] [-p <hex>] [-f <fn>] [--emu] [--start <dec>] [--end <dec>]"
|
||||||
|
@ -7062,6 +7062,22 @@
|
||||||
],
|
],
|
||||||
"usage": "hf mfp wrp [-hv] -a <hex> [-d <hex>]"
|
"usage": "hf mfp wrp [-hv] -a <hex> [-d <hex>]"
|
||||||
},
|
},
|
||||||
|
"hf mfu aesauth": {
|
||||||
|
"command": "hf mfu aesauth",
|
||||||
|
"description": "Tests AES key on Mifare Ultralight AES tags. If no key is specified, null key will be tried. Key index 0: DataProtKey (default) Key index 1: UIDRetrKey Key index 2: OriginalityKey",
|
||||||
|
"notes": [
|
||||||
|
"hf mfu aesauth",
|
||||||
|
"hf mfu aesauth --key <32 bytes> --index <0..2>"
|
||||||
|
],
|
||||||
|
"offline": false,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"--key <hex> AES key (32 hex bytes)",
|
||||||
|
"-i, --index <0..2> Key index, default: 0",
|
||||||
|
"-k Keep field on (only if a key is provided)"
|
||||||
|
],
|
||||||
|
"usage": "hf mfu aesauth [-hk] [--key <hex>] [-i <0..2>]"
|
||||||
|
},
|
||||||
"hf mfu amiibo": {
|
"hf mfu amiibo": {
|
||||||
"command": "hf mfu amiibo",
|
"command": "hf mfu amiibo",
|
||||||
"description": "Tries to read all memory from amiibo tag and decrypt it",
|
"description": "Tries to read all memory from amiibo tag and decrypt it",
|
||||||
|
@ -8825,12 +8841,13 @@
|
||||||
},
|
},
|
||||||
"lf em 410x clone": {
|
"lf em 410x clone": {
|
||||||
"command": "lf em 410x clone",
|
"command": "lf em 410x clone",
|
||||||
"description": "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469 or Hitag S/8211/8268/8310 tag.",
|
"description": "clone a EM410x ID to a T55x7, Q5/T5555, EM4305/4469, Hitag S/8211/8268/8310 or Hitag \u00b5/8265 tag.",
|
||||||
"notes": [
|
"notes": [
|
||||||
"lf em 410x clone --id 0F0368568B -> encode for T55x7 tag",
|
"lf em 410x clone --id 0F0368568B -> encode for T55x7 tag",
|
||||||
"lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag",
|
"lf em 410x clone --id 0F0368568B --q5 -> encode for Q5/T5555 tag",
|
||||||
"lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469",
|
"lf em 410x clone --id 0F0368568B --em -> encode for EM4305/4469",
|
||||||
"lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310"
|
"lf em 410x clone --id 0F0368568B --hts -> encode for Hitag S/8211/8268/8310",
|
||||||
|
"lf em 410x clone --id 0F0368568B --htu -> encode for Hitag \u00b5/8265 tag"
|
||||||
],
|
],
|
||||||
"offline": false,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
|
@ -8840,9 +8857,10 @@
|
||||||
"--q5 optional - specify writing to Q5/T5555 tag",
|
"--q5 optional - specify writing to Q5/T5555 tag",
|
||||||
"--em optional - specify writing to EM4305/4469 tag",
|
"--em optional - specify writing to EM4305/4469 tag",
|
||||||
"--hts optional - specify writing to Hitag S/8211/8268/8310 tag",
|
"--hts optional - specify writing to Hitag S/8211/8268/8310 tag",
|
||||||
|
"--htu optional - specify writing to Hitag \u00b5/8265 tag",
|
||||||
"--electra optional - add Electra blocks to tag"
|
"--electra optional - add Electra blocks to tag"
|
||||||
],
|
],
|
||||||
"usage": "lf em 410x clone [-h] [--clk <dec>] --id <hex> [--q5] [--em] [--hts] [--electra]"
|
"usage": "lf em 410x clone [-h] [--clk <dec>] --id <hex> [--q5] [--em] [--hts] [--htu] [--electra]"
|
||||||
},
|
},
|
||||||
"lf em 410x reader": {
|
"lf em 410x reader": {
|
||||||
"command": "lf em 410x reader",
|
"command": "lf em 410x reader",
|
||||||
|
@ -9959,7 +9977,7 @@
|
||||||
},
|
},
|
||||||
"lf hitag help": {
|
"lf hitag help": {
|
||||||
"command": "lf hitag help",
|
"command": "lf hitag help",
|
||||||
"description": "help This help list List Hitag trace history hts { Hitag S/8211 operations } test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t hitag2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
|
"description": "help This help list List Hitag trace history hts { Hitag S/8211 operations } htu { Hitag \u00b5/8265 operations } test Perform self tests view Display content from tag dump file lookup Uses authentication trace to check for key in dictionary file --------------------------------------------------------------------------------------- lf hitag list available offline: yes Alias of `trace list -t ht2` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
|
||||||
"notes": [
|
"notes": [
|
||||||
"lf hitag list --frame -> show frame delay times",
|
"lf hitag list --frame -> show frame delay times",
|
||||||
"lf hitag list -1 -> use trace buffer"
|
"lf hitag list -1 -> use trace buffer"
|
||||||
|
@ -10003,7 +10021,7 @@
|
||||||
},
|
},
|
||||||
"lf hitag hts help": {
|
"lf hitag hts help": {
|
||||||
"command": "lf hitag hts help",
|
"command": "lf hitag hts help",
|
||||||
"description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hitags` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
|
"description": "help This help list List Hitag S trace history --------------------------------------------------------------------------------------- lf hitag hts list available offline: yes Alias of `trace list -t hts` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
|
||||||
"notes": [
|
"notes": [
|
||||||
"lf hitag hts list --frame -> show frame delay times",
|
"lf hitag hts list --frame -> show frame delay times",
|
||||||
"lf hitag hts list -1 -> use trace buffer"
|
"lf hitag hts list -1 -> use trace buffer"
|
||||||
|
@ -10086,14 +10104,16 @@
|
||||||
"description": "Simulate Hitag S transponder You need to `lf hitag hts eload` first",
|
"description": "Simulate Hitag S transponder You need to `lf hitag hts eload` first",
|
||||||
"notes": [
|
"notes": [
|
||||||
"lf hitag hts sim",
|
"lf hitag hts sim",
|
||||||
"lf hitag hts sim --82xx"
|
"lf hitag hts sim --82xx",
|
||||||
|
"lf hitag hts sim -t 30 -> set threshold to 30"
|
||||||
],
|
],
|
||||||
"offline": false,
|
"offline": false,
|
||||||
"options": [
|
"options": [
|
||||||
"-h, --help This help",
|
"-h, --help This help",
|
||||||
"-8, --82xx simulate 8268/8310"
|
"-8, --82xx simulate 8268/8310",
|
||||||
|
"-t, --threshold <dec> set edge detect threshold (def: 127)"
|
||||||
],
|
],
|
||||||
"usage": "lf hitag hts sim [-h8]"
|
"usage": "lf hitag hts sim [-h8] [-t <dec>]"
|
||||||
},
|
},
|
||||||
"lf hitag hts wrbl": {
|
"lf hitag hts wrbl": {
|
||||||
"command": "lf hitag hts wrbl",
|
"command": "lf hitag hts wrbl",
|
||||||
|
@ -10119,6 +10139,110 @@
|
||||||
],
|
],
|
||||||
"usage": "lf hitag hts wrbl [-h8] [--nrar <hex>] [--crypto] [-k <hex>] [-m <dec>] -p <dec> -d <hex>"
|
"usage": "lf hitag hts wrbl [-h8] [--nrar <hex>] [--crypto] [-k <hex>] [-m <dec>] -p <dec> -d <hex>"
|
||||||
},
|
},
|
||||||
|
"lf hitag htu dump": {
|
||||||
|
"command": "lf hitag htu dump",
|
||||||
|
"description": "Read all Hitag \u00b5 memory and save to file 82xx password mode: - default password 00000000",
|
||||||
|
"notes": [
|
||||||
|
"lf hitag htu dump --82xx -> use def pwd",
|
||||||
|
"lf hitag htu dump --82xx -k 9AC4999C -> pwd mode"
|
||||||
|
],
|
||||||
|
"offline": false,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"-8, --82xx 82xx mode",
|
||||||
|
"-k, --key <hex> pwd, 4 hex bytes",
|
||||||
|
"-f, --file <fn> specify file name",
|
||||||
|
"--ns no save to file"
|
||||||
|
],
|
||||||
|
"usage": "lf hitag htu dump [-h8] [-k <hex>] [-f <fn>] [--ns]"
|
||||||
|
},
|
||||||
|
"lf hitag htu help": {
|
||||||
|
"command": "lf hitag htu help",
|
||||||
|
"description": "help This help list List Hitag \u00b5 trace history --------------------------------------------------------------------------------------- lf hitag htu list available offline: yes Alias of `trace list -t htu` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
|
||||||
|
"notes": [
|
||||||
|
"lf hitag htu list --frame -> show frame delay times",
|
||||||
|
"lf hitag htu list -1 -> use trace buffer"
|
||||||
|
],
|
||||||
|
"offline": true,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"-1, --buffer use data from trace buffer",
|
||||||
|
"--frame show frame delay times",
|
||||||
|
"-c mark CRC bytes",
|
||||||
|
"-r show relative times (gap and duration)",
|
||||||
|
"-u display times in microseconds instead of clock cycles",
|
||||||
|
"-x show hexdump to convert to pcap(ng)",
|
||||||
|
"or to import into Wireshark using encapsulation type \"ISO 14443\"",
|
||||||
|
"-f, --file <fn> filename of dictionary"
|
||||||
|
],
|
||||||
|
"usage": "lf hitag htu list [-h1crux] [--frame] [-f <fn>]"
|
||||||
|
},
|
||||||
|
"lf hitag htu rdbl": {
|
||||||
|
"command": "lf hitag htu rdbl",
|
||||||
|
"description": "Read Hitag \u00b5 memory. 82xx password mode: - default password 00000000",
|
||||||
|
"notes": [
|
||||||
|
"lf hitag htu rdbl -p 1 -> Hitag \u00b5, plain mode",
|
||||||
|
"lf hitag htu rdbl -p 1 --82xx -> 82xx, password mode, def pass",
|
||||||
|
"lf hitag htu rdbl -p 1 --82xx -k 9AC4999C -> 82xx, password mode"
|
||||||
|
],
|
||||||
|
"offline": false,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"-8, --82xx 82xx mode",
|
||||||
|
"-k, --key <hex> pwd, 4 hex bytes",
|
||||||
|
"-p, --page <dec> block address to read from (def: 0)",
|
||||||
|
"-c, --count <dec> how many blocks to read. '0' reads all blocks (def: 1)"
|
||||||
|
],
|
||||||
|
"usage": "lf hitag htu rdbl [-h8] [-k <hex>] [-p <dec>] [-c <dec>]"
|
||||||
|
},
|
||||||
|
"lf hitag htu reader": {
|
||||||
|
"command": "lf hitag htu reader",
|
||||||
|
"description": "Act as a Hitag \u00b5 reader. Look for Hitag \u00b5 tags until Enter or the pm3 button is pressed",
|
||||||
|
"notes": [
|
||||||
|
"lf hitag htu reader",
|
||||||
|
"lf hitag htu reader -@ -> Continuous mode"
|
||||||
|
],
|
||||||
|
"offline": false,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"-@ continuous reader mode"
|
||||||
|
],
|
||||||
|
"usage": "lf hitag htu reader [-h@]"
|
||||||
|
},
|
||||||
|
"lf hitag htu sim": {
|
||||||
|
"command": "lf hitag htu sim",
|
||||||
|
"description": "Simulate Hitag \u00b5 transponder You need to `lf hitag htu eload` first",
|
||||||
|
"notes": [
|
||||||
|
"lf hitag htu sim",
|
||||||
|
"lf hitag htu sim --82xx",
|
||||||
|
"lf hitag htu sim -t 30 -> set threshold to 30"
|
||||||
|
],
|
||||||
|
"offline": false,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"-8, --82xx simulate 82xx",
|
||||||
|
"-t, --threshold <dec> set edge detect threshold (def: 127)"
|
||||||
|
],
|
||||||
|
"usage": "lf hitag htu sim [-h8] [-t <dec>]"
|
||||||
|
},
|
||||||
|
"lf hitag htu wrbl": {
|
||||||
|
"command": "lf hitag htu wrbl",
|
||||||
|
"description": "Write a block in Hitag \u00b5 memory. 82xx password mode: - default password 00000000",
|
||||||
|
"notes": [
|
||||||
|
"lf hitag htu wrbl -p 6 -d 01020304 -> Hitag \u00b5, plain mode",
|
||||||
|
"lf hitag htu wrbl -p 6 -d 01020304 --82xx -> use def pwd",
|
||||||
|
"lf hitag htu wrbl -p 6 -d 01020304 --82xx -k 9AC4999C -> 82xx, password mode"
|
||||||
|
],
|
||||||
|
"offline": false,
|
||||||
|
"options": [
|
||||||
|
"-h, --help This help",
|
||||||
|
"-8, --82xx 82xx mode",
|
||||||
|
"-k, --key <hex> pwd, 4 hex bytes",
|
||||||
|
"-p, --page <dec> block address to write to",
|
||||||
|
"-d, --data <hex> data, 4 hex bytes"
|
||||||
|
],
|
||||||
|
"usage": "lf hitag htu wrbl [-h8] [-k <hex>] -p <dec> -d <hex>"
|
||||||
|
},
|
||||||
"lf hitag info": {
|
"lf hitag info": {
|
||||||
"command": "lf hitag info",
|
"command": "lf hitag info",
|
||||||
"description": "Hitag 2 tag information",
|
"description": "Hitag 2 tag information",
|
||||||
|
@ -10992,8 +11116,8 @@
|
||||||
"-r, --reset Reset configuration to default values",
|
"-r, --reset Reset configuration to default values",
|
||||||
"-p, --pwd <hex> Password, 7bytes, LSB-order",
|
"-p, --pwd <hex> Password, 7bytes, LSB-order",
|
||||||
"-d, --delay <dec> Tag initialization delay (in us)",
|
"-d, --delay <dec> Tag initialization delay (in us)",
|
||||||
"--lw <dec> offset, low pulses width (in us)",
|
"--lw <dec> offset, low pulses width (in us), optional!",
|
||||||
"--lp <dec> offset, low pulses position (in us)"
|
"--lp <dec> offset, low pulses position (in us), optional!"
|
||||||
],
|
],
|
||||||
"usage": "lf pcf7931 config [-hr] [-p <hex>] [-d <dec>] [--lw <dec>] [--lp <dec>]"
|
"usage": "lf pcf7931 config [-hr] [-p <hex>] [-d <dec>] [--lw <dec>] [--lp <dec>]"
|
||||||
},
|
},
|
||||||
|
@ -13012,9 +13136,10 @@
|
||||||
"",
|
"",
|
||||||
"trace list -t des -> interpret as MIFARE DESFire",
|
"trace list -t des -> interpret as MIFARE DESFire",
|
||||||
"trace list -t felica -> interpret as ISO18092 / FeliCa",
|
"trace list -t felica -> interpret as ISO18092 / FeliCa",
|
||||||
"trace list -t hitag1 -> interpret as Hitag 1",
|
"trace list -t ht1 -> interpret as Hitag 1",
|
||||||
"trace list -t hitag2 -> interpret as Hitag 2",
|
"trace list -t ht2 -> interpret as Hitag 2",
|
||||||
"trace list -t hitags -> interpret as Hitag S",
|
"trace list -t hts -> interpret as Hitag S",
|
||||||
|
"trace list -t htu -> interpret as Hitag \u00b5",
|
||||||
"trace list -t iclass -> interpret as iCLASS",
|
"trace list -t iclass -> interpret as iCLASS",
|
||||||
"trace list -t legic -> interpret as LEGIC",
|
"trace list -t legic -> interpret as LEGIC",
|
||||||
"trace list -t lto -> interpret as LTO-CM",
|
"trace list -t lto -> interpret as LTO-CM",
|
||||||
|
@ -13230,8 +13355,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"commands_extracted": 760,
|
"commands_extracted": 767,
|
||||||
"extracted_by": "PM3Help2JSON v1.00",
|
"extracted_by": "PM3Help2JSON v1.00",
|
||||||
"extracted_on": "2025-03-12T15:46:33"
|
"extracted_on": "2025-03-19T20:08:46"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -617,6 +617,7 @@ Check column "offline" for their availability.
|
||||||
|`hf mfu otptear `|N |`Tear-off test on OTP bits`
|
|`hf mfu otptear `|N |`Tear-off test on OTP bits`
|
||||||
|`hf mfu cauth `|N |`Ultralight-C - Authentication`
|
|`hf mfu cauth `|N |`Ultralight-C - Authentication`
|
||||||
|`hf mfu setpwd `|N |`Ultralight-C - Set 3DES key`
|
|`hf mfu setpwd `|N |`Ultralight-C - Set 3DES key`
|
||||||
|
|`hf mfu aesauth `|N |`Ultralight-AES - Authentication`
|
||||||
|`hf mfu dump `|N |`Dump MIFARE Ultralight family tag to binary file`
|
|`hf mfu dump `|N |`Dump MIFARE Ultralight family tag to binary file`
|
||||||
|`hf mfu incr `|N |`Increments Ev1/NTAG counter`
|
|`hf mfu incr `|N |`Increments Ev1/NTAG counter`
|
||||||
|`hf mfu info `|N |`Tag information`
|
|`hf mfu info `|N |`Tag information`
|
||||||
|
@ -1096,6 +1097,21 @@ Check column "offline" for their availability.
|
||||||
|`lf hitag hts sim `|N |`Simulate Hitag S transponder`
|
|`lf hitag hts sim `|N |`Simulate Hitag S transponder`
|
||||||
|
|
||||||
|
|
||||||
|
### lf hitag htu
|
||||||
|
|
||||||
|
{ Hitag µ/8265 operations }
|
||||||
|
|
||||||
|
|command |offline |description
|
||||||
|
|------- |------- |-----------
|
||||||
|
|`lf hitag htu help `|Y |`This help`
|
||||||
|
|`lf hitag htu list `|Y |`List Hitag µ trace history`
|
||||||
|
|`lf hitag htu reader `|N |`Act like a Hitag µ reader`
|
||||||
|
|`lf hitag htu rdbl `|N |`Read Hitag µ block`
|
||||||
|
|`lf hitag htu dump `|N |`Dump Hitag µ blocks to a file`
|
||||||
|
|`lf hitag htu wrbl `|N |`Write Hitag µ block`
|
||||||
|
|`lf hitag htu sim `|N |`Simulate Hitag µ transponder`
|
||||||
|
|
||||||
|
|
||||||
### lf idteck
|
### lf idteck
|
||||||
|
|
||||||
{ Idteck RFIDs... }
|
{ Idteck RFIDs... }
|
||||||
|
|
|
@ -155,14 +155,20 @@ This is the cheapest and most common ID82xx chip available. It is usually sold a
|
||||||
|
|
||||||
#### Characteristics
|
#### 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`
|
* UID `00 00 00 00 00 00`
|
||||||
* Password protection (4b), usually "00000000"(default) or "9AC4999C"(FURUI)
|
* Password protection (4b), usually "00000000"(default) or "9AC4999C"(FURUI)
|
||||||
* CON0
|
* Config block 0xFF
|
||||||
* bit 0-1 -> data rate ’00’... 2kbit/s ’01’... 4kbit/s ’10’... 8kbit/s ’11’... 2kbit/s
|
* Byte0
|
||||||
* bit 2 when set, fixed to MC 2kbit/s
|
* bit 0-1 : Data Rate. ’00’ -> 2kbit/s, ’01’ -> 4kbit/s, ’10’ -> 8kbit/s, ’11’ -> 2kbit/s
|
||||||
* bit 3-6 reversed? all blocks always read without password and write with password
|
* bit 2 : 1 -> fixed to 2kbit/s
|
||||||
* bit 7 -> enable TTF
|
* 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
|
* Currently unimplemented in proxmark3 client
|
||||||
* Other names:
|
* Other names:
|
||||||
* ID8210 (CN)
|
* ID8210 (CN)
|
||||||
|
|
147
doc/md/em4x70/arbitrary_lf_em_commands.md
Normal file
147
doc/md/em4x70/arbitrary_lf_em_commands.md
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
# arbitrary lf em commands
|
||||||
|
|
||||||
|
Goals:
|
||||||
|
1. Improved logging of `lf em` commands and responses
|
||||||
|
2. Greater certainty in command sequences
|
||||||
|
3. Easier testing of new commands
|
||||||
|
|
||||||
|
## Methodology
|
||||||
|
|
||||||
|
This is documenting the actual commands used by existing code. Phases include:
|
||||||
|
* Document the existing command sequences
|
||||||
|
* Document the existing logging APIs
|
||||||
|
* Define small set of timing-sensitive functions as abstractions
|
||||||
|
* Implement the abstractions
|
||||||
|
* Add logging
|
||||||
|
|
||||||
|
The goal is to improve logging and debugging, and allow easily testing new LF commands.
|
||||||
|
|
||||||
|
## EM4x70 (aka ID48, aka Megamos)
|
||||||
|
|
||||||
|
Only six command sequences currently used:
|
||||||
|
|
||||||
|
#define EM4X70_COMMAND_ID 0x01
|
||||||
|
#define EM4X70_COMMAND_UM1 0x02
|
||||||
|
#define EM4X70_COMMAND_AUTH 0x03
|
||||||
|
#define EM4X70_COMMAND_PIN 0x04
|
||||||
|
#define EM4X70_COMMAND_WRITE 0x05
|
||||||
|
#define EM4X70_COMMAND_UM2 0x07
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### ID Command
|
||||||
|
|
||||||
|
Wait for `LIW` (listen window), and start transmission at next `LIW`:
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window sync
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b001` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
tag | HEADER | HEADER (0b1111'1111'1111'0000)
|
||||||
|
tag | 32-bits | ID (D31..D0)
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
### UM1 Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b010` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
tag | 16-bits | HEADER
|
||||||
|
tag | 32-bits | UM1 data
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
### UM2 Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b111` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
tag | 16-bits | HEADER
|
||||||
|
tag | 64-bits | UM2 data
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
|
||||||
|
### Auth Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b011` | CMD
|
||||||
|
reader | `0b0` | command parity bit
|
||||||
|
reader | 56-bits | RN
|
||||||
|
reader | 7-bits | Tdiv == 0b0000000 (always zero)
|
||||||
|
reader | 28-bits | f(RN)
|
||||||
|
tag | 16-bits | HEADER
|
||||||
|
tag | 20-bits | g(RN)
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
### Write Word
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b101` | CMD
|
||||||
|
reader | `0b0` | command parity bit
|
||||||
|
reader | 4-bits | address/block to write
|
||||||
|
reader | 1-bit | address/block parity bit
|
||||||
|
reader | 25-bits | 5x5 data w/ row and column parity
|
||||||
|
tag | ACK | Wait (TWA) for ACK ... time to wait before searching for ACK
|
||||||
|
tag | ACK | Wait (WEE) for ACK ... time to wait before searching for ACK
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### PIN Command
|
||||||
|
|
||||||
|
source | bits | comment
|
||||||
|
----------|---------|---------
|
||||||
|
tag | LIW | listen window
|
||||||
|
reader | `0b00` | RM
|
||||||
|
reader | `0b100` | CMD
|
||||||
|
reader | `0b1` | command parity bit
|
||||||
|
reader | 32-bits | ID of the tag
|
||||||
|
reader | 32-bits | PIN
|
||||||
|
tag | ACK | Wait (TWALB) for ACK ... time to wait before searching for ACK
|
||||||
|
tag | HEADER | DELAYED (TWEE) header ... time to wait before searching for header
|
||||||
|
tag | 32-bits | ID of the tag
|
||||||
|
tag | LIW | tag reverts to be ready for next command
|
||||||
|
|
||||||
|
|
||||||
|
### Abstraction required
|
||||||
|
|
||||||
|
Possible items to abstract:
|
||||||
|
* bits to send: quantity of bits to be sent + storage containing those bits
|
||||||
|
* bits to receive: expected bits to receive + storage to receive those bits
|
||||||
|
* LIW: special-case handling to synchronize next command
|
||||||
|
* ACK: special-case handling to wait for ACK
|
||||||
|
* HEADER: special-case handling to wait for HEADER
|
||||||
|
* DELAY: ticks to delay before processing next item
|
||||||
|
|
||||||
|
Special handling required for:
|
||||||
|
* `HEADER` --> 12-bits of zero, 4-bits of one. Consider a timeout: if tag disappears, no pulse found, while sometimes expect long time before HEADER appears (as in SEND_PIN). Read of header may miss the first few bits during transition, so need to special-case handling of this detection.
|
||||||
|
* `LIW` --> Timing-sensitive, syncs reader with tag ... reader must send during 32 period where chip's modulator is ON.
|
||||||
|
* `ACK` --> This is currently a time-to-delay.
|
||||||
|
Should this be a maximum time to wait for ACK?
|
||||||
|
Currently, could sit waiting for long time
|
||||||
|
if no tag present, as `check_ack()` has no timeout.
|
||||||
|
|
||||||
|
```C
|
||||||
|
WaitTicks(EM4X70_T_TAG_TWA);
|
||||||
|
if (check_ack())
|
||||||
|
WaitTicks(EM4X70_T_TAG_WEE);
|
||||||
|
if (check_ack())
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
1517
doc/md/em4x70/lf_em4x70_trace_notes.md
Normal file
1517
doc/md/em4x70/lf_em4x70_trace_notes.md
Normal file
File diff suppressed because it is too large
Load diff
|
@ -202,10 +202,15 @@ extern bool g_tearoff_enabled;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// bit stream operations
|
// bit stream operations
|
||||||
#define TEST_BIT(data, i) (*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1
|
#define TEST_BIT_MSB(data, i) ((*((data) + ((i) / 8)) >> (7 - ((i) % 8))) & 1)
|
||||||
#define SET_BIT(data, i) *((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8)))
|
#define SET_BIT_MSB(data, i) (*((data) + ((i) / 8)) |= (1 << (7 - ((i) % 8))))
|
||||||
#define CLEAR_BIT(data, i) *((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8)))
|
#define CLEAR_BIT_MSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << (7 - ((i) % 8))))
|
||||||
#define FLIP_BIT(data, i) *((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8)))
|
#define FLIP_BIT_MSB(data, i) (*((data) + ((i) / 8)) ^= (1 << (7 - ((i) % 8))))
|
||||||
|
|
||||||
|
#define TEST_BIT_LSB(data, i) ((*((data) + ((i) / 8)) >> ((i) % 8)) & 1)
|
||||||
|
#define SET_BIT_LSB(data, i) (*((data) + ((i) / 8)) |= (1 << ((i) % 8)))
|
||||||
|
#define CLEAR_BIT_LSB(data, i) (*((data) + ((i) / 8)) &= ~(1 << ((i) % 8)))
|
||||||
|
#define FLIP_BIT_LSB(data, i) (*((data) + ((i) / 8)) ^= (1 << ((i) % 8)))
|
||||||
|
|
||||||
// time for decompressing and loading the image to the FPGA
|
// time for decompressing and loading the image to the FPGA
|
||||||
#define FPGA_LOAD_WAIT_TIME (1500)
|
#define FPGA_LOAD_WAIT_TIME (1500)
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#define EM4X70_NUM_BLOCKS 16
|
#define EM4X70_NUM_BLOCKS 16
|
||||||
|
|
||||||
|
@ -28,24 +29,36 @@
|
||||||
#define EM4X70_PIN_WORD_LOWER 10
|
#define EM4X70_PIN_WORD_LOWER 10
|
||||||
#define EM4X70_PIN_WORD_UPPER 11
|
#define EM4X70_PIN_WORD_UPPER 11
|
||||||
|
|
||||||
|
/// @brief Command transport structure for EM4x70 commands.
|
||||||
|
/// @details
|
||||||
|
/// This structure is used to transport data from the PC
|
||||||
|
/// to the proxmark3, and contain all data needed for
|
||||||
|
/// a given `lf em 4x70 ...` command to be processed
|
||||||
|
/// on the proxmark3.
|
||||||
|
/// The only requirement is that this structure remain
|
||||||
|
/// smaller than the NG buffer size (256 bytes).
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// ISSUE: `bool` type does not have a standard-defined size.
|
|
||||||
// therefore, compatibility between architectures /
|
|
||||||
// compilers is not guaranteed.
|
|
||||||
// ISSUE: C99 has no _Static_assert() ... was added in C11
|
|
||||||
// TODO: add _Static_assert(sizeof(bool)==1);
|
|
||||||
// TODO: add _Static_assert(sizeof(em4x70_data_t)==36);
|
|
||||||
bool parity;
|
bool parity;
|
||||||
|
|
||||||
// Used for writing address
|
// Used for writing address
|
||||||
uint8_t address;
|
uint8_t address;
|
||||||
// ISSUE: Presumes target is little-endian
|
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||||
uint16_t word;
|
uint16_t word;
|
||||||
|
|
||||||
// PIN to unlock
|
// PIN to unlock
|
||||||
|
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||||
uint32_t pin;
|
uint32_t pin;
|
||||||
|
|
||||||
// Used for authentication
|
// Used for authentication
|
||||||
|
//
|
||||||
|
// IoT safe subset of C++ would be helpful here,
|
||||||
|
// to support variable-bit-length integer types
|
||||||
|
// as integral integer types.
|
||||||
|
//
|
||||||
|
// Even C23 would work for this (GCC14+, Clang15+):
|
||||||
|
// _BitInt(56) rnd;
|
||||||
|
// _BitInt(28) frnd;
|
||||||
|
// _BitInt(20) grnd;
|
||||||
uint8_t frnd[4];
|
uint8_t frnd[4];
|
||||||
uint8_t grnd[3];
|
uint8_t grnd[3];
|
||||||
uint8_t rnd[7];
|
uint8_t rnd[7];
|
||||||
|
@ -54,9 +67,20 @@ typedef struct {
|
||||||
uint8_t crypt_key[12];
|
uint8_t crypt_key[12];
|
||||||
|
|
||||||
// used for bruteforce the partial key
|
// used for bruteforce the partial key
|
||||||
// ISSUE: Presumes target is little-endian
|
// BUGBUG: Non-portable ... presumes stored in little-endian form!
|
||||||
uint16_t start_key;
|
uint16_t start_key;
|
||||||
|
|
||||||
} em4x70_data_t;
|
} em4x70_data_t;
|
||||||
|
//_Static_assert(sizeof(em4x70_data_t) == 36);
|
||||||
|
|
||||||
|
// ISSUE: `bool` type does not have a standard-defined size.
|
||||||
|
// therefore, compatibility between architectures /
|
||||||
|
// compilers is not guaranteed.
|
||||||
|
// TODO: verify alignof(bool) == 1
|
||||||
|
//_Static_assert(sizeof(bool) == 1, "bool size mismatch");
|
||||||
|
typedef union {
|
||||||
|
uint8_t data[32];
|
||||||
|
} em4x70_tag_t;
|
||||||
|
//_Static_assert(sizeof(em4x70_tag_t) == 32, "em4x70_tag_t size mismatch");
|
||||||
|
|
||||||
#endif /* EM4X70_H__ */
|
#endif /* EM4X70_H__ */
|
||||||
|
|
109
include/hitag.h
109
include/hitag.h
|
@ -13,7 +13,7 @@
|
||||||
//
|
//
|
||||||
// See LICENSE.txt for the text of the license.
|
// See LICENSE.txt for the text of the license.
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Hitag 2, Hitag S
|
// Hitag 2, Hitag S, Hitag µ
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,13 +39,40 @@
|
||||||
#define HITAGS_UID_PADR 0
|
#define HITAGS_UID_PADR 0
|
||||||
#define HITAGS_CONFIG_PADR 1
|
#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
|
// need to see which limits these cards has
|
||||||
#define HITAG1_MAX_BYTE_SIZE 64
|
#define HITAG1_MAX_BYTE_SIZE 64
|
||||||
#define HITAGU_MAX_BYTE_SIZE 64
|
|
||||||
#define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE)
|
#define HITAG_MAX_BYTE_SIZE (64 * HITAG_BLOCK_SIZE)
|
||||||
|
|
||||||
#define HITAG2_CONFIG_BLOCK 3
|
#define HITAG2_CONFIG_BLOCK 3
|
||||||
|
|
||||||
|
// Modulation types - used by shared code
|
||||||
|
typedef enum modulation {
|
||||||
|
AC2K = 0, // Amplitude modulation 2000 bits/s
|
||||||
|
AC4K, // Amplitude modulation 4000 bits/s
|
||||||
|
MC4K, // Manchester modulation 4000 bits/s
|
||||||
|
MC8K // Manchester modulation 8000 bits/s
|
||||||
|
} MOD;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HTSF_PLAIN,
|
HTSF_PLAIN,
|
||||||
HTSF_82xx,
|
HTSF_82xx,
|
||||||
|
@ -63,6 +90,12 @@ typedef enum {
|
||||||
HT2F_TEST_AUTH_ATTEMPTS,
|
HT2F_TEST_AUTH_ATTEMPTS,
|
||||||
HT2F_UID_ONLY,
|
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;
|
} PACKED hitag_function;
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
@ -125,7 +158,7 @@ struct hitagS_tag {
|
||||||
int max_page;
|
int max_page;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
uint8_t pages[64][4];
|
uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE];
|
||||||
struct {
|
struct {
|
||||||
// page 0
|
// page 0
|
||||||
uint32_t uid_le;
|
uint32_t uid_le;
|
||||||
|
@ -143,11 +176,62 @@ struct hitagS_tag {
|
||||||
|
|
||||||
} PACKED;
|
} 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 {
|
typedef struct {
|
||||||
hitag_function cmd;
|
hitag_function cmd;
|
||||||
uint8_t page;
|
uint8_t page;
|
||||||
uint8_t page_count;
|
uint8_t page_count;
|
||||||
uint8_t data[HITAGS_PAGE_SIZE];
|
uint8_t data[HITAG_BLOCK_SIZE];
|
||||||
uint8_t NrAr[HITAG_NRAR_SIZE];
|
uint8_t NrAr[HITAG_NRAR_SIZE];
|
||||||
// unaligned access to key as uint64_t will abort.
|
// unaligned access to key as uint64_t will abort.
|
||||||
// todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place?
|
// todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place?
|
||||||
|
@ -163,6 +247,9 @@ typedef struct {
|
||||||
|
|
||||||
// Hitag S section
|
// Hitag S section
|
||||||
uint8_t mode;
|
uint8_t mode;
|
||||||
|
|
||||||
|
// Hitag µ section
|
||||||
|
uint8_t uid[HITAGU_UID_SIZE];
|
||||||
} PACKED lf_hitag_data_t;
|
} PACKED lf_hitag_data_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -178,4 +265,18 @@ typedef struct {
|
||||||
int8_t pages_reason[HITAGS_MAX_PAGES];
|
int8_t pages_reason[HITAGS_MAX_PAGES];
|
||||||
uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE];
|
uint8_t pages[HITAGS_MAX_PAGES][HITAGS_PAGE_SIZE];
|
||||||
} PACKED lf_hts_read_response_t;
|
} 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
|
#endif
|
||||||
|
|
|
@ -602,6 +602,12 @@ typedef struct {
|
||||||
#define CMD_LF_HITAGS_WRITE 0x0375
|
#define CMD_LF_HITAGS_WRITE 0x0375
|
||||||
#define CMD_LF_HITAGS_UID 0x037A
|
#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_LF_HITAG_ELOAD 0x0376
|
||||||
|
|
||||||
#define CMD_HF_ISO14443A_ANTIFUZZ 0x0380
|
#define CMD_HF_ISO14443A_ANTIFUZZ 0x0380
|
||||||
|
|
|
@ -455,12 +455,14 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
|
||||||
#define LTO 12
|
#define LTO 12
|
||||||
#define PROTO_HITAG2 13
|
#define PROTO_HITAG2 13
|
||||||
#define PROTO_HITAGS 14
|
#define PROTO_HITAGS 14
|
||||||
#define PROTO_CRYPTORF 15
|
#define PROTO_HITAGU 15
|
||||||
#define SEOS 16
|
#define PROTO_CRYPTORF 16
|
||||||
#define PROTO_MFPLUS 17
|
#define SEOS 17
|
||||||
#define PROTO_TEXKOM 18
|
#define PROTO_MFPLUS 18
|
||||||
#define PROTO_XEROX 19
|
#define PROTO_TEXKOM 19
|
||||||
#define PROTO_FMCOS20 20
|
#define PROTO_XEROX 20
|
||||||
|
#define PROTO_FMCOS20 21
|
||||||
|
#define COUNT_OF_PROTOCOLS 22
|
||||||
|
|
||||||
// Picopass fuses
|
// Picopass fuses
|
||||||
#define FUSE_FPERS 0x80
|
#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_WRITE_BLOCK 0x90 // 1001 WRITE BLOCK
|
||||||
#define HITAGS_QUIET 0x70 // 0111 QUIET
|
#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
|
// LTO-CM commands
|
||||||
#define LTO_REQ_STANDARD 0x45
|
#define LTO_REQ_STANDARD 0x45
|
||||||
#define LTO_REQ_ALL 0x4A
|
#define LTO_REQ_ALL 0x4A
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue