Merge pull request #63 from RfidResearchGroup/master

Update
This commit is contained in:
mwalker33 2020-12-11 08:41:18 +11:00 committed by GitHub
commit c9691ab843
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 3660 additions and 1523 deletions

View file

@ -3,7 +3,26 @@ 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]
- Fix `hf 15 sim` - Added basic response to GET_SYSTEM_INFO and READBLOCK requests in order to fix iso15693 tag sim
- Fix issue #844 - `lf t55xx config` => recompute block0 (@cyberpunk-re)
- EM4x50: changed cli parameter from w (word) to d (data) (@tharexde)
- EM4x50: new function 4x50 login: authenticate against tag (@tharexde)
- EM4x50: new function 4x50 brute: guess password within a given password range (@tharexde)
- EM4x50: new function 4x50 chk: try passwords from dictionary (without option -> T55xx default dictionary or -f user dictionary) (@tharexde)
- EM4x50: new function 4x50 reader: read data from tag (configured data -> standard read mode), incl. option -@ (@tharexde)
- EM4x50: new function 4x50 sim: simulate dump from file or emulator/flash (@tharexde)
- EM4x50: new function 4x50 restore: restore dump file (bin, eml, json) onto tag (@tharexde)
- EM4x50: new function 4x50 esave: dump em4x50 content in emulator memory to file (bin + eml + json) (@tharexde)
- EM4x50: new function 4x50 eload: upload em4x50 file content (bin, eml, json) to emulator memory (@tharexde)
- EM4x50: added LED signals (@tharexde)
- EM4x50: added json format for 4x50 dump (@tharexde)
- EM4x50: relocated write requests in function 4x50 wipe from device to client (@tharexde)
- EM4x50: renamed 4x50_write_password to 4x50 writepwd (@tharexde)
- EM4x50: all hex input parameters now have to be given in lsb format (output is still msb + lsb) (@tharexde)
- EM4x50: changed cli parameter from a (address) to b (block) (@tharexde)
- EM4x50: switched to cliparser for all functions (@tharexde)
- EM4x50: stabilized and accelerated tag detection (@tharexde)
- EM4x50: removed global tag structure on device side (@tharexde)
- Fix `hf 15 sim` - Added basic response to GET_SYSTEM_INFO and READBLOCK requests in order to fix iso15693 tag sim (@cyberpunk-re)
- Added `mf mfu sim t 7 n <numreads>` - MFU emulation now supports automatic exit after <num> blocks read. (@cyberpunk-re)
- Added T55xx Guide to assist in learning how to use the T55xx chip (@mwalker33)
- Fix 'hf iclass wrbl' - dealing with tags in unsecured vs secured pagemode now is correct (@iceman1001)

View file

@ -69,6 +69,12 @@ else
SRC_EM4x50 =
endif
ifneq (,$(findstring WITH_EM4x70,$(APP_CFLAGS)))
SRC_EM4x70 = em4x70.c
else
SRC_EM4x70 =
endif
ifneq (,$(findstring WITH_LCD,$(APP_CFLAGS)))
SRC_LCD = fonts.c LCD.c
else
@ -106,6 +112,7 @@ THUMBSRC = start.c \
$(SRC_FPC) \
$(SRC_HITAG) \
$(SRC_EM4x50) \
$(SRC_EM4x70) \
$(SRC_SPIFFS) \
$(SRC_ISO14443a) \
$(SRC_ISO14443b) \

View file

@ -0,0 +1,359 @@
//-----------------------------------------------------------------------------
// Tharexde, 2020
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// main code for EM4x50 simulator and collector aka THAREXDE
//-----------------------------------------------------------------------------
#include <inttypes.h>
#include "standalone.h"
#include "proxmark3_arm.h"
#include "appmain.h"
#include "BigBuf.h"
#include "fpgaloader.h"
#include "util.h"
#include "dbprint.h"
#include "spiffs.h"
#include "../em4x50.h"
/*
* `lf_tharexde` simulates hardcoded words/blocks, reads words of standard read
* mode of EM4x50 tags and stores them in internal flash.
* It requires RDV4 hardware (for flash and battery).
*
* On entering stand-alone mode, this module will start reading/record EM4x50 data.
* Every found / collected data will be written/appended to the logfile in flash
* as a text string.
*
* LEDs:
* - LED A: simulating
* - LED B: reading / record
* - LED C: writing to flash
* - LED D: unmounting/sync'ing flash (normally < 100ms)
*
* To retrieve log file from flash:
*
* 1. mem spiffs dump o lf_em4x50collect.log f lf_em4x50collect.log
* Copies log file from flash to your client.
*
* 2. exit the Proxmark3 client
*
* 3. more lf_tharexdecollect.log
*
* This module emits debug strings during normal operation -- so try it out in
* the lab connected to PM3 client before taking it into the field.
*
* To delete the log file from flash:
*
* 1. mem spiffs remove lf_tharexdecollect.log
*/
#define STATE_SIM 0
#define STATE_READ 1
#define STATE_BRUTE 2
#define EM4X50_TAG_WORD 45
#define EM4X50_PWD_SPEED 27
#define LF_EM4X50SIMULATE_INPUTFILE "lf_em4x50simulate.eml"
#define LF_EM4X50COLLECT_LOGFILE "lf_em4x50collect.log"
#define LF_EM4X50BRUTE_INPUTFILE "lf_em4x50brute.eml"
#define LF_EM4X50BRUTE_LOGFILE "lf_em4x50brute.log"
bool input_exists;
bool log_exists;
static void LoadDataInstructions(const char *inputfile) {
Dbprintf("");
Dbprintf("To load datafile into flash and display it:");
Dbprintf(_YELLOW_("1.") " edit inputfile %s", inputfile);
Dbprintf(_YELLOW_("2.") " start proxmark3 client");
Dbprintf(_YELLOW_("3.") " mem spiffs load f %s o %s", inputfile, inputfile);
Dbprintf(_YELLOW_("4.") " start standalone mode");
}
static void DownloadLogInstructions(const char *logfile) {
Dbprintf("");
Dbprintf("To get the logfile from flash and display it:");
Dbprintf(_YELLOW_("1.") " mem spiffs dump o %s f %s", logfile, logfile);
Dbprintf(_YELLOW_("2.") " exit proxmark3 client");
Dbprintf(_YELLOW_("3.") " cat %s", logfile);
}
static int get_input_data_from_file(uint32_t *words, char *inputfile) {
size_t now = 0;
if (exists_in_spiffs(inputfile)) {
uint32_t size = size_in_spiffs(inputfile);
uint8_t *mem = BigBuf_malloc(size);
Dbprintf(_YELLOW_("found input file %s"), inputfile);
rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE);
now = size / 9;
for (int i = 0; i < now; i++)
for (int j = 0; j < 4; j++)
words[i] |= (hex2int(mem[2 * j + 9 * i]) << 4 | hex2int(mem[2 * j + 1 + 9 * i])) << ((3 - j) * 8);
Dbprintf(_YELLOW_("read data from input file"));
}
BigBuf_free();
return (now > 0) ? now : 0;
}
static void append(const char *filename, uint8_t *entry, size_t entry_len) {
LED_D_ON();
if (log_exists == false) {
rdv40_spiffs_write(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE);
log_exists = true;
} else {
rdv40_spiffs_append(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE);
}
LED_D_OFF();
}
void ModInfo(void) {
DbpString(_YELLOW_(" LF EM4x50 sim/collector/bruteforce mode") " - a.k.a tharexde");
}
void RunMod(void) {
bool state_change = true;//, password_found = false;
int pwd_found = false;
uint8_t state = STATE_SIM;
// declarations for simulating
uint32_t words[33] = {0x0};
uint32_t pwd = 0x0;
uint32_t passwords[2] = {0x0};
size_t now = 0;
// declarations for reading
int no_words = 0;
//uint32_t words[EM4X50_TAG_WORD];
uint8_t entry[81];
rdv40_spiffs_lazy_mount();
StandAloneMode();
Dbprintf(_YELLOW_("Standalone mode THAREXDE started"));
for (;;) {
WDT_HIT();
if (data_available()) break;
// press button - toggle between SIM, READ and BRUTE
// hold button - exit
int button_pressed = BUTTON_CLICKED(1000);
if (button_pressed == BUTTON_SINGLE_CLICK) {
SpinUp(100);
switch (state) {
case STATE_SIM:
state = STATE_READ;
break;
case STATE_READ:
state = STATE_BRUTE;
break;
case STATE_BRUTE:
state = STATE_SIM;
break;
default:
break;
}
state_change = true;
} else if (button_pressed == BUTTON_HOLD) {
SpinDown(100);
break;
}
if (state == STATE_SIM) {
if (state_change) {
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125);
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK;
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK;
LEDsoff();
LED_A_ON();
Dbprintf("");
Dbprintf(_YELLOW_("switched to EM4x50 simulating mode"));
now = get_input_data_from_file(words, LF_EM4X50SIMULATE_INPUTFILE);
if (now > 0) {
Dbprintf(_YELLOW_("simulating %i blocks"), now);
for (int i = 0; i < now; i++)
Dbprintf("%2i -> %lx", i + 1, words[i]);
} else {
Dbprintf(_RED_("error in input data"));
}
state_change = false;
}
em4x50_sim_send_listen_window();
for (int i = 0; i < now; i++) {
em4x50_sim_send_listen_window();
em4x50_sim_send_word(words[i]);
}
} else if (state == STATE_READ) {
if (state_change) {
LEDsoff();
LED_B_ON();
Dbprintf("");
Dbprintf(_YELLOW_("switched to EM4x50 reading mode"));
memset(entry, 0, sizeof(entry));
memset(words, 0, sizeof(words));
log_exists = exists_in_spiffs(LF_EM4X50COLLECT_LOGFILE);
state_change = false;
}
no_words = em4x50_standalone_read(words);
if (no_words > 0) {
memset(entry, 0, sizeof(entry));
sprintf((char *)entry, "found new EM4x50 tag:");
Dbprintf("%s", entry);
strcat((char *)entry, "\n");
append(LF_EM4X50COLLECT_LOGFILE, entry, strlen((char *)entry));
for (int i = 0; i < no_words; i++) {
sprintf((char *)entry, " %2i -> 0x%08"PRIx32"", i + 1, words[i]);
Dbprintf("%s", entry);
strcat((char *)entry, "\n");
append(LF_EM4X50COLLECT_LOGFILE, entry, strlen((char *)entry));
}
}
} else if (state == STATE_BRUTE) {
if (state_change) {
LEDsoff();
LED_C_ON();
Dbprintf("");
Dbprintf(_YELLOW_("switched to EM4x50 brute force mode"));
log_exists = exists_in_spiffs(LF_EM4X50BRUTE_LOGFILE);
now = get_input_data_from_file(passwords, LF_EM4X50BRUTE_INPUTFILE);
if (now == 2) {
// print some information
int no_iter = passwords[1] - passwords[0] + 1;
int dur_s = no_iter / EM4X50_PWD_SPEED;
int dur_h = dur_s / 3600;
int dur_m = (dur_s - dur_h * 3600) / 60;
dur_s -= dur_h * 3600 + dur_m * 60;
//iterprint = no_iter/10;
Dbprintf(_YELLOW_("trying %i passwords in range [0x%08x, 0x%08x]"),
no_iter, passwords[0], passwords[1]);
Dbprintf(_YELLOW_("estimated duration: %ih%im%is"),
dur_h, dur_m, dur_s);
} else {
Dbprintf(_RED_("error in input data"));
break;
}
state_change = false;
}
pwd_found = em4x50_standalone_brute(passwords[0], passwords[1], &pwd);
if (pwd_found == PM3_ETIMEOUT) {
// timeout -> no EM4x50 tag on reader?
Dbprintf(_YELLOW_("timeout - no EM4x50 tag detected"));
} else if (pwd_found == true) {
// password found -> write to logfile
sprintf((char *)entry, "password found: 0x%08"PRIx32, pwd);
Dbprintf(_YELLOW_("%s"), entry);
strcat((char *)entry, "\n");
append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry));
break;
} else {
if (pwd == passwords[1] + 1) {
// finished without success -> write to logfile
sprintf((char *)entry, "no password found");
Dbprintf(_YELLOW_("%s"), entry);
strcat((char *)entry, "\n");
append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry));
} else {
// stopped -> write to logfile
sprintf((char *)entry, "stopped search - last password: 0x%08"PRIx32, pwd);
Dbprintf(_YELLOW_("%s"), entry);
strcat((char *)entry, "\n");
append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry));
// replace start password by last tested password in
// inputfile (spiffs) so that brute forcing process will
// be continued when envoking brute force mode again
sprintf((char *)entry, "%08"PRIx32"\n%08"PRIx32"\n", pwd, passwords[1]);
rdv40_spiffs_write(LF_EM4X50BRUTE_INPUTFILE,
entry,
strlen((char *)entry),
RDV40_SPIFFS_SAFETY_SAFE);
}
break;
}
}
}
if (state == STATE_READ) {
DownloadLogInstructions(LF_EM4X50COLLECT_LOGFILE);
} else if (state == STATE_BRUTE) {
LoadDataInstructions(LF_EM4X50BRUTE_INPUTFILE);
DownloadLogInstructions(LF_EM4X50BRUTE_LOGFILE);
} else {
LoadDataInstructions(LF_EM4X50SIMULATE_INPUTFILE);
}
LED_D_ON();
rdv40_spiffs_lazy_unmount();
LED_D_OFF();
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
Dbprintf("");
Dbprintf(_YELLOW_("[=] Standalone mode THAREXDE stopped"));
}

