Some tunings of otptear:

- make tearoff_delay_us and tearoff_enabled globals
- use tearoff_hook and remove Dbprintf in critical tearoff timing
- move initial write from MifareU_Otp_Tearoff to CmdHF14AMfuOtpTearoff and make it optional (old behavior was writing initial 00000000 when -d was not provided)
- tearoff: compare with initial write, not with previous tearoff outcome
- rephrase some messages
- track all begin and end of erase and write phases, with quite complex logic to cover multiple cases (starting in middle of erased phase, starting with write 0, ...) and report them
- check against initial write error
- repeat same timing (up to 10x) in case of write/read errors then quit
- typos
This commit is contained in:
Philippe Teuwen 2021-03-07 23:43:53 +01:00
parent 55ccd18afd
commit e5c5629cf2
9 changed files with 121 additions and 93 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

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

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