1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
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.
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 // High frequency Legic commands
17 //-----------------------------------------------------------------------------
18 #include "cmdhflegic.h"
20 #include <ctype.h> // tolower
22 #include "pm3line.h" // pm3line_read, pm3line_free
23 #include "cliparser.h"
24 #include "cmdparser.h" // command_t
25 #include "comms.h" // clearCommandBuffer
29 #include "fileutils.h" //saveFile
31 static int CmdHelp(const char *Cmd
);
33 #define LEGIC_PRIME_MIM22 22
34 #define LEGIC_PRIME_MIM256 256
35 #define LEGIC_PRIME_MIM1024 1024
36 #define LEGIC_BLOCK_SIZE 8
37 #define LEGIC_PACKET_SIZE (PM3_CMD_DATA_SIZE - sizeof(legic_packet_t))
39 static bool legic_xor(uint8_t *data
, uint16_t cardsize
) {
42 PrintAndLogEx(INFO
, "No obsfuscation such small dump");
46 uint8_t crc
= data
[4];
47 uint32_t calc_crc
= CRC8Legic(data
, 4);
48 if (crc
!= calc_crc
) {
49 PrintAndLogEx(INFO
, "CRC mismatch, obsfuscation not possible");
53 for (uint16_t i
= 22; i
< cardsize
; i
++) {
56 PrintAndLogEx(SUCCESS
, "Applying xoring of data done!");
60 static int decode_and_print_memory(uint16_t card_size
, const uint8_t *input_buffer
) {
62 if (!(card_size
== LEGIC_PRIME_MIM22
|| card_size
== LEGIC_PRIME_MIM256
|| card_size
== LEGIC_PRIME_MIM1024
)) {
63 PrintAndLogEx(FAILED
, "Bytebuffer is not any known legic card size! (MIM22, MIM256, MIM1024)");
67 // copy input buffer into newly allocated buffer, because the existing code mutates the data inside.
68 uint8_t *data
= calloc(card_size
, sizeof(uint8_t));
70 PrintAndLogEx(WARNING
, "Cannot allocate memory");
73 memcpy(data
, input_buffer
, card_size
);
75 int i
= 0, k
= 0, segmentNum
= 0, segment_len
= 0, segment_flag
= 0;
76 int wrp
= 0, wrc
= 0, dcf
= 0;
77 uint8_t stamp_len
= 0;
78 char token_type
[6] = {0, 0, 0, 0, 0, 0};
80 int return_value
= PM3_SUCCESS
;
82 // Output CDF System area (9 bytes) plus remaining header area (12 bytes)
84 uint32_t calc_crc
= CRC8Legic(data
, 4);
86 PrintAndLogEx(INFO
, "--- " _CYAN_("Tag Information") " ----------------------------------------");
87 PrintAndLogEx(NORMAL
, "");
88 PrintAndLogEx(SUCCESS
, " " _CYAN_("CDF: System Area"));
89 PrintAndLogEx(INFO
, "------------------------------------------------------");
90 PrintAndLogEx(SUCCESS
, "MCD: " _GREEN_("%02X") " MSN: " _GREEN_("%s") " MCC: " _GREEN_("%02X") " ( %s )",
92 sprint_hex(data
+ 1, 3),
94 (calc_crc
== crc
) ? _GREEN_("ok") : _RED_("fail")
97 // MCD = Manufacturer ID (should be list meaning something?)
100 dcf
= ((int)data
[6] << 8) | (int)data
[5];
102 // New unwritten media?
105 PrintAndLogEx(SUCCESS
, "DCF: %d (%02x %02x), Token Type=NM (New Media)",
111 } else if (dcf
> 60000) { // Master token?
115 if (data
[6] == 0xEC) {
116 strncpy(token_type
, "XAM", sizeof(token_type
) - 1);
118 stamp_len
= 0x0c - (data
[5] >> 4);
121 uint8_t tmp
= data
[5] & 0x7F;
123 strncpy(token_type
, "IAM", sizeof(token_type
) - 1);
124 fl
= (0x2F - tmp
) + 1;
125 } else if (tmp
>= 0x30 && tmp
<= 0x6F) {
126 strncpy(token_type
, "SAM", sizeof(token_type
) - 1);
127 fl
= (0x6F - tmp
) + 1;
128 } else if (tmp
>= 0x70 && tmp
<= 0x7F) {
129 strncpy(token_type
, "GAM", sizeof(token_type
) - 1);
130 fl
= (0x7F - tmp
) + 1;
133 stamp_len
= 0xFC - data
[6];
136 PrintAndLogEx(SUCCESS
, "DCF: %d (%02x %02x) Token Type=" _YELLOW_("%s") " (OLE=%01u) OL=%02u FL=%02u",
141 (data
[5] & 0x80) >> 7,
146 } else { // Is IM(-S) type of card...
148 if (data
[7] == 0x9F && data
[8] == 0xFF) {
150 strncpy(token_type
, "IM-S", sizeof(token_type
) - 1);
152 strncpy(token_type
, "IM", sizeof(token_type
) - 1);
155 PrintAndLogEx(SUCCESS
, "DCF: %d (%02x %02x) Token Type = %s (OLE = %01u)",
160 (data
[5] & 0x80) >> 7
164 // Makes no sense to show this on blank media...
168 PrintAndLogEx(SUCCESS
, "WRP = %02u WRC = %01u RD = %01u SSC = %02X",
170 (data
[7] & 0x70) >> 4,
171 (data
[7] & 0x80) >> 7,
176 // Header area is only available on IM-S cards, on master tokens this data is the master token data itself
177 if (bIsSegmented
|| dcf
> 60000) {
179 PrintAndLogEx(SUCCESS
, "Master token data");
180 PrintAndLogEx(SUCCESS
, "%s", sprint_hex(data
+ 8, 14));
182 PrintAndLogEx(SUCCESS
, "Remaining Header Area");
183 PrintAndLogEx(SUCCESS
, "%s", sprint_hex(data
+ 9, 13));
187 PrintAndLogEx(INFO
, "------------------------------------------------------");
189 uint8_t segCrcBytes
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
190 uint32_t segCalcCRC
= 0;
193 // Not a data card by dcf or too small to contain data (MIM22)?
194 if (dcf
> 60000 || card_size
== LEGIC_PRIME_MIM22
) {
198 PrintAndLogEx(INFO
, "");
199 PrintAndLogEx(SUCCESS
, _CYAN_("ADF: User Area"));
200 PrintAndLogEx(INFO
, "------------------------------------------------------");
204 // Data start point on segmented cards
208 for (segmentNum
= 1; segmentNum
< 128; segmentNum
++) {
209 // for decoding the segment header we need at least 4 bytes left in buffer
210 if ((i
+ 4) > card_size
) {
211 PrintAndLogEx(FAILED
, "Cannot read segment header, because the input buffer is too small.");
212 PrintAndLogEx(FAILED
, "Please check that the data is correct and properly aligned");
213 return_value
= PM3_EOUTOFBOUND
;
216 segment_len
= ((data
[i
+ 1] ^ crc
) & 0x0f) * 256 + (data
[i
] ^ crc
);
217 segment_flag
= ((data
[i
+ 1] ^ crc
) & 0xf0) >> 4;
218 wrp
= (data
[i
+ 2] ^ crc
);
219 wrc
= ((data
[i
+ 3] ^ crc
) & 0x70) >> 4;
221 bool hasWRC
= (wrc
> 0);
222 bool hasWRP
= (wrp
> wrc
);
223 int wrp_len
= (wrp
- wrc
);
224 int remain_seg_payload_len
= (segment_len
- wrp
- 5);
226 // validate segment-crc
227 segCrcBytes
[0] = data
[0]; //uid0
228 segCrcBytes
[1] = data
[1]; //uid1
229 segCrcBytes
[2] = data
[2]; //uid2
230 segCrcBytes
[3] = data
[3]; //uid3
231 segCrcBytes
[4] = (data
[i
] ^ crc
); //hdr0
232 segCrcBytes
[5] = (data
[i
+ 1] ^ crc
); //hdr1
233 segCrcBytes
[6] = (data
[i
+ 2] ^ crc
); //hdr2
234 segCrcBytes
[7] = (data
[i
+ 3] ^ crc
); //hdr3
236 segCalcCRC
= CRC8Legic(segCrcBytes
, 8);
237 segCRC
= data
[i
+ 4] ^ crc
;
239 PrintAndLogEx(SUCCESS
, "Segment....... " _YELLOW_("%02u"), segmentNum
);
240 PrintAndLogEx(SUCCESS
, "Raw header.... 0x%02X 0x%02X 0x%02X 0x%02X",
246 PrintAndLogEx(SUCCESS
, "Segment len... %u Flag: 0x%X (valid:%01u last:%01u)",
249 (segment_flag
& 0x4) >> 2,
250 (segment_flag
& 0x8) >> 3
252 PrintAndLogEx(SUCCESS
, " WRP: %02u WRC: %02u RD: %01u CRC: 0x%02X ( %s )",
255 ((data
[i
+ 3] ^ crc
) & 0x80) >> 7,
257 (segCRC
== segCalcCRC
) ? _GREEN_("ok") : _RED_("fail")
262 // for printing the complete segment we need at least wrc + wrp_len + remain_seg_payload_len bytes
263 if ((i
+ wrc
+ wrp_len
+ remain_seg_payload_len
) > card_size
) {
264 PrintAndLogEx(FAILED
, "Cannot read segment body, because the input buffer is too small. "
265 "Please check that the data is correct and properly aligned. ");
266 return_value
= PM3_EOUTOFBOUND
;
271 PrintAndLogEx(INFO
, "");
272 PrintAndLogEx(SUCCESS
, _CYAN_("WRC protected area:") " (I %d | K %d| WRC %d)", i
, k
, wrc
);
273 PrintAndLogEx(INFO
, "");
274 PrintAndLogEx(INFO
, "## | data | ascii");
275 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
277 for (k
= i
; k
< (i
+ wrc
); ++k
)
280 print_hex_break(data
+ i
, wrc
, 16);
281 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
282 PrintAndLogEx(INFO
, "");
287 PrintAndLogEx(SUCCESS
, _CYAN_("Remaining write protected area:") " (I %d | K %d | WRC %d | WRP %d WRP_LEN %d)", i
, k
, wrc
, wrp
, wrp_len
);
288 PrintAndLogEx(INFO
, "");
289 PrintAndLogEx(INFO
, "## | data | ascii");
290 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
292 for (k
= i
; k
< (i
+ wrp_len
); ++k
)
295 print_hex_break(data
+ i
, wrp_len
, 16);
296 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
297 PrintAndLogEx(INFO
, "");
300 // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...)
302 PrintAndLogEx(SUCCESS
, "Card ID: " _YELLOW_("%2X%02X%02X"),
309 if (remain_seg_payload_len
> 0) {
310 PrintAndLogEx(SUCCESS
, _CYAN_("Remaining segment payload:") " (I %d | K %d | Remain LEN %d)", i
, k
, remain_seg_payload_len
);
311 PrintAndLogEx(INFO
, "");
312 PrintAndLogEx(INFO
, "## | data | ascii");
313 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
315 for (k
= i
; k
< (i
+ remain_seg_payload_len
); ++k
)
318 print_hex_break(data
+ i
, remain_seg_payload_len
, 16);
319 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------\n");
320 i
+= remain_seg_payload_len
;
322 // end with last segment
323 if (segment_flag
& 0x8)
329 // Data start point on unsegmented cards
332 wrp
= data
[7] & 0x0F;
333 wrc
= (data
[7] & 0x70) >> 4;
335 bool hasWRC
= (wrc
> 0);
336 bool hasWRP
= (wrp
> wrc
);
337 int wrp_len
= (wrp
- wrc
);
338 int remain_seg_payload_len
= (card_size
- 22 - wrp
);
340 PrintAndLogEx(SUCCESS
, "Unsegmented card - WRP: %02u WRC: %02u RD: %01u",
343 (data
[7] & 0x80) >> 7
346 // for printing the complete segment we need at least wrc + wrp_len + remain_seg_payload_len bytes
347 if ((i
+ wrc
+ wrp_len
+ remain_seg_payload_len
) > card_size
) {
348 PrintAndLogEx(FAILED
, "Cannot read segment body, because the input buffer is too small. "
349 "Please check that the data is correct and properly aligned. ");
350 return_value
= PM3_EOUTOFBOUND
;
355 PrintAndLogEx(SUCCESS
, _CYAN_("WRC protected area:") " (I %d | WRC %d)", i
, wrc
);
356 PrintAndLogEx(INFO
, "");
357 PrintAndLogEx(INFO
, "## | data | ascii");
358 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
359 print_hex_break(data
+ i
, wrc
, 16);
360 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
361 PrintAndLogEx(INFO
, "");
366 PrintAndLogEx(SUCCESS
, _CYAN_("Remaining write protected area:") " (I %d | WRC %d | WRP %d | WRP_LEN %d)", i
, wrc
, wrp
, wrp_len
);
367 PrintAndLogEx(INFO
, "");
368 PrintAndLogEx(INFO
, "## | data | ascii");
369 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
370 print_hex_break(data
+ i
, wrp_len
, 16);
371 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
372 PrintAndLogEx(INFO
, "");
375 // Q: does this one work?
376 // A: Only if KGH/BGH is used with BCD encoded card number. Maybe this will show just garbage
378 PrintAndLogEx(SUCCESS
, "Card ID: " _YELLOW_("%2X%02X%02X"),
386 if (remain_seg_payload_len
> 0) {
387 PrintAndLogEx(SUCCESS
, _CYAN_("Remaining segment payload:") " (I %d | Remain LEN %d)", i
, remain_seg_payload_len
);
388 PrintAndLogEx(INFO
, "");
389 PrintAndLogEx(INFO
, "## | data | ascii");
390 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
391 print_hex_break(data
+ i
, remain_seg_payload_len
, 16);
392 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------\n");
398 return (return_value
);
402 * Output BigBuf and deobfuscate LEGIC RF tag data.
403 * This is based on information given in the talk held
404 * by Henryk Ploetz and Karsten Nohl at 26c3
406 static int CmdLegicInfo(const char *Cmd
) {
407 CLIParserContext
*ctx
;
408 CLIParserInit(&ctx
, "hf legic info",
409 "Gets information from a LEGIC Prime tag like systemarea, user areas, etc",
414 arg_lit0("v", "verbose", "verbose output"),
417 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
418 bool verbose
= arg_get_lit(ctx
, 1);
421 uint16_t datalen
= 0;
424 legic_card_select_t card
;
425 if (legic_get_type(&card
) != PM3_SUCCESS
) {
426 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
430 PrintAndLogEx(SUCCESS
, "Reading full tag memory of " _YELLOW_("%d") " bytes...", card
.cardsize
);
432 // allocate receiver buffer
433 uint8_t *data
= calloc(card
.cardsize
, sizeof(uint8_t));
435 PrintAndLogEx(WARNING
, "Cannot allocate memory");
439 int status
= legic_read_mem(0, card
.cardsize
, 0x55, data
, &datalen
);
440 if (status
!= PM3_SUCCESS
) {
441 PrintAndLogEx(WARNING
, "Failed reading memory");
447 PrintAndLogEx(NORMAL
, "");
448 PrintAndLogEx(INFO
, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii");
449 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
450 print_hex_break(data
, datalen
, 16);
453 PrintAndLogEx(NORMAL
, "");
454 decode_and_print_memory(card
.cardsize
, data
);
460 // offset in data memory
461 // number of bytes to read
462 static int CmdLegicRdbl(const char *Cmd
) {
463 CLIParserContext
*ctx
;
464 CLIParserInit(&ctx
, "hf legic rdbl",
465 "Read data from a LEGIC Prime tag",
466 "hf legic rdbl -o 0 -l 16 -> read 16 bytes from offset 0 (system header)\n"
467 "hf legic rdbl -o 0 -l 4 --iv 55 -> read 4 bytes from offset 0\n"
468 "hf legic rdbl -o 0 -l 256 --iv 55 -> read 256 bytes from offset 0");
472 arg_int0("o", "offset", "<dec>", "offset in data array to start download from"),
473 arg_int0("l", "length", "<dec>", "number of bytes to read"),
474 arg_str0(NULL
, "iv", "<hex>", "Initialization vector to use. Must be odd and 7bits max"),
477 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
479 int offset
= arg_get_int_def(ctx
, 1, 0);
480 int len
= arg_get_int_def(ctx
, 2, 16);
483 uint8_t iv
[1] = {0x01};
484 CLIGetHexWithReturn(ctx
, 3, iv
, &iv_len
);
488 if (len
+ offset
>= LEGIC_PRIME_MIM1024
) {
489 PrintAndLogEx(WARNING
, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", LEGIC_PRIME_MIM1024
, len
+ offset
);
490 return PM3_EOUTOFBOUND
;
493 PrintAndLogEx(SUCCESS
, "Reading %d bytes, from offset %d", len
, offset
);
495 // allocate receiver buffer
496 uint8_t *data
= calloc(len
, sizeof(uint8_t));
498 PrintAndLogEx(WARNING
, "Cannot allocate memory");
502 uint16_t datalen
= 0;
503 int status
= legic_read_mem(offset
, len
, iv
[0], data
, &datalen
);
504 if (status
== PM3_SUCCESS
) {
505 PrintAndLogEx(INFO
, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii");
506 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
507 print_hex_break(data
, datalen
, 16);
513 static int CmdLegicSim(const char *Cmd
) {
514 CLIParserContext
*ctx
;
515 CLIParserInit(&ctx
, "hf legic sim",
516 "Simulates a LEGIC Prime tag.\n"
517 "Following types supported (MIM22, MIM256, MIM1024)",
518 "hf legic sim --22\n"
523 arg_lit0(NULL
, "22", "LEGIC Prime MIM22"),
524 arg_lit0(NULL
, "256", "LEGIC Prime MIM256 (def)"),
525 arg_lit0(NULL
, "1024", "LEGIC Prime MIM1024"),
528 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
529 bool m1
= arg_get_lit(ctx
, 1);
530 bool m2
= arg_get_lit(ctx
, 2);
531 bool m3
= arg_get_lit(ctx
, 3);
535 if (m1
+ m2
+ m3
> 1) {
536 PrintAndLogEx(WARNING
, "Only specify one LEGIC Prime Type");
538 } else if (m1
+ m2
+ m3
== 0) {
547 payload
.send_reply
= true;
555 clearCommandBuffer();
556 SendCommandNG(CMD_HF_LEGIC_SIMULATE
, (uint8_t *)&payload
, sizeof(payload
));
557 PacketResponseNG resp
;
559 PrintAndLogEx(INFO
, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
561 if (kbd_enter_pressed()) {
562 SendCommandNG(CMD_BREAK_LOOP
, NULL
, 0);
563 PrintAndLogEx(DEBUG
, "User aborted");
567 if (WaitForResponseTimeout(CMD_HF_LEGIC_SIMULATE
, &resp
, 1500)) {
572 PrintAndLogEx(HINT
, "Try `" _YELLOW_("hf legic list") "` to view trace log");
573 PrintAndLogEx(INFO
, "Done!");
577 static int CmdLegicWrbl(const char *Cmd
) {
578 CLIParserContext
*ctx
;
579 CLIParserInit(&ctx
, "hf legic wrbl",
580 "Write data to a LEGIC Prime tag. It autodetects tagsize to ensure proper write",
581 "hf legic wrbl -o 0 -d 11223344 -> Write 0x11223344 starting from offset 0)\n"
582 "hf legic wrbl -o 10 -d DEADBEEF -> Write 0xdeadbeef starting from offset 10");
586 arg_int1("o", "offset", "<dec>", "offset in data array to start writing"),
587 arg_str1("d", "data", "<hex>", "data to write"),
588 arg_lit0(NULL
, "danger", "Auto-confirm dangerous operations"),
591 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
593 int offset
= arg_get_int_def(ctx
, 1, 0);
596 uint8_t data
[LEGIC_PRIME_MIM1024
] = {0};
597 CLIGetHexWithReturn(ctx
, 2, data
, &dlen
);
599 bool autoconfirm
= arg_get_lit(ctx
, 3);
603 // OUT-OF-BOUNDS checks
604 // UID 4+1 bytes can't be written to.
606 PrintAndLogEx(WARNING
, "Out-of-bounds, bytes 0-1-2-3-4 can't be written to. Offset = %d", offset
);
607 return PM3_EOUTOFBOUND
;
611 legic_card_select_t card
;
612 if (legic_get_type(&card
) != PM3_SUCCESS
) {
613 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
617 legic_print_type(card
.cardsize
, 0);
619 if (dlen
+ offset
> card
.cardsize
) {
620 PrintAndLogEx(WARNING
, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", card
.cardsize
, dlen
+ offset
);
621 return PM3_EOUTOFBOUND
;
624 if ((offset
== 5 || offset
== 6) && (! autoconfirm
)) {
625 PrintAndLogEx(INFO
, "############# DANGER ################");
626 PrintAndLogEx(WARNING
, "# changing the DCF is irreversible #");
627 PrintAndLogEx(INFO
, "#####################################");
628 const char *confirm
= "Do you really want to continue? y(es)/n(o) : ";
629 bool overwrite
= false;
630 char *answer
= pm3line_read(confirm
);
631 overwrite
= (answer
[0] == 'y' || answer
[0] == 'Y');
632 pm3line_free(answer
);
633 if (overwrite
== false) {
634 PrintAndLogEx(WARNING
, "command cancelled");
635 return PM3_EOPABORTED
;
642 PrintAndLogEx(SUCCESS
, "Writing to tag to offset %i", offset
);
644 legic_packet_t
*payload
= calloc(1, sizeof(legic_packet_t
) + dlen
);
645 payload
->offset
= (offset
& 0xFFFF);
646 payload
->iv
= (IV
& 0x7F);
648 memcpy(payload
->data
, data
, dlen
);
650 PacketResponseNG resp
;
651 clearCommandBuffer();
652 SendCommandNG(CMD_HF_LEGIC_WRITER
, (uint8_t *)payload
, sizeof(legic_packet_t
) + dlen
);
656 while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER
, &resp
, 2000) == false) {
658 PrintAndLogEx(NORMAL
, "." NOLF
);
660 PrintAndLogEx(WARNING
, "\ncommand execution time out");
664 PrintAndLogEx(NORMAL
, "");
666 if (resp
.status
!= PM3_SUCCESS
) {
667 PrintAndLogEx(WARNING
, "Failed writing tag");
674 static int CmdLegicCalcCrc(const char *Cmd
) {
675 CLIParserContext
*ctx
;
676 CLIParserInit(&ctx
, "hf legic crc",
677 "Calculates the legic crc8/crc16 on the given data",
678 "hf legic crc -d deadbeef1122\n"
679 "hf legic crc -d deadbeef1122 --mcc 9A -t 16 -> CRC Type 16");
683 arg_str1("d", "data", "<hex>", "bytes to calculate crc over"),
684 arg_str0(NULL
, "mcc", "<hex>", "MCC hex byte (UID CRC)"),
685 arg_int0("t", "type", "<dec>", "CRC Type (default: 8)"),
688 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
691 uint8_t data
[4096] = {0};
693 CLIGetHexWithReturn(ctx
, 1, data
, &data_len
);
696 uint8_t mcc
[1] = {0}; // formerly uidcrc
698 CLIGetHexWithReturn(ctx
, 2, mcc
, &mcc_len
);
700 int type
= arg_get_int_def(ctx
, 3, 0);
706 init_table(CRC_LEGIC_16
);
707 PrintAndLogEx(SUCCESS
, "Legic crc16: %X", crc16_legic(data
, data_len
, mcc
[0]));
710 PrintAndLogEx(SUCCESS
, "Legic crc8: %X", CRC8Legic(data
, data_len
));
717 int legic_read_mem(uint32_t offset
, uint32_t len
, uint32_t iv
, uint8_t *out
, uint16_t *outlen
) {
721 legic_packet_t
*payload
= calloc(1, sizeof(legic_packet_t
));
722 payload
->offset
= (offset
& 0xFFFF);
726 clearCommandBuffer();
727 SendCommandNG(CMD_HF_LEGIC_READER
, (uint8_t *)payload
, sizeof(legic_packet_t
));
729 PacketResponseNG resp
;
732 while (WaitForResponseTimeout(CMD_HF_LEGIC_READER
, &resp
, 1000) == false) {
734 PrintAndLogEx(NORMAL
, "." NOLF
);
736 PrintAndLogEx(WARNING
, "\ncommand execution time out");
740 PrintAndLogEx(NORMAL
, "");
742 *outlen
= resp
.data
.asDwords
[0];
743 if (resp
.status
!= PM3_SUCCESS
) {
744 PrintAndLogEx(WARNING
, "Failed reading tag");
749 PrintAndLogEx(WARNING
, "Fail, only managed to read %u bytes", *outlen
);
751 // copy data from device
752 if (GetFromDevice(BIG_BUF_EML
, out
, *outlen
, 0, NULL
, 0, NULL
, 2500, false) == false) {
753 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
759 int legic_print_type(uint32_t tagtype
, uint8_t spaces
) {
762 char *spacer
= spc
+ (10 - spaces
);
764 if (tagtype
== LEGIC_PRIME_MIM22
)
765 PrintAndLogEx(SUCCESS
, "%sTYPE: " _YELLOW_("MIM%d card (outdated)"), spacer
, tagtype
);
766 else if (tagtype
== LEGIC_PRIME_MIM256
)
767 PrintAndLogEx(SUCCESS
, "%sTYPE: " _YELLOW_("MIM%d card (234 bytes)"), spacer
, tagtype
);
768 else if (tagtype
== LEGIC_PRIME_MIM1024
)
769 PrintAndLogEx(SUCCESS
, "%sTYPE: " _YELLOW_("MIM%d card (1002 bytes)"), spacer
, tagtype
);
771 PrintAndLogEx(INFO
, "%sTYPE: " _YELLOW_("Unknown %06x"), spacer
, tagtype
);
774 int legic_get_type(legic_card_select_t
*card
) {
779 clearCommandBuffer();
780 SendCommandNG(CMD_HF_LEGIC_INFO
, NULL
, 0);
781 PacketResponseNG resp
;
782 if (WaitForResponseTimeout(CMD_HF_LEGIC_INFO
, &resp
, 1500) == false)
785 if (resp
.status
!= PM3_SUCCESS
)
788 memcpy(card
, resp
.data
.asBytes
, sizeof(legic_card_select_t
));
792 void legic_chk_iv(uint32_t *iv
) {
793 if ((*iv
& 0x7F) != *iv
) {
795 PrintAndLogEx(INFO
, "Truncating IV to 7bits, %u", *iv
);
798 if ((*iv
& 1) == 0) {
800 PrintAndLogEx(INFO
, "LSB of IV must be SET %u", *iv
);
804 void legic_seteml(uint8_t *src
, uint32_t offset
, uint32_t numofbytes
) {
806 PrintAndLogEx(INFO
, "Uploading to emulator memory");
807 PrintAndLogEx(INFO
, "." NOLF
);
810 g_conn
.block_after_ACK
= true;
811 for (size_t i
= offset
; i
< numofbytes
; i
+= LEGIC_PACKET_SIZE
) {
813 size_t len
= MIN((numofbytes
- i
), LEGIC_PACKET_SIZE
);
814 if (len
== numofbytes
- i
) {
815 // Disable fast mode on last packet
816 g_conn
.block_after_ACK
= false;
819 legic_packet_t
*payload
= calloc(1, sizeof(legic_packet_t
) + len
);
822 memcpy(payload
->data
, src
+ i
, len
);
824 clearCommandBuffer();
825 SendCommandNG(CMD_HF_LEGIC_ESET
, (uint8_t *)payload
, sizeof(legic_packet_t
) + len
);
827 PrintAndLogEx(NORMAL
, "." NOLF
);
830 PrintAndLogEx(NORMAL
, "");
831 PrintAndLogEx(SUCCESS
, "uploaded " _YELLOW_("%d") " bytes to emulator memory", numofbytes
);
834 static int CmdLegicReader(const char *Cmd
) {
835 CLIParserContext
*ctx
;
836 CLIParserInit(&ctx
, "hf legic reader",
837 "Read UID and type information from a LEGIC Prime tag",
842 arg_lit0("@", NULL
, "optional - continuous reader mode"),
845 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
846 bool cm
= arg_get_lit(ctx
, 1);
850 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
853 return readLegicUid(cm
, true);
856 static int CmdLegicDump(const char *Cmd
) {
857 CLIParserContext
*ctx
;
858 CLIParserInit(&ctx
, "hf legic dump",
859 "Read all memory from LEGIC Prime tags and saves to (bin/json) dump file\n"
860 "It autodetects card type (MIM22, MIM256, MIM1024)",
861 "hf legic dump --> use UID as filename\n"
862 "hf legic dump -f myfile \n"
863 "hf legic dump --de --> use UID as filename and deobfuscate data");
867 arg_str0("f", "file", "<fn>", "Dump filename"),
868 arg_lit0(NULL
, "de", "deobfuscate dump data (xor with MCC)"),
871 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
874 char filename
[FILE_PATH_SIZE
] = {0};
875 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
877 bool shall_deobsfuscate
= arg_get_lit(ctx
, 2);
881 legic_card_select_t card
;
882 if (legic_get_type(&card
) != PM3_SUCCESS
) {
883 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
886 uint16_t dumplen
= card
.cardsize
;
888 legic_print_type(dumplen
, 0);
889 PrintAndLogEx(SUCCESS
, "Reading tag memory." NOLF
);
891 legic_packet_t
*payload
= calloc(1, sizeof(legic_packet_t
));
894 payload
->len
= dumplen
;
896 clearCommandBuffer();
897 SendCommandNG(CMD_HF_LEGIC_READER
, (uint8_t *)payload
, sizeof(legic_packet_t
));
899 PacketResponseNG resp
;
902 while (WaitForResponseTimeout(CMD_HF_LEGIC_READER
, &resp
, 2000) == false) {
904 PrintAndLogEx(NORMAL
, "." NOLF
);
906 PrintAndLogEx(WARNING
, "\ncommand execution time out");
910 PrintAndLogEx(NORMAL
, "");
912 if (resp
.status
!= PM3_SUCCESS
) {
913 PrintAndLogEx(WARNING
, "Failed dumping tag data");
917 uint16_t readlen
= resp
.data
.asDwords
[0];
918 uint8_t *data
= calloc(readlen
, sizeof(uint8_t));
920 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
924 if (readlen
!= dumplen
)
925 PrintAndLogEx(WARNING
, "Fail, only managed to read 0x%02X bytes of 0x%02X", readlen
, dumplen
);
927 // copy data from device
928 if (GetFromDevice(BIG_BUF_EML
, data
, readlen
, 0, NULL
, 0, NULL
, 2500, false) == false) {
929 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
934 if (shall_deobsfuscate
) {
935 // Deobfuscate the whole dump. Unused data (after the last sector) will be MCC since
936 // 0x00 ^ MCC = MCC. Finding the end of used data is not part of this function.
937 if (legic_xor(data
, dumplen
) == false) {
938 PrintAndLogEx(FAILED
, "Deobsfuscate failed, exiting...");
939 PrintAndLogEx(HINT
, "Try running command without `--de` parameter");
945 // user supplied filename?
947 PrintAndLogEx(INFO
, "Using UID as filename");
948 strcat(filename
, "hf-legic-");
949 FillFileNameByUID(filename
, data
, "-dump", 4);
952 pm3_save_dump(filename
, data
, readlen
, jsfLegic_v2
);
957 static int CmdLegicRestore(const char *Cmd
) {
958 CLIParserContext
*ctx
;
959 CLIParserInit(&ctx
, "hf legic restore",
960 "Reads (bin/eml/json) file and it autodetects card type and verifies that the file has the same size\n"
961 "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]",
962 "hf legic restore -f myfile --> use user specified filename\n"
963 "hf legic restore -f myfile --ob --> use UID as filename and obfuscate data");
967 arg_str1("f", "file", "<fn>", "Specify a filename to restore"),
968 arg_lit0(NULL
, "ob", "obfuscate dump data (xor with MCC)"),
971 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
974 char filename
[FILE_PATH_SIZE
] = {0};
975 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
977 bool shall_obsfuscate
= arg_get_lit(ctx
, 2);
981 legic_card_select_t card
;
982 if (legic_get_type(&card
) != PM3_SUCCESS
) {
983 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
987 legic_print_type(card
.cardsize
, 0);
990 uint8_t *dump
= NULL
;
991 size_t bytes_read
= 0;
992 int res
= pm3_load_dump(filename
, (void **)&dump
, &bytes_read
, LEGIC_PRIME_MIM1024
);
993 if (res
!= PM3_SUCCESS
) {
998 if (card
.cardsize
!= bytes_read
) {
999 PrintAndLogEx(WARNING
, "Fail, filesize and cardsize is not equal. [%u != %zu]", card
.cardsize
, bytes_read
);
1004 if (shall_obsfuscate
) {
1005 if (legic_xor(dump
, card
.cardsize
) == false) {
1006 PrintAndLogEx(FAILED
, "Obsfuscate failed, exiting...");
1007 PrintAndLogEx(HINT
, "Try running command without `--ob` parameter");
1013 PrintAndLogEx(SUCCESS
, "Restoring to card");
1016 g_conn
.block_after_ACK
= true;
1018 // transfer to device
1019 PacketResponseNG resp
;
1020 // 7 = skip UID bytes and MCC
1021 for (size_t i
= 7; i
< bytes_read
; i
+= LEGIC_PACKET_SIZE
) {
1023 size_t len
= MIN((bytes_read
- i
), LEGIC_PACKET_SIZE
);
1024 if (len
== bytes_read
- i
) {
1025 // Disable fast mode on last packet
1026 g_conn
.block_after_ACK
= false;
1029 legic_packet_t
*payload
= calloc(1, sizeof(legic_packet_t
) + len
);
1030 payload
->offset
= i
;
1033 memcpy(payload
->data
, dump
+ i
, len
);
1035 clearCommandBuffer();
1036 SendCommandNG(CMD_HF_LEGIC_WRITER
, (uint8_t *)payload
, sizeof(legic_packet_t
) + len
);
1039 uint8_t timeout
= 0;
1040 while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER
, &resp
, 2000) == false) {
1042 PrintAndLogEx(NORMAL
, "." NOLF
);
1044 PrintAndLogEx(WARNING
, "\ncommand execution time out");
1046 return PM3_ETIMEOUT
;
1049 PrintAndLogEx(NORMAL
, "");
1051 if (resp
.status
!= PM3_SUCCESS
) {
1052 PrintAndLogEx(WARNING
, "Failed writing tag");
1054 return PM3_ERFTRANS
;
1056 PrintAndLogEx(SUCCESS
, "Wrote chunk [offset %zu | len %zu | total %zu", i
, len
, i
+ len
);
1060 PrintAndLogEx(SUCCESS
, "Done!");
1064 static int CmdLegicELoad(const char *Cmd
) {
1065 CLIParserContext
*ctx
;
1066 CLIParserInit(&ctx
, "hf legic eload",
1067 "Loads a LEGIC Prime dump file into emulator memory",
1068 "hf legic eload -f myfile\n"
1069 "hf legic eload -f myfile --obfuscate\n"
1072 void *argtable
[] = {
1074 arg_str1("f", "file", "<fn>", "Filename to load"),
1075 arg_lit0(NULL
, "obfuscate", "Obfuscate dump data (xor with MCC)"),
1078 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1081 char filename
[FILE_PATH_SIZE
] = {0};
1082 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
1084 bool shall_obsfuscate
= arg_get_lit(ctx
, 2);
1088 uint8_t *dump
= NULL
;
1089 size_t bytes_read
= 0;
1090 int res
= pm3_load_dump(filename
, (void **)&dump
, &bytes_read
, LEGIC_PRIME_MIM1024
);
1091 if (res
!= PM3_SUCCESS
) {
1096 if (bytes_read
!= LEGIC_PRIME_MIM22
&&
1097 bytes_read
!= LEGIC_PRIME_MIM256
&&
1098 bytes_read
!= LEGIC_PRIME_MIM1024
) {
1099 PrintAndLogEx(ERR
, "File content error. Read %zu bytes", bytes_read
);
1104 if (shall_obsfuscate
) {
1105 legic_xor(dump
, bytes_read
);
1108 legic_seteml(dump
, 0, bytes_read
);
1112 PrintAndLogEx(HINT
, "You are ready to simulate. See " _YELLOW_("`hf legic sim -h`"));
1113 PrintAndLogEx(SUCCESS
, "Done!");
1117 static int CmdLegicESave(const char *Cmd
) {
1118 CLIParserContext
*ctx
;
1119 CLIParserInit(&ctx
, "hf legic esave",
1120 "Saves a (bin/json) dump file of emulator memory",
1121 "hf legic esave --> uses UID as filename\n"
1122 "hf legic esave -f myfile --22\n"
1123 "hf legic esave -f myfile --22 --de\n"
1126 void *argtable
[] = {
1128 arg_str0("f", "file", "<fn>", "Filename to save"),
1129 arg_lit0(NULL
, "22", "LEGIC Prime MIM22"),
1130 arg_lit0(NULL
, "256", "LEGIC Prime MIM256 (def)"),
1131 arg_lit0(NULL
, "1024", "LEGIC Prime MIM1024"),
1132 arg_lit0(NULL
, "de", "De-obfuscate dump data (xor with MCC)"),
1135 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1138 char filename
[FILE_PATH_SIZE
] = {0};
1139 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
1141 bool m1
= arg_get_lit(ctx
, 2);
1142 bool m2
= arg_get_lit(ctx
, 3);
1143 bool m3
= arg_get_lit(ctx
, 4);
1144 bool shall_deobsfuscate
= arg_get_lit(ctx
, 5);
1148 if (m1
+ m2
+ m3
> 1) {
1149 PrintAndLogEx(WARNING
, "Only specify one LEGIC Prime Type");
1151 } else if (m1
+ m2
+ m3
== 0) {
1155 size_t numofbytes
= LEGIC_PRIME_MIM256
;
1157 numofbytes
= LEGIC_PRIME_MIM22
;
1159 numofbytes
= LEGIC_PRIME_MIM256
;
1161 numofbytes
= LEGIC_PRIME_MIM1024
;
1164 uint8_t *data
= calloc(numofbytes
, sizeof(uint8_t));
1166 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1170 // download emulator memory
1171 PrintAndLogEx(SUCCESS
, "Reading emulator memory...");
1172 if (GetFromDevice(BIG_BUF_EML
, data
, numofbytes
, 0, NULL
, 0, NULL
, 2500, false) == false) {
1173 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
1175 return PM3_ETIMEOUT
;
1178 // user supplied filename?
1180 PrintAndLogEx(INFO
, "Using UID as filename");
1181 strcat(filename
, "hf-legic-");
1182 FillFileNameByUID(filename
, data
, "-dump", 4);
1185 if (shall_deobsfuscate
) {
1186 legic_xor(data
, numofbytes
);
1189 pm3_save_dump(filename
, data
, numofbytes
, jsfLegic_v2
);
1193 static int CmdLegicEView(const char *Cmd
) {
1195 CLIParserContext
*ctx
;
1196 CLIParserInit(&ctx
, "hf legic eview",
1197 "It displays emulator memory",
1199 "hf legic eview --22\n"
1201 void *argtable
[] = {
1203 arg_lit0(NULL
, "22", "LEGIC Prime MIM22"),
1204 arg_lit0(NULL
, "256", "LEGIC Prime MIM256 (def)"),
1205 arg_lit0(NULL
, "1024", "LEGIC Prime MIM1024"),
1206 arg_lit0("v", "verbose", "verbose output"),
1209 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1211 bool m1
= arg_get_lit(ctx
, 1);
1212 bool m2
= arg_get_lit(ctx
, 2);
1213 bool m3
= arg_get_lit(ctx
, 3);
1214 bool verbose
= arg_get_lit(ctx
, 4);
1218 if (m1
+ m2
+ m3
> 1) {
1219 PrintAndLogEx(WARNING
, "Only specify one LEGIC Prime Type");
1221 } else if (m1
+ m2
+ m3
== 0) {
1225 size_t bytes
= LEGIC_PRIME_MIM256
;
1227 bytes
= LEGIC_PRIME_MIM22
;
1229 bytes
= LEGIC_PRIME_MIM256
;
1231 bytes
= LEGIC_PRIME_MIM1024
;
1233 uint8_t *dump
= calloc(bytes
, sizeof(uint8_t));
1235 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1239 PrintAndLogEx(INFO
, "downloading emulator memory");
1240 if (GetFromDevice(BIG_BUF_EML
, dump
, bytes
, 0, NULL
, 0, NULL
, 2500, false) == false) {
1241 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
1243 return PM3_ETIMEOUT
;
1247 PrintAndLogEx(NORMAL
, "");
1248 PrintAndLogEx(INFO
, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii");
1249 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
1250 print_hex_break(dump
, bytes
, 16);
1253 PrintAndLogEx(NORMAL
, "");
1254 decode_and_print_memory(bytes
, dump
);
1260 static int CmdLegicEInfo(const char *Cmd
) {
1262 CLIParserContext
*ctx
;
1263 CLIParserInit(&ctx
, "hf legic einfo",
1264 "It decodes and displays emulator memory",
1266 "hf legic eview --22\n"
1268 void *argtable
[] = {
1270 arg_lit0(NULL
, "22", "LEGIC Prime MIM22"),
1271 arg_lit0(NULL
, "256", "LEGIC Prime MIM256 (def)"),
1272 arg_lit0(NULL
, "1024", "LEGIC Prime MIM1024"),
1275 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1276 bool m1
= arg_get_lit(ctx
, 1);
1277 bool m2
= arg_get_lit(ctx
, 2);
1278 bool m3
= arg_get_lit(ctx
, 3);
1282 if (m1
+ m2
+ m3
> 1) {
1283 PrintAndLogEx(WARNING
, "Only specify one LEGIC Prime Type");
1285 } else if (m1
+ m2
+ m3
== 0) {
1289 size_t card_size
= LEGIC_PRIME_MIM256
;
1291 card_size
= LEGIC_PRIME_MIM22
;
1293 card_size
= LEGIC_PRIME_MIM256
;
1295 card_size
= LEGIC_PRIME_MIM1024
;
1297 uint8_t *dump
= calloc(card_size
, sizeof(uint8_t));
1299 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1303 PrintAndLogEx(INFO
, "downloading emulator memory");
1304 if (GetFromDevice(BIG_BUF_EML
, dump
, card_size
, 0, NULL
, 0, NULL
, 2500, false) == false) {
1305 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
1307 return PM3_ETIMEOUT
;
1310 decode_and_print_memory(card_size
, dump
);
1316 static int CmdLegicWipe(const char *Cmd
) {
1317 CLIParserContext
*ctx
;
1318 CLIParserInit(&ctx
, "hf legic wipe",
1319 "Fills a LEGIC Prime tags memory with zeros. From byte7 and to the end\n"
1320 "It autodetects card type",
1323 void *argtable
[] = {
1327 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1331 legic_card_select_t card
;
1332 if (legic_get_type(&card
) != PM3_SUCCESS
) {
1333 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
1338 uint8_t *data
= calloc(card
.cardsize
, sizeof(uint8_t));
1340 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1344 legic_print_type(card
.cardsize
, 0);
1346 PrintAndLogEx(SUCCESS
, "Erasing");
1348 g_conn
.block_after_ACK
= true;
1350 // transfer to device
1351 PacketResponseNG resp
;
1352 for (size_t i
= 7; i
< card
.cardsize
; i
+= LEGIC_PACKET_SIZE
) {
1354 PrintAndLogEx(NORMAL
, "." NOLF
);
1356 size_t len
= MIN((card
.cardsize
- i
), LEGIC_PACKET_SIZE
);
1357 if (len
== card
.cardsize
- i
) {
1358 // Disable fast mode on last packet
1359 g_conn
.block_after_ACK
= false;
1362 legic_packet_t
*payload
= calloc(1, sizeof(legic_packet_t
) + len
);
1363 payload
->offset
= i
;
1366 memcpy(payload
->data
, data
+ i
, len
);
1368 clearCommandBuffer();
1369 SendCommandNG(CMD_HF_LEGIC_WRITER
, (uint8_t *)payload
, sizeof(legic_packet_t
) + len
);
1372 uint8_t timeout
= 0;
1373 while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER
, &resp
, 2000) == false) {
1375 PrintAndLogEx(NORMAL
, "." NOLF
);
1377 PrintAndLogEx(WARNING
, "\ncommand execution time out");
1379 return PM3_ETIMEOUT
;
1382 PrintAndLogEx(NORMAL
, "");
1384 if (resp
.status
!= PM3_SUCCESS
) {
1385 PrintAndLogEx(WARNING
, "failed writing tag");
1387 return PM3_ERFTRANS
;
1390 PrintAndLogEx(SUCCESS
, "Done!\n");
1395 static int CmdLegicList(const char *Cmd
) {
1396 return CmdTraceListAlias(Cmd
, "hf legic", "legic");
1399 static int CmdLegicView(const char *Cmd
) {
1400 CLIParserContext
*ctx
;
1401 CLIParserInit(&ctx
, "hf legic view",
1402 "Print a LEGIC Prime dump file (bin/eml/json)",
1403 "hf legic view -f hf-legic-01020304-dump.bin"
1405 void *argtable
[] = {
1407 arg_str1("f", "file", "<fn>", "Specify a filename for dump file"),
1408 arg_lit0("v", "verbose", "verbose output"),
1411 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
1413 char filename
[FILE_PATH_SIZE
];
1414 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
1415 bool verbose
= arg_get_lit(ctx
, 2);
1419 uint8_t *dump
= NULL
;
1420 size_t bytes_read
= 0;
1421 int res
= pm3_load_dump(filename
, (void **)&dump
, &bytes_read
, LEGIC_PRIME_MIM1024
);
1422 if (res
!= PM3_SUCCESS
) {
1427 PrintAndLogEx(NORMAL
, "");
1428 PrintAndLogEx(INFO
, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii");
1429 PrintAndLogEx(INFO
, "---+-------------------------------------------------+-----------------");
1430 print_hex_break(dump
, bytes_read
, 16);
1433 PrintAndLogEx(NORMAL
, "");
1434 decode_and_print_memory(bytes_read
, dump
);
1439 static command_t CommandTable
[] = {
1440 {"-----------", CmdHelp
, AlwaysAvailable
, "--------------------- " _CYAN_("operations") " ---------------------"},
1441 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
1442 {"dump", CmdLegicDump
, IfPm3Legicrf
, "Dump LEGIC Prime tag to binary file"},
1443 {"info", CmdLegicInfo
, IfPm3Legicrf
, "Display deobfuscated and decoded LEGIC Prime tag data"},
1444 {"list", CmdLegicList
, AlwaysAvailable
, "List LEGIC history"},
1445 {"rdbl", CmdLegicRdbl
, IfPm3Legicrf
, "Read bytes from a LEGIC Prime tag"},
1446 {"reader", CmdLegicReader
, IfPm3Legicrf
, "LEGIC Prime Reader UID and tag info"},
1447 {"restore", CmdLegicRestore
, IfPm3Legicrf
, "Restore a dump file onto a LEGIC Prime tag"},
1448 {"wipe", CmdLegicWipe
, IfPm3Legicrf
, "Wipe a LEGIC Prime tag"},
1449 {"wrbl", CmdLegicWrbl
, IfPm3Legicrf
, "Write data to a LEGIC Prime tag"},
1450 {"-----------", CmdHelp
, AlwaysAvailable
, "--------------------- " _CYAN_("simulation") " ---------------------"},
1451 {"sim", CmdLegicSim
, IfPm3Legicrf
, "Start tag simulator"},
1452 {"eload", CmdLegicELoad
, IfPm3Legicrf
, "Upload file into emulator memory"},
1453 {"esave", CmdLegicESave
, IfPm3Legicrf
, "Save emulator memory to file"},
1454 {"eview", CmdLegicEView
, IfPm3Legicrf
, "View emulator memory"},
1455 {"einfo", CmdLegicEInfo
, IfPm3Legicrf
, "Display deobfuscated and decoded emulator memory"},
1456 {"-----------", CmdHelp
, AlwaysAvailable
, "--------------------- " _CYAN_("utils") " ---------------------"},
1457 {"crc", CmdLegicCalcCrc
, AlwaysAvailable
, "Calculate Legic CRC over given bytes"},
1458 {"view", CmdLegicView
, AlwaysAvailable
, "Display deobfuscated and decoded content from tag dump file"},
1459 {NULL
, NULL
, NULL
, NULL
}
1462 static int CmdHelp(const char *Cmd
) {
1463 (void)Cmd
; // Cmd is not used so far
1464 CmdsHelp(CommandTable
);
1468 int CmdHFLegic(const char *Cmd
) {
1469 clearCommandBuffer();
1470 return CmdsParse(CommandTable
, Cmd
);
1473 int readLegicUid(bool loop
, bool verbose
) {
1476 legic_card_select_t card
;
1478 int resp
= legic_get_type(&card
);
1481 if (resp
!= PM3_SUCCESS
) {
1489 if (verbose
) PrintAndLogEx(WARNING
, "command execution time out");
1490 return PM3_ETIMEOUT
;
1492 if (verbose
) PrintAndLogEx(WARNING
, "legic card select failed");
1499 PrintAndLogEx(NORMAL
, "");
1500 PrintAndLogEx(SUCCESS
, " MCD: " _GREEN_("%02X"), card
.uid
[0]);
1501 PrintAndLogEx(SUCCESS
, " MSN: " _GREEN_("%s"), sprint_hex(card
.uid
+ 1, sizeof(card
.uid
) - 1));
1502 legic_print_type(card
.cardsize
, 0);
1504 } while (loop
&& kbd_enter_pressed() == false);