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

@ -88,7 +88,7 @@ static int get_input_data_from_file(uint32_t *words, char *inputfile) {
uint32_t size = size_in_spiffs(inputfile); uint32_t size = size_in_spiffs(inputfile);
uint8_t *mem = BigBuf_malloc(size); uint8_t *mem = BigBuf_malloc(size);
Dbprintf(_YELLOW_("found input file %s"), inputfile); Dbprintf(_YELLOW_("found input file %s"), inputfile);
rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE); rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE);
@ -153,7 +153,7 @@ void RunMod(void) {
if (button_pressed == BUTTON_SINGLE_CLICK) { if (button_pressed == BUTTON_SINGLE_CLICK) {
SpinUp(100); SpinUp(100);
switch (state) { switch (state) {
case STATE_SIM: case STATE_SIM:
@ -168,7 +168,7 @@ void RunMod(void) {
default: default:
break; break;
} }
state_change = true; state_change = true;
} else if (button_pressed == BUTTON_HOLD) { } else if (button_pressed == BUTTON_HOLD) {
@ -261,9 +261,9 @@ void RunMod(void) {
log_exists = exists_in_spiffs(LF_EM4X50BRUTE_LOGFILE); log_exists = exists_in_spiffs(LF_EM4X50BRUTE_LOGFILE);
now = get_input_data_from_file(passwords, LF_EM4X50BRUTE_INPUTFILE); now = get_input_data_from_file(passwords, LF_EM4X50BRUTE_INPUTFILE);
if (now == 2) { if (now == 2) {
// print some information // print some information
int no_iter = passwords[1] - passwords[0] + 1; int no_iter = passwords[1] - passwords[0] + 1;
int dur_s = no_iter / EM4X50_PWD_SPEED; int dur_s = no_iter / EM4X50_PWD_SPEED;
@ -277,7 +277,7 @@ void RunMod(void) {
no_iter, passwords[0], passwords[1]); no_iter, passwords[0], passwords[1]);
Dbprintf(_YELLOW_("estimated duration: %ih%im%is"), Dbprintf(_YELLOW_("estimated duration: %ih%im%is"),
dur_h, dur_m, dur_s); dur_h, dur_m, dur_s);
} else { } else {
Dbprintf(_RED_("error in input data")); Dbprintf(_RED_("error in input data"));
break; break;
@ -287,7 +287,7 @@ void RunMod(void) {
} }
pwd_found = em4x50_standalone_brute(passwords[0], passwords[1], &pwd); pwd_found = em4x50_standalone_brute(passwords[0], passwords[1], &pwd);
if (pwd_found == PM3_ETIMEOUT) { if (pwd_found == PM3_ETIMEOUT) {
// timeout -> no EM4x50 tag on reader? // timeout -> no EM4x50 tag on reader?
@ -313,15 +313,15 @@ void RunMod(void) {
strcat((char *)entry, "\n"); strcat((char *)entry, "\n");
append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry));
} else { } else {
// stopped -> write to logfile // stopped -> write to logfile
sprintf((char *)entry, "stopped search - last password: 0x%08"PRIx32, pwd); sprintf((char *)entry, "stopped search - last password: 0x%08"PRIx32, pwd);
Dbprintf(_YELLOW_("%s"), entry); Dbprintf(_YELLOW_("%s"), entry);
strcat((char *)entry, "\n"); strcat((char *)entry, "\n");
append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry));
// replace start password by last tested password in // replace start password by last tested password in
// inputfile (spiffs) so that brute forcing process will // inputfile (spiffs) so that brute forcing process will
// be continued when envoking brute force mode again // be continued when envoking brute force mode again

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
@ -66,18 +66,18 @@ static void wait_timer(uint32_t period) {
// extract and check parities // extract and check parities
// return result of parity check and extracted plain data // return result of parity check and extracted plain data
static bool extract_parities(uint64_t word, uint32_t *data) { static bool extract_parities(uint64_t word, uint32_t *data) {
uint8_t row_parities = 0x0, col_parities = 0x0; uint8_t row_parities = 0x0, col_parities = 0x0;
uint8_t row_parities_calculated = 0x0, col_parities_calculated = 0x0; uint8_t row_parities_calculated = 0x0, col_parities_calculated = 0x0;
*data = 0x0; *data = 0x0;
// extract plain data (32 bits) from raw word (45 bits) // extract plain data (32 bits) from raw word (45 bits)
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
*data <<= 8; *data <<= 8;
*data |= (word >> ((4 - i) * 9 + 1)) & 0xFF; *data |= (word >> ((4 - i) * 9 + 1)) & 0xFF;
} }
// extract row parities (4 bits + stop bit) from raw word (45 bits) // extract row parities (4 bits + stop bit) from raw word (45 bits)
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
row_parities <<= 1; row_parities <<= 1;
@ -107,7 +107,7 @@ static bool extract_parities(uint64_t word, uint32_t *data) {
col_parities_calculated ^= (*data >> ((3 - j) * 8 + (7 - i))) & 0x1; col_parities_calculated ^= (*data >> ((3 - j) * 8 + (7 - i))) & 0x1;
} }
} }
if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated)) if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated))
return true; return true;
@ -192,7 +192,7 @@ static bool get_signalproperties(void) {
// about 2 samples per bit period // about 2 samples per bit period
wait_timer(T0 * EM4X50_T_TAG_HALF_PERIOD); wait_timer(T0 * EM4X50_T_TAG_HALF_PERIOD);
// ignore first samples // ignore first samples
if ((i > SIGNAL_IGNORE_FIRST_SAMPLES) && (AT91C_BASE_SSC->SSC_RHR > noise)) { if ((i > SIGNAL_IGNORE_FIRST_SAMPLES) && (AT91C_BASE_SSC->SSC_RHR > noise)) {
signal_found = true; signal_found = true;
@ -230,7 +230,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;
LED_A_OFF(); LED_A_OFF();
return true; return true;
} }
@ -291,7 +291,7 @@ static uint32_t get_pulse_length(void) {
// check if pulse length <pl> corresponds to given length <length> // check if pulse length <pl> corresponds to given length <length>
static bool check_pulse_length(uint32_t pl, int length) { static bool check_pulse_length(uint32_t pl, int length) {
return ((pl >= T0 * (length - EM4X50_TAG_TOLERANCE)) && (pl <= T0 * (length + EM4X50_TAG_TOLERANCE))); return ((pl >= T0 * (length - EM4X50_TAG_TOLERANCE)) && (pl <= T0 * (length + EM4X50_TAG_TOLERANCE)));
} }
// send single bit according to EM4x50 application note and datasheet // send single bit according to EM4x50 application note and datasheet
@ -347,12 +347,12 @@ static void em4x50_reader_send_byte_with_parity(uint8_t byte) {
// word hast be sent in msb notation // word hast be sent in msb notation
static void em4x50_reader_send_word(const uint32_t word) { static void em4x50_reader_send_word(const uint32_t word) {
uint8_t bytes[4] = {0x0, 0x0, 0x0, 0x0}; uint8_t bytes[4] = {0x0, 0x0, 0x0, 0x0};
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
bytes[i] = (word >> (24 - (8 * i))) & 0xFF; bytes[i] = (word >> (24 - (8 * i))) & 0xFF;
em4x50_reader_send_byte_with_parity(bytes[i]); em4x50_reader_send_byte_with_parity(bytes[i]);
} }
// send column parities // send column parities
em4x50_reader_send_byte(bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3]); em4x50_reader_send_byte(bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3]);
@ -363,7 +363,7 @@ static void em4x50_reader_send_word(const uint32_t word) {
// find single listen window // find single listen window
static bool find_single_listen_window(void) { static bool find_single_listen_window(void) {
int cnt_pulses = 0; int cnt_pulses = 0;
LED_B_ON(); LED_B_ON();
while (cnt_pulses < EM4X50_T_WAITING_FOR_SNGLLIW) { while (cnt_pulses < EM4X50_T_WAITING_FOR_SNGLLIW) {
@ -393,7 +393,7 @@ static bool find_single_listen_window(void) {
// -> 34 words + 34 single listen windows -> about 1600 pulses // -> 34 words + 34 single listen windows -> about 1600 pulses
static int find_double_listen_window(bool bcommand) { static int find_double_listen_window(bool bcommand) {
int cnt_pulses = 0; int cnt_pulses = 0;
LED_B_ON(); LED_B_ON();
while (cnt_pulses < EM4X50_T_WAITING_FOR_DBLLIW) { while (cnt_pulses < EM4X50_T_WAITING_FOR_DBLLIW) {
@ -453,7 +453,7 @@ static int find_double_listen_window(bool bcommand) {
cnt_pulses++; cnt_pulses++;
} }
LED_B_OFF(); LED_B_OFF();
return PM3_EFAILED; return PM3_EFAILED;
} }
@ -481,7 +481,7 @@ static bool check_ack(bool bliw) {
if (BUTTON_PRESS()) if (BUTTON_PRESS())
return false; return false;
if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) {
// The received signal is either ACK or NAK. // The received signal is either ACK or NAK.
@ -532,9 +532,9 @@ static int get_word_from_bitstream(uint32_t *data) {
int cnt = 0; int cnt = 0;
uint32_t pl = 0; uint32_t pl = 0;
uint64_t word = 0x0; uint64_t word = 0x0;
LED_C_ON(); LED_C_ON();
*data = 0x0; *data = 0x0;
// initial bit value depends on last pulse length of listen window // initial bit value depends on last pulse length of listen window
@ -562,7 +562,7 @@ static int get_word_from_bitstream(uint32_t *data) {
cnt++; cnt++;
word <<= 1; word <<= 1;
pl = get_pulse_length(); pl = get_pulse_length();
if (check_pulse_length(pl, EM4X50_T_TAG_FULL_PERIOD)) { if (check_pulse_length(pl, EM4X50_T_TAG_FULL_PERIOD)) {
@ -613,9 +613,9 @@ static int get_word_from_bitstream(uint32_t *data) {
return (extract_parities(word, data)) ? --cnt : 0; return (extract_parities(word, data)) ? --cnt : 0;
} }
} }
LED_C_OFF(); LED_C_OFF();
return PM3_EOPABORTED; return PM3_EOPABORTED;
} }
@ -697,7 +697,7 @@ bool em4x50_sim_send_word(uint32_t word) {
// word has tobe sent in msb, not lsb // word has tobe sent in msb, not lsb
word = reflect32(word); word = reflect32(word);
// 4 bytes each with even row parity bit // 4 bytes each with even row parity bit
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (em4x50_sim_send_byte_with_parity((word >> ((3 - i) * 8)) & 0xFF) == false) { if (em4x50_sim_send_byte_with_parity((word >> ((3 - i) * 8)) & 0xFF) == false) {
@ -777,7 +777,7 @@ static bool login(uint32_t password) {
// send password // send password
em4x50_reader_send_word(password); em4x50_reader_send_word(password);
wait_timer(T0 * EM4X50_T_TAG_TPP); wait_timer(T0 * EM4X50_T_TAG_TPP);
// check if ACK is returned // check if ACK is returned
@ -800,7 +800,7 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) {
for (*pwd = start; *pwd <= stop; (*pwd)++) { for (*pwd = start; *pwd <= stop; (*pwd)++) {
if (login(*pwd) == PM3_SUCCESS) { if (login(*pwd) == PM3_SUCCESS) {
pwd_found = true; pwd_found = true;
// to be safe login 5 more times // to be safe login 5 more times
@ -810,11 +810,11 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) {
break; break;
} }
} }
if (pwd_found) if (pwd_found)
break; break;
} }
// print password every 500 iterations // print password every 500 iterations
if ((++cnt % 500) == 0) { if ((++cnt % 500) == 0) {
@ -828,10 +828,10 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) {
// print data // print data
Dbprintf("|%8i | 0x%08x | 0x%08x |", cnt, reflect32(*pwd), *pwd); Dbprintf("|%8i | 0x%08x | 0x%08x |", cnt, reflect32(*pwd), *pwd);
} }
if (BUTTON_PRESS()) if (BUTTON_PRESS())
break; break;
} }
// print footer // print footer
@ -853,7 +853,7 @@ void em4x50_login(uint32_t *password) {
reply_ng(CMD_LF_EM4X50_LOGIN, status, NULL, 0); reply_ng(CMD_LF_EM4X50_LOGIN, status, NULL, 0);
} }
// envoke password search // envoke password search
void em4x50_brute(em4x50_data_t *etd) { void em4x50_brute(em4x50_data_t *etd) {
em4x50_setup_read(); em4x50_setup_read();
@ -904,7 +904,7 @@ void em4x50_chk(uint8_t *filename) {
pwd = 0x0; pwd = 0x0;
for (int j = 0; j < 4; j++) for (int j = 0; j < 4; j++)
pwd |= (*(pwds + 4 * i + j)) << ((3 - j) * 8); pwd |= (*(pwds + 4 * i + j)) << ((3 - j) * 8);
if ((status = login(pwd)) == PM3_SUCCESS) if ((status = login(pwd)) == PM3_SUCCESS)
break; break;
} }
@ -1062,7 +1062,7 @@ void em4x50_reader(void) {
// writes <word> to specified <addresses> // writes <word> to specified <addresses>
static int write(uint32_t word, uint32_t addresses) { static int write(uint32_t word, uint32_t addresses) {
if (request_receive_mode() == PM3_SUCCESS) { if (request_receive_mode() == PM3_SUCCESS) {
// send write command // send write command
@ -1078,7 +1078,7 @@ static int write(uint32_t word, uint32_t addresses) {
reply_ng(CMD_LF_EM4X50_WRITE, PM3_ETEAROFF, NULL, 0); reply_ng(CMD_LF_EM4X50_WRITE, PM3_ETEAROFF, NULL, 0);
return PM3_ETEAROFF; return PM3_ETEAROFF;
} else { } else {
// wait for T0 * EM4X50_T_TAG_TWA (write access time) // wait for T0 * EM4X50_T_TAG_TWA (write access time)
wait_timer(T0 * EM4X50_T_TAG_TWA); wait_timer(T0 * EM4X50_T_TAG_TWA);
@ -1174,7 +1174,7 @@ void em4x50_write(em4x50_data_t *etd) {
// if password is given renew login after reset // if password is given renew login after reset
if (etd->pwd_given) if (etd->pwd_given)
status = login(etd->password1); status = login(etd->password1);
if (status == PM3_SUCCESS) { if (status == PM3_SUCCESS) {
// call a selective read // call a selective read
@ -1224,11 +1224,11 @@ void em4x50_sim(uint8_t *filename) {
int status = PM3_SUCCESS; int status = PM3_SUCCESS;
uint8_t *em4x50_mem = BigBuf_get_EM_addr(); uint8_t *em4x50_mem = BigBuf_get_EM_addr();
uint32_t words[EM4X50_NO_WORDS] = {0x0}; uint32_t words[EM4X50_NO_WORDS] = {0x0};
#ifdef WITH_FLASH #ifdef WITH_FLASH
if (strlen((char *)filename) != 0) { if (strlen((char *)filename) != 0) {
BigBuf_free(); BigBuf_free();
int changed = rdv40_spiffs_lazy_mount(); int changed = rdv40_spiffs_lazy_mount();
@ -1245,7 +1245,7 @@ void em4x50_sim(uint8_t *filename) {
for (int i = 0; i < EM4X50_NO_WORDS; i++) for (int i = 0; i < EM4X50_NO_WORDS; i++)
words[i] = reflect32(bytes_to_num(em4x50_mem + (i * 4), 4)); words[i] = reflect32(bytes_to_num(em4x50_mem + (i * 4), 4));
// only if valid em4x50 data (e.g. uid == serial) // only if valid em4x50 data (e.g. uid == serial)
if (words[EM4X50_DEVICE_SERIAL] != words[EM4X50_DEVICE_ID]) { if (words[EM4X50_DEVICE_SERIAL] != words[EM4X50_DEVICE_ID]) {
@ -1260,7 +1260,7 @@ void em4x50_sim(uint8_t *filename) {
// iceman, will need a usb cmd check to break as well // iceman, will need a usb cmd check to break as well
while (BUTTON_PRESS() == false) { while (BUTTON_PRESS() == false) {
WDT_HIT(); WDT_HIT();
em4x50_sim_send_listen_window(); em4x50_sim_send_listen_window();
for (int i = fwr; i <= lwr; i++) { for (int i = fwr; i <= lwr; i++) {
@ -1276,7 +1276,7 @@ void em4x50_sim(uint8_t *filename) {
} else { } else {
status = PM3_ENODATA; status = PM3_ENODATA;
} }
BigBuf_free(); BigBuf_free();
lf_finalize(); lf_finalize();
reply_ng(CMD_LF_EM4X50_SIM, status, NULL, 0); reply_ng(CMD_LF_EM4X50_SIM, status, NULL, 0);

File diff suppressed because it is too large Load diff

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

@ -1727,7 +1727,7 @@ void SimTagIso15693(uint8_t *uid) {
if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) {
bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH);
uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM;
// Build INVENTORY command // Build INVENTORY command
uint8_t resp_inv[CMD_INV_RESP] = {0}; uint8_t resp_inv[CMD_INV_RESP] = {0};
@ -1743,30 +1743,30 @@ void SimTagIso15693(uint8_t *uid) {
resp_inv[7] = uid[2]; resp_inv[7] = uid[2];
resp_inv[8] = uid[1]; resp_inv[8] = uid[1];
resp_inv[9] = uid[0]; resp_inv[9] = uid[0];
// CRC // CRC
AddCrc15(resp_inv, 10); AddCrc15(resp_inv, 10);
CodeIso15693AsTag(resp_inv, CMD_INV_RESP); CodeIso15693AsTag(resp_inv, CMD_INV_RESP);
tosend_t *ts = get_tosend(); tosend_t *ts = get_tosend();
TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow);
LogTrace_ISO15693(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); LogTrace_ISO15693(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false);
chip_state = SELECTED; chip_state = SELECTED;
} }
// GET_SYSTEM_INFO // GET_SYSTEM_INFO
if ((cmd[1] == ISO15_CMD_SYSINFO)) { if ((cmd[1] == ISO15_CMD_SYSINFO)) {
bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH);
uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM;
// Build GET_SYSTEM_INFO command // Build GET_SYSTEM_INFO command
uint8_t resp_sysinfo[CMD_SYSINFO_RESP] = {0}; uint8_t resp_sysinfo[CMD_SYSINFO_RESP] = {0};
resp_sysinfo[0] = 0; // Response flags. resp_sysinfo[0] = 0; // Response flags.
resp_sysinfo[1] = 0x0F; // Information flags (0x0F - DSFID, AFI, Mem size, IC) resp_sysinfo[1] = 0x0F; // Information flags (0x0F - DSFID, AFI, Mem size, IC)
// 64-bit UID // 64-bit UID
resp_sysinfo[2] = uid[7]; resp_sysinfo[2] = uid[7];
resp_sysinfo[3] = uid[6]; resp_sysinfo[3] = uid[6];
@ -1776,42 +1776,42 @@ void SimTagIso15693(uint8_t *uid) {
resp_sysinfo[7] = uid[2]; resp_sysinfo[7] = uid[2];
resp_sysinfo[8] = uid[1]; resp_sysinfo[8] = uid[1];
resp_sysinfo[9] = uid[0]; resp_sysinfo[9] = uid[0];
resp_sysinfo[10] = 0; // DSFID resp_sysinfo[10] = 0; // DSFID
resp_sysinfo[11] = 0; // AFI resp_sysinfo[11] = 0; // AFI
resp_sysinfo[12] = 0x1B; // Memory size. resp_sysinfo[12] = 0x1B; // Memory size.
resp_sysinfo[13] = 0x03; // Memory size. resp_sysinfo[13] = 0x03; // Memory size.
resp_sysinfo[14] = 0x01; // IC reference. resp_sysinfo[14] = 0x01; // IC reference.
// CRC // CRC
AddCrc15(resp_sysinfo, 15); AddCrc15(resp_sysinfo, 15);
CodeIso15693AsTag(resp_sysinfo, CMD_SYSINFO_RESP); CodeIso15693AsTag(resp_sysinfo, CMD_SYSINFO_RESP);
tosend_t *ts = get_tosend(); tosend_t *ts = get_tosend();
TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow);
LogTrace_ISO15693(resp_sysinfo, CMD_SYSINFO_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); LogTrace_ISO15693(resp_sysinfo, CMD_SYSINFO_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false);
} }
// READ_BLOCK // READ_BLOCK
if ((cmd[1] == ISO15_CMD_READ)) { if ((cmd[1] == ISO15_CMD_READ)) {
bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH);
uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM;
// Build GET_SYSTEM_INFO command // Build GET_SYSTEM_INFO command
uint8_t resp_readblock[CMD_READBLOCK_RESP] = {0}; uint8_t resp_readblock[CMD_READBLOCK_RESP] = {0};
resp_readblock[0] = 0; // Response flags. resp_readblock[0] = 0; // Response flags.
resp_readblock[1] = 0; // Block data. resp_readblock[1] = 0; // Block data.
resp_readblock[2] = 0; // Block data. resp_readblock[2] = 0; // Block data.
resp_readblock[3] = 0; // Block data. resp_readblock[3] = 0; // Block data.
resp_readblock[4] = 0; // Block data. resp_readblock[4] = 0; // Block data.
// CRC // CRC
AddCrc15(resp_readblock, 5); AddCrc15(resp_readblock, 5);
CodeIso15693AsTag(resp_readblock, CMD_READBLOCK_RESP); CodeIso15693AsTag(resp_readblock, CMD_READBLOCK_RESP);
tosend_t *ts = get_tosend(); tosend_t *ts = get_tosend();
TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow);

View file

@ -515,7 +515,7 @@ void doCotagAcquisition(void) {
if (BUTTON_PRESS()) if (BUTTON_PRESS())
break; break;
if (checker == 4000) { if (checker == 4000) {
if (data_available()) if (data_available())
break; break;

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
@ -43,4 +44,4 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
404142434445464748494a4b4c4d4e4f 404142434445464748494a4b4c4d4e4f
303132333435363738393a3b3c3d3e3f 303132333435363738393a3b3c3d3e3f
9CABF398358405AE2F0E2B3D31C99A8A # Default key 9CABF398358405AE2F0E2B3D31C99A8A # Default key
605F5E5D5C5B5A59605F5E5D5C5B5A59 # access control 605F5E5D5C5B5A59605F5E5D5C5B5A59 # access control

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,
@ -3422,7 +3422,7 @@ static int CmdHFiClassEncode(const char *Cmd) {
} }
// add binary sentinel bit. // add binary sentinel bit.
pushBit(&bout, 1); pushBit(&bout, 1);
// convert binary string to hex bytes // convert binary string to hex bytes
for (int i = 0; i < bin_len; i++) { for (int i = 0; i < bin_len; i++) {
char c = bin[i]; char c = bin[i];
@ -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

@ -276,7 +276,7 @@ static int CmdHFMFPInfo(const char *Cmd) {
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------");
PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------");

View file

@ -510,7 +510,7 @@ static int CmdAWIDBrute(const char *Cmd) {
break; break;
} }
// truncate card number // truncate card number
if ((cn & 0xFFFF) != cn) { if ((cn & 0xFFFF) != cn) {
cn &= 0xFFFF; cn &= 0xFFFF;
@ -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

@ -118,7 +118,7 @@ void printEM410x(uint32_t hi, uint64_t id, bool verbose) {
if (hi) { if (hi) {
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id); PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%06X%016" PRIX64), hi, id);
} else { } else {
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id); PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
} }
return; return;
} }
@ -395,7 +395,7 @@ static int CmdEM410xReader(const char *Cmd) {
// emulate an EM410X tag // emulate an EM410X tag
static int CmdEM410xSim(const char *Cmd) { static int CmdEM410xSim(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x sim", CLIParserInit(&ctx, "lf em 410x sim",
"Enables simulation of EM 410x card.\n" "Enables simulation of EM 410x card.\n"
@ -433,7 +433,7 @@ static int CmdEM410xSim(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdEM410xBrute(const char *Cmd) { static int CmdEM410xBrute(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x brute", CLIParserInit(&ctx, "lf em 410x brute",
"bruteforcing by emulating EM 410x tag", "bruteforcing by emulating EM 410x tag",
@ -452,10 +452,10 @@ static int CmdEM410xBrute(const char *Cmd) {
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
// clock default 64 in EM410x // clock default 64 in EM410x
uint32_t clk = arg_get_u32_def(ctx, 1, 64); uint32_t clk = arg_get_u32_def(ctx, 1, 64);
// default pause time: 1 second // default pause time: 1 second
uint32_t delay = arg_get_u32_def(ctx, 2, 1000); uint32_t delay = arg_get_u32_def(ctx, 2, 1000);
int fnlen = 0; int fnlen = 0;
@ -467,7 +467,7 @@ static int CmdEM410xBrute(const char *Cmd) {
PrintAndLogEx(ERR, "Error: Please specify a filename"); PrintAndLogEx(ERR, "Error: Please specify a filename");
return PM3_EINVARG; return PM3_EINVARG;
} }
uint32_t uidcnt = 0; uint32_t uidcnt = 0;
uint8_t stUidBlock = 20; uint8_t stUidBlock = 20;
uint8_t *p = NULL; uint8_t *p = NULL;
@ -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);
@ -585,7 +585,7 @@ static int CmdEM410xClone(const char *Cmd) {
CLIParserContext *ctx; CLIParserContext *ctx;
CLIParserInit(&ctx, "lf em 410x clone", CLIParserInit(&ctx, "lf em 410x clone",
"Writes EM410x ID to a T55x7 or Q5/T5555 tag", "Writes EM410x ID to a T55x7 or Q5/T5555 tag",
"lf em 410x clone --id 0F0368568B -> write id to T55x7 tag\n" "lf em 410x clone --id 0F0368568B -> write id to T55x7 tag\n"
"lf em 410x clone --id 0F0368568B --q5 -> write id to Q5/T5555 tag" "lf em 410x clone --id 0F0368568B --q5 -> write id to Q5/T5555 tag"
); );
@ -598,7 +598,7 @@ static int CmdEM410xClone(const char *Cmd) {
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
// clock default 64 in EM410x // clock default 64 in EM410x
uint32_t clk = arg_get_u32_def(ctx, 1, 64); uint32_t clk = arg_get_u32_def(ctx, 1, 64);
int uid_len = 0; int uid_len = 0;
uint8_t uid[5] = {0}; uint8_t uid[5] = {0};

View file

@ -773,7 +773,7 @@ int CmdEM4x05Write(const char *Cmd) {
void *argtable[] = { void *argtable[] = {
arg_param_begin, arg_param_begin,
arg_int0("a", "addr", "<dec>", "memory address to write to. (0-13)"), arg_int0("a", "addr", "<dec>", "memory address to write to. (0-13)"),
arg_str1("d", "data", "<hex>", "data to write, 4 bytes hex"), arg_str1("d", "data", "<hex>", "data to write, 4 bytes hex"),
arg_str0("p", "pwd", "<hex>", "optional - password, 4 bytes hex"), arg_str0("p", "pwd", "<hex>", "optional - password, 4 bytes hex"),
arg_lit0(NULL, "po", "protect operation"), arg_lit0(NULL, "po", "protect operation"),
arg_param_end arg_param_end
@ -784,14 +784,14 @@ int CmdEM4x05Write(const char *Cmd) {
uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 3, 0xFFFFFFFFFFFFFFFF); uint64_t inputpwd = arg_get_u64_hexstr_def(ctx, 3, 0xFFFFFFFFFFFFFFFF);
bool protect_operation = arg_get_lit(ctx, 4); bool protect_operation = arg_get_lit(ctx, 4);
CLIParserFree(ctx); CLIParserFree(ctx);
if ((addr > 13) && (protect_operation == false)) { if ((addr > 13) && (protect_operation == false)) {
PrintAndLogEx(WARNING, "Address must be between 0 and 13"); PrintAndLogEx(WARNING, "Address must be between 0 and 13");
return PM3_EINVARG; return PM3_EINVARG;
} }
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);
@ -807,14 +807,14 @@ int CmdEM4x05Write(const char *Cmd) {
int res = PM3_SUCCESS; int res = PM3_SUCCESS;
// 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;
} }
} }
@ -2146,4 +2146,4 @@ static int CmdHelp(const char *Cmd) {
int CmdLFEM4X05(const char *Cmd) { int CmdLFEM4X05(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
return CmdsParse(CommandTable, Cmd); return CmdsParse(CommandTable, Cmd);
} }

View file

@ -28,7 +28,7 @@ static void prepare_result(const uint8_t *data, int fwr, int lwr, em4x50_word_t
// restructure received result in "em4x50_word_t" structure // restructure received result in "em4x50_word_t" structure
for (int i = fwr; i <= lwr; i++) { for (int i = fwr; i <= lwr; i++) {
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
words[i].byte[j] = data[i * 4 + (3 - j)]; words[i].byte[j] = data[i * 4 + (3 - j)];
} }
} }
} }
@ -131,7 +131,7 @@ static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len
int res = 0; int res = 0;
uint32_t serial = 0x0, device_id = 0x0; uint32_t serial = 0x0, device_id = 0x0;
if (str_endswith(filename, ".eml")) if (str_endswith(filename, ".eml"))
res = loadFileEML(filename, data, bytes_read) != PM3_SUCCESS; res = loadFileEML(filename, data, bytes_read) != PM3_SUCCESS;
else if (str_endswith(filename, ".json")) else if (str_endswith(filename, ".json"))
@ -202,7 +202,7 @@ int CmdEM4x50ELoad(const char *Cmd) {
// upload to emulator memory // upload to emulator memory
PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to emulator memory", filename); PrintAndLogEx(INFO, "Uploading dump " _YELLOW_("%s") " to emulator memory", filename);
em4x50_seteml(data, 0, DUMP_FILESIZE); em4x50_seteml(data, 0, DUMP_FILESIZE);
PrintAndLogEx(INFO, "Done"); PrintAndLogEx(INFO, "Done");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -236,7 +236,7 @@ int CmdEM4x50ESave(const char *Cmd) {
PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
return PM3_ETIMEOUT; return PM3_ETIMEOUT;
} }
// valid em4x50 data? // valid em4x50 data?
uint32_t serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4); uint32_t serial = bytes_to_num(data + 4 * EM4X50_DEVICE_SERIAL, 4);
uint32_t device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4); uint32_t device_id = bytes_to_num(data + 4 * EM4X50_DEVICE_ID, 4);
@ -244,7 +244,7 @@ int CmdEM4x50ESave(const char *Cmd) {
PrintAndLogEx(WARNING, "No valid em4x50 data in flash memory."); PrintAndLogEx(WARNING, "No valid em4x50 data in flash memory.");
return PM3_ENODATA; return PM3_ENODATA;
} }
// user supplied filename? // user supplied filename?
if (fnlen == 0) { if (fnlen == 0) {
PrintAndLogEx(INFO, "Using UID as filename"); PrintAndLogEx(INFO, "Using UID as filename");
@ -314,7 +314,7 @@ int CmdEM4x50Login(const char *Cmd) {
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int pwd_len = 0; int pwd_len = 0;
uint8_t pwd[4] = {0x0}; uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
@ -323,7 +323,7 @@ int CmdEM4x50Login(const char *Cmd) {
if (pwd_len != 4) { if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes"); PrintAndLogEx(FAILED, "password length must be 4 bytes");
return PM3_EINVARG; return PM3_EINVARG;
} }
uint32_t password = BYTES2UINT32(pwd); uint32_t password = BYTES2UINT32(pwd);
@ -357,30 +357,30 @@ int CmdEM4x50Brute(const char *Cmd) {
arg_param_end arg_param_end
}; };
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);
if (first_len != 4) { if (first_len != 4) {
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;
etd.password1 = BYTES2UINT32(first); etd.password1 = BYTES2UINT32(first);
etd.password2 = BYTES2UINT32(last); etd.password2 = BYTES2UINT32(last);
// 27 passwords/second (empirical value) // 27 passwords/second (empirical value)
const int speed = 27; const int speed = 27;
// print some information // print some information
int no_iter = etd.password2 - etd.password1 + 1; int no_iter = etd.password2 - etd.password1 + 1;
@ -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
@ -432,7 +432,7 @@ int CmdEM4x50Chk(const char *Cmd) {
char filename[FILE_PATH_SIZE] = {0}; char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParserFree(ctx); CLIParserFree(ctx);
if (IfPm3Flash() == false) { if (IfPm3Flash() == false) {
PrintAndLogEx(WARNING, "no flash memory available"); PrintAndLogEx(WARNING, "no flash memory available");
return PM3_EFLASH; return PM3_EFLASH;
@ -452,17 +452,17 @@ int CmdEM4x50Chk(const char *Cmd) {
int res = loadFileDICTIONARY(filename, data, &datalen, 4, &key_count); int res = loadFileDICTIONARY(filename, data, &datalen, 4, &key_count);
if (res || !key_count) if (res || !key_count)
return PM3_EFILE; return PM3_EFILE;
PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button"); PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button");
int status = PM3_EFAILED; int status = PM3_EFAILED;
int keyblock = 2000; // block with 2000 bytes -> 500 keys int keyblock = 2000; // block with 2000 bytes -> 500 keys
uint8_t destfn[32] = "em4x50_chk.bin"; uint8_t destfn[32] = "em4x50_chk.bin";
PacketResponseNG resp; PacketResponseNG resp;
int bytes_remaining = datalen; int bytes_remaining = datalen;
while (bytes_remaining > 0) { while (bytes_remaining > 0) {
PrintAndLogEx(INPLACE, "Remaining keys: %i ", bytes_remaining / 4); PrintAndLogEx(INPLACE, "Remaining keys: %i ", bytes_remaining / 4);
// upload to flash. // upload to flash.
@ -476,7 +476,7 @@ int CmdEM4x50Chk(const char *Cmd) {
clearCommandBuffer(); clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_CHK, destfn, sizeof(destfn)); SendCommandNG(CMD_LF_EM4X50_CHK, destfn, sizeof(destfn));
WaitForResponseTimeoutW(CMD_LF_EM4X50_CHK, &resp, -1, false); WaitForResponseTimeoutW(CMD_LF_EM4X50_CHK, &resp, -1, false);
status = resp.status; status = resp.status;
if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED)) if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED))
break; break;
@ -486,7 +486,7 @@ int CmdEM4x50Chk(const char *Cmd) {
} }
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
// print response // print response
if (status == PM3_SUCCESS) { if (status == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Key " _GREEN_("found: %02x %02x %02x %02x"), PrintAndLogEx(SUCCESS, "Key " _GREEN_("found: %02x %02x %02x %02x"),
@ -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");
} }
@ -578,7 +578,7 @@ int CmdEM4x50Read(const char *Cmd) {
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int addr = arg_get_int_def(ctx, 1, 0); int addr = arg_get_int_def(ctx, 1, 0);
int pwd_len = 0; int pwd_len = 0;
uint8_t pwd[4] = {0x0}; uint8_t pwd[4] = {0x0};
@ -587,7 +587,7 @@ int CmdEM4x50Read(const char *Cmd) {
if (addr <= 0 || addr >= EM4X50_NO_WORDS) { if (addr <= 0 || addr >= EM4X50_NO_WORDS) {
return PM3_EINVARG; return PM3_EINVARG;
} }
em4x50_data_t etd; em4x50_data_t etd;
@ -629,11 +629,11 @@ int CmdEM4x50Info(const char *Cmd) {
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int pwd_len = 0; int pwd_len = 0;
uint8_t pwd[4] = {0x0}; uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
CLIParserFree(ctx); CLIParserFree(ctx);
em4x50_data_t etd = {.pwd_given = false}; em4x50_data_t etd = {.pwd_given = false};
if (pwd_len) { if (pwd_len) {
if (pwd_len != 4) { if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
@ -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"));
@ -708,7 +708,7 @@ int CmdEM4x50Reader(const char *Cmd) {
PrintAndLogEx(INFO, _GREEN_(" %s") "| %s", sprint_hex(words[i].byte, 4), r); PrintAndLogEx(INFO, _GREEN_(" %s") "| %s", sprint_hex(words[i].byte, 4), r);
} }
PrintAndLogEx(INFO, "-------------+-------------"); PrintAndLogEx(INFO, "-------------+-------------");
} }
} while (cm && !kbd_enter_pressed()); } while (cm && !kbd_enter_pressed());
@ -813,11 +813,11 @@ int CmdEM4x50Write(const char *Cmd) {
arg_str0("p", "pwd", "<hex>", "password, 4 bytes, lsb"), arg_str0("p", "pwd", "<hex>", "password, 4 bytes, lsb"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int addr = arg_get_int_def(ctx, 1, 0); int addr = arg_get_int_def(ctx, 1, 0);
int word_len = 0; int word_len = 0;
uint8_t word[4] = {0x0}; uint8_t word[4] = {0x0};
CLIGetHexWithReturn(ctx, 2, word, &word_len); CLIGetHexWithReturn(ctx, 2, word, &word_len);
@ -826,18 +826,18 @@ int CmdEM4x50Write(const char *Cmd) {
uint8_t pwd[4] = {0x0}; uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len); CLIGetHexWithReturn(ctx, 3, pwd, &pwd_len);
CLIParserFree(ctx); CLIParserFree(ctx);
if (addr <= 0 || addr >= EM4X50_NO_WORDS) { if (addr <= 0 || addr >= EM4X50_NO_WORDS) {
PrintAndLogEx(FAILED, "address has to be within range [0, 31]"); PrintAndLogEx(FAILED, "address has to be within range [0, 31]");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (word_len != 4) { if (word_len != 4) {
PrintAndLogEx(FAILED, "word/data length must be 4 bytes instead of %d", word_len); PrintAndLogEx(FAILED, "word/data length must be 4 bytes instead of %d", word_len);
return PM3_EINVARG; return PM3_EINVARG;
} }
em4x50_data_t etd = {.pwd_given = false}; em4x50_data_t etd = {.pwd_given = false};
if (pwd_len) { if (pwd_len) {
if (pwd_len != 4) { if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
@ -895,7 +895,7 @@ int CmdEM4x50WritePwd(const char *Cmd) {
arg_str1("n", "new", "<hex>", "new password, 4 hex bytes, lsb"), arg_str1("n", "new", "<hex>", "new password, 4 hex bytes, lsb"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int pwd_len = 0; int pwd_len = 0;
uint8_t pwd[4] = {0x0}; uint8_t pwd[4] = {0x0};
@ -907,7 +907,7 @@ int CmdEM4x50WritePwd(const char *Cmd) {
CLIParserFree(ctx); CLIParserFree(ctx);
em4x50_data_t etd; em4x50_data_t etd;
if (pwd_len != 4) { if (pwd_len != 4) {
PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len);
return PM3_EINVARG; return PM3_EINVARG;
@ -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;
} }
@ -961,7 +961,7 @@ int CmdEM4x50Wipe(const char *Cmd) {
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int pwd_len = 0; int pwd_len = 0;
uint8_t pwd[4] = {0x0}; uint8_t pwd[4] = {0x0};
CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len);
@ -977,7 +977,7 @@ int CmdEM4x50Wipe(const char *Cmd) {
etd.password1 = BYTES2UINT32(pwd); etd.password1 = BYTES2UINT32(pwd);
etd.pwd_given = true; etd.pwd_given = true;
// clear password // clear password
PacketResponseNG resp; PacketResponseNG resp;
clearCommandBuffer(); clearCommandBuffer();
@ -996,13 +996,13 @@ int CmdEM4x50Wipe(const char *Cmd) {
// from now on new password 0x0 // from now on new password 0x0
etd.password1 = 0x0; etd.password1 = 0x0;
// clear data (words 1 to 31) // clear data (words 1 to 31)
for (int i = 1; i < EM4X50_DEVICE_SERIAL; i++) { for (int i = 1; i < EM4X50_DEVICE_SERIAL; i++) {
// no login necessary for blocks 3 to 31 // no login necessary for blocks 3 to 31
etd.pwd_given = (i <= EM4X50_CONTROL); etd.pwd_given = (i <= EM4X50_CONTROL);
PrintAndLogEx(INPLACE, "Wiping block %i", i); PrintAndLogEx(INPLACE, "Wiping block %i", i);
etd.addresses = i << 8 | i; etd.addresses = i << 8 | i;
@ -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;
@ -1044,7 +1044,7 @@ int CmdEM4x50Restore(const char *Cmd) {
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
int uidLen = 0; int uidLen = 0;
uint8_t uid[4] = {0x0}; uint8_t uid[4] = {0x0};
@ -1099,7 +1099,7 @@ int CmdEM4x50Restore(const char *Cmd) {
etd.addresses = i << 8 | i; etd.addresses = i << 8 | i;
etd.word = reflect32(BYTES2UINT32((data + 4 * i))); etd.word = reflect32(BYTES2UINT32((data + 4 * i)));
PacketResponseNG resp; PacketResponseNG resp;
clearCommandBuffer(); clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd));
@ -1116,7 +1116,7 @@ int CmdEM4x50Restore(const char *Cmd) {
} }
} }
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Done"); PrintAndLogEx(INFO, "Done");
return PM3_SUCCESS; return PM3_SUCCESS;
} }
@ -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,41 +16,78 @@
#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:"));
// Print out each section as memory map in datasheet
for(int i=1; i <= 32; i+=2) {
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, "");
} }
int em4x70_info(void) { int em4x70_info(void) {
em4x70_data_t edata = { em4x70_data_t edata = {
.parity = false // TODO: try both? or default to true .parity = false // TODO: try both? or default to true
}; };
clearCommandBuffer(); clearCommandBuffer();
SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(edata)); SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(edata));
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

@ -727,7 +727,7 @@ static int CmdFdxBClone(const char *Cmd) {
} }
uint32_t extended = 0; uint32_t extended = 0;
bool has_extended = false; bool has_extended = false;
if (extended_len) { if (extended_len) {
extended = bytes_to_num(edata, extended_len); extended = bytes_to_num(edata, extended_len);
has_extended = true; has_extended = true;
@ -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;
@ -820,7 +820,7 @@ static int CmdFdxBSim(const char *Cmd) {
CLIParserFree(ctx); CLIParserFree(ctx);
uint32_t extended = 0; uint32_t extended = 0;
bool has_extended = false; bool has_extended = false;
if (extended_len) { if (extended_len) {
extended = bytes_to_num(edata, extended_len); extended = bytes_to_num(edata, extended_len);
has_extended = true; has_extended = true;

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

@ -60,16 +60,16 @@ static int sendTry(uint8_t format_idx, wiegand_card_t *card, uint32_t delay, boo
if (HIDPack(format_idx, card, &packed) == false) { if (HIDPack(format_idx, card, &packed) == false) {
PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format.");
return PM3_ESOFT; return PM3_ESOFT;
} }
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
@ -665,7 +665,7 @@ static int CmdIndalaClone(const char *Cmd) {
uint8_t max = 0; uint8_t max = 0;
uint32_t blocks[8] = {0}; uint32_t blocks[8] = {0};
char cardtype[16] = {"T55x7"}; char cardtype[16] = {"T55x7"};
if (is_long_uid) { if (is_long_uid) {
blocks[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK2 | (7 << T55x7_MAXBLOCK_SHIFT); blocks[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK2 | (7 << T55x7_MAXBLOCK_SHIFT);
@ -673,7 +673,7 @@ static int CmdIndalaClone(const char *Cmd) {
blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK2 | (7 << T5555_MAXBLOCK_SHIFT); blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK2 | (7 << T5555_MAXBLOCK_SHIFT);
snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); snprintf(cardtype, sizeof(cardtype), "Q5/T5555");
} }
if (em) { if (em) {
blocks[0] = EM4305_INDALA_224_CONFIG_BLOCK; blocks[0] = EM4305_INDALA_224_CONFIG_BLOCK;
snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); snprintf(cardtype, sizeof(cardtype), "EM4305/4469");
@ -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 {
@ -737,7 +737,7 @@ static int CmdIndalaClone(const char *Cmd) {
blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK1 | (2 << T5555_MAXBLOCK_SHIFT); blocks[0] = T5555_FIXED | T5555_SET_BITRATE(32) | T5555_MODULATION_PSK1 | (2 << T5555_MAXBLOCK_SHIFT);
snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); snprintf(cardtype, sizeof(cardtype), "Q5/T5555");
} }
if (em) { if (em) {
blocks[0] = EM4305_INDALA_64_CONFIG_BLOCK; blocks[0] = EM4305_INDALA_64_CONFIG_BLOCK;
snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); snprintf(cardtype, sizeof(cardtype), "EM4305/4469");
@ -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,11 +219,11 @@ 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));
int res; int res;
if (em) { if (em) {
res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false); res = em4x05_clone_tag(blocks, ARRAYLEN(blocks), 0, false);

View file

@ -264,7 +264,7 @@ static int CmdMotorolaSim(const char *Cmd) {
}; };
CLIExecWithReturn(ctx, Cmd, argtable, true); CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIParserFree(ctx); CLIParserFree(ctx);
// PSK sim. // PSK sim.
PrintAndLogEx(INFO, " PSK1 at 66 kHz... Interesting."); PrintAndLogEx(INFO, " PSK1 at 66 kHz... Interesting.");
PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); PrintAndLogEx(INFO, " To be implemented, feel free to contribute!");

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 {
@ -373,7 +373,7 @@ static int CmdLFNedapClone(const char *Cmd) {
arg_u64_0(NULL, "st", "<dec>", "optional - sub type (default 5)"), arg_u64_0(NULL, "st", "<dec>", "optional - sub type (default 5)"),
arg_u64_1(NULL, "cc", "<dec>", "customer code (0-4095)"), arg_u64_1(NULL, "cc", "<dec>", "customer code (0-4095)"),
arg_u64_1(NULL, "id", "<dec>", "ID (0-99999)"), arg_u64_1(NULL, "id", "<dec>", "ID (0-99999)"),
arg_lit0("l", "long", "optional - long (128), default to short (64)"), arg_lit0("l", "long", "optional - long (128), default to short (64)"),
arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"),
arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"),
arg_param_end arg_param_end
@ -395,17 +395,17 @@ static int CmdLFNedapClone(const char *Cmd) {
} }
if (sub_type > 0xF) { if (sub_type > 0xF) {
PrintAndLogEx(FAILED, "out-of-range, valid subtype is between 0-15"); PrintAndLogEx(FAILED, "out-of-range, valid subtype is between 0-15");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (customer_code > 0xFFF) { if (customer_code > 0xFFF) {
PrintAndLogEx(FAILED, "out-of-range, valid customer code is between 0-4095"); PrintAndLogEx(FAILED, "out-of-range, valid customer code is between 0-4095");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (id > 99999) { if (id > 99999) {
PrintAndLogEx(FAILED, "out-of-range, id max value is 99999"); PrintAndLogEx(FAILED, "out-of-range, id max value is 99999");
return PM3_EINVARG; return PM3_EINVARG;
} }
PrintAndLogEx(SUCCESS, "NEDAP (%s) - ID: " _GREEN_("%05u") " subtype: " _GREEN_("%1u") " customer code: " _GREEN_("%u / 0x%03X") PrintAndLogEx(SUCCESS, "NEDAP (%s) - ID: " _GREEN_("%05u") " subtype: " _GREEN_("%1u") " customer code: " _GREEN_("%u / 0x%03X")
@ -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);
@ -492,7 +492,7 @@ static int CmdLFNedapSim(const char *Cmd) {
arg_u64_0(NULL, "st", "<dec>", "optional - sub type (default 5)"), arg_u64_0(NULL, "st", "<dec>", "optional - sub type (default 5)"),
arg_u64_1(NULL, "cc", "<dec>", "customer code (0-4095)"), arg_u64_1(NULL, "cc", "<dec>", "customer code (0-4095)"),
arg_u64_1(NULL, "id", "<dec>", "ID (0-99999)"), arg_u64_1(NULL, "id", "<dec>", "ID (0-99999)"),
arg_lit0("l", "long", "optional - long (128), default to short (64)"), arg_lit0("l", "long", "optional - long (128), default to short (64)"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -502,20 +502,20 @@ static int CmdLFNedapSim(const char *Cmd) {
uint32_t id = arg_get_u32_def(ctx, 3, 0); uint32_t id = arg_get_u32_def(ctx, 3, 0);
bool is_long = arg_get_lit(ctx, 4); bool is_long = arg_get_lit(ctx, 4);
CLIParserFree(ctx); CLIParserFree(ctx);
if (sub_type > 0xF) { if (sub_type > 0xF) {
PrintAndLogEx(FAILED, "out-of-range, valid subtype is between 0-15"); PrintAndLogEx(FAILED, "out-of-range, valid subtype is between 0-15");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (customer_code > 0xFFF) { if (customer_code > 0xFFF) {
PrintAndLogEx(FAILED, "out-of-range, valid customer code is between 0-4095"); PrintAndLogEx(FAILED, "out-of-range, valid customer code is between 0-4095");
return PM3_EINVARG; return PM3_EINVARG;
} }
if (id > 99999) { if (id > 99999) {
PrintAndLogEx(FAILED, "out-of-range, id max value is 99999"); PrintAndLogEx(FAILED, "out-of-range, id max value is 99999");
return PM3_EINVARG; return PM3_EINVARG;
} }
PrintAndLogEx(SUCCESS, "NEDAP (%s) - ID: " _GREEN_("%05u") " subtype: " _GREEN_("%1u") " customer code: " _GREEN_("%u / 0x%03X") PrintAndLogEx(SUCCESS, "NEDAP (%s) - ID: " _GREEN_("%05u") " subtype: " _GREEN_("%1u") " customer code: " _GREEN_("%u / 0x%03X")

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

@ -2806,7 +2806,7 @@ char *GetModelStrFromCID(uint32_t cid) {
} }
char *GetConfigBlock0Source(uint8_t id) { char *GetConfigBlock0Source(uint8_t id) {
static char buf[40]; static char buf[40];
char *retStr = buf; char *retStr = buf;

View file

@ -129,7 +129,7 @@ typedef struct {
notSet = 0x00, notSet = 0x00,
autoDetect = 0x01, autoDetect = 0x01,
userSet = 0x02, userSet = 0x02,
tagRead = 0x03, tagRead = 0x03,
} block0Status; } block0Status;
enum { enum {
RF_8 = 0x00, RF_8 = 0x00,

View file

@ -330,7 +330,7 @@ static int CmdTIWrite(const char *Cmd) {
arg_param_begin, arg_param_begin,
arg_str1("r", "raw", "<hex>", "raw hex data. 8 bytes max"), arg_str1("r", "raw", "<hex>", "raw hex data. 8 bytes max"),
arg_str0(NULL, "crc", "<hex>", "optional - crc"), arg_str0(NULL, "crc", "<hex>", "optional - crc"),
arg_param_end arg_param_end
}; };
CLIExecWithReturn(ctx, Cmd, argtable, false); CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -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