mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-09-11 18:29:22 -07:00
Merge branch 'master' of https://github.com/Antiklesys/proxmark3
This commit is contained in:
@@ -3,7 +3,10 @@ All notable changes to this project will be documented in this file.
|
||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||
|
||||
## [unreleased][unreleased]
|
||||
- Fixed `trace list -t topaz` - now parses topaz reader frames up to 16 bytes (@iceman1001)
|
||||
- Fixed `hw bwm` - doc and comment references still named the flat `hw bwmsetcap`/`bwmcharge`/`bwmautooff` commands from before the subgroup move (@xilni)
|
||||
- Added TA1=96 support via pwm to sim module `sim024.bin` - v4.66 (@antiklesys)
|
||||
- Added `hf mfu ndefformat` - NDEF format Ultralight/NTAG tags, restores the NXP factory Capability Container for the detected type (@0x6r1an0y)
|
||||
- Changed `lf hitag` - refactoring hitag2 operations and now they work much better (@iceman1001)
|
||||
- Fixed `lf hitag list` - now prints in same style like the rest (@iceman1001)
|
||||
- Changed pathing - client now supports `~/` tilde sign in paths (@iceman1001)
|
||||
@@ -79,6 +82,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
||||
- Fixed `hf_mf_uscuid_prog.lua` - corrected the script name shown in its usage text (@c-barron)
|
||||
- Added `hf felica seacauth1` command (@kormax)
|
||||
- Added `lf trovan` commands to support Trovan Animal ID (@iceman1001)
|
||||
- Added support for emulating ST25TA tag (IKEA Rothult) with custom NDEF response (@sanduuz)
|
||||
- Added `hf mf gdmgetblk/gdmgethidblk/gdmsethidblk/gdmsetuid/gdmwipe/gdmsetsig` (@0x6r1an0y)
|
||||
- Changed `hf mf gdmparsecfg/gdmsetblk` (@0x6r1an0y)
|
||||
- Renamed `hf mf gdmcfg` (@0x6r1an0y)
|
||||
|
||||
+75
-4
@@ -27,6 +27,7 @@
|
||||
#include "bwm_forward.h" // bwm_fwd_negotiate_baud()
|
||||
#include "bwm_wifi.h"
|
||||
#endif
|
||||
#include "pm3_cmd.h"
|
||||
#include "proxmark3_arm.h"
|
||||
#include "dbprint.h"
|
||||
#include "pmflash.h"
|
||||
@@ -107,7 +108,7 @@ static bool s_autooff_setup = false;
|
||||
// Auto power-off on USB unplug. CRITICAL: only powers off on a USB-present -> absent
|
||||
// TRANSITION - i.e. the board was running on USB and the cable was pulled. A board that
|
||||
// booted on battery (button press, no USB) must NOT auto-off, or it could never be used
|
||||
// unplugged at all (and the hw bwmautooff toggle would be unreachable, since setting it
|
||||
// unplugged at all (and the hw bwm autooff toggle would be unreachable, since setting it
|
||||
// needs a client/USB). So we require having seen USB present at least once this session
|
||||
// before an absent reading triggers shutdown.
|
||||
static void bwm_autooff_check(void) {
|
||||
@@ -1143,7 +1144,7 @@ static bool QCTestPM5(uint8_t *failed_item, uint32_t timeout_ms) {
|
||||
// 蜂鸣器 (PB13 使能, PC9 = TMR8_CH4 调制) 由通用 buzzer 模块驱动
|
||||
BuzzerSetup();
|
||||
|
||||
LEDsoff(); // 在开始测试之前线关闭所有LED
|
||||
LEDsoff(); // 在开始测试之前先关闭所有LED
|
||||
|
||||
*failed_item = 2;
|
||||
// 在开始测试之前,如果按钮是按下的,则认为失败,有可能按钮不良卡住了
|
||||
@@ -1224,6 +1225,59 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int8_t QCTestPM5IO(uint16_t index, uint8_t status) {
|
||||
// !!! No one is allowed to modify the definition and order of this list except DXL.
|
||||
// Otherwise, the PM5 factory tester will fail to check.
|
||||
struct qc_io_map {
|
||||
crm_periph_clock_type gpio_clk;
|
||||
gpio_type *gpio_group;
|
||||
uint16_t gpio_pin;
|
||||
} const qc_io_map[] = {
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_14}, // SIGIN1-5x2P-SWD-CLK
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_13}, // SIGIN2-5x2P-SWD-DIO
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_3}, // SIGIN3-5x2P-UART_RX
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_2}, // SIGIN4-5x2P-UART_TX
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_7}, // SIGIN5-CEP-USB_RXP(MOSI)
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_6}, // SIGIN6-CEP-USB_RXN(MISO)
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_9}, // SIGIN7-CEP-USB_SBU(UART-1-line)
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_11}, // SIGIN8-CEP-USB_DN
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_4}, // SIGIN9-CEP-USB_TXP(CSN)
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_12}, // SIGIN10-CEP-USB_DP
|
||||
{.gpio_clk = CRM_GPIOA_PERIPH_CLOCK, .gpio_group = GPIOA, .gpio_pin = GPIO_PINS_5}, // SIGIN11-CEP-USB_TXN(CLK)
|
||||
};
|
||||
if (index >= ARRAYLEN(qc_io_map)) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
// For RXP/RXN/TXP/TXN
|
||||
gpio_inter_usb_spi_role_setup();
|
||||
Gpio_Inter_USB_SPI_Role_High();
|
||||
// Enable the clock for the GPIO port and configure the pin as output
|
||||
crm_periph_clock_enable(qc_io_map[index].gpio_clk, TRUE);
|
||||
// Configure the GPIO pin as output or input based on the status parameter
|
||||
gpio_init_type gpio_init_struct;
|
||||
gpio_default_para_init(&gpio_init_struct);
|
||||
gpio_init_struct.gpio_pins = qc_io_map[index].gpio_pin;
|
||||
gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
|
||||
// Set the GPIO pin state or RESET to default based on the status parameter( -> Preset <- )
|
||||
if (status == 0) {
|
||||
gpio_bits_reset(qc_io_map[index].gpio_group, qc_io_map[index].gpio_pin);
|
||||
} else if (status == 1) {
|
||||
gpio_bits_set(qc_io_map[index].gpio_group, qc_io_map[index].gpio_pin);
|
||||
} else {
|
||||
// Reset to Default state or MUX
|
||||
if (index == 0 || index == 1) {
|
||||
// SIGIN1-SWD-CLK or SIGIN2-SWD-DIO
|
||||
gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
|
||||
gpio_init_struct.gpio_pull = index == 0 ? GPIO_PULL_DOWN : GPIO_PULL_UP;
|
||||
gpio_pin_mux_config(qc_io_map[index].gpio_group, qc_io_map[index].gpio_pin, GPIO_MUX_0);
|
||||
} else {
|
||||
gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
|
||||
}
|
||||
}
|
||||
gpio_init(qc_io_map[index].gpio_group, &gpio_init_struct);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// static, not on the stack: this is 512 bytes and PacketReceived is already deep
|
||||
@@ -3871,13 +3925,30 @@ static void PacketReceived(PacketCommandNG *packet) {
|
||||
break;
|
||||
}
|
||||
#ifdef PM5
|
||||
case CMD_PM5_QC_TEST: {
|
||||
case CMD_PM5_QC_TEST_HW: {
|
||||
uint8_t failed_item = 0;
|
||||
uint32_t timeout_ms = 0;
|
||||
if (packet->length >= sizeof(timeout_ms)) {
|
||||
memcpy(&timeout_ms, packet->data.asBytes, sizeof(timeout_ms));
|
||||
}
|
||||
reply_ng(CMD_PM5_QC_TEST, QCTestPM5(&failed_item, timeout_ms) ? PM3_SUCCESS : PM3_EFAILED, &failed_item, 1);
|
||||
reply_ng(CMD_PM5_QC_TEST_HW, QCTestPM5(&failed_item, timeout_ms) ? PM3_SUCCESS : PM3_EFAILED, &failed_item, 1);
|
||||
break;
|
||||
}
|
||||
case CMD_PM5_QC_TEST_IO: {
|
||||
struct p {
|
||||
uint32_t pwd; // 0xDEADBEEF
|
||||
uint16_t index; // index of the IO to test
|
||||
uint8_t status; // 0 = low, 1 = high, 2 = float or RESET TO DEFAULT
|
||||
} PACKED;
|
||||
struct p *payload = (struct p *)packet->data.asBytes;
|
||||
// !!! IMPORTANT !!!
|
||||
// This password must not be written in the client software.
|
||||
// Users should never use this command without knowing its purpose, otherwise it may damage the device.
|
||||
if (payload->pwd != 0xDEADBEEF) {
|
||||
reply_ng(CMD_PM5_QC_TEST_IO, PM3_EFAILED, NULL, 0);
|
||||
break;
|
||||
}
|
||||
reply_ng(CMD_PM5_QC_TEST_IO, QCTestPM5IO(payload->index, payload->status), NULL, 0);
|
||||
break;
|
||||
}
|
||||
case CMD_PM5_RGB_SET: {
|
||||
|
||||
+85
-6
@@ -1050,18 +1050,23 @@ void test_beep(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void test_power_of_by_btn(void) {
|
||||
void test_power_off_by_btn(void) {
|
||||
usb_enable();
|
||||
SpinDelay(1000);
|
||||
dxl_print_dbg("SystemStart\n");
|
||||
// dxl_print_dbg("SystemStart\n");
|
||||
while (1) {
|
||||
// 第一阶段,等按下
|
||||
if (is_btn_pressed()) {
|
||||
// 第二阶段,等抬起
|
||||
while (is_btn_pressed()) {}
|
||||
// 第三阶段,等一会儿
|
||||
SpinDelay(50);
|
||||
// 拉低直接关机
|
||||
dxl_print_dbg("SystemOff\n");
|
||||
// dxl_print_dbg("SystemOff\n");
|
||||
Gpio_ARM_Power_ON_Low();
|
||||
// 拉低关机的话,还会有一段PWR电容放电时间,此时我们应当让系统进入死循环,不再处理任何事情
|
||||
while (1) {
|
||||
dxl_print_dbg("Waiting Power Off\n");
|
||||
// dxl_print_dbg("Waiting Power Off\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1827,6 +1832,74 @@ void test_config_uart_tx2_to_dbgio(void) {
|
||||
gpio_init(GPIOA, &gpio_init_struct); // PA2_TX
|
||||
}
|
||||
|
||||
#include "fpga_loader.h"
|
||||
#include "lfsampling.h"
|
||||
|
||||
void test_max_power(void) {
|
||||
FpgaResetComInterface();
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
|
||||
SpinDelay(100);
|
||||
FpgaSetup24MHzClk();
|
||||
|
||||
uint8_t data;
|
||||
|
||||
// 开所有的灯
|
||||
LED_A_ON();
|
||||
LED_B_ON();
|
||||
LED_C_ON();
|
||||
LED_D_ON();
|
||||
test_i2c_rgb_simple();
|
||||
|
||||
// 0x03 DischargeCurrentControl 寄存器
|
||||
data = 0xE1; // 修改放电电流到 3A
|
||||
I2C_BufferWrite(&data, 1, 0x03, 0x93);
|
||||
// 0x05 ChargerTermination/TimerControl 寄存器
|
||||
data = 0x1A; // 禁用定时器,正常为了安全可能需要在MainLoop喂狗
|
||||
I2C_BufferWrite(&data, 1, 0x05, 0x93);
|
||||
|
||||
#if 0
|
||||
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER);
|
||||
#else
|
||||
|
||||
// 开场
|
||||
// FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD
|
||||
// FPGA_MAJOR_MODE_HF_READER
|
||||
// FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD);
|
||||
LFSetupFPGAForADC(LF_DIVISOR_125, true);
|
||||
|
||||
gpio_fpga_mod_only_setup();
|
||||
Gpio_SSC_DOUT_Low();
|
||||
|
||||
// 配置低频高Q
|
||||
data = 0x87; // 0x87 = 10000111: 125k + 高Q + 开启两个LED
|
||||
I2C_BufferWrite(&data, 1, 0x02, 0x51 << 1);
|
||||
|
||||
#endif
|
||||
|
||||
// 调到最高输出电压
|
||||
FpgaSendCommand(FPGA_CMD_SET_PWR_PWM_LOW_COUNT, 4095u & 0xFFF);
|
||||
|
||||
// 蜂鸣器持续响着
|
||||
test_beep();
|
||||
}
|
||||
|
||||
void test_nvic_reset(void) {
|
||||
LED_A_ON();
|
||||
SpinDelay(100);
|
||||
LED_A_OFF();
|
||||
LED_B_ON();
|
||||
while (1) {
|
||||
if (is_btn_pressed()) {
|
||||
LED_B_OFF();
|
||||
LED_C_ON();
|
||||
while (is_btn_pressed()) {}
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 覆盖 UnitTestMain 实现单元测试
|
||||
void UnitTestMain(void);
|
||||
|
||||
@@ -1847,13 +1920,19 @@ void UnitTestMain(void) {
|
||||
// ------------------------------- 关闭所有的LED -------------------------------
|
||||
LEDsoff();
|
||||
|
||||
// ------------------------------- 功耗测试 -------------------------------
|
||||
// test_max_power();
|
||||
|
||||
// ------------------------------- 关闭FPGA输出 -------------------------------
|
||||
// 不然的话调试的时候也会很发热
|
||||
SpinDelay(500);
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
|
||||
// ------------------------------- 测试 NVIC 系统复位 -------------------------------
|
||||
// test_nvic_reset();
|
||||
|
||||
// ------------------------------- 测试 配置UART TX2为DBGIO输出 -------------------------------
|
||||
test_config_uart_tx2_to_dbgio();
|
||||
// test_config_uart_tx2_to_dbgio();
|
||||
|
||||
// ------------------------------- 测试 蓝牙电池套件UART通信 -------------------------------
|
||||
// test_bwm_uart();
|
||||
@@ -1886,7 +1965,7 @@ void UnitTestMain(void) {
|
||||
// test_i2c_ant_software();
|
||||
|
||||
// ------------------------------- 测试按钮关机 -------------------------------
|
||||
// test_power_of_by_btn();
|
||||
// test_power_off_by_btn();
|
||||
|
||||
// ------------------------------- 测试蜂鸣器 -------------------------------
|
||||
// usb_enable();
|
||||
|
||||
@@ -228,7 +228,7 @@ void bwm_print_battery_status(void) {
|
||||
// Capacity. FCC is the gauge's learned present full capacity; health = FCC/design.
|
||||
// NOTE: only meaningful once the gauge has run an Impedance Track learning cycle
|
||||
// (a full charge/discharge). Before that it is an unconverged estimate. If Design
|
||||
// Capacity was never provisioned (hw bwmsetcap), the ratio is against the gauge
|
||||
// Capacity was never provisioned (hw bwm setcap), the ratio is against the gauge
|
||||
// default, not the fitted cell - so it can read wildly wrong.
|
||||
uint16_t fcc = 0, design = 0;
|
||||
if (bwm_gauge_read16(BWM_GAUGE_FCC, &fcc) && fcc > 0) {
|
||||
@@ -247,7 +247,7 @@ void bwm_print_battery_status(void) {
|
||||
// --- BQ27427 provisioning: set Design Capacity for the fitted cell -------------
|
||||
// One-time. The gauge ships with a ~1000+ mAh default profile, so RemainingCapacity
|
||||
// reads wrong for the fitted pack until Design Capacity is programmed. Invoked by the
|
||||
// `hw bwmsetcap` client command (CMD_PM5_BWM_SET_CAP) - deliberately NOT run at boot,
|
||||
// `hw bwm setcap` client command (CMD_PM5_BWM_SET_CAP) - deliberately NOT run at boot,
|
||||
// because a config-update cycle disrupts the Impedance Track learning cycle.
|
||||
// Per BQ27427 TRM (SLUUCD5): State subclass 82 (0x52), Design Capacity at offset 6
|
||||
// -> block addr 0x46 (MSB)/0x47 (LSB), big-endian. Assumes gauge UNSEALED (factory default).
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "common.h"
|
||||
|
||||
// Reference design capacity for the fitted cell (VXE 502540, 500 mAh / 3.7 V).
|
||||
// Used as the default target for `hw bwmsetcap` and as the fall-back divisor for
|
||||
// Used as the default target for `hw bwm setcap` and as the fall-back divisor for
|
||||
// the battery-health estimate.
|
||||
#define BWM_DEFAULT_DESIGN_CAP_MAH 500
|
||||
#define BWM_DEFAULT_VCHG_MV 4100 // default charge-voltage target (mV); snaps to 4095 (15mV step)
|
||||
@@ -52,7 +52,7 @@ void bwm_detect_and_init(void);
|
||||
void bwm_print_battery_status(void);
|
||||
|
||||
// One-time BQ27427 Design Capacity provisioning (CMD_PM5_BWM_SET_CAP /
|
||||
// `hw bwmsetcap`). Idempotent - returns true without a CFGUPDATE cycle if the
|
||||
// `hw bwm setcap`). Idempotent - returns true without a CFGUPDATE cycle if the
|
||||
// value is already correct. Assumes the gauge is UNSEALED (factory default).
|
||||
bool bwm_gauge_provision_capacity(uint16_t cap_mah);
|
||||
|
||||
|
||||
+43
-5
@@ -19,6 +19,7 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "iso14443a.h"
|
||||
|
||||
#include "pm3_cmd.h"
|
||||
#include "string.h"
|
||||
#include "proxmark3_arm.h"
|
||||
#include "cmd.h"
|
||||
@@ -1776,13 +1777,20 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u
|
||||
#define DYNAMIC_RESPONSE_BUFFER_SIZE 64
|
||||
#define DYNAMIC_MODULATION_BUFFER_SIZE 512
|
||||
|
||||
uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE);
|
||||
uint16_t dynamic_response_buffer_size = DYNAMIC_RESPONSE_BUFFER_SIZE;
|
||||
uint16_t dynamic_modulation_buffer_size = DYNAMIC_MODULATION_BUFFER_SIZE;
|
||||
if (tagType == 10) {
|
||||
dynamic_response_buffer_size = ST25TA_EML_NDEF_MAX + 4;
|
||||
dynamic_modulation_buffer_size = 4096;
|
||||
}
|
||||
|
||||
uint8_t *dynamic_response_buffer = BigBuf_calloc(dynamic_response_buffer_size);
|
||||
if (dynamic_response_buffer == NULL) {
|
||||
BigBuf_free_keep_EM();
|
||||
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
|
||||
return;
|
||||
}
|
||||
uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE);
|
||||
uint8_t *dynamic_modulation_buffer = BigBuf_calloc(dynamic_modulation_buffer_size);
|
||||
if (dynamic_modulation_buffer == NULL) {
|
||||
BigBuf_free_keep_EM();
|
||||
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
|
||||
@@ -1803,6 +1811,22 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t st25ta_ndef[ST25TA_EML_NDEF_MAX] = {0};
|
||||
uint16_t st25ta_ndef_len = 0;
|
||||
if (tagType == 10) {
|
||||
uint8_t st25ta_header[ST25TA_EML_DATA_OFFSET] = {0};
|
||||
emlGet(st25ta_header, 0, sizeof(st25ta_header));
|
||||
if (memcmp(st25ta_header + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4) == 0) {
|
||||
st25ta_ndef_len = st25ta_header[ST25TA_EML_LEN_OFFSET] |
|
||||
(st25ta_header[ST25TA_EML_LEN_OFFSET + 1] << 8);
|
||||
if (st25ta_ndef_len <= ST25TA_EML_NDEF_MAX) {
|
||||
emlGet(st25ta_ndef, ST25TA_EML_DATA_OFFSET, st25ta_ndef_len);
|
||||
} else {
|
||||
st25ta_ndef_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mfu_dump_t *mfu_em_dump = NULL;
|
||||
if (tagType == 2 || tagType == 7) {
|
||||
mfu_em_dump = (mfu_dump_t *)BigBuf_get_EM_addr();
|
||||
@@ -2448,10 +2472,24 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u
|
||||
if (tagType == 10) {
|
||||
// we replay 90 00 for all commands but the read bin and we deny the verify cmd.
|
||||
|
||||
static const uint8_t default_st25ta_ndef[] = {
|
||||
0x00, 0x1b, 0xd1, 0x01, 0x17, 0x54, 0x02, 0x7a,
|
||||
0x68, 0xa2, 0x34, 0xcb, 0xd0, 0xe2, 0x03, 0xc7,
|
||||
0x3e, 0x62, 0x0b, 0xe8, 0xc6, 0x3c, 0x85, 0x2c,
|
||||
0xc5, 0x31, 0x31, 0x31, 0x32, 0x90, 0x00
|
||||
};
|
||||
const uint8_t *ndef = default_st25ta_ndef;
|
||||
uint16_t ndef_len = sizeof(default_st25ta_ndef);
|
||||
|
||||
if (st25ta_ndef_len > 0) {
|
||||
ndef = st25ta_ndef;
|
||||
ndef_len = st25ta_ndef_len;
|
||||
}
|
||||
|
||||
if (memcmp("\x02\xa2\xb0\x00\x00\x1d\x51\x69", receivedCmd, 8) == 0) {
|
||||
dynamic_response_info.response[0] = receivedCmd[0];
|
||||
memcpy(dynamic_response_info.response + 1, "\x00\x1b\xd1\x01\x17\x54\x02\x7a\x68\xa2\x34\xcb\xd0\xe2\x03\xc7\x3e\x62\x0b\xe8\xc6\x3c\x85\x2c\xc5\x31\x31\x31\x32\x90\x00", 31);
|
||||
dynamic_response_info.response_n = 32;
|
||||
memcpy(dynamic_response_info.response + 1, ndef, ndef_len);
|
||||
dynamic_response_info.response_n = ndef_len + 1;
|
||||
} else if (memcmp("\x02\x00\x20\x00\x01\x00\x6e\xa9", receivedCmd, 8) == 0) {
|
||||
dynamic_response_info.response[0] = receivedCmd[0];
|
||||
dynamic_response_info.response[1] = 0x63;
|
||||
@@ -2546,7 +2584,7 @@ void SimulateIso14443aTagEx(uint8_t tagType, uint16_t flags, uint8_t *useruid, u
|
||||
AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n);
|
||||
dynamic_response_info.response_n += 2;
|
||||
|
||||
if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) {
|
||||
if (prepare_tag_modulation(&dynamic_response_info, dynamic_modulation_buffer_size) == false) {
|
||||
if (g_dbglevel >= DBG_DEBUG) DbpString("Error preparing tag response");
|
||||
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
|
||||
break;
|
||||
|
||||
@@ -56,6 +56,13 @@ if (NOT SKIPPYTHON EQUAL 1)
|
||||
pkg_search_module(PYTHON3EMBED QUIET ${PYTHON3_PKGCONFIG}-embed)
|
||||
endif (NOT SKIPPYTHON EQUAL 1)
|
||||
|
||||
if (MINGW)
|
||||
# Prefer static archives for single-exe deployments.
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
set(ZLIB_USE_STATIC_LIBS ON)
|
||||
set(BZIP2_USE_STATIC_LIBS ON)
|
||||
endif()
|
||||
|
||||
# If cross-compiled, we need to init source and build.
|
||||
if (CMAKE_TOOLCHAIN_FILE)
|
||||
if (ANDROID)
|
||||
@@ -692,7 +699,26 @@ add_library(pm3rrg_rdv4 SHARED
|
||||
${ADDITIONAL_SRC}
|
||||
)
|
||||
|
||||
target_compile_definitions(pm3rrg_rdv4 PRIVATE LIBPM3)
|
||||
add_library(pm3rrg_rdv4_static STATIC
|
||||
${PM3_ROOT}/client/src/proxmark3.c
|
||||
${PM3_ROOT}/client/src/guidummy.cpp
|
||||
${TARGET_SOURCES}
|
||||
${ADDITIONAL_SRC}
|
||||
)
|
||||
|
||||
set(PM3_LIBRARY_TARGETS pm3rrg_rdv4 pm3rrg_rdv4_static)
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
target_compile_definitions(${target} PRIVATE LIBPM3)
|
||||
target_compile_options(${target} PUBLIC -Wall -Werror -O3)
|
||||
target_include_directories(${target} PRIVATE
|
||||
${PM3_ROOT}/common
|
||||
${PM3_ROOT}/common_fpga
|
||||
${PM3_ROOT}/include
|
||||
${PM3_ROOT}/client/src
|
||||
${PM3_ROOT}/client/include
|
||||
${ADDITIONAL_DIRS}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
# Fail the build if a client source file is missing from TARGET_SOURCES.
|
||||
@@ -700,17 +726,22 @@ if (UNIX AND NOT APPLE)
|
||||
set_property(TARGET pm3rrg_rdv4 APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--no-undefined")
|
||||
endif()
|
||||
|
||||
target_compile_options(pm3rrg_rdv4 PUBLIC -Wall -Werror -O3)
|
||||
if (EMBED_READLINE)
|
||||
if (NOT SKIPREADLINE EQUAL 1)
|
||||
add_dependencies(pm3rrg_rdv4 ncurses readline)
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
add_dependencies(${target} ncurses readline)
|
||||
endforeach()
|
||||
endif (NOT SKIPREADLINE EQUAL 1)
|
||||
endif (EMBED_READLINE)
|
||||
if (EMBED_BZIP2)
|
||||
add_dependencies(pm3rrg_rdv4 bzip2)
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
add_dependencies(${target} bzip2)
|
||||
endforeach()
|
||||
endif (EMBED_BZIP2)
|
||||
if (EMBED_LZ4)
|
||||
add_dependencies(pm3rrg_rdv4 lz4)
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
add_dependencies(${target} lz4)
|
||||
endforeach()
|
||||
endif (EMBED_LZ4)
|
||||
|
||||
if (MINGW)
|
||||
@@ -718,7 +749,9 @@ if (MINGW)
|
||||
# and setting _ISOC99_SOURCE sets internally __USE_MINGW_ANSI_STDIO=1
|
||||
# FTR __USE_MINGW_ANSI_STDIO seems deprecated in Mingw32
|
||||
# but not Mingw64 https://fr.osdn.net/projects/mingw/lists/archive/users/2019-January/000199.html
|
||||
target_compile_definitions(pm3rrg_rdv4 PRIVATE _ISOC99_SOURCE)
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
target_compile_definitions(${target} PRIVATE _ISOC99_SOURCE)
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}")
|
||||
@@ -727,15 +760,6 @@ if (MINGW)
|
||||
set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK})
|
||||
endif (MINGW)
|
||||
|
||||
target_include_directories(pm3rrg_rdv4 PRIVATE
|
||||
${PM3_ROOT}/common
|
||||
${PM3_ROOT}/common_fpga
|
||||
${PM3_ROOT}/include
|
||||
${PM3_ROOT}/client/src
|
||||
${PM3_ROOT}/client/include
|
||||
${ADDITIONAL_DIRS}
|
||||
)
|
||||
|
||||
if (NOT APPLE)
|
||||
# required for Raspberry Pi, but breaks with clang (OSX). Need to be at the end of the linker line.
|
||||
set(ADDITIONAL_LNK ${ADDITIONAL_LNK} -Wl,--as-needed -latomic -Wl,--no-as-needed)
|
||||
@@ -759,22 +783,26 @@ if (NOT WHEREAMI_FOUND)
|
||||
set(ADDITIONAL_LNK pm3rrg_rdv4_whereami ${ADDITIONAL_LNK})
|
||||
endif (NOT WHEREAMI_FOUND)
|
||||
|
||||
target_link_libraries(pm3rrg_rdv4 PRIVATE
|
||||
m
|
||||
pm3rrg_rdv4_mbedtls
|
||||
pm3rrg_rdv4_cliparser
|
||||
pm3rrg_rdv4_lua
|
||||
pm3rrg_rdv4_tinycbor
|
||||
pm3rrg_rdv4_amiibo
|
||||
pm3rrg_rdv4_reveng
|
||||
pm3rrg_rdv4_hardnested
|
||||
pm3rrg_rdv4_id48
|
||||
pm3rrg_rdv4_mqtt
|
||||
pm3rrg_rdv4_vec
|
||||
${ADDITIONAL_LNK})
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
target_link_libraries(${target} PRIVATE
|
||||
m
|
||||
pm3rrg_rdv4_mbedtls
|
||||
pm3rrg_rdv4_cliparser
|
||||
pm3rrg_rdv4_lua
|
||||
pm3rrg_rdv4_tinycbor
|
||||
pm3rrg_rdv4_amiibo
|
||||
pm3rrg_rdv4_reveng
|
||||
pm3rrg_rdv4_hardnested
|
||||
pm3rrg_rdv4_id48
|
||||
pm3rrg_rdv4_mqtt
|
||||
pm3rrg_rdv4_vec
|
||||
${ADDITIONAL_LNK})
|
||||
endforeach()
|
||||
|
||||
if (NOT SKIPPTHREAD EQUAL 1)
|
||||
target_link_libraries(pm3rrg_rdv4 PRIVATE pthread)
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
target_link_libraries(${target} PRIVATE pthread)
|
||||
endforeach()
|
||||
endif (NOT SKIPPTHREAD EQUAL 1)
|
||||
|
||||
if (NOT SKIPPYTHON EQUAL 1)
|
||||
@@ -786,4 +814,6 @@ if (NOT SKIPPYTHON EQUAL 1)
|
||||
endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND)
|
||||
endif (NOT SKIPPYTHON EQUAL 1)
|
||||
|
||||
target_link_directories(pm3rrg_rdv4 PRIVATE ${ADDITIONAL_LNKDIRS})
|
||||
foreach (target IN LISTS PM3_LIBRARY_TARGETS)
|
||||
target_link_directories(${target} PRIVATE ${ADDITIONAL_LNKDIRS})
|
||||
endforeach()
|
||||
|
||||
@@ -7,7 +7,7 @@ Polls `hw status` at a fixed interval and logs the fuel-gauge readings to CSV
|
||||
(plot with pm5_battery_test_graph.py).
|
||||
|
||||
If not yet done, you should set up the gauge first for meaningful numbers:
|
||||
hw bwmsetcap --cap 500 # your cell's mAh
|
||||
hw bwm setcap --cap 500 # your cell's mAh
|
||||
then fully charge
|
||||
|
||||
Usage:
|
||||
|
||||
@@ -545,10 +545,10 @@
|
||||
},
|
||||
{
|
||||
"AID": "FE137A",
|
||||
"Vendor": "Fermax Australia Pty Ltd",
|
||||
"Country": "AU",
|
||||
"Name": "Fermax High Security DESFire Credential",
|
||||
"Description": "Fermax Australia PACS Access Control",
|
||||
"Vendor": "Fermax Electronica SAU",
|
||||
"Country": "ES",
|
||||
"Name": "Fermax AC-MAX",
|
||||
"Description": "Fermax Proximity Card/Keyring DESFire EV2/EV3 2k Credential",
|
||||
"Type": "pacs"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -449,9 +449,9 @@
|
||||
"description": "Default system code used by FeliCa Lite/Lite-S tags",
|
||||
"services": [
|
||||
{
|
||||
"name": "Konami",
|
||||
"type": "e-amusement",
|
||||
"description": "Konami E-amusement",
|
||||
"name": "Konami e-amusement pass",
|
||||
"type": "arcade",
|
||||
"description": "Konami e-amusement pass (AIC)",
|
||||
"nodes": [
|
||||
{
|
||||
"code": "0b00",
|
||||
@@ -475,9 +475,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Aime",
|
||||
"type": "e-amusement",
|
||||
"description": "AIME E-amusement",
|
||||
"name": "Sega Aime",
|
||||
"type": "arcade",
|
||||
"description": "Sega Aime (AIC)",
|
||||
"nodes": [
|
||||
{
|
||||
"code": "0b00",
|
||||
@@ -488,9 +488,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "NESiCA",
|
||||
"type": "e-amusement",
|
||||
"description": "NESiCA E-amusement",
|
||||
"name": "Taito NESiCA",
|
||||
"type": "arcade",
|
||||
"description": "Taito NESiCA (AIC)",
|
||||
"nodes": [
|
||||
{
|
||||
"code": "0b00",
|
||||
@@ -501,9 +501,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Bandai Namco Pasport",
|
||||
"type": "e-amusement",
|
||||
"description": "Bandai Namco Pasport E-amusement",
|
||||
"name": "Bandai Namco Passport",
|
||||
"type": "arcade",
|
||||
"description": "Bandai Namco Passport (AIC)",
|
||||
"nodes": [
|
||||
{
|
||||
"code": "0b00",
|
||||
|
||||
@@ -1106,6 +1106,7 @@ int CmdHF14ASim(const char *Cmd) {
|
||||
bool ulauth_1a2_mirror;
|
||||
} PACKED payload;
|
||||
|
||||
memset(&payload, 0x00, sizeof(payload));
|
||||
payload.tagtype = tagtype;
|
||||
payload.flags = flags;
|
||||
payload.exitAfter = exitAfterNReads;
|
||||
|
||||
+54
-7
@@ -58,13 +58,13 @@ static bool legic_xor(uint8_t *data, uint16_t cardsize) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc) {
|
||||
void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc) {
|
||||
for (uint16_t i = 22; i < cardsize; i++) {
|
||||
data[i] ^= crc;
|
||||
}
|
||||
}
|
||||
|
||||
static bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
|
||||
bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
|
||||
// Segment headers are parsed from offset 22 everywhere else in LEGIC decoding.
|
||||
size_t start = 22;
|
||||
bool found_segment = false;
|
||||
@@ -96,7 +96,7 @@ static bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, co
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
|
||||
bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) {
|
||||
// Decoded segmented payload starts at byte 22.
|
||||
size_t start = 22;
|
||||
bool found_kgh = false;
|
||||
@@ -142,7 +142,7 @@ static bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const
|
||||
return true;
|
||||
}
|
||||
|
||||
static int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb) {
|
||||
int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb) {
|
||||
PrintAndLogEx(SUCCESS, "%s", verb);
|
||||
|
||||
// fast push mode
|
||||
@@ -183,21 +183,68 @@ static int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "Failed writing tag");
|
||||
PrintAndLogEx(WARNING, "Failed writing tag at offset %zu len %zu. Status: %d", i, len, resp.status);
|
||||
g_conn.block_after_ACK = false;
|
||||
return PM3_ERFTRANS;
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "Wrote chunk [offset %zu | len %zu | total %zu", i, len, i + len);
|
||||
PrintAndLogEx(SUCCESS, "Wrote chunk [offset %zu | len %zu | end %zu of %zu]", i, len, i + len, bytes_read);
|
||||
}
|
||||
|
||||
g_conn.block_after_ACK = false;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) {
|
||||
int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) {
|
||||
return legic_write_bytes_to_tag(7, 0x55, dump, bytes_read, "Restoring to card");
|
||||
}
|
||||
|
||||
int legic_migrate_dump(uint8_t *dump, size_t bytes_read, bool rewrite_kgh, const uint8_t dcf[2], bool allow_dcf) {
|
||||
if (dump == NULL || bytes_read <= 22) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (dcf != NULL && !allow_dcf) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
legic_card_select_t card;
|
||||
if (legic_get_type(&card) != PM3_SUCCESS) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (card.cardsize < bytes_read) {
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
uint8_t target_uid[4] = {0};
|
||||
memcpy(target_uid, card.uid, sizeof(target_uid));
|
||||
uint8_t target_mcc = (uint8_t)CRC8Legic(target_uid, sizeof(target_uid));
|
||||
|
||||
if (!legic_clone_update_segment_crcs(dump, bytes_read, target_uid)) {
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
if (rewrite_kgh && !legic_clone_update_kgh_crcs(dump, bytes_read, target_uid)) {
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
|
||||
memcpy(dump, target_uid, sizeof(target_uid));
|
||||
dump[4] = target_mcc;
|
||||
legic_xor_with_crc(dump, bytes_read, dump[4]);
|
||||
|
||||
int write_res = legic_write_dump_to_tag(dump, bytes_read);
|
||||
if (write_res != PM3_SUCCESS) {
|
||||
return write_res;
|
||||
}
|
||||
|
||||
if (dcf != NULL) {
|
||||
uint8_t dcf_dump[7] = {0};
|
||||
memcpy(dcf_dump + 5, dcf, 2);
|
||||
return legic_write_bytes_to_tag(5, 0x55, dcf_dump, sizeof(dcf_dump), "Applying explicit DCF update");
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdLegicMigrate(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf legic migrate",
|
||||
|
||||
@@ -31,5 +31,11 @@ int legic_get_type(legic_card_select_t *card);
|
||||
void legic_chk_iv(uint32_t *iv);
|
||||
void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes);
|
||||
int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uint16_t *outlen);
|
||||
int legic_migrate_dump(uint8_t *dump, size_t bytes_read, bool rewrite_kgh, const uint8_t dcf[2], bool allow_dcf);
|
||||
void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc);
|
||||
bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]);
|
||||
bool legic_clone_update_kgh_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]);
|
||||
int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb);
|
||||
int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read);
|
||||
|
||||
#endif
|
||||
|
||||
+376
-151
File diff suppressed because it is too large
Load Diff
+369
-2
@@ -68,8 +68,9 @@
|
||||
#define MIFAREU3P_KEY_SIZE 16
|
||||
#define MIFAREULC_KEY_INDEX 3
|
||||
|
||||
// NDEF data area starts at block 4 and READ takes a one byte block number,
|
||||
// so block 255 is the last one reachable.
|
||||
// The Capability Container sits in block 3, the NDEF data area starts at block 4
|
||||
// and READ takes a one byte block number, so block 255 is the last one reachable.
|
||||
#define MFU_NDEF_CC_BLOCK 3
|
||||
#define MFU_NDEF_FIRST_BLOCK 4
|
||||
#define MFU_NDEF_MAX_BYTES ((0xFF - MFU_NDEF_FIRST_BLOCK + 1) * MFU_BLOCK_SIZE)
|
||||
|
||||
@@ -7851,6 +7852,371 @@ int CmdHF14MfuNDEFWrite(const char *Cmd) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// NDEF formatting - restores the NXP factory delivery content (Capability
|
||||
// Container + empty NDEF message) for the detected tag type. Block 3 is OTP.
|
||||
// Per-type sources and rationale: doc/mfu_ndef_format_notes.md
|
||||
typedef struct {
|
||||
uint64_t tagtype;
|
||||
const char *name;
|
||||
uint8_t page[3][MFU_BLOCK_SIZE]; // pages 03h, 04h, 05h
|
||||
} mfu_ndef_format_t;
|
||||
|
||||
static const mfu_ndef_format_t mfu_ndef_format_table[] = {
|
||||
{ MFU_TT_UL, "MIFARE Ultralight", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_UL_C, "MIFARE Ultralight C", {{0xE1, 0x10, 0x12, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_UL_EV1_48, "MIFARE Ultralight EV1 48", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_UL_EV1_128, "MIFARE Ultralight EV1 128", {{0xE1, 0x10, 0x10, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_NTAG_203, "NTAG203", {{0xE1, 0x10, 0x12, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_NTAG_210, "NTAG210", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_NTAG_210u, "NTAG210u", {{0xE1, 0x10, 0x06, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_NTAG_212, "NTAG212", {{0xE1, 0x10, 0x10, 0x00}, {0x01, 0x03, 0x90, 0x0A}, {0x34, 0x03, 0x00, 0xFE}} },
|
||||
{ MFU_TT_NTAG_213, "NTAG213", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} },
|
||||
{ MFU_TT_NTAG_213_F, "NTAG213F", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} },
|
||||
{ MFU_TT_NTAG_213_TT, "NTAG213TT", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} },
|
||||
{ MFU_TT_NTAG_213_C, "NTAG213C", {{0xE1, 0x10, 0x12, 0x00}, {0x01, 0x03, 0xA0, 0x0C}, {0x34, 0x03, 0x00, 0xFE}} },
|
||||
{ MFU_TT_NTAG_215, "NTAG215", {{0xE1, 0x10, 0x3E, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_NTAG_216, "NTAG216", {{0xE1, 0x10, 0x6D, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
{ MFU_TT_NTAG_216_F, "NTAG216F", {{0xE1, 0x10, 0x6D, 0x00}, {0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}} },
|
||||
};
|
||||
|
||||
static const mfu_ndef_format_t *mfu_get_ndef_format(uint64_t tagtype) {
|
||||
for (size_t i = 0; i < ARRAYLEN(mfu_ndef_format_table); i++) {
|
||||
uint64_t tt = mfu_ndef_format_table[i].tagtype;
|
||||
if ((tagtype & tt) == tt) {
|
||||
return &mfu_ndef_format_table[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int CmdHF14MfuNDEFFormat(const char *Cmd) {
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mfu ndefformat",
|
||||
"Format a MIFARE Ultralight / NTAG tag for NDEF by writing the Capability\n"
|
||||
"Container to block 3, followed by an empty NDEF message.\n"
|
||||
"\n"
|
||||
"Writes NXP factory delivery content for the detected tag type. Block 3 is\n"
|
||||
"One Time Programmable; unknown types and unreachable CCs are refused.\n"
|
||||
"\n"
|
||||
"Note: the tag is re-selected and re-authenticated for each block written.",
|
||||
"hf mfu ndefformat\n"
|
||||
"hf mfu ndefformat -v\n"
|
||||
"hf mfu ndefformat --erase\n"
|
||||
"hf mfu ndefformat -k FFFFFFFF\n"
|
||||
"hf mfu ndefformat -k 49454D4B41455242214E4143554F5946\n"
|
||||
"hf mfu ndefformat -d E1101200 --force"
|
||||
);
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("k", "key", "<hex>", "Authentication key (UL-C/UL-AES 16 bytes, EV1/NTAG 4 bytes)"),
|
||||
arg_lit0("l", NULL, "Swap entered key endianness"),
|
||||
arg_str0("d", "data", "<hex>", "Capability Container to write, 4 bytes. Overrides the detected type"),
|
||||
arg_lit0(NULL, "erase", "Also zero the rest of the NDEF data area"),
|
||||
arg_lit0(NULL, "force", "Continue on an unknown tag type, or with an oversized -d value"),
|
||||
arg_lit0("v", "verbose", "Verbose output"),
|
||||
arg_lit0(NULL, "schann", "use secure channel. Must have key"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
int ak_len = 0;
|
||||
uint8_t authenticationkey[16] = {0x00};
|
||||
CLIGetHexWithReturn(ctx, 1, authenticationkey, &ak_len);
|
||||
bool swap_endian = arg_get_lit(ctx, 2);
|
||||
|
||||
int cc_len = 0;
|
||||
uint8_t cc_override[MFU_BLOCK_SIZE] = {0x00};
|
||||
CLIGetHexWithReturn(ctx, 3, cc_override, &cc_len);
|
||||
|
||||
bool erase = arg_get_lit(ctx, 4);
|
||||
bool force = arg_get_lit(ctx, 5);
|
||||
bool verbose = arg_get_lit(ctx, 6);
|
||||
bool use_schann = arg_get_lit(ctx, 7);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if ((cc_len != 0) && (cc_len != MFU_BLOCK_SIZE)) {
|
||||
PrintAndLogEx(WARNING, "Capability Container must be %d bytes, got %d", MFU_BLOCK_SIZE, cc_len);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
bool has_auth_key = false;
|
||||
bool has_pwd = false;
|
||||
switch (ak_len) {
|
||||
case 0:
|
||||
break;
|
||||
case 4:
|
||||
has_pwd = true;
|
||||
break;
|
||||
case 16:
|
||||
has_auth_key = true;
|
||||
break;
|
||||
default:
|
||||
PrintAndLogEx(WARNING, "ERROR: Key is incorrect length\n");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (use_schann && (has_auth_key == false)) {
|
||||
PrintAndLogEx(WARNING, "Secure channel must be called with key");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint64_t tagtype = GetHF14AMfU_Type();
|
||||
if (tagtype == MFU_TT_UL_ERROR) {
|
||||
PrintAndLogEx(WARNING, "No Ultralight / NTAG based tag found");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
ul_print_type(tagtype, 0);
|
||||
|
||||
const mfu_ndef_format_t *fmt = mfu_get_ndef_format(tagtype);
|
||||
if (fmt == NULL) {
|
||||
if (cc_len == 0) {
|
||||
PrintAndLogEx(FAILED, "Don't know the Capability Container for this tag type");
|
||||
PrintAndLogEx(INFO, "Block 3 is One Time Programmable, a wrong value can not be undone,");
|
||||
PrintAndLogEx(INFO, "so this command will not guess one.");
|
||||
PrintAndLogEx(HINT, "Hint: supply it yourself with `" _YELLOW_("hf mfu ndefformat -d <hex> --force") "`");
|
||||
return PM3_ENOTIMPL;
|
||||
}
|
||||
if (force == false) {
|
||||
PrintAndLogEx(FAILED, "Unknown tag type, add `" _YELLOW_("--force") "` to write anyway");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
if (erase) {
|
||||
PrintAndLogEx(FAILED, "Refusing to erase on an unknown tag type");
|
||||
PrintAndLogEx(INFO, "The end of the data area can not be established, so the erase");
|
||||
PrintAndLogEx(INFO, "could run into the lock bytes, configuration or key pages.");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
// pages 03h, 04h and 05h as they will be written
|
||||
uint8_t pages[3][MFU_BLOCK_SIZE] = {{0}};
|
||||
if (fmt != NULL) {
|
||||
memcpy(pages, fmt->page, sizeof(pages));
|
||||
} else {
|
||||
// unknown type, -d plus --force: user supplied CC and an empty NDEF message
|
||||
const uint8_t empty[2][MFU_BLOCK_SIZE] = {{0x03, 0x00, 0xFE, 0x00}, {0x00, 0x00, 0x00, 0x00}};
|
||||
memcpy(pages[1], empty[0], MFU_BLOCK_SIZE);
|
||||
memcpy(pages[2], empty[1], MFU_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (cc_len == MFU_BLOCK_SIZE) {
|
||||
|
||||
// -d must not announce more memory than this tag type actually has
|
||||
if ((fmt != NULL) && (cc_override[2] > fmt->page[0][2]) && (force == false)) {
|
||||
PrintAndLogEx(FAILED, "Capability Container announces more memory than this tag has");
|
||||
PrintAndLogEx(INFO, " requested... %d bytes ( MLEN %02X )", cc_override[2] * 8, cc_override[2]);
|
||||
PrintAndLogEx(INFO, " tag holds... %d bytes ( MLEN %02X )", fmt->page[0][2] * 8, fmt->page[0][2]);
|
||||
PrintAndLogEx(INFO, "Block 3 is One Time Programmable, this can not be undone.");
|
||||
PrintAndLogEx(HINT, "Hint: add `" _YELLOW_("--force") "` if you really mean it");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
memcpy(pages[0], cc_override, MFU_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
PrintAndLogEx(INFO, "Tag type... " _YELLOW_("%s"), (fmt != NULL) ? fmt->name : "unknown");
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
PrintAndLogEx(INFO, "Block %2u... %s"
|
||||
, MFU_NDEF_CC_BLOCK + i
|
||||
, sprint_hex_inrow(pages[i], MFU_BLOCK_SIZE)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *auth_key_ptr = authenticationkey;
|
||||
if (swap_endian) {
|
||||
if (ak_len == 16) {
|
||||
if ((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C) {
|
||||
auth_key_ptr = SwapEndian64(authenticationkey, ak_len, 8);
|
||||
} else if ((tagtype & MFU_TT_UL_AES) == MFU_TT_UL_AES) {
|
||||
auth_key_ptr = SwapEndian64(authenticationkey, ak_len, 16);
|
||||
}
|
||||
} else if (ak_len == 4) {
|
||||
auth_key_ptr = SwapEndian64(authenticationkey, ak_len, 4);
|
||||
}
|
||||
}
|
||||
|
||||
iso14a_card_select_t card;
|
||||
uint8_t pack[4] = {0, 0, 0, 0};
|
||||
if (ul_auth_select(&card, tagtype, (has_auth_key || has_pwd), auth_key_ptr, pack, sizeof(pack), use_schann) == PM3_ESOFT) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// read blocks 0..3 so the current Capability Container can be inspected
|
||||
uint8_t data[16] = {0x00};
|
||||
int status = ul_read(0, data, sizeof(data), use_schann);
|
||||
DropField();
|
||||
|
||||
if (status <= 0) {
|
||||
PrintAndLogEx(ERR, "Error: tag didnt answer to READ");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (status != 16) {
|
||||
PrintAndLogEx(ERR, "Error: tag returned %d bytes, need 16 to read the NDEF Container", status);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// block 3 is OTP: refuse a target the current content can't reach via OR
|
||||
uint8_t *cur = data + (MFU_NDEF_CC_BLOCK * MFU_BLOCK_SIZE);
|
||||
bool blank = true;
|
||||
bool reachable = true;
|
||||
for (uint8_t i = 0; i < MFU_BLOCK_SIZE; i++) {
|
||||
if (cur[i] != 0x00) {
|
||||
blank = false;
|
||||
}
|
||||
if ((cur[i] | pages[0][i]) != pages[0][i]) {
|
||||
reachable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachable == false) {
|
||||
PrintAndLogEx(FAILED, "Capability Container can not be written on this tag");
|
||||
PrintAndLogEx(INFO, " on tag now... " _RED_("%s"), sprint_hex_inrow(cur, MFU_BLOCK_SIZE));
|
||||
PrintAndLogEx(INFO, " wanted....... " _GREEN_("%s"), sprint_hex_inrow(pages[0], MFU_BLOCK_SIZE));
|
||||
PrintAndLogEx(INFO, "Block 3 is One Time Programmable. A write is OR'ed with the current");
|
||||
PrintAndLogEx(INFO, "content, so a bit that is already 1 can not be cleared again.");
|
||||
ndef_print_CC(cur);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (blank == false) {
|
||||
PrintAndLogEx(WARNING, "Tag already carries a Capability Container ( " _YELLOW_("%s") " )"
|
||||
, sprint_hex_inrow(cur, MFU_BLOCK_SIZE)
|
||||
);
|
||||
if (verbose) {
|
||||
ndef_print_CC(cur);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t keytype = 0;
|
||||
if (has_auth_key || has_pwd) {
|
||||
if ((tagtype & MFU_TT_UL_C) == MFU_TT_UL_C) {
|
||||
keytype = 1; // UL_C auth
|
||||
} else if ((tagtype & MFU_TT_UL_AES) == MFU_TT_UL_AES) {
|
||||
keytype = 3; // UL_AES auth
|
||||
} else {
|
||||
keytype = 2; // UL_EV1/NTAG auth
|
||||
}
|
||||
}
|
||||
|
||||
// erase range from the table MLEN, never from -d - can't run past user memory
|
||||
uint16_t last_block = MFU_NDEF_CC_BLOCK + 2;
|
||||
if (erase && (fmt != NULL)) {
|
||||
last_block = (uint16_t)(MFU_NDEF_CC_BLOCK + (fmt->page[0][2] * 2));
|
||||
}
|
||||
|
||||
// block 255 is the last one WRITE can reach with its single address byte
|
||||
if (last_block > 0xFF) {
|
||||
PrintAndLogEx(INFO, "Data area runs past block 255, stopping at the last addressable block");
|
||||
last_block = 0xFF;
|
||||
}
|
||||
|
||||
uint16_t total = (uint16_t)(last_block - MFU_NDEF_CC_BLOCK + 1);
|
||||
|
||||
PrintAndLogEx(INFO, "Writing " _YELLOW_("%u") " blocks from block " _YELLOW_("%d"), total, MFU_NDEF_CC_BLOCK);
|
||||
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to abort");
|
||||
|
||||
uint16_t done = 0;
|
||||
const uint8_t zeros[MFU_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
for (uint16_t blockno = MFU_NDEF_CC_BLOCK; blockno <= last_block; blockno++) {
|
||||
|
||||
if (kbd_enter_pressed()) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(WARNING, "aborted via keyboard!");
|
||||
if (done > 0) {
|
||||
PrintAndLogEx(WARNING, "tag holds a partially written NDEF data area, blocks %d..%u"
|
||||
, MFU_NDEF_CC_BLOCK
|
||||
, blockno - 1
|
||||
);
|
||||
}
|
||||
return PM3_EOPABORTED;
|
||||
}
|
||||
|
||||
const uint8_t *src = zeros;
|
||||
if (blockno < MFU_NDEF_CC_BLOCK + 3) {
|
||||
src = pages[blockno - MFU_NDEF_CC_BLOCK];
|
||||
}
|
||||
|
||||
int res = mfu_write_block(src, MFU_BLOCK_SIZE, keytype, auth_key_ptr, (uint8_t)blockno, use_schann);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
||||
// a protected tag simply stops answering, so the write times out
|
||||
// rather than coming back with an explicit error
|
||||
if (res == PM3_ETIMEOUT) {
|
||||
PrintAndLogEx(FAILED, "Write block %u ( " _RED_("timeout") " )", blockno);
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "Write block %u ( " _RED_("fail") " )", blockno);
|
||||
}
|
||||
PrintAndLogEx(HINT, "Hint: Check password / key!");
|
||||
|
||||
if (done > 0) {
|
||||
PrintAndLogEx(WARNING, "tag holds a partially written NDEF data area, blocks %d..%u"
|
||||
, MFU_NDEF_CC_BLOCK
|
||||
, blockno - 1
|
||||
);
|
||||
}
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
done++;
|
||||
PrintAndLogEx(INPLACE, "Block %u / %u", done, total);
|
||||
}
|
||||
|
||||
DropField();
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
|
||||
// read the formatted blocks back rather than trust the write status alone
|
||||
if (ul_auth_select(&card, tagtype, (has_auth_key || has_pwd), auth_key_ptr, pack, sizeof(pack), use_schann) == PM3_ESOFT) {
|
||||
PrintAndLogEx(WARNING, "Wrote the tag but could not re-select it to verify");
|
||||
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu ndefread") "` to check it yourself");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint8_t verify[16] = {0x00};
|
||||
status = ul_read(MFU_NDEF_CC_BLOCK, verify, sizeof(verify), use_schann);
|
||||
DropField();
|
||||
|
||||
if (status != 16) {
|
||||
PrintAndLogEx(WARNING, "Wrote the tag but could not read it back to verify");
|
||||
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu ndefread") "` to check it yourself");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
// the read started at block 3, so the three formatted blocks are at offset 0
|
||||
if (memcmp(verify, pages[0], MFU_BLOCK_SIZE) != 0) {
|
||||
PrintAndLogEx(FAILED, "Capability Container did not take");
|
||||
PrintAndLogEx(INFO, " wanted...... " _GREEN_("%s"), sprint_hex_inrow(pages[0], MFU_BLOCK_SIZE));
|
||||
PrintAndLogEx(INFO, " on tag now.. " _RED_("%s"), sprint_hex_inrow(verify, MFU_BLOCK_SIZE));
|
||||
PrintAndLogEx(HINT, "Hint: block 3 is OTP, check whether its block locking bit is set");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (memcmp(verify + MFU_BLOCK_SIZE, pages[1], 2 * MFU_BLOCK_SIZE) != 0) {
|
||||
PrintAndLogEx(FAILED, "Capability Container is correct but the empty NDEF message is not");
|
||||
// one sprint_hex_inrow() call per line: it returns a shared static buffer
|
||||
PrintAndLogEx(INFO, " wanted...... " _GREEN_("%s"), sprint_hex_inrow(pages[1], 2 * MFU_BLOCK_SIZE));
|
||||
PrintAndLogEx(INFO, " on tag now.. " _RED_("%s"), sprint_hex_inrow(verify + MFU_BLOCK_SIZE, 2 * MFU_BLOCK_SIZE));
|
||||
PrintAndLogEx(HINT, "Hint: these blocks are ordinary user memory, check the lock bytes");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
PrintAndLogEx(INFO, "Verified blocks %d..%d against the tag", MFU_NDEF_CC_BLOCK, MFU_NDEF_CC_BLOCK + 2);
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Format ( " _GREEN_("ok") " )");
|
||||
PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf mfu ndefwrite") "` to write a NDEF message");
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
// utility function. Retrieves emulator memory
|
||||
static int GetMfuDumpFromEMul(mfu_dump_t **buf) {
|
||||
|
||||
@@ -8722,6 +9088,7 @@ static command_t CommandTable[] = {
|
||||
{"dump", CmdHF14AMfUDump, IfPm3Iso14443a, "Dump MIFARE Ultralight family tag to binary file"},
|
||||
{"incr", CmdHF14AMfUIncr, IfPm3Iso14443a, "Increments Ev1/NTAG counter"},
|
||||
{"info", CmdHF14AMfUInfo, IfPm3Iso14443a, "Tag information"},
|
||||
{"ndefformat", CmdHF14MfuNDEFFormat, IfPm3Iso14443a, "Format tag as NDEF, writes the Capability Container"},
|
||||
{"ndefread", CmdHF14MfuNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"},
|
||||
{"ndefwrite", CmdHF14MfuNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"},
|
||||
{"rdbl", CmdHF14AMfURdBl, IfPm3Iso14443a, "Read block"},
|
||||
|
||||
@@ -59,6 +59,7 @@ int mfuc_test_authentication_support(void);
|
||||
int CmdHFMFUltra(const char *Cmd);
|
||||
int CmdHF14MfuNDEFRead(const char *Cmd); // used by cmdnfc.c
|
||||
int CmdHF14MfuNDEFWrite(const char *Cmd); // used by cmdnfc.c
|
||||
int CmdHF14MfuNDEFFormat(const char *Cmd); // used by cmdnfc.c
|
||||
int CmdHF14MfUTamper(const char *Cmd);
|
||||
|
||||
#define MFU_TT_UNKNOWN 0x0ULL
|
||||
|
||||
+378
-4
@@ -26,6 +26,7 @@
|
||||
#include "cliparser.h"
|
||||
#include "crc16.h"
|
||||
#include "cmdhf14a.h"
|
||||
#include "pm3_cmd.h"
|
||||
#include "protocols.h" // definitions of ISO14A/7816 protocol
|
||||
#include "iso7816/apduinfo.h" // GetAPDUCodeDescription
|
||||
#include "nfc/ndef.h" // NDEFRecordsDecodeAndPrint
|
||||
@@ -34,6 +35,8 @@
|
||||
#include "protocols.h" // ISO7816 APDU return codes
|
||||
#include "crypto/libpcrypto.h" // ecdsa
|
||||
#include "crypto/originality.h"
|
||||
#include "mifare/mifarehost.h" // mf_eml_set_mem_xt / mf_eml_get_mem_xt
|
||||
#include "ui.h"
|
||||
|
||||
#define TIMEOUT 2000
|
||||
|
||||
@@ -335,29 +338,397 @@ static int CmdHFST25TAInfo(const char *Cmd) {
|
||||
return infoHFST25TA();
|
||||
}
|
||||
|
||||
static int st25ta_ndef_records_len(const uint8_t *data, size_t data_len, size_t *records_len) {
|
||||
if (data == NULL || records_len == NULL || data_len < 3) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
for (;;) {
|
||||
if (offset + 3 > data_len) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint8_t flags = data[offset];
|
||||
uint8_t type_len = data[offset + 1];
|
||||
bool short_record = (flags & 0x10) != 0;
|
||||
bool id_length_present = (flags & 0x08) != 0;
|
||||
bool message_end = (flags & 0x40) != 0;
|
||||
size_t header_len = short_record ? 3 : 6;
|
||||
uint32_t payload_len = 0;
|
||||
|
||||
if (short_record) {
|
||||
payload_len = data[offset + 2];
|
||||
} else {
|
||||
if (offset + 6 > data_len) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
payload_len = (data[offset + 2] << 24) |
|
||||
(data[offset + 3] << 16) |
|
||||
(data[offset + 4] << 8) |
|
||||
data[offset + 5];
|
||||
}
|
||||
|
||||
uint8_t id_len = 0;
|
||||
if (id_length_present) {
|
||||
if (offset + header_len >= data_len) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
id_len = data[offset + header_len];
|
||||
header_len++;
|
||||
}
|
||||
|
||||
size_t record_len = header_len + type_len + id_len + payload_len;
|
||||
if (record_len == 0 || offset + record_len > data_len) {
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
offset += record_len;
|
||||
if (message_end) {
|
||||
*records_len = offset;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int st25ta_normalize_sim_ndef(const uint8_t *src, size_t src_len, uint8_t *dst, uint16_t *dst_len) {
|
||||
if (src == NULL || src_len == 0 || dst == NULL || dst_len == NULL) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (src_len >= 4 && src[src_len - 2] == 0x90 && src[src_len - 1] == 0x00) {
|
||||
uint16_t nlen = (src[0] << 8) | src[1];
|
||||
if (nlen + 4 != src_len) {
|
||||
nlen = src_len - 4;
|
||||
}
|
||||
|
||||
if (src_len > ST25TA_EML_NDEF_MAX) {
|
||||
PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_EML_NDEF_MAX);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
dst[0] = (nlen >> 8) & 0xff;
|
||||
dst[1] = nlen & 0xff;
|
||||
memcpy(dst + 2, src + 2, src_len - 4);
|
||||
dst[src_len - 2] = 0x90;
|
||||
dst[src_len - 1] = 0x00;
|
||||
*dst_len = src_len;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (src_len >= 2) {
|
||||
uint16_t nlen = (src[0] << 8) | src[1];
|
||||
if (nlen + 2 == src_len) {
|
||||
if (src_len + 2 > ST25TA_EML_NDEF_MAX) {
|
||||
PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_EML_NDEF_MAX);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
dst[0] = (nlen >> 8) & 0xff;
|
||||
dst[1] = nlen & 0xff;
|
||||
memcpy(dst + 2, src + 2, nlen);
|
||||
dst[src_len] = 0x90;
|
||||
dst[src_len + 1] = 0x00;
|
||||
*dst_len = src_len + 2;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if (src_len + 4 > ST25TA_EML_NDEF_MAX) {
|
||||
PrintAndLogEx(ERR, "NDEF response too large. Max %u bytes", ST25TA_EML_NDEF_MAX);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
dst[0] = (src_len >> 8) & 0xff;
|
||||
dst[1] = src_len & 0xff;
|
||||
memcpy(dst + 2, src, src_len);
|
||||
dst[src_len + 2] = 0x90;
|
||||
dst[src_len + 3] = 0x00;
|
||||
*dst_len = src_len + 4;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int st25ta_upload_sim_data(const uint8_t *uid, uint8_t uid_len, const uint8_t *ndef, uint16_t ndef_len) {
|
||||
uint8_t data[ST25TA_EML_DATA_OFFSET + ST25TA_EML_NDEF_MAX] = {0};
|
||||
uint16_t data_len = ST25TA_EML_DATA_OFFSET + ndef_len;
|
||||
memcpy(data + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4);
|
||||
data[ST25TA_EML_LEN_OFFSET] = ndef_len & 0xff;
|
||||
data[ST25TA_EML_LEN_OFFSET + 1] = (ndef_len >> 8) & 0xff;
|
||||
data[ST25TA_EML_UIDLEN_OFFSET] = uid_len;
|
||||
if (uid_len == 7) {
|
||||
memcpy(data + ST25TA_EML_UID_OFFSET, uid, uid_len);
|
||||
}
|
||||
memcpy(data + ST25TA_EML_DATA_OFFSET, ndef, ndef_len);
|
||||
|
||||
for (uint16_t offset = 0; offset < data_len;) {
|
||||
uint8_t chunk_len = MIN(data_len - offset, UINT8_MAX);
|
||||
int res = mf_eml_set_mem_xt(data + offset, offset, chunk_len, 1);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
offset += chunk_len;
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int st25ta_download_sim_data(uint8_t *uid, uint8_t *uid_len, uint8_t *ndef, uint16_t *ndef_len) {
|
||||
uint8_t header[ST25TA_EML_DATA_OFFSET] = {0};
|
||||
int res = mf_eml_get_mem_xt(header, 0, sizeof(header), 1);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (memcmp(header + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4) != 0) {
|
||||
PrintAndLogEx(ERR, "No ST25TA data loaded in emulator memory");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint16_t len = header[ST25TA_EML_LEN_OFFSET] |
|
||||
(header[ST25TA_EML_LEN_OFFSET + 1] << 8);
|
||||
if (len == 0 || len > ST25TA_EML_NDEF_MAX) {
|
||||
PrintAndLogEx(ERR, "Invalid ST25TA NDEF response length in emulator memory: %u", len);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (uid_len != NULL) {
|
||||
*uid_len = header[ST25TA_EML_UIDLEN_OFFSET];
|
||||
}
|
||||
if (uid != NULL && header[ST25TA_EML_UIDLEN_OFFSET] == 7) {
|
||||
memcpy(uid, header + ST25TA_EML_UID_OFFSET, 7);
|
||||
}
|
||||
|
||||
for (uint16_t offset = 0; offset < len;) {
|
||||
uint8_t chunk_len = MIN(len - offset, UINT8_MAX);
|
||||
res = mf_eml_get_mem_xt(ndef + offset, ST25TA_EML_DATA_OFFSET + offset, chunk_len, 1);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
offset += chunk_len;
|
||||
}
|
||||
|
||||
*ndef_len = len;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHFST25TAELoad(const char *Cmd) {
|
||||
int uidlen = 0;
|
||||
uint8_t uid[7] = {0};
|
||||
int ndef_hex_len = 0;
|
||||
uint8_t ndef_hex[ST25TA_EML_NDEF_MAX] = {0};
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf st25ta eload",
|
||||
"Load ST25TA NDEF response into emulator memory",
|
||||
"hf st25ta eload --ndef 001BD1011754027A68A234CBD0E203C73E620BE8C63C852CC5313131329000\n"
|
||||
"hf st25ta eload -f my-ndef.bin\n"
|
||||
"hf st25ta eload -u 02E2007D0FCA4C -f my-ndef.bin\n");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("u", "uid", "<hex>", "optional 7 byte UID to store for simulation"),
|
||||
arg_str0(NULL, "ndef", "<hex>", "NDEF data or full READ BINARY response"),
|
||||
arg_str0("f", "file", "<fn>", "load NDEF data or full READ BINARY response from file"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
CLIGetHexWithReturn(ctx, 1, uid, &uidlen);
|
||||
int ndef_res = CLIParamHexToBuf(arg_get_str(ctx, 2), ndef_hex, sizeof(ndef_hex), &ndef_hex_len);
|
||||
int fnlen = 0;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (uidlen != 0 && uidlen != 7) {
|
||||
PrintAndLogEx(ERR, "UID must be 7 hex bytes");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (ndef_res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Invalid NDEF hex");
|
||||
return ndef_res;
|
||||
}
|
||||
|
||||
if ((ndef_hex_len > 0) == (fnlen > 0)) {
|
||||
PrintAndLogEx(ERR, "Use exactly one of --ndef or -f");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t ndef[ST25TA_EML_NDEF_MAX] = {0};
|
||||
uint16_t ndef_len = 0;
|
||||
if (ndef_hex_len > 0) {
|
||||
int res = st25ta_normalize_sim_ndef(ndef_hex, ndef_hex_len, ndef, &ndef_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
uint8_t *file_data = NULL;
|
||||
size_t file_len = 0;
|
||||
int res = loadFile_safe(filename, "", (void **)&file_data, &file_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
res = st25ta_normalize_sim_ndef(file_data, file_len, ndef, &ndef_len);
|
||||
free(file_data);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
int res = st25ta_upload_sim_data(uid, uidlen, ndef, ndef_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Failed uploading ST25TA data to emulator memory");
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Uploaded ST25TA NDEF response to emulator memory (" _YELLOW_("%u") " bytes)", ndef_len);
|
||||
if (uidlen == 7) {
|
||||
PrintAndLogEx(SUCCESS, "Stored UID " _YELLOW_("%s"), sprint_hex_inrow(uid, uidlen));
|
||||
}
|
||||
PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf st25ta sim`"));
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHFST25TAEView(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf st25ta eview",
|
||||
"Display ST25TA emulator memory",
|
||||
"hf st25ta eview\n"
|
||||
"hf st25ta eview -v\n");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_lit0("v", "verbose", "verbose NDEF output"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
bool verbose = arg_get_lit(ctx, 1);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
uint8_t uid[7] = {0};
|
||||
uint8_t uid_len = 0;
|
||||
uint8_t ndef[ST25TA_EML_NDEF_MAX] = {0};
|
||||
uint16_t ndef_len = 0;
|
||||
int res = st25ta_download_sim_data(uid, &uid_len, ndef, &ndef_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("ST25TA emulator memory") " ----------------");
|
||||
if (uid_len == 7) {
|
||||
PrintAndLogEx(INFO, "UID................ " _YELLOW_("%s"), sprint_hex_inrow(uid, uid_len));
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "UID................ " _YELLOW_("not loaded"));
|
||||
}
|
||||
PrintAndLogEx(INFO, "NDEF response len.. " _YELLOW_("%u") " bytes", ndef_len);
|
||||
PrintAndLogEx(INFO, "NDEF response raw:");
|
||||
print_buffer(ndef, ndef_len, 1);
|
||||
|
||||
if (ndef_len >= 4 && ndef[ndef_len - 2] == 0x90 && ndef[ndef_len - 1] == 0x00) {
|
||||
uint16_t nlen = (ndef[0] << 8) | ndef[1];
|
||||
size_t records_len = ndef_len - 4;
|
||||
if (nlen <= records_len) {
|
||||
records_len = nlen;
|
||||
}
|
||||
|
||||
size_t calculated_len = 0;
|
||||
if (st25ta_ndef_records_len(ndef + 2, records_len, &calculated_len) == PM3_SUCCESS &&
|
||||
calculated_len < records_len) {
|
||||
records_len = calculated_len;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "--- " _CYAN_("NDEF records") " --------------------------");
|
||||
NDEFRecordsDecodeAndPrint(ndef + 2, records_len, verbose);
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHFST25TAESave(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf st25ta esave",
|
||||
"Save ST25TA emulator memory NDEF response to file",
|
||||
"hf st25ta esave\n"
|
||||
"hf st25ta esave -f hf-st25ta-ndef\n");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("f", "file", "<fn>", "Specify a filename for dump file"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
int fnlen = 0;
|
||||
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (fnlen == 0) {
|
||||
snprintf(filename, sizeof(filename), "hf-st25ta-ndef");
|
||||
}
|
||||
|
||||
uint8_t ndef[ST25TA_EML_NDEF_MAX] = {0};
|
||||
uint16_t ndef_len = 0;
|
||||
int res = st25ta_download_sim_data(NULL, NULL, ndef, &ndef_len);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
|
||||
if (ndef_len < 4 || ndef[ndef_len - 2] != 0x90 || ndef[ndef_len - 1] != 0x00) {
|
||||
PrintAndLogEx(ERR, "Loaded ST25TA data is not a READ BINARY response ending in 9000");
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
uint16_t nlen = (ndef[0] << 8) | ndef[1];
|
||||
if (nlen == 0 || nlen + 4 > ndef_len) {
|
||||
PrintAndLogEx(ERR, "Invalid ST25TA NLEN in emulator memory: %u", nlen);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
return pm3_save_dump(filename, ndef + 2, nlen, jsfNDEF);
|
||||
}
|
||||
|
||||
static int CmdHFST25TASim(const char *Cmd) {
|
||||
int uidlen = 0;
|
||||
uint8_t uid[7] = {0};
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf st25ta sim",
|
||||
"Emulating ST25TA512B tag with 7 byte UID",
|
||||
"hf st25ta sim -u 02E2007D0FCA4C\n");
|
||||
"Emulating ST25TA512B tag. Uses NDEF data from `hf st25ta eload` if loaded.",
|
||||
"hf st25ta sim -u 02E2007D0FCA4C\n"
|
||||
"hf st25ta sim\n");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("u", "uid", "<hex>", "7 byte UID"),
|
||||
arg_str0("u", "uid", "<hex>", "7 byte UID"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
CLIGetHexWithReturn(ctx, 1, uid, &uidlen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (uidlen != 7) {
|
||||
if (uidlen != 0 && uidlen != 7) {
|
||||
PrintAndLogEx(ERR, "UID must be 7 hex bytes");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (uidlen == 0) {
|
||||
uint8_t header[ST25TA_EML_DATA_OFFSET] = {0};
|
||||
int res = mf_eml_get_mem_xt(header, 0, sizeof(header), 1);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
if (memcmp(header + ST25TA_EML_MAGIC_OFFSET, ST25TA_EML_MAGIC, 4) == 0 &&
|
||||
header[ST25TA_EML_UIDLEN_OFFSET] == 7) {
|
||||
memcpy(uid, header + ST25TA_EML_UID_OFFSET, sizeof(uid));
|
||||
uidlen = sizeof(uid);
|
||||
} else {
|
||||
PrintAndLogEx(ERR, "No UID supplied and no UID loaded. Use " _YELLOW_("`hf st25ta sim -u <uid>`") " or " _YELLOW_("`hf st25ta eload -u <uid> ...`"));
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
char param[40];
|
||||
snprintf(param, sizeof(param), "-t 10 -u %s", sprint_hex_inrow(uid, uidlen));
|
||||
return CmdHF14ASim(param);
|
||||
@@ -811,6 +1182,9 @@ static int CmdHFST25TAList(const char *Cmd) {
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, AlwaysAvailable, "This help"},
|
||||
{"eload", CmdHFST25TAELoad, IfPm3Iso14443a, "Upload NDEF response into emulator memory"},
|
||||
{"esave", CmdHFST25TAESave, IfPm3Iso14443a, "Save emulator memory to file"},
|
||||
{"eview", CmdHFST25TAEView, IfPm3Iso14443a, "View emulator memory"},
|
||||
{"info", CmdHFST25TAInfo, IfPm3Iso14443a, "Tag information"},
|
||||
{"list", CmdHFST25TAList, AlwaysAvailable, "List ISO 14443A/7816 history"},
|
||||
{"ndefread", CmdHFST25TANdefRead, AlwaysAvailable, "read NDEF file on tag"},
|
||||
|
||||
+81
-5
@@ -2267,19 +2267,95 @@ static int CmdPM5QCTest(const char *Cmd) {
|
||||
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hw qc_pm5", "QC Test for the PM5",
|
||||
"hw qc_pm5 -> run QC test with default 20 second timeout\n"
|
||||
"hw qc_pm5 -t 3 -> run QC test with a 3 second timeout");
|
||||
"hw qc_pm5 -> run hardware QC test with default 20 second timeout\n"
|
||||
"hw qc_pm5 -t 3 -> run hardware QC test with a 3 second timeout\n"
|
||||
"hw qc_pm5 --iolow <index> --pwd <hex> -> drive PM5 test IO <index> low\n"
|
||||
"hw qc_pm5 --iohigh <index> --pwd <hex> -> drive PM5 test IO <index> high\n"
|
||||
"hw qc_pm5 --ioreset <index> --pwd <hex> -> reset PM5 test IO <index> to default");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_u64_0("t", "timeout", "<s>", "test sequence timeout in seconds (default 20)"),
|
||||
arg_u64_0(NULL, "iolow", "<index>", "drive PM5 test IO <index> low"),
|
||||
arg_u64_0(NULL, "iohigh", "<index>", "drive PM5 test IO <index> high"),
|
||||
arg_u64_0(NULL, "ioreset", "<index>", "reset PM5 test IO <index> to default"),
|
||||
arg_str0(NULL, "pwd", "<hex>", "QC test password in hex (required for IO tests)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
uint32_t timeout_ms = arg_get_u32_def(ctx, 1, 20);
|
||||
timeout_ms *= 1000;
|
||||
|
||||
bool io_low = arg_get_u64_count(ctx, 2) > 0;
|
||||
bool io_high = arg_get_u64_count(ctx, 3) > 0;
|
||||
bool io_reset = arg_get_u64_count(ctx, 4) > 0;
|
||||
int io_count = (io_low ? 1 : 0) + (io_high ? 1 : 0) + (io_reset ? 1 : 0);
|
||||
|
||||
uint16_t io_index = 0;
|
||||
uint8_t io_status = 0;
|
||||
if (io_low) {
|
||||
io_index = (uint16_t)arg_get_u32(ctx, 2);
|
||||
io_status = 0;
|
||||
} else if (io_high) {
|
||||
io_index = (uint16_t)arg_get_u32(ctx, 3);
|
||||
io_status = 1;
|
||||
} else if (io_reset) {
|
||||
io_index = (uint16_t)arg_get_u32(ctx, 4);
|
||||
io_status = 2;
|
||||
}
|
||||
|
||||
uint8_t pwd[4];
|
||||
int dlen = 0;
|
||||
int res = CLIParamHexToBuf(arg_get_str(ctx, 5), pwd, sizeof(pwd), &dlen);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (io_count > 1) {
|
||||
PrintAndLogEx(ERR, "only one of --iolow, --iohigh, --ioreset may be specified");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
if (io_count == 1) {
|
||||
if (res || dlen != sizeof(pwd)) {
|
||||
PrintAndLogEx(ERR, "invalid password format, must be hex(8 chars)");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
PrintAndLogEx(WARNING, _RED_("!!! WARNING: PM5 IO QC test drives the test IO pins directly !!!"));
|
||||
PrintAndLogEx(WARNING, _RED_("!!! This can damage the device if used incorrectly !!!"));
|
||||
PrintAndLogEx(WARNING, _RED_("!!! Only proceed if you know exactly what you are doing !!!"));
|
||||
PrintAndLogEx(INFO, "PM5 IO QC test password: 0x%02X%02X%02X%02X", pwd[0], pwd[1], pwd[2], pwd[3]);
|
||||
|
||||
struct {
|
||||
uint32_t pwd; // QC password, verified by the device
|
||||
uint16_t index; // index of the IO to test
|
||||
uint8_t status; // 0 = low, 1 = high, 2 = float or RESET TO DEFAULT
|
||||
} PACKED payload = {
|
||||
.pwd = BYTES2UINT32_BE(pwd),
|
||||
.index = io_index,
|
||||
.status = io_status,
|
||||
};
|
||||
|
||||
PrintAndLogEx(INFO, "Performing PM5 IO QC test (index %u, status %u)...", io_index, io_status);
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_PM5_QC_TEST_IO, (uint8_t *)&payload, sizeof(payload));
|
||||
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_PM5_QC_TEST_IO, &resp, 1000) == false) {
|
||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||
PrintAndLogEx(WARNING, "command execution time out");
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "failed to perform IO QC test on PM5 (wrong password?)");
|
||||
return resp.status;
|
||||
}
|
||||
PrintAndLogEx(INFO, "PM5 IO QC test successful.");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (timeout_ms == 0) {
|
||||
PrintAndLogEx(ERR, "timeout must be greater than zero");
|
||||
return PM3_EINVARG;
|
||||
@@ -2288,11 +2364,11 @@ static int CmdPM5QCTest(const char *Cmd) {
|
||||
PrintAndLogEx(INFO, "Performing QC test for the PM5...");
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_PM5_QC_TEST, (uint8_t *)&timeout_ms, sizeof(timeout_ms));
|
||||
SendCommandNG(CMD_PM5_QC_TEST_HW, (uint8_t *)&timeout_ms, sizeof(timeout_ms));
|
||||
|
||||
PacketResponseNG resp;
|
||||
// wait a bit longer than the device side sequence timeout, with headroom for RTC drift
|
||||
if (WaitForResponseTimeout(CMD_PM5_QC_TEST, &resp, timeout_ms + (timeout_ms / 5) + 1000) == false) {
|
||||
if (WaitForResponseTimeout(CMD_PM5_QC_TEST_HW, &resp, timeout_ms + (timeout_ms / 5) + 1000) == false) {
|
||||
SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
|
||||
PrintAndLogEx(WARNING, "command execution time out");
|
||||
return PM3_ETIMEOUT;
|
||||
@@ -2594,7 +2670,7 @@ static command_t CommandTable[] = {
|
||||
{"fpga", CmdFPGA, IfPm3Present, "Fpga commands"},
|
||||
{"fpgaoff", CmdFPGAOff, IfPm3Present, "Turn off FPGA on device"},
|
||||
{"ant_pm5", CmdPM5Ant, IfPm5StdAnt, "Control the antennal of pm5"},
|
||||
{"qc_pm5", CmdPM5QCTest, IfPm5, "Perform QC test for the PM5"},
|
||||
{"qc_pm5", CmdPM5QCTest, IfPm5, "Perform QC test (hardware or IO) for the PM5"},
|
||||
{"factorydata", CmdDeviceFactoryData, IfI2cEeprom, "Get/Set the factory data for Device"},
|
||||
{"lcd", CmdLCD, IfPm3Lcd, "Send command/data to LCD"},
|
||||
{"lcdreset", CmdLCDReset, IfPm3Lcd, "Hardware reset LCD"},
|
||||
|
||||
+5
-1
@@ -391,12 +391,16 @@ static int CmdNFCType2Write(const char *Cmd) {
|
||||
return CmdHF14MfuNDEFWrite(Cmd);
|
||||
}
|
||||
|
||||
static int CmdNFCType2Format(const char *Cmd) {
|
||||
return CmdHF14MfuNDEFFormat(Cmd);
|
||||
}
|
||||
|
||||
static int CmdNFCType2Help(const char *Cmd);
|
||||
|
||||
static command_t CommandNFCType2Table[] = {
|
||||
|
||||
{"--------", CmdNFCType2Help, AlwaysAvailable, "-------------- " _CYAN_("NFC Forum Tag Type 2") " ---------------"},
|
||||
// {"format", CmdNFCType2Format, IfPm3Iso14443a, "format ISO-14443-a tag as NFC Tag"},
|
||||
{"format", CmdNFCType2Format, IfPm3Iso14443a, "format MIFARE Ultralight / NTAG as NFC Forum Tag Type 2"},
|
||||
{"read", CmdNFCType2Read, IfPm3Iso14443a, "read NFC Forum Tag Type 2"},
|
||||
{"write", CmdNFCType2Write, IfPm3Iso14443a, "write NFC Forum Tag Type 2"},
|
||||
{"--------", CmdNFCType2Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"},
|
||||
|
||||
@@ -56,11 +56,13 @@ static bool next_record_is_response(uint16_t tracepos, uint8_t *trace) {
|
||||
return (hdr->isResponse);
|
||||
}
|
||||
|
||||
// Topaz reader commands are at most 16 bytes long (RSEG/READ8/WRITE-*8:
|
||||
// cmd + adds + 8 data bytes + 4 UID bytes + 2 CRC bytes)
|
||||
#define MAX_TOPAZ_READER_CMD_LEN 16
|
||||
|
||||
static bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen,
|
||||
uint8_t *trace, const uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) {
|
||||
|
||||
#define MAX_TOPAZ_READER_CMD_LEN 16
|
||||
|
||||
uint32_t last_timestamp = timestamp + *duration;
|
||||
|
||||
if ((*data_len != 1) || (frame[0] == TOPAZ_WUPA) || (frame[0] == TOPAZ_REQA)) return false;
|
||||
@@ -515,7 +517,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
|
||||
}
|
||||
|
||||
uint32_t end_of_transmission_timestamp = 0;
|
||||
uint8_t topaz_reader_command[9];
|
||||
uint8_t topaz_reader_command[MAX_TOPAZ_READER_CMD_LEN];
|
||||
char explanation[60] = {0};
|
||||
tracelog_hdr_t *first_hdr = (tracelog_hdr_t *)(trace);
|
||||
tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user