fix little endian vs big endian in the macros... again... but this time correct
[RRG-proxmark3.git] / client / src / cmdhflegic.c
blobc1ada36a3652de7803f15e70fad4c881ed3991f3
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // High frequency Legic commands
9 //-----------------------------------------------------------------------------
10 #include "cmdhflegic.h"
12 #include <stdio.h> // for Mingw readline
13 #include <ctype.h> // tolower
15 #ifdef HAVE_READLINE
16 #include <readline/readline.h>
17 #endif
19 #include "cliparser.h"
20 #include "cmdparser.h" // command_t
21 #include "comms.h" // clearCommandBuffer
22 #include "cmdtrace.h"
23 #include "crc.h"
24 #include "crc16.h"
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) {
33 if (cardsize <= 22) {
34 PrintAndLogEx(INFO, "No obsfuscation such small dump");
35 return false;
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");
42 return false;
45 for (uint16_t i = 22; i < cardsize; i++) {
46 data[i] ^= crc;
48 PrintAndLogEx(SUCCESS, "applying xoring of data done!");
49 return true;
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",
61 "hf legic info");
63 void *argtable[] = {
64 arg_param_begin,
65 arg_param_end
67 CLIExecWithReturn(ctx, Cmd, argtable, true);
68 CLIParserFree(ctx);
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;
73 uint16_t datalen = 0;
74 char token_type[6] = {0, 0, 0, 0, 0, 0};
75 int dcf = 0;
76 int bIsSegmented = 0;
78 // tagtype
79 legic_card_select_t card;
80 if (legic_get_type(&card) != PM3_SUCCESS) {
81 PrintAndLogEx(WARNING, "Failed to identify tagtype");
82 return PM3_ESOFT;
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));
89 if (!data) {
90 PrintAndLogEx(WARNING, "Cannot allocate memory");
91 return PM3_EMALLOC;
94 int status = legic_read_mem(0, card.cardsize, 0x55, data, &datalen);
95 if (status != PM3_SUCCESS) {
96 PrintAndLogEx(WARNING, "Failed reading memory");
97 free(data);
98 return status;
101 // Output CDF System area (9 bytes) plus remaining header area (12 bytes)
102 crc = data[4];
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)",
108 data[0],
109 sprint_hex(data + 1, 3),
110 data[4],
111 (calc_crc == crc) ? _GREEN_("OK") : _RED_("Fail")
114 // MCD = Manufacturer ID (should be list meaning something?)
116 token_type[0] = 0;
117 dcf = ((int)data[6] << 8) | (int)data[5];
119 // New unwritten media?
120 if (dcf == 0xFFFF) {
122 PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x), Token Type=NM (New Media)",
123 dcf,
124 data[5],
125 data[6]
128 } else if (dcf > 60000) { // Master token?
130 int fl = 0;
132 if (data[6] == 0xec) {
133 strncpy(token_type, "XAM", sizeof(token_type) - 1);
134 fl = 1;
135 stamp_len = 0x0c - (data[5] >> 4);
136 } else {
137 switch (data[5] & 0x7f) {
138 case 0x00 ... 0x2f:
139 strncpy(token_type, "IAM", sizeof(token_type) - 1);
140 fl = (0x2f - (data[5] & 0x7f)) + 1;
141 break;
142 case 0x30 ... 0x6f:
143 strncpy(token_type, "SAM", sizeof(token_type) - 1);
144 fl = (0x6f - (data[5] & 0x7f)) + 1;
145 break;
146 case 0x70 ... 0x7f:
147 strncpy(token_type, "GAM", sizeof(token_type) - 1);
148 fl = (0x7f - (data[5] & 0x7f)) + 1;
149 break;
152 stamp_len = 0xfc - data[6];
155 PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x), Token Type=" _YELLOW_("%s") " (OLE=%01u), OL=%02u, FL=%02u",
156 dcf,
157 data[5],
158 data[6],
159 token_type,
160 (data[5] & 0x80) >> 7,
161 stamp_len,
165 } else { // Is IM(-S) type of card...
167 if (data[7] == 0x9F && data[8] == 0xFF) {
168 bIsSegmented = 1;
169 strncpy(token_type, "IM-S", sizeof(token_type) - 1);
170 } else {
171 strncpy(token_type, "IM", sizeof(token_type) - 1);
174 PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x), Token Type = %s (OLE = %01u)",
175 dcf,
176 data[5],
177 data[6],
178 token_type,
179 (data[5] & 0x80) >> 7
183 // Makes no sence to show this on blank media...
184 if (dcf != 0xFFFF) {
186 if (bIsSegmented) {
187 PrintAndLogEx(SUCCESS, "WRP = %02u, WRC = %01u, RD = %01u, SSC = %02X",
188 data[7] & 0x0f,
189 (data[7] & 0x70) >> 4,
190 (data[7] & 0x80) >> 7,
191 data[8]
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) {
197 if (dcf > 60000) {
198 PrintAndLogEx(SUCCESS, "Master token data");
199 PrintAndLogEx(SUCCESS, "%s", sprint_hex(data + 8, 14));
200 } else {
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;
210 uint32_t segCRC = 0;
212 // Not Data card?
213 if (dcf > 60000)
214 goto out;
216 PrintAndLogEx(SUCCESS, _CYAN_("ADF: User Area"));
217 PrintAndLogEx(NORMAL, "------------------------------------------------------");
219 if (bIsSegmented) {
221 // Data start point on segmented cards
222 i = 22;
224 // decode segments
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",
251 data[i] ^ crc,
252 data[i + 1] ^ crc,
253 data[i + 2] ^ crc,
254 data[i + 3] ^ crc
256 PrintAndLogEx(SUCCESS, "Segment len | %u, Flag: 0x%X (valid:%01u, last:%01u)",
257 segment_len,
258 segment_flag,
259 (segment_flag & 0x4) >> 2,
260 (segment_flag & 0x8) >> 3
262 PrintAndLogEx(SUCCESS, " | WRP: %02u, WRC: %02u, RD: %01u, CRC: 0x%02X (%s)",
263 wrp,
264 wrc,
265 ((data[i + 3] ^ crc) & 0x80) >> 7,
266 segCRC,
267 (segCRC == segCalcCRC) ? _GREEN_("OK") : _RED_("Fail")
270 i += 5;
272 if (hasWRC) {
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)
278 data[k] ^= crc;
280 print_hex_break(data + i, wrc, 16);
281 PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n");
282 i += wrc;
285 if (hasWRP) {
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)
291 data[k] ^= crc;
293 print_hex_break(data + i, wrp_len, 16);
294 PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n");
295 i += wrp_len;
297 // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...)
298 if (wrp_len == 8) {
299 PrintAndLogEx(SUCCESS, "Card ID: " _YELLOW_("%2X%02X%02X"),
300 data[i - 4] ^ crc,
301 data[i - 3] ^ crc,
302 data[i - 2] ^ crc
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)
312 data[k] ^= crc;
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)
320 goto out;
322 } // end for loop
324 } else {
326 // Data start point on unsegmented cards
327 i = 8;
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",
338 wrp,
339 wrc,
340 (data[7] & 0x80) >> 7
343 if (hasWRC) {
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");
349 i += wrc;
352 if (hasWRP) {
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");
358 i += wrp_len;
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
362 if (wrp_len == 8) {
363 PrintAndLogEx(SUCCESS, "Card ID: " _YELLOW_("%2X%02X%02X"),
364 data[i - 4],
365 data[i - 3],
366 data[i - 2]
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");
380 out:
381 free(data);
382 return PM3_SUCCESS;
385 // params:
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");
396 void *argtable[] = {
397 arg_param_begin,
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"),
401 arg_param_end
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);
409 int iv_len = 0;
410 uint8_t iv[1] = {0x01}; // formerly uidcrc
412 CLIGetHexWithReturn(ctx, 3, iv, &iv_len);
414 CLIParserFree(ctx);
416 // sanity checks
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));
426 if (!data) {
427 PrintAndLogEx(WARNING, "Cannot allocate memory");
428 return PM3_EMALLOC;
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);
438 free(data);
439 return status;
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");
450 void *argtable[] = {
451 arg_param_begin,
452 arg_int0("t", "type", "<dec>", "Tag type to simulate."),
453 arg_param_end
455 CLIExecWithReturn(ctx, Cmd, argtable, true);
457 struct {
458 uint8_t tagtype;
459 bool send_reply;
460 } PACKED payload;
462 payload.send_reply = true;
463 payload.tagtype = arg_get_int_def(ctx, 1, 1);
465 CLIParserFree(ctx);
467 if (payload.tagtype > 2) {
468 PrintAndLogEx(ERR, "Invalid tag type selected.");
469 return PM3_EINVARG;
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)) {
482 break;
486 if (keypress)
487 SendCommandNG(CMD_BREAK_LOOP, NULL, 0);
489 PrintAndLogEx(INFO, "Done");
490 return PM3_SUCCESS;
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");
500 void *argtable[] = {
501 arg_param_begin,
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"),
505 arg_param_end
507 CLIExecWithReturn(ctx, Cmd, argtable, false);
509 int offset = arg_get_int_def(ctx, 1, 0);
511 int dlen = 0;
512 uint8_t data[MAX_LENGTH] = {0};
513 CLIGetHexWithReturn(ctx, 2, data, &dlen);
515 bool autoconfirm = arg_get_lit(ctx, 3);
517 CLIParserFree(ctx);
519 uint32_t IV = 0x55;
521 // OUT-OF-BOUNDS checks
522 // UID 4+1 bytes can't be written to.
523 if (offset < 5) {
524 PrintAndLogEx(WARNING, "Out-of-bounds, bytes 0-1-2-3-4 can't be written to. Offset = %d", offset);
525 return PM3_EOUTOFBOUND;
528 // tagtype
529 legic_card_select_t card;
530 if (legic_get_type(&card) != PM3_SUCCESS) {
531 PrintAndLogEx(WARNING, "Failed to identify tagtype");
532 return PM3_ESOFT;
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;
548 #ifdef HAVE_READLINE
549 char *answer = readline(confirm);
550 overwrite = (answer[0] == 'y' || answer[0] == 'Y');
551 #else
552 PrintAndLogEx(NORMAL, "%s" NOLF, confirm);
553 char *answer = NULL;
554 size_t anslen = 0;
555 if (getline(&answer, &anslen, stdin) > 0) {
556 overwrite = (answer[0] == 'y' || answer[0] == 'Y');
558 PrintAndLogEx(NORMAL, "");
559 #endif
560 free(answer);
561 if (overwrite == false) {
562 PrintAndLogEx(WARNING, "command cancelled");
563 return PM3_EOPABORTED;
567 legic_chk_iv(&IV);
569 PrintAndLogEx(SUCCESS, "Writing to tag");
571 PacketResponseNG resp;
572 clearCommandBuffer();
573 SendCommandOLD(CMD_HF_LEGIC_WRITER, offset, dlen, IV, data, dlen);
575 uint8_t timeout = 0;
576 while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
577 ++timeout;
578 PrintAndLogEx(NORMAL, "." NOLF);
579 if (timeout > 7) {
580 PrintAndLogEx(WARNING, "\ncommand execution time out");
581 return PM3_ETIMEOUT;
584 PrintAndLogEx(NORMAL, "");
586 uint8_t isOK = resp.oldarg[0] & 0xFF;
587 if (!isOK) {
588 PrintAndLogEx(WARNING, "Failed writing tag");
589 return PM3_ERFTRANS;
592 return PM3_SUCCESS;
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");
602 void *argtable[] = {
603 arg_param_begin,
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)"),
607 arg_param_end
609 CLIExecWithReturn(ctx, Cmd, argtable, false);
611 int data_len = 0;
612 uint8_t data[4096] = {0};
614 CLIGetHexWithReturn(ctx, 1, data, &data_len);
616 int mcc_len = 0;
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);
623 CLIParserFree(ctx);
625 switch (type) {
626 case 16:
627 init_table(CRC_LEGIC);
628 PrintAndLogEx(SUCCESS, "Legic crc16: %X", crc16_legic(data, data_len, mcc[0]));
629 break;
630 default:
631 PrintAndLogEx(SUCCESS, "Legic crc8: %X", CRC8Legic(data, data_len));
632 break;
635 return PM3_SUCCESS;
638 int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uint16_t *outlen) {
640 legic_chk_iv(&iv);
642 clearCommandBuffer();
643 SendCommandMIX(CMD_HF_LEGIC_READER, offset, len, iv, NULL, 0);
644 PacketResponseNG resp;
646 uint8_t timeout = 0;
647 while (!WaitForResponseTimeout(CMD_ACK, &resp, 1000)) {
648 ++timeout;
649 PrintAndLogEx(NORMAL, "." NOLF);
650 if (timeout > 14) {
651 PrintAndLogEx(WARNING, "\ncommand execution time out");
652 return PM3_ETIMEOUT;
655 PrintAndLogEx(NORMAL, "");
657 uint8_t isOK = resp.oldarg[0] & 0xFF;
658 *outlen = resp.oldarg[1];
659 if (!isOK) {
660 PrintAndLogEx(WARNING, "Failed reading tag");
661 return PM3_ESOFT;
664 if (*outlen != len)
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");
670 return PM3_ETIMEOUT;
672 return PM3_SUCCESS;
675 int legic_print_type(uint32_t tagtype, uint8_t spaces) {
676 char spc[11] = " ";
677 spc[10] = 0x00;
678 char *spacer = spc + (10 - spaces);
680 if (tagtype == 22)
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);
686 else
687 PrintAndLogEx(INFO, "%sTYPE: " _YELLOW_("Unknown %06x"), spacer, tagtype);
688 return PM3_SUCCESS;
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))
698 return PM3_ETIMEOUT;
700 uint8_t isOK = resp.oldarg[0] & 0xFF;
701 if (!isOK)
702 return PM3_ESOFT;
704 memcpy(card, resp.data.asBytes, sizeof(legic_card_select_t));
705 return PM3_SUCCESS;
707 void legic_chk_iv(uint32_t *iv) {
708 if ((*iv & 0x7F) != *iv) {
709 *iv &= 0x7F;
710 PrintAndLogEx(INFO, "Truncating IV to 7bits, %u", *iv);
712 // IV must be odd
713 if ((*iv & 1) == 0) {
714 *iv |= 0x01;
715 PrintAndLogEx(INFO, "LSB of IV must be SET %u", *iv);
718 void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) {
719 // fast push mode
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",
736 "hf legic reader");
738 void *argtable[] = {
739 arg_param_begin,
740 arg_lit0("@", NULL, "optional - continuous reader mode"),
741 arg_param_end
743 CLIExecWithReturn(ctx, Cmd, argtable, true);
744 bool cm = arg_get_lit(ctx, 1);
745 CLIParserFree(ctx);
747 if (cm) {
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");
763 void *argtable[] = {
764 arg_param_begin,
765 arg_str0("f", "file", "<filename>", "specify a filename for dump file"),
766 arg_lit0(NULL, "de", "deobfuscate dump data (xor with MCC)"),
767 arg_param_end
769 CLIExecWithReturn(ctx, Cmd, argtable, true);
771 int fnlen = 0;
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);
776 CLIParserFree(ctx);
778 // tagtype
779 legic_card_select_t card;
780 if (legic_get_type(&card) != PM3_SUCCESS) {
781 PrintAndLogEx(WARNING, "Failed to identify tagtype");
782 return PM3_ESOFT;
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;
793 uint8_t timeout = 0;
794 while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
795 ++timeout;
796 PrintAndLogEx(NORMAL, "." NOLF);
797 if (timeout > 7) {
798 PrintAndLogEx(WARNING, "\ncommand execution time out");
799 return PM3_ETIMEOUT;
802 PrintAndLogEx(NORMAL, "");
804 uint8_t isOK = resp.oldarg[0] & 0xFF;
805 if (!isOK) {
806 PrintAndLogEx(WARNING, "Failed dumping tag data");
807 return PM3_ERFTRANS;
810 uint16_t readlen = resp.oldarg[1];
811 uint8_t *data = calloc(readlen, sizeof(uint8_t));
812 if (!data) {
813 PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
814 return PM3_EMALLOC;
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");
823 free(data);
824 return PM3_ETIMEOUT;
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");
833 free(data);
834 return PM3_EFAILED;
838 // user supplied filename?
839 if (fnlen < 1) {
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);
848 free(data);
849 return PM3_SUCCESS;
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");
860 void *argtable[] = {
861 arg_param_begin,
862 arg_str1("f", "file", "<filename>", "specify a filename to restore"),
863 arg_lit0(NULL, "ob", "obfuscate dump data (xor with MCC)"),
864 arg_param_end
866 CLIExecWithReturn(ctx, Cmd, argtable, false);
868 int fnlen = 0;
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);
873 CLIParserFree(ctx);
875 // tagtype
876 legic_card_select_t card;
877 if (legic_get_type(&card) != PM3_SUCCESS) {
878 PrintAndLogEx(WARNING, "Failed to identify tagtype");
879 return PM3_ESOFT;
882 legic_print_type(card.cardsize, 0);
884 // set up buffer
885 uint8_t *data = calloc(card.cardsize, sizeof(uint8_t));
886 if (!data) {
887 PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
888 return PM3_EMALLOC;
891 size_t numofbytes;
892 if (loadFile_safe(filename, ".bin", (void **)&data, &numofbytes) != PM3_SUCCESS) {
893 free(data);
894 PrintAndLogEx(WARNING, "Error, reading file");
895 return PM3_EFILE;
898 if (card.cardsize != numofbytes) {
899 PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%u != %zu]", card.cardsize, numofbytes);
900 free(data);
901 return PM3_EFILE;
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");
908 free(data);
909 return PM3_EFAILED;
913 PrintAndLogEx(SUCCESS, "Restoring to card");
915 // fast push mode
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);
930 uint8_t timeout = 0;
931 while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
932 ++timeout;
933 PrintAndLogEx(NORMAL, "." NOLF);
934 if (timeout > 7) {
935 PrintAndLogEx(WARNING, "\ncommand execution time out");
936 free(data);
937 return PM3_ETIMEOUT;
940 PrintAndLogEx(NORMAL, "");
942 uint8_t isOK = resp.oldarg[0] & 0xFF;
943 if (!isOK) {
944 PrintAndLogEx(WARNING, "Failed writing tag [msg = %u]", (uint8_t)(resp.oldarg[1] & 0xFF));
945 free(data);
946 return PM3_ERFTRANS;
948 PrintAndLogEx(SUCCESS, "Wrote chunk [offset %zu | len %zu | total %zu", i, len, i + len);
951 free(data);
952 PrintAndLogEx(SUCCESS, "Done!");
953 return PM3_SUCCESS;
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");
964 void *argtable[] = {
965 arg_param_begin,
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)"),
969 arg_param_end
971 CLIExecWithReturn(ctx, Cmd, argtable, false);
973 int fnlen = 0;
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)) {
980 case 0:
981 numofbytes = 22;
982 break;
983 case 1:
984 numofbytes = 256;
985 break;
986 case 2:
987 numofbytes = 1024;
988 break;
989 default:
990 PrintAndLogEx(ERR, "Unknown tag type");
991 CLIParserFree(ctx);
992 return PM3_EINVARG;
995 bool shall_obsfuscate = arg_get_lit(ctx, 3);
997 CLIParserFree(ctx);
999 // set up buffer
1000 uint8_t *data = calloc(numofbytes, sizeof(uint8_t));
1001 if (!data) {
1002 PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
1003 return PM3_EMALLOC;
1006 if (loadFile_safe(filename, ".bin", (void **)&data, &numofbytes) != PM3_SUCCESS) {
1007 free(data);
1008 PrintAndLogEx(WARNING, "Error, reading file");
1009 return PM3_EFILE;
1012 if (shall_obsfuscate) {
1013 legic_xor(data, numofbytes);
1016 PrintAndLogEx(SUCCESS, "Uploading to emulator memory");
1017 legic_seteml(data, 0, numofbytes);
1019 free(data);
1020 PrintAndLogEx(SUCCESS, "Done");
1021 return PM3_SUCCESS;
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[] = {
1034 arg_param_begin,
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)"),
1038 arg_param_end
1040 CLIExecWithReturn(ctx, Cmd, argtable, true);
1042 int fnlen = 0;
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)) {
1049 case 0:
1050 numofbytes = 22;
1051 break;
1052 case 1:
1053 numofbytes = 256;
1054 break;
1055 case 2:
1056 numofbytes = 1024;
1057 break;
1058 default:
1059 PrintAndLogEx(ERR, "Unknown tag type");
1060 CLIParserFree(ctx);
1061 return PM3_EINVARG;
1064 bool shall_deobsfuscate = arg_get_lit(ctx, 3);
1066 CLIParserFree(ctx);
1068 // set up buffer
1069 uint8_t *data = calloc(numofbytes, sizeof(uint8_t));
1070 if (!data) {
1071 PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
1072 return PM3_EMALLOC;
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");
1079 free(data);
1080 return PM3_ETIMEOUT;
1083 // user supplied filename?
1084 if (fnlen < 1) {
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);
1097 return PM3_SUCCESS;
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",
1105 "hf legic wipe");
1107 void *argtable[] = {
1108 arg_param_begin,
1109 arg_param_end
1111 CLIExecWithReturn(ctx, Cmd, argtable, true);
1112 CLIParserFree(ctx);
1114 // tagtype
1115 legic_card_select_t card;
1116 if (legic_get_type(&card) != PM3_SUCCESS) {
1117 PrintAndLogEx(WARNING, "Failed to identify tagtype");
1118 return PM3_ESOFT;
1121 // set up buffer
1122 uint8_t *data = calloc(card.cardsize, sizeof(uint8_t));
1123 if (!data) {
1124 PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
1125 return PM3_EMALLOC;
1128 legic_print_type(card.cardsize, 0);
1130 PrintAndLogEx(SUCCESS, "Erasing");
1131 // fast push mode
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)) {
1150 ++timeout;
1151 PrintAndLogEx(NORMAL, "." NOLF);
1152 if (timeout > 7) {
1153 PrintAndLogEx(WARNING, "\ncommand execution time out");
1154 free(data);
1155 return PM3_ETIMEOUT;
1158 PrintAndLogEx(NORMAL, "");
1160 uint8_t isOK = resp.oldarg[0] & 0xFF;
1161 if (!isOK) {
1162 PrintAndLogEx(WARNING, "Failed writing tag [msg = %u]", (uint8_t)(resp.oldarg[1] & 0xFF));
1163 free(data);
1164 return PM3_ERFTRANS;
1167 PrintAndLogEx(SUCCESS, "ok\n");
1168 free(data);
1169 return PM3_SUCCESS;
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);
1196 return PM3_SUCCESS;
1199 int CmdHFLegic(const char *Cmd) {
1200 clearCommandBuffer();
1201 return CmdsParse(CommandTable, Cmd);
1204 int readLegicUid(bool loop, bool verbose) {
1206 do {
1207 legic_card_select_t card;
1209 int resp = legic_get_type(&card);
1211 if (loop) {
1212 if (resp != PM3_SUCCESS) {
1213 continue;
1215 } else {
1216 switch (resp) {
1217 case PM3_EINVARG:
1218 return PM3_EINVARG;
1219 case PM3_ETIMEOUT:
1220 if (verbose) PrintAndLogEx(WARNING, "command execution time out");
1221 return PM3_ETIMEOUT;
1222 case PM3_ESOFT:
1223 if (verbose) PrintAndLogEx(WARNING, "legic card select failed");
1224 return PM3_ESOFT;
1225 default:
1226 break;
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);
1237 return PM3_SUCCESS;