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
This commit is contained in:
pwpiwi 2019-11-13 18:00:51 +01:00 committed by GitHub
parent 496bb4be33
commit d3bcdbdabf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -18,6 +18,7 @@
#include <stdbool.h>
#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;
int CmdHFList(const char *Cmd) {
// 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));
}
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", "<filename>", "load trace from file"),
arg_str0("s", "save", "<filename>", "save trace to file"),
arg_lit0("u", "us", "display times in microseconds instead of clock cycles"),
arg_str0(NULL, NULL, "<protocol>", "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
};
// 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;
}
if (param1 == 'c' || param2 == 'c' || param3 == 'c' || param4 == 'c') {
markCRCBytes = true;
}
if (param1 == 'l' || param2 == 'l' || param3 == 'l' || param4 == 'l') {
loadFromFile = true;
}
if (param1 == 'p' || param2 == 'p' || param3 == 'p' || param4 == 'p') {
PCSCtrace = true;
}
if ((loadFromFile || saveToFile) && strlen(filename) == 0) {
errors = true;
}
if (loadFromFile && saveToFile) {
errors = true;
}
if (errors) {
PrintAndLog("List or save protocol data.");
PrintAndLog("Usage: hf list <protocol> [f] [c] [p] [l <filename>]");
PrintAndLog(" hf list save <filename>");
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 <protocol> 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");
if (CLIParserParseString(Cmd, argtable, arg_getsize(argtable), true)){
CLIParserFree();
return 0;
}
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;
}
char load_filename[FILE_PATH_SIZE] = {0};
if (loadFromFile) {
strncpy(load_filename, arg_get_str(5)->sval[0], FILE_PATH_SIZE);
}
char save_filename[FILE_PATH_SIZE] = {0};
if (saveToFile) {
strncpy(save_filename, arg_get_str(6)->sval[0], FILE_PATH_SIZE);
}
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;
}
}
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);
}
}