Merge branch 'master' into 4x50_eview

update 201217
This commit is contained in:
tharexde 2020-12-17 20:42:41 +01:00
commit c37b74a721
49 changed files with 2732 additions and 671 deletions

View file

@ -7,6 +7,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
- Added `hf iclass encode` - encode a wiegand binary to a encrypted credential (@iceman1001) - Added `hf iclass encode` - encode a wiegand binary to a encrypted credential (@iceman1001)
- Changed `recoverpk.py` - now tests more ECDSA curves (@doegox) - Changed `recoverpk.py` - now tests more ECDSA curves (@doegox)
- Added `hf 14a apdufuzz`- a naive apdu cla/ins/p1p2/len fuzzer (@iceman1001) - Added `hf 14a apdufuzz`- a naive apdu cla/ins/p1p2/len fuzzer (@iceman1001)
- Improved `hf 14a apdufuzz/apdufind` to find hidden APDUs (@ikarus23)
- Fix mixed up INC/DEC in MIFARE protocol defs (@vortixdev) - Fix mixed up INC/DEC in MIFARE protocol defs (@vortixdev)
- Added `lf em 4x70 info` - new support for ID48 transponders (@cmolson) - Added `lf em 4x70 info` - new support for ID48 transponders (@cmolson)
- Fix multiple coverity scan issues (@iceman1001) - Fix multiple coverity scan issues (@iceman1001)

View file

