Reader and card simulation have almost no common code. Moreover the sim uses an SSP Clock at 212kHz for all timings to prevent any drifting from the PRNG. This clock speed is not available in reader simulation mode (SSP runs at up to 3.4MHz, and changes speed between TX and RX). For these reasons having the code in separate files makes it significantly cleaner.
1270 lines
36 KiB
C
1270 lines
36 KiB
C
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
|
|
//
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
// the license.
|
|
//-----------------------------------------------------------------------------
|
|
// High frequency Legic commands
|
|
//-----------------------------------------------------------------------------
|
|
#include "cmdhflegic.h"
|
|
|
|
static int CmdHelp(const char *Cmd);
|
|
|
|
#define MAX_LENGTH 1024
|
|
|
|
int usage_legic_calccrc(void){
|
|
PrintAndLogEx(NORMAL, "Calculates the legic crc8/crc16 on the given data.");
|
|
PrintAndLogEx(NORMAL, "There must be an even number of hexsymbols as input.");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic crc [h] d <data> u <uidcrc> c <8|16>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " d <data> : (hex symbols) bytes to calculate crc over");
|
|
PrintAndLogEx(NORMAL, " u <uidcrc> : MCC hexbyte");
|
|
PrintAndLogEx(NORMAL, " c <8|16> : Crc type");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic crc d deadbeef1122");
|
|
PrintAndLogEx(NORMAL, " hf legic crc d deadbeef1122 u 9A c 16");
|
|
return 0;
|
|
}
|
|
int usage_legic_rdmem(void){
|
|
PrintAndLogEx(NORMAL, "Read data from a legic tag.");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic rdmem [h] <offset> <length> <IV>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " <offset> : (hex) offset in data array to start download from");
|
|
PrintAndLogEx(NORMAL, " <length> : (hex) number of bytes to read");
|
|
PrintAndLogEx(NORMAL, " <IV> : (hex) (optional) Initialization vector to use. Must be odd and 7bits max");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic rdmem 0 16 - reads from byte[0] 0x16 bytes(system header)");
|
|
PrintAndLogEx(NORMAL, " hf legic rdmem 0 4 55 - reads from byte[0] 0x4 bytes with IV 0x55");
|
|
PrintAndLogEx(NORMAL, " hf legic rdmem 0 100 55 - reads 0x100 bytes with IV 0x55");
|
|
return 0;
|
|
}
|
|
int usage_legic_sim(void){
|
|
PrintAndLogEx(NORMAL, "Simulates a LEGIC Prime tag. MIM22, MIM256, MIM1024 types can be emulated");
|
|
PrintAndLogEx(NORMAL, "Use eload/esave to upload a dump into emulator memory");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic sim [h] <tagtype>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " <tagtype> : 0 = MIM22");
|
|
PrintAndLogEx(NORMAL, " : 1 = MIM256 (default)");
|
|
PrintAndLogEx(NORMAL, " : 2 = MIM1024");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic sim");
|
|
return 0;
|
|
}
|
|
int usage_legic_write(void){
|
|
PrintAndLogEx(NORMAL, "Write data to a LEGIC Prime tag. It autodetects tagsize to make sure size");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic write [h] o <offset> d <data (hex symbols)>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " o <offset> : (hex) offset in data array to start writing");
|
|
//PrintAndLogEx(NORMAL, " <IV> : (optional) Initialization vector to use (ODD and 7bits)");
|
|
PrintAndLogEx(NORMAL, " d <data> : (hex symbols) bytes to write ");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic write o 10 d 11223344 - Write 0x11223344 starting from offset 0x10");
|
|
return 0;
|
|
}
|
|
int usage_legic_reader(void){
|
|
PrintAndLogEx(NORMAL, "Read UID and type information from a legic tag.");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic reader [h]");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic reader");
|
|
return 0;
|
|
}
|
|
int usage_legic_info(void){
|
|
PrintAndLogEx(NORMAL, "Reads information from a legic prime tag.");
|
|
PrintAndLogEx(NORMAL, "Shows systemarea, user areas etc");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic info [h]");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic info");
|
|
return 0;
|
|
}
|
|
int usage_legic_dump(void){
|
|
PrintAndLogEx(NORMAL, "Reads all pages from LEGIC Prime MIM22, MIM256, MIM1024");
|
|
PrintAndLogEx(NORMAL, "and saves binary dump into the file `filename.bin` or `cardUID.bin`");
|
|
PrintAndLogEx(NORMAL, "It autodetects card type.\n");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic dump [h] o <filename w/o .bin>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " o <filename> : filename w/o '.bin' to dump bytes");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic dump");
|
|
PrintAndLogEx(NORMAL, " hf legic dump o myfile");
|
|
return 0;
|
|
}
|
|
int usage_legic_restore(void){
|
|
PrintAndLogEx(NORMAL, "Reads binary file and it autodetects card type and verifies that the file has the same size");
|
|
PrintAndLogEx(NORMAL, "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]\n");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic restore [h] i <filename w/o .bin>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " i <filename> : filename w/o '.bin' to restore bytes on to card from");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic restore i myfile");
|
|
return 0;
|
|
}
|
|
int usage_legic_eload(void){
|
|
PrintAndLogEx(NORMAL, "It loads binary dump from the file `filename.bin`");
|
|
PrintAndLogEx(NORMAL, "Usage: hf legic eload [h] [card memory] <file name w/o `.bin`>");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " [card memory] : 0 = MIM22");
|
|
PrintAndLogEx(NORMAL, " : 1 = MIM256 (default)");
|
|
PrintAndLogEx(NORMAL, " : 2 = MIM1024");
|
|
PrintAndLogEx(NORMAL, " <filename> : filename w/o .bin to load");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic eload 2 myfile");
|
|
return 0;
|
|
}
|
|
int usage_legic_esave(void){
|
|
PrintAndLogEx(NORMAL, "It saves binary dump into the file `filename.bin` or `cardID.bin`");
|
|
PrintAndLogEx(NORMAL, " Usage: hf legic esave [h] [card memory] [file name w/o `.bin`]");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, " [card memory] : 0 = MIM22");
|
|
PrintAndLogEx(NORMAL, " : 1 = MIM256 (default)");
|
|
PrintAndLogEx(NORMAL, " : 2 = MIM1024");
|
|
PrintAndLogEx(NORMAL, " <filename> : filename w/o .bin to load");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic esave 2 myfile");
|
|
return 0;
|
|
}
|
|
int usage_legic_wipe(void){
|
|
PrintAndLogEx(NORMAL, "Fills a legic tag memory with zeros. From byte7 and to the end.");
|
|
PrintAndLogEx(NORMAL, " Usage: hf legic wipe [h]");
|
|
PrintAndLogEx(NORMAL, "Options:");
|
|
PrintAndLogEx(NORMAL, " h : this help");
|
|
PrintAndLogEx(NORMAL, "");
|
|
PrintAndLogEx(NORMAL, "Examples:");
|
|
PrintAndLogEx(NORMAL, " hf legic wipe");
|
|
return 0;
|
|
}
|
|
/*
|
|
* Output BigBuf and deobfuscate LEGIC RF tag data.
|
|
* This is based on information given in the talk held
|
|
* by Henryk Ploetz and Karsten Nohl at 26c3
|
|
*/
|
|
int CmdLegicInfo(const char *Cmd) {
|
|
|
|
char cmdp = tolower(param_getchar(Cmd, 0));
|
|
if ( cmdp == 'h' ) return usage_legic_info();
|
|
|
|
int i = 0, k = 0, segmentNum = 0, segment_len = 0, segment_flag = 0;
|
|
int crc = 0, wrp = 0, wrc = 0;
|
|
uint8_t stamp_len = 0;
|
|
uint16_t datalen = 0;
|
|
char token_type[5] = {0,0,0,0,0};
|
|
int dcf = 0;
|
|
int bIsSegmented = 0;
|
|
|
|
// tagtype
|
|
legic_card_select_t card;
|
|
if (legic_get_type(&card)) {
|
|
PrintAndLogEx(WARNING, "Failed to identify tagtype");
|
|
return 1;
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "Reading tag memory %d b...", card.cardsize);
|
|
|
|
// allocate receiver buffer
|
|
uint8_t *data = malloc(card.cardsize);
|
|
if (!data) {
|
|
PrintAndLogEx(WARNING, "Cannot allocate memory");
|
|
return 2;
|
|
}
|
|
memset(data, 0, card.cardsize);
|
|
|
|
int status = legic_read_mem(0, card.cardsize, 0x55, data, &datalen);
|
|
if ( status > 0 ) {
|
|
PrintAndLogEx(WARNING, "Failed reading memory");
|
|
free(data);
|
|
return 3;
|
|
}
|
|
|
|
// Output CDF System area (9 bytes) plus remaining header area (12 bytes)
|
|
crc = data[4];
|
|
uint32_t calc_crc = CRC8Legic(data, 4);
|
|
|
|
PrintAndLogEx(NORMAL, "\nCDF: System Area");
|
|
PrintAndLogEx(NORMAL, "------------------------------------------------------");
|
|
PrintAndLogEx(NORMAL, "MCD: %02x, MSN: %02x %02x %02x, MCC: %02x %s",
|
|
data[0],
|
|
data[1],
|
|
data[2],
|
|
data[3],
|
|
data[4],
|
|
(calc_crc == crc) ? "OK":"Fail"
|
|
);
|
|
|
|
// MCD = Manufacturer ID (should be list meaning something?)
|
|
|
|
token_type[0] = 0;
|
|
dcf = ((int)data[6] << 8) | (int)data[5];
|
|
|
|
// New unwritten media?
|
|
if (dcf == 0xFFFF) {
|
|
|
|
PrintAndLogEx(NORMAL, "DCF: %d (%02x %02x), Token Type=NM (New Media)",
|
|
dcf,
|
|
data[5],
|
|
data[6]
|
|
);
|
|
|
|
} else if (dcf > 60000) { // Master token?
|
|
|
|
int fl = 0;
|
|
|
|
if (data[6] == 0xec) {
|
|
strncpy(token_type, "XAM", sizeof(token_type));
|
|
fl = 1;
|
|
stamp_len = 0x0c - (data[5] >> 4);
|
|
} else {
|
|
switch (data[5] & 0x7f) {
|
|
case 0x00 ... 0x2f:
|
|
strncpy(token_type, "IAM", sizeof(token_type));
|
|
fl = (0x2f - (data[5] & 0x7f)) + 1;
|
|
break;
|
|
case 0x30 ... 0x6f:
|
|
strncpy(token_type, "SAM", sizeof(token_type));
|
|
fl = (0x6f - (data[5] & 0x7f)) + 1;
|
|
break;
|
|
case 0x70 ... 0x7f:
|
|
strncpy(token_type, "GAM", sizeof(token_type));
|
|
fl = (0x7f - (data[5] & 0x7f)) + 1;
|
|
break;
|
|
}
|
|
|
|
stamp_len = 0xfc - data[6];
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "DCF: %d (%02x %02x), Token Type=%s (OLE=%01u), OL=%02u, FL=%02u",
|
|
dcf,
|
|
data[5],
|
|
data[6],
|
|
token_type,
|
|
(data[5] & 0x80 )>> 7,
|
|
stamp_len,
|
|
fl
|
|
);
|
|
|
|
} else { // Is IM(-S) type of card...
|
|
|
|
if (data[7] == 0x9F && data[8] == 0xFF) {
|
|
bIsSegmented = 1;
|
|
strncpy(token_type, "IM-S", sizeof(token_type));
|
|
} else {
|
|
strncpy(token_type, "IM", sizeof(token_type));
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "DCF: %d (%02x %02x), Token Type=%s (OLE=%01u)",
|
|
dcf,
|
|
data[5],
|
|
data[6],
|
|
token_type,
|
|
(data[5]&0x80) >> 7
|
|
);
|
|
}
|
|
|
|
// Makes no sence to show this on blank media...
|
|
if (dcf != 0xFFFF) {
|
|
|
|
if (bIsSegmented) {
|
|
PrintAndLogEx(NORMAL, "WRP=%02u, WRC=%01u, RD=%01u, SSC=%02x",
|
|
data[7] & 0x0f,
|
|
(data[7] & 0x70) >> 4,
|
|
(data[7] & 0x80) >> 7,
|
|
data[8]
|
|
);
|
|
}
|
|
|
|
// Header area is only available on IM-S cards, on master tokens this data is the master token data itself
|
|
if (bIsSegmented || dcf > 60000) {
|
|
if (dcf > 60000) {
|
|
PrintAndLogEx(NORMAL, "Master token data");
|
|
PrintAndLogEx(NORMAL, "%s", sprint_hex(data+8, 14));
|
|
} else {
|
|
PrintAndLogEx(NORMAL, "Remaining Header Area");
|
|
PrintAndLogEx(NORMAL, "%s", sprint_hex(data+9, 13));
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8_t segCrcBytes[8] = {0,0,0,0,0,0,0,0};
|
|
uint32_t segCalcCRC = 0;
|
|
uint32_t segCRC = 0;
|
|
|
|
// Not Data card?
|
|
if (dcf > 60000)
|
|
goto out;
|
|
|
|
PrintAndLogEx(NORMAL, "\nADF: User Area");
|
|
PrintAndLogEx(NORMAL, "------------------------------------------------------");
|
|
|
|
if(bIsSegmented) {
|
|
|
|
// Data start point on segmented cards
|
|
i = 22;
|
|
|
|
// decode segments
|
|
for (segmentNum=1; segmentNum < 128; segmentNum++ )
|
|
{
|
|
segment_len = ((data[i+1] ^ crc) & 0x0f) * 256 + (data[i] ^ crc);
|
|
segment_flag = ((data[i+1] ^ crc) & 0xf0) >> 4;
|
|
wrp = (data[i+2] ^ crc);
|
|
wrc = ((data[i+3] ^ crc) & 0x70) >> 4;
|
|
|
|
bool hasWRC = (wrc > 0);
|
|
bool hasWRP = (wrp > wrc);
|
|
int wrp_len = (wrp - wrc);
|
|
int remain_seg_payload_len = (segment_len - wrp - 5);
|
|
|
|
// validate segment-crc
|
|
segCrcBytes[0]=data[0]; //uid0
|
|
segCrcBytes[1]=data[1]; //uid1
|
|
segCrcBytes[2]=data[2]; //uid2
|
|
segCrcBytes[3]=data[3]; //uid3
|
|
segCrcBytes[4]=(data[i] ^ crc); //hdr0
|
|
segCrcBytes[5]=(data[i+1] ^ crc); //hdr1
|
|
segCrcBytes[6]=(data[i+2] ^ crc); //hdr2
|
|
segCrcBytes[7]=(data[i+3] ^ crc); //hdr3
|
|
|
|
segCalcCRC = CRC8Legic(segCrcBytes, 8);
|
|
segCRC = data[i+4] ^ crc;
|
|
|
|
PrintAndLogEx(NORMAL, "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)",
|
|
segmentNum,
|
|
data[i] ^ crc,
|
|
data[i+1] ^ crc,
|
|
data[i+2] ^ crc,
|
|
data[i+3] ^ crc,
|
|
segment_len,
|
|
segment_flag,
|
|
(segment_flag & 0x4) >> 2,
|
|
(segment_flag & 0x8) >> 3,
|
|
wrp,
|
|
wrc,
|
|
((data[i+3]^crc) & 0x80) >> 7,
|
|
segCRC,
|
|
( segCRC == segCalcCRC ) ? "OK" : "fail"
|
|
);
|
|
|
|
i += 5;
|
|
|
|
if ( hasWRC ) {
|
|
PrintAndLogEx(NORMAL, "WRC protected area: (I %d | K %d| WRC %d)", i, k, wrc);
|
|
PrintAndLogEx(NORMAL, "\nrow | data");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------");
|
|
|
|
for ( k=i; k < (i + wrc); ++k)
|
|
data[k] ^= crc;
|
|
|
|
print_hex_break( data+i, wrc, 16);
|
|
|
|
i += wrc;
|
|
}
|
|
|
|
if ( hasWRP ) {
|
|
PrintAndLogEx(NORMAL, "Remaining write protected area: (I %d | K %d | WRC %d | WRP %d WRP_LEN %d)",i, k, wrc, wrp, wrp_len);
|
|
PrintAndLogEx(NORMAL, "\nrow | data");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------");
|
|
|
|
for (k=i; k < (i+wrp_len); ++k)
|
|
data[k] ^= crc;
|
|
|
|
print_hex_break( data+i, wrp_len, 16);
|
|
|
|
i += wrp_len;
|
|
|
|
// does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...)
|
|
if( wrp_len == 8 )
|
|
PrintAndLogEx(NORMAL, "Card ID: %2X%02X%02X", data[i-4]^crc, data[i-3]^crc, data[i-2]^crc);
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "Remaining segment payload: (I %d | K %d | Remain LEN %d)", i, k, remain_seg_payload_len);
|
|
PrintAndLogEx(NORMAL, "\nrow | data");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------");
|
|
|
|
for ( k=i; k < (i+remain_seg_payload_len); ++k)
|
|
data[k] ^= crc;
|
|
|
|
print_hex_break( data+i, remain_seg_payload_len, 16);
|
|
|
|
i += remain_seg_payload_len;
|
|
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n");
|
|
|
|
// end with last segment
|
|
if (segment_flag & 0x8)
|
|
goto out;
|
|
|
|
} // end for loop
|
|
|
|
} else {
|
|
|
|
// Data start point on unsegmented cards
|
|
i = 8;
|
|
|
|
wrp = data[7] & 0x0F;
|
|
wrc = (data[7] & 0x70) >> 4;
|
|
|
|
bool hasWRC = (wrc > 0);
|
|
bool hasWRP = (wrp > wrc);
|
|
int wrp_len = (wrp - wrc);
|
|
int remain_seg_payload_len = (1024 - 22 - wrp); // Any chance to get physical card size here!?
|
|
|
|
PrintAndLogEx(NORMAL, "Unsegmented card - WRP: %02u, WRC: %02u, RD: %01u",
|
|
wrp,
|
|
wrc,
|
|
(data[7] & 0x80) >> 7
|
|
);
|
|
|
|
if ( hasWRC ) {
|
|
PrintAndLogEx(NORMAL, "WRC protected area: (I %d | WRC %d)", i, wrc);
|
|
PrintAndLogEx(NORMAL, "\nrow | data");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------");
|
|
print_hex_break( data+i, wrc, 16);
|
|
i += wrc;
|
|
}
|
|
|
|
if ( hasWRP ) {
|
|
PrintAndLogEx(NORMAL, "Remaining write protected area: (I %d | WRC %d | WRP %d | WRP_LEN %d)", i, wrc, wrp, wrp_len);
|
|
PrintAndLogEx(NORMAL, "\nrow | data");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------");
|
|
print_hex_break( data + i, wrp_len, 16);
|
|
i += wrp_len;
|
|
|
|
// does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...)
|
|
if( wrp_len == 8 )
|
|
PrintAndLogEx(NORMAL, "Card ID: %2X%02X%02X", data[i-4], data[i-3], data[i-2]);
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "Remaining segment payload: (I %d | Remain LEN %d)", i, remain_seg_payload_len);
|
|
PrintAndLogEx(NORMAL, "\nrow | data");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------");
|
|
print_hex_break( data + i, remain_seg_payload_len, 16);
|
|
i += remain_seg_payload_len;
|
|
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n");
|
|
}
|
|
|
|
out:
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
// params:
|
|
// offset in data memory
|
|
// number of bytes to read
|
|
int CmdLegicRdmem(const char *Cmd) {
|
|
|
|
char cmdp = tolower(param_getchar(Cmd, 0));
|
|
if ( cmdp == 'h' ) return usage_legic_rdmem();
|
|
|
|
uint32_t offset = 0, len = 0, iv = 1;
|
|
uint16_t datalen = 0;
|
|
sscanf(Cmd, "%x %x %x", &offset, &len, &iv);
|
|
|
|
PrintAndLogEx(NORMAL, "Reading %d bytes, from offset %d", len, offset);
|
|
|
|
// allocate receiver buffer
|
|
uint8_t *data = malloc(len);
|
|
if ( !data ){
|
|
PrintAndLogEx(WARNING, "Cannot allocate memory");
|
|
return 2;
|
|
}
|
|
memset(data, 0, len);
|
|
|
|
int status = legic_read_mem(offset, len, iv, data, &datalen);
|
|
if ( status == 0 ) {
|
|
PrintAndLogEx(NORMAL, "\n ## | 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");
|
|
PrintAndLogEx(NORMAL, "-----+------------------------------------------------------------------------------------------------");
|
|
print_hex_break(data, datalen, 32);
|
|
}
|
|
free(data);
|
|
return status;
|
|
}
|
|
|
|
int CmdLegicRfSim(const char *Cmd) {
|
|
|
|
char cmdp = param_getchar(Cmd, 0);
|
|
if ( cmdp == 'H' || cmdp == 'h' ) return usage_legic_sim();
|
|
|
|
UsbCommand c = {CMD_SIMULATE_TAG_LEGIC_RF, {1}};
|
|
sscanf(Cmd, " %" SCNi64, &c.arg[0]);
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicRfWrite(const char *Cmd) {
|
|
|
|
uint8_t *data = NULL;
|
|
uint8_t cmdp = 0;
|
|
bool errors = false;
|
|
int len = 0, bg, en;
|
|
uint32_t offset = 0, IV = 0x55;
|
|
|
|
while(param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
|
switch (tolower(param_getchar(Cmd, cmdp))) {
|
|
case 'd':
|
|
// peek at length of the input string so we can
|
|
// figure out how many elements to malloc in "data"
|
|
bg=en=0;
|
|
if (param_getptr(Cmd, &bg, &en, cmdp+1)) {
|
|
errors = true;
|
|
break;
|
|
}
|
|
len = (en - bg + 1);
|
|
|
|
// check that user entered even number of characters
|
|
// for hex data string
|
|
if (len & 1) {
|
|
errors = true;
|
|
break;
|
|
}
|
|
|
|
// limit number of bytes to write. This is not a 'restore' command.
|
|
if ( (len>>1) > 100 ){
|
|
PrintAndLogEx(NORMAL, "Max bound on 100bytes to write a one time.");
|
|
PrintAndLogEx(NORMAL, "Use the 'hf legic restore' command if you want to write the whole tag at once");
|
|
errors = true;
|
|
}
|
|
|
|
// it's possible for user to accidentally enter "b" parameter
|
|
// more than once - we have to clean previous malloc
|
|
if (data)
|
|
free(data);
|
|
|
|
data = malloc(len >> 1);
|
|
if ( data == NULL ) {
|
|
PrintAndLogEx(WARNING, "Can't allocate memory. exiting");
|
|
errors = true;
|
|
break;
|
|
}
|
|
|
|
if (param_gethex(Cmd, cmdp+1, data, len)) {
|
|
errors = true;
|
|
break;
|
|
}
|
|
|
|
len >>= 1;
|
|
cmdp += 2;
|
|
break;
|
|
case 'o':
|
|
offset = param_get32ex(Cmd, cmdp+1, 4, 16);
|
|
cmdp += 2;
|
|
break;
|
|
case 'h':
|
|
errors = true;
|
|
break;
|
|
default:
|
|
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
|
errors = true;
|
|
break;
|
|
}
|
|
}
|
|
//Validations
|
|
if (errors || cmdp == 0){
|
|
if (data)
|
|
free(data);
|
|
return usage_legic_write();
|
|
}
|
|
|
|
// tagtype
|
|
legic_card_select_t card;
|
|
if (legic_get_type(&card)) {
|
|
PrintAndLogEx(WARNING, "Failed to identify tagtype");
|
|
return -1;
|
|
}
|
|
|
|
legic_print_type(card.cardsize, 0);
|
|
|
|
// OUT-OF-BOUNDS checks
|
|
// UID 4+1 bytes can't be written to.
|
|
if ( offset < 5 ) {
|
|
PrintAndLogEx(NORMAL, "Out-of-bounds, bytes 0-1-2-3-4 can't be written to. Offset = %d", offset);
|
|
return -2;
|
|
}
|
|
|
|
if ( len + offset >= card.cardsize ) {
|
|
PrintAndLogEx(NORMAL, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", card.cardsize, len + offset);
|
|
return -2;
|
|
}
|
|
|
|
if (offset == 5 || offset == 6) {
|
|
PrintAndLogEx(NORMAL, "############# DANGER ################");
|
|
PrintAndLogEx(NORMAL, "# changing the DCF is irreversible #");
|
|
PrintAndLogEx(NORMAL, "#####################################");
|
|
char *answer = NULL;
|
|
answer = readline("do you really want to continue? y(es) n(o) : ");
|
|
bool overwrite = (answer[0] == 'y' || answer[0] == 'Y');
|
|
if (!overwrite){
|
|
PrintAndLogEx(NORMAL, "command cancelled");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
legic_chk_iv(&IV);
|
|
|
|
PrintAndLogEx(NORMAL, "Writing to tag");
|
|
|
|
UsbCommand c = {CMD_WRITER_LEGIC_RF, {offset, len, IV}};
|
|
memcpy(c.d.asBytes, data, len);
|
|
UsbCommand resp;
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
return 1;
|
|
}
|
|
uint8_t isOK = resp.arg[0] & 0xFF;
|
|
if ( !isOK ) {
|
|
PrintAndLogEx(WARNING, "Failed writing tag");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicCalcCrc(const char *Cmd){
|
|
|
|
uint8_t *data = NULL;
|
|
uint8_t cmdp = 0, uidcrc = 0, type=0;
|
|
bool errors = false;
|
|
int len = 0;
|
|
int bg, en;
|
|
|
|
while(param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
|
switch (tolower(param_getchar(Cmd, cmdp))) {
|
|
case 'd':
|
|
// peek at length of the input string so we can
|
|
// figure out how many elements to malloc in "data"
|
|
bg=en=0;
|
|
if (param_getptr(Cmd, &bg, &en, cmdp+1)) {
|
|
errors = true;
|
|
break;
|
|
}
|
|
len = (en - bg + 1);
|
|
|
|
// check that user entered even number of characters
|
|
// for hex data string
|
|
if (len & 1) {
|
|
errors = true;
|
|
break;
|
|
}
|
|
|
|
// it's possible for user to accidentally enter "b" parameter
|
|
// more than once - we have to clean previous malloc
|
|
if (data) free(data);
|
|
data = malloc(len >> 1);
|
|
if ( data == NULL ) {
|
|
PrintAndLogEx(WARNING, "Can't allocate memory. exiting");
|
|
errors = true;
|
|
break;
|
|
}
|
|
|
|
if (param_gethex(Cmd, cmdp+1, data, len)) {
|
|
errors = true;
|
|
break;
|
|
}
|
|
|
|
len >>= 1;
|
|
cmdp += 2;
|
|
break;
|
|
case 'u':
|
|
uidcrc = param_get8ex(Cmd, cmdp+1, 0, 16);
|
|
cmdp += 2;
|
|
break;
|
|
case 'c':
|
|
type = param_get8ex(Cmd, cmdp+1, 0, 10);
|
|
cmdp += 2;
|
|
break;
|
|
case 'h':
|
|
errors = true;
|
|
break;
|
|
default:
|
|
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
|
errors = true;
|
|
break;
|
|
}
|
|
}
|
|
//Validations
|
|
if (errors || cmdp == 0){
|
|
if (data) free(data);
|
|
return usage_legic_calccrc();
|
|
}
|
|
|
|
switch (type){
|
|
case 16:
|
|
init_table(CRC_LEGIC);
|
|
PrintAndLogEx(NORMAL, "Legic crc16: %X", crc16_legic(data, len, uidcrc));
|
|
break;
|
|
default:
|
|
PrintAndLogEx(NORMAL, "Legic crc8: %X", CRC8Legic(data, len) );
|
|
break;
|
|
}
|
|
|
|
if (data) free(data);
|
|
return 0;
|
|
}
|
|
|
|
int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uint16_t *outlen) {
|
|
|
|
legic_chk_iv(&iv);
|
|
|
|
UsbCommand c = {CMD_READER_LEGIC_RF, {offset, len, iv}};
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
UsbCommand resp;
|
|
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 3000) ) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
return 1;
|
|
}
|
|
|
|
uint8_t isOK = resp.arg[0] & 0xFF;
|
|
*outlen = resp.arg[1];
|
|
if ( !isOK ) {
|
|
PrintAndLogEx(WARNING, "Failed reading tag");
|
|
return 2;
|
|
}
|
|
|
|
if ( *outlen != len )
|
|
PrintAndLogEx(WARNING, "Fail, only managed to read %u bytes", *outlen);
|
|
|
|
// copy data from device
|
|
if ( !GetFromDevice( BIG_BUF_EML, out, *outlen, 0, NULL, 2500, false) ) {
|
|
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
|
|
return 4;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int legic_print_type(uint32_t tagtype, uint8_t spaces){
|
|
char spc[11] = " ";
|
|
spc[10]=0x00;
|
|
char *spacer = spc + (10-spaces);
|
|
|
|
if ( tagtype == 22 )
|
|
PrintAndLogEx(NORMAL, "%sTYPE : MIM%d card (outdated)", spacer, tagtype);
|
|
else if ( tagtype == 256 )
|
|
PrintAndLogEx(NORMAL, "%sTYPE : MIM%d card (234 bytes)", spacer, tagtype);
|
|
else if ( tagtype == 1024 )
|
|
PrintAndLogEx(NORMAL, "%sTYPE : MIM%d card (1002 bytes)", spacer, tagtype);
|
|
else
|
|
PrintAndLogEx(NORMAL, "%sTYPE : Unknown %06x", spacer, tagtype);
|
|
return 0;
|
|
}
|
|
int legic_get_type(legic_card_select_t *card){
|
|
|
|
if ( card == NULL ) return 1;
|
|
|
|
UsbCommand c = {CMD_LEGIC_INFO, {0,0,0}};
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
UsbCommand resp;
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500))
|
|
return 2;
|
|
|
|
uint8_t isOK = resp.arg[0] & 0xFF;
|
|
if ( !isOK )
|
|
return 3;
|
|
|
|
memcpy(card, (legic_card_select_t *)resp.d.asBytes, sizeof(legic_card_select_t));
|
|
return 0;
|
|
}
|
|
void legic_chk_iv(uint32_t *iv){
|
|
if ( (*iv & 0x7F) != *iv ){
|
|
*iv &= 0x7F;
|
|
PrintAndLogEx(NORMAL, "Truncating IV to 7bits, %u", *iv);
|
|
}
|
|
// IV must be odd
|
|
if ( (*iv & 1) == 0 ){
|
|
*iv |= 0x01;
|
|
PrintAndLogEx(NORMAL, "LSB of IV must be SET %u", *iv);
|
|
}
|
|
}
|
|
void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) {
|
|
size_t len = 0;
|
|
|
|
UsbCommand c = {CMD_LEGIC_ESET, {0, 0, 0}};
|
|
for(size_t i = offset; i < numofbytes; i += USB_CMD_DATA_SIZE) {
|
|
|
|
len = MIN((numofbytes - i), USB_CMD_DATA_SIZE);
|
|
c.arg[0] = i; // offset
|
|
c.arg[1] = len; // number of bytes
|
|
memcpy(c.d.asBytes, src+i, len);
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
}
|
|
}
|
|
|
|
int HFLegicReader(const char *Cmd, bool verbose) {
|
|
|
|
char cmdp = tolower(param_getchar(Cmd, 0));
|
|
if ( cmdp == 'h' ) return usage_legic_reader();
|
|
|
|
legic_card_select_t card;
|
|
switch(legic_get_type(&card)){
|
|
case 1:
|
|
return 2;
|
|
case 2:
|
|
if ( verbose ) PrintAndLogEx(WARNING, "command execution time out");
|
|
return 1;
|
|
case 3:
|
|
if ( verbose ) PrintAndLogEx(NORMAL, "legic card select failed");
|
|
return 2;
|
|
default: break;
|
|
}
|
|
PrintAndLogEx(NORMAL, " UID : %s", sprint_hex(card.uid, sizeof(card.uid)));
|
|
legic_print_type(card.cardsize, 0);
|
|
return 0;
|
|
}
|
|
int CmdLegicReader(const char *Cmd){
|
|
return HFLegicReader(Cmd, true);
|
|
}
|
|
|
|
int CmdLegicDump(const char *Cmd){
|
|
|
|
FILE *f;
|
|
char filename[FILE_PATH_SIZE] = {0x00};
|
|
char *fnameptr = filename;
|
|
size_t fileNlen = 0;
|
|
bool errors = false;
|
|
uint16_t dumplen;
|
|
uint8_t cmdp = 0;
|
|
|
|
memset(filename, 0, sizeof(filename));
|
|
|
|
while(param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
|
switch (tolower(param_getchar(Cmd, cmdp))) {
|
|
case 'h':
|
|
return usage_legic_dump();
|
|
case 'o':
|
|
fileNlen = param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE);
|
|
if (!fileNlen)
|
|
errors = true;
|
|
if (fileNlen > FILE_PATH_SIZE-5)
|
|
fileNlen = FILE_PATH_SIZE-5;
|
|
cmdp += 2;
|
|
break;
|
|
default:
|
|
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
|
errors = true;
|
|
break;
|
|
}
|
|
}
|
|
//Validations
|
|
if (errors) return usage_legic_dump();
|
|
|
|
// tagtype
|
|
legic_card_select_t card;
|
|
if (legic_get_type(&card)) {
|
|
PrintAndLogEx(WARNING, "Failed to identify tagtype");
|
|
return -1;
|
|
}
|
|
dumplen = card.cardsize;
|
|
|
|
legic_print_type(dumplen, 0);
|
|
PrintAndLogEx(NORMAL, "Reading tag memory %d b...", dumplen);
|
|
|
|
UsbCommand c = {CMD_READER_LEGIC_RF, {0x00, dumplen, 0x55}};
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
UsbCommand resp;
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) {
|
|
PrintAndLogEx(NORMAL, "Command execute time-out");
|
|
return 1;
|
|
}
|
|
|
|
uint8_t isOK = resp.arg[0] & 0xFF;
|
|
if ( !isOK ) {
|
|
PrintAndLogEx(WARNING, "Failed dumping tag data");
|
|
return 2;
|
|
}
|
|
|
|
uint16_t readlen = resp.arg[1];
|
|
uint8_t *data = malloc(readlen);
|
|
if (!data) {
|
|
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
|
|
return 3;
|
|
}
|
|
memset(data, 0, readlen);
|
|
|
|
if ( readlen != dumplen )
|
|
PrintAndLogEx(WARNING, "Fail, only managed to read 0x%02X bytes of 0x%02X", readlen, dumplen);
|
|
|
|
// copy data from device
|
|
if (!GetFromDevice( BIG_BUF_EML, data, readlen, 0, NULL, 2500, false) ) {
|
|
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
|
|
free(data);
|
|
return 4;
|
|
}
|
|
|
|
// user supplied filename?
|
|
if (fileNlen < 1)
|
|
sprintf(fnameptr,"%02X%02X%02X%02X.bin", data[0], data[1], data[2], data[3]);
|
|
else
|
|
sprintf(fnameptr + fileNlen,".bin");
|
|
|
|
f = fopen(filename,"wb");
|
|
if (!f) {
|
|
PrintAndLogEx(WARNING, "Could not create file name %s", filename);
|
|
if (data)
|
|
free(data);
|
|
return 5;
|
|
}
|
|
fwrite(data, 1, readlen, f);
|
|
fflush(f);
|
|
fclose(f);
|
|
free(data);
|
|
PrintAndLogEx(NORMAL, "Wrote %d bytes to %s", readlen, filename);
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicRestore(const char *Cmd){
|
|
|
|
FILE *f;
|
|
char filename[FILE_PATH_SIZE] = {0x00};
|
|
char *fnameptr = filename;
|
|
size_t fileNlen = 0;
|
|
bool errors = false;
|
|
uint16_t numofbytes;
|
|
uint8_t cmdp = 0;
|
|
|
|
memset(filename, 0, sizeof(filename));
|
|
|
|
while(param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
|
switch (tolower(param_getchar(Cmd, cmdp))) {
|
|
case 'h':
|
|
errors = true;
|
|
break;
|
|
case 'i':
|
|
fileNlen = param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE);
|
|
if (!fileNlen)
|
|
errors = true;
|
|
|
|
if (fileNlen > FILE_PATH_SIZE-5)
|
|
fileNlen = FILE_PATH_SIZE-5;
|
|
cmdp += 2;
|
|
break;
|
|
default:
|
|
PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
|
errors = true;
|
|
break;
|
|
}
|
|
}
|
|
//Validations
|
|
if (errors || cmdp == 0) return usage_legic_restore();
|
|
|
|
// tagtype
|
|
legic_card_select_t card;
|
|
if (legic_get_type(&card)) {
|
|
PrintAndLogEx(WARNING, "Failed to identify tagtype");
|
|
return 1;
|
|
}
|
|
numofbytes = card.cardsize;
|
|
|
|
// set up buffer
|
|
uint8_t *data = malloc(numofbytes);
|
|
if (!data) {
|
|
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
|
|
return 2;
|
|
}
|
|
memset(data, 0, numofbytes);
|
|
|
|
legic_print_type(numofbytes, 0);
|
|
|
|
// set up file
|
|
fnameptr += fileNlen;
|
|
sprintf(fnameptr, ".bin");
|
|
|
|
f = fopen(filename,"rb");
|
|
if (!f) {
|
|
PrintAndLogEx(NORMAL, "File %s not found or locked", filename);
|
|
return 3;
|
|
}
|
|
|
|
// verify size of dumpfile is the same as card.
|
|
fseek(f, 0, SEEK_END); // seek to end of file
|
|
size_t filesize = ftell(f); // get current file pointer
|
|
fseek(f, 0, SEEK_SET); // seek back to beginning of file
|
|
|
|
if ( filesize != numofbytes) {
|
|
PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%u != %u]", filesize, numofbytes);
|
|
free(data);
|
|
fclose(f);
|
|
return 4;
|
|
}
|
|
|
|
// load file
|
|
size_t bytes_read = fread(data, 1, numofbytes, f);
|
|
fclose(f);
|
|
|
|
if ( bytes_read == 0){
|
|
PrintAndLogEx(NORMAL, "File reading error");
|
|
free(data);
|
|
return 2;
|
|
}
|
|
|
|
PrintAndLogEx(NORMAL, "Restoring to card");
|
|
|
|
// transfer to device
|
|
size_t len = 0;
|
|
UsbCommand c = {CMD_WRITER_LEGIC_RF, {0, 0, 0x55}};
|
|
UsbCommand resp;
|
|
for(size_t i = 7; i < numofbytes; i += USB_CMD_DATA_SIZE) {
|
|
|
|
len = MIN((numofbytes - i), USB_CMD_DATA_SIZE);
|
|
c.arg[0] = i; // offset
|
|
c.arg[1] = len; // number of bytes
|
|
memcpy(c.d.asBytes, data+i, len);
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 4000)) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
free(data);
|
|
return 1;
|
|
}
|
|
uint8_t isOK = resp.arg[0] & 0xFF;
|
|
if ( !isOK ) {
|
|
PrintAndLogEx(WARNING, "Failed writing tag [msg = %u]", resp.arg[1] & 0xFF);
|
|
free(data);
|
|
return 1;
|
|
}
|
|
PrintAndLogEx(NORMAL, "Wrote chunk [offset %d | len %d | total %d", i, len, i+len);
|
|
}
|
|
|
|
free(data);
|
|
PrintAndLogEx(NORMAL, "\nWrote %d bytes to card from file %s", numofbytes, filename);
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicELoad(const char *Cmd) {
|
|
FILE * f;
|
|
char filename[FILE_PATH_SIZE];
|
|
char *fnameptr = filename;
|
|
int len, numofbytes;
|
|
int nameParamNo = 1;
|
|
|
|
char cmdp = tolower(param_getchar(Cmd, 0));
|
|
if ( cmdp == 'h' || cmdp == 0x00)
|
|
return usage_legic_eload();
|
|
|
|
switch (cmdp) {
|
|
case '0' : numofbytes = 22; break;
|
|
case '1' :
|
|
case '\0': numofbytes = 256; break;
|
|
case '2' : numofbytes = 1024; break;
|
|
default : numofbytes = 256; nameParamNo = 0;break;
|
|
}
|
|
|
|
// set up buffer
|
|
uint8_t *data = malloc(numofbytes);
|
|
if (!data) {
|
|
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
|
|
return 3;
|
|
}
|
|
memset(data, 0, numofbytes);
|
|
|
|
// set up file
|
|
len = param_getstr(Cmd, nameParamNo, filename, FILE_PATH_SIZE);
|
|
if (len > FILE_PATH_SIZE - 5)
|
|
len = FILE_PATH_SIZE - 5;
|
|
fnameptr += len;
|
|
sprintf(fnameptr, ".bin");
|
|
|
|
// open file
|
|
f = fopen(filename,"rb");
|
|
if (!f) {
|
|
PrintAndLogEx(NORMAL, "File %s not found or locked", filename);
|
|
free(data);
|
|
return 1;
|
|
}
|
|
|
|
// load file
|
|
size_t bytes_read = fread(data, 1, numofbytes, f);
|
|
if ( bytes_read == 0){
|
|
PrintAndLogEx(NORMAL, "File reading error");
|
|
free(data);
|
|
fclose(f);
|
|
f = NULL;
|
|
return 2;
|
|
}
|
|
fclose(f);
|
|
f = NULL;
|
|
|
|
// transfer to device
|
|
legic_seteml(data, 0, numofbytes);
|
|
|
|
free(data);
|
|
PrintAndLogEx(NORMAL, "\nLoaded %d bytes from file: %s to emulator memory", numofbytes, filename);
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicESave(const char *Cmd) {
|
|
FILE *f;
|
|
char filename[FILE_PATH_SIZE];
|
|
char *fnameptr = filename;
|
|
int fileNlen, numofbytes, nameParamNo = 1;
|
|
|
|
memset(filename, 0, sizeof(filename));
|
|
|
|
char cmdp = tolower(param_getchar(Cmd, 0));
|
|
|
|
if ( cmdp == 'h' || cmdp == 0x00)
|
|
return usage_legic_esave();
|
|
|
|
switch (cmdp) {
|
|
case '0' : numofbytes = 22; break;
|
|
case '1' :
|
|
case '\0': numofbytes = 256; break;
|
|
case '2' : numofbytes = 1024; break;
|
|
default : numofbytes = 256; nameParamNo = 0; break;
|
|
}
|
|
|
|
fileNlen = param_getstr(Cmd, nameParamNo, filename, FILE_PATH_SIZE);
|
|
|
|
if (fileNlen > FILE_PATH_SIZE - 5)
|
|
fileNlen = FILE_PATH_SIZE - 5;
|
|
|
|
// set up buffer
|
|
uint8_t *data = malloc(numofbytes);
|
|
if (!data) {
|
|
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
|
|
return 3;
|
|
}
|
|
memset(data, 0, numofbytes);
|
|
|
|
// download emulator memory
|
|
PrintAndLogEx(NORMAL, "Reading emulator memory...");
|
|
if (!GetFromDevice( BIG_BUF_EML, data, numofbytes, 0, NULL, 2500, false)) {
|
|
PrintAndLogEx(WARNING, "Fail, transfer from device time-out");
|
|
free(data);
|
|
return 4;
|
|
}
|
|
|
|
// user supplied filename?
|
|
if (fileNlen < 1)
|
|
sprintf(fnameptr,"%02X%02X%02X%02X.bin", data[0], data[1], data[2], data[3]);
|
|
else
|
|
sprintf(fnameptr + fileNlen,".bin");
|
|
|
|
// open file
|
|
f = fopen(filename,"wb");
|
|
if (!f) {
|
|
PrintAndLogEx(WARNING, "Could not create file name %s", filename);
|
|
free(data);
|
|
return 1;
|
|
}
|
|
fwrite(data, 1, numofbytes, f);
|
|
fclose(f);
|
|
free(data);
|
|
PrintAndLogEx(NORMAL, "\nSaved %d bytes from emulator memory to file: %s", numofbytes, filename);
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicWipe(const char *Cmd){
|
|
|
|
char cmdp = tolower(param_getchar(Cmd, 0));
|
|
|
|
if ( cmdp == 'h') return usage_legic_wipe();
|
|
|
|
// tagtype
|
|
legic_card_select_t card;
|
|
if (legic_get_type(&card)) {
|
|
PrintAndLogEx(WARNING, "Failed to identify tagtype");
|
|
return 1;
|
|
}
|
|
|
|
// set up buffer
|
|
uint8_t *data = malloc(card.cardsize);
|
|
if (!data) {
|
|
PrintAndLogEx(WARNING, "Fail, cannot allocate memory");
|
|
return 2;
|
|
}
|
|
memset(data, 0, card.cardsize);
|
|
|
|
legic_print_type(card.cardsize, 0);
|
|
|
|
PrintAndLogEx(NORMAL, "Erasing");
|
|
|
|
// transfer to device
|
|
size_t len = 0;
|
|
UsbCommand c = {CMD_WRITER_LEGIC_RF, {0, 0, 0x55}};
|
|
UsbCommand resp;
|
|
for(size_t i = 7; i < card.cardsize; i += USB_CMD_DATA_SIZE) {
|
|
|
|
printf("."); fflush(stdout);
|
|
len = MIN((card.cardsize - i), USB_CMD_DATA_SIZE);
|
|
c.arg[0] = i; // offset
|
|
c.arg[1] = len; // number of bytes
|
|
memcpy(c.d.asBytes, data+i, len);
|
|
clearCommandBuffer();
|
|
SendCommand(&c);
|
|
|
|
if (!WaitForResponseTimeout(CMD_ACK, &resp, 4000)) {
|
|
PrintAndLogEx(WARNING, "command execution time out");
|
|
free(data);
|
|
return 3;
|
|
}
|
|
uint8_t isOK = resp.arg[0] & 0xFF;
|
|
if ( !isOK ) {
|
|
PrintAndLogEx(WARNING, "Failed writing tag [msg = %u]", resp.arg[1] & 0xFF);
|
|
free(data);
|
|
return 4;
|
|
}
|
|
}
|
|
PrintAndLogEx(NORMAL, "ok\n");
|
|
return 0;
|
|
}
|
|
|
|
int CmdLegicList(const char *Cmd) {
|
|
CmdTraceList("legic");
|
|
return 0;
|
|
}
|
|
|
|
static command_t CommandTable[] = {
|
|
{"help", CmdHelp, 1, "This help"},
|
|
{"reader", CmdLegicReader, 1, "LEGIC Prime Reader UID and tag info"},
|
|
{"info", CmdLegicInfo, 0, "Display deobfuscated and decoded LEGIC Prime tag data"},
|
|
{"dump", CmdLegicDump, 0, "Dump LEGIC Prime tag to binary file"},
|
|
{"restore", CmdLegicRestore, 0, "Restore a dump onto a LEGIC Prime tag"},
|
|
{"rdmem", CmdLegicRdmem, 0, "Read bytes from a LEGIC Prime tag"},
|
|
{"sim", CmdLegicRfSim, 0, "Start tag simulator"},
|
|
{"write", CmdLegicRfWrite, 0, "Write data to a LEGIC Prime tag"},
|
|
{"crc", CmdLegicCalcCrc, 1, "Calculate Legic CRC over given bytes"},
|
|
{"eload", CmdLegicELoad, 1, "Load binary dump to emulator memory"},
|
|
{"esave", CmdLegicESave, 1, "Save emulator memory to binary file"},
|
|
{"list", CmdLegicList, 1, "[Deprecated] List LEGIC history"},
|
|
{"wipe", CmdLegicWipe, 1, "Wipe a LEGIC Prime tag"},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
int CmdHFLegic(const char *Cmd) {
|
|
clearCommandBuffer();
|
|
CmdsParse(CommandTable, Cmd);
|
|
return 0;
|
|
}
|
|
|
|
int CmdHelp(const char *Cmd) {
|
|
CmdsHelp(CommandTable);
|
|
return 0;
|
|
} |