textual
[RRG-proxmark3.git] / client / src / cmdtrace.c
blob977a7483b29b614349ff285268e6acee5a7dee10
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2018 iceman <iceman at iuse.se>
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // Trace commands
9 //-----------------------------------------------------------------------------
10 #include "cmdtrace.h"
12 #include <ctype.h>
14 #include "cmdparser.h" // command_t
15 #include "protocols.h"
16 #include "parity.h" // oddparity
17 #include "cmdhflist.h" // annotations
18 #include "commonutil.h" // ARRAYLEN
19 #include "mifare/mifaredefault.h" // mifare default key array
20 #include "comms.h" // for sending cmds to device. GetFromBigBuf
21 #include "fileutils.h" // for saveFile
22 #include "cmdlfhitag.h" // annotate hitag
23 #include "pm3_cmd.h" // tracelog_hdr_t
24 #include "cliparser.h" // args..
26 static int CmdHelp(const char *Cmd);
28 // trace pointer
29 static uint8_t *g_trace;
30 static long g_traceLen = 0;
32 static bool is_last_record(uint16_t tracepos, uint16_t traceLen) {
33 return ((tracepos + TRACELOG_HDR_LEN) >= traceLen);
36 static bool next_record_is_response(uint16_t tracepos, uint8_t *trace) {
37 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
38 return (hdr->isResponse);
41 static bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen,
42 uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) {
44 #define MAX_TOPAZ_READER_CMD_LEN 16
46 uint32_t last_timestamp = timestamp + *duration;
48 if ((*data_len != 1) || (frame[0] == TOPAZ_WUPA) || (frame[0] == TOPAZ_REQA)) return false;
50 memcpy(topaz_reader_command, frame, *data_len);
52 while (!is_last_record(*tracepos, traceLen) && !next_record_is_response(*tracepos, trace)) {
54 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + *tracepos);
56 *tracepos += TRACELOG_HDR_LEN + hdr->data_len;
58 if ((hdr->data_len == 1) && (*data_len + hdr->data_len <= MAX_TOPAZ_READER_CMD_LEN)) {
59 memcpy(topaz_reader_command + *data_len, hdr->frame, hdr->data_len);
60 *data_len += hdr->data_len;
61 last_timestamp = hdr->timestamp + hdr->duration;
62 } else {
63 // rewind and exit
64 *tracepos = *tracepos - hdr->data_len - TRACELOG_HDR_LEN;
65 break;
67 *tracepos += TRACELOG_PARITY_LEN(hdr);
70 *duration = last_timestamp - timestamp;
72 return true;
75 static uint16_t printHexLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol) {
76 // sanity check
77 if (is_last_record(tracepos, traceLen)) return traceLen;
79 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
81 if (TRACELOG_HDR_LEN + hdr->data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) {
82 return traceLen;
85 //set trace position
86 tracepos += TRACELOG_HDR_LEN + hdr->data_len + TRACELOG_PARITY_LEN(hdr);
88 if (hdr->data_len == 0) {
89 PrintAndLogEx(NORMAL, "<empty trace - possible error>");
90 return tracepos;
93 uint16_t ret;
95 switch (protocol) {
96 case ISO_14443A: {
97 /* https://www.kaiser.cx/pcap-iso14443.html defines a pseudo header:
98 * version (currently 0x00), event (Rdr: 0xfe, Tag: 0xff), length (2 bytes)
99 * to convert to pcap(ng) via text2pcap or to import into Wireshark
100 * we use format timestamp, newline, offset (0x000000), pseudo header, data
101 * `text2pcap -t "%S." -l 264 -n <input-text-file> <output-pcapng-file>`
103 char line[(hdr->data_len * 3) + 1];
104 char *ptr = &line[0];
106 for (int i = 0; i < hdr->data_len ; i++) {
107 ptr += sprintf(ptr, "%02x ", hdr->frame[i]);
110 char data_len_str[5];
111 char temp_str1[3] = {0};
112 char temp_str2[3] = {0};
114 sprintf(data_len_str, "%04x", hdr->data_len);
115 memmove(temp_str1, data_len_str, 2);
116 memmove(temp_str2, data_len_str + 2, 2);
118 PrintAndLogEx(NORMAL, "0.%010u", hdr->timestamp);
119 PrintAndLogEx(NORMAL, "000000 00 %s %s %s %s",
120 (hdr->isResponse ? "ff" : "fe"),
121 temp_str1,
122 temp_str2,
123 line);
124 ret = tracepos;
125 break;
127 default:
128 PrintAndLogEx(NORMAL, "Currently only 14a supported");
129 ret = traceLen;
130 break;
133 return ret;
136 static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes, uint32_t *prev_eot, bool use_us,
137 const uint64_t *mfDicKeys, uint32_t mfDicKeysCount) {
138 // sanity check
139 if (is_last_record(tracepos, traceLen)) {
140 PrintAndLogEx(DEBUG, "last record triggered. t-pos: %u t-len %u", tracepos, traceLen);
141 return traceLen;
144 uint32_t end_of_transmission_timestamp = 0;
145 uint32_t duration;
146 uint16_t data_len;
147 uint8_t topaz_reader_command[9];
148 char explanation[40] = {0};
149 uint8_t mfData[32] = {0};
150 size_t mfDataLen = 0;
151 tracelog_hdr_t *first_hdr = (tracelog_hdr_t *)(trace);
152 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
154 duration = hdr->duration;
155 data_len = hdr->data_len;
157 if (tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) {
158 PrintAndLogEx(DEBUG, "trace pos offset %"PRIu64 " larger than reported tracelen %u", tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr), traceLen);
159 return traceLen;
162 // adjust for different time scales
163 if (protocol == ICLASS || protocol == ISO_15693) {
164 duration *= 32;
167 uint8_t *frame = hdr->frame;
168 uint8_t *parityBytes = hdr->frame + data_len;
170 tracepos += TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr);
172 if (protocol == TOPAZ && !hdr->isResponse) {
173 // topaz reader commands come in 1 or 9 separate frames with 7 or 8 Bits each.
174 // merge them:
175 if (merge_topaz_reader_frames(hdr->timestamp, &duration, &tracepos, traceLen, trace, frame, topaz_reader_command, &data_len)) {
176 frame = topaz_reader_command;
180 //Check the CRC status
181 uint8_t crcStatus = 2;
183 if (data_len > 2) {
184 switch (protocol) {
185 case ICLASS:
186 crcStatus = iclass_CRC_check(hdr->isResponse, frame, data_len);
187 break;
188 case ISO_14443B:
189 case TOPAZ:
190 crcStatus = iso14443B_CRC_check(frame, data_len);
191 break;
192 case FELICA:
193 crcStatus = !felica_CRC_check(frame + 2, data_len - 4);
194 break;
195 case PROTO_MIFARE:
196 crcStatus = mifare_CRC_check(hdr->isResponse, frame, data_len);
197 break;
198 case ISO_14443A:
199 case MFDES:
200 case LTO:
201 crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len);
202 break;
203 case ISO_7816_4:
204 crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len) == 1 ? 3 : 0;
205 crcStatus = iso14443B_CRC_check(frame, data_len) == 1 ? 4 : crcStatus;
206 break;
207 case THINFILM:
208 frame[data_len - 1] ^= frame[data_len - 2];
209 frame[data_len - 2] ^= frame[data_len - 1];
210 frame[data_len - 1] ^= frame[data_len - 2];
211 crcStatus = iso14443A_CRC_check(true, frame, data_len);
212 frame[data_len - 1] ^= frame[data_len - 2];
213 frame[data_len - 2] ^= frame[data_len - 1];
214 frame[data_len - 1] ^= frame[data_len - 2];
215 break;
216 case ISO_15693:
217 crcStatus = iso15693_CRC_check(frame, data_len);
218 break;
219 case PROTO_CRYPTORF:
220 case PROTO_HITAG1:
221 case PROTO_HITAG2:
222 case PROTO_HITAGS:
223 default:
224 break;
227 //0 CRC-command, CRC not ok
228 //1 CRC-command, CRC ok
229 //2 Not crc-command
231 //--- Draw the data column
232 char line[18][120] = {{0}};
234 if (data_len == 0) {
235 if (protocol == ICLASS && duration == 2048) {
236 sprintf(line[0], "<SOF>");
237 } else if (protocol == ISO_15693 && duration == 512) {
238 sprintf(line[0], "<EOF>");
239 } else {
240 sprintf(line[0], "<empty trace - possible error>");
244 for (int j = 0; j < data_len && j / 18 < 18; j++) {
245 uint8_t parityBits = parityBytes[j >> 3];
246 if (protocol != LEGIC
247 && protocol != ISO_14443B
248 && protocol != ISO_15693
249 && protocol != ICLASS
250 && protocol != ISO_7816_4
251 && protocol != PROTO_HITAG1
252 && protocol != PROTO_HITAG2
253 && protocol != PROTO_HITAGS
254 && protocol != THINFILM
255 && protocol != FELICA
256 && protocol != LTO
257 && protocol != PROTO_CRYPTORF
258 && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE)
259 && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) {
261 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
262 } else if (protocol == ICLASS && hdr->isResponse == false) {
263 uint8_t parity = 0;
264 for (int i = 0; i < 6; i++) {
265 parity ^= ((frame[0] >> i) & 1);
267 if (parity == ((frame[0] >> 7) & 1)) {
268 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x ", frame[j]);
269 } else {
270 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
273 } else {
274 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x ", frame[j]);
279 if (markCRCBytes) {
280 //CRC-command
281 if (crcStatus == 0 || crcStatus == 1) {
282 char *pos1 = line[(data_len - 2) / 18] + (((data_len - 2) % 18) * 4) - 1;
283 (*pos1) = '[';
284 char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4) - 1;
285 sprintf(pos2, "%c", ']');
289 // Draw the CRC column
290 const char *crcstrings[] = { "!crc", " ok ", " ", "A ok", "B ok" };
291 const char *crc = crcstrings[crcStatus];
293 // mark short bytes (less than 8 Bit + Parity)
294 if (protocol == ISO_14443A ||
295 protocol == PROTO_MIFARE ||
296 protocol == THINFILM) {
298 // approximated with 128 * (9 * data_len);
299 uint16_t bitime = 1056 + 32;
301 if (duration < bitime) {
303 uint8_t m = 7;
304 while (m > 0) {
305 bitime -= 128;
306 if (duration > bitime) {
307 break;
309 m--;
312 if (data_len) {
313 line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 2] = '(';
314 line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 3] = m + 0x30;
315 line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 4] = ')';
321 uint32_t previous_end_of_transmission_timestamp = 0;
322 if (prev_eot) {
323 if (*prev_eot) {
324 previous_end_of_transmission_timestamp = *prev_eot;
325 } else {
326 previous_end_of_transmission_timestamp = hdr->timestamp;
330 end_of_transmission_timestamp = hdr->timestamp + duration;
332 if (prev_eot)
333 *prev_eot = end_of_transmission_timestamp;
335 // Always annotate these protocols both reader/tag messages
336 switch (protocol) {
337 case PROTO_MIFARE:
338 annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, TRACELOG_PARITY_LEN(hdr), hdr->isResponse);
339 break;
340 case PROTO_HITAG1:
341 annotateHitag1(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
342 break;
343 case PROTO_HITAG2:
344 annotateHitag2(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
345 break;
346 case PROTO_HITAGS:
347 annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
348 break;
349 case ICLASS:
350 annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
351 break;
352 default:
353 break;
356 if (hdr->isResponse == false) {
358 switch (protocol) {
359 case LEGIC:
360 annotateLegic(explanation, sizeof(explanation), frame, data_len);
361 break;
362 case ISO_14443A:
363 annotateIso14443a(explanation, sizeof(explanation), frame, data_len);
364 break;
365 case MFDES:
366 annotateMfDesfire(explanation, sizeof(explanation), frame, data_len);
367 break;
368 case ISO_14443B:
369 annotateIso14443b(explanation, sizeof(explanation), frame, data_len);
370 break;
371 case TOPAZ:
372 annotateTopaz(explanation, sizeof(explanation), frame, data_len);
373 break;
374 case ISO_7816_4:
375 annotateIso14443a(explanation, sizeof(explanation), frame, data_len);
376 annotateIso7816(explanation, sizeof(explanation), frame, data_len);
377 break;
378 case ISO_15693:
379 annotateIso15693(explanation, sizeof(explanation), frame, data_len);
380 break;
381 case FELICA:
382 annotateFelica(explanation, sizeof(explanation), frame, data_len);
383 break;
384 case LTO:
385 annotateLTO(explanation, sizeof(explanation), frame, data_len);
386 break;
387 case PROTO_CRYPTORF:
388 annotateCryptoRF(explanation, sizeof(explanation), frame, data_len);
389 break;
390 default:
391 break;
395 int num_lines = MIN((data_len - 1) / 18 + 1, 18);
396 for (int j = 0; j < num_lines ; j++) {
397 if (j == 0) {
399 uint32_t time1 = hdr->timestamp - first_hdr->timestamp;
400 uint32_t time2 = end_of_transmission_timestamp - first_hdr->timestamp;
401 if (prev_eot) {
402 time1 = hdr->timestamp - previous_end_of_transmission_timestamp;
403 time2 = duration;
406 if (hdr->isResponse) {
407 // tag row
408 if (use_us) {
409 PrintAndLogEx(NORMAL, " %10.1f | %10.1f | Tag |%-72s | %s| %s",
410 (float)time1 / 13.56,
411 (float)time2 / 13.56,
412 line[j],
413 (j == num_lines - 1) ? crc : " ",
414 (j == num_lines - 1) ? explanation : ""
416 } else {
417 PrintAndLogEx(NORMAL, " %10u | %10u | Tag |%-72s | %s| %s",
418 (hdr->timestamp - first_hdr->timestamp),
419 (end_of_transmission_timestamp - first_hdr->timestamp),
420 line[j],
421 (j == num_lines - 1) ? crc : " ",
422 (j == num_lines - 1) ? explanation : ""
425 } else {
426 // reader row
427 if (use_us) {
428 PrintAndLogEx(NORMAL,
429 _YELLOW_(" %10.1f") " | " _YELLOW_("%10.1f") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
430 (float)time1 / 13.56,
431 (float)time2 / 13.56,
432 line[j],
433 (j == num_lines - 1) ? crc : " ",
434 (j == num_lines - 1) ? explanation : ""
436 } else {
437 PrintAndLogEx(NORMAL,
438 _YELLOW_(" %10u") " | " _YELLOW_("%10u") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
439 (hdr->timestamp - first_hdr->timestamp),
440 (end_of_transmission_timestamp - first_hdr->timestamp),
441 line[j],
442 (j == num_lines - 1) ? crc : " ",
443 (j == num_lines - 1) ? explanation : ""
449 } else {
450 if (hdr->isResponse) {
451 PrintAndLogEx(NORMAL, " | | |%-72s | %s| %s",
452 line[j],
453 (j == num_lines - 1) ? crc : " ",
454 (j == num_lines - 1) ? explanation : ""
456 } else {
457 PrintAndLogEx(NORMAL, " | | |" _YELLOW_("%-72s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
458 line[j],
459 (j == num_lines - 1) ? crc : " ",
460 (j == num_lines - 1) ? explanation : ""
466 if (protocol == PROTO_MIFARE) {
467 if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen, mfDicKeys, mfDicKeysCount)) {
468 memset(explanation, 0x00, sizeof(explanation));
469 if (hdr->isResponse == false) {
470 annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen);
472 uint8_t crcc = iso14443A_CRC_check(hdr->isResponse, mfData, mfDataLen);
473 PrintAndLogEx(NORMAL, " | | * |%-72s | %-4s| %s",
474 sprint_hex_inrow_spaces(mfData, mfDataLen, 2),
475 (crcc == 0 ? "!crc" : (crcc == 1 ? " ok " : " ")),
476 explanation);
480 if (is_last_record(tracepos, traceLen)) {
481 return traceLen;
484 if (showWaitCycles && hdr->isResponse == false && next_record_is_response(tracepos, trace)) {
486 tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
488 PrintAndLogEx(NORMAL, " %10u | %10u | %s |fdt (Frame Delay Time): " _YELLOW_("%d"),
489 (end_of_transmission_timestamp - first_hdr->timestamp),
490 (next_hdr->timestamp - first_hdr->timestamp),
491 " ",
492 (next_hdr->timestamp - end_of_transmission_timestamp));
495 return tracepos;
498 static int download_trace(void) {
500 if (IfPm3Present() == false) {
501 PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter '1' for working from Tracebuffer");
502 return PM3_EINVARG;
505 // reserve some space.
506 if (g_trace)
507 free(g_trace);
509 g_traceLen = 0;
511 g_trace = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
512 if (g_trace == NULL) {
513 PrintAndLogEx(FAILED, "Cannot allocate memory for trace");
514 return PM3_EMALLOC;
517 PrintAndLogEx(INFO, "downloading tracelog data from device");
519 // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE
520 PacketResponseNG response;
521 if (!GetFromDevice(BIG_BUF, g_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &response, 4000, true)) {
522 PrintAndLogEx(WARNING, "timeout while waiting for reply.");
523 free(g_trace);
524 return PM3_ETIMEOUT;
527 g_traceLen = response.oldarg[2];
529 // if tracelog buffer was larger and we need to download more.
530 if (g_traceLen > PM3_CMD_DATA_SIZE) {
532 free(g_trace);
533 g_trace = calloc(g_traceLen, sizeof(uint8_t));
534 if (g_trace == NULL) {
535 PrintAndLogEx(FAILED, "Cannot allocate memory for trace");
536 return PM3_EMALLOC;
539 if (!GetFromDevice(BIG_BUF, g_trace, g_traceLen, 0, NULL, 0, NULL, 2500, false)) {
540 PrintAndLogEx(WARNING, "command execution time out");
541 free(g_trace);
542 return PM3_ETIMEOUT;
545 return PM3_SUCCESS;
548 // sanity check. Don't use proxmark if it is offline and you didn't specify useTraceBuffer
550 static int SanityOfflineCheck( bool useTraceBuffer ){
551 if ( !useTraceBuffer && offline) {
552 PrintAndLogEx(NORMAL, "Your proxmark3 device is offline. Specify [1] to use TraceBuffer data instead");
553 return 0;
555 return 1;
559 static int CmdTraceLoad(const char *Cmd) {
561 CLIParserContext *ctx;
562 CLIParserInit(&ctx, "trace load",
563 "Load protocol data from binary file to trace buffer\n"
564 "File extension is <.trace>",
565 "trace load -f mytracefile -> w/o file extension"
568 void *argtable[] = {
569 arg_param_begin,
570 arg_strx0("f", "file", "<filename>", "trace file to load"),
571 arg_param_end
573 CLIExecWithReturn(ctx, Cmd, argtable, false);
575 int fnlen = 0;
576 char filename[FILE_PATH_SIZE] = {0};
577 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
578 CLIParserFree(ctx);
580 if (g_trace)
581 free(g_trace);
583 size_t len = 0;
584 if (loadFile_safe(filename, ".trace", (void **)&g_trace, &len) != PM3_SUCCESS) {
585 PrintAndLogEx(FAILED, "Could not open file " _YELLOW_("%s"), filename);
586 return PM3_EIO;
589 g_traceLen = (long)len;
591 PrintAndLogEx(SUCCESS, "Recorded Activity (TraceLen = " _YELLOW_("%lu") " bytes)", g_traceLen);
592 return PM3_SUCCESS;
595 static int CmdTraceSave(const char *Cmd) {
597 CLIParserContext *ctx;
598 CLIParserInit(&ctx, "trace save",
599 "Save protocol data from trace buffer to binary file\n"
600 "File extension is <.trace>",
601 "trace save -f mytracefile -> w/o file extension"
604 void *argtable[] = {
605 arg_param_begin,
606 arg_strx0("f", "file", "<filename>", "trace file to save"),
607 arg_param_end
609 CLIExecWithReturn(ctx, Cmd, argtable, false);
611 int fnlen = 0;
612 char filename[FILE_PATH_SIZE] = {0};
613 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
614 CLIParserFree(ctx);
616 if (g_traceLen == 0) {
617 download_trace();
618 if (g_traceLen == 0) {
619 PrintAndLogEx(WARNING, "trace is empty, nothing to save");
620 return PM3_SUCCESS;
624 saveFile(filename, ".trace", g_trace, g_traceLen);
625 return PM3_SUCCESS;
628 int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol) {
629 CLIParserContext *ctx;
630 char desc[500] = {0};
631 snprintf(desc, sizeof(desc) - 1,
632 "Alias of `trace list -t %s` with selected protocol data to annotate trace buffer\n"
633 "You can load a trace from file (see `trace load -h`) or it be downloaded from device by default\n"
634 "It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
635 protocol);
636 char example[200] = {0};
637 snprintf(example, sizeof(example) - 1,
638 "%s list -f -> show frame delay times\n"
639 "%s list -1 -> use trace buffer ",
640 alias, alias);
641 char fullalias[100] = {0};
642 snprintf(fullalias, sizeof(fullalias) - 1, "%s list", alias);
643 CLIParserInit(&ctx, fullalias, desc, example);
645 void *argtable[] = {
646 arg_param_begin,
647 arg_lit0("1", "buffer", "use data from trace buffer"),
648 arg_lit0("f", NULL, "show frame delay times"),
649 arg_lit0("c", NULL, "mark CRC bytes"),
650 arg_lit0("r", NULL, "show relative times (gap and duration)"),
651 arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"),
652 arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n"
653 " or to import into Wireshark using encapsulation type \"ISO 14443\""),
654 arg_strx0(NULL, "dict", "<file>", "use dictionary keys file"),
655 arg_param_end
657 CLIExecWithReturn(ctx, Cmd, argtable, true);
658 CLIParserFree(ctx);
660 char args[128] = {0};
661 snprintf(args, sizeof(args), "-t %s ", protocol);
662 strncat(args, Cmd, sizeof(args) - strlen(args) - 1);
663 return CmdTraceList(args);
666 int CmdTraceList(const char *Cmd) {
667 CLIParserContext *ctx;
668 CLIParserInit(&ctx, "trace list",
669 "Annotate trace buffer with selected protocol data\n"
670 "You can load a trace from file (see `trace load -h`) or it be downloaded from device by default\n",
671 "trace list -t raw -> just show raw data without annotations\n"
672 "trace list -t 14a -> interpret as " _YELLOW_("ISO14443-A") " communications\n"
673 "trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") " communications\n"
674 "trace list -t topaz -> interpret as " _YELLOW_("Topaz") " communications\n"
675 "trace list -t mf -> interpret as " _YELLOW_("MIFARE Classic") " communications and decrypt crypto1 stream\n"
676 "trace list -t des -> interpret as " _YELLOW_("MIFARE DESFire") " communications\n"
677 "trace list -t 14b -> interpret as " _YELLOW_("ISO14443-B") " communications\n"
678 "trace list -t 7816 -> interpret as " _YELLOW_("ISO7816-4") " communications\n"
679 "trace list -t 15 -> interpret as " _YELLOW_("ISO15693") " communications\n"
680 "trace list -t iclass -> interpret as " _YELLOW_("iCLASS") " communications\n"
681 "trace list -t legic -> interpret as " _YELLOW_("LEGIC") " communications\n"
682 "trace list -t felica -> interpret as " _YELLOW_("ISO18092 / FeliCa") " communications\n"
683 "trace list -t hitag1 -> interpret as " _YELLOW_("Hitag1") " communications\n"
684 "trace list -t hitag2 -> interpret as " _YELLOW_("Hitag2") " communications\n"
685 "trace list -t hitags -> interpret as " _YELLOW_("HitagS") " communications\n"
686 "trace list -t lto -> interpret as " _YELLOW_("LTO-CM") " communications\n"
687 "trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") " communitcations\n"
688 "trace list -t mf --dict <mfc_default_keys> -> use dictionary keys file\n"
689 "trace list -t 14a -f -> show frame delay times\n"
690 "trace list -t 14a -1 -> use trace buffer "
693 void *argtable[] = {
694 arg_param_begin,
695 arg_lit0("1", "buffer", "use data from trace buffer"),
696 arg_lit0("f", NULL, "show frame delay times"),
697 arg_lit0("c", NULL, "mark CRC bytes"),
698 arg_lit0("r", NULL, "show relative times (gap and duration)"),
699 arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"),
700 arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n"
701 " or to import into Wireshark using encapsulation type \"ISO 14443\""),
702 arg_strx0("t", "type", NULL, "protocol to annotate the trace"),
703 arg_strx0(NULL, "dict", "<file>", "use dictionary keys file"),
704 arg_param_end
706 CLIExecWithReturn(ctx, Cmd, argtable, false);
708 bool use_buffer = arg_get_lit(ctx, 1);
709 bool show_wait_cycles = arg_get_lit(ctx, 2);
710 bool mark_crc = arg_get_lit(ctx, 3);
711 bool use_relative = arg_get_lit(ctx, 4);
712 bool use_us = arg_get_lit(ctx, 5);
713 bool show_hex = arg_get_lit(ctx, 6);
715 int tlen = 0;
716 char type[10] = {0};
717 CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)type, sizeof(type), &tlen);
718 str_lower(type);
720 int diclen = 0;
721 char dictionary[FILE_PATH_SIZE + 2] = {0};
722 if (CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)dictionary, FILE_PATH_SIZE, &diclen)) {
723 PrintAndLogEx(FAILED, "Dictionary file name too long or invalid.");
724 diclen = 0;
727 CLIParserFree(ctx);
729 clearCommandBuffer();
731 // no crc, no annotations
732 uint8_t protocol = -1;
734 // validate type of output
735 if (strcmp(type, "iclass") == 0) protocol = ICLASS;
736 else if (strcmp(type, "14a") == 0) protocol = ISO_14443A;
737 else if (strcmp(type, "14b") == 0) protocol = ISO_14443B;
738 else if (strcmp(type, "topaz") == 0) protocol = TOPAZ;
739 else if (strcmp(type, "7816") == 0) protocol = ISO_7816_4;
740 else if (strcmp(type, "des") == 0) protocol = MFDES;
741 else if (strcmp(type, "legic") == 0) protocol = LEGIC;
742 else if (strcmp(type, "15") == 0) protocol = ISO_15693;
743 else if (strcmp(type, "felica") == 0) protocol = FELICA;
744 else if (strcmp(type, "mf") == 0) protocol = PROTO_MIFARE;
745 else if (strcmp(type, "hitag1") == 0) protocol = PROTO_HITAG1;
746 else if (strcmp(type, "hitag2") == 0) protocol = PROTO_HITAG2;
747 else if (strcmp(type, "hitags") == 0) protocol = PROTO_HITAGS;
748 else if (strcmp(type, "thinfilm") == 0) protocol = THINFILM;
749 else if (strcmp(type, "lto") == 0) protocol = LTO;
750 else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF;
751 else if (strcmp(type, "raw") == 0) protocol = -1;
753 if (use_buffer == false) {
754 download_trace();
755 } else if (g_traceLen == 0) {
756 PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace, consider using 'trace load' or removing parameter '1'");
757 return PM3_EINVARG;
760 PrintAndLogEx(SUCCESS, "Recorded activity (trace len = " _YELLOW_("%lu") " bytes)", g_traceLen);
761 if (g_traceLen == 0) {
762 return PM3_SUCCESS;
765 uint16_t tracepos = 0;
768 if (protocol == FELICA) {
769 printFelica(g_traceLen, g_trace);
770 } */
772 if (show_hex) {
773 while (tracepos < g_traceLen) {
774 tracepos = printHexLine(tracepos, g_traceLen, g_trace, protocol);
776 } else {
778 if (use_relative) {
779 PrintAndLogEx(INFO, _YELLOW_("gap") " = time between transfers. " _YELLOW_("duration") " = duration of data transfer. " _YELLOW_("src") " = source of transfer");
780 } else {
781 PrintAndLogEx(INFO, _YELLOW_("start") " = start of start frame " _YELLOW_("end") " = end of frame. " _YELLOW_("src") " = source of transfer");
784 if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == TOPAZ || protocol == LTO) {
785 if (use_us)
786 PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in microseconds");
787 else
788 PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in carrier periods (1/13.56MHz)");
791 if (protocol == THINFILM) {
792 if (use_us)
793 PrintAndLogEx(INFO, _YELLOW_("Thinfilm") " - all times are in microseconds");
794 else
795 PrintAndLogEx(INFO, _YELLOW_("Thinfilm") " - all times are in carrier periods (1/13.56MHz)");
798 if (protocol == ICLASS || protocol == ISO_15693) {
799 if (use_us)
800 PrintAndLogEx(INFO, _YELLOW_("ISO15693 / iCLASS") " - all times are in microseconds");
801 else
802 PrintAndLogEx(INFO, _YELLOW_("ISO15693 / iCLASS") " - all times are in carrier periods (1/13.56MHz)");
805 if (protocol == LEGIC)
806 PrintAndLogEx(INFO, _YELLOW_("LEGIC") " - Reader Mode: Timings are in ticks (1us == 1.5ticks)\n"
807 " Tag Mode: Timings are in sub carrier periods (1/212 kHz == 4.7us)");
809 if (protocol == ISO_14443B || protocol == PROTO_CRYPTORF) {
810 if (use_us)
811 PrintAndLogEx(INFO, _YELLOW_("ISO14443B") " - all times are in microseconds");
812 else
813 PrintAndLogEx(INFO, _YELLOW_("ISO14443B") " - all times are in carrier periods (1/13.56MHz)");
816 if (protocol == ISO_7816_4)
817 PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings N/A");
819 if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS)
820 PrintAndLogEx(INFO, _YELLOW_("Hitag1 / Hitag2 / HitagS") " - Timings in ETU (8us)");
822 if (protocol == FELICA) {
823 if (use_us)
824 PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in microseconds");
825 else
826 PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in carrier periods (1/13.56MHz)");
830 const uint64_t *dicKeys = NULL;
831 uint32_t dicKeysCount = 0;
832 bool dictionaryLoad = false;
834 if (protocol == PROTO_MIFARE) {
835 if (diclen > 0) {
836 uint8_t *keyBlock = NULL;
837 int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, 6, &dicKeysCount);
838 if (res != PM3_SUCCESS || dicKeysCount == 0 || keyBlock == NULL) {
839 PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)");
840 } else {
841 dicKeys = calloc(dicKeysCount, sizeof(uint64_t));
842 for (int i = 0; i < dicKeysCount; i++) {
843 uint64_t key = bytes_to_num(keyBlock + i * 6, 6);
844 memcpy((uint8_t *) &dicKeys[i], &key, sizeof(uint64_t));
846 dictionaryLoad = true;
848 if (keyBlock != NULL) {
849 free(keyBlock);
852 if (dicKeys == NULL) {
853 dicKeys = g_mifare_default_keys;
854 dicKeysCount = ARRAYLEN(g_mifare_default_keys);
858 PrintAndLogEx(NORMAL, "");
859 if (use_relative) {
860 PrintAndLogEx(NORMAL, " Gap | Duration | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation");
861 } else {
862 PrintAndLogEx(NORMAL, " Start | End | Src | Data (! denotes parity error) | CRC | Annotation");
864 PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------");
866 // clean authentication data used with the mifare classic decrypt fct
867 if (protocol == ISO_14443A || protocol == PROTO_MIFARE)
868 ClearAuthData();
870 uint32_t previous_EOT = 0;
871 uint32_t *prev_EOT = NULL;
872 if (use_relative) {
873 prev_EOT = &previous_EOT;
876 while (tracepos < g_traceLen) {
877 tracepos = printTraceLine(tracepos, g_traceLen, g_trace, protocol, show_wait_cycles, mark_crc, prev_EOT, use_us, dicKeys, dicKeysCount);
879 if (kbd_enter_pressed())
880 break;
883 if (dictionaryLoad)
884 free((void *) dicKeys);
887 if (show_hex)
888 PrintAndLogEx(HINT, "syntax to use: " _YELLOW_("`text2pcap -t \"%%S.\" -l 264 -n <input-text-file> <output-pcapng-file>`"));
890 return PM3_SUCCESS;
893 static command_t CommandTable[] = {
894 {"help", CmdHelp, AlwaysAvailable, "This help"},
895 {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"},
896 {"load", CmdTraceLoad, AlwaysAvailable, "Load trace from file"},
897 {"save", CmdTraceSave, AlwaysAvailable, "Save trace buffer to file"},
898 {NULL, NULL, NULL, NULL}
901 static int CmdHelp(const char *Cmd) {
902 (void)Cmd; // Cmd is not used so far
903 CmdsHelp(CommandTable);
904 return PM3_SUCCESS;
907 int CmdTrace(const char *Cmd) {
908 clearCommandBuffer();
909 return CmdsParse(CommandTable, Cmd);