1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
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
7 //-----------------------------------------------------------------------------
8 // High frequency Legic commands
9 //-----------------------------------------------------------------------------
10 #include "cmdhflegic.h"
12 #include <stdio.h> // for Mingw readline
13 #include <ctype.h> // tolower
16 #include <readline/readline.h>
19 #include "cliparser.h"
20 #include "cmdparser.h" // command_t
21 #include "comms.h" // clearCommandBuffer
25 #include "fileutils.h" //saveFile
27 static int CmdHelp(const char *Cmd
);
29 #define MAX_LENGTH 1024
31 static bool legic_xor(uint8_t *data
, uint16_t cardsize
) {
34 PrintAndLogEx(INFO
, "No obsfuscation such small dump");
38 uint8_t crc
= data
[4];
39 uint32_t calc_crc
= CRC8Legic(data
, 4);
40 if (crc
!= calc_crc
) {
41 PrintAndLogEx(INFO
, "Crc mismatch, obsfuscation not possible");
45 for (uint16_t i
= 22; i
< cardsize
; i
++) {
48 PrintAndLogEx(SUCCESS
, "applying xoring of data done!");
53 * Output BigBuf and deobfuscate LEGIC RF tag data.
54 * This is based on information given in the talk held
55 * by Henryk Ploetz and Karsten Nohl at 26c3
57 static int CmdLegicInfo(const char *Cmd
) {
58 CLIParserContext
*ctx
;
59 CLIParserInit(&ctx
, "hf legic info",
60 "Gets information from a LEGIC Prime tag like systemarea, user areas, etc",
67 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
70 int i
= 0, k
= 0, segmentNum
= 0, segment_len
= 0, segment_flag
= 0;
71 int crc
= 0, wrp
= 0, wrc
= 0;
72 uint8_t stamp_len
= 0;
74 char token_type
[6] = {0, 0, 0, 0, 0, 0};
79 legic_card_select_t card
;
80 if (legic_get_type(&card
) != PM3_SUCCESS
) {
81 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
85 PrintAndLogEx(SUCCESS
, "Reading full tag memory of " _YELLOW_("%d") " bytes...", card
.cardsize
);
87 // allocate receiver buffer
88 uint8_t *data
= calloc(card
.cardsize
, sizeof(uint8_t));
90 PrintAndLogEx(WARNING
, "Cannot allocate memory");
94 int status
= legic_read_mem(0, card
.cardsize
, 0x55, data
, &datalen
);
95 if (status
!= PM3_SUCCESS
) {
96 PrintAndLogEx(WARNING
, "Failed reading memory");
101 // Output CDF System area (9 bytes) plus remaining header area (12 bytes)
103 uint32_t calc_crc
= CRC8Legic(data
, 4);
105 PrintAndLogEx(SUCCESS
, " " _CYAN_("CDF: System Area"));
106 PrintAndLogEx(NORMAL
, "------------------------------------------------------");
107 PrintAndLogEx(SUCCESS
, "MCD: " _GREEN_("%02X") " MSN: " _GREEN_("%s") " MCC: " _GREEN_("%02X") " (%s)",
109 sprint_hex(data
+ 1, 3),
111 (calc_crc
== crc
) ? _GREEN_("OK") : _RED_("Fail")
114 // MCD = Manufacturer ID (should be list meaning something?)
117 dcf
= ((int)data
[6] << 8) | (int)data
[5];
119 // New unwritten media?
122 PrintAndLogEx(SUCCESS
, "DCF: %d (%02x %02x), Token Type=NM (New Media)",
128 } else if (dcf
> 60000) { // Master token?
132 if (data
[6] == 0xec) {
133 strncpy(token_type
, "XAM", sizeof(token_type
) - 1);
135 stamp_len
= 0x0c - (data
[5] >> 4);
137 switch (data
[5] & 0x7f) {
139 strncpy(token_type
, "IAM", sizeof(token_type
) - 1);
140 fl
= (0x2f - (data
[5] & 0x7f)) + 1;
143 strncpy(token_type
, "SAM", sizeof(token_type
) - 1);
144 fl
= (0x6f - (data
[5] & 0x7f)) + 1;
147 strncpy(token_type
, "GAM", sizeof(token_type
) - 1);
148 fl
= (0x7f - (data
[5] & 0x7f)) + 1;
152 stamp_len
= 0xfc - data
[6];
155 PrintAndLogEx(SUCCESS
, "DCF: %d (%02x %02x), Token Type=" _YELLOW_("%s") " (OLE=%01u), OL=%02u, FL=%02u",
160 (data
[5] & 0x80) >> 7,
165 } else { // Is IM(-S) type of card...
167 if (data
[7] == 0x9F && data
[8] == 0xFF) {
169 strncpy(token_type
, "IM-S", sizeof(token_type
) - 1);
171 strncpy(token_type
, "IM", sizeof(token_type
) - 1);
174 PrintAndLogEx(SUCCESS
, "DCF: %d (%02x %02x), Token Type = %s (OLE = %01u)",
179 (data
[5] & 0x80) >> 7
183 // Makes no sence to show this on blank media...
187 PrintAndLogEx(SUCCESS
, "WRP = %02u, WRC = %01u, RD = %01u, SSC = %02X",
189 (data
[7] & 0x70) >> 4,
190 (data
[7] & 0x80) >> 7,
195 // Header area is only available on IM-S cards, on master tokens this data is the master token data itself
196 if (bIsSegmented
|| dcf
> 60000) {
198 PrintAndLogEx(SUCCESS
, "Master token data");
199 PrintAndLogEx(SUCCESS
, "%s", sprint_hex(data
+ 8, 14));
201 PrintAndLogEx(SUCCESS
, "Remaining Header Area");
202 PrintAndLogEx(SUCCESS
, "%s", sprint_hex(data
+ 9, 13));
206 PrintAndLogEx(NORMAL
, "------------------------------------------------------");
208 uint8_t segCrcBytes
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
209 uint32_t segCalcCRC
= 0;
216 PrintAndLogEx(SUCCESS
, _CYAN_("ADF: User Area"));
217 PrintAndLogEx(NORMAL
, "------------------------------------------------------");
221 // Data start point on segmented cards
225 for (segmentNum
= 1; segmentNum
< 128; segmentNum
++) {
226 segment_len
= ((data
[i
+ 1] ^ crc
) & 0x0f) * 256 + (data
[i
] ^ crc
);
227 segment_flag
= ((data
[i
+ 1] ^ crc
) & 0xf0) >> 4;
228 wrp
= (data
[i
+ 2] ^ crc
);
229 wrc
= ((data
[i
+ 3] ^ crc
) & 0x70) >> 4;
231 bool hasWRC
= (wrc
> 0);
232 bool hasWRP
= (wrp
> wrc
);
233 int wrp_len
= (wrp
- wrc
);
234 int remain_seg_payload_len
= (segment_len
- wrp
- 5);
236 // validate segment-crc
237 segCrcBytes
[0] = data
[0]; //uid0
238 segCrcBytes
[1] = data
[1]; //uid1
239 segCrcBytes
[2] = data
[2]; //uid2
240 segCrcBytes
[3] = data
[3]; //uid3
241 segCrcBytes
[4] = (data
[i
] ^ crc
); //hdr0
242 segCrcBytes
[5] = (data
[i
+ 1] ^ crc
); //hdr1
243 segCrcBytes
[6] = (data
[i
+ 2] ^ crc
); //hdr2
244 segCrcBytes
[7] = (data
[i
+ 3] ^ crc
); //hdr3
246 segCalcCRC
= CRC8Legic(segCrcBytes
, 8);
247 segCRC
= data
[i
+ 4] ^ crc
;
249 PrintAndLogEx(SUCCESS
, "Segment | " _YELLOW_("%02u"), segmentNum
);
250 PrintAndLogEx(SUCCESS
, "raw header | 0x%02X 0x%02X 0x%02X 0x%02X",
256 PrintAndLogEx(SUCCESS
, "Segment len | %u, Flag: 0x%X (valid:%01u, last:%01u)",
259 (segment_flag
& 0x4) >> 2,
260 (segment_flag
& 0x8) >> 3
262 PrintAndLogEx(SUCCESS
, " | WRP: %02u, WRC: %02u, RD: %01u, CRC: 0x%02X (%s)",
265 ((data
[i
+ 3] ^ crc
) & 0x80) >> 7,
267 (segCRC
== segCalcCRC
) ? _GREEN_("OK") : _RED_("Fail")
273 PrintAndLogEx(SUCCESS
, "\nWRC protected area: (I %d | K %d| WRC %d)", i
, k
, wrc
);
274 PrintAndLogEx(NORMAL
, "\nrow | data");
275 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------");
277 for (k
= i
; k
< (i
+ wrc
); ++k
)
280 print_hex_break(data
+ i
, wrc
, 16);
281 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------\n");
286 PrintAndLogEx(SUCCESS
, "Remaining write protected area: (I %d | K %d | WRC %d | WRP %d WRP_LEN %d)", i
, k
, wrc
, wrp
, wrp_len
);
287 PrintAndLogEx(NORMAL
, "\nrow | data");
288 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------");
290 for (k
= i
; k
< (i
+ wrp_len
); ++k
)
293 print_hex_break(data
+ i
, wrp_len
, 16);
294 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------\n");
297 // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...)
299 PrintAndLogEx(SUCCESS
, "Card ID: " _YELLOW_("%2X%02X%02X"),
306 if (remain_seg_payload_len
> 0) {
307 PrintAndLogEx(SUCCESS
, "Remaining segment payload: (I %d | K %d | Remain LEN %d)", i
, k
, remain_seg_payload_len
);
308 PrintAndLogEx(NORMAL
, "\nrow | data");
309 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------");
311 for (k
= i
; k
< (i
+ remain_seg_payload_len
); ++k
)
314 print_hex_break(data
+ i
, remain_seg_payload_len
, 16);
315 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------\n");
316 i
+= remain_seg_payload_len
;
318 // end with last segment
319 if (segment_flag
& 0x8)
326 // Data start point on unsegmented cards
329 wrp
= data
[7] & 0x0F;
330 wrc
= (data
[7] & 0x70) >> 4;
332 bool hasWRC
= (wrc
> 0);
333 bool hasWRP
= (wrp
> wrc
);
334 int wrp_len
= (wrp
- wrc
);
335 int remain_seg_payload_len
= (card
.cardsize
- 22 - wrp
);
337 PrintAndLogEx(SUCCESS
, "Unsegmented card - WRP: %02u, WRC: %02u, RD: %01u",
340 (data
[7] & 0x80) >> 7
344 PrintAndLogEx(SUCCESS
, "WRC protected area: (I %d | WRC %d)", i
, wrc
);
345 PrintAndLogEx(NORMAL
, "\nrow | data");
346 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------");
347 print_hex_break(data
+ i
, wrc
, 16);
348 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------\n");
353 PrintAndLogEx(SUCCESS
, "Remaining write protected area: (I %d | WRC %d | WRP %d | WRP_LEN %d)", i
, wrc
, wrp
, wrp_len
);
354 PrintAndLogEx(NORMAL
, "\nrow | data");
355 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------");
356 print_hex_break(data
+ i
, wrp_len
, 16);
357 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------\n");
360 // Q: does this one work?
361 // A: Only if KGH/BGH is used with BCD encoded card number. Maybe this will show just garbage
363 PrintAndLogEx(SUCCESS
, "Card ID: " _YELLOW_("%2X%02X%02X"),
371 if (remain_seg_payload_len
> 0) {
372 PrintAndLogEx(SUCCESS
, "Remaining segment payload: (I %d | Remain LEN %d)", i
, remain_seg_payload_len
);
373 PrintAndLogEx(NORMAL
, "\nrow | data");
374 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------");
375 print_hex_break(data
+ i
, remain_seg_payload_len
, 16);
376 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------\n");
386 // offset in data memory
387 // number of bytes to read
388 static int CmdLegicRdbl(const char *Cmd
) {
389 CLIParserContext
*ctx
;
390 CLIParserInit(&ctx
, "hf legic rdbl",
391 "Read data from a LEGIC Prime tag",
392 "hf legic rdbl -o 0 -l 16 -> reads from byte[0] 16 bytes(system header)\n"
393 "hf legic rdbl -o 0 -l 4 --iv 55 -> reads from byte[0] 4 bytes with IV 0x55\n"
394 "hf legic rdbl -o 0 -l 256 --iv 55 -> reads from byte[0] 256 bytes with IV 0x55");
398 arg_int1("o", "offset", "<dec>", "offset in data array to start download from"),
399 arg_int1("l", "length", "<dec>", "number of bytes to read"),
400 arg_str0(NULL
, "iv", "<hex>", "Initialization vector to use. Must be odd and 7bits max"),
403 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
405 int offset
= arg_get_int_def(ctx
, 1, 0);
407 int len
= arg_get_int_def(ctx
, 2, 0);
410 uint8_t iv
[1] = {0x01}; // formerly uidcrc
412 CLIGetHexWithReturn(ctx
, 3, iv
, &iv_len
);
417 if (len
+ offset
>= MAX_LENGTH
) {
418 PrintAndLogEx(WARNING
, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", MAX_LENGTH
, len
+ offset
);
419 return PM3_EOUTOFBOUND
;
422 PrintAndLogEx(SUCCESS
, "Reading %d bytes, from offset %d", len
, offset
);
424 // allocate receiver buffer
425 uint8_t *data
= calloc(len
, sizeof(uint8_t));
427 PrintAndLogEx(WARNING
, "Cannot allocate memory");
431 uint16_t datalen
= 0;
432 int status
= legic_read_mem(offset
, len
, iv
[0], data
, &datalen
);
433 if (status
== PM3_SUCCESS
) {
434 PrintAndLogEx(NORMAL
, " ## | 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F");
435 PrintAndLogEx(NORMAL
, "-----+------------------------------------------------------------------------------------------------");
436 print_hex_break(data
, datalen
, 32);
442 static int CmdLegicSim(const char *Cmd
) {
443 CLIParserContext
*ctx
;
444 CLIParserInit(&ctx
, "hf legic sim",
445 "Simulates a LEGIC Prime tag. MIM22, MIM256, MIM1024 types can be emulated",
446 "hf legic sim -t 0 -> Simulate Type MIM22\n"
447 "hf legic sim -t 1 -> Simulate Type MIM256 (default)\n"
448 "hf legic sim -t 2 -> Simulate Type MIM1024");
452 arg_int0("t", "type", "<dec>", "Tag type to simulate."),
455 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
462 payload
.send_reply
= true;
463 payload
.tagtype
= arg_get_int_def(ctx
, 1, 1);
467 if (payload
.tagtype
> 2) {
468 PrintAndLogEx(ERR
, "Invalid tag type selected.");
472 clearCommandBuffer();
473 SendCommandNG(CMD_HF_LEGIC_SIMULATE
, (uint8_t *)&payload
, sizeof(payload
));
474 PacketResponseNG resp
;
476 PrintAndLogEx(INFO
, "Press pm3-button to abort simulation");
477 bool keypress
= kbd_enter_pressed();
478 while (keypress
== false) {
479 keypress
= kbd_enter_pressed();
481 if (WaitForResponseTimeout(CMD_HF_LEGIC_SIMULATE
, &resp
, 1500)) {
487 SendCommandNG(CMD_BREAK_LOOP
, NULL
, 0);
489 PrintAndLogEx(INFO
, "Done");
493 static int CmdLegicWrbl(const char *Cmd
) {
494 CLIParserContext
*ctx
;
495 CLIParserInit(&ctx
, "hf legic wrbl",
496 "Write data to a LEGIC Prime tag. It autodetects tagsize to ensure proper write",
497 "hf legic wrbl -o 0 -d 11223344 -> Write 0x11223344 starting from offset 0)\n"
498 "hf legic wrbl -o 10 -d DEADBEEF -> Write 0xdeadbeef starting from offset 10");
502 arg_int1("o", "offset", "<dec>", "offset in data array to start writing"),
503 arg_str1("d", "data", "<hex>", "data to write"),
504 arg_lit0(NULL
, "danger", "Auto-confirm dangerous operations"),
507 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
509 int offset
= arg_get_int_def(ctx
, 1, 0);
512 uint8_t data
[MAX_LENGTH
] = {0};
513 CLIGetHexWithReturn(ctx
, 2, data
, &dlen
);
515 bool autoconfirm
= arg_get_lit(ctx
, 3);
521 // OUT-OF-BOUNDS checks
522 // UID 4+1 bytes can't be written to.
524 PrintAndLogEx(WARNING
, "Out-of-bounds, bytes 0-1-2-3-4 can't be written to. Offset = %d", offset
);
525 return PM3_EOUTOFBOUND
;
529 legic_card_select_t card
;
530 if (legic_get_type(&card
) != PM3_SUCCESS
) {
531 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
535 legic_print_type(card
.cardsize
, 0);
537 if (dlen
+ offset
> card
.cardsize
) {
538 PrintAndLogEx(WARNING
, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", card
.cardsize
, dlen
+ offset
);
539 return PM3_EOUTOFBOUND
;
542 if ((offset
== 5 || offset
== 6) && (! autoconfirm
)) {
543 PrintAndLogEx(INFO
, "############# DANGER ################");
544 PrintAndLogEx(WARNING
, "# changing the DCF is irreversible #");
545 PrintAndLogEx(INFO
, "#####################################");
546 const char *confirm
= "Do you really want to continue? y(es)/n(o) : ";
547 bool overwrite
= false;
549 char *answer
= readline(confirm
);
550 overwrite
= (answer
[0] == 'y' || answer
[0] == 'Y');
552 PrintAndLogEx(NORMAL
, "%s" NOLF
, confirm
);
555 if (getline(&answer
, &anslen
, stdin
) > 0) {
556 overwrite
= (answer
[0] == 'y' || answer
[0] == 'Y');
558 PrintAndLogEx(NORMAL
, "");
561 if (overwrite
== false) {
562 PrintAndLogEx(WARNING
, "command cancelled");
563 return PM3_EOPABORTED
;
569 PrintAndLogEx(SUCCESS
, "Writing to tag");
571 PacketResponseNG resp
;
572 clearCommandBuffer();
573 SendCommandOLD(CMD_HF_LEGIC_WRITER
, offset
, dlen
, IV
, data
, dlen
);
576 while (!WaitForResponseTimeout(CMD_ACK
, &resp
, 2000)) {
578 PrintAndLogEx(NORMAL
, "." NOLF
);
580 PrintAndLogEx(WARNING
, "\ncommand execution time out");
584 PrintAndLogEx(NORMAL
, "");
586 uint8_t isOK
= resp
.oldarg
[0] & 0xFF;
588 PrintAndLogEx(WARNING
, "Failed writing tag");
595 static int CmdLegicCalcCrc(const char *Cmd
) {
596 CLIParserContext
*ctx
;
597 CLIParserInit(&ctx
, "hf legic crc",
598 "Calculates the legic crc8/crc16 on the given data",
599 "hf legic crc -d deadbeef1122\n"
600 "hf legic crc -d deadbeef1122 --mcc 9A -t 16 -> CRC Type 16");
604 arg_str1("d", "data", "<hex>", "bytes to calculate crc over"),
605 arg_str0(NULL
, "mcc", "<hex>", "MCC hex byte (UID CRC)"),
606 arg_int0("t", "type", "<dec>", "CRC Type (default: 8)"),
609 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
612 uint8_t data
[4096] = {0};
614 CLIGetHexWithReturn(ctx
, 1, data
, &data_len
);
617 uint8_t mcc
[1] = {0}; // formerly uidcrc
619 CLIGetHexWithReturn(ctx
, 2, mcc
, &mcc_len
);
621 int type
= arg_get_int_def(ctx
, 3, 0);
627 init_table(CRC_LEGIC
);
628 PrintAndLogEx(SUCCESS
, "Legic crc16: %X", crc16_legic(data
, data_len
, mcc
[0]));
631 PrintAndLogEx(SUCCESS
, "Legic crc8: %X", CRC8Legic(data
, data_len
));
638 int legic_read_mem(uint32_t offset
, uint32_t len
, uint32_t iv
, uint8_t *out
, uint16_t *outlen
) {
642 clearCommandBuffer();
643 SendCommandMIX(CMD_HF_LEGIC_READER
, offset
, len
, iv
, NULL
, 0);
644 PacketResponseNG resp
;
647 while (!WaitForResponseTimeout(CMD_ACK
, &resp
, 1000)) {
649 PrintAndLogEx(NORMAL
, "." NOLF
);
651 PrintAndLogEx(WARNING
, "\ncommand execution time out");
655 PrintAndLogEx(NORMAL
, "");
657 uint8_t isOK
= resp
.oldarg
[0] & 0xFF;
658 *outlen
= resp
.oldarg
[1];
660 PrintAndLogEx(WARNING
, "Failed reading tag");
665 PrintAndLogEx(WARNING
, "Fail, only managed to read %u bytes", *outlen
);
667 // copy data from device
668 if (!GetFromDevice(BIG_BUF_EML
, out
, *outlen
, 0, NULL
, 0, NULL
, 2500, false)) {
669 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
675 int legic_print_type(uint32_t tagtype
, uint8_t spaces
) {
678 char *spacer
= spc
+ (10 - spaces
);
681 PrintAndLogEx(SUCCESS
, "%sTYPE: " _YELLOW_("MIM%d card (outdated)"), spacer
, tagtype
);
682 else if (tagtype
== 256)
683 PrintAndLogEx(SUCCESS
, "%sTYPE: " _YELLOW_("MIM%d card (234 bytes)"), spacer
, tagtype
);
684 else if (tagtype
== 1024)
685 PrintAndLogEx(SUCCESS
, "%sTYPE: " _YELLOW_("MIM%d card (1002 bytes)"), spacer
, tagtype
);
687 PrintAndLogEx(INFO
, "%sTYPE: " _YELLOW_("Unknown %06x"), spacer
, tagtype
);
690 int legic_get_type(legic_card_select_t
*card
) {
692 if (card
== NULL
) return PM3_EINVARG
;
694 clearCommandBuffer();
695 SendCommandNG(CMD_HF_LEGIC_INFO
, NULL
, 0);
696 PacketResponseNG resp
;
697 if (!WaitForResponseTimeout(CMD_ACK
, &resp
, 1500))
700 uint8_t isOK
= resp
.oldarg
[0] & 0xFF;
704 memcpy(card
, resp
.data
.asBytes
, sizeof(legic_card_select_t
));
707 void legic_chk_iv(uint32_t *iv
) {
708 if ((*iv
& 0x7F) != *iv
) {
710 PrintAndLogEx(INFO
, "Truncating IV to 7bits, %u", *iv
);
713 if ((*iv
& 1) == 0) {
715 PrintAndLogEx(INFO
, "LSB of IV must be SET %u", *iv
);
718 void legic_seteml(uint8_t *src
, uint32_t offset
, uint32_t numofbytes
) {
720 conn
.block_after_ACK
= true;
721 for (size_t i
= offset
; i
< numofbytes
; i
+= PM3_CMD_DATA_SIZE
) {
723 size_t len
= MIN((numofbytes
- i
), PM3_CMD_DATA_SIZE
);
724 if (len
== numofbytes
- i
) {
725 // Disable fast mode on last packet
726 conn
.block_after_ACK
= false;
728 clearCommandBuffer();
729 SendCommandOLD(CMD_HF_LEGIC_ESET
, i
, len
, 0, src
+ i
, len
);
732 static int CmdLegicReader(const char *Cmd
) {
733 CLIParserContext
*ctx
;
734 CLIParserInit(&ctx
, "hf legic reader",
735 "Read UID and type information from a LEGIC Prime tag",
740 arg_lit0("@", NULL
, "optional - continuous reader mode"),
743 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
744 bool cm
= arg_get_lit(ctx
, 1);
748 PrintAndLogEx(INFO
, "Press " _GREEN_("<Enter>") " to exit");
751 return readLegicUid(cm
, true);
754 static int CmdLegicDump(const char *Cmd
) {
755 CLIParserContext
*ctx
;
756 CLIParserInit(&ctx
, "hf legic dump",
757 "Read all memory from LEGIC Prime MIM22, MIM256, MIM1024 and saves bin/eml/json dump file\n"
758 "It autodetects card type.",
759 "hf legic dump --> use UID as filename\n"
760 "hf legic dump -f myfile --> use user specified filename\n"
761 "hf legic dump --de --> use UID as filename and deobfuscate data");
765 arg_str0("f", "file", "<filename>", "specify a filename for dump file"),
766 arg_lit0(NULL
, "de", "deobfuscate dump data (xor with MCC)"),
769 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
772 char filename
[FILE_PATH_SIZE
] = {0};
773 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
775 bool shall_deobsfuscate
= arg_get_lit(ctx
, 2);
779 legic_card_select_t card
;
780 if (legic_get_type(&card
) != PM3_SUCCESS
) {
781 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
784 uint16_t dumplen
= card
.cardsize
;
786 legic_print_type(dumplen
, 0);
787 PrintAndLogEx(SUCCESS
, "Reading tag memory %d b...", dumplen
);
789 clearCommandBuffer();
790 SendCommandMIX(CMD_HF_LEGIC_READER
, 0x00, dumplen
, 0x55, NULL
, 0);
791 PacketResponseNG resp
;
794 while (!WaitForResponseTimeout(CMD_ACK
, &resp
, 2000)) {
796 PrintAndLogEx(NORMAL
, "." NOLF
);
798 PrintAndLogEx(WARNING
, "\ncommand execution time out");
802 PrintAndLogEx(NORMAL
, "");
804 uint8_t isOK
= resp
.oldarg
[0] & 0xFF;
806 PrintAndLogEx(WARNING
, "Failed dumping tag data");
810 uint16_t readlen
= resp
.oldarg
[1];
811 uint8_t *data
= calloc(readlen
, sizeof(uint8_t));
813 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
817 if (readlen
!= dumplen
)
818 PrintAndLogEx(WARNING
, "Fail, only managed to read 0x%02X bytes of 0x%02X", readlen
, dumplen
);
820 // copy data from device
821 if (!GetFromDevice(BIG_BUF_EML
, data
, readlen
, 0, NULL
, 0, NULL
, 2500, false)) {
822 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
827 if (shall_deobsfuscate
) {
828 // Deobfuscate the whole dump. Unused data (after the last sector) will be MCC since
829 // 0x00 ^ MCC = MCC. Finding the end of used data is not part of this function.
830 if (legic_xor(data
, dumplen
) == false) {
831 PrintAndLogEx(FAILED
, "Deobsfuscate failed, exiting...");
832 PrintAndLogEx(HINT
, "Try running command without `--de` parameter");
838 // user supplied filename?
840 PrintAndLogEx(INFO
, "Using UID as filename");
841 strcat(filename
, "hf-legic-");
842 FillFileNameByUID(filename
, data
, "-dump", 4);
845 saveFile(filename
, ".bin", data
, readlen
);
846 saveFileEML(filename
, data
, readlen
, 8);
847 saveFileJSON(filename
, jsfLegic
, data
, readlen
, NULL
);
852 static int CmdLegicRestore(const char *Cmd
) {
853 CLIParserContext
*ctx
;
854 CLIParserInit(&ctx
, "hf legic restore",
855 "Reads binary file and it autodetects card type and verifies that the file has the same size\n"
856 "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]",
857 "hf legic restore -f myfile --> use user specified filename\n"
858 "hf legic restore -f myfile --ob --> use UID as filename and obfuscate data");
862 arg_str1("f", "file", "<filename>", "specify a filename to restore"),
863 arg_lit0(NULL
, "ob", "obfuscate dump data (xor with MCC)"),
866 CLIExecWithReturn(ctx
, Cmd
, argtable
, false);
869 char filename
[FILE_PATH_SIZE
] = {0};
870 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
872 bool shall_obsfuscate
= arg_get_lit(ctx
, 2);
876 legic_card_select_t card
;
877 if (legic_get_type(&card
) != PM3_SUCCESS
) {
878 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
882 legic_print_type(card
.cardsize
, 0);
885 uint8_t *data
= calloc(card
.cardsize
, sizeof(uint8_t));
887 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
892 if (loadFile_safe(filename
, ".bin", (void **)&data
, &numofbytes
) != PM3_SUCCESS
) {
894 PrintAndLogEx(WARNING
, "Error, reading file");
898 if (card
.cardsize
!= numofbytes
) {
899 PrintAndLogEx(WARNING
, "Fail, filesize and cardsize is not equal. [%u != %zu]", card
.cardsize
, numofbytes
);
904 if (shall_obsfuscate
) {
905 if (legic_xor(data
, card
.cardsize
) == false) {
906 PrintAndLogEx(FAILED
, "Obsfuscate failed, exiting...");
907 PrintAndLogEx(HINT
, "Try running command without `--ob` parameter");
913 PrintAndLogEx(SUCCESS
, "Restoring to card");
916 conn
.block_after_ACK
= true;
918 // transfer to device
919 PacketResponseNG resp
;
920 for (size_t i
= 7; i
< numofbytes
; i
+= PM3_CMD_DATA_SIZE
) {
922 size_t len
= MIN((numofbytes
- i
), PM3_CMD_DATA_SIZE
);
923 if (len
== numofbytes
- i
) {
924 // Disable fast mode on last packet
925 conn
.block_after_ACK
= false;
927 clearCommandBuffer();
928 SendCommandOLD(CMD_HF_LEGIC_WRITER
, i
, len
, 0x55, data
+ i
, len
);
931 while (!WaitForResponseTimeout(CMD_ACK
, &resp
, 2000)) {
933 PrintAndLogEx(NORMAL
, "." NOLF
);
935 PrintAndLogEx(WARNING
, "\ncommand execution time out");
940 PrintAndLogEx(NORMAL
, "");
942 uint8_t isOK
= resp
.oldarg
[0] & 0xFF;
944 PrintAndLogEx(WARNING
, "Failed writing tag [msg = %u]", (uint8_t)(resp
.oldarg
[1] & 0xFF));
948 PrintAndLogEx(SUCCESS
, "Wrote chunk [offset %zu | len %zu | total %zu", i
, len
, i
+ len
);
952 PrintAndLogEx(SUCCESS
, "Done!");
956 static int CmdLegicELoad(const char *Cmd
) {
957 CLIParserContext
*ctx
;
958 CLIParserInit(&ctx
, "hf legic eload",
959 "Loads a LEGIC binary dump into emulator memory",
960 "hf legic eload -f myfile -t 0 -> Simulate Type MIM22\n"
961 "hf legic eload -f myfile -t 1 -> Simulate Type MIM256 (default)\n"
962 "hf legic eload -f myfile -t 2 -> Simulate Type MIM1024");
966 arg_str1("f", "file", "<filename>", "Specify a filename to restore"),
967 arg_int0("t", "type", "<dec>", "Tag type to simulate."),
968 arg_lit0(NULL
, "obfuscate", "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 size_t numofbytes
= 0;
979 switch (arg_get_int_def(ctx
, 2, 1)) {
990 PrintAndLogEx(ERR
, "Unknown tag type");
995 bool shall_obsfuscate
= arg_get_lit(ctx
, 3);
1000 uint8_t *data
= calloc(numofbytes
, sizeof(uint8_t));
1002 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1006 if (loadFile_safe(filename
, ".bin", (void **)&data
, &numofbytes
) != PM3_SUCCESS
) {
1008 PrintAndLogEx(WARNING
, "Error, reading file");
1012 if (shall_obsfuscate
) {
1013 legic_xor(data
, numofbytes
);
1016 PrintAndLogEx(SUCCESS
, "Uploading to emulator memory");
1017 legic_seteml(data
, 0, numofbytes
);
1020 PrintAndLogEx(SUCCESS
, "Done");
1024 static int CmdLegicESave(const char *Cmd
) {
1025 CLIParserContext
*ctx
;
1026 CLIParserInit(&ctx
, "hf legic esave",
1027 "Saves bin/eml/json dump file of emulator memory",
1028 "hf legic esave --> uses UID as filename\n"
1029 "hf legic esave -f myfile -t 0 --> Type MIM22\n"
1030 "hf legic esave -f myfile -t 1 --> Type MIM256 (default)\n"
1031 "hf legic esave -f myfile -t 2 --> Type MIM1024");
1033 void *argtable
[] = {
1035 arg_str0("f", "file", "<filename>", "Specify a filename to save"),
1036 arg_int0("t", "type", "<dec>", "Tag type"),
1037 arg_lit0(NULL
, "deobfuscate", "De-obfuscate dump data (xor with MCC)"),
1040 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1043 char filename
[FILE_PATH_SIZE
] = {0};
1044 CLIParamStrToBuf(arg_get_str(ctx
, 1), (uint8_t *)filename
, FILE_PATH_SIZE
, &fnlen
);
1046 size_t numofbytes
= 0;
1048 switch (arg_get_int_def(ctx
, 2, 1)) {
1059 PrintAndLogEx(ERR
, "Unknown tag type");
1064 bool shall_deobsfuscate
= arg_get_lit(ctx
, 3);
1069 uint8_t *data
= calloc(numofbytes
, sizeof(uint8_t));
1071 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1075 // download emulator memory
1076 PrintAndLogEx(SUCCESS
, "Reading emulator memory...");
1077 if (!GetFromDevice(BIG_BUF_EML
, data
, numofbytes
, 0, NULL
, 0, NULL
, 2500, false)) {
1078 PrintAndLogEx(WARNING
, "Fail, transfer from device time-out");
1080 return PM3_ETIMEOUT
;
1083 // user supplied filename?
1085 PrintAndLogEx(INFO
, "Using UID as filename");
1086 strcat(filename
, "hf-legic-");
1087 FillFileNameByUID(filename
, data
, "-dump", 4);
1090 if (shall_deobsfuscate
) {
1091 legic_xor(data
, numofbytes
);
1094 saveFile(filename
, ".bin", data
, numofbytes
);
1095 saveFileEML(filename
, data
, numofbytes
, 8);
1096 saveFileJSON(filename
, jsfLegic
, data
, numofbytes
, NULL
);
1100 static int CmdLegicWipe(const char *Cmd
) {
1101 CLIParserContext
*ctx
;
1102 CLIParserInit(&ctx
, "hf legic wipe",
1103 "Fills a LEGIC Prime tags memory with zeros. From byte7 and to the end\n"
1104 "It autodetects card type",
1107 void *argtable
[] = {
1111 CLIExecWithReturn(ctx
, Cmd
, argtable
, true);
1115 legic_card_select_t card
;
1116 if (legic_get_type(&card
) != PM3_SUCCESS
) {
1117 PrintAndLogEx(WARNING
, "Failed to identify tagtype");
1122 uint8_t *data
= calloc(card
.cardsize
, sizeof(uint8_t));
1124 PrintAndLogEx(WARNING
, "Fail, cannot allocate memory");
1128 legic_print_type(card
.cardsize
, 0);
1130 PrintAndLogEx(SUCCESS
, "Erasing");
1132 conn
.block_after_ACK
= true;
1134 // transfer to device
1135 PacketResponseNG resp
;
1136 for (size_t i
= 7; i
< card
.cardsize
; i
+= PM3_CMD_DATA_SIZE
) {
1138 PrintAndLogEx(NORMAL
, "." NOLF
);
1140 size_t len
= MIN((card
.cardsize
- i
), PM3_CMD_DATA_SIZE
);
1141 if (len
== card
.cardsize
- i
) {
1142 // Disable fast mode on last packet
1143 conn
.block_after_ACK
= false;
1145 clearCommandBuffer();
1146 SendCommandOLD(CMD_HF_LEGIC_WRITER
, i
, len
, 0x55, data
+ i
, len
);
1148 uint8_t timeout
= 0;
1149 while (!WaitForResponseTimeout(CMD_ACK
, &resp
, 2000)) {
1151 PrintAndLogEx(NORMAL
, "." NOLF
);
1153 PrintAndLogEx(WARNING
, "\ncommand execution time out");
1155 return PM3_ETIMEOUT
;
1158 PrintAndLogEx(NORMAL
, "");
1160 uint8_t isOK
= resp
.oldarg
[0] & 0xFF;
1162 PrintAndLogEx(WARNING
, "Failed writing tag [msg = %u]", (uint8_t)(resp
.oldarg
[1] & 0xFF));
1164 return PM3_ERFTRANS
;
1167 PrintAndLogEx(SUCCESS
, "ok\n");
1172 static int CmdLegicList(const char *Cmd
) {
1173 return CmdTraceListAlias(Cmd
, "hf legic", "legic");
1176 static command_t CommandTable
[] = {
1177 {"help", CmdHelp
, AlwaysAvailable
, "This help"},
1178 {"list", CmdLegicList
, AlwaysAvailable
, "List LEGIC history"},
1179 {"reader", CmdLegicReader
, IfPm3Legicrf
, "LEGIC Prime Reader UID and tag info"},
1180 {"info", CmdLegicInfo
, IfPm3Legicrf
, "Display deobfuscated and decoded LEGIC Prime tag data"},
1181 {"dump", CmdLegicDump
, IfPm3Legicrf
, "Dump LEGIC Prime tag to binary file"},
1182 {"restore", CmdLegicRestore
, IfPm3Legicrf
, "Restore a dump file onto a LEGIC Prime tag"},
1183 {"rdbl", CmdLegicRdbl
, IfPm3Legicrf
, "Read bytes from a LEGIC Prime tag"},
1184 {"sim", CmdLegicSim
, IfPm3Legicrf
, "Start tag simulator"},
1185 {"wrbl", CmdLegicWrbl
, IfPm3Legicrf
, "Write data to a LEGIC Prime tag"},
1186 {"crc", CmdLegicCalcCrc
, AlwaysAvailable
, "Calculate Legic CRC over given bytes"},
1187 {"eload", CmdLegicELoad
, AlwaysAvailable
, "Load binary dump to emulator memory"},
1188 {"esave", CmdLegicESave
, AlwaysAvailable
, "Save emulator memory to binary file"},
1189 {"wipe", CmdLegicWipe
, IfPm3Legicrf
, "Wipe a LEGIC Prime tag"},
1190 {NULL
, NULL
, NULL
, NULL
}
1193 static int CmdHelp(const char *Cmd
) {
1194 (void)Cmd
; // Cmd is not used so far
1195 CmdsHelp(CommandTable
);
1199 int CmdHFLegic(const char *Cmd
) {
1200 clearCommandBuffer();
1201 return CmdsParse(CommandTable
, Cmd
);
1204 int readLegicUid(bool loop
, bool verbose
) {
1207 legic_card_select_t card
;
1209 int resp
= legic_get_type(&card
);
1212 if (resp
!= PM3_SUCCESS
) {
1220 if (verbose
) PrintAndLogEx(WARNING
, "command execution time out");
1221 return PM3_ETIMEOUT
;
1223 if (verbose
) PrintAndLogEx(WARNING
, "legic card select failed");
1230 PrintAndLogEx(NORMAL
, "");
1231 PrintAndLogEx(SUCCESS
, " MCD: " _GREEN_("%02X"), card
.uid
[0]);
1232 PrintAndLogEx(SUCCESS
, " MSN: " _GREEN_("%s"), sprint_hex(card
.uid
+ 1, sizeof(card
.uid
) - 1));
1233 legic_print_type(card
.cardsize
, 0);
1235 } while (loop
&& kbd_enter_pressed() == false);