Merge pull request #66 from RfidResearchGroup/master

Update
This commit is contained in:
mwalker33 2021-03-08 10:57:54 +11:00 committed by GitHub
commit 1614d920b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 143 additions and 140 deletions

View file

@ -73,8 +73,8 @@ extern uint32_t _stack_start, _stack_end;
struct common_area common_area __attribute__((section(".commonarea")));
static int button_status = BUTTON_NO_CLICK;
static bool allow_send_wtx = false;
static uint16_t tearoff_delay_us = 0;
static bool tearoff_enabled = false;
uint16_t tearoff_delay_us = 0;
bool tearoff_enabled = false;
int tearoff_hook(void) {
if (tearoff_enabled) {

View file

@ -1881,7 +1881,7 @@ void iClass_WriteBlock(uint8_t *msg) {
iclass_send_as_reader(write, write_len, &start_time, &eof_time);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
res = false;
switch_off();
if (payload->req.send_reply)

View file

@ -2972,7 +2972,7 @@ void ReaderIso14443a(PacketCommandNG *c) {
}
}
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
FpgaDisableTracing();
reply_mix(CMD_ACK, 0, 0, 0, NULL, 0);
} else {

View file

@ -2092,7 +2092,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) {
uint32_t eof_time = 0;
CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
FpgaDisableTracing();
reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0);
} else {

View file

@ -1477,7 +1477,7 @@ int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t
tosend_t *ts = get_tosend();
TransmitTo15693Tag(ts->buf, ts->max, &start_time);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
res = PM3_ETEAROFF;
@ -1598,7 +1598,7 @@ void ReaderIso15693(uint32_t parameter) {
uint32_t eof_time;
int recvlen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time);
if (recvlen == PM3_ETEAROFF) { // tearoff occured
if (recvlen == PM3_ETEAROFF) { // tearoff occurred
reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0);
} else {
@ -1928,7 +1928,7 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint
uint32_t start_time = 0;
int recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time);
if (recvlen == PM3_ETEAROFF) { // tearoff occured
if (recvlen == PM3_ETEAROFF) { // tearoff occurred
reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0);
} else {

View file

@ -2639,7 +2639,7 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) {
SendForward(len, false);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
StopTicks();
reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_ETEAROFF, NULL, 0);
} else {
@ -2681,7 +2681,7 @@ void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd) {
SendForward(len, false);
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured
if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred
StopTicks();
reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_ETEAROFF, NULL, 0);
} else {

View file

@ -2713,20 +2713,15 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) {
//
// Tear-off attack against MFU.
// - Moebius et al
void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) {
void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_testwrite) {
uint8_t blockNo = blno;
uint8_t data_fullwrite[4] = {0x00};
uint8_t data_testwrite[4] = {0x00};
memcpy(data_fullwrite, datain, 4);
memcpy(data_testwrite, datain + 4, 4);
if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off");
if (tearoff_time > 43000)
tearoff_time = 43000;
MifareUWriteBlockEx(blockNo, 0, data_fullwrite, false);
tearoff_delay_us = tearoff_time;
tearoff_enabled = true;
LEDsoff();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
@ -2750,15 +2745,9 @@ void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) {
return;
};
// send
ReaderTransmit(cmd, sizeof(cmd), NULL);
// Wait before cutting power. aka tear-off
LED_D_ON();
SpinDelayUsPrecision(tearoff_time);
if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!"));
switch_off();
ReaderTransmit(cmd, sizeof(cmd), NULL);
tearoff_hook();
reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0);
}

View file

