Merge branch 'master' into apdufuzz

This commit is contained in:
ikarus 2020-12-13 00:14:34 +01:00
commit 9300b8b65c
35 changed files with 657 additions and 361 deletions

View file

@ -1174,6 +1174,14 @@ static void PacketReceived(PacketCommandNG *packet) {
em4x70_write((em4x70_data_t *)packet->data.asBytes); em4x70_write((em4x70_data_t *)packet->data.asBytes);
break; 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;
}
#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

@ -70,7 +70,7 @@ 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 EM4170_setup_read(void) {
@ -158,7 +158,7 @@ static bool get_signalproperties(void) {
gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100; gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100;
// Basic sanity check // Basic sanity check
if(gHigh - gLow < EM4X70_MIN_AMPLITUDE) { if (gHigh - gLow < EM4X70_MIN_AMPLITUDE) {
return false; return false;
} }
@ -178,7 +178,7 @@ static uint32_t get_pulse_length(void) {
do { do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)); } while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
@ -188,7 +188,7 @@ static uint32_t get_pulse_length(void) {
do { do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout)); } while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
@ -196,7 +196,7 @@ static uint32_t get_pulse_length(void) {
timeout = (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) + GetTicks(); timeout = (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) + GetTicks();
do { do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)); } while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
@ -217,7 +217,7 @@ static uint32_t get_pulse_invert_length(void) {
do { do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout)); } while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
@ -227,7 +227,7 @@ static uint32_t get_pulse_invert_length(void) {
do { do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)); } while (IS_HIGH(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
@ -235,7 +235,7 @@ static uint32_t get_pulse_invert_length(void) {
timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD);
do { do {
sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR;
}while (IS_LOW(sample) && !IS_TIMEOUT(timeout)); } while (IS_LOW(sample) && !IS_TIMEOUT(timeout));
if (IS_TIMEOUT(timeout)) if (IS_TIMEOUT(timeout))
return 0; return 0;
@ -289,7 +289,7 @@ static void em4x70_send_nibble(uint8_t nibble, bool with_parity) {
// 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++) {
@ -298,17 +298,23 @@ static void em4x70_send_nibble(uint8_t nibble, bool with_parity) {
parity ^= bit; parity ^= bit;
} }
if(with_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) { static void em4x70_send_word(const uint16_t word) {
// Split into nibbles // Split into nibbles
uint8_t nibbles[4]; uint8_t nibbles[4];
uint8_t j = 0; uint8_t j = 0;
for(int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
uint8_t byte = (word >> (8*i)) & 0xff; uint8_t byte = (word >> (8 * i)) & 0xff;
nibbles[j++] = (byte >> 4) & 0xf; nibbles[j++] = (byte >> 4) & 0xf;
nibbles[j++] = byte & 0xf; nibbles[j++] = byte & 0xf;
} }
@ -354,6 +360,87 @@ static bool check_ack(void) {
return false; 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);
if (num < 10) {
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(TICKS_PER_FC * EM4X70_T_TAG_TWALB);
// <-- Receive ACK
if (check_ack()) {
// <w> Writes Lock Bits
WaitTicks(TICKS_PER_FC * EM4X70_T_TAG_WEE);
// <-- Receive header + ID
uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH];
int num = em4x70_receive(tag_id);
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) { static int write(const uint16_t word, const uint8_t address) {
// writes <word> to specified <address> // writes <word> to specified <address>
@ -390,7 +477,7 @@ static int write(const uint16_t word, const uint8_t address) {
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_SNGLLIW) {
/* /*
80 ( 64 + 16 ) 80 ( 64 + 16 )
80 ( 64 + 16 ) 80 ( 64 + 16 )
@ -398,13 +485,12 @@ 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) && if (check_pulse_length(get_pulse_invert_length(), 80) &&
check_pulse_length(get_pulse_invert_length(), 80) && check_pulse_length(get_pulse_invert_length(), 80) &&
check_pulse_length(get_pulse_length(), 96) && check_pulse_length(get_pulse_length(), 96) &&
check_pulse_length(get_pulse_length(), 64) ) check_pulse_length(get_pulse_length(), 64)) {
{
if(command) { if (command) {
/* Here we are after the 64 duration edge. /* Here we are after the 64 duration edge.
* em4170 says we need to wait about 48 RF clock cycles. * em4170 says we need to wait about 48 RF clock cycles.
* depends on the delay between tag and us * depends on the delay between tag and us
@ -426,14 +512,14 @@ static bool find_listen_window(bool command) {
static void bits2bytes(const 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;
} }
} }
@ -456,14 +542,14 @@ static uint8_t bits2byte(const uint8_t *bits, int length) {
static bool send_command_and_read(uint8_t command, uint8_t resp_len_bits, uint8_t *out_bytes) { static bool send_command_and_read(uint8_t command, uint8_t resp_len_bits, uint8_t *out_bytes) {
int retries = EM4X70_COMMAND_RETRIES; int retries = EM4X70_COMMAND_RETRIES;
while(retries) { while (retries) {
retries--; retries--;
if(find_listen_window(true)) { if (find_listen_window(true)) {
uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0}; uint8_t bits[EM4X70_MAX_RECEIVE_LENGTH] = {0};
em4x70_send_nibble(command, command_parity); em4x70_send_nibble(command, command_parity);
int len = em4x70_receive(bits); int len = em4x70_receive(bits);
if(len < resp_len_bits) { if (len < resp_len_bits) {
Dbprintf("Invalid data received length: %d", len); Dbprintf("Invalid data received length: %d", len);
return false; return false;
} }
@ -532,22 +618,22 @@ static int em4x70_receive(uint8_t *bits) {
// 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; int pulse_count = 0;
while(pulse_count < 12){ while (pulse_count < 12) {
pl = get_pulse_invert_length(); pl = get_pulse_invert_length();
pulse_count++; pulse_count++;
if(check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) { if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) {
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(); get_pulse_invert_length();
} }
@ -555,7 +641,7 @@ static int em4x70_receive(uint8_t *bits) {
// between two listen windows only pulse lengths of 1, 1.5 and 2 are possible // between two listen windows only pulse lengths of 1, 1.5 and 2 are possible
while (bit_pos < EM4X70_MAX_RECEIVE_LENGTH) { while (bit_pos < EM4X70_MAX_RECEIVE_LENGTH) {
if(edge) if (edge)
pl = get_pulse_length(); pl = get_pulse_length();
else else
pl = get_pulse_invert_length(); pl = get_pulse_invert_length();
@ -568,7 +654,7 @@ static int em4x70_receive(uint8_t *bits) {
} else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) { } else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) {
// pulse length = 1.5 -> flip edge detection // pulse length = 1.5 -> flip edge detection
if(edge) { if (edge) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
edge = 0; edge = 0;
@ -581,7 +667,7 @@ static int em4x70_receive(uint8_t *bits) {
} else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD)) { } else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD)) {
// pulse length of 2 // pulse length of 2
if(edge) { if (edge) {
bits[bit_pos++] = 0; bits[bit_pos++] = 0;
bits[bit_pos++] = 1; bits[bit_pos++] = 1;
} else { } else {
@ -589,7 +675,7 @@ 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)) || } else if ((edge && check_pulse_length(pl, 3 * EM4X70_T_TAG_FULL_PERIOD)) ||
(!edge && check_pulse_length(pl, 80))) { (!edge && check_pulse_length(pl, 80))) {
// LIW detected (either invert or normal) // LIW detected (either invert or normal)
@ -637,7 +723,7 @@ void em4x70_write(em4x70_data_t *etd) {
// Write // Write
status = write(etd->word, etd->address) == PM3_SUCCESS; status = write(etd->word, etd->address) == PM3_SUCCESS;
if(status) { if (status) {
// Read Tag after writing // Read Tag after writing
em4x70_read_id(); em4x70_read_id();
em4x70_read_um1(); em4x70_read_um1();
@ -651,4 +737,58 @@ void em4x70_write(em4x70_data_t *etd) {
reply_ng(CMD_LF_EM4X70_WRITE, status, tag.data, sizeof(tag.data)); 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();
EM4170_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();
EM4170_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));
}

View file

@ -19,5 +19,7 @@ typedef struct {
void em4x70_info(em4x70_data_t *etd); void em4x70_info(em4x70_data_t *etd);
void em4x70_write(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);
#endif /* EM4x70_H */ #endif /* EM4x70_H */

View file

@ -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

@ -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

@ -317,10 +317,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);
@ -610,7 +610,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"));
@ -971,7 +971,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;
@ -1118,10 +1118,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 flash memory"}, {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "upload dump of EM4x50 to flash memory"},
{"esave", CmdEM4x50ESave, IfPm3EM4x50, "save flash memory to file"}, {"esave", CmdEM4x50ESave, IfPm3EM4x50, "save flash memory to file"},

View file

@ -32,12 +32,12 @@ static void print_info_result(uint8_t *data) {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, _YELLOW_("EM4x70 data:")); PrintAndLogEx(INFO, _YELLOW_("EM4x70 data:"));
for(int i=1; i <= 32; i+=2) { for (int i = 1; i <= 32; i += 2) {
PrintAndLogEx(NORMAL, "%02X %02X", data[32-i], data[32-i-1]); PrintAndLogEx(NORMAL, "%02X %02X", data[32 - i], data[32 - i - 1]);
} }
PrintAndLogEx(NORMAL, "Tag ID: %02X %02X %02X %02X", data[7], data[6], data[5], data[4]); PrintAndLogEx(NORMAL, "Tag ID: %02X %02X %02X %02X", data[7], data[6], data[5], data[4]);
PrintAndLogEx(NORMAL, "Lockbit 0: %d %s", (data[3] & 0x40) ? 1:0, (data[3] & 0x40) ? "LOCKED":"UNLOCKED"); PrintAndLogEx(NORMAL, "Lockbit 0: %d %s", (data[3] & 0x40) ? 1 : 0, (data[3] & 0x40) ? "LOCKED" : "UNLOCKED");
PrintAndLogEx(NORMAL, "Lockbit 1: %d", (data[3] & 0x80) ? 1:0); PrintAndLogEx(NORMAL, "Lockbit 1: %d", (data[3] & 0x80) ? 1 : 0);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
} }
@ -80,18 +80,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 info -p -> adds parity bit to command\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
}; };
@ -104,7 +104,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;
} }
@ -113,7 +113,7 @@ 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; return PM3_ESOFT;
} }
@ -124,15 +124,15 @@ int CmdEM4x70Write(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 4x10 write", CLIParserInit(&ctx, "lf em 4x70 write",
"Write EM4x70\n", "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 -> write 'c0de' to block 15\n"
"lf em 4x70 write -p -b 15 -d c0de -> adds parity bit to commands\n" "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\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_int1("b", "block", "<dec>", "block/word address, dec"), arg_int1("b", "block", "<dec>", "block/word address, dec"),
arg_str1("d", "data", "<hex>", "data, 2 bytes"), arg_str1("d", "data", "<hex>", "data, 2 bytes"),
arg_param_end arg_param_end
@ -181,10 +181,135 @@ int CmdEM4x70Write(const char *Cmd) {
return PM3_ESOFT; 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 11223344556677 --frn 11223344\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;
}
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"}, {"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"},
{"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"},
{"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"},
{NULL, NULL, NULL, NULL} {NULL, NULL, NULL, NULL}
}; };

