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 static int CmdHelp(const char *Cmd
); 
  14 int usage_legic_calccrc8(void){ 
  15         PrintAndLog("Calculates the legic crc8/crc16 on the input hexbytes."); 
  16         PrintAndLog("There must be an even number of hexsymbols as input."); 
  17         PrintAndLog("Usage:  hf legic crc8 [h] b <hexbytes> u <uidcrc> c <crc type>"); 
  18         PrintAndLog("Options:"); 
  19         PrintAndLog("      b <hexbytes>  : hex bytes"); 
  20         PrintAndLog("      u <uidcrc>    : MCC hexbyte"); 
  21         PrintAndLog("      c <crc type>  : 8|16 bit crc size"); 
  23         PrintAndLog("Samples:"); 
  24         PrintAndLog("      hf legic crc8 b deadbeef1122"); 
  25         PrintAndLog("      hf legic crc8 b deadbeef1122 u 9A c 16"); 
  29 int usage_legic_load(void){ 
  30         PrintAndLog("It loads datasamples from the file `filename` to device memory"); 
  31         PrintAndLog("Usage:  hf legic load <file name>"); 
  33         PrintAndLog("Samples:"); 
  34         PrintAndLog("      hf legic load filename"); 
  38 int usage_legic_read(void){      
  39         PrintAndLog("Read data from a legic tag."); 
  40         PrintAndLog("Usage:  hf legic read <offset> <num of bytes>"); 
  41         PrintAndLog("Options:"); 
  42         PrintAndLog("  <offset>        : offset in data array to start download from"); 
  43         PrintAndLog("  <num of bytes>  : number of bytes to download"); 
  45         PrintAndLog("Samples:"); 
  46         PrintAndLog("      hf legic read"); 
  51  *  Output BigBuf and deobfuscate LEGIC RF tag data. 
  52  *  This is based on information given in the talk held 
  53  *  by Henryk Ploetz and Karsten Nohl at 26c3 
  55 int CmdLegicDecode(const char *Cmd
) { 
  56         // Index for the bytearray. 
  58         int k 
= 0, segmentNum
; 
  61         uint8_t stamp_len 
= 0; 
  65         uint8_t data_buf
[1052]; // receiver buffer,  should be 1024.. 
  70         // download EML memory, where the "legic read" command puts the data. 
  71         GetEMLFromBigBuf(data_buf
, sizeof(data_buf
), 0); 
  72         if ( !WaitForResponseTimeout(CMD_ACK
, NULL
, 2000)){ 
  73                 PrintAndLog("Command execute timeout"); 
  77         // Output CDF System area (9 bytes) plus remaining header area (12 bytes) 
  79         uint32_t calc_crc 
=  CRC8Legic(data_buf
, 4);     
  81         PrintAndLog("\nCDF: System Area"); 
  82         PrintAndLog("------------------------------------------------------"); 
  83         PrintAndLog("MCD: %02x, MSN: %02x %02x %02x, MCC: %02x %s", 
  89                 (calc_crc 
== crc
) ? "OK":"Fail"  
  94         dcf 
= ((int)data_buf
[6] << 8) | (int)data_buf
[5]; 
  96         // New unwritten media? 
  99                 PrintAndLog("DCF: %d (%02x %02x), Token Type=NM (New Media)", 
 105         } else if(dcf 
> 60000) {                // Master token? 
 109                 if(data_buf
[6] == 0xec) { 
 110                         strncpy(token_type
, "XAM", sizeof(token_type
)); 
 112                         stamp_len 
= 0x0c - (data_buf
[5] >> 4); 
 114         switch (data_buf
[5] & 0x7f) { 
 116                         strncpy(token_type
, "IAM",sizeof(token_type
)); 
 117                                         fl 
= (0x2f - (data_buf
[5] & 0x7f)) + 1; 
 120                         strncpy(token_type
, "SAM",sizeof(token_type
)); 
 121                                         fl 
= (0x6f - (data_buf
[5] & 0x7f)) + 1; 
 124                         strncpy(token_type
, "GAM",sizeof(token_type
)); 
 125                                         fl 
= (0x7f - (data_buf
[5] & 0x7f)) + 1; 
 129         stamp_len 
= 0xfc - data_buf
[6]; 
 132                 PrintAndLog("DCF: %d (%02x %02x), Token Type=%s (OLE=%01u), OL=%02u, FL=%02u", 
 137                 (data_buf
[5]&0x80)>>7, 
 142         } else {                                                // Is IM(-S) type of card... 
 144                 if(data_buf
[7] == 0x9F && data_buf
[8] == 0xFF) { 
 146                         strncpy(token_type
, "IM-S", sizeof(token_type
)); 
 148                         strncpy(token_type
, "IM", sizeof(token_type
)); 
 151                 PrintAndLog("DCF: %d (%02x %02x), Token Type=%s (OLE=%01u)", 
 156                         (data_buf
[5]&0x80)>>7 
 160         // Makes no sence to show this on blank media... 
 164                         PrintAndLog("WRP=%02u, WRC=%01u, RD=%01u, SSC=%02x", 
 166                 (data_buf
[7]&0x70)>>4, 
 167                 (data_buf
[7]&0x80)>>7, 
 172                 // Header area is only available on IM-S cards, on master tokens this data is the master token data itself 
 173                 if(bIsSegmented 
|| dcf 
> 60000) { 
 175                                 PrintAndLog("Master token data"); 
 176                                 PrintAndLog("%s", sprint_hex(data_buf
+8, 14)); 
 178         PrintAndLog("Remaining Header Area"); 
 179         PrintAndLog("%s", sprint_hex(data_buf
+9, 13)); 
 185         uint8_t segCrcBytes
[8] = {0x00}; 
 186         uint32_t segCalcCRC 
= 0; 
 193         PrintAndLog("\nADF: User Area"); 
 194         PrintAndLog("------------------------------------------------------"); 
 198                         // Data start point on segmented cards 
 202                         for (segmentNum
=1; segmentNum 
< 128; segmentNum
++ ) 
 204                 segment_len 
= ((data_buf
[i
+1]^crc
)&0x0f) * 256 + (data_buf
[i
]^crc
); 
 205                 segment_flag 
= ((data_buf
[i
+1]^crc
)&0xf0)>>4; 
 206                 wrp 
= (data_buf
[i
+2]^crc
); 
 207                 wrc 
= ((data_buf
[i
+3]^crc
)&0x70)>>4; 
 209                 bool hasWRC 
= (wrc 
> 0); 
 210                 bool hasWRP 
= (wrp 
> wrc
); 
 211                 int wrp_len 
= (wrp 
- wrc
); 
 212                 int remain_seg_payload_len 
= (segment_len 
- wrp 
- 5); 
 214                 // validate segment-crc 
 215                 segCrcBytes
[0]=data_buf
[0];                     //uid0 
 216                 segCrcBytes
[1]=data_buf
[1];                     //uid1 
 217                 segCrcBytes
[2]=data_buf
[2];                     //uid2 
 218                 segCrcBytes
[3]=data_buf
[3];                     //uid3 
 219                 segCrcBytes
[4]=(data_buf
[i
]^crc
);       //hdr0 
 220                 segCrcBytes
[5]=(data_buf
[i
+1]^crc
); //hdr1 
 221                 segCrcBytes
[6]=(data_buf
[i
+2]^crc
); //hdr2 
 222                 segCrcBytes
[7]=(data_buf
[i
+3]^crc
); //hdr3 
 224                 segCalcCRC 
= CRC8Legic(segCrcBytes
, 8); 
 225                 segCRC 
= data_buf
[i
+4]^crc
; 
 227                 PrintAndLog("Segment %02u \nraw header | 0x%02X 0x%02X 0x%02X 0x%02X \nSegment len: %u,  Flag: 0x%X (valid:%01u, last:%01u), WRP: %02u, WRC: %02u, RD: %01u, CRC: 0x%02X (%s)", 
 235                         (segment_flag 
& 0x4) >> 2, 
 236                         (segment_flag 
& 0x8) >> 3, 
 239                         ((data_buf
[i
+3]^crc
) & 0x80) >> 7, 
 241                         ( segCRC 
== segCalcCRC 
) ? "OK" : "fail" 
 247                         PrintAndLog("WRC protected area:   (I %d | K %d| WRC %d)", i
, k
, wrc
); 
 248                         PrintAndLog("\nrow  | data"); 
 249                         PrintAndLog("-----+------------------------------------------------"); 
 251                                         for ( k
=i
; k 
< (i
+wrc
); ++k
) 
 254                         print_hex_break( data_buf
+i
, wrc
, 16); 
 260                         PrintAndLog("Remaining write protected area:  (I %d | K %d | WRC %d | WRP %d  WRP_LEN %d)",i
, k
, wrc
, wrp
, wrp_len
); 
 261                         PrintAndLog("\nrow  | data"); 
 262                         PrintAndLog("-----+------------------------------------------------"); 
 264                                         for (k
=i
; k 
< (i
+wrp_len
); ++k
) 
 267                         print_hex_break( data_buf
+i
, wrp_len
, 16); 
 271                                         // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...) 
 273                                 PrintAndLog("Card ID: %2X%02X%02X", data_buf
[i
-4]^crc
, data_buf
[i
-3]^crc
, data_buf
[i
-2]^crc
);                    
 276                 PrintAndLog("Remaining segment payload:  (I %d | K %d | Remain LEN %d)", i
, k
, remain_seg_payload_len
); 
 277                 PrintAndLog("\nrow  | data"); 
 278                 PrintAndLog("-----+------------------------------------------------"); 
 280                                 for ( k
=i
; k 
< (i
+remain_seg_payload_len
); ++k
) 
 283                 print_hex_break( data_buf
+i
, remain_seg_payload_len
, 16); 
 285                 i 
+= remain_seg_payload_len
; 
 287                 PrintAndLog("-----+------------------------------------------------\n"); 
 289                 // end with last segment 
 290                 if (segment_flag 
& 0x8) return 0; 
 296                         // Data start point on unsegmented cards 
 299                         wrp          
= data_buf
[7] & 0x0F; 
 300                         wrc          
= (data_buf
[7] & 0x07) >> 4; 
 302                         bool hasWRC 
= (wrc 
> 0); 
 303                         bool hasWRP 
= (wrp 
> wrc
); 
 304                         int wrp_len 
= (wrp 
- wrc
); 
 305                         int remain_seg_payload_len 
= (1024 - 22 - wrp
); // Any chance to get physical card size here!? 
 307                         PrintAndLog("Unsegmented card - WRP: %02u, WRC: %02u, RD: %01u", 
 310                                 (data_buf
[7] & 0x80) >> 7 
 314                                 PrintAndLog("WRC protected area:   (I %d | WRC %d)", i
, wrc
); 
 315                                 PrintAndLog("\nrow  | data"); 
 316                                 PrintAndLog("-----+------------------------------------------------"); 
 317                                 print_hex_break( data_buf
+i
, wrc
, 16); 
 322                                 PrintAndLog("Remaining write protected area:  (I %d | WRC %d | WRP %d | WRP_LEN %d)", i
, wrc
, wrp
, wrp_len
); 
 323                                 PrintAndLog("\nrow  | data"); 
 324                                 PrintAndLog("-----+------------------------------------------------"); 
 325                                 print_hex_break( data_buf
+i
, wrp_len
, 16); 
 328                                 // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...) 
 330                                         PrintAndLog("Card ID: %2X%02X%02X", data_buf
[i
-4], data_buf
[i
-3], data_buf
[i
-2]); 
 333                         PrintAndLog("Remaining segment payload:  (I %d | Remain LEN %d)", i
, remain_seg_payload_len
); 
 334                         PrintAndLog("\nrow  | data"); 
 335                         PrintAndLog("-----+------------------------------------------------"); 
 336                         print_hex_break( data_buf
+i
, remain_seg_payload_len
, 16); 
 337                         i 
+= remain_seg_payload_len
; 
 339                         PrintAndLog("-----+------------------------------------------------\n"); 
 346 int CmdLegicRFRead(const char *Cmd
) { 
 351         char cmdp 
= param_getchar(Cmd
, 0); 
 352         if ( cmdp 
== 'H' || cmdp 
== 'h' ) return usage_legic_read(); 
 354         int byte_count
=0, offset
=0; 
 355         sscanf(Cmd
, "%i %i", &offset
, &byte_count
); 
 356         if(byte_count 
== 0) byte_count 
= -1; 
 357         if(byte_count 
+ offset 
> 1024) byte_count 
= 1024 - offset
; 
 359         UsbCommand c
= {CMD_READER_LEGIC_RF
, {offset
, byte_count
, 0}}; 
 360         clearCommandBuffer(); 
 365 int CmdLegicLoad(const char *Cmd
) { 
 367         char cmdp 
= param_getchar(Cmd
, 0); 
 368         if ( cmdp 
== 'H' || cmdp 
== 'h' || cmdp 
== 0x00) return usage_legic_load(); 
 370         char filename
[FILE_PATH_SIZE
] = {0x00}; 
 371         int len 
= strlen(Cmd
); 
 373         if (len 
> FILE_PATH_SIZE
) { 
 374                 PrintAndLog("Filepath too long (was %s bytes), max allowed is %s ", len
, FILE_PATH_SIZE
); 
 377         memcpy(filename
, Cmd
, len
); 
 379     FILE *f 
= fopen(filename
, "r"); 
 381         PrintAndLog("couldn't open '%s'", Cmd
); 
 387         uint8_t data
[USB_CMD_DATA_SIZE
] = {0x00}; 
 390     while ( fgets(line
, sizeof(line
), f
) ) { 
 391         int res 
= sscanf(line
, "%x %x %x %x %x %x %x %x",  
 392             (unsigned int *)&data
[index
], 
 393                         (unsigned int *)&data
[index 
+ 1], 
 394                         (unsigned int *)&data
[index 
+ 2], 
 395                         (unsigned int *)&data
[index 
+ 3], 
 396             (unsigned int *)&data
[index 
+ 4], 
 397                         (unsigned int *)&data
[index 
+ 5], 
 398                         (unsigned int *)&data
[index 
+ 6], 
 399                         (unsigned int *)&data
[index 
+ 7]); 
 402           PrintAndLog("Error: could not read samples"); 
 408                 if ( index 
== USB_CMD_DATA_SIZE 
){ 
 409 //                      PrintAndLog("sent %d | %d | %d", index, offset, totalbytes); 
 410                         UsbCommand c 
= { CMD_DOWNLOADED_SIM_SAMPLES_125K
, {offset
, 0, 0}}; 
 411                         memcpy(c
.d
.asBytes
, data
, sizeof(data
)); 
 412                         clearCommandBuffer(); 
 414                         if ( !WaitForResponseTimeout(CMD_ACK
, NULL
, 1500)){ 
 415                                 PrintAndLog("Command execute timeout"); 
 428                 UsbCommand c 
= { CMD_DOWNLOADED_SIM_SAMPLES_125K
, {offset
, 0, 0}}; 
 429                 memcpy(c
.d
.asBytes
, data
, 8); 
 430                 clearCommandBuffer(); 
 432                 if ( !WaitForResponseTimeout(CMD_ACK
, NULL
, 1500)){ 
 433                                 PrintAndLog("Command execute timeout"); 
 439     PrintAndLog("loaded %u samples", totalbytes
); 
 443 int CmdLegicSave(const char *Cmd
) { 
 444         int requested 
= 1024; 
 447         char filename
[FILE_PATH_SIZE
]; 
 448         uint8_t got
[1024] = {0x00}; 
 450         sscanf(Cmd
, " %s %i %i", filename
, &requested
, &offset
); 
 452         /* If no length given save entire legic read buffer */ 
 453         /* round up to nearest 8 bytes so the saved data can be used with legicload */ 
 457         if (requested 
% 8 != 0) { 
 458                 int remainder 
= requested 
% 8; 
 459                 requested 
= requested 
+ 8 - remainder
; 
 462         if (offset 
+ requested 
> sizeof(got
)) { 
 463                 PrintAndLog("Tried to read past end of buffer, <bytes> + <offset> > 1024"); 
 467         GetFromBigBuf(got
, requested
, offset
); 
 468         if ( !WaitForResponseTimeout(CMD_ACK
, NULL
, 2000)){ 
 469                 PrintAndLog("Command execute timeout");  
 473         FILE *f 
= fopen(filename
, "w"); 
 475                 PrintAndLog("couldn't open '%s'", Cmd
+1); 
 479         for (int j 
= 0; j 
< requested
; j 
+= 8) { 
 480                 fprintf(f
, "%02x %02x %02x %02x %02x %02x %02x %02x\n", 
 481                         got
[j
+0], got
[j
+1], got
[j
+2], got
[j
+3], 
 482                         got
[j
+4], got
[j
+5],     got
[j
+6], got
[j
+7] 
 485                 if (delivered 
>= requested
) break; 
 489         PrintAndLog("saved %u samples", delivered
); 
 493 //TODO: write a help text (iceman) 
 494 int CmdLegicRfSim(const char *Cmd
) { 
 495         UsbCommand c 
= {CMD_SIMULATE_TAG_LEGIC_RF
, {6,3,0}}; 
 496         sscanf(Cmd
, " %"lli
" %"lli
" %"lli
, &c
.arg
[0], &c
.arg
[1], &c
.arg
[2]); 
 497         clearCommandBuffer(); 
 502 //TODO: write a help text (iceman) 
 503 int CmdLegicRfWrite(const char *Cmd
) { 
 504     UsbCommand c 
= {CMD_WRITER_LEGIC_RF
}; 
 505     int res 
= sscanf(Cmd
, " 0x%"llx
" 0x%"llx
, &c
.arg
[0], &c
.arg
[1]); 
 507                 PrintAndLog("Please specify the offset and length as two hex strings"); 
 510         clearCommandBuffer(); 
 515 //TODO: write a help text (iceman) 
 516 int CmdLegicRfRawWrite(const char *Cmd
) { 
 518     UsbCommand c 
= { CMD_RAW_WRITER_LEGIC_RF
, {0,0,0} }; 
 519     int res 
= sscanf(Cmd
, " 0x%"llx
" 0x%"llx
, &c
.arg
[0], &c
.arg
[1]); 
 521                 PrintAndLog("Please specify the offset and value as two hex strings"); 
 525         if (c
.arg
[0] == 0x05 || c
.arg
[0] == 0x06) { 
 526                 PrintAndLog("############# DANGER !! #############"); 
 527                 PrintAndLog("# changing the DCF is irreversible  #"); 
 528                 PrintAndLog("#####################################"); 
 529                 PrintAndLog("do youe really want to continue? y(es) n(o)");              
 530                 if (scanf(" %c", &answer
) > 0 && (answer 
== 'y' || answer 
== 'Y')) { 
 537         clearCommandBuffer(); 
 542 //TODO: write a help text (iceman) 
 543 int CmdLegicRfFill(const char *Cmd
) { 
 544     UsbCommand cmd 
= {CMD_WRITER_LEGIC_RF
, {0,0,0} }; 
 545     int res 
= sscanf(Cmd
, " 0x%"llx
" 0x%"llx
" 0x%"llx
, &cmd
.arg
[0], &cmd
.arg
[1], &cmd
.arg
[2]); 
 547         PrintAndLog("Please specify the offset, length and value as two hex strings"); 
 552     UsbCommand c 
= {CMD_DOWNLOADED_SIM_SAMPLES_125K
, {0, 0, 0}}; 
 553         memset(c
.d
.asBytes
, cmd
.arg
[2], 48); 
 555         for(i 
= 0; i 
< 22; i
++) { 
 558                 clearCommandBuffer(); 
 560                 WaitForResponse(CMD_ACK
, NULL
); 
 562         clearCommandBuffer(); 
 567 int CmdLegicCalcCrc8(const char *Cmd
){ 
 569         uint8_t *data 
= NULL
; 
 570         uint8_t cmdp 
= 0, uidcrc 
= 0, type
=0; 
 575         while(param_getchar(Cmd
, cmdp
) != 0x00) { 
 576                 switch(param_getchar(Cmd
, cmdp
)) { 
 579                         // peek at length of the input string so we can 
 580                         // figure out how many elements to malloc in "data" 
 582                         param_getptr(Cmd
, &bg
, &en
, cmdp
+1); 
 585                         // check that user entered even number of characters 
 586                         // for hex data string 
 592                         // it's possible for user to accidentally enter "b" parameter 
 593                         // more than once - we have to clean previous malloc 
 594                         if (data
) free(data
); 
 595                         data 
= malloc(len 
>> 1); 
 596                         if ( data 
== NULL 
) { 
 597                                 PrintAndLog("Can't allocate memory. exiting"); 
 602                         param_gethex(Cmd
, cmdp
+1, data
, len
); 
 609                         uidcrc 
= param_get8ex(Cmd
, cmdp
+1, 0, 16); 
 614                         type 
= param_get8ex(Cmd
, cmdp
+1, 0, 10); 
 622                         PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd
, cmdp
)); 
 630                 if (data
) free(data
); 
 631                 return usage_legic_calccrc8(); 
 636                         PrintAndLog("LEGIC CRC16: %X", CRC16Legic(data
, len
, uidcrc
)); 
 639                         PrintAndLog("LEGIC CRC8: %X",  CRC8Legic(data
, len
) ); 
 643         if (data
) free(data
); 
 647 static command_t CommandTable
[] =  { 
 648         {"help",        CmdHelp
,        1, "This help"}, 
 649         {"decode",      CmdLegicDecode
, 0, "Display deobfuscated and decoded LEGIC RF tag data (use after hf legic reader)"}, 
 650         {"read",        CmdLegicRFRead
, 0, "[offset][length] -- read bytes from a LEGIC card"}, 
 651         {"save",        CmdLegicSave
,   0, "<filename> [<length>] -- Store samples"}, 
 652         {"load",        CmdLegicLoad
,   0, "<filename> -- Restore samples"}, 
 653         {"sim",         CmdLegicRfSim
,  0, "[phase drift [frame drift [req/resp drift]]] Start tag simulator (use after load or read)"}, 
 654         {"write",       CmdLegicRfWrite
,0, "<offset> <length> -- Write sample buffer (user after load or read)"}, 
 655         {"writeRaw",CmdLegicRfRawWrite
, 0, "<address> <value> -- Write direct to address"}, 
 656         {"fill",        CmdLegicRfFill
, 0, "<offset> <length> <value> -- Fill/Write tag with constant value"}, 
 657         {"crc8",        CmdLegicCalcCrc8
, 1, "Calculate Legic CRC8 over given hexbytes"}, 
 658         {NULL
, NULL
, 0, NULL
} 
 661 int CmdHFLegic(const char *Cmd
) { 
 662         clearCommandBuffer(); 
 663         CmdsParse(CommandTable
, Cmd
); 
 667 int CmdHelp(const char *Cmd
) { 
 668         CmdsHelp(CommandTable
);