View file

@ -31,6 +31,7 @@
#include "hitag2.h"
#include "hitagS.h"
#include "em4x50.h"
#include "em4x70.h"
#include "iclass.h"
#include "legicrfsim.h"
//#include "cryptorfsim.h"
@ -479,6 +480,12 @@ static void SendCapabilities(void) {
#else
capabilities.compiled_with_em4x50 = false;
#endif
#ifdef WITH_EM4x70
capabilities.compiled_with_em4x70 = true;
#else
capabilities.compiled_with_em4x70 = false;
#endif
#ifdef WITH_HFSNIFF
capabilities.compiled_with_hfsniff = true;
#else
@ -1106,16 +1113,61 @@ static void PacketReceived(PacketCommandNG *packet) {
em4x50_write((em4x50_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X50_WRITE_PASSWORD: {
em4x50_write_password((em4x50_data_t *)packet->data.asBytes);
case CMD_LF_EM4X50_WRITEPWD: {
em4x50_writepwd((em4x50_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X50_READ: {
em4x50_read((em4x50_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X50_WIPE: {
em4x50_wipe((em4x50_data_t *)packet->data.asBytes);
case CMD_LF_EM4X50_BRUTE: {
em4x50_brute((em4x50_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X50_LOGIN: {
em4x50_login((uint32_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X50_SIM: {
//-----------------------------------------------------------------------------
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
em4x50_sim((uint8_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X50_READER: {
em4x50_reader();
break;
}
case CMD_LF_EM4X50_ESET: {
//-----------------------------------------------------------------------------
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
emlSet(packet->data.asBytes, packet->oldarg[0], packet->oldarg[1]);
break;
}
case CMD_LF_EM4X50_CHK: {
//-----------------------------------------------------------------------------
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_LF) here although FPGA is not
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
em4x50_chk((uint8_t *)packet->data.asBytes);
break;
}
#endif
#ifdef WITH_EM4x70
case CMD_LF_EM4X70_INFO: {
em4x70_info((em4x70_data_t *)packet->data.asBytes);
break;
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -13,14 +13,19 @@
#include "../include/em4x50.h"
typedef struct {
uint8_t sectors[34][7];
} em4x50_tag_t;
int em4x50_standalone_read(uint32_t *words);
int em4x50_standalone_brute(uint32_t start, uint32_t stop, uint32_t *pwd);
bool em4x50_sim_send_listen_window(void);
bool em4x50_sim_send_word(uint32_t word);
void em4x50_info(em4x50_data_t *etd);
void em4x50_write(em4x50_data_t *etd);
void em4x50_write_password(em4x50_data_t *etd);
void em4x50_writepwd(em4x50_data_t *etd);
void em4x50_read(em4x50_data_t *etd);
void em4x50_wipe(em4x50_data_t *etd);
void em4x50_brute(em4x50_data_t *etd);
void em4x50_login(uint32_t *password);
void em4x50_sim(uint8_t *filename);
void em4x50_reader(void);
void em4x50_chk(uint8_t *filename);
#endif /* EM4X50_H */

563
armsrc/em4x70.c Normal file
View file

@ -0,0 +1,563 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 sirloins based on em4x50
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4170 commands
//-----------------------------------------------------------------------------
#include "fpgaloader.h"
#include "ticks.h"
#include "dbprint.h"
#include "lfadc.h"
#include "commonutil.h"
#include "em4x70.h"
#include "appmain.h" // tear
static em4x70_tag_t tag = { 0 };
// EM4170 requires a parity bit on commands, other variants do not.
static bool command_parity = true;
#define EM4X70_T_TAG_QUARTER_PERIOD 8
#define EM4X70_T_TAG_HALF_PERIOD 16
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD 24
#define EM4X70_T_TAG_FULL_PERIOD 32
#define EM4X70_T_TAG_TWA 128 // Write Access Time
#define EM4X70_T_TAG_DIV 224 // Divergency Time
#define EM4X70_T_TAG_AUTH 4224 // Authentication Time
#define EM4X70_T_TAG_WEE 3072 // EEPROM write Time
#define EM4X70_T_TAG_TWALB 128 // Write Access Time of Lock Bits
#define EM4X70_T_WAITING_FOR_SNGLLIW 160 // Unsure
#define TICKS_PER_FC 12 // 1 fc = 8us, 1.5us per tick = 12 ticks
#define EM4X70_MIN_AMPLITUDE 10 // Minimum difference between a high and low signal
#define EM4X70_TAG_TOLERANCE 10
#define EM4X70_TAG_WORD 48
/**
* These IDs are from the EM4170 datasheet
* Some versions of the chip require a fourth
* (even) parity bit, others do not
*/
#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
static uint8_t gHigh = 0;
static uint8_t gLow = 0;
#define IS_HIGH(sample) (sample>gLow ? true : false)
#define IS_LOW(sample) (sample<gHigh ? true : false)
#define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks)
static uint8_t bits2byte(uint8_t *bits, int length);
static void bits2bytes(uint8_t *bits, int length, uint8_t *out);
static int em4x70_receive(uint8_t *bits);
static bool find_listen_window(bool command);
static void init_tag(void) {
memset(tag.data, 0x00, sizeof(tag.data)/sizeof(tag.data[0]));
}
static void EM4170_setup_read(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD);
// 50ms for the resonant antenna to settle.
SpinDelay(50);
// Now set up the SSC to get the ADC samples that are now streaming at us.
FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125);
// Connect the A/D to the peak-detected low-frequency path.
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
// Steal this pin from the SSP (SPI communication channel with fpga) and
// use it to control the modulation
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
// Disable modulation at default, which means enable the field
LOW(GPIO_SSC_DOUT);
// Start the timer
StartTicks();
// Watchdog hit
WDT_HIT();
}
static bool get_signalproperties(void) {
// calculate signal properties (mean amplitudes) from measured data:
// 32 amplitudes (maximum values) -> mean amplitude value -> gHigh -> gLow
bool signal_found = false;
int no_periods = 32, pct = 50, noise = 140; // pct originally 75, found 50 was working better for me
uint8_t sample_ref = 127;
uint8_t sample_max_mean = 0;
uint8_t sample_max[no_periods];
uint32_t sample_max_sum = 0;
memset(sample_max, 0x00, sizeof(sample_max));
// wait until signal/noise > 1 (max. 32 periods)
for (int i = 0; i < TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD * no_periods; i++) {
// about 2 samples per bit period
WaitTicks(TICKS_PER_FC * EM4X70_T_TAG_HALF_PERIOD);
if (AT91C_BASE_SSC->SSC_RHR > noise) {
signal_found = true;
break;
}
}
if (signal_found == false)
return false;
// calculate mean maximum value of 32 periods, each period has a length of
// 3 single "full periods" to eliminate the influence of a listen window
for (int i = 0; i < no_periods; i++) {
uint32_t start_ticks = GetTicks();
//AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (GetTicks() - start_ticks < TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) {
volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
if (sample > sample_max[i])
sample_max[i] = sample;
}
sample_max_sum += sample_max[i];
}
sample_max_mean = sample_max_sum / no_periods;
// set global envelope variables
gHigh = sample_ref + pct * (sample_max_mean - sample_ref) / 100;
gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100;
// Basic sanity check
if(gHigh - gLow < EM4X70_MIN_AMPLITUDE) {
return false;
}
Dbprintf("%s: gHigh %d gLow: %d", __func__, gHigh, gLow);
return true;
}
/**
* get_pulse_length
*
* Times falling edge pulses
*/
static uint32_t get_pulse_length(void) {
uint8_t sample;
uint32_t timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout))
return 0;
uint32_t start_ticks = GetTicks();
timeout = start_ticks + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout))
return 0;
timeout = (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) + GetTicks();
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout))
return 0;
return GetTicks() - start_ticks;
}
/**
* get_pulse_invert_length
*
* Times rising edge pules
* TODO: convert to single function with get_pulse_length()
*/
static uint32_t get_pulse_invert_length(void) {
uint8_t sample;
uint32_t timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout))
return 0;
uint32_t start_ticks = GetTicks();
timeout = start_ticks + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout))
return 0;
timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout))
return 0;
return GetTicks() - start_ticks;
}
static bool check_pulse_length(uint32_t pl, int length, int margin) {
// check if pulse length <pl> corresponds to given length <length>
//Dbprintf("%s: pulse length %d vs %d", __func__, pl, length * TICKS_PER_FC);
return ((pl >= TICKS_PER_FC * (length - margin)) & (pl <= TICKS_PER_FC * (length + margin)));
}
static void em4x70_send_bit(int bit) {
// send single bit according to EM4170 application note and datasheet
uint32_t start_ticks = GetTicks();
if (bit == 0) {
// disable modulation (drop the field) for 4 cycles of carrier
LOW(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * 4);
// enable modulation (activates the field) for remaining first
// half of bit period
HIGH(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_HALF_PERIOD);
// disable modulation for second half of bit period
LOW(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD);
} else {
// bit = "1" means disable modulation for full bit period
LOW(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD);
}
}
/**
* em4x70_send_command
*/
static void em4170_send_command(uint8_t command) {
int parity = 0;
int msb_bit = 0;
// Non automotive EM4x70 based tags are 3 bits + 1 parity.
// So drop the MSB and send a parity bit instead after the command
if(command_parity)
msb_bit = 1;
for (int i = msb_bit; i < 4; i++) {
int bit = (command >> (3 - i)) & 1;
em4x70_send_bit(bit);
parity ^= bit;
}
if(command_parity)
em4x70_send_bit(parity);
}
static bool find_listen_window(bool command) {
int cnt = 0;
while(cnt < EM4X70_T_WAITING_FOR_SNGLLIW) {
/*
80 ( 64 + 16 )
80 ( 64 + 16 )
Flip Polarity
96 ( 64 + 32 )
64 ( 32 + 16 +16 )*/
if (check_pulse_length(get_pulse_invert_length(), 80, EM4X70_TAG_TOLERANCE)) {
if (check_pulse_length(get_pulse_invert_length(), 80, EM4X70_TAG_TOLERANCE)) {
if (check_pulse_length(get_pulse_length(), 96, EM4X70_TAG_TOLERANCE)) {
if (check_pulse_length(get_pulse_length(), 64, EM4X70_TAG_TOLERANCE)) {
if(command) {
/* Here we are after the 64 duration edge.
* em4170 says we need to wait about 48 RF clock cycles.
* depends on the delay between tag and us
*
* I've found between 4-5 quarter periods (32-40) works best
*/
WaitTicks(TICKS_PER_FC * 5 * EM4X70_T_TAG_QUARTER_PERIOD);
// Send RM Command
em4x70_send_bit(0);
em4x70_send_bit(0);
}
return true;
}
}
}
}
cnt++;
}
return false;
}
static void bits2bytes(uint8_t *bits, int length, uint8_t *out) {
if(length%8 != 0) {
Dbprintf("Should have a multiple of 8 bits, was sent %d", length);
}
int num_bytes = length / 8; // We should have a multiple of 8 here
for(int i=1; i <= num_bytes; i++) {
out[num_bytes-i] = bits2byte(bits, 8);
bits+=8;
//Dbprintf("Read: %02X", out[num_bytes-i]);
}
}
static uint8_t bits2byte(uint8_t *bits, int length) {
// converts <length> separate bits into a single "byte"
uint8_t byte = 0;
for (int i = 0; i < length; i++) {
byte |= bits[i];
if (i != length - 1)
byte <<= 1;
}
return byte;
}
/*static void print_array(uint8_t *bits, int len) {
if(len%8 != 0) {
Dbprintf("Should have a multiple of 8 bits, was sent %d", len);
}
int num_bytes = len / 8; // We should have a multiple of 8 here
uint8_t bytes[8];
for(int i=0;i<num_bytes;i++) {
bytes[i] = bits2byte(bits, 8);
bits+=8;
Dbprintf("Read: %02X", bytes[i]);
}
}*/
/**
* em4x70_read_id
*
* read pre-programmed ID (4 bytes)
*/
static bool em4x70_read_id(void) {
if(find_listen_window(true)) {
uint8_t bits[64] = {0};
em4170_send_command(EM4X70_COMMAND_ID);
int num = em4x70_receive(bits);
if(num < 32) {
Dbprintf("Invalid ID Received");
return false;
}
bits2bytes(bits, num, &tag.data[4]);
return true;
}
return false;
}
/**
* em4x70_read_um1
*
* read user memory 1 (4 bytes including lock bits)
*/
static bool em4x70_read_um1(void) {
if(find_listen_window(true)) {
uint8_t bits[64] = {0};
em4170_send_command(EM4X70_COMMAND_UM1);
int num = em4x70_receive(bits);
if(num < 32) {
Dbprintf("Invalid UM1 data received");
return false;
}
bits2bytes(bits, num, &tag.data[0]);
return true;
}
return false;
}
/**
* em4x70_read_um2
*
* read user memory 2 (8 bytes)
*/
static bool em4x70_read_um2(void) {
if(find_listen_window(true)) {
uint8_t bits[64] = {0};
em4170_send_command(EM4X70_COMMAND_UM2);
int num = em4x70_receive(bits);
if(num < 64) {
Dbprintf("Invalid UM2 data received");
return false;
}
bits2bytes(bits, num, &tag.data[24]);
return true;
}
return false;
}
static bool find_EM4X70_Tag(void) {
Dbprintf("%s: Start", __func__);
// function is used to check wether a tag on the proxmark is an
// EM4170 tag or not -> speed up "lf search" process
return find_listen_window(false);
}
static int em4x70_receive(uint8_t *bits) {
uint32_t pl;
int bit_pos = 0;
uint8_t edge = 0;
bool foundheader = false;
// Read out the header
// 12 Manchester 1's (may miss some during settle period)
// 4 Manchester 0's
// Skip a few leading 1's as it could be noisy
WaitTicks(TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
// wait until we get the transition from 1's to 0's which is 1.5 full windows
int pulse_count = 0;
while(pulse_count < 12){
pl = get_pulse_invert_length();
pulse_count++;
if(check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD, EM4X70_TAG_TOLERANCE)) {
foundheader = true;
break;
}
}
if(!foundheader) {
Dbprintf("Failed to find read header");
return 0;
}
// Skip next 3 0's, header check consumes the first 0
for(int i = 0; i < 3; i++) {
get_pulse_invert_length();
}
// identify remaining bits based on pulse lengths
// between two listen windows only pulse lengths of 1, 1.5 and 2 are possible
while (true) {
if(edge)
pl = get_pulse_length();
else
pl = get_pulse_invert_length();
if (check_pulse_length(pl, EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) {
// pulse length = 1
bits[bit_pos++] = edge;
} else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) {
// pulse length = 1.5 -> flip edge detection
if(edge) {
bits[bit_pos++] = 0;
bits[bit_pos++] = 0;
edge = 0;
} else {
bits[bit_pos++] = 1;
bits[bit_pos++] = 1;
edge = 1;
}
} else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) {
// pulse length of 2
if(edge) {
bits[bit_pos++] = 0;
bits[bit_pos++] = 1;
} else {
bits[bit_pos++] = 1;
bits[bit_pos++] = 0;
}
} else if ( (edge && check_pulse_length(pl, 3 * EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) ||
(!edge && check_pulse_length(pl, 80, EM4X70_T_TAG_QUARTER_PERIOD))) {
// LIW detected (either invert or normal)
return --bit_pos;
}
}
return bit_pos;
}
void em4x70_info(em4x70_data_t *etd) {
uint8_t status = 0;
// Support tags with and without command parity bits
command_parity = etd->parity;
init_tag();
EM4170_setup_read();
// Find the Tag
if (get_signalproperties() && find_EM4X70_Tag()) {
// Read ID, UM1 and UM2
status = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2();
}
StopTicks();
lf_finalize();
reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data));
}

22
armsrc/em4x70.h Normal file
View file

@ -0,0 +1,22 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 sirloins
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4x70 commands
//-----------------------------------------------------------------------------
#ifndef EM4x70_H
#define EM4x70_H
#include "../include/em4x70.h"
typedef struct {
uint8_t data[32];
} em4x70_tag_t;
void em4x70_info(em4x70_data_t *etd);
#endif /* EM4x70_H */

View file

@ -31,6 +31,7 @@ void lf_wait_periods(size_t periods);
void lf_init(bool reader, bool simulate);
void lf_finalize(void);
size_t lf_detect_field_drop(size_t max);
bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len);
void lf_modulation(bool modulation);

View file

@ -252,6 +252,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdlfem410x.c
${PM3_ROOT}/client/src/cmdlfem4x05.c
${PM3_ROOT}/client/src/cmdlfem4x50.c
${PM3_ROOT}/client/src/cmdlfem4x70.c
${PM3_ROOT}/client/src/cmdlffdxb.c
${PM3_ROOT}/client/src/cmdlfgallagher.c
${PM3_ROOT}/client/src/cmdlfguard.c

View file

@ -493,6 +493,7 @@ SRCS = aiddesfire.c \
cmdlfem410x.c \
cmdlfem4x05.c \
cmdlfem4x50.c \
cmdlfem4x70.c \
cmdlffdxb.c \
cmdlfguard.c \
cmdlfgallagher.c \

View file

@ -4066,6 +4066,13 @@
"service_provider": "PAYCULT",
"system_integrator": "PAYCULT"
},
{
"application": "Access Control (SIO)",
"company": "HID Global",
"mad": "0x3D01",
"service_provider": "HID Corporation",
"system_integrator": "HID Corporation"
},
{
"application": "City transport bus, ferry, administration",
"company": "VFJ Technology Pty Ltd",

View file

@ -709,7 +709,7 @@ static int Cmdmandecoderaw(const char *Cmd) {
if (Em410xDecode(bits, &size, &idx, &hi, &id) == 1) {
//need to adjust to set bitstream back to manchester encoded data
//setDemodBuff(bits, size, idx);
printEM410x(hi, id);
printEM410x(hi, id, false);
}
}
return PM3_SUCCESS;

View file

@ -11,10 +11,8 @@
// High frequency ISO14443A commands
//-----------------------------------------------------------------------------
#include "cmdhf14a.h"
#include <ctype.h>
#include <string.h>
#include "cmdparser.h" // command_t
#include "commonutil.h" // ARRAYLEN
#include "comms.h" // clearCommandBuffer
@ -29,6 +27,9 @@
#include "aidsearch.h"
#include "cmdhf.h" // handle HF plot
#include "protocols.h" // MAGIC_GEN_1A
#include "cliparser.h"
#include "protocols.h" // definitions of ISO14A/7816 protocol
#include "emv/apduinfo.h" // GetAPDUCodeDescription
bool APDUInFramingEnable = true;
@ -2119,6 +2120,139 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
return select_status;
}
static uint16_t get_sw(uint8_t *d, uint8_t n) {
if (n < 2)
return 0;
n -= 2;
return d[n] * 0x0100 + d[n + 1];
}
static int CmdHf14AFuzzapdu(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf 14a apdufuzz",
"Fuzz APDU's of ISO7816 protocol to find valid CLS/INS/P1P2/LE commands.\n"
"It loops all 256 possible values for each byte.\n"
"Tag must be on antenna before running.",
"hf 14a apdufuzz\n"
"hf 14a apdufuzz --cla 80\n"
);
void *argtable[] = {
arg_param_begin,
arg_str0(NULL, "cla", "<hex>", "start CLASS value (1 hex byte)"),
arg_str0(NULL, "ins", "<hex>", "start INSTRUCTION value (1 hex byte)"),
arg_str0(NULL, "p1", "<hex>", "start P1 value (1 hex byte)"),
arg_str0(NULL, "p2", "<hex>", "start P2 value (1 hex byte)"),
arg_str0(NULL, "le", "<hex>", "start LENGTH value (1 hex byte)"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int cla_len = 0;
uint8_t cla[1] = {0};
CLIGetHexWithReturn(ctx, 1, cla, &cla_len);
int ins_len = 0;
uint8_t ins[1] = {0};
CLIGetHexWithReturn(ctx, 2, ins, &ins_len);
int p1_len = 0;
uint8_t p1[1] = {0};
CLIGetHexWithReturn(ctx, 3, p1, &p1_len);
int p2_len = 0;
uint8_t p2[1] = {0};
CLIGetHexWithReturn(ctx, 4, p2, &p2_len);
int le_len = 0;
uint8_t le[1] = {0};
CLIGetHexWithReturn(ctx, 5, le, &le_len);
bool verbose = arg_get_lit(ctx, 6);
CLIParserFree(ctx);
bool activate_field = true;
bool keep_field_on = true;
uint8_t a = cla[0];
uint8_t b = ins[0];
uint8_t c = p1[0];
uint8_t d = p2[0];
uint8_t e = le[0];
PrintAndLogEx(SUCCESS, "Starting the apdu fuzzer [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " LE " _GREEN_("%02x")" ]", a,b,c,d,e);
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
uint8_t response[PM3_CMD_DATA_SIZE];
int resplen = 0;
uint8_t aSELECT_AID[80];
int aSELECT_AID_n = 0;
param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n);
int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
if (res) {
DropField();
return res;
}
if (activate_field)
activate_field = false;
uint64_t t1 = msclock();
do {
do {
do {
do {
do {
if (kbd_enter_pressed()) {
goto out;
}
uint8_t foo[5] = {a, b, c, d, e};
int foo_n = sizeof(foo);
if (verbose) {
PrintAndLogEx(INFO, "%s", sprint_hex(foo, sizeof(foo)));
}
res = ExchangeAPDU14a(foo, foo_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
if (res) {
e++;
continue;
}
uint16_t sw = get_sw(response, resplen);
if (sw != 0x6a86 &&
sw != 0x6986 &&
sw != 0x6d00
) {
PrintAndLogEx(INFO, "%02X %02X %02X %02X %02X (%04x - %s)", a,b,c,d,e, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
}
e++;
if (verbose) {
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
}
} while (e);
d++;
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
} while (d);
c++;
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
} while (c);
b++;
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
} while (b);
a++;
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e);
} while(a);
out:
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000);
DropField();
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"},
@ -2132,6 +2266,7 @@ static command_t CommandTable[] = {
{"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
{"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"},
{"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"},
{"apdufuzz", CmdHf14AFuzzapdu, IfPm3Iso14443a, "Fuzz APDU - CLA/INS/P1P2"},
{NULL, NULL, NULL, NULL}
};

View file

@ -1696,7 +1696,7 @@ static int CmdHFiClassRestore(const char *Cmd) {
CLIParserInit(&ctx, "hf iclass restore",
"Restore data from dumpfile onto a iCLASS tag",
"hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0\n"
"hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite"
"hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite\n"
"hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 -k 1122334455667788 --elite\n"
);

View file

@ -12,7 +12,7 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "cliparser.h"
#include "cmdparser.h" // command_t
#include "comms.h"
#include "cmdtrace.h"
@ -22,28 +22,6 @@
static int CmdHelp(const char *Cmd);
static int usage_thinfilm_info(void) {
PrintAndLogEx(NORMAL, "Usage: hf thinfilm info [h]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h this help");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " hf thinfilm info");
return PM3_SUCCESS;
}
static int usage_thinfilm_sim(void) {
PrintAndLogEx(NORMAL, "Usage: hf thinfilm sim [h] [d <data>]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h this help");
PrintAndLogEx(NORMAL, " d <bytes> bytes to send, in hex");
PrintAndLogEx(NORMAL, " r raw, provided bytes should include CRC");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, " hf thinfilm sim d B70470726f786d61726b2e636f6d");
return PM3_SUCCESS;
}
// Printing function based upon the code in libnfc
// ref
// https://github.com/nfc-tools/libnfc/blob/master/utils/nfc-barcode.c
@ -119,26 +97,17 @@ static int print_barcode(uint8_t *barcode, const size_t barcode_len, bool verbos
}
static int CmdHfThinFilmInfo(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf thinfilm info",
"Get info from Thinfilm tags",
"hf thinfilm info");
uint8_t cmdp = 0;
bool errors = false;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h':
return usage_thinfilm_info();
default:
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
//Validations
if (errors) {
usage_thinfilm_info();
return PM3_EINVARG;
}
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
return infoThinFilm(true);
}
@ -168,45 +137,40 @@ int infoThinFilm(bool verbose) {
}
static int CmdHfThinFilmSim(const char *Cmd) {
uint8_t cmdp = 0;
uint8_t data[512];
int datalen = 0;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf thinfilm sim",
"Simulate Thinfilm tag",
"hf thinfilm sim -d B70470726f786d61726b2e636f6d");
void *argtable[] = {
arg_param_begin,
arg_str1("d", "data", "<hex>", "bytes to send"),
arg_lit0(NULL, "raw", "raw, provided bytes should include CRC"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int data_len = 0;
uint8_t data[512] = {0};
CLIGetHexWithReturn(ctx, 1, data, &data_len);
bool addcrc = true;
bool errors = false;
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
switch (tolower(param_getchar(Cmd, cmdp))) {
case 'h':
return usage_thinfilm_sim();
case 'd':
// Retrieve the data
param_gethex_ex(Cmd, cmdp + 1, data, &datalen);
datalen >>= 1;
cmdp += 2;
break;
case 'r':
if (arg_get_lit(ctx, 2)) {
addcrc = false;
cmdp++;
break;
default:
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
errors = true;
break;
}
}
//Validations
if (errors || cmdp == 0 || datalen == 0 || datalen > 512) return usage_thinfilm_sim();
if (addcrc && datalen <= 510) {
CLIParserFree(ctx);
if (addcrc && data_len <= 510) {
uint8_t b1, b2;
compute_crc(CRC_14443_A, data, datalen, &b1, &b2);
data[datalen++] = b2;
data[datalen++] = b1;
compute_crc(CRC_14443_A, data, data_len, &b1, &b2);
data[data_len++] = b2;
data[data_len++] = b1;
}
clearCommandBuffer();
SendCommandNG(CMD_HF_THINFILM_SIMULATE, (uint8_t *)&data, datalen);
SendCommandNG(CMD_HF_THINFILM_SIMULATE, (uint8_t *)&data, data_len);
PacketResponseNG resp;
PrintAndLogEx(SUCCESS, "press pm3-button to abort simulation");

View file

@ -14,6 +14,7 @@
#include <string.h>
#include <ctype.h>
#include <inttypes.h>
#include "cliparser.h"
#include "cmdparser.h" // command_t
#include "comms.h"
#include "cmdtrace.h"
@ -393,21 +394,42 @@ static int topaz_print_NDEF(uint8_t *data, size_t maxsize) {
}
static int CmdHFTopazReader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf topaz reader",
"Read UID from Topaz tags",
"hf topaz reader");
bool verbose = true;
char ctmp = tolower(param_getchar(Cmd, 0));
if (ctmp == 's') verbose = false;
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
return readTopazUid(verbose);
}
// read a Topaz tag and print some useful information
static int CmdHFTopazInfo(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf topaz info",
"Get info from Topaz tags",
"hf topaz info");
bool verbose = true;
char ctmp = tolower(param_getchar(Cmd, 0));
if (ctmp == 's') verbose = false;
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = arg_get_lit(ctx, 1);
CLIParserFree(ctx);
int status = readTopazUid(verbose);
if (status != PM3_SUCCESS)
@ -469,13 +491,34 @@ static int CmdHFTopazInfo(const char *Cmd) {
}
static int CmdHFTopazSim(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf topaz sim",
"Simulate a Topaz tag",
"hf topaz sim <- Not yet implemented");
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
PrintAndLogEx(INFO, "not yet implemented");
return PM3_SUCCESS;
}
static int CmdHFTopazCmdRaw(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf topaz raw",
"Send raw hex data to Topaz tags",
"hf topaz raw <- Not yet implemented");
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
PrintAndLogEx(INFO, "not yet implemented. Use hf 14 raw with option -T.");
return PM3_SUCCESS;
}
@ -490,6 +533,25 @@ static int CmdHFTopazList(const char *Cmd) {
return CmdTraceList(args);
}
static int CmdHFTopazSniff(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf topaz sniff",
"Sniff Topaz reader-tag communication",
"hf topaz sniff");
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
uint8_t param = 0;
SendCommandNG(CMD_HF_ISO14443A_SNIFF, (uint8_t *)&param, sizeof(uint8_t));
return PM3_SUCCESS;
}
static int CmdHelp(const char *Cmd);
static command_t CommandTable[] = {
@ -498,7 +560,7 @@ static command_t CommandTable[] = {
{"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"},
{"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"},
{"sim", CmdHFTopazSim, IfPm3Iso14443a, "<UID> -- Simulate Topaz tag"},
{"sniff", CmdHF14ASniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"},
{"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"},
{"raw", CmdHFTopazCmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
{NULL, NULL, 0, NULL}
};

View file

@ -31,6 +31,7 @@
#include "cmdlfem410x.h" // for em4x menu
#include "cmdlfem4x05.h" // for em4x05 / 4x69
#include "cmdlfem4x50.h" // for em4x50
#include "cmdlfem4x70.h" // for em4x70
#include "cmdlfhid.h" // for hid menu
#include "cmdlfhitag.h" // for hitag menu
#include "cmdlfidteck.h" // for idteck menu
@ -1370,6 +1371,14 @@ static bool CheckChipType(bool getDeviceData) {
goto out;
}
// check for em4x70 chips
if (detect_4x70_block()) {
PrintAndLogEx(SUCCESS, "Chipset detection: " _GREEN_("EM4x70"));
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 4x70`") " commands");
retval = true;
goto out;
}
PrintAndLogEx(NORMAL, "Couldn't identify a chipset");
out:
save_restoreGB(GRAPH_RESTORE);

View file

@ -12,6 +12,8 @@
#include "cmdlfem410x.h"
#include "cmdlfem4x05.h"
#include "cmdlfem4x50.h"
#include "cmdlfem4x70.h"
#include <inttypes.h>
#include <stdlib.h>
#include "cmdparser.h" // command_t
@ -25,6 +27,7 @@ static command_t CommandTable[] = {
{"410x", CmdLFEM410X, AlwaysAvailable, "EM 4102 commands..."},
{"4x05", CmdLFEM4X05, AlwaysAvailable, "EM 4205 / 4305 / 4369 / 4469 commands..."},
{"4x50", CmdLFEM4X50, AlwaysAvailable, "EM 4350 / 4450 commands..."},
{"4x70", CmdLFEM4X70, AlwaysAvailable, "EM 4070 / 4170 commands..."},
{NULL, NULL, NULL, NULL}
};

View file

@ -1,5 +1,8 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
// modified marshmellow
// modified Iceman, 2020
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
@ -10,13 +13,11 @@
#include "cmdlfem410x.h"
#include "cmdlfem4x50.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <stdlib.h>
#include "fileutils.h"
#include "cmdparser.h" // command_t
#include "comms.h"
@ -37,88 +38,6 @@
static uint64_t g_em410xid = 0;
static int CmdHelp(const char *Cmd);
//////////////// 410x commands
static int usage_lf_em410x_demod(void) {
PrintAndLogEx(NORMAL, "Usage: lf em 410x_demod [h] [clock] <0|1> [maxError]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h - this help");
PrintAndLogEx(NORMAL, " clock - set clock as integer, optional, if not set, autodetect.");
PrintAndLogEx(NORMAL, " <0|1> - 0 normal output, 1 for invert output");
PrintAndLogEx(NORMAL, " maxerror - set maximum allowed errors, default = 100.");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod") " = demod an EM410x Tag ID from GraphBuffer");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 32") " = demod an EM410x Tag ID from GraphBuffer using a clock of RF/32");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 32 1") " = demod an EM410x Tag ID from GraphBuffer using a clock of RF/32 and inverting data");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 1") " = demod an EM410x Tag ID from GraphBuffer while inverting data");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_demod 64 1 0") " = demod an EM410x Tag ID from GraphBuffer using a clock of RF/64 and inverting data and allowing 0 demod errors");
return PM3_SUCCESS;
}
static int usage_lf_em410x_watch(void) {
PrintAndLogEx(NORMAL, "Enables IOProx compatible reader mode printing details of scanned tags.");
PrintAndLogEx(NORMAL, "By default, values are printed and logged until the button is pressed or another USB command is issued.");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Usage: lf em 410x_watch");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_watch"));
return PM3_SUCCESS;
}
static int usage_lf_em410x_clone(void) {
PrintAndLogEx(NORMAL, "Writes EM410x ID to a T55x7 or Q5/T5555 tag");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Usage: lf em 410x_clone [h] <id> <card> [clock]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h - this help");
PrintAndLogEx(NORMAL, " <id> - ID number");
PrintAndLogEx(NORMAL, " <card> - 0|1 0 = Q5/T5555, 1 = T55x7");
PrintAndLogEx(NORMAL, " <clock> - 16|32|40|64, optional, set R/F clock rate, defaults to 64");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_clone 0F0368568B 1") " = write ID to t55x7 card");
return PM3_SUCCESS;
}
static int usage_lf_em410x_ws(void) {
PrintAndLogEx(NORMAL, "Watch 'nd Spoof, activates reader, waits until a EM410x tag gets presented then it starts simulating the found UID");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Usage: lf em 410x_spoof [h]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h - this help");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_spoof"));
return PM3_SUCCESS;
}
static int usage_lf_em410x_sim(void) {
PrintAndLogEx(NORMAL, "Simulating EM410x tag");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Usage: lf em 410x_sim [h] <uid> <clock>");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h - this help");
PrintAndLogEx(NORMAL, " uid - uid (10 HEX symbols)");
PrintAndLogEx(NORMAL, " clock - clock (32|64) (optional)");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_sim 0F0368568B"));
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_sim 0F0368568B 32"));
return PM3_SUCCESS;
}
static int usage_lf_em410x_brute(void) {
PrintAndLogEx(NORMAL, "Bruteforcing by emulating EM410x tag");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(NORMAL, "Usage: lf em 410x_brute [h] ids.txt [d 2000] [c clock]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " h - this help");
PrintAndLogEx(NORMAL, " ids.txt - file with UIDs in HEX format, one per line");
PrintAndLogEx(NORMAL, " d (2000) - pause delay in milliseconds between UIDs simulation, default 1000 ms (optional)");
PrintAndLogEx(NORMAL, " c (32) - clock (32|64), default 64 (optional)");
PrintAndLogEx(NORMAL, "Examples:");
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt"));
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt c 32"));
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt d 3000"));
PrintAndLogEx(NORMAL, _YELLOW_(" lf em 410x_brute ids.txt d 3000 c 32"));
return PM3_SUCCESS;
}
/* Read the ID of an EM410x tag.
* Format:
* 1111 1111 1 <-- standard non-repeatable header
@ -129,63 +48,62 @@ static int usage_lf_em410x_brute(void) {
*/
// Construct the graph for emulating an EM410X tag
static void ConstructEM410xEmulGraph(const char *uid, const uint8_t clock) {
static void em410x_construct_emul_graph(uint8_t *uid, uint8_t clock) {
int i, j, binary[4], parity[4];
uint32_t n;
/* clear our graph */
// clear our graph
ClearGraph(true);
/* write 16 zero bit sledge */
for (i = 0; i < 20; i++)
// write 16 zero bit sledge
for (uint8_t i = 0; i < 20; i++)
AppendGraph(false, clock, 0);
/* write 9 start bits */
for (i = 0; i < 9; i++)
// write 9 start bits
for (uint8_t i = 0; i < 9; i++)
AppendGraph(false, clock, 1);
/* for each hex char */
parity[0] = parity[1] = parity[2] = parity[3] = 0;
for (i = 0; i < 10; i++) {
/* read each hex char */
sscanf(&uid[i], "%1x", &n);
for (j = 3; j >= 0; j--, n /= 2)
binary[j] = n % 2;
uint8_t bs[8], parity[8];
memset(parity, 0, sizeof(parity));
/* append each bit */
AppendGraph(false, clock, binary[0]);
AppendGraph(false, clock, binary[1]);
AppendGraph(false, clock, binary[2]);
AppendGraph(false, clock, binary[3]);
for (uint8_t i = 0; i < 5; i++) {
/* append parity bit */
AppendGraph(false, clock, binary[0] ^ binary[1] ^ binary[2] ^ binary[3]);
for (uint8_t j = 0; j < 8; j++) {
bs[j] = (uid[i] >> (7 - j) & 1);
}
PrintAndLogEx(DEBUG, "uid[%d] 0x%02x (%s)", i, uid[i], sprint_bin(bs, 4));
/* keep track of column parity */
parity[0] ^= binary[0];
parity[1] ^= binary[1];
parity[2] ^= binary[2];
parity[3] ^= binary[3];
for (uint8_t j = 0; j < 2; j++) {
// append each bit
AppendGraph(false, clock, bs[0 + (4 * j)]);
AppendGraph(false, clock, bs[1 + (4 * j)]);
AppendGraph(false, clock, bs[2 + (4 * j)]);
AppendGraph(false, clock, bs[3 + (4 * j)]);
// append parity bit
AppendGraph(false, clock, bs[0 + (4 * j)] ^ bs[1 + (4 * j)] ^ bs[2 + (4 * j)] ^ bs[3 + (4 * j)]);
// keep track of column parity
parity[0] ^= bs[0 + (4 * j)];
parity[1] ^= bs[1 + (4 * j)];
parity[2] ^= bs[2 + (4 * j)];
parity[3] ^= bs[3 + (4 * j)];
}
}
/* parity columns */
// parity columns
AppendGraph(false, clock, parity[0]);
AppendGraph(false, clock, parity[1]);
AppendGraph(false, clock, parity[2]);
AppendGraph(false, clock, parity[3]);
/* stop bit */
// stop bit
AppendGraph(true, clock, 0);
}
//by marshmellow
//print 64 bit EM410x ID in multiple formats
void printEM410x(uint32_t hi, uint64_t id) {
void printEM410x(uint32_t hi, uint64_t id, bool verbose) {
if (!id && !hi) return;
PrintAndLogEx(SUCCESS, "EM410x%s pattern found", (hi) ? " XL" : "");
uint64_t n = 1;
uint64_t id2lo = 0;
uint8_t m, i;
@ -195,26 +113,36 @@ void printEM410x(uint32_t hi, uint64_t id) {
}
}
if (verbose == false) {
if (hi) {
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id);
} else {
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
}
return;
}
if (hi) {
//output 88 bit em id
PrintAndLogEx(NORMAL, "\nEM TAG ID : "_YELLOW_("%06X%016" PRIX64), hi, id);
PrintAndLogEx(NORMAL, "Clock rate : "_YELLOW_("RF/%d"), g_DemodClock);
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id);
PrintAndLogEx(SUCCESS, "EM410x XL ( RF/%d )", g_DemodClock);
} else {
//output 40 bit em id
PrintAndLogEx(NORMAL, "\nEM TAG ID : "_YELLOW_("%010" PRIX64), id);
PrintAndLogEx(NORMAL, "Clock rate : "_YELLOW_("RF/%d"), g_DemodClock);
PrintAndLogEx(NORMAL, "\nPossible de-scramble patterns\n");
PrintAndLogEx(NORMAL, "Unique TAG ID : %010" PRIX64, id2lo);
PrintAndLogEx(NORMAL, "HoneyWell IdentKey {");
PrintAndLogEx(NORMAL, "DEZ 8 : %08" PRIu64, id & 0xFFFFFF);
PrintAndLogEx(NORMAL, "DEZ 10 : %010" PRIu64, id & 0xFFFFFFFF);
PrintAndLogEx(NORMAL, "DEZ 5.5 : %05" PRIu64 ".%05" PRIu64, (id >> 16LL) & 0xFFFF, (id & 0xFFFF));
PrintAndLogEx(NORMAL, "DEZ 3.5A : %03" PRIu64 ".%05" PRIu64, (id >> 32ll), (id & 0xFFFF));
PrintAndLogEx(NORMAL, "DEZ 3.5B : %03" PRIu64 ".%05" PRIu64, (id & 0xFF000000) >> 24, (id & 0xFFFF));
PrintAndLogEx(NORMAL, "DEZ 3.5C : %03" PRIu64 ".%05" PRIu64, (id & 0xFF0000) >> 16, (id & 0xFFFF));
PrintAndLogEx(NORMAL, "DEZ 14/IK2 : %014" PRIu64, id);
PrintAndLogEx(NORMAL, "DEZ 15/IK3 : %015" PRIu64, id2lo);
PrintAndLogEx(NORMAL, "DEZ 20/ZK : %02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64,
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
PrintAndLogEx(SUCCESS, "EM410x ( RF/%d )", g_DemodClock);
PrintAndLogEx(INFO, "-------- " _CYAN_("Possible de-scramble patterns") " ---------");
PrintAndLogEx(SUCCESS, "Unique TAG ID : %010" PRIX64, id2lo);
PrintAndLogEx(INFO, "HoneyWell IdentKey");
PrintAndLogEx(SUCCESS, " DEZ 8 : %08" PRIu64, id & 0xFFFFFF);
PrintAndLogEx(SUCCESS, " DEZ 10 : %010" PRIu64, id & 0xFFFFFFFF);
PrintAndLogEx(SUCCESS, " DEZ 5.5 : %05" PRIu64 ".%05" PRIu64, (id >> 16LL) & 0xFFFF, (id & 0xFFFF));
PrintAndLogEx(SUCCESS, " DEZ 3.5A : %03" PRIu64 ".%05" PRIu64, (id >> 32ll), (id & 0xFFFF));
PrintAndLogEx(SUCCESS, " DEZ 3.5B : %03" PRIu64 ".%05" PRIu64, (id & 0xFF000000) >> 24, (id & 0xFFFF));
PrintAndLogEx(SUCCESS, " DEZ 3.5C : %03" PRIu64 ".%05" PRIu64, (id & 0xFF0000) >> 16, (id & 0xFFFF));
PrintAndLogEx(SUCCESS, " DEZ 14/IK2 : %014" PRIu64, id);
PrintAndLogEx(SUCCESS, " DEZ 15/IK3 : %015" PRIu64, id2lo);
PrintAndLogEx(SUCCESS, " DEZ 20/ZK : %02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64,
(id2lo & 0xf000000000) >> 36,
(id2lo & 0x0f00000000) >> 32,
(id2lo & 0x00f0000000) >> 28,
@ -226,9 +154,11 @@ void printEM410x(uint32_t hi, uint64_t id) {
(id2lo & 0x00000000f0) >> 4,
(id2lo & 0x000000000f)
);
PrintAndLogEx(INFO, "");
uint64_t paxton = (((id >> 32) << 24) | (id & 0xffffff)) + 0x143e00;
PrintAndLogEx(NORMAL, "}\nOther : %05" PRIu64 "_%03" PRIu64 "_%08" PRIu64, (id & 0xFFFF), ((id >> 16LL) & 0xFF), (id & 0xFFFFFF));
PrintAndLogEx(NORMAL, "Pattern Paxton : %" PRIu64 " [0x%" PRIX64 "]", paxton, paxton);
PrintAndLogEx(SUCCESS, "Other : %05" PRIu64 "_%03" PRIu64 "_%08" PRIu64, (id & 0xFFFF), ((id >> 16LL) & 0xFF), (id & 0xFFFFFF));
PrintAndLogEx(SUCCESS, "Pattern Paxton : %" PRIu64 " [0x%" PRIX64 "]", paxton, paxton);
uint32_t p1id = (id & 0xFFFFFF);
uint8_t arr[32] = {0x00};
@ -268,12 +198,13 @@ void printEM410x(uint32_t hi, uint64_t id) {
p1 |= arr[2] << 4;
p1 |= arr[1] << 5;
p1 |= arr[0] << 9;
PrintAndLogEx(NORMAL, "Pattern 1 : %d [0x%X]", p1, p1);
PrintAndLogEx(SUCCESS, "Pattern 1 : %d [0x%X]", p1, p1);
uint16_t sebury1 = id & 0xFFFF;
uint8_t sebury2 = (id >> 16) & 0x7F;
uint32_t sebury3 = id & 0x7FFFFF;
PrintAndLogEx(NORMAL, "Pattern Sebury : %d %d %d [0x%X 0x%X 0x%X]", sebury1, sebury2, sebury3, sebury1, sebury2, sebury3);
PrintAndLogEx(SUCCESS, "Pattern Sebury : %d %d %d [0x%X 0x%X 0x%X]", sebury1, sebury2, sebury3, sebury1, sebury2, sebury3);
PrintAndLogEx(INFO, "------------------------------------------------");
}
}
/* Read the ID of an EM410x tag.
@ -321,9 +252,8 @@ int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo) {
printDemodBuff(0, false, false, true);
}
if (verbose)
printEM410x(*hi, *lo);
printEM410x(*hi, *lo, verbose);
g_em410xid = *lo;
return PM3_SUCCESS;
}
@ -342,8 +272,19 @@ int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify,
// this read loops on device side.
// uses the demod in lfops.c
static int CmdEM410xWatch(const char *Cmd) {
uint8_t c = tolower(param_getchar(Cmd, 0));
if (c == 'h') return usage_lf_em410x_watch();
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x watch",
"Enables Electro Marine (EM) compatible reader mode printing details of scanned tags.\n"
"Run until the button is pressed or another USB command is issued.",
"lf em 410x watch"
);
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
PrintAndLogEx(SUCCESS, "Watching for EM410x cards - place tag on antenna");
PrintAndLogEx(INFO, "Press pm3-button to stop reading cards");
@ -367,111 +308,189 @@ int demodEM410x(bool verbose) {
}
static int CmdEM410xDemod(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (strlen(Cmd) > 10 || cmdp == 'h') return usage_lf_em410x_demod();
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x demod",
"Try to find EM 410x preamble, if found decode / descramble data",
"lf em 410x demod -> demod an EM410x Tag ID from GraphBuffer\n"
"lf em 410x demod --clk 32 -> demod an EM410x Tag ID from GraphBuffer using a clock of RF/32\n"
"lf em 410x demod --clk 32 -i -> demod an EM410x Tag ID from GraphBuffer using a clock of RF/32 and inverting data\n"
"lf em 410x demod -i -> demod an EM410x Tag ID from GraphBuffer while inverting data\n"
"lf em 410x demod --clk 64 -i --err 0 -> demod an EM410x Tag ID from GraphBuffer using a clock of RF/64 and inverting data and allowing 0 demod errors"
);
void *argtable[] = {
arg_param_begin,
arg_u64_0(NULL, "clk", "<dec>", "optional - clock (default autodetect)"),
arg_u64_0(NULL, "err", "<dec>", "optional - maximum allowed errors (default 100)"),
arg_u64_0(NULL, "len", "<dec>", "optional - maximum length"),
arg_lit0("i", "invert", "optional - invert output"),
arg_lit0("a", "amp", "optional - amplify signal"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int clk = arg_get_u32_def(ctx, 1, 0);
int max_err = arg_get_u32_def(ctx, 2, 100);
size_t max_len = arg_get_u32_def(ctx, 3, 0);
bool invert = arg_get_lit(ctx, 4);
bool amplify = arg_get_lit(ctx, 5);
CLIParserFree(ctx);
uint32_t hi = 0;
uint64_t lo = 0;
int clk = 0;
int invert = 0;
int maxErr = 100;
size_t maxLen = 0;
char amp = tolower(param_getchar(Cmd, 0));
sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &amp);
bool amplify = amp == 'a';
if (AskEm410xDemod(clk, invert, maxErr, maxLen, amplify, &hi, &lo, true) != PM3_SUCCESS)
if (AskEm410xDemod(clk, invert, max_err, max_len, amplify, &hi, &lo, true) != PM3_SUCCESS)
return PM3_ESOFT;
g_em410xid = lo;
return PM3_SUCCESS;
}
// this read is the "normal" read, which download lf signal and tries to demod here.
static int CmdEM410xRead(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (strlen(Cmd) > 10 || cmdp == 'h') return usage_lf_em410x_demod();
static int CmdEM410xReader(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x reader",
"read EM 410x tag",
"lf em 410x reader -> reader\n"
"lf em 410x reader -@ -> continuous reader mode\n"
"lf em 410x reader --clk 32 -> reader using a clock of RF/32\n"
"lf em 410x reader --clk 32 -i -> reader using a clock of RF/32 and inverting data\n"
"lf em 410x reader -i -> reader while inverting data\n"
"lf em 410x reader --clk 64 -i --err 0 -> reader using a clock of RF/64 and inverting data and allowing 0 demod errors"
);
void *argtable[] = {
arg_param_begin,
arg_u64_0(NULL, "clk", "<dec>", "optional - clock (default autodetect)"),
arg_u64_0(NULL, "err", "<dec>", "optional - maximum allowed errors (default 100)"),
arg_u64_0(NULL, "len", "<dec>", "optional - maximum length"),
arg_lit0("i", "invert", "optional - invert output"),
arg_lit0("a", "amp", "optional - amplify signal"),
arg_lit0("@", NULL, "optional - continuous reader mode"),
arg_lit0("v", "verbose", "verbose output"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
int clk = arg_get_u32_def(ctx, 1, 0);
int max_err = arg_get_u32_def(ctx, 2, 100);
size_t max_len = arg_get_u32_def(ctx, 3, 0);
bool invert = arg_get_lit(ctx, 4);
bool amplify = arg_get_lit(ctx, 5);
bool cm = arg_get_lit(ctx, 6);
bool verbose = arg_get_lit(ctx, 7);
CLIParserFree(ctx);
if (cm) {
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
}
do {
uint32_t hi = 0;
uint64_t lo = 0;
int clk = 0;
int invert = 0;
int maxErr = 100;
size_t maxLen = 0;
char amp = tolower(param_getchar(Cmd, 0));
sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &amp);
bool amplify = amp == 'a';
lf_read(false, 12288);
return AskEm410xDemod(clk, invert, maxErr, maxLen, amplify, &hi, &lo, true);
AskEm410xDemod(clk, invert, max_err, max_len, amplify, &hi, &lo, verbose);
} while (cm && !kbd_enter_pressed());
return PM3_SUCCESS;
}
// emulate an EM410X tag
static int CmdEM410xSim(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_lf_em410x_sim();
uint8_t uid[5] = {0x00};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x sim",
"Enables simulation of EM 410x card.\n"
"Simulation runs until the button is pressed or another USB command is issued.",
"lf em 410x sim --id 0F0368568B\n"
"lf em 410x sim --id 0F0368568B --clk 32"
);
/* clock is 64 in EM410x tags */
uint8_t clk = 64;
void *argtable[] = {
arg_param_begin,
arg_u64_0(NULL, "clk", "<dec>", "optional - clock [32|64] (default 64)"),
arg_str1("i", "id", "<hex>", "ID number (5 hex bytes)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
if (param_gethex(Cmd, 0, uid, 10)) {
PrintAndLogEx(FAILED, "UID must include 10 HEX symbols");
// clock is 64 in EM410x tags
int clk = arg_get_u32_def(ctx, 1, 64);
int uid_len = 0;
uint8_t uid[5] = {0};
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
CLIParserFree(ctx);
if (uid_len != 5) {
PrintAndLogEx(FAILED, "UID must include 5 hex bytes (%u)", uid_len);
return PM3_EINVARG;
}
param_getdec(Cmd, 1, &clk);
PrintAndLogEx(SUCCESS, "Starting simulating UID "_YELLOW_("%02X%02X%02X%02X%02X")" clock: "_YELLOW_("%d"), uid[0], uid[1], uid[2], uid[3], uid[4], clk);
PrintAndLogEx(SUCCESS, "Starting simulating UID "_YELLOW_("%s")" clock: "_YELLOW_("%d"), sprint_hex_inrow(uid, sizeof(uid)), clk);
PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation");
ConstructEM410xEmulGraph(Cmd, clk);
em410x_construct_emul_graph(uid, clk);
CmdLFSim("0"); //240 start_gap.
CmdLFSim("0"); // 240 start_gap.
return PM3_SUCCESS;
}
static int CmdEM410xBrute(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x brute",
"bruteforcing by emulating EM 410x tag",
"lf em 410x brute -f ids.txt\n"
"lf em 410x brute -f ids.txt --clk 32\n"
"lf em 410x brute -f ids.txt --delay 3000\n"
"lf em 410x brute -f ids.txt --delay 3000 --clk 32\n"
);
void *argtable[] = {
arg_param_begin,
arg_u64_1(NULL, "clk", "<dec>", "optional - clock [32|64] (default 64)"),
arg_u64_1(NULL, "delay", "<dec>", "optional - pause delay in milliseconds between UIDs simulation (default 1000ms)"),
arg_str1("f", "file", "<hex>", "file with UIDs in HEX format, one per line"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
// clock default 64 in EM410x
uint32_t clk = arg_get_u32_def(ctx, 1, 64);
// default pause time: 1 second
uint32_t delay = arg_get_u32_def(ctx, 2, 1000);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
FILE *f = NULL;
char buf[11];
uint32_t uidcnt = 0;
uint8_t stUidBlock = 20;
uint8_t *uidBlock = NULL, *p = NULL;
uint8_t uid[5] = {0x00};
/* clock is 64 in EM410x tags */
uint8_t clock1 = 64;
/* default pause time: 1 second */
uint32_t delay = 1000;
CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx);
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_lf_em410x_brute();
cmdp = tolower(param_getchar(Cmd, 1));
if (cmdp == 'd') {
delay = param_get32ex(Cmd, 2, 1000, 10);
param_getdec(Cmd, 4, &clock1);
} else if (cmdp == 'c') {
param_getdec(Cmd, 2, &clock1);
delay = param_get32ex(Cmd, 4, 1000, 10);
}
int filelen = param_getstr(Cmd, 0, filename, FILE_PATH_SIZE);
if (filelen == 0) {
if (fnlen == 0) {
PrintAndLogEx(ERR, "Error: Please specify a filename");
return PM3_EINVARG;
}
uint32_t uidcnt = 0;
uint8_t stUidBlock = 20;
uint8_t *p = NULL;
uint8_t uid[5] = {0x00};
// open file
FILE *f = NULL;
if ((f = fopen(filename, "r")) == NULL) {
PrintAndLogEx(ERR, "Error: Could not open UIDs file ["_YELLOW_("%s")"]", filename);
return PM3_EFILE;
}
uidBlock = calloc(stUidBlock, 5);
if (uidBlock == NULL) {
// allocate mem for file contents
uint8_t *uidblock = calloc(stUidBlock, 5);
if (uidblock == NULL) {
fclose(f);
return PM3_ESOFT;
PrintAndLogEx(ERR, "Error: can't allocate memory");
return PM3_EMALLOC;
}
// read file into memory
char buf[11];
while (fgets(buf, sizeof(buf), f)) {
if (strlen(buf) < 10 || buf[9] == '\n') continue;
while (fgetc(f) != '\n' && !feof(f)); //goto next line
@ -481,7 +500,7 @@ static int CmdEM410xBrute(const char *Cmd) {
if (param_gethex(buf, 0, uid, 10)) {
PrintAndLogEx(FAILED, "UIDs must include 10 HEX symbols");
free(uidBlock);
free(uidblock);
fclose(f);
return PM3_ESOFT;
}
@ -489,109 +508,118 @@ static int CmdEM410xBrute(const char *Cmd) {
buf[10] = 0;
if (stUidBlock - uidcnt < 2) {
p = realloc(uidBlock, 5 * (stUidBlock += 10));
p = realloc(uidblock, 5 * (stUidBlock += 10));
if (!p) {
PrintAndLogEx(WARNING, "Cannot allocate memory for UIDs");
free(uidBlock);
free(uidblock);
fclose(f);
return PM3_ESOFT;
}
uidBlock = p;
uidblock = p;
}
memset(uidBlock + 5 * uidcnt, 0, 5);
num_to_bytes(strtoll(buf, NULL, 16), 5, uidBlock + 5 * uidcnt);
memset(uidblock + 5 * uidcnt, 0, 5);
num_to_bytes(strtoll(buf, NULL, 16), 5, uidblock + 5 * uidcnt);
uidcnt++;
memset(buf, 0, sizeof(buf));
}
fclose(f);
if (uidcnt == 0) {
PrintAndLogEx(FAILED, "No UIDs found in file");
free(uidBlock);
free(uidblock);
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "Loaded "_YELLOW_("%d")" UIDs from "_YELLOW_("%s")", pause delay:"_YELLOW_("%d")" ms", uidcnt, filename, delay);
// loop
uint8_t testuid[5];
for (uint32_t c = 0; c < uidcnt; ++c) {
char testuid[11];
testuid[10] = 0;
if (kbd_enter_pressed()) {
PrintAndLogEx(WARNING, "\nAborted via keyboard!\n");
free(uidBlock);
free(uidblock);
return PM3_EOPABORTED;
}
sprintf(testuid, "%010" PRIX64, bytes_to_num(uidBlock + 5 * c, 5));
PrintAndLogEx(NORMAL, "Bruteforce %d / %d: simulating UID %s, clock %d", c + 1, uidcnt, testuid, clock1);
memcpy(testuid, uidblock + 5 * c, 5);
PrintAndLogEx(INFO, "Bruteforce %d / %d: simulating UID " _YELLOW_("%s")
, c + 1
, uidcnt
, sprint_hex_inrow(testuid, sizeof(testuid))
);
ConstructEM410xEmulGraph(testuid, clock1);
em410x_construct_emul_graph(testuid, clk);
CmdLFSim("0"); //240 start_gap.
msleep(delay);
}
free(uidBlock);
free(uidblock);
return PM3_SUCCESS;
}
//currently only supports manchester modulations
static int CmdEM410xWatchnSpoof(const char *Cmd) {
static int CmdEM410xSpoof(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x spoof",
"Watch 'nd Spoof, activates reader\n"
"Waits until a EM 410x tag gets presented then Proxmark3 starts simulating the found UID",
"lf em 410x spoof"
);
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 'h') return usage_lf_em410x_ws();
void *argtable[] = {
arg_param_begin,
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx);
// loops if the captured ID was in XL-format.
CmdEM410xWatch(Cmd);
CmdEM410xReader("-@");
PrintAndLogEx(SUCCESS, "# Replaying captured ID: "_YELLOW_("%010" PRIx64), g_em410xid);
CmdLFaskSim("");
return PM3_SUCCESS;
}
static int CmdEM410xClone(const char *Cmd) {
char cmdp = tolower(param_getchar(Cmd, 0));
if (cmdp == 0x00 || cmdp == 'h') return usage_lf_em410x_clone();
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x clone",
"Writes EM410x ID to a T55x7 or Q5/T5555 tag",
"lf em 410x clone --id 0F0368568B -> write id to T55x7 tag\n"
"lf em 410x clone --id 0F0368568B --q5 -> write id to Q5/T5555 tag"
);
uint64_t id = param_get64ex(Cmd, 0, -1, 16);
uint8_t card = param_get8ex(Cmd, 1, 0xFF, 10);
uint8_t clock1 = param_get8ex(Cmd, 2, 0, 10);
void *argtable[] = {
arg_param_begin,
arg_u64_0(NULL, "clk", "<dec>", "optional - clock <16|32|40|64> (default 64)"),
arg_str1("u", "uid", "<hex>", "ID number (5 hex bytes)"),
arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
// Check ID
if (id == 0xFFFFFFFFFFFFFFFF) {
PrintAndLogEx(ERR, "error, ID is required\n");
usage_lf_em410x_clone();
return PM3_EINVARG;
}
if (id >= 0x10000000000) {
PrintAndLogEx(ERR, "error, given EM410x ID is longer than 40 bits\n");
usage_lf_em410x_clone();
return PM3_EINVARG;
}
// clock default 64 in EM410x
uint32_t clk = arg_get_u32_def(ctx, 1, 64);
int uid_len = 0;
uint8_t uid[5] = {0};
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
bool q5 = arg_get_lit(ctx, 3);
CLIParserFree(ctx);
// Check Card
if (card > 1) {
PrintAndLogEx(FAILED, "error, bad card type selected\n");
usage_lf_em410x_clone();
return PM3_EINVARG;
}
// Check Clock
if (clock1 == 0)
clock1 = 64;
uint64_t id = bytes_to_num(uid, uid_len);
// Allowed clock rates: 16, 32, 40 and 64
if ((clock1 != 16) && (clock1 != 32) && (clock1 != 64) && (clock1 != 40)) {
PrintAndLogEx(FAILED, "error, clock rate" _RED_("%d")" not valid", clock1);
PrintAndLogEx(INFO, "supported clock rates: " _YELLOW_("16, 32, 40, 60") "\n");
usage_lf_em410x_clone();
if ((clk != 16) && (clk != 32) && (clk != 64) && (clk != 40)) {
PrintAndLogEx(FAILED, "supported clock rates are " _YELLOW_("16, 32, 40, 64") " got " _RED_("%d") "\n", clk);
return PM3_EINVARG;
}
PrintAndLogEx(SUCCESS, "Writing " _YELLOW_("%s") " tag with UID 0x%010" PRIx64 " (clock rate: %d)", (card == 1) ? "T55x7" : "Q5/T5555", id, clock1);
char cardtype[16] = {"T55x7"};
if (q5) {
snprintf(cardtype, sizeof(cardtype), "Q5/T5555");
}
PrintAndLogEx(SUCCESS, "Preparing to clone EM4102 to " _YELLOW_("%s") " tag with ID " _GREEN_("%010" PRIX64) " (RF/%d)", cardtype, id, clk);
// NOTE: We really should pass the clock in as a separate argument, but to
// provide for backwards-compatibility for older firmware, and to avoid
// having to add another argument to CMD_LF_EM410X_WRITE, we just store
@ -604,8 +632,8 @@ static int CmdEM410xClone(const char *Cmd) {
uint32_t low;
} PACKED params;
params.card = card;
params.clock = clock1;
params.card = (q5) ? 0 : 1;
params.clock = clk;
params.high = (uint32_t)(id >> 32);
params.low = (uint32_t)id;
@ -617,7 +645,7 @@ static int CmdEM410xClone(const char *Cmd) {
switch (resp.status) {
case PM3_SUCCESS: {
PrintAndLogEx(SUCCESS, "Done");
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 410x_read`") " to verify");
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf em 410x reader`") " to verify");
break;
}
default: {
@ -632,11 +660,11 @@ static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
//{"demod", CmdEMdemodASK, IfPm3Lf, "Extract ID from EM410x tag on antenna)"},
{"demod", CmdEM410xDemod, AlwaysAvailable, "demodulate a EM410x tag from the GraphBuffer"},
{"read", CmdEM410xRead, IfPm3Lf, "attempt to read and extract tag data"},
{"reader", CmdEM410xReader, IfPm3Lf, "attempt to read and extract tag data"},
{"sim", CmdEM410xSim, IfPm3Lf, "simulate EM410x tag"},
{"brute", CmdEM410xBrute, IfPm3Lf, "reader bruteforce attack by simulating EM410x tags"},
{"watch", CmdEM410xWatch, IfPm3Lf, "watches for EM410x 125/134 kHz tags (option 'h' for 134)"},
{"spoof", CmdEM410xWatchnSpoof, IfPm3Lf, "watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" },
{"spoof", CmdEM410xSpoof, IfPm3Lf, "watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" },
{"clone", CmdEM410xClone, IfPm3Lf, "write EM410x UID to T55x7 or Q5/T5555 tag"},
{NULL, NULL, NULL, NULL}
};

View file

@ -16,7 +16,7 @@
int CmdLFEM410X(const char *Cmd);
int demodEM410x(bool verbose);
void printEM410x(uint32_t hi, uint64_t id);
void printEM410x(uint32_t hi, uint64_t id, bool verbose);
int AskEm410xDecode(bool verbose, uint32_t *hi, uint64_t *lo);
int AskEm410xDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, uint32_t *hi, uint64_t *lo, bool verbose);

File diff suppressed because it is too large Load diff

View file

@ -11,20 +11,27 @@
#ifndef CMDLFEM4X50_H__
#define CMDLFEM4X50_H__
#include"common.h"
#include "em4x50.h"
int CmdLFEM4X50(const char *Cmd);
int read_em4x50_uid(void);
bool detect_4x50_block(void);
int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out, bool verbose);
int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out);
int CmdEM4x50Info(const char *Cmd);
int CmdEM4x50Write(const char *Cmd);
int CmdEM4x50WritePassword(const char *Cmd);
int CmdEM4x50WritePwd(const char *Cmd);
int CmdEM4x50Read(const char *Cmd);
int CmdEM4x50Dump(const char *Cmd);
int CmdEM4x50Wipe(const char *Cmd);
int CmdEM4x50Brute(const char *Cmd);
int CmdEM4x50Login(const char *Cmd);
int CmdEM4x50Restore(const char *Cmd);
int CmdEM4x50Sim(const char *Cmd);
int CmdEM4x50Reader(const char *Cmd);
int CmdEM4x50ELoad(const char *Cmd);
int CmdEM4x50ESave(const char *Cmd);
int CmdEM4x50Chk(const char *Cmd);
#endif

132
client/src/cmdlfem4x70.c Normal file
View file

@ -0,0 +1,132 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 sirloins
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4x70 commands
//-----------------------------------------------------------------------------
#include "cmdlfem4x70.h"
#include <ctype.h>
#include "cmdparser.h" // command_t
#include "cliparser.h"
#include "fileutils.h"
#include "commonutil.h"
#include "em4x70.h"
static int CmdHelp(const char *Cmd);
static void print_info_result(uint8_t *data) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
PrintAndLogEx(INFO, "-------------------------------------------------------------");
// data section
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, _YELLOW_("EM4x70 data:"));
for(int i=1; i <= 32; i+=2) {
PrintAndLogEx(NORMAL, "%02X %02X", data[32-i], data[32-i-1]);
}
PrintAndLogEx(NORMAL, "Tag ID: %02X %02X %02X %02X", data[7], data[6], data[5], data[4]);
PrintAndLogEx(NORMAL, "Lockbit 0: %d", (data[3] & 0x40) ? 1:0);
PrintAndLogEx(NORMAL, "Lockbit 1: %d", (data[3] & 0x80) ? 1:0);
PrintAndLogEx(NORMAL, "");
}
int em4x70_info(void) {
em4x70_data_t edata = {
.parity = false // TODO: try both? or default to true
};
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(edata));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "(em4x70) timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status) {
print_info_result(resp.data.asBytes);
return PM3_SUCCESS;
}
return PM3_ESOFT;
}
//quick test for EM4x70 tag
bool detect_4x70_block(void) {
return em4x70_info() == PM3_SUCCESS;
}
int CmdEM4x70Info(const char *Cmd) {
// envoke reading of a EM4x70 tag which has to be on the antenna because
// decoding is done by the device (not on client side)
em4x70_data_t etd = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x10 info",
"Tag Information EM4x70\n"
" Tag variants include ID48 automotive transponder.\n"
" ID48 does not use command parity (default).\n"
" V4070 and EM4170 do require parity bit.",
"lf em 4x70 info\n"
"lf em 4x70 -p -> adds parity bit to commands\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("p", "parity", "Add parity bit when sending commands"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
etd.parity = arg_get_lit(ctx, 0);
CLIParserFree(ctx);
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status) {
print_info_result(resp.data.asBytes);
return PM3_SUCCESS;
}
PrintAndLogEx(FAILED, "reading tag " _RED_("failed"));
return PM3_ESOFT;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"info", CmdEM4x70Info, IfPm3EM4x70, "tag information EM4x70"},
{NULL, NULL, NULL, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdLFEM4X70(const char *Cmd) {
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}

25
client/src/cmdlfem4x70.h Normal file
View file

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 sirloins
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4x70 commands
//-----------------------------------------------------------------------------
#ifndef CMDLFEM4X70_H__
#define CMDLFEM4X70_H__
#include "common.h"
#include "em4x50.h"
#define TIMEOUT 2000
int CmdLFEM4X70(const char *Cmd);
int CmdEM4x70Info(const char *Cmd);
int em4x70_info(void);
bool detect_4x70_block(void);
#endif

View file

@ -196,12 +196,12 @@ int demodNexWatch(bool verbose) {
}
// output
PrintAndLogEx(SUCCESS, " NexWatch raw id : " _YELLOW_("0x%"PRIx32), rawid);
PrintAndLogEx(SUCCESS, " NexWatch raw id : " _YELLOW_("0x%08"PRIx32), rawid);
if (m_idx < ARRAYLEN(items)) {
PrintAndLogEx(SUCCESS, " fingerprint : " _GREEN_("%s"), items[m_idx].desc);
}
PrintAndLogEx(SUCCESS, " 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%"PRIx32)")", cn, cn);
PrintAndLogEx(SUCCESS, " 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%08"PRIx32)")", cn, cn);
PrintAndLogEx(SUCCESS, " mode : %x", mode);
if (parity == calc_parity) {
@ -212,7 +212,7 @@ int demodNexWatch(bool verbose) {
PrintAndLogEx(DEBUG, " checksum : %s (0x%02X)", (m_idx < ARRAYLEN(items)) ? _GREEN_("ok") : _RED_("fail"), chk);
PrintAndLogEx(INFO, " Raw : " _YELLOW_("%"PRIX32"%"PRIX32"%"PRIX32), raw1, raw2, raw3);
PrintAndLogEx(INFO, " Raw : " _YELLOW_("%08"PRIX32"%08"PRIX32"%08"PRIX32), raw1, raw2, raw3);
return PM3_SUCCESS;
}

View file

@ -51,6 +51,7 @@ t55xx_conf_block_t config = {
.inverted = false,
.offset = 0x00,
.block0 = 0x00,
.block0Status = notSet,
.Q5 = false,
.usepwd = false,
.downlink_mode = refFixedBit
@ -739,6 +740,7 @@ static int CmdT55xxSetConfig(const char *Cmd) {
for (; i < 9; i++) {
if (rates[i] == bitRate) {
config.bitrate = i;
config.block0 = ((config.block0 & ~(0x1c0000)) | (i << 18));
break;
}
}
@ -789,6 +791,7 @@ static int CmdT55xxSetConfig(const char *Cmd) {
PrintAndLogEx(WARNING, "Unknown modulation '%s'", modulation);
errors = true;
}
config.block0 = ((config.block0 & ~(0x1f000)) | (config.modulation << 12));
break;
case 'i':
if ((param_getchar(Cmd, cmdp + 1) == '0') || (param_getchar(Cmd, cmdp + 1) == '1')) {
@ -822,6 +825,7 @@ static int CmdT55xxSetConfig(const char *Cmd) {
config.ST = true;
cmdp += 1;
}
config.block0 = ((config.block0 & ~(0x8)) | (config.ST << 3));
break;
case 'r':
errors = param_getdec(Cmd, cmdp + 1, &downlink_mode);
@ -841,10 +845,9 @@ static int CmdT55xxSetConfig(const char *Cmd) {
//Validations
if (errors) return usage_t55xx_config();
config.block0Status = userSet;
if (gotconf) {
SetConfigWithBlock0Ex(block0, config.offset, config.Q5);
} else {
config.block0 = 0;
}
return printConfiguration(config);
@ -1335,6 +1338,7 @@ bool t55xxTryDetectModulationEx(uint8_t downlink_mode, bool print_config, uint32
config.pwd = pwd & 0xffffffff;
}
config.block0Status = autoDetect;
if (print_config)
printConfiguration(config);
@ -1370,6 +1374,7 @@ bool t55xxTryDetectModulationEx(uint8_t downlink_mode, bool print_config, uint32
PrintAndLogEx(NORMAL, "--[%d]---------------", i + 1);
}
config.block0Status = autoDetect;
if (print_config)
printConfiguration(tests[i]);
}
@ -1640,7 +1645,7 @@ int printConfiguration(t55xx_conf_block_t b) {
PrintAndLogEx(INFO, " Inverted : %s", (b.inverted) ? _GREEN_("Yes") : "No");
PrintAndLogEx(INFO, " Offset : %d", b.offset);
PrintAndLogEx(INFO, " Seq. Term. : %s", (b.ST) ? _GREEN_("Yes") : "No");
PrintAndLogEx(INFO, " Block0 : 0x%08X", b.block0);
PrintAndLogEx(INFO, " Block0 : 0x%08X %s", b.block0, GetConfigBlock0Source(b.block0Status));
PrintAndLogEx(INFO, " Downlink Mode : %s", GetDownlinkModeStr(b.downlink_mode));
PrintAndLogEx(INFO, " Password Set : %s", (b.usepwd) ? _RED_("Yes") : _GREEN_("No"));
if (b.usepwd) {
@ -2800,6 +2805,28 @@ char *GetModelStrFromCID(uint32_t cid) {
return buf;
}
char *GetConfigBlock0Source(uint8_t id) {
static char buf[40];
char *retStr = buf;
switch (id) {
case autoDetect:
snprintf(retStr, sizeof(buf), _YELLOW_("(Auto detect)"));
break;
case userSet:
snprintf(retStr, sizeof(buf), _YELLOW_("(User set)"));
break;
case tagRead:
snprintf(retStr, sizeof(buf), _GREEN_("(Tag read)"));
break;
default:
snprintf(retStr, sizeof(buf), _RED_("(Unknown)"));
break;
}
return buf;
}
char *GetSelectedModulationStr(uint8_t id) {
static char buf[20];

View file

@ -125,6 +125,12 @@ typedef struct {
bool inverted;
uint8_t offset;
uint32_t block0;
enum {
notSet = 0x00,
autoDetect = 0x01,
userSet = 0x02,
tagRead = 0x03,
} block0Status;
enum {
RF_8 = 0x00,
RF_16 = 0x01,
@ -166,6 +172,7 @@ char *GetSaferStr(uint32_t id);
char *GetQ5ModulationStr(uint32_t id);
char *GetModulationStr(uint32_t id, bool xmode);
char *GetModelStrFromCID(uint32_t cid);
char *GetConfigBlock0Source(uint8_t id);
char *GetSelectedModulationStr(uint8_t id);
char *GetDownlinkModeStr(uint8_t downlink_mode);
void printT5xxHeader(uint8_t page);

View file

@ -101,6 +101,13 @@ bool IfPm3EM4x50(void) {
return pm3_capabilities.compiled_with_em4x50;
}
bool IfPm3EM4x70(void) {
if (!IfPm3Present())
return false;
return pm3_capabilities.compiled_with_em4x70;
}
bool IfPm3Hfsniff(void) {
if (!IfPm3Present())
return false;

View file

@ -35,6 +35,7 @@ bool IfPm3FpcUsartFromUsb(void);
bool IfPm3Lf(void);
bool IfPm3Hitag(void);
bool IfPm3EM4x50(void);
bool IfPm3EM4x70(void);
bool IfPm3Hfsniff(void);
bool IfPm3Hfplot(void);
bool IfPm3Iso14443a(void);

View file

@ -123,7 +123,7 @@ int CmdWiegandDecode(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_lit0("p", "parity", "ignore invalid parity"),
arg_strx1(NULL, "raw", "<hex>", "raw hex to be decoded"),
arg_strx1("r", "raw", "<hex>", "raw hex to be decoded"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);

View file

@ -521,6 +521,20 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data,
}
break;
}
case jsfEM4x50: {
JsonSaveStr(root, "FileType", "EM4X50");
JsonSaveBufAsHexCompact(root, "$.Card.Protection", data + (1 * 4), 4);
JsonSaveBufAsHexCompact(root, "$.Card.Config", data + (2 * 4), 4);
JsonSaveBufAsHexCompact(root, "$.Card.Serial", data + (32 * 4), 4);
JsonSaveBufAsHexCompact(root, "$.Card.UID", data + (33 * 4), 4);
for (size_t i = 0; i < (datalen / 4); i++) {
char path[PATH_MAX_LENGTH] = {0};
sprintf(path, "$.blocks.%zu", i);
JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4);
}
break;
}
case jsfMfPlusKeys: {
JsonSaveStr(root, "FileType", "mfp");
JsonSaveBufAsHexCompact(root, "$.Card.UID", &data[0], 7);
@ -1139,6 +1153,27 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz
*datalen = sptr;
}
if (!strcmp(ctype, "EM4X50")) {
size_t sptr = 0;
for (size_t i = 0; i < (maxdatalen / 4); i++) {
if (sptr + 4 > maxdatalen) {
retval = PM3_EMALLOC;
goto out;
}
char blocks[30] = {0};
sprintf(blocks, "$.blocks.%zu", i);
size_t len = 0;
JsonLoadBufAsHex(root, blocks, &udata[sptr], 4, &len);
if (!len)
break;
sptr += len;
}
*datalen = sptr;
}
out:
if (callback != NULL) {

View file

@ -66,6 +66,7 @@ typedef enum {
jsfMfDesfireKeys,
jsfEM4x05,
jsfEM4x69,
jsfEM4x50,
} JSONFileType;
typedef enum {

View file

@ -1146,8 +1146,7 @@ static int l_em4x50_read(lua_State *L) {
em4x50_data_t etd;
memset(&etd, 0x00, sizeof(em4x50_data_t));
etd.addr_given = true;
etd.address = addr & 0xFF;
etd.newpwd_given = false;
etd.addresses = addr & 0xFF;
// get password
const char *p_pwd = luaL_checkstring(L, 2);
@ -1162,31 +1161,29 @@ static int l_em4x50_read(lua_State *L) {
PrintAndLogEx(DEBUG, " Pwd %08X", pwd);
etd.password[0] = pwd & 0xFF;
etd.password[1] = (pwd >> 8) & 0xFF;
etd.password[2] = (pwd >> 16) & 0xFF;
etd.password[3] = (pwd >> 24) & 0xFF;
etd.password1 = pwd;
etd.pwd_given = true;
}
PrintAndLogEx(DEBUG, "Addr %u", etd.address);
PrintAndLogEx(DEBUG, "Addr %u", etd.addresses & 0xFF);
if (etd.pwd_given)
PrintAndLogEx(DEBUG, " Pwd %s", sprint_hex(etd.password, sizeof(etd.password)));
PrintAndLogEx(DEBUG, " Pwd %08x", etd.password1);
em4x50_word_t words[EM4X50_NO_WORDS];
int res = em4x50_read(&etd, words, false);
int res = em4x50_read(&etd, words);
if (res != PM3_SUCCESS) {
return returnToLuaWithError(L, "Failed to read EM4x50 data");
}
uint32_t word = (
words[etd.address].byte[0] << 24 |
words[etd.address].byte[1] << 16 |
words[etd.address].byte[2] << 8 |
words[etd.address].byte[3]
words[etd.addresses & 0xFF].byte[0] << 24 |
words[etd.addresses & 0xFF].byte[1] << 16 |
words[etd.addresses & 0xFF].byte[2] << 8 |
words[etd.addresses & 0xFF].byte[3]
);
lua_pushinteger(L, word);
return 1;
}

View file

@ -97,6 +97,22 @@ uint16_t reflect16(uint16_t b) {
return v;
}
uint32_t reflect32(uint32_t b) {
// https://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable
uint32_t v = b; // 32-bit word to reverse bit order
// swap odd and even bits
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1);
// swap consecutive pairs
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2);
// swap nibbles ...
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
// swap bytes
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs
v = ( v >> 16 ) | ( v << 16);
return v;
}
void num_to_bytes(uint64_t n, size_t len, uint8_t *dest) {
while (len--) {
dest[len] = (uint8_t) n;

View file

@ -47,6 +47,7 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers
uint32_t reflect(uint32_t v, int b); // used in crc.c ...
uint8_t reflect8(uint8_t b); // dedicated 8bit reversal
uint16_t reflect16(uint16_t b); // dedicated 16bit reversal
uint32_t reflect32(uint32_t b); // dedicated 32bit reversal
void num_to_bytes(uint64_t n, size_t len, uint8_t *dest);
uint64_t bytes_to_num(uint8_t *src, size_t len);

View file

@ -105,6 +105,9 @@ endif
ifneq ($(SKIP_EM4x50),1)
PLATFORM_DEFS += -DWITH_EM4x50
endif
ifneq ($(SKIP_EM4x70),1)
PLATFORM_DEFS += -DWITH_EM4x70
endif
# common HF support
ifneq ($(SKIP_ISO15693),1)

View file

@ -141,13 +141,6 @@ hf mfu otptear
hf mfdes enum
hf mfdes getuid
hf mfdes info
hf thinfilm info
hf thinfilm sim
hf topaz info
hf topaz reader
hf topaz sim
hf topaz sniff
hf topaz raw
hw connect
hw dbg
hw detectreader
@ -174,11 +167,14 @@ lf simfsk
lf simpsk
lf simbidir
lf sniff
lf tune
lf em 410x
lf em 4x05
lf em 4x50
lf hitag info
lf hitag reader
lf hitag sim
lf hitag sniff
lf hitag writer
lf hitag dump
lf hitag cc

View file

@ -580,6 +580,7 @@ Check column "offline" for their availability.
|`lf em 410x `|Y |`EM 410x commands...`
|`lf em 4x05 `|Y |`EM 4x05 commands...`
|`lf em 4x50 `|Y |`EM 4x50 commands...`
|`lf em 4x70 `|Y |`EM 4x70 commands...`
### lf fdxb

View file

@ -34,32 +34,20 @@
#define LAST_WORD_WRITE_INHIBITED 3 // fourth byte
// misc
#define STATUS_NO_WORDS 0xfc
#define STATUS_SUCCESS 0x2
#define STATUS_LOGIN 0x1
#define NO_CHARS_MAX 400
#define TIMEOUT 2000
#define DUMP_FILESIZE 136
typedef struct {
bool addr_given;
bool pwd_given;
bool newpwd_given;
uint8_t password[4];
uint8_t new_password[4];
uint8_t addresses[4];
uint8_t address;
uint8_t word[4];
uint32_t password1;
uint32_t password2;
uint32_t word;
uint32_t addresses;
} PACKED em4x50_data_t;
typedef struct {
uint8_t byte[4];
uint8_t row_parity[4];
uint8_t col_parity;
uint8_t stopbit;
bool rparity[4];
bool cparity[8];
bool stopparity;
bool parity;
} PACKED em4x50_word_t;
#endif /* EM4X50_H__ */

18
include/em4x70.h Normal file
View file

@ -0,0 +1,18 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 sirloins
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Low frequency EM4x70 structs
//-----------------------------------------------------------------------------
#ifndef EM4X70_H__
#define EM4X70_H__
typedef struct {
bool parity;
} em4x70_data_t;
#endif /* EM4X70_H__ */

View file

@ -198,6 +198,7 @@ typedef struct {
bool compiled_with_lf : 1;
bool compiled_with_hitag : 1;
bool compiled_with_em4x50 : 1;
bool compiled_with_em4x70 : 1;
// hf
bool compiled_with_hfsniff : 1;
bool compiled_with_hfplot : 1;
@ -507,9 +508,15 @@ typedef struct {
#define CMD_LF_EM410X_WATCH 0x021C
#define CMD_LF_EM4X50_INFO 0x0240
#define CMD_LF_EM4X50_WRITE 0x0241
#define CMD_LF_EM4X50_WRITE_PASSWORD 0x0242
#define CMD_LF_EM4X50_WRITEPWD 0x0242
#define CMD_LF_EM4X50_READ 0x0243
#define CMD_LF_EM4X50_WIPE 0x0244
#define CMD_LF_EM4X50_BRUTE 0x0245
#define CMD_LF_EM4X50_LOGIN 0x0246
#define CMD_LF_EM4X50_SIM 0x0250
#define CMD_LF_EM4X50_READER 0x0251
#define CMD_LF_EM4X50_ESET 0x0252
#define CMD_LF_EM4X50_CHK 0x0253
#define CMD_LF_EM4X70_INFO 0x0260
// Sampling configuration for LF reader/sniffer
#define CMD_LF_SAMPLING_SET_CONFIG 0x021D
#define CMD_LF_FSK_SIMULATE 0x021E

View file

@ -160,8 +160,8 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MIFARE_MAGICWUPC1 0x40
#define MIFARE_MAGICWUPC2 0x43
#define MIFARE_MAGICWIPEC 0x41
#define MIFARE_CMD_INC 0xC0
#define MIFARE_CMD_DEC 0xC1
#define MIFARE_CMD_DEC 0xC0
#define MIFARE_CMD_INC 0xC1
#define MIFARE_CMD_RESTORE 0xC2
#define MIFARE_CMD_TRANSFER 0xB0

View file

@ -6,8 +6,8 @@
#define MIFARE_AUTH_KEYA 0x60
#define MIFARE_AUTH_KEYB 0x61
#define MIFARE_CMD_INC 0xC0
#define MIFARE_CMD_DEC 0xC1
#define MIFARE_CMD_DEC 0xC0
#define MIFARE_CMD_INC 0xC1
#define MIFARE_CMD_RESTORE 0xC2
#define MIFARE_CMD_TRANSFER 0xB0

View file

@ -364,7 +364,7 @@ while true; do
"AWID - len: 50 FC: 2001 Card: 13371337 - Wiegand: 20fa201980f92, Raw: 0128b12eb1811d7117e22111"; then break; fi
if ! CheckExecute slow "lf T55 em410x test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_em410x.pm3; lf search 1'" "EM410x ID found"; then break; fi
if ! CheckExecute slow "lf T55 em410x test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_em410x.pm3; lf em 410x demod'" \
"EM TAG ID : 0F0368568B"; then break; fi
"EM 410x ID 0F0368568B"; then break; fi
if ! CheckExecute slow "lf T55 fdxb_animal test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_animal.pm3; lf search 1'" "FDX-B ID found"; then break; fi
if ! CheckExecute slow "lf T55 fdxb_animal test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_fdxb_animal.pm3; lf fdxb demod'" \
"Animal ID 999-000000112233"; then break; fi
@ -391,7 +391,7 @@ while true; do
"Fmt 26 FC: 123 Card: 1337 checksum: 10"; then break; fi
if ! CheckExecute slow "lf T55 indala_224 test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_indala_224.pm3; lf search 1'" "Indala ID found"; then break; fi
if ! CheckExecute slow "lf T55 indala_224 test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_indala_224.pm3; lf indala demod'" \
"Indala - len 224 Raw: 80000001b23523a6c2e31eba3cbee4afb3c6ad1fcf649393928c14e5"; then break; fi
"Indala (len 224) Raw: 80000001b23523a6c2e31eba3cbee4afb3c6ad1fcf649393928c14e5"; then break; fi
if ! CheckExecute slow "lf T55 io test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_io.pm3; lf search 1'" "IO Prox ID found"; then break; fi
if ! CheckExecute slow "lf T55 io test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_io.pm3; lf io demod'" \
"IO Prox - XSF(01)01:01337, Raw: 007840603059cf3f (ok)"; then break; fi
@ -415,7 +415,7 @@ while true; do
"NEDAP (64b) - ID: 12345 subtype: 1 customer code: 291 / 0x123 Raw: FF82246508209953"; then break; fi
if ! CheckExecute slow "lf T55 nexwatch test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_nexwatch.pm3; lf search 1'" "NexWatch ID found"; then break; fi
if ! CheckExecute slow "lf T55 nexwatch test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_nexwatch.pm3; lf nexwatch demod'" \
"Raw : 56000000213C9F8F150C00"; then break; fi
"Raw : 5600000000213C9F8F150C00"; then break; fi
if ! CheckExecute slow "lf T55 nexwatch_nexkey test" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_nexwatch_nexkey.pm3; lf search 1'" "NexWatch ID found"; then break; fi
if ! CheckExecute slow "lf T55 nexwatch_nexkey test2" "$CLIENTBIN -c 'data load -f traces/lf_ATA5577_nexwatch_nexkey.pm3; lf nexwatch demod'" \
"88bit id : 521512301 (0x1f15a56d)"; then break; fi

View file

@ -199,6 +199,39 @@ CURVES = {
0xCF5AC8395BAFEB13C02DA292DDED7A83
)
),
"secp192k1": (
711,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37,
0xFFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D,
0x000000000000000000000000000000000000000000000000,
0x000000000000000000000000000000000000000000000003,
(
0xDB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D,
0x9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D
)
),
"secp192r1": (
409,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC,
0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1,
(
0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012,
0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811
)
),
"secp224k1": (
712,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D,
0x10000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7,
0x00000000000000000000000000000000000000000000000000000000,
0x00000000000000000000000000000000000000000000000000000005,
(
0xA1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C,
0x7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5
)
),
"secp224r1": (
713,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001,
@ -210,6 +243,51 @@ CURVES = {
0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34
)
),
"secp256k1": (
714,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000007,
(
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
)
),
## openssl uses the name: prime256v1.
"secp256r1": (
415,
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF,
0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551,
0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC,
0x5AC635D8AA3A93E7B3EbBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B,
(
0x6B17D1F2E12c4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,
0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
)
),
"secp384r1": (
715,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC,
0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF,
(
0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7,
0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F
)
),
"secp521r1": (
716,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409,
0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC,
0x0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00,
(
0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66,
0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650
)
)
}
def get_curve(name):
@ -300,24 +378,28 @@ class EllipticCurve:
#######################################################################
def recover(data, signature, alghash=None):
recovered = set()
if len(signature) == 32:
curve = get_curve("secp128r1")
recoverable = False
elif len(signature) == 33:
curve = get_curve("secp128r1")
recoverable = True
elif len(signature) == 56:
curve = get_curve("secp224r1")
recoverable = False
elif len(signature) == 57:
curve = get_curve("secp224r1")
recoverable = True
def guess_curvename(signature):
l = (len(signature) // 2) & 0xfe
if l == 32 :
curves = [ "secp128r1" ]
elif l == 48:
curves = [ "secp192k1", "secp192r1" ]
elif l == 56:
curves = [ "secp224k1", "secp224r1" ]
elif l == 64:
curves = [ "secp256k1", "secp256r1" ]
elif l == 96:
curves = [ "secp384r1" ]
elif l == 132:
curves = [ "secp521r1" ]
else:
print("Unsupported signature size %i" % len(signature))
exit(1)
raise ValueError("Unsupported signature size %i" % len(signature))
return curves
def recover(data, signature, curvename, alghash=None):
recovered = set()
curve = get_curve(curvename)
recoverable = len(signature) % 1 == 1
if (recoverable):
try:
pk = curve.recover(signature, data, hash=alghash)
@ -339,7 +421,7 @@ def recover(data, signature, alghash=None):
pass
return recovered
def recover_multiple(uids, sigs, alghash=None):
def recover_multiple(uids, sigs, curvename, alghash=None):
recovered = set()
assert len(uids) == len(sigs)
for i in range(len(uids)):
@ -349,7 +431,7 @@ def recover_multiple(uids, sigs, alghash=None):
signature = binascii.unhexlify(sigs[i])
if debug:
print("Signature (%2i): " % len(signature), binascii.hexlify(signature))
recovered_tmp = recover(data, signature, alghash)
recovered_tmp = recover(data, signature, curvename, alghash)
if i == 0:
if recovered_tmp == set():
break
@ -408,12 +490,19 @@ def selftests():
{'name': "ICODE DNA, ICODE SLIX2",
'samples': ["EE5F9B00180104E0", "32D9E7579CD77E6F1FA11419231E874826984C5F189FDE1421684563A9663377"],
'pk': "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0" },
# uses secp256r1?, SHA-256,
# {'name': "Minecraft Earth",
# 'samples': ["aa", "DF0E506DFF8FCFC4B7B979D917644445F1230D2C7CDC342AFA842CA240C210BE7275F62073A9670F2DCEFC602CBEE771C2B4CD4A04F3D1EA11F49ABDF7E8B721"],
# 'pk': "" },
]
succeeded = True
for t in tests:
print("Testing %-25s" % (t['name']+":"), end="")
recovered = recover_multiple(t['samples'][::2], t['samples'][1::2])
recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], alghash="sha256")
curvenames = guess_curvename(t['samples'][1])
recovered = set()
for c in curvenames:
for h in [None, "sha1", "sha256", "sha512"]:
recovered |= recover_multiple(t['samples'][::2], t['samples'][1::2], c, alghash=h)
if (len(recovered) == 1):
pk = recovered.pop()
pk = binascii.hexlify(pk).decode('utf8')
@ -442,14 +531,15 @@ if __name__ == "__main__":
print("Usage: \n%s UID SIGN [UID SIGN] [...]" % sys.argv[0])
print("Example: \n%s 04ee45daa34084 ebb6102bff74b087d18a57a54bc375159a04ea9bc61080b7f4a85afe1587d73b" % sys.argv[0])
exit(1)
print("Assuming no hash was used in the signature generation:")
recovered = recover_multiple(sys.argv[1:][::2], sys.argv[1:][1::2])
print("Possible uncompressed Pk(s):")
for pk in list(recovered):
print(binascii.hexlify(pk).decode('utf8'))
print("Assuming SHA-256 was used in the signature generation:")
recovered = recover_multiple(sys.argv[1:][::2], sys.argv[1:][1::2], alghash="sha256")
uids, sigs = sys.argv[1:][::2], sys.argv[1:][1::2]
curvenames = guess_curvename(sigs[0])
for c in curvenames:
print("\nAssuming curve=%s" % c)
print("========================")
for h in [None, "sha1", "sha256", "sha512"]:
print("Assuming hash=%s" % h)
recovered = recover_multiple(uids, sigs, c, alghash=h)
if recovered:
print("Possible uncompressed Pk(s):")
for pk in list(recovered):
print(binascii.hexlify(pk).decode('utf8'))