@ -111,7 +111,7 @@ Next place to visit is the [Proxmark Forum](http://www.proxmark.org/forum/index.
- [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3)
- [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/)
- [Twitter](https://twitter.com/proxmark3/) - [Twitter](https://twitter.com/proxmark3/)
- [Proxmark3 community discord server](https://discord.gg/zjxc8ZB) - [Proxmark3 community discord server](https://discord.gg/QfPvGFRQxH)
_no slack channel_ _no slack channel_

View file

@ -1170,6 +1170,26 @@ static void PacketReceived(PacketCommandNG *packet) {
em4x70_info((em4x70_data_t *)packet->data.asBytes); em4x70_info((em4x70_data_t *)packet->data.asBytes);
break; break;
} }
case CMD_LF_EM4X70_WRITE: {
em4x70_write((em4x70_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X70_UNLOCK: {
em4x70_unlock((em4x70_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X70_AUTH: {
em4x70_auth((em4x70_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X70_WRITEPIN: {
em4x70_write_pin((em4x70_data_t *)packet->data.asBytes);
break;
}
case CMD_LF_EM4X70_WRITEKEY: {
em4x70_write_key((em4x70_data_t *)packet->data.asBytes);
break;
}
#endif #endif
#ifdef WITH_ISO15693 #ifdef WITH_ISO15693

View file

@ -37,11 +37,11 @@
#define EM4X50_T_TAG_WAITING_FOR_SIGNAL 75 #define EM4X50_T_TAG_WAITING_FOR_SIGNAL 75
#define EM4X50_T_WAITING_FOR_DBLLIW 1550 #define EM4X50_T_WAITING_FOR_DBLLIW 1550
#define EM4X50_T_WAITING_FOR_SNGLLIW 140 // this value seems to be #define EM4X50_T_WAITING_FOR_SNGLLIW 140 // this value seems to be
// critical; // critical;
// if it's too low // if it's too low
// (e.g. < 120) some cards // (e.g. < 120) some cards
// are no longer readable // are no longer readable
// although they're ok // although they're ok
#define EM4X50_TAG_TOLERANCE 8 #define EM4X50_TAG_TOLERANCE 8
#define EM4X50_TAG_WORD 45 #define EM4X50_TAG_WORD 45

View file

@ -21,28 +21,35 @@ static em4x70_tag_t tag = { 0 };
// EM4170 requires a parity bit on commands, other variants do not. // EM4170 requires a parity bit on commands, other variants do not.
static bool command_parity = true; static bool command_parity = true;
#define EM4X70_T_TAG_QUARTER_PERIOD 8 // Conversion from Ticks to RF periods
#define EM4X70_T_TAG_HALF_PERIOD 16 // 1 us = 1.5 ticks
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD 24 // 1RF Period = 8us = 12 Ticks
#define EM4X70_T_TAG_FULL_PERIOD 32 #define TICKS_PER_FC 12
#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 // Chip timing from datasheet
// Converted into Ticks for timing functions
#define EM4X70_T_TAG_QUARTER_PERIOD (8 * TICKS_PER_FC)
#define EM4X70_T_TAG_HALF_PERIOD (16 * TICKS_PER_FC)
#define EM4X70_T_TAG_THREE_QUARTER_PERIOD (24 * TICKS_PER_FC)
#define EM4X70_T_TAG_FULL_PERIOD (32 * TICKS_PER_FC) // 1 Bit Period
#define EM4X70_T_TAG_TWA (128 * TICKS_PER_FC) // Write Access Time
#define EM4X70_T_TAG_DIV (224 * TICKS_PER_FC) // Divergency Time
#define EM4X70_T_TAG_AUTH (4224 * TICKS_PER_FC) // Authentication Time
#define EM4X70_T_TAG_WEE (3072 * TICKS_PER_FC) // EEPROM write Time
#define EM4X70_T_TAG_TWALB (672 * TICKS_PER_FC) // Write Access Time of Lock Bits
#define EM4X70_T_TAG_BITMOD (4 * TICKS_PER_FC) // Initial time to stop modulation when sending 0
#define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW
#define TICKS_PER_FC 12 // 1 fc = 8us, 1.5us per tick = 12 ticks #define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this
#define EM4X70_MIN_AMPLITUDE 10 // Minimum difference between a high and low signal #define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window
#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods)
#define EM4X70_TAG_TOLERANCE 10
#define EM4X70_TAG_WORD 48
#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command
#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command
/** /**
* These IDs are from the EM4170 datasheet * These IDs are from the EM4170 datasheet
* Some versions of the chip require a fourth * Some versions of the chip require a
* (even) parity bit, others do not * (even) parity bit, others do not
*/ */
#define EM4X70_COMMAND_ID 0x01 #define EM4X70_COMMAND_ID 0x01
@ -52,24 +59,28 @@ static bool command_parity = true;
#define EM4X70_COMMAND_WRITE 0x05 #define EM4X70_COMMAND_WRITE 0x05
#define EM4X70_COMMAND_UM2 0x07 #define EM4X70_COMMAND_UM2 0x07
static uint8_t gHigh = 0; // Constants used to determing high/low state of signal
static uint8_t gLow = 0; #define EM4X70_NOISE_THRESHOLD 13 // May depend on noise in environment
#define HIGH_SIGNAL_THRESHOLD (127 + EM4X70_NOISE_THRESHOLD)
#define LOW_SIGNAL_THRESHOLD (127 - EM4X70_NOISE_THRESHOLD)
#define IS_HIGH(sample) (sample>gLow ? true : false) #define IS_HIGH(sample) (sample > LOW_SIGNAL_THRESHOLD ? true : false)
#define IS_LOW(sample) (sample<gHigh ? true : false) #define IS_LOW(sample) (sample < HIGH_SIGNAL_THRESHOLD ? true : false)
// Timing related macros
#define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks) #define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks)
#define TICKS_ELAPSED(start_ticks) (GetTicks() - start_ticks)
static uint8_t bits2byte(const uint8_t *bits, int length);
static uint8_t bits2byte(uint8_t *bits, int length); static void bits2bytes(const uint8_t *bits, int length, uint8_t *out);
static void bits2bytes(uint8_t *bits, int length, uint8_t *out); static int em4x70_receive(uint8_t *bits, size_t length);
static int em4x70_receive(uint8_t *bits);
static bool find_listen_window(bool command); static bool find_listen_window(bool command);
static void init_tag(void) { static void init_tag(void) {
memset(tag.data, 0x00, sizeof(tag.data)/sizeof(tag.data[0])); memset(tag.data, 0x00, sizeof(tag.data) / sizeof(tag.data[0]));
} }
static void EM4170_setup_read(void) { static void em4x70_setup_read(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD);
@ -102,208 +113,312 @@ static void EM4170_setup_read(void) {
static bool get_signalproperties(void) { static bool get_signalproperties(void) {
// calculate signal properties (mean amplitudes) from measured data: // Simple check to ensure we see a signal above the noise threshold
// 32 amplitudes (maximum values) -> mean amplitude value -> gHigh -> gLow uint32_t no_periods = 32;
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) // wait until signal/noise > 1 (max. 32 periods)
for (int i = 0; i < TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD * no_periods; i++) { for (int i = 0; i < EM4X70_T_TAG_FULL_PERIOD * no_periods; i++) {
// about 2 samples per bit period // about 2 samples per bit period
WaitTicks(TICKS_PER_FC * EM4X70_T_TAG_HALF_PERIOD); WaitTicks(EM4X70_T_TAG_HALF_PERIOD);
if (AT91C_BASE_SSC->SSC_RHR > noise) { if (AT91C_BASE_SSC->SSC_RHR > HIGH_SIGNAL_THRESHOLD) {
signal_found = true; return true;
break;
} }
} }
return false;
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 * get_falling_pulse_length
* *
* Times falling edge pulses * Returns time between falling edge pulse in ticks
*/ */
static uint32_t get_pulse_length(void) { static uint32_t get_falling_pulse_length(void) {
uint8_t sample; uint32_t timeout = GetTicks() + EM4X70_T_TAG_TIMEOUT;
uint32_t timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do { while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout));
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
uint32_t start_ticks = GetTicks(); uint32_t start_ticks = GetTicks();
timeout = start_ticks + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do { while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout));
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
timeout = (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) + GetTicks(); while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout));
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
return GetTicks() - start_ticks; return TICKS_ELAPSED(start_ticks);
} }
/** /**
* get_pulse_invert_length * get_rising_pulse_length
* *
* Times rising edge pules * Returns time between rising edge pulse in ticks
* TODO: convert to single function with get_pulse_length()
*/ */
static uint32_t get_pulse_invert_length(void) { static uint32_t get_rising_pulse_length(void) {
uint8_t sample; uint32_t timeout = GetTicks() + EM4X70_T_TAG_TIMEOUT;
uint32_t timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do { while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout));
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
uint32_t start_ticks = GetTicks(); uint32_t start_ticks = GetTicks();
timeout = start_ticks + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do { while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout));
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout));
do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
return GetTicks() - start_ticks; return TICKS_ELAPSED(start_ticks);
} }
static bool check_pulse_length(uint32_t pl, int length, int margin) { static uint32_t get_pulse_length(edge_detection_t edge) {
if(edge == RISING_EDGE)
return get_rising_pulse_length();
else if(edge == FALLING_EDGE)
return get_falling_pulse_length();
return 0;
}
static bool check_pulse_length(uint32_t pl, uint32_t length) {
// check if pulse length <pl> corresponds to given length <length> // 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 >= (length - EM4X70_T_TAG_TOLERANCE)) & (pl <= (length + EM4X70_T_TAG_TOLERANCE)));
return ((pl >= TICKS_PER_FC * (length - margin)) & (pl <= TICKS_PER_FC * (length + margin)));
} }
static void em4x70_send_bit(int bit) { static void em4x70_send_bit(bool bit) {
// send single bit according to EM4170 application note and datasheet // send single bit according to EM4170 application note and datasheet
uint32_t start_ticks = GetTicks(); uint32_t start_ticks = GetTicks();
if (bit == 0) { if (bit == 0) {
// disable modulation (drop the field) for 4 cycles of carrier // disable modulation (drop the field) n cycles of carrier
LOW(GPIO_SSC_DOUT); LOW(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * 4); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_BITMOD);
// enable modulation (activates the field) for remaining first // enable modulation (activates the field) for remaining first
// half of bit period // half of bit period
HIGH(GPIO_SSC_DOUT); HIGH(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_HALF_PERIOD); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_HALF_PERIOD);
// disable modulation for second half of bit period // disable modulation for second half of bit period
LOW(GPIO_SSC_DOUT); LOW(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD);
} else { } else {
// bit = "1" means disable modulation for full bit period // bit = "1" means disable modulation for full bit period
LOW(GPIO_SSC_DOUT); LOW(GPIO_SSC_DOUT);
while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD); while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD);
} }
} }
/** /**
* em4x70_send_command * em4x70_send_nibble
*
* sends 4 bits of data + 1 bit of parity (with_parity)
*
*/ */
static void em4170_send_command(uint8_t command) { static void em4x70_send_nibble(uint8_t nibble, bool with_parity) {
int parity = 0; int parity = 0;
int msb_bit = 0; int msb_bit = 0;
// Non automotive EM4x70 based tags are 3 bits + 1 parity. // Non automotive EM4x70 based tags are 3 bits + 1 parity.
// So drop the MSB and send a parity bit instead after the command // So drop the MSB and send a parity bit instead after the command
if(command_parity) if (command_parity)
msb_bit = 1; msb_bit = 1;
for (int i = msb_bit; i < 4; i++) { for (int i = msb_bit; i < 4; i++) {
int bit = (command >> (3 - i)) & 1; int bit = (nibble >> (3 - i)) & 1;
em4x70_send_bit(bit); em4x70_send_bit(bit);
parity ^= bit; parity ^= bit;
} }
if(command_parity) if (with_parity)
em4x70_send_bit(parity); em4x70_send_bit(parity);
} }
static void em4x70_send_byte(uint8_t byte) {
// Send byte msb first
for (int i = 0; i < 8; i++)
em4x70_send_bit((byte >> (7 - i)) & 1);
}
static void em4x70_send_word(const uint16_t word) {
// Split into nibbles
uint8_t nibbles[4];
uint8_t j = 0;
for (int i = 0; i < 2; i++) {
uint8_t byte = (word >> (8 * i)) & 0xff;
nibbles[j++] = (byte >> 4) & 0xf;
nibbles[j++] = byte & 0xf;
}
// send 16 bit word with parity bits according to EM4x70 datasheet
// sent as 4 x nibbles (4 bits + parity)
for (int i = 0; i < 4; i++) {
em4x70_send_nibble(nibbles[i], true);
}
// send column parities (4 bit)
em4x70_send_nibble(nibbles[0] ^ nibbles[1] ^ nibbles[2] ^ nibbles[3], false);
// send final stop bit (always "0")
em4x70_send_bit(0);
}
static bool check_ack(void) {
// returns true if signal structue corresponds to ACK, anything else is
// counted as NAK (-> false)
// ACK 64 + 64
// NACK 64 + 48
if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) &&
check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) {
// ACK
return true;
}
// Othewise it was a NACK or Listen Window
return false;
}
static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *response) {
if (find_listen_window(true)) {
em4x70_send_nibble(EM4X70_COMMAND_AUTH, true);
// Send 56-bit Random number
for (int i = 0; i < 7; i++) {
em4x70_send_byte(rnd[i]);
}
// Send 7 x 0's (Diversity bits)
for (int i = 0; i < 7; i++) {
em4x70_send_bit(0);
}
// Send 28-bit f(RN)
// Send first 24 bits
for (int i = 0; i < 3; i++) {
em4x70_send_byte(frnd[i]);
}
// Send last 4 bits (no parity)
em4x70_send_nibble((frnd[3] >> 4) & 0xf, false);
// Receive header, 20-bit g(RN), LIW
uint8_t grnd[EM4X70_MAX_RECEIVE_LENGTH] = {0};
int num = em4x70_receive(grnd, 20);
if (num < 20) {
Dbprintf("Auth failed");
return PM3_ESOFT;
}
bits2bytes(grnd, 24, response);
return PM3_SUCCESS;
}
return PM3_ESOFT;
}
static int send_pin(const uint32_t pin) {
// sends pin code for unlocking
if (find_listen_window(true)) {
// send PIN command
em4x70_send_nibble(EM4X70_COMMAND_PIN, true);
// --> Send TAG ID (bytes 4-7)
for (int i = 0; i < 4; i++) {
em4x70_send_byte(tag.data[7 - i]);
}
// --> Send PIN
for (int i = 0; i < 4 ; i++) {
em4x70_send_byte((pin >> (i * 8)) & 0xff);
}
// Wait TWALB (write access lock bits)
WaitTicks(EM4X70_T_TAG_TWALB);
// <-- Receive ACK
if (check_ack()) {
// <w> Writes Lock Bits
WaitTicks(EM4X70_T_TAG_WEE);
// <-- Receive header + ID
uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH];
int num = em4x70_receive(tag_id, 32);
if (num < 32) {
Dbprintf("Invalid ID Received");
return PM3_ESOFT;
}
bits2bytes(tag_id, num, &tag.data[4]);
return PM3_SUCCESS;
}
}
return PM3_ESOFT;
}
static int write(const uint16_t word, const uint8_t address) {
// writes <word> to specified <address>
if (find_listen_window(true)) {
// send write command
em4x70_send_nibble(EM4X70_COMMAND_WRITE, true);
// send address data with parity bit
em4x70_send_nibble(address, true);
// send data word
em4x70_send_word(word);
// Wait TWA
WaitTicks(EM4X70_T_TAG_TWA);
// look for ACK sequence
if (check_ack()) {
// now EM4x70 needs EM4X70_T_TAG_TWEE (EEPROM write time)
// for saving data and should return with ACK
WaitTicks(EM4X70_T_TAG_WEE);
if (check_ack()) {
return PM3_SUCCESS;
}
}
}
return PM3_ESOFT;
}
static bool find_listen_window(bool command) { static bool find_listen_window(bool command) {
int cnt = 0; int cnt = 0;
while(cnt < EM4X70_T_WAITING_FOR_SNGLLIW) { while (cnt < EM4X70_T_WAITING_FOR_LIW) {
/* /*
80 ( 64 + 16 ) 80 ( 64 + 16 )
80 ( 64 + 16 ) 80 ( 64 + 16 )
@ -311,26 +426,24 @@ static bool find_listen_window(bool command) {
96 ( 64 + 32 ) 96 ( 64 + 32 )
64 ( 32 + 16 +16 )*/ 64 ( 32 + 16 +16 )*/
if (check_pulse_length(get_pulse_invert_length(), 80, EM4X70_TAG_TOLERANCE)) { if (check_pulse_length(get_pulse_length(RISING_EDGE), (2*EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_HALF_PERIOD) &&
if (check_pulse_length(get_pulse_invert_length(), 80, EM4X70_TAG_TOLERANCE)) { check_pulse_length(get_pulse_length(RISING_EDGE), (2*EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_HALF_PERIOD) &&
if (check_pulse_length(get_pulse_length(), 96, EM4X70_TAG_TOLERANCE)) { check_pulse_length(get_pulse_length(FALLING_EDGE), (2*EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_FULL_PERIOD) &&
if (check_pulse_length(get_pulse_length(), 64, EM4X70_TAG_TOLERANCE)) { check_pulse_length(get_pulse_length(FALLING_EDGE), EM4X70_T_TAG_FULL_PERIOD + (2*EM4X70_T_TAG_HALF_PERIOD))) {
if(command) {
/* Here we are after the 64 duration edge. if (command) {
* em4170 says we need to wait about 48 RF clock cycles. /* Here we are after the 64 duration edge.
* depends on the delay between tag and us * 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 *
*/ * 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 WaitTicks( 4 * EM4X70_T_TAG_QUARTER_PERIOD );
em4x70_send_bit(0); // Send RM Command
em4x70_send_bit(0); em4x70_send_bit(0);
} em4x70_send_bit(0);
return true;
}
}
} }
return true;
} }
cnt++; cnt++;
} }
@ -338,22 +451,21 @@ static bool find_listen_window(bool command) {
return false; return false;
} }
static void bits2bytes(uint8_t *bits, int length, uint8_t *out) { static void bits2bytes(const uint8_t *bits, int length, uint8_t *out) {
if(length%8 != 0) { if (length % 8 != 0) {
Dbprintf("Should have a multiple of 8 bits, was sent %d", length); 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 int num_bytes = length / 8; // We should have a multiple of 8 here
for(int i=1; i <= num_bytes; i++) { for (int i = 1; i <= num_bytes; i++) {
out[num_bytes-i] = bits2byte(bits, 8); out[num_bytes - i] = bits2byte(bits, 8);
bits+=8; bits += 8;
//Dbprintf("Read: %02X", out[num_bytes-i]);
} }
} }
static uint8_t bits2byte(uint8_t *bits, int length) { static uint8_t bits2byte(const uint8_t *bits, int length) {
// converts <length> separate bits into a single "byte" // converts <length> separate bits into a single "byte"
uint8_t byte = 0; uint8_t byte = 0;
@ -368,22 +480,28 @@ static uint8_t bits2byte(uint8_t *bits, int length) {
return byte; return byte;
} }
/*static void print_array(uint8_t *bits, int len) { static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length) {
if(len%8 != 0) { int retries = EM4X70_COMMAND_RETRIES;
Dbprintf("Should have a multiple of 8 bits, was sent %d", len); while (retries) {
retries--;
if (find_listen_window(true)) {
uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0};
size_t out_length_bits = length * 8;
em4x70_send_nibble(command, command_parity);
int len = em4x70_receive(bits, out_length_bits);
if (len < out_length_bits) {
Dbprintf("Invalid data received length: %d, expected %d", len, out_length_bits);
return false;
}
bits2bytes(bits, len, bytes);
return true;
}
} }
return false;
}
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]);
}
}*/
/** /**
@ -393,18 +511,8 @@ static uint8_t bits2byte(uint8_t *bits, int length) {
*/ */
static bool em4x70_read_id(void) { static bool em4x70_read_id(void) {
if(find_listen_window(true)) { return send_command_and_read(EM4X70_COMMAND_ID, &tag.data[4], 4);
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;
} }
/** /**
@ -413,18 +521,9 @@ static bool em4x70_read_id(void) {
* read user memory 1 (4 bytes including lock bits) * read user memory 1 (4 bytes including lock bits)
*/ */
static bool em4x70_read_um1(void) { static bool em4x70_read_um1(void) {
if(find_listen_window(true)) {
uint8_t bits[64] = {0}; return send_command_and_read(EM4X70_COMMAND_UM1, &tag.data[0], 4);
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;
} }
@ -434,34 +533,22 @@ static bool em4x70_read_um1(void) {
* read user memory 2 (8 bytes) * read user memory 2 (8 bytes)
*/ */
static bool em4x70_read_um2(void) { static bool em4x70_read_um2(void) {
if(find_listen_window(true)) {
uint8_t bits[64] = {0}; return send_command_and_read(EM4X70_COMMAND_UM2, &tag.data[24], 8);
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) { static bool find_em4x70_tag(void) {
Dbprintf("%s: Start", __func__);
// function is used to check wether a tag on the proxmark is an // function is used to check wether a tag on the proxmark is an
// EM4170 tag or not -> speed up "lf search" process // EM4170 tag or not -> speed up "lf search" process
return find_listen_window(false); return find_listen_window(false);
} }
static int em4x70_receive(uint8_t *bits) { static int em4x70_receive(uint8_t *bits, size_t length) {
uint32_t pl; uint32_t pl;
int bit_pos = 0; int bit_pos = 0;
uint8_t edge = 0; edge_detection_t edge = RISING_EDGE;
bool foundheader = false; bool foundheader = false;
// Read out the header // Read out the header
@ -469,60 +556,58 @@ static int em4x70_receive(uint8_t *bits) {
// 4 Manchester 0's // 4 Manchester 0's
// Skip a few leading 1's as it could be noisy // Skip a few leading 1's as it could be noisy
WaitTicks(TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); WaitTicks(6 * EM4X70_T_TAG_FULL_PERIOD);
// wait until we get the transition from 1's to 0's which is 1.5 full windows // wait until we get the transition from 1's to 0's which is 1.5 full windows
int pulse_count = 0; for(int i = 0; i < EM4X70_T_READ_HEADER_LEN; i++) {
while(pulse_count < 12){ pl = get_pulse_length(edge);
pl = get_pulse_invert_length(); if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) {
pulse_count++;
if(check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD, EM4X70_TAG_TOLERANCE)) {
foundheader = true; foundheader = true;
break; break;
} }
} }
if(!foundheader) { if (!foundheader) {
Dbprintf("Failed to find read header"); Dbprintf("Failed to find read header");
return 0; return 0;
} }
// Skip next 3 0's, header check consumes the first 0 // Skip next 3 0's, header check consumes the first 0
for(int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
get_pulse_invert_length(); // If pulse length is not 1 bit, then abort early
if(!check_pulse_length(get_pulse_length(edge), EM4X70_T_TAG_FULL_PERIOD)) {
return 0;
}
} }
// identify remaining bits based on pulse lengths // identify remaining bits based on pulse lengths
// between two listen windows only pulse lengths of 1, 1.5 and 2 are possible // between listen windows only pulse lengths of 1, 1.5 and 2 are possible
while (true) { while (bit_pos < length) {
if(edge) pl = get_pulse_length(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)) { if (check_pulse_length(pl, EM4X70_T_TAG_FULL_PERIOD)) {
// pulse length = 1 // pulse length 1 -> assign bit
bits[bit_pos++] = edge; bits[bit_pos++] = edge == FALLING_EDGE ? 1 : 0;
} else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) { } else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) {
// pulse length = 1.5 -> flip edge detection // pulse length 1.5 -> 2 bits + flip edge detection
if(edge) { if (edge == FALLING_EDGE) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
edge = 0; edge = RISING_EDGE;
} else { } else {
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
edge = 1; edge = FALLING_EDGE;
} }
} else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) { } else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD)) {
// pulse length of 2 // pulse length of 2 -> two bits
if(edge) { if (edge == FALLING_EDGE) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
} else { } else {
@ -530,15 +615,13 @@ static int em4x70_receive(uint8_t *bits) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
} }
} else if ( (edge && check_pulse_length(pl, 3 * EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) || } else {
(!edge && check_pulse_length(pl, 80, EM4X70_T_TAG_QUARTER_PERIOD))) { // Listen Window, or invalid bit
break;
// LIW detected (either invert or normal)
return --bit_pos;
} }
} }
return bit_pos;
return bit_pos;
} }
void em4x70_info(em4x70_data_t *etd) { void em4x70_info(em4x70_data_t *etd) {
@ -549,10 +632,10 @@ void em4x70_info(em4x70_data_t *etd) {
command_parity = etd->parity; command_parity = etd->parity;
init_tag(); init_tag();
EM4170_setup_read(); em4x70_setup_read();
// Find the Tag // Find the Tag
if (get_signalproperties() && find_EM4X70_Tag()) { if (get_signalproperties() && find_em4x70_tag()) {
// Read ID, UM1 and UM2 // Read ID, UM1 and UM2
status = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2(); status = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2();
} }
@ -561,3 +644,165 @@ void em4x70_info(em4x70_data_t *etd) {
lf_finalize(); lf_finalize();
reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data)); reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data));
} }
void em4x70_write(em4x70_data_t *etd) {
uint8_t status = 0;
command_parity = etd->parity;
init_tag();
em4x70_setup_read();
// Find the Tag
if (get_signalproperties() && find_em4x70_tag()) {
// Write
status = write(etd->word, etd->address) == PM3_SUCCESS;
if (status) {
// Read Tag after writing
em4x70_read_id();
em4x70_read_um1();
em4x70_read_um2();
}
}
StopTicks();
lf_finalize();
reply_ng(CMD_LF_EM4X70_WRITE, status, tag.data, sizeof(tag.data));
}
void em4x70_unlock(em4x70_data_t *etd) {
uint8_t status = 0;
command_parity = etd->parity;
init_tag();
em4x70_setup_read();
// Find the Tag
if (get_signalproperties() && find_em4x70_tag()) {
// Read ID (required for send_pin command)
if (em4x70_read_id()) {
// Send PIN
status = send_pin(etd->pin) == PM3_SUCCESS;
// If the write succeeded, read the rest of the tag
if (status) {
// Read Tag
// ID doesn't change
em4x70_read_um1();
em4x70_read_um2();
}
}
}
StopTicks();
lf_finalize();
reply_ng(CMD_LF_EM4X70_UNLOCK, status, tag.data, sizeof(tag.data));
}
void em4x70_auth(em4x70_data_t *etd) {
uint8_t status = 0;
uint8_t response[3] = {0};
command_parity = etd->parity;
init_tag();
em4x70_setup_read();
// Find the Tag
if (get_signalproperties() && find_em4x70_tag()) {
// Authenticate and get tag response
status = authenticate(etd->rnd, etd->frnd, response) == PM3_SUCCESS;
}
StopTicks();
lf_finalize();
reply_ng(CMD_LF_EM4X70_AUTH, status, response, sizeof(response));
}
void em4x70_write_pin(em4x70_data_t *etd) {
uint8_t status = 0;
command_parity = etd->parity;
init_tag();
em4x70_setup_read();
// Find the Tag
if (get_signalproperties() && find_em4x70_tag()) {
// Read ID (required for send_pin command)
if (em4x70_read_id()) {
// Write new PIN
if( (write( etd->pin & 0xFFFF, EM4X70_PIN_WORD_UPPER) == PM3_SUCCESS) &&
(write((etd->pin >> 16) & 0xFFFF, EM4X70_PIN_WORD_LOWER) == PM3_SUCCESS)) {
// Now Try to authenticate using the new PIN
// Send PIN
status = send_pin(etd->pin) == PM3_SUCCESS;
// If the write succeeded, read the rest of the tag
if (status) {
// Read Tag
// ID doesn't change
em4x70_read_um1();
em4x70_read_um2();
}
}
}
}
StopTicks();
lf_finalize();
reply_ng(CMD_LF_EM4X70_WRITEPIN, status, tag.data, sizeof(tag.data));
}
void em4x70_write_key(em4x70_data_t *etd) {
uint8_t status = 0;
command_parity = etd->parity;
init_tag();
em4x70_setup_read();
// Find the Tag
if (get_signalproperties() && find_em4x70_tag()) {
// Read ID to ensure we can write to card
if (em4x70_read_id()) {
status = 1;
// Write each crypto block
for(int i = 0; i < 6; i++) {
uint16_t key_word = (etd->crypt_key[(i*2)+1] << 8) + etd->crypt_key[i*2];
// Write each word, abort if any failure occurs
if (write(key_word, 9-i) != PM3_SUCCESS) {
status = 0;
break;
}
}
// TODO: Ideally here we would perform a test authentication
// to ensure the new key was written correctly. This is
// what the datasheet suggests. We can't do that until
// we have the crypto algorithm implemented.
}
}
StopTicks();
lf_finalize();
reply_ng(CMD_LF_EM4X70_WRITEKEY, status, tag.data, sizeof(tag.data));
}

