style
[RRG-proxmark3.git] / client / src / cmdtrace.c
blob8b217ec226ecdc76158f62401d0eeadc150f6618
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // Trace commands
17 //-----------------------------------------------------------------------------
18 #include "cmdtrace.h"
20 #include <ctype.h>
22 #include "cmdparser.h" // command_t
23 #include "protocols.h"
24 #include "parity.h" // oddparity
25 #include "cmdhflist.h" // annotations
26 #include "commonutil.h" // ARRAYLEN
27 #include "mifare/mifaredefault.h" // mifare default key array
28 #include "comms.h" // for sending cmds to device. GetFromBigBuf
29 #include "fileutils.h" // for saveFile
30 #include "cmdlfhitag.h" // annotate hitag
31 #include "pm3_cmd.h" // tracelog_hdr_t
32 #include "cliparser.h" // args..
34 static int CmdHelp(const char *Cmd);
36 // trace pointer
37 static uint8_t *gs_trace;
38 static uint16_t gs_traceLen = 0;
40 static bool is_last_record(uint16_t tracepos, uint16_t traceLen) {
41 return ((tracepos + TRACELOG_HDR_LEN) >= traceLen);
44 static bool next_record_is_response(uint16_t tracepos, uint8_t *trace) {
45 const tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
46 return (hdr->isResponse);
49 static bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen,
50 uint8_t *trace, const uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) {
52 #define MAX_TOPAZ_READER_CMD_LEN 16
54 uint32_t last_timestamp = timestamp + *duration;
56 if ((*data_len != 1) || (frame[0] == TOPAZ_WUPA) || (frame[0] == TOPAZ_REQA)) return false;
58 memcpy(topaz_reader_command, frame, *data_len);
60 while (!is_last_record(*tracepos, traceLen) && !next_record_is_response(*tracepos, trace)) {
62 const tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + *tracepos);
64 *tracepos += TRACELOG_HDR_LEN + hdr->data_len;
66 if ((hdr->data_len == 1) && (*data_len + hdr->data_len <= MAX_TOPAZ_READER_CMD_LEN)) {
67 memcpy(topaz_reader_command + *data_len, hdr->frame, hdr->data_len);
68 *data_len += hdr->data_len;
69 last_timestamp = hdr->timestamp + hdr->duration;
70 } else {
71 // rewind and exit
72 *tracepos = *tracepos - hdr->data_len - TRACELOG_HDR_LEN;
73 break;
75 *tracepos += TRACELOG_PARITY_LEN(hdr);
78 *duration = last_timestamp - timestamp;
80 return true;
82 static uint8_t calc_pos(const uint8_t *d) {
83 // PCB [CID] [NAD] [INF] CRC CRC
84 uint8_t pos = 1;
85 if ((d[0] & 0x08) == 0x08) // cid byte following
86 pos++;
88 if ((d[0] & 0x04) == 0x04) // nad byte following
89 pos++;
91 return pos;
94 // Copy an existing buffer into client trace buffer
95 // I think this is cleaner than further globalizing gs_trace, and may lend itself to more modularity later?
96 bool ImportTraceBuffer(const uint8_t *trace_src, uint16_t trace_len) {
97 if (trace_len == 0 || trace_src == NULL) return (false);
98 if (gs_trace) {
99 free(gs_trace);
100 gs_traceLen = 0;
102 gs_trace = calloc(trace_len, sizeof(uint8_t));
103 if (gs_trace == NULL) {
104 return (false);
106 memcpy(gs_trace, trace_src, trace_len);
107 gs_traceLen = trace_len;
108 return (true);
111 static uint8_t extract_uid[10] = {0};
112 static uint8_t extract_uidlen = 0;
113 static uint8_t extract_epurse[8] = {0};
115 #define SKIP_TO_NEXT(a) (TRACELOG_HDR_LEN + (a)->data_len + TRACELOG_PARITY_LEN((a)))
117 static uint16_t extractChall_ev2(uint16_t tracepos, uint8_t *trace, uint8_t cmdpos, uint8_t long_jmp) {
118 tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
119 if (next_hdr->data_len != 21) {
120 return 0;
123 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
125 PrintAndLogEx(INFO, "1499999999 %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 16));
127 next_hdr = (tracelog_hdr_t *)(trace + tracepos);
128 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
130 if (next_hdr->frame[cmdpos] == MFDES_ADDITIONAL_FRAME) {
131 PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + cmdpos + long_jmp, 32));
132 } else {
133 PrintAndLogEx(NORMAL, "");
135 return tracepos;
138 static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) {
140 // sanity check
141 if (is_last_record(tracepos, traceLen)) {
142 return traceLen;
145 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
146 uint16_t data_len = hdr->data_len;
147 uint8_t *frame = hdr->frame;
149 // sanity check tracking position is less then available trace size
150 if (tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) {
151 PrintAndLogEx(DEBUG, "trace pos offset %"PRIu64 " larger than reported tracelen %u",
152 tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr),
153 traceLen
155 return traceLen;
158 // set trace position
159 tracepos += SKIP_TO_NEXT(hdr);
161 // sanity check data frame length
162 if (data_len == 0) {
163 return tracepos;
166 // extract MFC
167 switch (frame[0]) {
168 case MIFARE_AUTH_KEYA: {
169 if (data_len > 3) {
171 break;
173 case MIFARE_AUTH_KEYB: {
174 if (data_len > 3) {
176 break;
180 // extract MFU-C
181 switch (frame[0]) {
182 case MIFARE_ULC_AUTH_1: {
183 if (data_len != 4) {
184 break;
187 // time to skip to next
188 tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
189 tracepos += SKIP_TO_NEXT(next_hdr);
190 if (next_hdr->data_len != 11) {
191 break;
194 if (next_hdr->frame[0] != MIFARE_ULC_AUTH_2) {
195 break;
198 PrintAndLogEx(INFO, "MFU-C AUTH");
199 PrintAndLogEx(INFO, "3DES %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 8));
201 next_hdr = (tracelog_hdr_t *)(trace + tracepos);
202 tracepos += SKIP_TO_NEXT(next_hdr);
204 if (next_hdr->frame[0] == MIFARE_ULC_AUTH_2 && next_hdr->data_len == 19) {
205 PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + 1, 16));
208 return tracepos;
212 // extract iCLASS
213 // --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b
215 if (hdr->isResponse == false) {
217 uint8_t c = frame[0] & 0x0F;
218 switch (c) {
219 case ICLASS_CMD_SELECT: {
221 const tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
222 tracepos += SKIP_TO_NEXT(next_hdr);
223 if (next_hdr->data_len != 10) {
224 break;
226 memcpy(extract_uid, next_hdr->frame, 8);
227 extract_uidlen = 8;
228 break;
230 case ICLASS_CMD_READCHECK: {
232 // get epurse
233 if (frame[1] == 2 && data_len == 2) {
234 const tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
235 tracepos += SKIP_TO_NEXT(next_hdr);
236 if (next_hdr->data_len < 8) {
237 break;
239 memcpy(extract_epurse, next_hdr->frame, 8);
241 break;
243 case ICLASS_CMD_CHECK: {
244 // get macs
245 if (data_len == 9) {
246 if (extract_uidlen == 8) {
247 PrintAndLogEx(INFO, "hf iclass lookup --csn %s " NOLF, sprint_hex_inrow(extract_uid, extract_uidlen));
248 PrintAndLogEx(NORMAL, "--epurse %s " NOLF, sprint_hex_inrow(extract_epurse, 8));
249 PrintAndLogEx(NORMAL, "--macs %s " NOLF, sprint_hex_inrow(frame + 1, 8));
250 PrintAndLogEx(NORMAL, "-f iclass_default_keys.dic");
251 return tracepos;
254 break;
259 // extract UID
260 switch (frame[0]) {
261 case ISO14443A_CMD_ANTICOLL_OR_SELECT: {
262 // 93 20 = Anticollision (usage: 9320 - answer: 4bytes UID+1byte UID-bytes-xor)
263 // 93 50 = Bit oriented anti-collision (usage: 9350+ up to 5bytes, 9350 answer - up to 5bytes UID+BCC)
264 // 93 70 = Select (usage: 9370+5bytes 9370 answer - answer: 1byte SAK)
265 if (frame[1] == 0x70) {
266 if (frame[2] == 0x88) {
267 memcpy(extract_uid, frame + 3, 3);
268 extract_uidlen = 3;
269 } else {
270 memcpy(extract_uid, frame + 2, 4);
271 extract_uidlen = 4;
272 PrintAndLogEx(INFO, "UID... " _YELLOW_("%s"), sprint_hex_inrow(extract_uid, extract_uidlen));
275 break;
277 case ISO14443A_CMD_ANTICOLL_OR_SELECT_2: {
278 // 95 20 = Anticollision of cascade level2
279 // 95 50 = Bit oriented anti-collision level2
280 // 95 70 = Select of cascade level2
281 if (frame[1] == 0x70) {
282 if (frame[2] == 0x88) {
283 memcpy(extract_uid + extract_uidlen, frame + 3, 3);
284 extract_uidlen += 3;
285 } else {
286 memcpy(extract_uid + extract_uidlen, frame + 2, 4);
287 extract_uidlen += 4;
288 PrintAndLogEx(INFO, "UID... " _YELLOW_("%s"), sprint_hex_inrow(extract_uid, extract_uidlen));
291 break;
293 case ISO14443A_CMD_ANTICOLL_OR_SELECT_3: {
294 // 97 20 = Anticollision of cascade level3
295 // 97 50 = Bit oriented anti-collision level3
296 // 97 70 = Select of cascade level3
297 if (frame[1] == 0x70) {
298 memcpy(extract_uid + extract_uidlen, frame + 2, 4);
299 extract_uidlen += 4;
300 PrintAndLogEx(INFO, "UID... " _YELLOW_("%s"), sprint_hex_inrow(extract_uid, extract_uidlen));
302 break;
306 // extract DESFIRE
307 if ((frame[0] & 0xC0) != 0x00) {
308 return tracepos;
311 if (hdr->isResponse) {
312 return tracepos;
315 // PCB [CID] [NAD] [INF] CRC CRC
316 uint8_t pos = calc_pos(frame);
317 uint8_t long_jmp = (data_len > 6) ? 4 : 1;
319 for (uint8_t i = 0; i < 2; i++, pos++) {
321 switch (frame[pos]) {
323 case MFDES_AUTHENTICATE: {
325 // Assume wrapped or unwrapped
326 PrintAndLogEx(INFO, "AUTH NATIVE (keyNo %d)", frame[pos + long_jmp]);
327 if (next_record_is_response(tracepos, trace) == false) {
328 break;
331 tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
332 if (next_hdr->data_len < 7) {
333 break;
335 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
337 PrintAndLogEx(INFO, "DES 1499999999 %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 8));
339 next_hdr = (tracelog_hdr_t *)(trace + tracepos);
340 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
342 if (next_hdr->frame[pos] == MFDES_ADDITIONAL_FRAME) {
343 PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + pos + long_jmp, 16));
344 } else {
345 PrintAndLogEx(NORMAL, "");
347 return tracepos; // AUTHENTICATE_NATIVE
349 case MFDES_AUTHENTICATE_ISO: {
350 // Assume wrapped or unwrapped
351 PrintAndLogEx(INFO, "AUTH ISO (keyNo %d)", frame[pos + long_jmp]);
352 if (next_record_is_response(tracepos, trace) == false) {
353 break;
356 tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
357 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
358 if (next_hdr->data_len < 7) {
359 break;
362 uint8_t tdea = 8;
363 if (next_hdr->data_len > 20) {
364 tdea = 16;
365 PrintAndLogEx(INFO, "3TDEA 1499999999 %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, tdea));
366 } else {
367 PrintAndLogEx(INFO, "2TDEA 1499999999 %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, tdea));
370 next_hdr = (tracelog_hdr_t *)(trace + tracepos);
371 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
373 if (next_hdr->frame[pos] == MFDES_ADDITIONAL_FRAME) {
374 PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + pos + long_jmp, (tdea << 1)));
375 } else {
376 PrintAndLogEx(NORMAL, "");
378 return tracepos; // AUTHENTICATE_STANDARD
380 case MFDES_AUTHENTICATE_AES: {
381 // Assume wrapped or unwrapped
382 PrintAndLogEx(INFO, "AUTH AES (keyNo %d)", frame[pos + long_jmp]);
383 if (next_record_is_response(tracepos, trace)) {
384 break;
387 tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
388 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
389 if (next_hdr->data_len < 7) {
390 break;
393 PrintAndLogEx(INFO, "AES 1499999999 %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 8));
395 next_hdr = (tracelog_hdr_t *)(trace + tracepos);
396 tracepos += TRACELOG_HDR_LEN + next_hdr->data_len + TRACELOG_PARITY_LEN(next_hdr);
398 if (next_hdr->frame[pos] == MFDES_ADDITIONAL_FRAME) {
399 PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + pos + long_jmp, 16));
400 } else {
401 PrintAndLogEx(NORMAL, "");
403 return tracepos;
405 case MFDES_AUTHENTICATE_EV2F: {
406 PrintAndLogEx(INFO, "AUTH EV2 First");
407 uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp);
408 if (tmp == 0)
409 break;
410 else
411 return tmp;
414 case MFDES_AUTHENTICATE_EV2NF: {
415 PrintAndLogEx(INFO, "AUTH EV2 Non First");
416 uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp);
417 if (tmp == 0)
418 break;
419 else
420 return tmp;
425 return tracepos;
428 static uint16_t printHexLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol) {
429 // sanity check
430 if (is_last_record(tracepos, traceLen)) return traceLen;
432 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
434 if (TRACELOG_HDR_LEN + hdr->data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) {
435 return traceLen;
438 //set trace position
439 tracepos += TRACELOG_HDR_LEN + hdr->data_len + TRACELOG_PARITY_LEN(hdr);
441 if (hdr->data_len == 0) {
442 PrintAndLogEx(NORMAL, "<empty trace - possible error>");
443 return tracepos;
446 uint16_t ret;
448 switch (protocol) {
449 case ISO_14443A: {
450 /* https://www.kaiser.cx/pcap-iso14443.html defines a pseudo header:
451 * version (currently 0x00), event (Rdr: 0xfe, Tag: 0xff), length (2 bytes)
452 * to convert to pcap(ng) via text2pcap or to import into Wireshark
453 * we use format timestamp, newline, offset (0x000000), pseudo header, data
454 * `text2pcap -t "%S." -l 264 -n <input-text-file> <output-pcapng-file>`
456 int line_len = (hdr->data_len * 3) + 1;
457 char line[line_len];
458 char *ptr = line;
460 for (int i = 0; i < hdr->data_len ; i++) {
461 ptr += snprintf(ptr, line_len, "%02x ", hdr->frame[i]);
462 line_len -= 3;
463 if (line_len <= 0) {
464 break;
468 char data_len_str[5];
469 char temp_str1[3] = {0};
470 char temp_str2[3] = {0};
472 snprintf(data_len_str, sizeof(data_len_str), "%04x", hdr->data_len);
473 memmove(temp_str1, data_len_str, 2);
474 memmove(temp_str2, data_len_str + 2, 2);
476 PrintAndLogEx(NORMAL, "0.%010u", hdr->timestamp);
477 PrintAndLogEx(NORMAL, "000000 00 %s %s %s %s",
478 (hdr->isResponse ? "ff" : "fe"),
479 temp_str1,
480 temp_str2,
481 line);
482 ret = tracepos;
483 break;
485 default:
486 PrintAndLogEx(NORMAL, "Currently only 14a supported");
487 ret = traceLen;
488 break;
491 return ret;
494 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,
495 const uint64_t *mfDicKeys, uint32_t mfDicKeysCount) {
496 // sanity check
497 if (is_last_record(tracepos, traceLen)) {
498 PrintAndLogEx(DEBUG, "last record triggered. t-pos: %u t-len %u", tracepos, traceLen);
499 return traceLen;
502 uint32_t end_of_transmission_timestamp = 0;
503 uint8_t topaz_reader_command[9];
504 char explanation[60] = {0};
505 tracelog_hdr_t *first_hdr = (tracelog_hdr_t *)(trace);
506 tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos);
508 uint32_t duration = hdr->duration;
509 uint16_t data_len = hdr->data_len;
511 if (tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr) > traceLen) {
512 PrintAndLogEx(DEBUG, "trace pos offset %"PRIu64 " larger than reported tracelen %u",
513 tracepos + TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr),
514 traceLen
516 return traceLen;
519 // adjust for different time scales
520 if (protocol == ICLASS || protocol == ISO_15693) {
521 duration *= 32;
524 uint8_t *frame = hdr->frame;
525 uint8_t *parityBytes = hdr->frame + data_len;
527 tracepos += TRACELOG_HDR_LEN + data_len + TRACELOG_PARITY_LEN(hdr);
529 if (protocol == TOPAZ && !hdr->isResponse) {
530 // topaz reader commands come in 1 or 9 separate frames with 7 or 8 Bits each.
531 // merge them:
532 if (merge_topaz_reader_frames(hdr->timestamp, &duration, &tracepos, traceLen, trace, frame, topaz_reader_command, &data_len)) {
533 frame = topaz_reader_command;
537 //Check the CRC status
538 uint8_t crcStatus = 2;
540 if (data_len > 2) {
541 switch (protocol) {
542 case ICLASS:
543 crcStatus = iclass_CRC_check(hdr->isResponse, frame, data_len);
544 break;
545 case ISO_14443B:
546 case TOPAZ:
547 crcStatus = iso14443B_CRC_check(frame, data_len);
548 break;
549 case FELICA:
550 crcStatus = !felica_CRC_check(frame + 2, data_len - 4);
551 break;
552 case PROTO_MIFARE:
553 case PROTO_MFPLUS:
554 crcStatus = mifare_CRC_check(hdr->isResponse, frame, data_len);
555 break;
556 case ISO_14443A:
557 case MFDES:
558 case LTO:
559 case SEOS:
560 crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len);
561 break;
562 case ISO_7816_4:
563 crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len) == 1 ? 3 : 0;
564 crcStatus = iso14443B_CRC_check(frame, data_len) == 1 ? 4 : crcStatus;
565 break;
566 case THINFILM:
567 frame[data_len - 1] ^= frame[data_len - 2];
568 frame[data_len - 2] ^= frame[data_len - 1];
569 frame[data_len - 1] ^= frame[data_len - 2];
570 crcStatus = iso14443A_CRC_check(true, frame, data_len);
571 frame[data_len - 1] ^= frame[data_len - 2];
572 frame[data_len - 2] ^= frame[data_len - 1];
573 frame[data_len - 1] ^= frame[data_len - 2];
574 break;
575 case ISO_15693:
576 crcStatus = iso15693_CRC_check(frame, data_len);
577 break;
578 case PROTO_HITAG1:
579 case PROTO_HITAGS:
580 crcStatus = hitag1_CRC_check(frame, (data_len * 8) - ((8 - parityBytes[0]) % 8));
581 case PROTO_CRYPTORF:
582 case PROTO_HITAG2:
583 default:
584 break;
587 //0 CRC-command, CRC not ok
588 //1 CRC-command, CRC ok
589 //2 Not crc-command
591 // Draw the data column
592 #define TRACE_MAX_LINES 36
593 // number of hex bytes to be printed per row (16 data + 2 crc)
594 #define TRACE_MAX_HEX_BYTES 18
596 char line[TRACE_MAX_LINES][160] = {{0}};
598 if (data_len == 0) {
599 if (protocol == ICLASS && duration == 2048) {
600 snprintf(line[0], sizeof(line[0]), "<SOF>");
601 } else if (protocol == ISO_15693 && duration == 512) {
602 snprintf(line[0], sizeof(line[0]), "<EOF>");
603 } else {
604 snprintf(line[0], sizeof(line[0]), "<empty trace - possible error>");
608 // uint8_t partialbytebuff = 0;
609 uint8_t offset = 0;
610 for (int j = 0; j < data_len && (j / TRACE_MAX_HEX_BYTES) < TRACE_MAX_HEX_BYTES; j++) {
611 uint8_t parityBits = parityBytes[j >> 3];
612 if (protocol != LEGIC
613 && protocol != ISO_14443B
614 && protocol != ISO_15693
615 && protocol != ICLASS
616 && protocol != ISO_7816_4
617 && protocol != PROTO_HITAG1
618 && protocol != PROTO_HITAG2
619 && protocol != PROTO_HITAGS
620 && protocol != THINFILM
621 && protocol != FELICA
622 && protocol != LTO
623 && protocol != PROTO_CRYPTORF
624 && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS || protocol == SEOS)
625 && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) {
627 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
629 } else if (protocol == ICLASS && hdr->isResponse == false) {
631 uint8_t parity = 0;
632 for (int i = 0; i < 6; i++) {
633 parity ^= ((frame[0] >> i) & 1);
636 if (parity == ((frame[0] >> 7) & 1)) {
637 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x ", frame[j]);
638 } else {
639 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]);
642 } else if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAG2) || (protocol == PROTO_HITAGS))) {
644 if (j == 0) {
646 // handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES
647 // This part prints the number of bits in the trace entry for hitag.
648 uint8_t nbits = parityBytes[0];
650 // only apply this to lesser than one byte
651 if (data_len == 1 && nbits != 0) {
653 snprintf(line[0], 120, "%2u: %02X ", nbits, frame[0] >> (8 - nbits));
655 } else {
656 if (nbits == 0) {
657 snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(data_len * 8), frame[0]);
658 } else {
659 snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(((data_len - 1) * 8) + nbits), frame[0]);
662 offset = 4;
664 } else {
665 snprintf(line[j / 18] + ((j % 18) * 4) + offset, 120, "%02X ", frame[j]);
668 } else {
669 snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02X ", frame[j]);
674 if (markCRCBytes && data_len > 2) {
675 // CRC-command
676 if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAGS))) {
677 // Note that UID REQUEST response has no CRC, but we don't know
678 // if the response we see is a UID
679 char *pos1 = line[(data_len - 1) / 18] + (((data_len - 1) % 18) * 4) + offset - 1;
680 (*pos1) = '[';
681 char *pos2 = line[(data_len) / 18] + (((data_len) % 18) * 4) + offset - 2;
682 (*pos2) = ']';
683 (*(pos2 + 1)) = '\0';
684 } else {
686 if (crcStatus == 0 || crcStatus == 1) {
688 char *pos1 = line[(data_len - 2) / TRACE_MAX_HEX_BYTES];
689 int delta = (data_len - 2) % TRACE_MAX_HEX_BYTES ? 1 : 0;
690 pos1 += (((data_len - 2) % TRACE_MAX_HEX_BYTES) * 4) - delta;
692 (*(pos1 + 6 + delta)) = '\0';
694 char *cb_str = str_dup(pos1 + delta);
696 if (g_session.supports_colors) {
697 if (crcStatus == 0) {
698 snprintf(pos1, 24, AEND " " _RED_("%s"), cb_str);
699 } else {
700 snprintf(pos1, 24, AEND " " _GREEN_("%s"), cb_str);
702 } else {
703 snprintf(pos1, 9, "[%s]", cb_str);
706 // odd case of second crc byte is alone in a new line
707 if (strlen(cb_str) < 5) {
709 free(cb_str);
711 pos1 = line[((data_len - 2) / TRACE_MAX_HEX_BYTES) + 1];
712 cb_str = str_dup(pos1);
714 if (g_session.supports_colors) {
715 if (crcStatus == 0) {
716 snprintf(pos1, 24, _RED_("%s"), cb_str);
717 } else {
718 snprintf(pos1, 24, _GREEN_("%s"), cb_str);
720 } else {
721 snprintf(pos1, 9, "[%s]", cb_str);
725 free(cb_str);
730 // Draw the CRC column
731 const char *crcstrings[] = { _RED_(" !! "), _GREEN_(" ok "), " ", _GREEN_("A ok"), _GREEN_("B ok") };
732 const char *crc = crcstrings[crcStatus];
734 // mark short bytes (less than 8 Bit + Parity)
735 if (protocol == ISO_14443A ||
736 protocol == PROTO_MIFARE ||
737 protocol == PROTO_MFPLUS ||
738 protocol == THINFILM) {
740 // approximated with 128 * (9 * data_len);
741 uint16_t bitime = 1056 + 32;
743 if (duration < bitime) {
745 uint8_t m = 7;
746 while (m > 0) {
747 bitime -= 128;
748 if (duration > bitime) {
749 break;
751 m--;
754 if (data_len) {
755 line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 2] = '(';
756 line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 3] = m + 0x30;
757 line[(data_len - 1) / 16][((data_len - 1) % 16) * 4 + 4] = ')';
763 uint32_t previous_end_of_transmission_timestamp = 0;
764 if (prev_eot) {
765 if (*prev_eot) {
766 previous_end_of_transmission_timestamp = *prev_eot;
767 } else {
768 previous_end_of_transmission_timestamp = hdr->timestamp;
772 end_of_transmission_timestamp = hdr->timestamp + duration;
774 if (prev_eot) {
775 *prev_eot = end_of_transmission_timestamp;
778 // Always annotate these protocols both reader/tag messages
779 switch (protocol) {
780 case ISO_14443A:
781 case ISO_7816_4:
782 annotateIso14443a(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
783 break;
784 case PROTO_MIFARE:
785 case PROTO_MFPLUS:
786 annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, TRACELOG_PARITY_LEN(hdr), hdr->isResponse);
787 break;
788 case PROTO_HITAG1:
789 annotateHitag1(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
790 break;
791 case PROTO_HITAG2:
792 annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse, mfDicKeys, mfDicKeysCount, false);
793 break;
794 case PROTO_HITAGS:
795 annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
796 break;
797 case ICLASS:
798 annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
799 break;
800 default:
801 break;
804 if (hdr->isResponse == false) {
805 switch (protocol) {
806 case LEGIC:
807 annotateLegic(explanation, sizeof(explanation), frame, data_len);
808 break;
809 case MFDES:
810 annotateMfDesfire(explanation, sizeof(explanation), frame, data_len);
811 break;
812 case PROTO_MFPLUS:
813 annotateMfPlus(explanation, sizeof(explanation), frame, data_len);
814 break;
815 case ISO_14443B:
816 annotateIso14443b(explanation, sizeof(explanation), frame, data_len);
817 break;
818 case TOPAZ:
819 annotateTopaz(explanation, sizeof(explanation), frame, data_len);
820 break;
821 case ISO_7816_4:
822 annotateIso7816(explanation, sizeof(explanation), frame, data_len);
823 break;
824 case ISO_15693:
825 annotateIso15693(explanation, sizeof(explanation), frame, data_len);
826 break;
827 case FELICA:
828 annotateFelica(explanation, sizeof(explanation), frame, data_len);
829 break;
830 case LTO:
831 annotateLTO(explanation, sizeof(explanation), frame, data_len);
832 break;
833 case PROTO_CRYPTORF:
834 annotateCryptoRF(explanation, sizeof(explanation), frame, data_len);
835 break;
836 case SEOS:
837 annotateSeos(explanation, sizeof(explanation), frame, data_len);
838 break;
839 default:
840 break;
844 int str_padder = 72;
845 int num_lines = MIN((data_len - 1) / TRACE_MAX_HEX_BYTES + 1, TRACE_MAX_HEX_BYTES);
847 for (int j = 0; j < num_lines ; j++) {
849 bool last_line = (j == num_lines - 1);
850 str_padder = 72;
852 if (j == 0) {
855 uint32_t time1 = hdr->timestamp - first_hdr->timestamp;
856 uint32_t time2 = end_of_transmission_timestamp - first_hdr->timestamp;
857 if (prev_eot) {
858 time1 = hdr->timestamp - previous_end_of_transmission_timestamp;
859 time2 = duration;
862 // ansi codes addes extra chars that needs to be taken in consideration.
863 if (last_line && (memcmp(crc, "\x20\x20\x20\x20", 4) != 0) && g_session.supports_colors && markCRCBytes) {
864 str_padder = 85;
867 if (hdr->isResponse) {
868 // tag row
869 if (use_us) {
870 PrintAndLogEx(NORMAL, " %10.1f | %10.1f | Tag |%-*s | %s| %s",
871 (float)time1 / 13.56,
872 (float)time2 / 13.56,
873 str_padder,
874 line[j],
875 (last_line) ? crc : " ",
876 (last_line) ? explanation : ""
878 } else {
879 PrintAndLogEx(NORMAL, " %10u | %10u | Tag |%-*s | %s| %s",
880 time1,
881 time2,
882 str_padder,
883 line[j],
884 (last_line) ? crc : " ",
885 (last_line) ? explanation : ""
888 } else {
889 // reader row
890 if (use_us) {
891 PrintAndLogEx(NORMAL,
892 _YELLOW_(" %10.1f") " | " _YELLOW_("%10.1f") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
893 (float)time1 / 13.56,
894 (float)time2 / 13.56,
895 str_padder,
896 line[j],
897 (last_line) ? crc : " ",
898 (last_line) ? explanation : ""
900 } else {
901 PrintAndLogEx(NORMAL,
902 _YELLOW_(" %10u") " | " _YELLOW_("%10u") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
903 time1,
904 time2,
905 str_padder,
906 line[j],
907 (last_line) ? crc : " ",
908 (last_line) ? explanation : ""
913 } else {
916 if (last_line && (memcmp(crc, "\x20\x20\x20\x20", 4) != 0) && g_session.supports_colors && markCRCBytes) {
917 str_padder = 85;
918 // odd case of multiline, and last single byte on empty row has been colorised...
919 if (strlen(line[j]) < 14) {
920 str_padder = 81;
924 if (hdr->isResponse) {
925 PrintAndLogEx(NORMAL, " | | |%-*s | %s| %s",
926 str_padder,
927 line[j],
928 last_line ? crc : " ",
929 last_line ? explanation : ""
931 } else {
932 PrintAndLogEx(NORMAL, " | | |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
933 str_padder,
934 line[j],
935 last_line ? crc : " ",
936 last_line ? explanation : ""
943 if (protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) {
944 uint8_t mfData[32] = {0};
945 size_t mfDataLen = 0;
946 if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen, mfDicKeys, mfDicKeysCount)) {
947 memset(explanation, 0x00, sizeof(explanation));
949 if (protocol == PROTO_MFPLUS) {
950 annotateMfPlus(explanation, sizeof(explanation), mfData, mfDataLen);
951 } else {
952 annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen, hdr->isResponse);
954 uint8_t crcc = iso14443A_CRC_check(hdr->isResponse, mfData, mfDataLen);
956 // iceman: colorise crc bytes here will need a refactor of code from above.
957 for (int j = 0; j < mfDataLen; j += TRACE_MAX_HEX_BYTES) {
959 int plen = MIN((mfDataLen - j), TRACE_MAX_HEX_BYTES);
961 if (hdr->isResponse) {
962 PrintAndLogEx(NORMAL, " | | * |%-*s | %-4s| %s",
963 str_padder,
964 sprint_hex_inrow_spaces(mfData + j, plen, 2),
965 (crcc == 0 ? _RED_(" !! ") : (crcc == 1 ? _GREEN_(" ok ") : " ")),
966 explanation);
967 } else {
968 PrintAndLogEx(NORMAL, " | | * |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
969 str_padder,
970 sprint_hex_inrow_spaces(mfData + j, plen, 2),
971 (crcc == 0 ? _RED_(" !! ") : (crcc == 1 ? _GREEN_(" ok ") : " ")),
972 explanation);
978 if (protocol == PROTO_HITAG2) {
980 uint8_t ht2plain[9] = {0};
981 uint8_t n = 0;
982 if (hitag2_get_plain(ht2plain, &n)) {
984 memset(explanation, 0x00, sizeof(explanation));
986 // handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES
987 // This part prints the number of bits in the trace entry for hitag.
988 uint8_t nbits = parityBytes[0];
989 annotateHitag2(explanation, sizeof(explanation), ht2plain, n, nbits, hdr->isResponse, NULL, 0, true);
991 // iceman: colorise crc bytes here will need a refactor of code from above.
992 for (int j = 0; j < n && (j / TRACE_MAX_HEX_BYTES) < TRACE_MAX_HEX_BYTES; j++) {
995 if (j == 0) {
997 // only apply this to lesser than one byte
998 if (n == 1) {
999 snprintf(line[0], 120, "%2u: %02X ", nbits, ht2plain[0] >> (8 - nbits));
1000 } else {
1002 if (nbits == 0) {
1003 snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(n * 8), ht2plain[0]);
1004 } else {
1005 snprintf(line[0], 120, "%2u: %02X ", (uint16_t)(((n - 1) * 8) + nbits), ht2plain[0]);
1008 offset = 4;
1010 } else {
1011 snprintf(line[j / 18] + ((j % 18) * 4) + offset, 120, "%02X ", ht2plain[j]);
1015 num_lines = MIN((n - 1) / TRACE_MAX_HEX_BYTES + 1, TRACE_MAX_HEX_BYTES);
1017 for (int j = 0; j < num_lines ; j++) {
1018 if (hdr->isResponse) {
1019 PrintAndLogEx(NORMAL, " | | * |%-*s | %-4s| %s",
1020 str_padder,
1021 line[j],
1022 " ",
1023 explanation);
1024 } else {
1025 PrintAndLogEx(NORMAL, " | | * |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"),
1026 str_padder,
1027 line[j],
1028 " ",
1029 explanation);
1036 if (is_last_record(tracepos, traceLen)) {
1037 return traceLen;
1040 if (showWaitCycles && hdr->isResponse == false && next_record_is_response(tracepos, trace)) {
1042 const tracelog_hdr_t *next_hdr = (tracelog_hdr_t *)(trace + tracepos);
1044 uint32_t time1 = end_of_transmission_timestamp - first_hdr->timestamp;
1045 uint32_t time2 = next_hdr->timestamp - first_hdr->timestamp;
1046 if (prev_eot) {
1047 time1 = 0;
1048 time2 = next_hdr->timestamp - end_of_transmission_timestamp;
1051 if (use_us) {
1052 PrintAndLogEx(NORMAL, " %10.1f | %10.1f | %s |fdt (Frame Delay Time): " _YELLOW_("%.1f"),
1053 (float)time1 / 13.56,
1054 (float)time2 / 13.56,
1055 " ",
1056 (float)(next_hdr->timestamp - end_of_transmission_timestamp) / 13.56);
1057 } else {
1058 PrintAndLogEx(NORMAL, " %10u | %10u | %s |fdt (Frame Delay Time): " _YELLOW_("%d"),
1059 time1,
1060 time2,
1061 " ",
1062 (next_hdr->timestamp - end_of_transmission_timestamp));
1066 return tracepos;
1069 static int download_trace(void) {
1071 if (IfPm3Present() == false) {
1072 PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter `" _YELLOW_("-1") "` for working from Tracebuffer");
1073 return PM3_EINVARG;
1076 // reserve some space.
1077 if (gs_trace) {
1078 free(gs_trace);
1081 gs_traceLen = 0;
1083 gs_trace = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
1084 if (gs_trace == NULL) {
1085 PrintAndLogEx(FAILED, "Cannot allocate memory for trace");
1086 return PM3_EMALLOC;
1089 PrintAndLogEx(DEBUG, "downloading tracelog data from device");
1091 // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE
1092 PacketResponseNG resp;
1093 if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 4000, true)) {
1094 PrintAndLogEx(WARNING, "timeout while waiting for reply.");
1095 free(gs_trace);
1096 gs_trace = NULL;
1097 return PM3_ETIMEOUT;
1100 gs_traceLen = resp.oldarg[2];
1102 // if tracelog buffer was larger and we need to download more.
1103 if (gs_traceLen > PM3_CMD_DATA_SIZE) {
1105 free(gs_trace);
1106 gs_trace = calloc(gs_traceLen, sizeof(uint8_t));
1107 if (gs_trace == NULL) {
1108 PrintAndLogEx(FAILED, "Cannot allocate memory for trace");
1109 return PM3_EMALLOC;
1112 if (!GetFromDevice(BIG_BUF, gs_trace, gs_traceLen, 0, NULL, 0, NULL, 2500, false)) {
1113 PrintAndLogEx(WARNING, "command execution time out");
1114 free(gs_trace);
1115 gs_trace = NULL;
1116 return PM3_ETIMEOUT;
1119 return PM3_SUCCESS;
1122 // sanity check. Don't use proxmark if it is offline and you didn't specify useTraceBuffer
1124 static int SanityOfflineCheck( bool useTraceBuffer ){
1125 if ( !useTraceBuffer && offline) {
1126 PrintAndLogEx(NORMAL, "Your proxmark3 device is offline. Specify [1] to use TraceBuffer data instead");
1127 return 0;
1129 return 1;
1133 static int CmdTraceExtract(const char *Cmd) {
1134 CLIParserContext *ctx;
1135 CLIParserInit(&ctx, "trace extract",
1136 "Extracts protocol authentication challenges from trace buffer\n",
1137 "trace extract\n"
1138 "trace extract -1\n"
1141 void *argtable[] = {
1142 arg_param_begin,
1143 arg_lit0("1", "buffer", "use data from trace buffer"),
1144 arg_param_end
1146 CLIExecWithReturn(ctx, Cmd, argtable, true);
1147 bool use_buffer = arg_get_lit(ctx, 1);
1148 CLIParserFree(ctx);
1150 clearCommandBuffer();
1152 if (use_buffer == false) {
1153 download_trace();
1154 } else if (gs_traceLen == 0) {
1155 PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace.");
1156 PrintAndLogEx(FAILED, "Consider using `" _YELLOW_("trace load") "` or removing parameter `" _YELLOW_("-1") "`");
1157 return PM3_EINVARG;
1160 PrintAndLogEx(SUCCESS, "Recorded activity ( " _YELLOW_("%u") " bytes )", gs_traceLen);
1161 if (gs_traceLen == 0) {
1162 return PM3_SUCCESS;
1165 uint16_t tracepos = 0;
1167 while (tracepos < gs_traceLen) {
1168 tracepos = extractChallenges(tracepos, gs_traceLen, gs_trace);
1170 if (kbd_enter_pressed()) {
1171 break;
1174 return PM3_SUCCESS;
1177 static int CmdTraceLoad(const char *Cmd) {
1179 CLIParserContext *ctx;
1180 CLIParserInit(&ctx, "trace load",
1181 "Load protocol data from binary file to trace buffer\n"
1182 "File extension is <.trace>",
1183 "trace load -f mytracefile -> w/o file extension"
1186 void *argtable[] = {
1187 arg_param_begin,
1188 arg_str1("f", "file", "<fn>", "Specify trace file to load"),
1189 arg_param_end
1191 CLIExecWithReturn(ctx, Cmd, argtable, false);
1193 int fnlen = 0;
1194 char filename[FILE_PATH_SIZE] = {0};
1195 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
1196 CLIParserFree(ctx);
1198 if (gs_trace) {
1199 free(gs_trace); // maybe better to not clobber this until we have successful load?
1200 gs_trace = NULL;
1201 gs_traceLen = 0;
1204 size_t len = 0;
1205 if (loadFile_safe(filename, ".trace", (void **)&gs_trace, &len) != PM3_SUCCESS) {
1206 PrintAndLogEx(FAILED, "Could not open file " _YELLOW_("%s"), filename);
1207 return PM3_EIO;
1210 gs_traceLen = (long)len;
1212 PrintAndLogEx(SUCCESS, "Recorded Activity (TraceLen = " _YELLOW_("%u") " bytes)", gs_traceLen);
1213 PrintAndLogEx(HINT, "try " _YELLOW_("`trace list -1 -t ...`") " to view trace. Remember the " _YELLOW_("`-1`") " param");
1214 return PM3_SUCCESS;
1217 static int CmdTraceSave(const char *Cmd) {
1219 CLIParserContext *ctx;
1220 CLIParserInit(&ctx, "trace save",
1221 "Save protocol data from trace buffer to binary file\n"
1222 "File extension is <.trace>",
1223 "trace save -f mytracefile -> w/o file extension"
1226 void *argtable[] = {
1227 arg_param_begin,
1228 arg_str1("f", "file", "<fn>", "Specify trace file to save"),
1229 arg_param_end
1231 CLIExecWithReturn(ctx, Cmd, argtable, false);
1233 int fnlen = 0;
1234 char filename[FILE_PATH_SIZE] = {0};
1235 CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
1236 CLIParserFree(ctx);
1238 if (gs_traceLen == 0) {
1239 download_trace();
1240 if (gs_traceLen == 0) {
1241 PrintAndLogEx(WARNING, "trace is empty, nothing to save");
1242 return PM3_SUCCESS;
1246 saveFile(filename, ".trace", gs_trace, gs_traceLen);
1247 return PM3_SUCCESS;
1250 int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol) {
1251 CLIParserContext *ctx;
1252 char desc[500] = {0};
1253 snprintf(desc, sizeof(desc) - 1,
1254 "Alias of `trace list -t %s` with selected protocol data to annotate trace buffer\n"
1255 "You can load a trace from file (see `trace load -h`) or it be downloaded from device by default\n"
1256 "It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol",
1257 protocol);
1258 char example[200] = {0};
1259 snprintf(example, sizeof(example) - 1,
1260 "%s list --frame -> show frame delay times\n"
1261 "%s list -1 -> use trace buffer ",
1262 alias, alias);
1263 char fullalias[100] = {0};
1264 snprintf(fullalias, sizeof(fullalias) - 1, "%s list", alias);
1265 CLIParserInit(&ctx, fullalias, desc, example);
1267 void *argtable[] = {
1268 arg_param_begin,
1269 arg_lit0("1", "buffer", "use data from trace buffer"),
1270 arg_lit0(NULL, "frame", "show frame delay times"),
1271 arg_lit0("c", NULL, "mark CRC bytes"),
1272 arg_lit0("r", NULL, "show relative times (gap and duration)"),
1273 arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"),
1274 arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n"
1275 " or to import into Wireshark using encapsulation type \"ISO 14443\""),
1276 arg_str0("f", "file", "<fn>", "filename of dictionary"),
1277 arg_param_end
1279 CLIExecWithReturn(ctx, Cmd, argtable, true);
1280 CLIParserFree(ctx);
1282 char args[128] = {0};
1283 snprintf(args, sizeof(args), "-t %s ", protocol);
1284 strncat(args, Cmd, sizeof(args) - strlen(args) - 1);
1285 return CmdTraceList(args);
1288 int CmdTraceList(const char *Cmd) {
1289 CLIParserContext *ctx;
1290 CLIParserInit(&ctx, "trace list",
1291 "Annotate trace buffer with selected protocol data\n"
1292 "You can load a trace from file (see `trace load -h`) or it be downloaded from device by default\n",
1293 "trace list -t raw -> just show raw data without annotations\n"
1294 "\n"
1295 "trace list -t 14a -> interpret as " _YELLOW_("ISO14443-A") "\n"
1296 "trace list -t 14b -> interpret as " _YELLOW_("ISO14443-B") "\n"
1297 "trace list -t 15 -> interpret as " _YELLOW_("ISO15693") "\n"
1298 "trace list -t 7816 -> interpret as " _YELLOW_("ISO7816-4") "\n"
1299 "trace list -t cryptorf -> interpret as " _YELLOW_("CryptoRF") "\n\n"
1300 "trace list -t des -> interpret as " _YELLOW_("MIFARE DESFire") "\n"
1301 "trace list -t felica -> interpret as " _YELLOW_("ISO18092 / FeliCa") "\n"
1302 "trace list -t hitag1 -> interpret as " _YELLOW_("Hitag 1") "\n"
1303 "trace list -t hitag2 -> interpret as " _YELLOW_("Hitag 2") "\n"
1304 "trace list -t hitags -> interpret as " _YELLOW_("Hitag S") "\n"
1305 "trace list -t iclass -> interpret as " _YELLOW_("iCLASS") "\n"
1306 "trace list -t legic -> interpret as " _YELLOW_("LEGIC") "\n"
1307 "trace list -t lto -> interpret as " _YELLOW_("LTO-CM") "\n"
1308 "trace list -t mf -> interpret as " _YELLOW_("MIFARE Classic") " and decrypt crypto1 stream\n"
1309 "trace list -t seos -> interpret as " _YELLOW_("SEOS") "\n"
1310 "trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") "\n"
1311 "trace list -t topaz -> interpret as " _YELLOW_("Topaz") "\n"
1312 "trace list -t mfp -> interpret as " _YELLOW_("MIFARE Plus") "\n"
1313 "\n"
1314 "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file\n"
1315 "trace list -t 14a --frame -> show frame delay times\n"
1316 "trace list -t 14a -1 -> use trace buffer "
1319 void *argtable[] = {
1320 arg_param_begin,
1321 arg_lit0("1", "buffer", "use data from trace buffer"),
1322 arg_lit0(NULL, "frame", "show frame delay times"),
1323 arg_lit0("c", NULL, "mark CRC bytes"),
1324 arg_lit0("r", NULL, "show relative times (gap and duration)"),
1325 arg_lit0("u", NULL, "display times in microseconds instead of clock cycles"),
1326 arg_lit0("x", NULL, "show hexdump to convert to pcap(ng)\n"
1327 " or to import into Wireshark using encapsulation type \"ISO 14443\""),
1328 arg_str0("t", "type", NULL, "protocol to annotate the trace"),
1329 arg_str0("f", "file", "<fn>", "filename of dictionary"),
1330 arg_param_end
1332 CLIExecWithReturn(ctx, Cmd, argtable, true);
1334 bool use_buffer = arg_get_lit(ctx, 1);
1335 bool show_wait_cycles = arg_get_lit(ctx, 2);
1336 bool mark_crc = arg_get_lit(ctx, 3);
1337 bool use_relative = arg_get_lit(ctx, 4);
1338 bool use_us = arg_get_lit(ctx, 5);
1339 bool show_hex = arg_get_lit(ctx, 6);
1341 int tlen = 0;
1342 char type[10] = {0};
1343 CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)type, sizeof(type), &tlen);
1344 str_lower(type);
1346 int diclen = 0;
1347 char dictionary[FILE_PATH_SIZE + 2] = {0};
1348 if (CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)dictionary, FILE_PATH_SIZE, &diclen)) {
1349 PrintAndLogEx(FAILED, "Dictionary file name too long or invalid.");
1350 diclen = 0;
1353 CLIParserFree(ctx);
1355 clearCommandBuffer();
1357 // no crc, no annotations
1358 uint8_t protocol = -1;
1360 // validate type of output
1361 if (strcmp(type, "14a") == 0) protocol = ISO_14443A;
1362 else if (strcmp(type, "14b") == 0) protocol = ISO_14443B;
1363 else if (strcmp(type, "15") == 0) protocol = ISO_15693;
1364 else if (strcmp(type, "7816") == 0) protocol = ISO_7816_4;
1365 else if (strcmp(type, "cryptorf") == 0) protocol = PROTO_CRYPTORF;
1366 else if (strcmp(type, "des") == 0) protocol = MFDES;
1367 else if (strcmp(type, "felica") == 0) protocol = FELICA;
1368 else if (strcmp(type, "hitag1") == 0) protocol = PROTO_HITAG1;
1369 else if (strcmp(type, "hitag2") == 0) protocol = PROTO_HITAG2;
1370 else if (strcmp(type, "hitags") == 0) protocol = PROTO_HITAGS;
1371 else if (strcmp(type, "iclass") == 0) protocol = ICLASS;
1372 else if (strcmp(type, "legic") == 0) protocol = LEGIC;
1373 else if (strcmp(type, "lto") == 0) protocol = LTO;
1374 else if (strcmp(type, "mf") == 0) protocol = PROTO_MIFARE;
1375 else if (strcmp(type, "raw") == 0) protocol = -1;
1376 else if (strcmp(type, "seos") == 0) protocol = SEOS;
1377 else if (strcmp(type, "thinfilm") == 0) protocol = THINFILM;
1378 else if (strcmp(type, "topaz") == 0) protocol = TOPAZ;
1379 else if (strcmp(type, "mfp") == 0) protocol = PROTO_MFPLUS;
1380 else if (strcmp(type, "") == 0) protocol = -1;
1381 else {
1382 PrintAndLogEx(FAILED, "Unknown protocol \"%s\"", type);
1383 return PM3_EINVARG;
1386 if (use_buffer == false) {
1387 download_trace();
1388 } else if (gs_traceLen == 0 || gs_trace == NULL) {
1390 if (IfPm3Present() == false) {
1391 PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace.");
1392 } else {
1393 PrintAndLogEx(FAILED, "You requested a trace list but there is no trace.");
1396 PrintAndLogEx(FAILED, "Consider using `" _YELLOW_("trace load") "` or removing parameter `" _YELLOW_("-1") "`");
1397 return PM3_EINVARG;
1400 PrintAndLogEx(SUCCESS, "Recorded activity ( " _YELLOW_("%u") " bytes )", gs_traceLen);
1401 if (gs_traceLen == 0) {
1402 return PM3_SUCCESS;
1405 uint16_t tracepos = 0;
1408 if (protocol == FELICA) {
1409 printFelica(gs_traceLen, gs_trace);
1410 } */
1412 if (show_hex) {
1413 while (tracepos < gs_traceLen) {
1414 tracepos = printHexLine(tracepos, gs_traceLen, gs_trace, protocol);
1416 } else {
1418 if (use_relative) {
1419 PrintAndLogEx(INFO, _YELLOW_("gap") " = time between transfers. " _YELLOW_("duration") " = duration of data transfer. " _YELLOW_("src") " = source of transfer.");
1420 } else {
1421 PrintAndLogEx(INFO, _YELLOW_("start") " = start of start frame. " _YELLOW_("end") " = end of frame. " _YELLOW_("src") " = source of transfer.");
1424 if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == PROTO_MFPLUS || protocol == TOPAZ || protocol == LTO) {
1425 if (use_us)
1426 PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in microseconds");
1427 else
1428 PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in carrier periods (1/13.56MHz)");
1431 if (protocol == THINFILM) {
1432 if (use_us)
1433 PrintAndLogEx(INFO, _YELLOW_("Thinfilm") " - all times are in microseconds");
1434 else
1435 PrintAndLogEx(INFO, _YELLOW_("Thinfilm") " - all times are in carrier periods (1/13.56MHz)");
1438 if (protocol == ICLASS || protocol == ISO_15693) {
1439 if (use_us)
1440 PrintAndLogEx(INFO, _YELLOW_("ISO15693 / iCLASS") " - all times are in microseconds");
1441 else
1442 PrintAndLogEx(INFO, _YELLOW_("ISO15693 / iCLASS") " - all times are in carrier periods (1/13.56MHz)");
1445 if (protocol == LEGIC)
1446 PrintAndLogEx(INFO, _YELLOW_("LEGIC") " - Reader Mode: Timings are in ticks (1us == 1.5ticks)\n"
1447 " Tag Mode: Timings are in sub carrier periods (1/212 kHz == 4.7us)");
1449 if (protocol == ISO_14443B || protocol == PROTO_CRYPTORF) {
1450 if (use_us)
1451 PrintAndLogEx(INFO, _YELLOW_("ISO14443B") " - all times are in microseconds");
1452 else
1453 PrintAndLogEx(INFO, _YELLOW_("ISO14443B") " - all times are in carrier periods (1/13.56MHz)");
1456 if (protocol == ISO_7816_4)
1457 PrintAndLogEx(INFO, _YELLOW_("ISO7816-4 / Smartcard") " - Timings n/a");
1459 if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) {
1460 PrintAndLogEx(INFO, _YELLOW_("Hitag 1 / Hitag 2 / Hitag S") " - Timings in ETU (8us)");
1463 if (protocol == FELICA) {
1464 if (use_us)
1465 PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in microseconds");
1466 else
1467 PrintAndLogEx(INFO, _YELLOW_("ISO18092 / FeliCa") " - all times are in carrier periods (1/13.56MHz)");
1471 const uint64_t *dicKeys = NULL;
1472 uint32_t dicKeysCount = 0;
1473 bool dictionaryLoad = false;
1475 if (protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) {
1476 if (diclen > 0) {
1477 uint8_t *keyBlock = NULL;
1478 int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, 6, &dicKeysCount);
1479 if (res != PM3_SUCCESS || dicKeysCount == 0 || keyBlock == NULL) {
1480 PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)");
1481 } else {
1482 dicKeys = calloc(dicKeysCount, sizeof(uint64_t));
1483 for (int i = 0; i < dicKeysCount; i++) {
1484 uint64_t key = bytes_to_num(keyBlock + i * 6, 6);
1485 memcpy((uint8_t *) &dicKeys[i], &key, sizeof(uint64_t));
1487 dictionaryLoad = true;
1489 if (keyBlock != NULL) {
1490 free(keyBlock);
1493 if (dicKeys == NULL) {
1494 dicKeys = g_mifare_default_keys;
1495 dicKeysCount = ARRAYLEN(g_mifare_default_keys);
1499 if (protocol == PROTO_HITAG2) {
1501 if (strlen(dictionary) == 0) {
1502 snprintf(dictionary, sizeof(dictionary), HITAG_DICTIONARY);
1505 // load keys
1506 uint8_t *keyBlock = NULL;
1507 int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, HITAG_CRYPTOKEY_SIZE, &dicKeysCount);
1508 if (res != PM3_SUCCESS || dicKeysCount == 0 || keyBlock == NULL) {
1509 PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!");
1510 } else {
1511 dicKeys = calloc(dicKeysCount, sizeof(uint64_t));
1512 for (int i = 0; i < dicKeysCount; i++) {
1513 uint64_t key = bytes_to_num(keyBlock + i * HITAG_CRYPTOKEY_SIZE, HITAG_CRYPTOKEY_SIZE);
1514 memcpy((uint8_t *) &dicKeys[i], &key, sizeof(uint64_t));
1516 dictionaryLoad = true;
1518 if (keyBlock != NULL) {
1519 free(keyBlock);
1523 PrintAndLogEx(NORMAL, "");
1524 if (use_relative) {
1525 PrintAndLogEx(NORMAL, " Gap | Duration | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation");
1526 } else {
1527 PrintAndLogEx(NORMAL, " Start | End | Src | Data (! denotes parity error) | CRC | Annotation");
1529 PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------");
1531 // clean authentication data used with the mifare classic decrypt fct
1532 if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) {
1533 ClearAuthData();
1536 // reset hitag state machine
1537 if (protocol == PROTO_HITAG1 || protocol == PROTO_HITAG2 || protocol == PROTO_HITAGS) {
1538 annotateHitag2_init();
1541 uint32_t previous_EOT = 0;
1542 uint32_t *prev_EOT = NULL;
1543 if (use_relative) {
1544 prev_EOT = &previous_EOT;
1547 while (tracepos < gs_traceLen) {
1548 tracepos = printTraceLine(tracepos, gs_traceLen, gs_trace, protocol, show_wait_cycles, mark_crc, prev_EOT, use_us, dicKeys, dicKeysCount);
1550 if (kbd_enter_pressed()) {
1551 break;
1555 if (dictionaryLoad) {
1556 free((void *) dicKeys);
1560 if (show_hex) {
1561 PrintAndLogEx(HINT, "syntax to use: " _YELLOW_("`text2pcap -t \"%%S.\" -l 264 -n <input-text-file> <output-pcapng-file>`"));
1564 return PM3_SUCCESS;
1567 static command_t CommandTable[] = {
1568 {"help", CmdHelp, AlwaysAvailable, "This help"},
1569 {"extract", CmdTraceExtract, AlwaysAvailable, "Extract authentication challenges found in trace"},
1570 {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"},
1571 {"load", CmdTraceLoad, AlwaysAvailable, "Load trace from file"},
1572 {"save", CmdTraceSave, AlwaysAvailable, "Save trace buffer to file"},
1573 {NULL, NULL, NULL, NULL}
1576 static int CmdHelp(const char *Cmd) {
1577 (void)Cmd; // Cmd is not used so far
1578 CmdsHelp(CommandTable);
1579 return PM3_SUCCESS;
1582 int CmdTrace(const char *Cmd) {
1583 clearCommandBuffer();
1584 return CmdsParse(CommandTable, Cmd);