@ -103,14 +103,6 @@ static inline uint32_t leadingzeros(uint64_t a) {
#else
return 0;
#endif
}
static inline uint32_t countones(uint64_t a) {
#if defined __GNUC__
return __builtin_popcountll(a);
#else
return 0;
#endif
}
const char *card_types[] = {
@ -2034,7 +2026,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
uint64_t a = bytes_to_num(data, 8);
bool starts = (leadingzeros(a) < 12);
bool ones = (countones(a) > 16 && countones(a) < 48);
bool ones = (bitcount64(a) > 16 && bitcount64(a) < 48);
if (starts && ones) {
PrintAndLogEx(INFO, "data looks encrypted, False Positives " _YELLOW_("ARE") " possible");

View file

@ -3102,7 +3102,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
"hf mfu otptear -b 3 -i 100 -s 1000\n"
"hf mfu otptear -b 3 -i 1 -e 200\n"
"hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE\n"
"hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quite when OTP is reset"
"hf mfu otptear -b 3 -i 100 -s 200 -e 2500 -d FFFFFFFF -t EEEEEEEE -m 00000000 -> quit when OTP is reset"
);
void *argtable[] = {
@ -3112,7 +3112,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
arg_u64_0("e", "end", "<dec>", "end time (def 3000 us)"),
arg_u64_0("s", "start", "<dec>", "start time (def 0 us)"),
arg_str0("d", "data", "<hex>", "initialise data before run (4 bytes)"),
arg_str0("t", "test", "<hex>", "test write data (4 bytes)"),
arg_str0("t", "test", "<hex>", "test write data (4 bytes, 00000000 by default)"),
arg_str0("m", "match", "<hex>", "exit criteria, if block matches this value (4 bytes)"),
arg_param_end
};
@ -3126,6 +3126,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
int d_len = 0;
uint8_t data[4] = {0x00};
CLIGetHexWithReturn(ctx, 5, data, &d_len);
bool use_data = (d_len > 0);
int t_len = 0;
uint8_t test[4] = {0x00};
@ -3150,7 +3151,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
return PM3_EINVARG;
}
if (start > (end - steps)) {
PrintAndLogEx(WARNING, "Start time larger then (end time + steps)");
PrintAndLogEx(WARNING, "Start time larger than (end time + steps)");
return PM3_EINVARG;
}
@ -3169,26 +3170,34 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
return PM3_EINVARG;
}
uint8_t teardata[8] = {0x00};
memcpy(teardata, data, sizeof(data));
memcpy(teardata + sizeof(data), test, sizeof(test));
uint8_t teardata[4] = {0x00};
memcpy(teardata, test, sizeof(test));
PrintAndLogEx(INFO, "----------------- " _CYAN_("MFU Tear off") " ---------------------");
PrintAndLogEx(INFO, "Starting Tear-off test");
PrintAndLogEx(INFO, "Target block no: %u", blockno);
PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(teardata, 4));
PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata + 4, 4));
if (use_data) {
PrintAndLogEx(INFO, "Target inital block data : %s", sprint_hex_inrow(data, 4));
}
PrintAndLogEx(INFO, "Target write block data : %s", sprint_hex_inrow(teardata, 4));
if (use_match) {
PrintAndLogEx(INFO, "Target match block data : %s", sprint_hex_inrow(match, 4));
}
PrintAndLogEx(INFO, "----------------------------------------------------");
uint8_t isOK;
bool got_pre = false, got_post = false, lock_on = false;
bool lock_on = false;
uint8_t pre[4] = {0};
uint8_t post[4] = {0};
uint32_t current = start;
int phase_clear = -1;
int phase_newwr = -1;
int phase_begin_clear = -1;
int phase_end_clear = -1;
int phase_begin_newwr = -1;
int phase_end_newwr = -1;
bool skip_phase1 = false;
uint8_t retries = 0;
uint8_t error_retries = 0;
while (current <= (end - steps)) {
while ((current <= (end - steps)) && (error_retries < 10)) {
if (kbd_enter_pressed()) {
PrintAndLogEx(INFO, "\naborted via keyboard!\n");
@ -3198,10 +3207,27 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
PrintAndLogEx(INFO, "Using tear-off delay " _GREEN_("%" PRIu32) " us", current);
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
PacketResponseNG resp;
got_pre = false;
if (use_data) {
SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, 0, 0, data, d_len);
bool got_written = false;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
isOK = resp.oldarg[0] & 0xff;
if (isOK) {
got_written = true;
}
}
if (! got_written) {
PrintAndLogEx(FAILED, "Failed to write block BEFORE");
error_retries++;
continue; // try again
}
}
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
bool got_pre = false;
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
isOK = resp.oldarg[0] & 0xFF;
if (isOK) {
@ -3209,9 +3235,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
got_pre = true;
}
}
if (! got_pre) {
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
error_retries++;
continue; // try again
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, 8);
SendCommandMIX(CMD_HF_MFU_OTP_TEAROFF, blockno, current, 0, teardata, sizeof(teardata));
// we be getting ACK that we are silently ignoring here..
@ -3222,10 +3252,11 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
if (resp.status != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Tear off reporting failure to select tag");
error_retries++;
continue;
}
got_post = false;
bool got_post = false;
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READBL, blockno, 0, 0, NULL, 0);
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
@ -3235,55 +3266,53 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
got_post = true;
}
}
if (! got_post) {
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
error_retries++;
continue; // try again
}
error_retries = 0;
char prestr[20] = {0};
snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre)));
char poststr[20] = {0};
snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post)));
if (got_pre && got_post) {
char prestr[20] = {0};
snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre)));
char poststr[20] = {0};
snprintf(poststr, sizeof(poststr), "%s", sprint_hex_inrow(post, sizeof(post)));
if (memcmp(pre, post, sizeof(pre)) == 0) {
PrintAndLogEx(INFO, "Current %02d (0x%02X) %s"
, blockno
, blockno
, poststr
);
} else {
// skip first message, since its the reset write.
if (current == start) {
PrintAndLogEx(INFO, "Inital write");
} else {
PrintAndLogEx(INFO, _CYAN_("Tear off occured") " : %02d (0x%02X) %s vs " _RED_("%s")
, blockno
, blockno
, prestr
, poststr
);
lock_on = true;
if (phase_clear == -1)
phase_clear = current;
// new write phase must be atleast 100us later..
if (phase_clear > -1 && phase_newwr == -1 && current > (phase_clear + 100))
phase_newwr = current;
}
}
if (use_match && memcmp(pre, match, sizeof(pre)) == 0) {
PrintAndLogEx(SUCCESS, "Block matches!\n");
break;
}
if (memcmp(pre, post, sizeof(pre)) == 0) {
PrintAndLogEx(INFO, "Current : %02d (0x%02X) %s"
, blockno
, blockno
, poststr
);
} else {
if (got_pre == false)
PrintAndLogEx(FAILED, "Failed to read block BEFORE");
if (got_post == false)
PrintAndLogEx(FAILED, "Failed to read block AFTER");
PrintAndLogEx(INFO, _CYAN_("Tear off occurred") " : %02d (0x%02X) %s => " _RED_("%s")
, blockno
, blockno
, prestr
, poststr
);
lock_on = true;
if ((phase_begin_clear == -1) && (bitcount32(*(uint32_t*)pre) > bitcount32(*(uint32_t*)post)))
phase_begin_clear = current;
if ((phase_begin_clear > -1) && (phase_end_clear == -1) && (bitcount32(*(uint32_t*)post) == 0))
phase_end_clear = current;
if ((current == start) && (phase_end_clear > -1))
skip_phase1 = true;
// new write phase must be atleast 100us later..
if (((bitcount32(*(uint32_t*)pre) == 0) || (phase_end_clear > -1)) && (phase_begin_newwr == -1) && (bitcount32(*(uint32_t*)post) != 0) && (skip_phase1 || (current > (phase_end_clear + 100))))
phase_begin_newwr = current;
if ((phase_begin_newwr > -1) && (phase_end_newwr == -1) && (memcmp(post, teardata, sizeof(teardata)) == 0))
phase_end_newwr = current;
}
if (use_match && memcmp(post, match, sizeof(post)) == 0) {
PrintAndLogEx(SUCCESS, "Block matches stop condition!\n");
break;
}
/* TEMPORALLY DISABLED
@ -3314,11 +3343,17 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) {
}
PrintAndLogEx(INFO, "----------------------------------------------------");
if (phase_clear > - 1) {
PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_clear);
if ((phase_begin_clear > - 1) && (phase_begin_clear != start)) {
PrintAndLogEx(INFO, "Erase phase start boundary around " _YELLOW_("%5d") " us", phase_begin_clear);
}
if (phase_newwr > - 1) {
PrintAndLogEx(INFO, "New phase boundary around " _YELLOW_("%d") " us", phase_newwr);
if ((phase_end_clear > - 1) && (phase_end_clear != start)){
PrintAndLogEx(INFO, "Erase phase end boundary around " _YELLOW_("%5d") " us", phase_end_clear);
}
if (phase_begin_newwr > - 1) {
PrintAndLogEx(INFO, "Write phase start boundary around " _YELLOW_("%5d") " us", phase_begin_newwr);
}
if (phase_end_newwr > - 1) {
PrintAndLogEx(INFO, "Write phase end boundary around " _YELLOW_("%5d") " us", phase_end_newwr);
}
PrintAndLogEx(NORMAL, "");
return PM3_SUCCESS;

View file

@ -118,16 +118,6 @@ static void verify_values(uint64_t *animalid, uint32_t *countryid, uint32_t *ext
}
}
static inline uint32_t bitcount(uint32_t a) {
#if defined __GNUC__
return __builtin_popcountl(a);
#else
a = a - ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
#endif
}
// FDX-B ISO11784/85 demod (aka animal tag) BIPHASE, inverted, rf/32, with preamble of 00000000001 (128bits)
// 8 databits + 1 parity (1)
// CIITT 16 chksum
@ -594,7 +584,7 @@ int demodFDXB(bool verbose) {
uint8_t bt_par = (extended & 0x100) >> 8;
uint8_t bt_temperature = extended & 0xff;
uint8_t bt_calc_parity = (bitcount(bt_temperature) & 0x1) ? 0 : 1;
uint8_t bt_calc_parity = (bitcount32(bt_temperature) & 0x1) ? 0 : 1;
uint8_t is_bt_temperature = (bt_calc_parity == bt_par) && !(extended & 0xe00) ;
if (is_bt_temperature) {

View file

@ -37,20 +37,10 @@ const uint8_t translateTable[10] = {8, 2, 1, 12, 4, 5, 10, 13, 0, 9};
const uint8_t invTranslateTable[16] = {8, 2, 1, 0xff, 4, 5, 0xff, 0xff, 0, 9, 6, 0xff, 3, 7, 0xff, 0xff};
const uint8_t preamble[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}; // zero inside
static inline uint32_t bitcount(uint32_t a) {
#if defined __GNUC__
return __builtin_popcountl(a);
#else
a = a - ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
return (((a + (a >> 4)) & 0x0f0f0f0f) * 0x01010101) >> 24;
#endif
}
static uint8_t isEven_64_63(const uint8_t *data) { // 8
uint32_t tmp[2];
memcpy(tmp, data, 8);
return (bitcount(tmp[0]) + (bitcount(tmp[1] & 0xfeffffff))) & 1;
return (bitcount32(tmp[0]) + (bitcount32(tmp[1] & 0xfeffffff))) & 1;
}
//NEDAP demod - ASK/Biphase (or Diphase), RF/64 with preamble of 1111111110 (always a 128 bit data stream)

View file

@ -447,7 +447,7 @@ static int CmdT55xxSetConfig(const char *Cmd) {
arg_lit0(NULL, "st", "set/reset Sequence Terminator on"),
arg_int0(NULL, "rate", "<dec>", "set bitrate <8|16|32|40|50|64|100|128>"),
arg_str0("c", "blk0", "<hex>", "set configuration from a block0 (4 hex bytes)"),
arg_int0("o", "offset", "<dec>", "set offset, where data should start decode in bitstream"),
arg_int0("o", "offset", "<0-255>", "set offset, where data should start decode in bitstream "),
};
uint8_t idx = 19;
@ -467,7 +467,8 @@ static int CmdT55xxSetConfig(const char *Cmd) {
bool use_q5 = arg_get_lit(ctx, idx++);
bool use_st = arg_get_lit(ctx, idx++);
int bitrate = arg_get_int_def(ctx, idx++, -1);
int bitrate = arg_get_int_def(ctx, idx, -1);
idx++;
bool gotconf = false;
uint32_t block0 = 0;
@ -481,7 +482,8 @@ static int CmdT55xxSetConfig(const char *Cmd) {
gotconf = true;
}
int offset = arg_get_int_def(ctx, idx++, -1);
int offset = arg_get_int_def(ctx, idx, -1);
idx++;
bool r0 = arg_get_lit(ctx, idx++);
bool r1 = arg_get_lit(ctx, idx++);
@ -519,7 +521,7 @@ static int CmdT55xxSetConfig(const char *Cmd) {
}
// validate user specified offset
if (offset > -1) {
if (offset > -1 && offset < 0x100) {
config.offset = offset;
}
@ -2939,35 +2941,36 @@ static int CmdT55xxWipe(const char *Cmd) {
PrintAndLogEx(INFO, "Begin wiping...");
// Creating cmd string for write block :)
char writeData[36] = {0};
char *ptrData = writeData;
snprintf(ptrData, sizeof(writeData), "-b 0 ");
char wcmd[36] = {0};
char *pwcmd = wcmd;
snprintf(pwcmd, sizeof(wcmd), "-b 0 ");
if (usepwd) {
snprintf(ptrData + strlen(writeData), sizeof(writeData) - strlen(writeData), "p %08x ", password);
snprintf(pwcmd + strlen(wcmd), sizeof(wcmd) - strlen(wcmd), "-p %08x ", password);
}
snprintf(ptrData + strlen(writeData), sizeof(writeData) - strlen(writeData), "d %08X", block0);
snprintf(pwcmd + strlen(wcmd), sizeof(wcmd) - strlen(wcmd), "-d %08X", block0);
if (CmdT55xxWriteBlock(ptrData) != PM3_SUCCESS)
if (CmdT55xxWriteBlock(pwcmd) != PM3_SUCCESS)
PrintAndLogEx(WARNING, "Warning: error writing blk 0");
for (uint8_t blk = 1; blk < 8; blk++) {
snprintf(ptrData, sizeof(writeData), "-b %d -d 0", blk);
snprintf(pwcmd, sizeof(wcmd), "-b %d -d 00000000", blk);
if (CmdT55xxWriteBlock(ptrData) != PM3_SUCCESS)
if (CmdT55xxWriteBlock(pwcmd) != PM3_SUCCESS)
PrintAndLogEx(WARNING, "Warning: error writing blk %d", blk);
memset(writeData, 0x00, sizeof(writeData));
memset(wcmd, 0x00, sizeof(wcmd));
}
// Check and rest t55xx downlink mode.
if (config.downlink_mode != T55XX_DLMODE_FIXED) { // Detect found a different mode so card must support
snprintf(ptrData, sizeof(writeData), "-b 3 --pg1 -d 00000000");
if (CmdT55xxWriteBlock(ptrData) != PM3_SUCCESS) {
snprintf(pwcmd, sizeof(wcmd), "-b 3 --pg1 -d 00000000");
if (CmdT55xxWriteBlock(pwcmd) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Warning: failed writing block 3 page 1 (config)");
}
memset(writeData, 0x00, sizeof(writeData));
memset(wcmd, 0x00, sizeof(wcmd));
}
return PM3_SUCCESS;
}

View file

@ -56,6 +56,10 @@ struct version_information {
#define DBG_EXTENDED 4 // errors + info + debug + breaking debug messages
extern int DBGLEVEL;
// tear-off
extern uint16_t tearoff_delay_us;
extern bool tearoff_enabled;
// reader voltage field detector
#define MF_MINFIELDV 4000