View file

@ -17,6 +17,16 @@ typedef struct {
uint8_t data[32]; uint8_t data[32];
} em4x70_tag_t; } em4x70_tag_t;
typedef enum {
RISING_EDGE,
FALLING_EDGE
}edge_detection_t;
void em4x70_info(em4x70_data_t *etd); void em4x70_info(em4x70_data_t *etd);
void em4x70_write(em4x70_data_t *etd);
void em4x70_unlock(em4x70_data_t *etd);
void em4x70_auth(em4x70_data_t *etd);
void em4x70_write_pin(em4x70_data_t *etd);
void em4x70_write_key(em4x70_data_t *etd);
#endif /* EM4x70_H */ #endif /* EM4x70_H */

View file

@ -228,6 +228,7 @@ set (TARGET_SOURCES
${PM3_ROOT}/client/src/cmdhf15.c ${PM3_ROOT}/client/src/cmdhf15.c
${PM3_ROOT}/client/src/cmdhfcryptorf.c ${PM3_ROOT}/client/src/cmdhfcryptorf.c
${PM3_ROOT}/client/src/cmdhfepa.c ${PM3_ROOT}/client/src/cmdhfepa.c
${PM3_ROOT}/client/src/cmdhfemrtd.c
${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffelica.c
${PM3_ROOT}/client/src/cmdhffido.c ${PM3_ROOT}/client/src/cmdhffido.c
${PM3_ROOT}/client/src/cmdhficlass.c ${PM3_ROOT}/client/src/cmdhficlass.c

View file

@ -469,6 +469,7 @@ SRCS = aiddesfire.c \
cmdhf15.c \ cmdhf15.c \
cmdhfcryptorf.c \ cmdhfcryptorf.c \
cmdhfepa.c \ cmdhfepa.c \
cmdhfemrtd.c \
cmdhffelica.c \ cmdhffelica.c \
cmdhffido.c \ cmdhffido.c \
cmdhficlass.c \ cmdhficlass.c \

View file

@ -8,6 +8,7 @@
43464F494D48504E4C4359454E528841 #NHIF 43464F494D48504E4C4359454E528841 #NHIF
6AC292FAA1315B4D858AB3A3D7D5933A 6AC292FAA1315B4D858AB3A3D7D5933A
404142434445464748494a4b4c4d4e4f 404142434445464748494a4b4c4d4e4f
3112B738D8862CCD34302EB299AAB456 # Gallagher AES (https://pastebin.com/GkbGLz8r)
00112233445566778899aabbccddeeff 00112233445566778899aabbccddeeff
2b7e151628aed2a6abf7158809cf4f3c 2b7e151628aed2a6abf7158809cf4f3c
fbeed618357133667c85e08f7236a8de fbeed618357133667c85e08f7236a8de

View file

@ -23,6 +23,7 @@
#include "cmdhf14b.h" // ISO14443-B #include "cmdhf14b.h" // ISO14443-B
#include "cmdhf15.h" // ISO15693 #include "cmdhf15.h" // ISO15693
#include "cmdhfepa.h" #include "cmdhfepa.h"
#include "cmdhfemrtd.h" // eMRTD
#include "cmdhflegic.h" // LEGIC #include "cmdhflegic.h" // LEGIC
#include "cmdhficlass.h" // ICLASS #include "cmdhficlass.h" // ICLASS
#include "cmdhfmf.h" // CLASSIC #include "cmdhfmf.h" // CLASSIC
@ -357,24 +358,25 @@ int CmdHFPlot(const char *Cmd) {
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("High Frequency") " -----------------------"}, {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("High Frequency") " -----------------------"},
{"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"},
{"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"},
{"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"},
// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"}, // {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"},
{"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"},
{"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, {"emrtd", CmdHFeMRTD, AlwaysAvailable, "{ Machine Readable Travel Document... }"},
{"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"},
{"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"}, {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"},
{"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, {"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"},
{"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"}, {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"},
{"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"}, {"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"},
{"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, {"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"},
{"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"},
{"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"},
{"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"},
{"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"},
{"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"},
{"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"},
{"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"},
{"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"},
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"},

View file

@ -2127,128 +2127,163 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) {
n -= 2; n -= 2;
return d[n] * 0x0100 + d[n + 1]; return d[n] * 0x0100 + d[n + 1];
} }
static int CmdHf14AFuzzapdu(const char *Cmd) {
static int CmdHf14AFindapdu(const char *Cmd) {
// TODO: What response values should be considerd "valid" or "instersting" (worth dispalying)?
// TODO: Option to select AID/File (and skip INS 0xA4).
// TODO: Validate the decoding of the APDU (not specific to this command, check
// https://cardwerk.com/smartcards/smartcard_standard_ISO7816-4_5_basic_organizations.aspx#chap5_3_2).
// TODO: Check all cases (APDUs) with no data bytes (no/short/extended length).
// TODO: Option to blacklist instructions (or whole APDUs).
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf 14a apdufuzz", CLIParserInit(&ctx, "hf 14a apdufind",
"Fuzz APDU's of ISO7816 protocol to find valid CLS/INS/P1P2/LE commands.\n" "Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1P2 commands.\n"
"It loops all 256 possible values for each byte.\n" "It loops all 256 possible values for each byte.\n"
"The loop oder is INS -> P1/P2 (alternating) -> CLA\n"
"Tag must be on antenna before running.", "Tag must be on antenna before running.",
"hf 14a apdufuzz\n" "hf 14a apdufind\n"
"hf 14a apdufuzz --cla 80\n" "hf 14a apdufind --cla 80\n"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_str0(NULL, "cla", "<hex>", "start CLASS value (1 hex byte)"), arg_str0("c", "cla", "<hex>", "Start value of CLASS (1 hex byte)"),
arg_str0(NULL, "ins", "<hex>", "start INSTRUCTION value (1 hex byte)"), arg_str0("i", "ins", "<hex>", "Start value of INSTRUCTION (1 hex byte)"),
arg_str0(NULL, "p1", "<hex>", "start P1 value (1 hex byte)"), arg_str0(NULL, "p1", "<hex>", "Start value of P1 (1 hex byte)"),
arg_str0(NULL, "p2", "<hex>", "start P2 value (1 hex byte)"), arg_str0(NULL, "p2", "<hex>", "Start value of P2 (1 hex byte)"),
arg_str0(NULL, "le", "<hex>", "start LENGTH value (1 hex byte)"), arg_u64_0("r", "reset", "<number>", "Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes"),
arg_lit0("v", "verbose", "verbose output"), arg_lit0("v", "verbose", "Verbose output"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, true);
int cla_len = 0; int cla_len = 0;
uint8_t cla[1] = {0}; uint8_t cla_arg[1] = {0};
CLIGetHexWithReturn(ctx, 1, cla, &cla_len); CLIGetHexWithReturn(ctx, 1, cla_arg, &cla_len);
int ins_len = 0; int ins_len = 0;
uint8_t ins[1] = {0}; uint8_t ins_arg[1] = {0};
CLIGetHexWithReturn(ctx, 2, ins, &ins_len); CLIGetHexWithReturn(ctx, 2, ins_arg, &ins_len);
int p1_len = 0; int p1_len = 0;
uint8_t p1[1] = {0}; uint8_t p1_arg[1] = {0};
CLIGetHexWithReturn(ctx, 3, p1, &p1_len); CLIGetHexWithReturn(ctx, 3, p1_arg, &p1_len);
int p2_len = 0; int p2_len = 0;
uint8_t p2[1] = {0}; uint8_t p2_arg[1] = {0};
CLIGetHexWithReturn(ctx, 4, p2, &p2_len); CLIGetHexWithReturn(ctx, 4, p2_arg, &p2_len);
uint64_t reset_time = arg_get_u64_def(ctx, 5, 5 * 60); // Reset every 5 minutes.
int le_len = 0;
uint8_t le[1] = {0};
CLIGetHexWithReturn(ctx, 5, le, &le_len);
bool verbose = arg_get_lit(ctx, 6); bool verbose = arg_get_lit(ctx, 6);
CLIParserFree(ctx); CLIParserFree(ctx);
bool activate_field = true; bool activate_field = true;
bool keep_field_on = true; bool keep_field_on = true;
uint8_t cla = cla_arg[0];
uint8_t a = cla[0]; uint8_t ins = ins_arg[0];
uint8_t b = ins[0]; uint8_t p1 = p1_arg[0];
uint8_t c = p1[0]; uint8_t p2 = p2_arg[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]; uint8_t response[PM3_CMD_DATA_SIZE];
int resplen = 0; int response_n = 0;
uint8_t aSELECT_AID[80]; uint8_t aSELECT_AID[80];
int aSELECT_AID_n = 0; int aSELECT_AID_n = 0;
// Check if the tag reponds to APDUs.
PrintAndLogEx(INFO, "Sending a test APDU (select file command) to check if the tag is responding to APDU");
param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); 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); int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, true, false, response, sizeof(response), &response_n);
if (res) { if (res) {
DropField(); PrintAndLogEx(FAILED, "Tag did not responde to a test APDU (select file command). Aborting");
return res; return res;
} }
PrintAndLogEx(SUCCESS, "Got response. Starting the APDU finder [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2);
PrintAndLogEx(INFO, "Press " _GREEN_("<Enter>") " to exit");
if (activate_field) bool inc_p1 = true;
activate_field = false; uint64_t t_start = msclock();
uint64_t t_last_reset = msclock();
uint64_t t1 = msclock(); // Enumerate APDUs.
do { do {
do { do {
do { do {
do { // Exit (was the Enter key pressed)?
do { if (kbd_enter_pressed()) {
if (kbd_enter_pressed()) { PrintAndLogEx(INFO, "User interrupted detected. Aborting");
goto out; goto out;
} }
uint8_t foo[5] = {a, b, c, d, e}; if (verbose) {
int foo_n = sizeof(foo); PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2);
}
if (verbose) { // Send APDU.
PrintAndLogEx(INFO, "%s", sprint_hex(foo, sizeof(foo))); uint8_t command[4] = {cla, ins, p1, p2};
} int command_n = sizeof(command);
res = ExchangeAPDU14a(foo, foo_n, activate_field, keep_field_on, response, sizeof(response), &resplen); res = ExchangeAPDU14a(command, command_n, activate_field, keep_field_on, response, sizeof(response), &response_n);
if (res) { if (res) {
e++; continue;
continue; }
}
uint16_t sw = get_sw(response, resplen); // Was there and length error? If so, try with Le length (case 2 instad of case 1,
if (sw != 0x6a86 && // https://stackoverflow.com/a/30679558). Le = 0x00 will get interpreted as extended length APDU
sw != 0x6986 && // with Le being 0x0100.
sw != 0x6d00 uint16_t sw = get_sw(response, response_n);
) { bool command_with_le = false;
PrintAndLogEx(INFO, "%02X %02X %02X %02X %02X (%04x - %s)", a,b,c,d,e, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); if (sw == 0x6700) {
} PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X\": %04X (%s)", cla, ins, p1, p2,
e++; sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
if (verbose) { PrintAndLogEx(INFO, "Resending current command with Le = 0x0100 (extended length APDU)");
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); uint8_t command2[7] = {cla, ins, p1, p2, 0x00};
} int command2_n = sizeof(command2);
res = ExchangeAPDU14a(command2, command2_n, activate_field, keep_field_on, response, sizeof(response), &response_n);
if (res) {
continue;
}
command_with_le = true;
}
} while (e); // Check response.
d++; sw = get_sw(response, response_n);
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); if (sw != 0x6a86 &&
} while (d); sw != 0x6986 &&
c++; sw != 0x6d00
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); ) {
} while (c); if (command_with_le) {
b++; PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X00\": %04X (%s)", cla, ins, p1, p2,
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
} while (b); } else {
a++; PrintAndLogEx(INFO, "Got response for APDU \"%02X%02X%02X%02X\": %04X (%s)", cla, ins, p1, p2,
PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff));
} while(a); }
// Show response data.
if (response_n > 2) {
PrintAndLogEx(SUCCESS, "Response data is: %s | %s", sprint_hex_inrow(response, response_n - 2),
sprint_ascii(response, response_n - 2));
}
}
activate_field = false; // Do not reativate the filed until the next reset.
} while (++ins != ins_arg[0]);
// Increment P1/P2 in an alternating fashion.
if (inc_p1) {
p1++;
} else {
p2++;
}
inc_p1 = !inc_p1;
// Check if re-selecting the card is needed.
uint64_t t_since_last_reset = ((msclock() - t_last_reset) / 1000);
if (t_since_last_reset > reset_time) {
DropField();
activate_field = true;
t_last_reset = msclock();
PrintAndLogEx(INFO, "Last reset was %" PRIu64 " seconds ago. Reseting the tag to prevent timeout issues", t_since_last_reset);
}
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2);
} while (p1 != p1_arg[0] || p2 != p2_arg[0]);
cla++;
PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2);
} while (cla != cla_arg[0]);
out: out:
PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); PrintAndLogEx(SUCCESS, "Runtime: %" PRIu64 " seconds\n", (msclock() - t_start) / 1000);
DropField(); DropField();
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -2266,7 +2301,7 @@ static command_t CommandTable[] = {
{"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"},
{"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, {"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"},
{"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"}, {"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"},
{"apdufuzz", CmdHf14AFuzzapdu, IfPm3Iso14443a, "Fuzz APDU - CLA/INS/P1P2"}, {"apdufind", CmdHf14AFindapdu, IfPm3Iso14443a, "Enuerate APDUs - CLA/INS/P1P2"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -26,6 +26,7 @@
#include "mifare/ndef.h" // NDEFRecordsDecodeAndPrint #include "mifare/ndef.h" // NDEFRecordsDecodeAndPrint
#define TIMEOUT 2000 #define TIMEOUT 2000
#define APDU_TIMEOUT 4000
// iso14b apdu input frame length // iso14b apdu input frame length
static uint16_t apdu_frame_length = 0; static uint16_t apdu_frame_length = 0;
@ -1438,7 +1439,7 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool
SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, 0, NULL, 0); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, 0, NULL, 0);
PacketResponseNG resp; PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, APDU_TIMEOUT)) {
uint8_t *recv = resp.data.asBytes; uint8_t *recv = resp.data.asBytes;
int rlen = resp.oldarg[0]; int rlen = resp.oldarg[0];
uint8_t res = resp.oldarg[1]; uint8_t res = resp.oldarg[1];
@ -1488,7 +1489,7 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) {
*dataoutlen = 0; *dataoutlen = 0;
bool chaining = false; bool chaining = false;
int res; int res;

View file

@ -15,6 +15,8 @@
int CmdHF14B(const char *Cmd); int CmdHF14B(const char *Cmd);
int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen);
int infoHF14B(bool verbose); int infoHF14B(bool verbose);
int readHF14B(bool verbose); int readHF14B(bool verbose);
#endif #endif

