From d3bcdbdabfccb09e93be14d6478eeccc0b131201 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 13 Nov 2019 18:00:51 +0100 Subject: [PATCH 1/1] mod 'hf list' (#881) * switch to argtable command line parsing (i.e. options must now be preceded by '-') * add option '-r' to display relative times * add option '-u' to display times in microseconds * fix: graceful exit if trace is requested from offline PM3 --- client/cmdhflist.c | 289 ++++++++++++++++++++++----------------------- 1 file changed, 141 insertions(+), 148 deletions(-) diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 29d10d92..ee21945f 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -18,6 +18,7 @@ #include #include "util.h" #include "ui.h" +#include "cliparser/cliparser.h" #include "comms.h" #include "iso14443crc.h" #include "iso15693tools.h" @@ -317,7 +318,7 @@ void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } -void annotateIso14443_4(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ +void annotateIso14443_4(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { // S-block if ((cmd[0] & 0xc3) == 0xc2) { switch (cmd[0] & 0x30) { @@ -364,8 +365,7 @@ void annotateIso14443_4(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ 0A 11 22 33 44 55 66 = Authenticate (11 22 33 44 55 66 = data to authenticate) **/ -void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ +void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]){ case ISO14443B_REQB : snprintf(exp,size,"REQB");break; case ISO14443B_ATTRIB : snprintf(exp,size,"ATTRIB");break; @@ -383,8 +383,7 @@ void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } -void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ +void annotateIso14443a(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]) { case ISO14443A_CMD_WUPA: @@ -802,22 +801,18 @@ static bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, boo } -bool is_last_record(uint16_t tracepos, uint8_t *trace, uint16_t traceLen) -{ +bool is_last_record(uint16_t tracepos, uint8_t *trace, uint16_t traceLen) { return(tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen); } -bool next_record_is_response(uint16_t tracepos, uint8_t *trace) -{ +bool next_record_is_response(uint16_t tracepos, uint8_t *trace) { uint16_t next_records_datalen = *((uint16_t *)(trace + tracepos + sizeof(uint32_t) + sizeof(uint16_t))); - return(next_records_datalen & 0x8000); } -bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) -{ +bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) { #define MAX_TOPAZ_READER_CMD_LEN 16 @@ -855,13 +850,13 @@ bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t } -uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes) -{ +uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes, uint32_t *prev_EOT, bool times_in_us) { bool isResponse; uint16_t data_len, parity_len; uint32_t duration; uint8_t topaz_reader_command[9]; - uint32_t timestamp, first_timestamp, EndOfTransmissionTimestamp; + uint32_t timestamp, first_timestamp; + uint32_t EndOfTransmissionTimestamp = 0; char explanation[30] = {0}; uint8_t mfData[32] = {0}; size_t mfDataLen = 0; @@ -982,8 +977,6 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui //--- Draw the CRC column char *crc = (crcStatus == 0 ? "!crc" : (crcStatus == 1 ? " ok " : " ")); - EndOfTransmissionTimestamp = timestamp + duration; - if (protocol == PROTO_MIFARE) annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, parity_len, isResponse); @@ -1000,16 +993,43 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } + uint32_t previousEndOfTransmissionTimestamp = 0; + if (prev_EOT) { + if (*prev_EOT) { + previousEndOfTransmissionTimestamp = *prev_EOT; + } else { + previousEndOfTransmissionTimestamp = timestamp; + } + } + EndOfTransmissionTimestamp = timestamp + duration; + if (prev_EOT) *prev_EOT = EndOfTransmissionTimestamp; + int num_lines = MIN((data_len - 1)/16 + 1, 16); for (int j = 0; j < num_lines ; j++) { if (j == 0) { - PrintAndLog(" %10" PRIu32 " | %10" PRIu32 " | %s |%-64s | %s| %s", - (timestamp - first_timestamp), - (EndOfTransmissionTimestamp - first_timestamp), - (isResponse ? "Tag" : "Rdr"), - line[j], - (j == num_lines-1) ? crc : " ", - (j == num_lines-1) ? explanation : ""); + uint32_t time1 = timestamp - first_timestamp; + uint32_t time2 = EndOfTransmissionTimestamp - first_timestamp; + if (prev_EOT) { + time1 = timestamp - previousEndOfTransmissionTimestamp; + time2 = duration; + } + if (times_in_us) { + PrintAndLog(" %10.1f | %10.1f | %s |%-64s | %s| %s", + (float)time1/13.56, + (float)time2/13.56, + isResponse ? "Tag" : "Rdr", + line[j], + (j == num_lines-1) ? crc : " ", + (j == num_lines-1) ? explanation : ""); + } else { + PrintAndLog(" %10" PRIu32 " | %10" PRIu32 " | %s |%-64s | %s| %s", + time1, + time2, + isResponse ? "Tag" : "Rdr", + line[j], + (j == num_lines-1) ? crc : " ", + (j == num_lines-1) ? explanation : ""); + } } else { PrintAndLog(" | | |%-64s | %s| %s", line[j], @@ -1047,129 +1067,88 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } -int CmdHFList(const char *Cmd) -{ - bool showWaitCycles = false; - bool markCRCBytes = false; - bool loadFromFile = false; - bool PCSCtrace = false; - bool saveToFile = false; - char param1 = '\0'; - char param2 = '\0'; - char param3 = '\0'; - char param4 = '\0'; - char type[40] = {0}; - char filename[FILE_PATH_SIZE] = {0}; - uint8_t protocol = 0; - - // parse command line - int tlen = param_getstr(Cmd, 0, type, sizeof(type)); - if (param_getlength(Cmd, 1) == 1) { - param1 = param_getchar(Cmd, 1); - } else { - param_getstr(Cmd, 1, filename, sizeof(filename)); - } - if (param_getlength(Cmd, 2) == 1) { - param2 = param_getchar(Cmd, 2); - } else if (strlen(filename) == 0) { - param_getstr(Cmd, 2, filename, sizeof(filename)); - } - if (param_getlength(Cmd, 3) == 1) { - param3 = param_getchar(Cmd, 3); - } else if (strlen(filename) == 0) { - param_getstr(Cmd, 3, filename, sizeof(filename)); - } - if (param_getlength(Cmd, 4) == 1) { - param4 = param_getchar(Cmd, 4); - } else if (strlen(filename) == 0) { - param_getstr(Cmd, 4, filename, sizeof(filename)); - } - - // Validate params - bool errors = false; - - if(tlen == 0) { - errors = true; - } - - if(param1 == 'h' - || (param1 != 0 && param1 != 'f' && param1 != 'c' && param1 != 'l' && param1 != 'p') - || (param2 != 0 && param2 != 'f' && param2 != 'c' && param2 != 'l' && param1 != 'p') - || (param3 != 0 && param3 != 'f' && param3 != 'c' && param3 != 'l' && param1 != 'p') - || (param4 != 0 && param4 != 'f' && param4 != 'c' && param4 != 'l' && param4 != 'p')) { - errors = true; - } - - if(!errors) { - if (strcmp(type, "iclass") == 0) protocol = ICLASS; - else if(strcmp(type, "14a") == 0) protocol = ISO_14443A; - else if(strcmp(type, "mf") == 0) protocol = PROTO_MIFARE; - else if(strcmp(type, "14b") == 0) protocol = ISO_14443B; - else if(strcmp(type, "topaz") == 0) protocol = TOPAZ; - else if(strcmp(type, "7816") == 0) protocol = ISO_7816_4; - else if(strcmp(type, "14-4") == 0) protocol = ISO_14443_4; - else if(strcmp(type, "15") == 0) protocol = ISO_15693; - else if(strcmp(type, "raw") == 0) protocol = -1;//No crc, no annotations - else if (strcmp(type, "save") == 0) saveToFile = true; - else errors = true; - } - - if (param1 == 'f' || param2 == 'f' || param3 == 'f' || param4 == 'f') { - showWaitCycles = true; - } +int CmdHFList(const char *Cmd) { + + CLIParserInit("hf list", "\nList or save protocol data.", + "examples: hf list 14a -f -- interpret as ISO14443A communication and display Frame Delay Times\n"\ + " hf list iclass -- interpret as iClass trace\n"\ + " hf list -s myCardTrace.trc -- save trace for later use\n"\ + " hf list 14a -l myCardTrace.trc -- load trace and interpret as ISO14443A communication\n"); + void* argtable[] = { + arg_param_begin, + arg_lit0("f", "fdt", "display fdt (frame delay times)"), + arg_lit0("r", "relative", "show relative times (gap and duration)"), + arg_lit0("c", "crc" , "mark CRC bytes"), + arg_lit0("p", "pcsc", "show trace buffer from PCSC card reader instead of PM3"), + arg_str0("l", "load", "", "load trace from file"), + arg_str0("s", "save", "", "save trace to file"), + arg_lit0("u", "us", "display times in microseconds instead of clock cycles"), + arg_str0(NULL, NULL, "", "protocol to interpret. Possible values:\n"\ + "\traw - just show raw data without annotations (default)\n"\ + "\t14a - interpret data as ISO14443A communications\n"\ + "\tmf - interpret data as ISO14443A communications and decrypt Mifare Crypto1 stream\n"\ + "\t14b - interpret data as ISO14443B communications\n"\ + "\t15 - interpret data as ISO15693 communications\n"\ + "\ticlass - interpret data as iClass communications\n"\ + "\ttopaz - interpret data as Topaz communications\n"\ + "\t7816 - interpret data as 7816-4 APDU communications\n"\ + "\t14-4 - interpret data as ISO14443-4 communications"), + arg_param_end + }; - if (param1 == 'c' || param2 == 'c' || param3 == 'c' || param4 == 'c') { - markCRCBytes = true; + if (CLIParserParseString(Cmd, argtable, arg_getsize(argtable), true)){ + CLIParserFree(); + return 0; } - if (param1 == 'l' || param2 == 'l' || param3 == 'l' || param4 == 'l') { - loadFromFile = true; + bool showWaitCycles = arg_get_lit(1); + bool relative_times = arg_get_lit(2); + bool markCRCBytes = arg_get_lit(3); + bool PCSCtrace = arg_get_lit(4); + bool loadFromFile = arg_get_str_len(5); + bool saveToFile = arg_get_str_len(6); + bool times_in_us = arg_get_lit(7); + + uint32_t previous_EOT = 0; + uint32_t *prev_EOT = NULL; + if (relative_times) { + prev_EOT = &previous_EOT; } - - if (param1 == 'p' || param2 == 'p' || param3 == 'p' || param4 == 'p') { - PCSCtrace = true; + + char load_filename[FILE_PATH_SIZE] = {0}; + if (loadFromFile) { + strncpy(load_filename, arg_get_str(5)->sval[0], FILE_PATH_SIZE); } - - if ((loadFromFile || saveToFile) && strlen(filename) == 0) { - errors = true; + char save_filename[FILE_PATH_SIZE] = {0}; + if (saveToFile) { + strncpy(save_filename, arg_get_str(6)->sval[0], FILE_PATH_SIZE); } - - if (loadFromFile && saveToFile) { - errors = true; + + uint8_t protocol = -1; + if (arg_get_str_len(8)) { + if (strcmp(arg_get_str(8)->sval[0], "iclass") == 0) protocol = ICLASS; + else if(strcmp(arg_get_str(8)->sval[0], "14a") == 0) protocol = ISO_14443A; + else if(strcmp(arg_get_str(8)->sval[0], "mf") == 0) protocol = PROTO_MIFARE; + else if(strcmp(arg_get_str(8)->sval[0], "14b") == 0) protocol = ISO_14443B; + else if(strcmp(arg_get_str(8)->sval[0], "topaz") == 0) protocol = TOPAZ; + else if(strcmp(arg_get_str(8)->sval[0], "7816") == 0) protocol = ISO_7816_4; + else if(strcmp(arg_get_str(8)->sval[0], "14-4") == 0) protocol = ISO_14443_4; + else if(strcmp(arg_get_str(8)->sval[0], "15") == 0) protocol = ISO_15693; + else if(strcmp(arg_get_str(8)->sval[0], "raw") == 0) protocol = -1;//No crc, no annotations + else { + PrintAndLog("hf list: invalid argument \"%s\"\nTry 'hf list --help' for more information.", arg_get_str(8)->sval[0]); + CLIParserFree(); + return 0; + } } - if (errors) { - PrintAndLog("List or save protocol data."); - PrintAndLog("Usage: hf list [f] [c] [p] [l ]"); - PrintAndLog(" hf list save "); - PrintAndLog(" f - show frame delay times as well"); - PrintAndLog(" c - mark CRC bytes"); - PrintAndLog(" p - use trace buffer from PCSC card reader instead of PM3"); - PrintAndLog(" l - load data from file instead of trace buffer"); - PrintAndLog(" save - save data to file"); - PrintAndLog("Supported values:"); - PrintAndLog(" raw - just show raw data without annotations"); - PrintAndLog(" 14a - interpret data as iso14443a communications"); - PrintAndLog(" mf - interpret data as iso14443a communications and decrypt crypto1 stream"); - PrintAndLog(" 14b - interpret data as iso14443b communications"); - PrintAndLog(" 15 - interpret data as iso15693 communications"); - PrintAndLog(" iclass - interpret data as iclass communications"); - PrintAndLog(" topaz - interpret data as topaz communications"); - PrintAndLog(" 7816 - interpret data as 7816-4 APDU communications"); - PrintAndLog(" 14-4 - interpret data as ISO14443-4 communications"); - PrintAndLog(""); - PrintAndLog("example: hf list 14a f"); - PrintAndLog("example: hf list iclass"); - PrintAndLog("example: hf list save myCardTrace.trc"); - PrintAndLog("example: hf list 14a l myCardTrace.trc"); - return 0; - } + CLIParserFree(); uint8_t *trace; uint32_t tracepos = 0; uint32_t traceLen = 0; - + if (loadFromFile) { #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions FILE *tracefile = NULL; @@ -1179,8 +1158,8 @@ int CmdHFList(const char *Cmd) PrintAndLog("Cannot allocate memory for trace"); return 2; } - if ((tracefile = fopen(filename,"rb")) == NULL) { - PrintAndLog("Could not open file %s", filename); + if ((tracefile = fopen(load_filename,"rb")) == NULL) { + PrintAndLog("Could not open file %s", load_filename); free(trace); return 0; } @@ -1206,7 +1185,9 @@ int CmdHFList(const char *Cmd) trace = malloc(USB_CMD_DATA_SIZE); // Query for the size of the trace UsbCommand response; - GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); + if (!(GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, 500, false))) { + return 1; + } traceLen = response.arg[2]; if (traceLen > USB_CMD_DATA_SIZE) { uint8_t *p = realloc(trace, traceLen); @@ -1216,33 +1197,45 @@ int CmdHFList(const char *Cmd) return 2; } trace = p; - GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); + if (!(GetFromBigBuf(trace, traceLen, 0, NULL, 500, false))) { + return 1; + } } } if (saveToFile) { FILE *tracefile = NULL; - if ((tracefile = fopen(filename,"wb")) == NULL) { - PrintAndLog("Could not create file %s", filename); + if ((tracefile = fopen(save_filename,"wb")) == NULL) { + PrintAndLog("Could not create file %s", save_filename); return 1; } fwrite(trace, 1, traceLen, tracefile); - PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); + PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, save_filename); fclose(tracefile); } else { PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); PrintAndLog(""); - PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); - PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); - PrintAndLog("iClass - Timings are not as accurate"); + if (relative_times) { + PrintAndLog("Gap = time between transfers. Duration = duration of data transfer. Src = Source of transfer"); + } else { + PrintAndLog("Start = Start of Frame, End = End of Frame. Src = Source of transfer"); + } + if (times_in_us) { + PrintAndLog("All times are in microseconds"); + } else { + PrintAndLog("All times are in carrier periods (1/13.56Mhz)"); + } PrintAndLog(""); - PrintAndLog(" Start | End | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation |"); + if (relative_times) { + PrintAndLog(" Gap | Duration | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation |"); + } else { + PrintAndLog(" Start | End | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation |"); + } PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); ClearAuthData(); - while(tracepos < traceLen) - { - tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); + while(tracepos < traceLen) { + tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes, prev_EOT, times_in_us); } } -- 2.39.5