View file

@ -19,6 +19,8 @@
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 CmdEM4x70Write(const char *Cmd);
int CmdEM4x70Unlock(const char *Cmd);
int CmdEM4x70Auth(const char *Cmd);
int em4x70_info(void); int em4x70_info(void);
bool detect_4x70_block(void); bool detect_4x70_block(void);

View file

@ -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

@ -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

@ -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 apdufuzz `|N |`Fuzz APDU - 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 managekeys `|Y |`[*] Manage keys to use with iclass commands`
|`hf iclass permutekey `|N |` Permute function from 'heart of darkness' paper` |`hf iclass permutekey `|N |` Permute function from 'heart of darkness' paper`
|`hf iclass view `|Y |`[options..] Display content from tag dump file` |`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

@ -19,6 +19,14 @@ typedef struct {
// Used for writing address // Used for writing address
uint8_t address; uint8_t address;
uint16_t word; uint16_t word;
// PIN to unlock
uint32_t pin;
// Used for authentication
uint8_t rnd[7];
uint8_t frnd[4];
} em4x70_data_t; } em4x70_data_t;
#endif /* EM4X70_H__ */ #endif /* EM4X70_H__ */

View file

@ -518,6 +518,8 @@ typedef struct {
#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_WRITE 0x0261
#define CMD_LF_EM4X70_UNLOCK 0x0262
#define CMD_LF_EM4X70_AUTH 0x0263
// 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