1330
client/src/cmdhfemrtd.c Normal file

File diff suppressed because it is too large Load diff

20
client/src/cmdhfemrtd.h Normal file
View file

@ -0,0 +1,20 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2020 A. Ozkal
//
// 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.
//-----------------------------------------------------------------------------
// High frequency Electronic Machine Readable Travel Document commands
//-----------------------------------------------------------------------------
#ifndef CMDHFEMRTD_H__
#define CMDHFEMRTD_H__
#include "common.h"
int CmdHFeMRTD(const char *Cmd);
int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available);
int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available);
#endif

View file

@ -624,7 +624,7 @@ static int CmdHFiClassReader(const char *Cmd) {
CLIParserInit(&ctx, "hf iclass reader", CLIParserInit(&ctx, "hf iclass reader",
"Act as a iCLASS reader. Look for iCLASS tags until Enter or the pm3 button is pressed", "Act as a iCLASS reader. Look for iCLASS tags until Enter or the pm3 button is pressed",
"hf iclass reader -@ -> continuous reader mode" "hf iclass reader -@ -> continuous reader mode"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -1700,7 +1700,7 @@ static int CmdHFiClassRestore(const char *Cmd) {
"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\n"
"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 --ki 0 --elite\n"
"hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 -k 1122334455667788 --elite\n" "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 -k 1122334455667788 --elite\n"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -3318,12 +3318,12 @@ static int CmdHFiClassPermuteKey(const char *Cmd) {
static int CmdHFiClassEncode(const char *Cmd) { static int CmdHFiClassEncode(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "hf iclass encode", CLIParserInit(&ctx, "hf iclass encode",
"Encode binary wiegand to block 7", "Encode binary wiegand to block 7",
"hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337\n" "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337\n"
"hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337, writing w elite key" "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337, writing w elite key"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -3449,11 +3449,11 @@ static int CmdHFiClassEncode(const char *Cmd) {
int isok = PM3_SUCCESS; int isok = PM3_SUCCESS;
// write // write
for (uint8_t i=0; i<4; i++) { for (uint8_t i = 0; i < 4; i++) {
isok = iclass_write_block(6 + i, credential + (i*8), key, use_credit_key, elite, rawkey, false, false, auth); isok = iclass_write_block(6 + i, credential + (i * 8), key, use_credit_key, elite, rawkey, false, false, auth);
switch (isok) { switch (isok) {
case PM3_SUCCESS: case PM3_SUCCESS:
PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i*8), 8)); PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8));
break; break;
default: default:
PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i);

View file

@ -550,7 +550,7 @@ static int CmdAWIDBrute(const char *Cmd) {
if (cn > 1) { if (cn > 1) {
if (down > 1) { if (down > 1) {
if (sendTry(fmtlen, fc, --down, delay, bits, size, verbose) != PM3_SUCCESS) { if (sendTry(fmtlen, fc, --down, delay, bits, size, verbose) != PM3_SUCCESS) {
return PM3_ESOFT; return PM3_ESOFT;
} }
} }
} }

View file

@ -190,9 +190,9 @@ static int CmdDestronClone(const char *Cmd) {
blocks[1] = (blocks[1] & 0xFFFF) | 0xAAE20000; blocks[1] = (blocks[1] & 0xFFFF) | 0xAAE20000;
PrintAndLogEx(INFO, "Preparing to clone Destron tag to " _YELLOW_("%s") " with ID: " _YELLOW_("%s") PrintAndLogEx(INFO, "Preparing to clone Destron tag to " _YELLOW_("%s") " with ID: " _YELLOW_("%s")
, cardtype , cardtype
, sprint_hex_inrow(data, datalen) , sprint_hex_inrow(data, datalen)
); );
print_blocks(blocks, ARRAYLEN(blocks)); print_blocks(blocks, ARRAYLEN(blocks));

View file

@ -543,10 +543,10 @@ static int CmdEM410xBrute(const char *Cmd) {
memcpy(testuid, uidblock + 5 * c, 5); memcpy(testuid, uidblock + 5 * c, 5);
PrintAndLogEx(INFO, "Bruteforce %d / %d: simulating UID " _YELLOW_("%s") PrintAndLogEx(INFO, "Bruteforce %d / %d: simulating UID " _YELLOW_("%s")
, c + 1 , c + 1
, uidcnt , uidcnt
, sprint_hex_inrow(testuid, sizeof(testuid)) , sprint_hex_inrow(testuid, sizeof(testuid))
); );
em410x_construct_emul_graph(testuid, clk); em410x_construct_emul_graph(testuid, clk);

View file

@ -791,7 +791,7 @@ int CmdEM4x05Write(const char *Cmd) {
} }
bool use_pwd = false; bool use_pwd = false;
uint32_t pwd = ( inputpwd != 0xFFFFFFFFFFFFFFFF) ? (inputpwd & 0xFFFFFFFF) : 0; uint32_t pwd = (inputpwd != 0xFFFFFFFFFFFFFFFF) ? (inputpwd & 0xFFFFFFFF) : 0;
if (pwd == 0xFFFFFFFF) { if (pwd == 0xFFFFFFFF) {
if (protect_operation) if (protect_operation)
PrintAndLogEx(INFO, "Writing protection words data %08X", data); PrintAndLogEx(INFO, "Writing protection words data %08X", data);
@ -809,12 +809,12 @@ int CmdEM4x05Write(const char *Cmd) {
// set Protect Words // set Protect Words
if (protect_operation) { if (protect_operation) {
res = em4x05_protect(pwd, use_pwd, data); res = em4x05_protect(pwd, use_pwd, data);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
} else { } else {
res = em4x05_write_word_ext(addr, pwd, use_pwd, data); res = em4x05_write_word_ext(addr, pwd, use_pwd, data);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
} }
@ -888,25 +888,25 @@ int CmdEM4x05Wipe(const char *Cmd) {
bool use_pwd = false; bool use_pwd = false;
uint32_t pwd = 0; uint32_t pwd = 0;
if ( inputpwd != 0xFFFFFFFFFFFFFFFF) { if (inputpwd != 0xFFFFFFFFFFFFFFFF) {
pwd = (inputpwd & 0xFFFFFFFF); pwd = (inputpwd & 0xFFFFFFFF);
use_pwd = true; use_pwd = true;
} }
// block 0 : User Data or Chip Info // block 0 : User Data or Chip Info
int res = em4x05_write_word_ext(0, pwd, use_pwd, chip_info); int res = em4x05_write_word_ext(0, pwd, use_pwd, chip_info);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
// block 1 : UID - this should be read only for EM4205 and EM4305 not sure about others // block 1 : UID - this should be read only for EM4205 and EM4305 not sure about others
res = em4x05_write_word_ext(1, pwd, use_pwd, chip_UID); res = em4x05_write_word_ext(1, pwd, use_pwd, chip_UID);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
PrintAndLogEx(INFO, "UID block write failed"); PrintAndLogEx(INFO, "UID block write failed");
} }
// block 2 : password // block 2 : password
res = em4x05_write_word_ext(2, pwd, use_pwd, block_data); res = em4x05_write_word_ext(2, pwd, use_pwd, block_data);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
@ -914,20 +914,20 @@ int CmdEM4x05Wipe(const char *Cmd) {
pwd = block_data; pwd = block_data;
// block 3 : user data // block 3 : user data
res = em4x05_write_word_ext(3, pwd, use_pwd, block_data); res = em4x05_write_word_ext(3, pwd, use_pwd, block_data);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
// block 4 : config // block 4 : config
res = em4x05_write_word_ext(4, pwd, use_pwd, config); res = em4x05_write_word_ext(4, pwd, use_pwd, config);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
// Remainder of user/data blocks // Remainder of user/data blocks
for (addr = 5; addr < 14; addr++) {// Clear user data blocks for (addr = 5; addr < 14; addr++) {// Clear user data blocks
res = em4x05_write_word_ext(addr, pwd, use_pwd, block_data); res = em4x05_write_word_ext(addr, pwd, use_pwd, block_data);
if ( res != PM3_SUCCESS) { if (res != PM3_SUCCESS) {
return res; return res;
} }
} }

View file

@ -359,10 +359,10 @@ int CmdEM4x50Brute(const char *Cmd) {
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int first_len = 0; int first_len = 0;
uint8_t first[4] = {0,0,0,0}; uint8_t first[4] = {0, 0, 0, 0};
CLIGetHexWithReturn(ctx, 1, first, &first_len); CLIGetHexWithReturn(ctx, 1, first, &first_len);
int last_len = 0; int last_len = 0;
uint8_t last[4] = {0,0,0,0}; uint8_t last[4] = {0, 0, 0, 0};
CLIGetHexWithReturn(ctx, 2, last, &last_len); CLIGetHexWithReturn(ctx, 2, last, &last_len);
CLIParserFree(ctx); CLIParserFree(ctx);
@ -370,9 +370,9 @@ int CmdEM4x50Brute(const char *Cmd) {
PrintAndLogEx(FAILED, "password length must be 4 bytes"); PrintAndLogEx(FAILED, "password length must be 4 bytes");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (last_len != 4) { if (last_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes"); PrintAndLogEx(FAILED, "password length must be 4 bytes");
return PM3_EINVARG; return PM3_EINVARG;
} }
em4x50_data_t etd; em4x50_data_t etd;
@ -390,10 +390,10 @@ int CmdEM4x50Brute(const char *Cmd) {
dur_s -= dur_h * 3600 + dur_m * 60; dur_s -= dur_h * 3600 + dur_m * 60;
PrintAndLogEx(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]" PrintAndLogEx(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]"
, no_iter , no_iter
, etd.password1 , etd.password1
, etd.password2 , etd.password2
); );
PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s); PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s);
// start // start
@ -494,7 +494,7 @@ int CmdEM4x50Chk(const char *Cmd) {
resp.data.asBytes[2], resp.data.asBytes[2],
resp.data.asBytes[1], resp.data.asBytes[1],
resp.data.asBytes[0] resp.data.asBytes[0]
); );
} else { } else {
PrintAndLogEx(FAILED, "No key found"); PrintAndLogEx(FAILED, "No key found");
} }
@ -652,7 +652,7 @@ int CmdEM4x50Info(const char *Cmd) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if ( resp.status == PM3_SUCCESS) if (resp.status == PM3_SUCCESS)
print_info_result(resp.data.asBytes); print_info_result(resp.data.asBytes);
else else
PrintAndLogEx(FAILED, "Reading tag " _RED_("failed")); PrintAndLogEx(FAILED, "Reading tag " _RED_("failed"));
@ -940,9 +940,9 @@ int CmdEM4x50WritePwd(const char *Cmd) {
} }
PrintAndLogEx(SUCCESS, "Writing new password %s (%s)" PrintAndLogEx(SUCCESS, "Writing new password %s (%s)"
, sprint_hex_inrow(npwd, sizeof(npwd)) , sprint_hex_inrow(npwd, sizeof(npwd))
, _GREEN_("ok") , _GREEN_("ok")
); );
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -1013,7 +1013,7 @@ int CmdEM4x50Wipe(const char *Cmd) {
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
if ( resp.status != PM3_SUCCESS) { if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(FAILED, "Wiping data " _RED_("failed")); PrintAndLogEx(FAILED, "Wiping data " _RED_("failed"));
return PM3_ESOFT; return PM3_ESOFT;
@ -1160,10 +1160,10 @@ static command_t CommandTable[] = {
{"login", CmdEM4x50Login, IfPm3EM4x50, "login into EM4x50"}, {"login", CmdEM4x50Login, IfPm3EM4x50, "login into EM4x50"},
{"rdbl", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"}, {"rdbl", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"},
{"wrbl", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"}, {"wrbl", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"},
{"writepwd",CmdEM4x50WritePwd, IfPm3EM4x50, "change password of EM4x50"}, {"writepwd", CmdEM4x50WritePwd, IfPm3EM4x50, "change password of EM4x50"},
{"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe EM4x50 tag"}, {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe EM4x50 tag"},
{"reader", CmdEM4x50Reader, IfPm3EM4x50, "show standard read mode data of EM4x50"}, {"reader", CmdEM4x50Reader, IfPm3EM4x50, "show standard read mode data of EM4x50"},
{"restore",CmdEM4x50Restore, IfPm3EM4x50, "restore EM4x50 dump to tag"}, {"restore", CmdEM4x50Restore, IfPm3EM4x50, "restore EM4x50 dump to tag"},
{"sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate EM4x50 tag"}, {"sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate EM4x50 tag"},
{"eload", CmdEM4x50ELoad, IfPm3EM4x50, "upload dump of EM4x50 to emulator memory"}, {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "upload dump of EM4x50 to emulator memory"},
{"esave", CmdEM4x50ESave, IfPm3EM4x50, "save emulator memory to file"}, {"esave", CmdEM4x50ESave, IfPm3EM4x50, "save emulator memory to file"},

View file

@ -16,25 +16,62 @@
#include "commonutil.h" #include "commonutil.h"
#include "em4x70.h" #include "em4x70.h"
#define LOCKBIT_0 BITMASK(6)
#define LOCKBIT_1 BITMASK(7)
#define BYTES2UINT16(x) ((x[1] << 8) | (x[0]))
#define BYTES2UINT32(x) ((x[3] << 24) | (x[2] << 16) | (x[1] << 8) | (x[0]))
#define INDEX_TO_BLOCK(x) (((32-x)/2)-1)
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
static void print_info_result(uint8_t *data) { static void print_info_result(const uint8_t *data) {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(INFO, "-----------------------------------------------");
// data section PrintAndLogEx(INFO, "Block | data | info");
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------+----------+-----------------------------");
PrintAndLogEx(INFO, _YELLOW_("EM4x70 data:"));
for(int i=1; i <= 32; i+=2) { // Print out each section as memory map in datasheet
PrintAndLogEx(NORMAL, "%02X %02X", data[32-i], data[32-i-1]);
// Start with UM2
for (int i = 0; i < 8; i += 2) {
PrintAndLogEx(INFO, " %2d | %02X %02X | UM2", INDEX_TO_BLOCK(i), data[31 - i], data[31 - i - 1]);
} }
PrintAndLogEx(NORMAL, "Tag ID: %02X %02X %02X %02X", data[7], data[6], data[5], data[4]); PrintAndLogEx(INFO, "------+----------+-----------------------------");
PrintAndLogEx(NORMAL, "Lockbit 0: %d", (data[3] & 0x40) ? 1:0);
PrintAndLogEx(NORMAL, "Lockbit 1: %d", (data[3] & 0x80) ? 1:0); // Print PIN (will never have data)
for (int i = 8; i < 12; i += 2) {
PrintAndLogEx(INFO, " %2d | -- -- | PIN write only", INDEX_TO_BLOCK(i));
}
PrintAndLogEx(INFO, "------+----------+-----------------------------");
// Print Crypt Key (will never have data)
for (int i = 12; i < 24; i += 2) {
PrintAndLogEx(INFO, " %2d | -- -- | KEY write-only", INDEX_TO_BLOCK(i));
}
PrintAndLogEx(INFO, "------+----------+-----------------------------");
// Print ID
for (int i = 24; i < 28; i += 2) {
PrintAndLogEx(INFO, " %2d | %02X %02X | ID", INDEX_TO_BLOCK(i), data[31 - i], data[31 - i - 1]);
}
PrintAndLogEx(INFO, "------+----------+-----------------------------");
// Print UM1
for (int i = 28; i < 32; i += 2) {
PrintAndLogEx(INFO, " %2d | %02X %02X | UM1", INDEX_TO_BLOCK(i), data[31 - i], data[31 - i - 1]);
}
PrintAndLogEx(INFO, "------+----------+-----------------------------");
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "Tag ID: %02X %02X %02X %02X", data[7], data[6], data[5], data[4]);
PrintAndLogEx(INFO, "Lockbit 0: %d", (data[3] & LOCKBIT_0) ? 1 : 0);
PrintAndLogEx(INFO, "Lockbit 1: %d", (data[3] & LOCKBIT_1) ? 1 : 0);
PrintAndLogEx(INFO, "Tag is %s.", (data[3] & LOCKBIT_0) ? _RED_("LOCKED") : _GREEN_("UNLOCKED"));
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
} }
@ -50,7 +87,7 @@ int em4x70_info(void) {
PacketResponseNG resp; PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT)) { if (!WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "(em4x70) timeout while waiting for reply."); PrintAndLogEx(WARNING, "(em4x70) Timeout while waiting for reply.");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
@ -77,18 +114,18 @@ int CmdEM4x70Info(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x10 info", CLIParserInit(&ctx, "lf em 4x70 info",
"Tag Information EM4x70\n" "Tag Information EM4x70\n"
" Tag variants include ID48 automotive transponder.\n" " Tag variants include ID48 automotive transponder.\n"
" ID48 does not use command parity (default).\n" " ID48 does not use command parity (default).\n"
" V4070 and EM4170 do require parity bit.", " V4070 and EM4170 do require parity bit.",
"lf em 4x70 info\n" "lf em 4x70 info\n"
"lf em 4x70 -p -> adds parity bit to commands\n" "lf em 4x70 info --par -> adds parity bit to command\n"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_lit0("p", "parity", "Add parity bit when sending commands"), arg_lit0(NULL, "par", "Add parity bit when sending commands"),
arg_param_end arg_param_end
}; };
@ -101,7 +138,7 @@ int CmdEM4x70Info(const char *Cmd) {
PacketResponseNG resp; PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT)) { if (!WaitForResponseTimeout(CMD_LF_EM4X70_INFO, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "timeout while waiting for reply."); PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
@ -110,13 +147,312 @@ int CmdEM4x70Info(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
PrintAndLogEx(FAILED, "reading tag " _RED_("failed")); PrintAndLogEx(FAILED, "Reading " _RED_("Failed"));
return PM3_ESOFT;
}
int CmdEM4x70Write(const char *Cmd) {
// write one block/word (16 bits) to the tag at given block address (0-15)
em4x70_data_t etd = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 write",
"Write EM4x70\n",
"lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n"
"lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
arg_int1("b", "block", "<dec>", "block/word address, dec"),
arg_str1("d", "data", "<hex>", "data, 2 bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
etd.parity = arg_get_lit(ctx, 1);
int addr = arg_get_int(ctx, 2);
int word_len = 0;
uint8_t word[2] = {0x0};
CLIGetHexWithReturn(ctx, 3, word, &word_len);
CLIParserFree(ctx);
if (addr < 0 || addr >= EM4X70_NUM_BLOCKS) {
PrintAndLogEx(FAILED, "block has to be within range [0, 15]");
return PM3_EINVARG;
}
if (word_len != 2) {
PrintAndLogEx(FAILED, "word/data length must be 2 bytes instead of %d", word_len);
return PM3_EINVARG;
}
etd.address = (uint8_t) addr;
etd.word = BYTES2UINT16(word);;
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_WRITE, &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, "Writing " _RED_("Failed"));
return PM3_ESOFT;
}
int CmdEM4x70Unlock(const char *Cmd) {
// send pin code to device, unlocking it for writing
em4x70_data_t etd = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 unlock",
"Unlock EM4x70 by sending PIN\n"
"Default pin may be:\n"
" AAAAAAAA\n"
" 00000000\n",
"lf em 4x70 unlock -p 11223344 -> Unlock with PIN\n"
"lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
arg_str1("p", "pin", "<hex>", "pin, 4 bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
etd.parity = arg_get_lit(ctx, 1);
int pin_len = 0;
uint8_t pin[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, pin, &pin_len);
CLIParserFree(ctx);
if (pin_len != 4) {
PrintAndLogEx(FAILED, "PIN length must be 4 bytes instead of %d", pin_len);
return PM3_EINVARG;
}
etd.pin = BYTES2UINT32(pin);
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_UNLOCK, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_UNLOCK, &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, "Unlocking tag " _RED_("failed"));
return PM3_ESOFT;
}
int CmdEM4x70Auth(const char *Cmd) {
// Authenticate transponder
// Send 56-bit random number + pre-computed f(rnd, k) to transponder.
// Transponder will respond with a response
em4x70_data_t etd = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 auth",
"Authenticate against an EM4x70 by sending random number (RN) and F(RN)\n"
" If F(RN) is incorrect based on the tag crypt key, the tag will not respond",
"lf em 4x70 auth --rnd 45F54ADA252AAC --frn 4866BB70 --> Test authentication, tag will respond if successful\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
arg_str1(NULL, "rnd", "<hex>", "Random 56-bit"),
arg_str1(NULL, "frn", "<hex>", "F(RN) 28-bit as 4 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
etd.parity = arg_get_lit(ctx, 1);
int rnd_len = 7;
CLIGetHexWithReturn(ctx, 2, etd.rnd, &rnd_len);
int frnd_len = 4;
CLIGetHexWithReturn(ctx, 3, etd.frnd, &frnd_len);
CLIParserFree(ctx);
if (rnd_len != 7) {
PrintAndLogEx(FAILED, "Random number length must be 7 bytes instead of %d", rnd_len);
return PM3_EINVARG;
}
if (frnd_len != 4) {
PrintAndLogEx(FAILED, "F(RN) length must be 4 bytes instead of %d", frnd_len);
return PM3_EINVARG;
}
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_AUTH, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_AUTH, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status) {
// Response is 20-bit from tag
PrintAndLogEx(INFO, "Tag Auth Response: %02X %02X %02X", resp.data.asBytes[2], resp.data.asBytes[1], resp.data.asBytes[0]);
return PM3_SUCCESS;
}
PrintAndLogEx(FAILED, "TAG Authentication " _RED_("Failed"));
return PM3_ESOFT;
}
int CmdEM4x70WritePIN(const char *Cmd) {
em4x70_data_t etd = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 writepin",
"Write PIN\n",
"lf em 4x70 writepin -p 11223344 -> Write PIN\n"
"lf em 4x70 writepin -p 11223344 --par -> Write PIN using parity commands\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
arg_str1("p", "pin", "<hex>", "pin, 4 bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
etd.parity = arg_get_lit(ctx, 1);
int pin_len = 0;
uint8_t pin[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, pin, &pin_len);
CLIParserFree(ctx);
if (pin_len != 4) {
PrintAndLogEx(FAILED, "PIN length must be 4 bytes instead of %d", pin_len);
return PM3_EINVARG;
}
etd.pin = BYTES2UINT32(pin);
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_WRITEPIN, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_WRITEPIN, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status) {
print_info_result(resp.data.asBytes);
PrintAndLogEx(INFO, "Writing new PIN: " _GREEN_("SUCCESS"));
return PM3_SUCCESS;
}
PrintAndLogEx(FAILED, "Writing new PIN: " _RED_("FAILED"));
return PM3_ESOFT;
}
int CmdEM4x70WriteKey(const char *Cmd) {
// Write new crypt key to tag
em4x70_data_t etd = {0};
CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x70 writekey",
"Write new 96-bit key to tag\n",
"lf em 4x70 writekey -k F32AA98CF5BE4ADFA6D3480B\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0(NULL, "par", "Add parity bit when sending commands"),
arg_str1("k", "key", "<hex>", "Crypt Key as 12 hex bytes"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
etd.parity = arg_get_lit(ctx, 1);
int key_len = 12;
CLIGetHexWithReturn(ctx, 2, etd.crypt_key, &key_len);
CLIParserFree(ctx);
if (key_len != 12) {
PrintAndLogEx(FAILED, "Crypt key length must be 12 bytes instead of %d", key_len);
return PM3_EINVARG;
}
clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_WRITEKEY, (uint8_t *)&etd, sizeof(etd));
PacketResponseNG resp;
if (!WaitForResponseTimeout(CMD_LF_EM4X70_WRITEKEY, &resp, TIMEOUT)) {
PrintAndLogEx(WARNING, "Timeout while waiting for reply.");
return PM3_ETIMEOUT;
}
if (resp.status) {
PrintAndLogEx(INFO, "Writing new crypt key: " _GREEN_("SUCCESS"));
return PM3_SUCCESS;
}
PrintAndLogEx(FAILED, "Writing new crypt key: " _RED_("FAILED"));
return PM3_ESOFT; return PM3_ESOFT;
} }
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"}, {"help", CmdHelp, AlwaysAvailable, "This help"},
{"info", CmdEM4x70Info, IfPm3EM4x70, "tag information EM4x70"}, {"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"},
{"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"},
{"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"},
{"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"},
{"writepin", CmdEM4x70WritePIN, IfPm3EM4x70, "Write PIN"},
{"writekey", CmdEM4x70WriteKey, IfPm3EM4x70, "Write Crypt Key"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -18,6 +18,11 @@
int CmdLFEM4X70(const char *Cmd); int CmdLFEM4X70(const char *Cmd);
int CmdEM4x70Info(const char *Cmd); int CmdEM4x70Info(const char *Cmd);
int CmdEM4x70Write(const char *Cmd);
int CmdEM4x70Unlock(const char *Cmd);
int CmdEM4x70Auth(const char *Cmd);
int CmdEM4x70WritePIN(const char *Cmd);
int CmdEM4x70WriteKey(const char *Cmd);
int em4x70_info(void); int em4x70_info(void);
bool detect_4x70_block(void); bool detect_4x70_block(void);

View file

@ -773,10 +773,10 @@ static int CmdFdxBClone(const char *Cmd) {
free(bs); free(bs);
PrintAndLogEx(INFO, "Preparing to clone FDX-B to " _YELLOW_("%s") " with animal ID: " _GREEN_("%04u-%"PRIu64) PrintAndLogEx(INFO, "Preparing to clone FDX-B to " _YELLOW_("%s") " with animal ID: " _GREEN_("%04u-%"PRIu64)
, cardtype , cardtype
, country_code , country_code
, national_code , national_code
); );
print_blocks(blocks, ARRAYLEN(blocks)); print_blocks(blocks, ARRAYLEN(blocks));
int res; int res;

View file

@ -233,7 +233,7 @@ static int CmdGallagherClone(const char *Cmd) {
static int CmdGallagherSim(const char *Cmd) { static int CmdGallagherSim(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf gallagher sim", CLIParserInit(&ctx, "lf gallagher sim",
"Enables simulation of GALLAGHER card with specified card number.\n" "Enables simulation of GALLAGHER card with specified card number.\n"
"Simulation runs until the button is pressed or another USB command is issued.\n", "Simulation runs until the button is pressed or another USB command is issued.\n",

View file

@ -226,10 +226,10 @@ static int CmdGuardClone(const char *Cmd) {
free(bs); free(bs);
PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u") PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u")
, cardtype , cardtype
, facilitycode , facilitycode
, cardnumber , cardnumber
); );
print_blocks(blocks, ARRAYLEN(blocks)); print_blocks(blocks, ARRAYLEN(blocks));
int res; int res;
@ -281,9 +281,9 @@ static int CmdGuardSim(const char *Cmd) {
} }
PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u") PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u")
, facilitycode , facilitycode
, cardnumber , cardnumber
); );
// Guard uses: clk: 64, invert: 0, encoding: 2 (ASK Biphase) // Guard uses: clk: 64, invert: 0, encoding: 2 (ASK Biphase)
lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs)); lf_asksim_t *payload = calloc(1, sizeof(lf_asksim_t) + sizeof(bs));

View file

@ -65,11 +65,11 @@ static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, boo
if (verbose) { if (verbose) {
PrintAndLogEx(INFO, "Trying FC: " _YELLOW_("%u") " CN: " _YELLOW_("%"PRIu64) " Issue level: " _YELLOW_("%u") " OEM: " _YELLOW_("%u") PrintAndLogEx(INFO, "Trying FC: " _YELLOW_("%u") " CN: " _YELLOW_("%"PRIu64) " Issue level: " _YELLOW_("%u") " OEM: " _YELLOW_("%u")
, card->FacilityCode , card->FacilityCode
, card->CardNumber , card->CardNumber
, card->IssueLevel , card->IssueLevel
, card->OEM , card->OEM
); );
} }
lf_hidsim_t payload; lf_hidsim_t payload;

View file

@ -250,7 +250,7 @@ static int CmdIndalaDemod(const char *Cmd) {
"lf indala demod --clock 32 -> demod a Indala tag from GraphBuffer using a clock of RF/32\n" "lf indala demod --clock 32 -> demod a Indala tag from GraphBuffer using a clock of RF/32\n"
"lf indala demod --clock 32 -i -> demod a Indala tag from GraphBuffer using a clock of RF/32 and inverting data\n" "lf indala demod --clock 32 -i -> demod a Indala tag from GraphBuffer using a clock of RF/32 and inverting data\n"
"lf indala demod --clock 64 -i --maxerror 0 -> demod a Indala tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors" "lf indala demod --clock 64 -i --maxerror 0 -> demod a Indala tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -281,7 +281,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) {
"It's now considered obsolete but remains because it has sometimes its advantages.", "It's now considered obsolete but remains because it has sometimes its advantages.",
"lf indala altdemod\n" "lf indala altdemod\n"
"lf indala altdemod --long -> demod a Indala tag from GraphBuffer as 224 bit long format" "lf indala altdemod --long -> demod a Indala tag from GraphBuffer as 224 bit long format"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
@ -583,9 +583,9 @@ static int CmdIndalaSim(const char *Cmd) {
// lf simpsk 1 c 32 r 2 d 0102030405060708 // lf simpsk 1 c 32 r 2 d 0102030405060708
PrintAndLogEx(SUCCESS, "Simulating " _YELLOW_("%s") " Indala raw " _YELLOW_("%s") PrintAndLogEx(SUCCESS, "Simulating " _YELLOW_("%s") " Indala raw " _YELLOW_("%s")
, (is_long_uid) ? "224b" : "64b" , (is_long_uid) ? "224b" : "64b"
, sprint_hex_inrow(raw, raw_len) , sprint_hex_inrow(raw, raw_len)
); );
PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command"); PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command");
// indala PSK, clock 32, carrier 0 // indala PSK, clock 32, carrier 0
@ -691,9 +691,9 @@ static int CmdIndalaClone(const char *Cmd) {
// 224 BIT UID // 224 BIT UID
// config for Indala (RF/32;PSK2 with RF/2;Maxblock=7) // config for Indala (RF/32;PSK2 with RF/2;Maxblock=7)
PrintAndLogEx(INFO, "Preparing to clone Indala 224bit to " _YELLOW_("%s") " raw " _GREEN_("%s") PrintAndLogEx(INFO, "Preparing to clone Indala 224bit to " _YELLOW_("%s") " raw " _GREEN_("%s")
, cardtype , cardtype
, sprint_hex_inrow(raw, raw_len) , sprint_hex_inrow(raw, raw_len)
); );
} else { } else {
@ -749,9 +749,9 @@ static int CmdIndalaClone(const char *Cmd) {
// config for Indala 64 format (RF/32;PSK1 with RF/2;Maxblock=2) // config for Indala 64 format (RF/32;PSK1 with RF/2;Maxblock=2)
PrintAndLogEx(INFO, "Preparing to clone Indala 64bit to " _YELLOW_("%s") " raw " _GREEN_("%s") PrintAndLogEx(INFO, "Preparing to clone Indala 64bit to " _YELLOW_("%s") " raw " _GREEN_("%s")
, cardtype , cardtype
, sprint_hex_inrow(raw, raw_len) , sprint_hex_inrow(raw, raw_len)
); );
} }
print_blocks(blocks, max); print_blocks(blocks, max);

View file

@ -318,12 +318,12 @@ static int CmdIOProxClone(const char *Cmd) {
blocks[2] = bytebits_to_byte(bits + 32, 32); blocks[2] = bytebits_to_byte(bits + 32, 32);
PrintAndLogEx(INFO, "Preparing to clone ioProx to " _YELLOW_("%s") " with Version: " _GREEN_("%u") " FC: " _GREEN_("%u (0x%02x)") " CN: " _GREEN_("%u") PrintAndLogEx(INFO, "Preparing to clone ioProx to " _YELLOW_("%s") " with Version: " _GREEN_("%u") " FC: " _GREEN_("%u (0x%02x)") " CN: " _GREEN_("%u")
, cardtype , cardtype
, version , version
, fc , fc
, fc , fc
, cn , cn
); );
print_blocks(blocks, ARRAYLEN(blocks)); print_blocks(blocks, ARRAYLEN(blocks));
int res; int res;

View file

@ -219,7 +219,7 @@ static int CmdJablotronClone(const char *Cmd) {
free(bits); free(bits);
uint64_t id = getJablontronCardId(fullcode); uint64_t id = getJablontronCardId(fullcode);
PrintAndLogEx(INFO, "Preparing to clone Jablotron to " _YELLOW_("%s") " with FullCode: " _GREEN_("%"PRIx64)" id: " _GREEN_("%"PRIx64), cardtype, fullcode, id); PrintAndLogEx(INFO, "Preparing to clone Jablotron to " _YELLOW_("%s") " with FullCode: " _GREEN_("%"PRIx64)" id: " _GREEN_("%"PRIx64), cardtype, fullcode, id);
print_blocks(blocks, ARRAYLEN(blocks)); print_blocks(blocks, ARRAYLEN(blocks));

View file

@ -145,13 +145,13 @@ int demodNedap(bool verbose) {
badgeId = r1 * 10000 + r2 * 1000 + r3 * 100 + r4 * 10 + r5; badgeId = r1 * 10000 + r2 * 1000 + r3 * 100 + r4 * 10 + r5;
PrintAndLogEx(SUCCESS, "NEDAP (%s) - ID: " _YELLOW_("%05u") " subtype: " _YELLOW_("%1u")" customer code: " _YELLOW_("%u / 0x%03X") " Raw: " _YELLOW_("%s") PrintAndLogEx(SUCCESS, "NEDAP (%s) - ID: " _YELLOW_("%05u") " subtype: " _YELLOW_("%1u")" customer code: " _YELLOW_("%u / 0x%03X") " Raw: " _YELLOW_("%s")
, (size == 128) ? "128b" : "64b" , (size == 128) ? "128b" : "64b"
, badgeId , badgeId
, subtype , subtype
, customerCode , customerCode
, customerCode , customerCode
, sprint_hex_inrow(data, size / 8) , sprint_hex_inrow(data, size / 8)
); );
PrintAndLogEx(DEBUG, "Checksum (%s) 0x%04X", _GREEN_("ok"), checksum); PrintAndLogEx(DEBUG, "Checksum (%s) 0x%04X", _GREEN_("ok"), checksum);
} else { } else {
@ -454,7 +454,7 @@ static int CmdLFNedapClone(const char *Cmd) {
NedapGen(sub_type, customer_code, id, is_long, data); NedapGen(sub_type, customer_code, id, is_long, data);
for (uint8_t i = 1; i < max ; i++) { for (uint8_t i = 1; i < max ; i++) {
blocks[i] = bytes_to_num (data + ((i - 1) * 4), 4); blocks[i] = bytes_to_num(data + ((i - 1) * 4), 4);
} }
PrintAndLogEx(SUCCESS, "Preparing to clone NEDAP to " _YELLOW_("%s") " tag", cardtype); PrintAndLogEx(SUCCESS, "Preparing to clone NEDAP to " _YELLOW_("%s") " tag", cardtype);

View file

@ -125,7 +125,7 @@ static int CmdLFPCF7931Config(const char *Cmd) {
} }
if (pwd_len) { if (pwd_len) {
memcpy(configPcf.Pwd, pwd, sizeof(configPcf.Pwd)); memcpy(configPcf.Pwd, pwd, sizeof(configPcf.Pwd));
} }
if (delay != -1) { if (delay != -1) {
configPcf.InitDelay = (delay & 0xFFFF); configPcf.InitDelay = (delay & 0xFFFF);
@ -134,7 +134,7 @@ static int CmdLFPCF7931Config(const char *Cmd) {
configPcf.OffsetWidth = (ow & 0xFFFF); configPcf.OffsetWidth = (ow & 0xFFFF);
} }
if (op != 0xFFFF) { if (op != 0xFFFF) {
configPcf.OffsetPosition =(op & 0xFFFF); configPcf.OffsetPosition = (op & 0xFFFF);
} }
pcf7931_printConfig(); pcf7931_printConfig();

View file

@ -354,7 +354,7 @@ static int CmdTIWrite(const char *Cmd) {
payload.crc = bytes_to_num(crc, crc_len); payload.crc = bytes_to_num(crc, crc_len);
clearCommandBuffer(); clearCommandBuffer();
SendCommandNG(CMD_LF_TI_WRITE, (uint8_t*)&payload, sizeof(payload)); SendCommandNG(CMD_LF_TI_WRITE, (uint8_t *)&payload, sizeof(payload));
PrintAndLogEx(SUCCESS, "Done"); PrintAndLogEx(SUCCESS, "Done");
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti reader`") " to verify"); PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf ti reader`") " to verify");
return PM3_SUCCESS; return PM3_SUCCESS;

View file

@ -165,19 +165,35 @@ int CmdRem(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "rem", CLIParserInit(&ctx, "rem",
"Add a text line in log file", "Add a text line in log file",
"rem" "rem my message -> adds a timestamp with `my message`"
); );
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_strx1(NULL, NULL, NULL, "message line you want inserted"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, false);
CLIParserFree(ctx);
struct arg_str* foo = arg_get_str(ctx, 1);
size_t count = 0;
size_t len = 0;
do {
count += strlen(foo->sval[len]);
} while (len++ < (foo->count - 1));
char s[count + foo->count];
memset(s, 0, sizeof(s));
len = 0;
do {
snprintf(s + strlen(s), sizeof(s) - strlen(s), "%s ", foo->sval[len]);
} while (len++ < (foo->count - 1));
CLIParserFree(ctx);
char buf[22] = {0}; char buf[22] = {0};
AppendDate(buf, sizeof(buf), NULL); AppendDate(buf, sizeof(buf), NULL);
PrintAndLogEx(NORMAL, "%s remark: %s", buf, Cmd); PrintAndLogEx(SUCCESS, "%s remark: %s", buf, s);
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -617,7 +617,7 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen) {
} }
int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVGPO(EMVCommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {
return EMVExchange(channel, LeaveFieldON, (sAPDU) {0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, Result, MaxResultLen, ResultLen, sw, tlv); return EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU) {0x80, 0xa8, 0x00, 0x00, PDOLLen, PDOL}, true, Result, MaxResultLen, ResultLen, sw, tlv);
} }
int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { int EMVReadRecord(EMVCommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) {

View file

@ -53,7 +53,7 @@ int preferences_load(void) {
session.overlay.h = 200; session.overlay.h = 200;
session.overlay.w = session.plot.w; session.overlay.w = session.plot.w;
session.overlay_sliders = true; session.overlay_sliders = true;
session.show_hints = false; session.show_hints = true;
// setDefaultPath (spDefault, ""); // setDefaultPath (spDefault, "");
// setDefaultPath (spDump, ""); // setDefaultPath (spDump, "");

View file

@ -109,7 +109,7 @@ uint32_t reflect32(uint32_t b) {
// swap bytes // swap bytes
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8); v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs // swap 2-byte long pairs
v = ( v >> 16 ) | ( v << 16); v = (v >> 16) | (v << 16);
return v; return v;
} }

View file

@ -1082,8 +1082,8 @@ int DetectPSKClock(uint8_t *dest, size_t size, int clock, size_t *firstPhaseShif
if (g_debugMode == 2) prnt("DEBUG PSK: firstFullWave: %zu, waveLen: %d", firstFullWave, fullWaveLen); if (g_debugMode == 2) prnt("DEBUG PSK: firstFullWave: %zu, waveLen: %d", firstFullWave, fullWaveLen);
// Avoid autodetect if user selected a clock // Avoid autodetect if user selected a clock
for(uint8_t validClk = 1; validClk < 8; validClk++) { for (uint8_t validClk = 1; validClk < 8; validClk++) {
if(clock == clk[validClk]) return(clock); if (clock == clk[validClk]) return (clock);
} }
//test each valid clock from greatest to smallest to see which lines up //test each valid clock from greatest to smallest to see which lines up

View file

@ -38,7 +38,6 @@ Check column "offline" for their availability.
|`analyse nuid `|Y |`create NUID from 7byte UID` |`analyse nuid `|Y |`create NUID from 7byte UID`
|`analyse demodbuff `|Y |`Load binary string to demodbuffer` |`analyse demodbuff `|Y |`Load binary string to demodbuffer`
|`analyse freq `|Y |`Calc wave lengths` |`analyse freq `|Y |`Calc wave lengths`
|`analyse foo `|Y |`muxer`
### data ### data
@ -143,6 +142,7 @@ Check column "offline" for their availability.
|`hf 14a raw `|N |`Send raw hex data to tag` |`hf 14a raw `|N |`Send raw hex data to tag`
|`hf 14a antifuzz `|N |`Fuzzing the anticollision phase. Warning! Readers may react strange` |`hf 14a antifuzz `|N |`Fuzzing the anticollision phase. Warning! Readers may react strange`
|`hf 14a config `|N |`Configure 14a settings (use with caution)` |`hf 14a config `|N |`Configure 14a settings (use with caution)`
|`hf 14a apdufind `|N |`Enuerate APDUs - CLA/INS/P1P2`
### hf 14b ### hf 14b
@ -248,28 +248,29 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`hf iclass help `|Y |`This help` |`hf iclass help `|Y |` This help`
|`hf iclass dump `|N |`[options..] Dump Picopass / iCLASS tag to file` |`hf iclass dump `|N |`[*] Dump Picopass / iCLASS tag to file`
|`hf iclass info `|Y |` Tag information` |`hf iclass info `|Y |` Tag information`
|`hf iclass list `|Y |` List iclass history` |`hf iclass list `|Y |` List iclass history`
|`hf iclass rdbl `|N |`[options..] Read Picopass / iCLASS block` |`hf iclass rdbl `|N |`[*] Read Picopass / iCLASS block`
|`hf iclass reader `|N |` Act like an Picopass / iCLASS reader` |`hf iclass reader `|N |` Act like an Picopass / iCLASS reader`
|`hf iclass restore `|N |`[options..] Restore a dump file onto a Picopass / iCLASS tag` |`hf iclass restore `|N |`[*] Restore a dump file onto a Picopass / iCLASS tag`
|`hf iclass sniff `|N |` Eavesdrop Picopass / iCLASS communication` |`hf iclass sniff `|N |` Eavesdrop Picopass / iCLASS communication`
|`hf iclass wrbl `|N |`[options..] Write Picopass / iCLASS block` |`hf iclass wrbl `|N |`[*] Write Picopass / iCLASS block`
|`hf iclass chk `|N |`[options..] Check keys` |`hf iclass chk `|N |`[*] Check keys`
|`hf iclass loclass `|Y |`[options..] Use loclass to perform bruteforce reader attack` |`hf iclass loclass `|Y |`[*] Use loclass to perform bruteforce reader attack`
|`hf iclass lookup `|Y |`[options..] Uses authentication trace to check for key in dictionary file` |`hf iclass lookup `|Y |`[*] Uses authentication trace to check for key in dictionary file`
|`hf iclass sim `|N |`[options..] Simulate iCLASS tag` |`hf iclass sim `|N |`[*] Simulate iCLASS tag`
|`hf iclass eload `|N |`[f <fn> ] Load Picopass / iCLASS dump file into emulator memory` |`hf iclass eload `|N |`[*] Load Picopass / iCLASS dump file into emulator memory`
|`hf iclass esave `|N |`[f <fn> ] Save emulator memory to file` |`hf iclass esave `|N |`[*] Save emulator memory to file`
|`hf iclass eview `|N |`[options..] View emulator memory` |`hf iclass eview `|N |`[.] View emulator memory`
|`hf iclass calcnewkey `|Y |`[options..] Calc diversified keys (blocks 3 & 4) to write new keys` |`hf iclass calcnewkey `|Y |`[*] Calc diversified keys (blocks 3 & 4) to write new keys`
|`hf iclass encrypt `|Y |`[options..] Encrypt given block data` |`hf iclass encode `|Y |`[*] Encode binary wiegand to block 7`
|`hf iclass decrypt `|Y |`[options..] Decrypt given block data or tag dump file` |`hf iclass encrypt `|Y |`[*] Encrypt given block data`
|`hf iclass managekeys `|Y |`[options..] Manage keys to use with iclass commands` |`hf iclass decrypt `|Y |`[*] Decrypt given block data or tag dump file`
|`hf iclass permutekey `|N |` Permute function from 'heart of darkness' paper` |`hf iclass managekeys `|Y |`[*] Manage keys to use with iclass commands`
|`hf iclass view `|Y |`[options..] Display content from tag dump file` |`hf iclass permutekey `|N |` Permute function from 'heart of darkness' paper`
|`hf iclass view `|Y |`[*] Display content from tag dump file`
### hf legic ### hf legic
@ -577,10 +578,10 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`lf em help `|Y |`This help` |`lf em help `|Y |`This help`
|`lf em 410x `|Y |`EM 410x commands...` |`lf em 410x `|Y |`EM 4102 commands...`
|`lf em 4x05 `|Y |`EM 4x05 commands...` |`lf em 4x05 `|Y |`EM 4205 / 4305 / 4369 / 4469 commands...`
|`lf em 4x50 `|Y |`EM 4x50 commands...` |`lf em 4x50 `|Y |`EM 4350 / 4450 commands...`
|`lf em 4x70 `|Y |`EM 4x70 commands...` |`lf em 4x70 `|Y |`EM 4070 / 4170 commands...`
### lf fdxb ### lf fdxb
@ -672,9 +673,9 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`lf indala help `|Y |`this help` |`lf indala help `|Y |`this help`
|`lf indala demod `|Y |`demodulate an indala tag (PSK1) from GraphBuffer` |`lf indala demod `|Y |`demodulate an Indala tag (PSK1) from GraphBuffer`
|`lf indala altdemod `|Y |`alternative method to Demodulate samples for Indala 64 bit UID (option '224' for 224 bit)` |`lf indala altdemod `|Y |`alternative method to demodulate samples for Indala 64 bit UID (option '224' for 224 bit)`
|`lf indala reader `|N |`read an Indala Prox tag from the antenna` |`lf indala reader `|N |`read an Indala tag from the antenna`
|`lf indala clone `|N |`clone Indala tag to T55x7 or Q5/T5555` |`lf indala clone `|N |`clone Indala tag to T55x7 or Q5/T5555`
|`lf indala sim `|N |`simulate Indala tag` |`lf indala sim `|N |`simulate Indala tag`
@ -686,10 +687,10 @@ Check column "offline" for their availability.
|command |offline |description |command |offline |description
|------- |------- |----------- |------- |------- |-----------
|`lf io help `|Y |`this help` |`lf io help `|Y |`this help`
|`lf io demod `|Y |`demodulate an IOProx tag from the GraphBuffer` |`lf io demod `|Y |`demodulate an ioProx tag from the GraphBuffer`
|`lf io reader `|N |`attempt to read and extract tag data` |`lf io reader `|N |`attempt to read and extract tag data`
|`lf io clone `|N |`clone IOProx tag to T55x7 or Q5/T5555` |`lf io clone `|N |`clone ioProx tag to T55x7 or Q5/T5555`
|`lf io sim `|N |`simulate IOProx tag` |`lf io sim `|N |`simulate ioProx tag`
|`lf io watch `|N |`continuously watch for cards. Reader mode` |`lf io watch `|N |`continuously watch for cards. Reader mode`
@ -1001,7 +1002,7 @@ Check column "offline" for their availability.
|------- |------- |----------- |------- |------- |-----------
|`wiegand help `|Y |`This help` |`wiegand help `|Y |`This help`
|`wiegand list `|Y |`List available wiegand formats` |`wiegand list `|Y |`List available wiegand formats`
|`wiegand encode `|Y |`Encode to wiegand raw hex` |`wiegand encode `|Y |`Encode to wiegand raw hex (currently for HID Prox)`
|`wiegand decode `|Y |`Convert raw hex to decoded wiegand format` |`wiegand decode `|Y |`Convert raw hex to decoded wiegand format (currently for HID Prox)`

View file

@ -426,6 +426,12 @@ Note: it seems some cards only accept the "change UID" command.
It accepts direct read of block0 (and only block0) without prior auth. It accepts direct read of block0 (and only block0) without prior auth.
Writing to block 0 has some side-effects:
* It changes also the UID. Changing the UID *does not* change block 0.
* ATQA and SAK bytes are automatically replaced by fixed values.
* On 4-byte UID cards, BCC byte is automatically corrected.
### Characteristics ### Characteristics
* UID: 4b and 7b versions * UID: 4b and 7b versions
@ -452,6 +458,8 @@ Equivalent:
``` ```
# change just UID: # change just UID:
hf 14a raw -s -c -t 2000 90FBCCCC07 11223344556677 hf 14a raw -s -c -t 2000 90FBCCCC07 11223344556677
# read block0:
hf 14a raw -s -c 3000
# write block0: # write block0:
hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000 hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000
# lock (uid/block0?) forever: # lock (uid/block0?) forever:

View file

@ -11,8 +11,29 @@
#ifndef EM4X70_H__ #ifndef EM4X70_H__
#define EM4X70_H__ #define EM4X70_H__
#define EM4X70_NUM_BLOCKS 16
// Common word/block addresses
#define EM4X70_PIN_WORD_LOWER 10
#define EM4X70_PIN_WORD_UPPER 11
typedef struct { typedef struct {
bool parity; bool parity;
// Used for writing address
uint8_t address;
uint16_t word;
// PIN to unlock
uint32_t pin;
// Used for authentication
uint8_t rnd[7];
uint8_t frnd[4];
// Used to write new key
uint8_t crypt_key[12];
} em4x70_data_t; } em4x70_data_t;
#endif /* EM4X70_H__ */ #endif /* EM4X70_H__ */

View file

@ -517,6 +517,11 @@ typedef struct {
#define CMD_LF_EM4X50_ESET 0x0252 #define CMD_LF_EM4X50_ESET 0x0252
#define CMD_LF_EM4X50_CHK 0x0253 #define CMD_LF_EM4X50_CHK 0x0253
#define CMD_LF_EM4X70_INFO 0x0260 #define CMD_LF_EM4X70_INFO 0x0260
#define CMD_LF_EM4X70_WRITE 0x0261
#define CMD_LF_EM4X70_UNLOCK 0x0262
#define CMD_LF_EM4X70_AUTH 0x0263
#define CMD_LF_EM4X70_WRITEPIN 0x0264
#define CMD_LF_EM4X70_WRITEKEY 0x0265
// Sampling configuration for LF reader/sniffer // Sampling configuration for LF reader/sniffer
#define CMD_LF_SAMPLING_SET_CONFIG 0x021D #define CMD_LF_SAMPLING_SET_CONFIG 0x021D
#define CMD_LF_FSK_SIMULATE 0x021E #define CMD_LF_FSK_SIMULATE 0x021E