//-----------------------------------------------------------------------------
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>, Hagen Fritsch
// Copyright (C) 2011 Gerhard de Koning Gans
+// Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende
//
// 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
#include <sys/stat.h>
#include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type
#include "data.h"
-//#include "proxusb.h"
#include "proxmark3.h"
#include "ui.h"
#include "cmdparser.h"
#include "common.h"
#include "util.h"
#include "cmdmain.h"
+#include "loclass/des.h"
+#include "loclass/cipherutils.h"
+#include "loclass/cipher.h"
+#include "loclass/ikeys.h"
+#include "loclass/elite_crack.h"
+#include "loclass/fileutils.h"
+#include "protocols.h"
+#include "usb_cmd.h"
+#include "cmdhfmfu.h"
+
+#define llX PRIx64
static int CmdHelp(const char *Cmd);
-int xorbits_8(uint8_t val)
-{
+#define ICLASS_KEYS_MAX 8
+static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = {
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
+};
+
+typedef struct iclass_block {
+ uint8_t d[8];
+} iclass_block_t;
+
+int xorbits_8(uint8_t val) {
uint8_t res = val ^ (val >> 1); //1st pass
res = res ^ (res >> 1); // 2nd pass
res = res ^ (res >> 2); // 3rd pass
return res & 1;
}
-int CmdHFiClassList(const char *Cmd)
-{
+int CmdHFiClassList(const char *Cmd) {
+ PrintAndLog("Deprecated command, use 'hf list iclass' instead");
+ return 0;
+}
+
+int CmdHFiClassSnoop(const char *Cmd) {
+ UsbCommand c = {CMD_SNOOP_ICLASS};
+ SendCommand(&c);
+ return 0;
+}
+
+int usage_hf_iclass_sim(void) {
+ PrintAndLog("Usage: hf iclass sim <option> [CSN]");
+ PrintAndLog(" options");
+ PrintAndLog(" 0 <CSN> simulate the given CSN");
+ PrintAndLog(" 1 simulate default CSN");
+ PrintAndLog(" 2 Reader-attack, gather reader responses to extract elite key");
+ PrintAndLog(" 3 Full simulation using emulator memory (see 'hf iclass eload')");
+ PrintAndLog(" example: hf iclass sim 0 031FEC8AF7FF12E0");
+ PrintAndLog(" example: hf iclass sim 2");
+ PrintAndLog(" example: hf iclass eload 'tagdump.bin'");
+ PrintAndLog(" hf iclass sim 3");
+ return 0;
+}
+
+#define NUM_CSNS 15
+int CmdHFiClassSim(const char *Cmd) {
+ uint8_t simType = 0;
+ uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ if (strlen(Cmd)<1) {
+ return usage_hf_iclass_sim();
+ }
+ simType = param_get8ex(Cmd, 0, 0, 10);
+
+ if(simType == 0)
+ {
+ if (param_gethex(Cmd, 1, CSN, 16)) {
+ PrintAndLog("A CSN should consist of 16 HEX symbols");
+ return usage_hf_iclass_sim();
+ }
+
+ PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8));
+ }
+ if(simType > 3)
+ {
+ PrintAndLog("Undefined simptype %d", simType);
+ return usage_hf_iclass_sim();
+ }
+
+ uint8_t numberOfCSNs=0;
+ if(simType == 2)
+ {
+ UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,NUM_CSNS}};
+ UsbCommand resp = {0};
+
+ uint8_t csns[8*NUM_CSNS] = {
+ 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0,
+ 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 };
+
+ memcpy(c.d.asBytes, csns, 8*NUM_CSNS);
+
+ SendCommand(&c);
+ if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) {
+ PrintAndLog("Command timed out");
+ return 0;
+ }
+
+ uint8_t num_mac_responses = resp.arg[1];
+ PrintAndLog("Mac responses: %d MACs obtained (should be %d)", num_mac_responses,NUM_CSNS);
+
+ size_t datalen = NUM_CSNS*24;
+ /*
+ * Now, time to dump to file. We'll use this format:
+ * <8-byte CSN><8-byte CC><4 byte NR><4 byte MAC>....
+ * So, it should wind up as
+ * 8 * 24 bytes.
+ *
+ * The returndata from the pm3 is on the following format
+ * <4 byte NR><4 byte MAC>
+ * CC are all zeroes, CSN is the same as was sent in
+ **/
+ void* dump = malloc(datalen);
+ memset(dump,0,datalen);//<-- Need zeroes for the CC-field
+ uint8_t i = 0;
+ for(i = 0 ; i < NUM_CSNS ; i++)
+ {
+ memcpy(dump+i*24, csns+i*8,8); //CSN
+ //8 zero bytes here...
+ //Then comes NR_MAC (eight bytes from the response)
+ memcpy(dump+i*24+16,resp.d.asBytes+i*8,8);
+
+ }
+ /** Now, save to dumpfile **/
+ saveFile("iclass_mac_attack", "bin", dump,datalen);
+ free(dump);
+ }else
+ {
+ UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}};
+ memcpy(c.d.asBytes, CSN, 8);
+ SendCommand(&c);
+ }
- bool ShowWaitCycles = false;
- char param = param_getchar(Cmd, 0);
+ return 0;
+}
- if (param != 0) {
- PrintAndLog("List data in trace buffer.");
- PrintAndLog("Usage: hf iclass list");
- PrintAndLog("h - help");
- PrintAndLog("sample: hf iclass list");
+int HFiClassReader(const char *Cmd, bool loop, bool verbose) {
+ bool tagFound = false;
+ UsbCommand c = {CMD_READER_ICLASS, {FLAG_ICLASS_READER_CSN|
+ FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_AA}};
+ // loop in client not device - else on windows have a communication error
+ c.arg[0] |= FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY;
+ UsbCommand resp;
+ while(!ukbhit()){
+ SendCommand(&c);
+ if (WaitForResponseTimeout(CMD_ACK,&resp, 4500)) {
+ uint8_t readStatus = resp.arg[0] & 0xff;
+ uint8_t *data = resp.d.asBytes;
+
+ if (verbose)
+ PrintAndLog("Readstatus:%02x", readStatus);
+ if( readStatus == 0){
+ //Aborted
+ if (verbose) PrintAndLog("Quitting...");
+ return 0;
+ }
+ if( readStatus & FLAG_ICLASS_READER_CSN){
+ PrintAndLog("CSN: %s",sprint_hex(data,8));
+ tagFound = true;
+ }
+ if( readStatus & FLAG_ICLASS_READER_CC) PrintAndLog("CC: %s",sprint_hex(data+16,8));
+ if( readStatus & FLAG_ICLASS_READER_CONF){
+ printIclassDumpInfo(data);
+ }
+ if (tagFound && !loop) return 1;
+ } else {
+ if (verbose) PrintAndLog("Command execute timeout");
+ }
+ if (!loop) break;
+ }
+ return 0;
+}
+
+int CmdHFiClassReader(const char *Cmd) {
+ return HFiClassReader(Cmd, true, true);
+}
+
+int CmdHFiClassReader_Replay(const char *Cmd) {
+ uint8_t readerType = 0;
+ uint8_t MAC[4]={0x00, 0x00, 0x00, 0x00};
+
+ if (strlen(Cmd)<1) {
+ PrintAndLog("Usage: hf iclass replay <MAC>");
+ PrintAndLog(" sample: hf iclass replay 00112233");
return 0;
}
- uint8_t got[1920];
- GetFromBigBuf(got,sizeof(got),0);
- WaitForResponse(CMD_ACK,NULL);
+ if (param_gethex(Cmd, 0, MAC, 8)) {
+ PrintAndLog("MAC must include 8 HEX symbols");
+ return 1;
+ }
- PrintAndLog("Recorded Activity");
- PrintAndLog("");
- PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer");
- PrintAndLog("All times are in carrier periods (1/13.56Mhz)");
+ UsbCommand c = {CMD_READER_ICLASS_REPLAY, {readerType}};
+ memcpy(c.d.asBytes, MAC, 4);
+ SendCommand(&c);
+
+ return 0;
+}
+
+int iclassEmlSetMem(uint8_t *data, int blockNum, int blocksCount) {
+ UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, 0}};
+ memcpy(c.d.asBytes, data, blocksCount * 16);
+ SendCommand(&c);
+ return 0;
+}
+
+int hf_iclass_eload_usage(void) {
+ PrintAndLog("Loads iclass tag-dump into emulator memory on device");
+ PrintAndLog("Usage: hf iclass eload f <filename>");
PrintAndLog("");
- PrintAndLog(" Start | End | Src | Data");
- PrintAndLog("-----------|-----------|-----|--------");
+ PrintAndLog("Example: hf iclass eload f iclass_tagdump-aa162d30f8ff12f1.bin");
+ return 0;
+}
- int i;
- uint32_t first_timestamp = 0;
- uint32_t timestamp;
- bool tagToReader;
- uint32_t parityBits;
- uint8_t len;
- uint8_t *frame;
- uint32_t EndOfTransmissionTimestamp = 0;
+int CmdHFiClassELoad(const char *Cmd) {
+ char opt = param_getchar(Cmd, 0);
+ if (strlen(Cmd)<1 || opt == 'h')
+ return hf_iclass_eload_usage();
- for( i=0; i < 1900;)
+ //File handling and reading
+ FILE *f;
+ char filename[FILE_PATH_SIZE];
+ if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0)
{
- //First 32 bits contain
- // isResponse (1 bit)
- // timestamp (remaining)
- //Then paritybits
- //Then length
- timestamp = *((uint32_t *)(got+i));
- parityBits = *((uint32_t *)(got+i+4));
- len = got[i+8];
- frame = (got+i+9);
- uint32_t next_timestamp = (*((uint32_t *)(got+i+9))) & 0x7fffffff;
-
- tagToReader = timestamp & 0x80000000;
- timestamp &= 0x7fffffff;
-
- if(i==0) {
- first_timestamp = timestamp;
- }
+ f = fopen(filename, "rb");
+ }else{
+ return hf_iclass_eload_usage();
+ }
- // Break and stick with current result if buffer was not completely full
- if (frame[0] == 0x44 && frame[1] == 0x44 && frame[2] == 0x44 && frame[3] == 0x44) break;
+ if(!f) {
+ PrintAndLog("Failed to read from file '%s'", filename);
+ return 1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ uint8_t *dump = malloc(fsize);
+
+
+ size_t bytes_read = fread(dump, 1, fsize, f);
+ fclose(f);
- char line[1000] = "";
+ printIclassDumpInfo(dump);
+ //Validate
- if(len)//We have some data to display
+ if (bytes_read < fsize)
+ {
+ prnlog("Error, could only read %d bytes (should be %d)",bytes_read, fsize );
+ free(dump);
+ return 1;
+ }
+ //Send to device
+ uint32_t bytes_sent = 0;
+ uint32_t bytes_remaining = bytes_read;
+
+ while(bytes_remaining > 0){
+ uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining);
+ UsbCommand c = {CMD_ICLASS_EML_MEMSET, {bytes_sent,bytes_in_packet,0}};
+ memcpy(c.d.asBytes, dump, bytes_in_packet);
+ SendCommand(&c);
+ bytes_remaining -= bytes_in_packet;
+ bytes_sent += bytes_in_packet;
+ }
+ free(dump);
+ PrintAndLog("Sent %d bytes of data to device emulator memory", bytes_sent);
+ return 0;
+}
+
+static int readKeyfile(const char *filename, size_t len, uint8_t* buffer) {
+ FILE *f = fopen(filename, "rb");
+ if(!f) {
+ PrintAndLog("Failed to read from file '%s'", filename);
+ return 1;
+ }
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ size_t bytes_read = fread(buffer, 1, len, f);
+ fclose(f);
+ if(fsize != len)
+ {
+ PrintAndLog("Warning, file size is %d, expected %d", fsize, len);
+ return 1;
+ }
+ if(bytes_read != len)
+ {
+ PrintAndLog("Warning, could only read %d bytes, expected %d" ,bytes_read, len);
+ return 1;
+ }
+ return 0;
+}
+
+int usage_hf_iclass_decrypt(void) {
+ PrintAndLog("Usage: hf iclass decrypt f <tagdump>");
+ PrintAndLog("");
+ PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside");
+ PrintAndLog("in the working directory. The file should be 16 bytes binary data");
+ PrintAndLog("");
+ PrintAndLog("example: hf iclass decrypt f tagdump_12312342343.bin");
+ PrintAndLog("");
+ PrintAndLog("OBS! This is pretty stupid implementation, it tries to decrypt every block after block 6. ");
+ PrintAndLog("Correct behaviour would be to decrypt only the application areas where the key is valid,");
+ PrintAndLog("which is defined by the configuration block.");
+ return 1;
+}
+
+int CmdHFiClassDecrypt(const char *Cmd) {
+ uint8_t key[16] = { 0 };
+ if(readKeyfile("iclass_decryptionkey.bin", 16, key))
+ {
+ usage_hf_iclass_decrypt();
+ return 1;
+ }
+ PrintAndLog("Decryption file found... ");
+ char opt = param_getchar(Cmd, 0);
+ if (strlen(Cmd)<1 || opt == 'h')
+ return usage_hf_iclass_decrypt();
+
+ //Open the tagdump-file
+ FILE *f;
+ char filename[FILE_PATH_SIZE];
+ if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0)
+ {
+ f = fopen(filename, "rb");
+ }else{
+ return usage_hf_iclass_decrypt();
+ }
+
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ uint8_t enc_dump[8] = {0};
+ uint8_t *decrypted = malloc(fsize);
+ des3_context ctx = { DES_DECRYPT ,{ 0 } };
+ des3_set2key_dec( &ctx, key);
+ size_t bytes_read = fread(enc_dump, 1, 8, f);
+
+ //Use the first block (CSN) for filename
+ char outfilename[FILE_PATH_SIZE] = { 0 };
+ snprintf(outfilename,FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x-decrypted",
+ enc_dump[0],enc_dump[1],enc_dump[2],enc_dump[3],
+ enc_dump[4],enc_dump[5],enc_dump[6],enc_dump[7]);
+
+ size_t blocknum =0;
+ while(bytes_read == 8)
+ {
+ if(blocknum < 7)
{
- int j,oddparity;
+ memcpy(decrypted+(blocknum*8), enc_dump, 8);
+ }else{
+ des3_crypt_ecb(&ctx, enc_dump,decrypted +(blocknum*8) );
+ }
+ printvar("decrypted block", decrypted +(blocknum*8), 8);
+ bytes_read = fread(enc_dump, 1, 8, f);
+ blocknum++;
+ }
+ fclose(f);
- for(j = 0; j < len ; j++)
- {
- oddparity = 0x01 ^ xorbits_8(frame[j] & 0xFF);
+ saveFile(outfilename,"bin", decrypted, blocknum*8);
- if (tagToReader && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) {
- sprintf(line+(j*4), "%02x! ", frame[j]);
+ return 0;
+}
+
+int usage_hf_iclass_encrypt(void) {
+ PrintAndLog("Usage: hf iclass encrypt <BlockData>");
+ PrintAndLog("");
+ PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside");
+ PrintAndLog("in the working directory. The file should be 16 bytes binary data");
+ PrintAndLog("");
+ PrintAndLog("example: hf iclass encrypt 0102030405060708");
+ PrintAndLog("");
+ return 0;
+}
+
+static int iClassEncryptBlkData(uint8_t *blkData) {
+ uint8_t key[16] = { 0 };
+ if(readKeyfile("iclass_decryptionkey.bin", 16, key))
+ {
+ usage_hf_iclass_encrypt();
+ return 1;
+ }
+ PrintAndLog("Decryption file found... ");
+
+ uint8_t encryptedData[16];
+ uint8_t *encrypted = encryptedData;
+ des3_context ctx = { DES_DECRYPT ,{ 0 } };
+ des3_set2key_enc( &ctx, key);
+
+ des3_crypt_ecb(&ctx, blkData,encrypted);
+ //printvar("decrypted block", decrypted, 8);
+ memcpy(blkData,encrypted,8);
+
+ return 1;
+}
+
+int CmdHFiClassEncryptBlk(const char *Cmd) {
+ uint8_t blkData[8] = {0};
+ char opt = param_getchar(Cmd, 0);
+ if (strlen(Cmd)<1 || opt == 'h')
+ return usage_hf_iclass_encrypt();
+
+ //get the bytes to encrypt
+ if (param_gethex(Cmd, 0, blkData, 16))
+ {
+ PrintAndLog("BlockData must include 16 HEX symbols");
+ return 0;
+ }
+ if (!iClassEncryptBlkData(blkData)) return 0;
+
+ printvar("encrypted block", blkData, 8);
+ return 1;
+}
+
+void Calc_wb_mac(uint8_t blockno, uint8_t *data, uint8_t *div_key, uint8_t MAC[4]) {
+ uint8_t WB[9];
+ WB[0] = blockno;
+ memcpy(WB + 1,data,8);
+ doMAC_N(WB,sizeof(WB),div_key,MAC);
+ //printf("Cal wb mac block [%02x][%02x%02x%02x%02x%02x%02x%02x%02x] : MAC [%02x%02x%02x%02x]",WB[0],WB[1],WB[2],WB[3],WB[4],WB[5],WB[6],WB[7],WB[8],MAC[0],MAC[1],MAC[2],MAC[3]);
+}
+
+static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose) {
+ UsbCommand resp;
+
+ UsbCommand c = {CMD_READER_ICLASS, {0}};
+ c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_CC | FLAG_ICLASS_READER_ONE_TRY;
+ if (use_credit_key)
+ c.arg[0] |= FLAG_ICLASS_READER_CEDITKEY;
+
+ clearCommandBuffer();
+ SendCommand(&c);
+ if (!WaitForResponseTimeout(CMD_ACK,&resp,4500))
+ {
+ PrintAndLog("Command execute timeout");
+ return false;
+ }
+
+ uint8_t isOK = resp.arg[0] & 0xff;
+ uint8_t *data = resp.d.asBytes;
+
+ memcpy(CSN,data,8);
+ if (CCNR!=NULL)memcpy(CCNR,data+16,8);
+ if(isOK > 0)
+ {
+ if (verbose) PrintAndLog("CSN: %s",sprint_hex(CSN,8));
+ }
+ if(isOK <= 1){
+ PrintAndLog("Failed to obtain CC! Aborting");
+ return false;
+ }
+ return true;
+}
+
+static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool verbose) {
+ uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+ if (!select_only(CSN, CCNR, use_credit_key, verbose))
+ return false;
+
+ //get div_key
+ HFiClassCalcDivKey(CSN, KEY, div_key, elite);
+
+ doMAC(CCNR, div_key, MAC);
+ UsbCommand resp;
+ UsbCommand d = {CMD_ICLASS_AUTHENTICATION, {0}};
+ memcpy(d.d.asBytes, MAC, 4);
+ clearCommandBuffer();
+ SendCommand(&d);
+ if (!WaitForResponseTimeout(CMD_ACK,&resp,4500))
+ {
+ PrintAndLog("Auth Command execute timeout");
+ return false;
+ }
+ uint8_t isOK = resp.arg[0] & 0xff;
+ if (!isOK) {
+ PrintAndLog("Authentication error");
+ return false;
+ }
+ return true;
+}
+
+int usage_hf_iclass_dump(void) {
+ PrintAndLog("Usage: hf iclass dump f <fileName> k <Key> c <CreditKey> e\n");
+ PrintAndLog("Options:");
+ PrintAndLog(" f <filename> : specify a filename to save dump to");
+ PrintAndLog(" k <Key> : *Access Key as 16 hex symbols or 1 hex to select key from memory");
+ PrintAndLog(" c <CreditKey>: Credit Key as 16 hex symbols or 1 hex to select key from memory");
+ PrintAndLog(" e : If 'e' is specified, the key is interpreted as the 16 byte");
+ PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack");
+ PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format");
+ PrintAndLog(" NOTE: * = required");
+ PrintAndLog("Samples:");
+ PrintAndLog(" hf iclass dump k 001122334455667B");
+ PrintAndLog(" hf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B");
+ PrintAndLog(" hf iclass dump k AAAAAAAAAAAAAAAA e");
+ return 0;
+}
+
+int CmdHFiClassReader_Dump(const char *Cmd) {
+
+ uint8_t MAC[4] = {0x00,0x00,0x00,0x00};
+ uint8_t div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t c_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t blockno = 0;
+ uint8_t numblks = 0;
+ uint8_t maxBlk = 31;
+ uint8_t app_areas = 1;
+ uint8_t kb = 2;
+ uint8_t KEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t CreditKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t keyNbr = 0;
+ uint8_t dataLen = 0;
+ uint8_t fileNameLen = 0;
+ char filename[FILE_PATH_SIZE]={0};
+ char tempStr[50] = {0};
+ bool have_debit_key = false;
+ bool have_credit_key = false;
+ bool use_credit_key = false;
+ bool elite = false;
+ bool errors = false;
+ uint8_t cmdp = 0;
+
+ while(param_getchar(Cmd, cmdp) != 0x00)
+ {
+ switch(param_getchar(Cmd, cmdp))
+ {
+ case 'h':
+ case 'H':
+ return usage_hf_iclass_dump();
+ case 'c':
+ case 'C':
+ have_credit_key = true;
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, CreditKEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(CreditKEY, iClass_Key_Table[keyNbr], 8);
} else {
- sprintf(line+(j*4), "%02x ", frame[j]);
+ PrintAndLog("\nERROR: Credit KeyNbr is invalid\n");
+ errors = true;
}
+ } else {
+ PrintAndLog("\nERROR: Credit Key is incorrect length\n");
+ errors = true;
}
- }else
- {
- if (ShowWaitCycles) {
- sprintf(line, "fdt (Frame Delay Time): %d", (next_timestamp - timestamp));
+ cmdp += 2;
+ break;
+ case 'e':
+ case 'E':
+ elite = true;
+ cmdp++;
+ break;
+ case 'f':
+ case 'F':
+ fileNameLen = param_getstr(Cmd, cmdp+1, filename);
+ if (fileNameLen < 1) {
+ PrintAndLog("No filename found after f");
+ errors = true;
}
+ cmdp += 2;
+ break;
+ case 'k':
+ case 'K':
+ have_debit_key = true;
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, KEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(KEY, iClass_Key_Table[keyNbr], 8);
+ } else {
+ PrintAndLog("\nERROR: Credit KeyNbr is invalid\n");
+ errors = true;
+ }
+ } else {
+ PrintAndLog("\nERROR: Credit Key is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ default:
+ PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+ errors = true;
+ break;
}
+ if(errors) return usage_hf_iclass_dump();
+ }
- char *crc = "";
+ if (cmdp < 2) return usage_hf_iclass_dump();
+ // if no debit key given try credit key on AA1 (not for iclass but for some picopass this will work)
+ if (!have_debit_key && have_credit_key) use_credit_key = true;
- if(len > 2)
- {
- uint8_t b1, b2;
- if(!tagToReader && len == 4) {
- // Rough guess that this is a command from the reader
- // For iClass the command byte is not part of the CRC
- ComputeCrc14443(CRC_ICLASS, &frame[1], len-3, &b1, &b2);
+ //get config and first 3 blocks
+ UsbCommand c = {CMD_READER_ICLASS, {FLAG_ICLASS_READER_CSN |
+ FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY}};
+ UsbCommand resp;
+ uint8_t tag_data[255*8];
+
+ clearCommandBuffer();
+ SendCommand(&c);
+ if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) {
+ PrintAndLog("Command execute timeout");
+ ul_switch_off_field();
+ return 0;
+ }
+ uint8_t readStatus = resp.arg[0] & 0xff;
+ uint8_t * data = resp.d.asBytes;
+
+ if(readStatus == 0){
+ PrintAndLog("No tag found...");
+ ul_switch_off_field();
+ return 0;
+}
+ if( readStatus & (FLAG_ICLASS_READER_CSN|FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_CC)){
+ memcpy(tag_data, data, 8*3);
+ blockno+=2; // 2 to force re-read of block 2 later. (seems to respond differently..)
+ numblks = data[8];
+ getMemConfig(data[13], data[12], &maxBlk, &app_areas, &kb);
+ // large memory - not able to dump pages currently
+ if (numblks > maxBlk) numblks = maxBlk;
+ }
+ ul_switch_off_field();
+ // authenticate debit key and get div_key - later store in dump block 3
+ if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, false)){
+ //try twice - for some reason it sometimes fails the first time...
+ if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, false)){
+ ul_switch_off_field();
+ return 0;
+ }
+ }
+
+ // begin dump
+ UsbCommand w = {CMD_ICLASS_DUMP, {blockno, numblks-blockno+1}};
+ clearCommandBuffer();
+ SendCommand(&w);
+ if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) {
+ PrintAndLog("Command execute time-out 1");
+ ul_switch_off_field();
+ return 1;
+ }
+ uint32_t blocksRead = resp.arg[1];
+ uint8_t isOK = resp.arg[0] & 0xff;
+ if (!isOK && !blocksRead) {
+ PrintAndLog("Read Block Failed");
+ ul_switch_off_field();
+ return 0;
+ }
+ uint32_t startindex = resp.arg[2];
+ if (blocksRead*8 > sizeof(tag_data)-(blockno*8)) {
+ PrintAndLog("Data exceeded Buffer size!");
+ blocksRead = (sizeof(tag_data)/8) - blockno;
+ }
+ // response ok - now get bigbuf content of the dump
+ GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex);
+ WaitForResponse(CMD_ACK,NULL);
+ size_t gotBytes = blocksRead*8 + blockno*8;
+
+ // try AA2
+ if (have_credit_key) {
+ //turn off hf field before authenticating with different key
+ ul_switch_off_field();
+ memset(MAC,0,4);
+ // AA2 authenticate credit key and git c_div_key - later store in dump block 4
+ if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false)){
+ //try twice - for some reason it sometimes fails the first time...
+ if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false)){
+ ul_switch_off_field();
+ return 0;
}
- else {
- // For other data.. CRC might not be applicable (UPDATE commands etc.)
- ComputeCrc14443(CRC_ICLASS, frame, len-2, &b1, &b2);
+ }
+ // do we still need to read more block? (aa2 enabled?)
+ if (maxBlk > blockno+numblks+1) {
+ // setup dump and start
+ w.arg[0] = blockno + blocksRead;
+ w.arg[1] = maxBlk - (blockno + blocksRead);
+ clearCommandBuffer();
+ SendCommand(&w);
+ if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) {
+ PrintAndLog("Command execute timeout 2");
+ ul_switch_off_field();
+ return 0;
}
-
- if (b1 != frame[len-2] || b2 != frame[len-1]) {
- crc = (tagToReader & (len < 8)) ? "" : " !crc";
+ uint8_t isOK = resp.arg[0] & 0xff;
+ blocksRead = resp.arg[1];
+ if (!isOK && !blocksRead) {
+ PrintAndLog("Read Block Failed 2");
+ ul_switch_off_field();
+ return 0;
+ }
+
+ startindex = resp.arg[2];
+ if (blocksRead*8 > sizeof(tag_data)-gotBytes) {
+ PrintAndLog("Data exceeded Buffer size!");
+ blocksRead = (sizeof(tag_data) - gotBytes)/8;
}
+ // get dumped data from bigbuf
+ GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex);
+ WaitForResponse(CMD_ACK,NULL);
+
+ gotBytes += blocksRead*8;
+ } else { //field is still on - turn it off...
+ ul_switch_off_field();
}
+ }
- i += (len + 9);
- EndOfTransmissionTimestamp = (*((uint32_t *)(got+i))) & 0x7fffffff;
+ // add diversified keys to dump
+ if (have_debit_key) memcpy(tag_data+(3*8),div_key,8);
+ if (have_credit_key) memcpy(tag_data+(4*8),c_div_key,8);
+ // print the dump
+ printf("CSN |00| %02X %02X %02X %02X %02X %02X %02X %02X |\n",tag_data[0],tag_data[1],tag_data[2]
+ ,tag_data[3],tag_data[4],tag_data[5],tag_data[6],tag_data[7]);
+ printIclassDumpContents(tag_data, 1, (gotBytes/8)-1, gotBytes-8);
+
+ if (filename[0] == 0){
+ snprintf(filename, FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x",
+ tag_data[0],tag_data[1],tag_data[2],tag_data[3],
+ tag_data[4],tag_data[5],tag_data[6],tag_data[7]);
+ }
- // Not implemented for iclass on the ARM-side
- //if (!ShowWaitCycles) i += 9;
+ // save the dump to .bin file
+ PrintAndLog("Saving dump file - %d blocks read", gotBytes/8);
+ saveFile(filename, "bin", tag_data, gotBytes);
+ return 1;
+}
- PrintAndLog(" %9d | %9d | %s | %s %s",
- (timestamp - first_timestamp),
- (EndOfTransmissionTimestamp - first_timestamp),
- (len?(tagToReader ? "Tag" : "Rdr"):" "),
- line, crc);
+static int WriteBlock(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool verbose) {
+ uint8_t MAC[4]={0x00,0x00,0x00,0x00};
+ uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, verbose))
+ return 0;
+
+ UsbCommand resp;
+
+ Calc_wb_mac(blockno,bldata,div_key,MAC);
+ UsbCommand w = {CMD_ICLASS_WRITEBLOCK, {blockno}};
+ memcpy(w.d.asBytes, bldata, 8);
+ memcpy(w.d.asBytes + 8, MAC, 4);
+
+ clearCommandBuffer();
+ SendCommand(&w);
+ if (!WaitForResponseTimeout(CMD_ACK,&resp,4500))
+ {
+ PrintAndLog("Write Command execute timeout");
+ return 0;
+ }
+ uint8_t isOK = resp.arg[0] & 0xff;
+ if (!isOK) {
+ PrintAndLog("Write Block Failed");
+ return 0;
}
+ PrintAndLog("Write Block Successful");
+ return 1;
+}
+
+int usage_hf_iclass_writeblock(void) {
+ PrintAndLog("Options:");
+ PrintAndLog(" b <Block> : The block number as 2 hex symbols");
+ PrintAndLog(" d <data> : Set the Data to write as 16 hex symbols");
+ PrintAndLog(" k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory");
+ PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n");
+ PrintAndLog(" e : If 'e' is specified, elite computations applied to key");
+ PrintAndLog("Samples:");
+ PrintAndLog(" hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA k 001122334455667B");
+ PrintAndLog(" hf iclass writeblk b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c");
+ PrintAndLog(" hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA n 0");
return 0;
}
-int CmdHFiClassListOld(const char *Cmd)
-{
- uint8_t got[1920];
- GetFromBigBuf(got,sizeof(got),0);
-
- PrintAndLog("recorded activity:");
- PrintAndLog(" ETU :rssi: who bytes");
- PrintAndLog("---------+----+----+-----------");
-
- int i = 0;
- int prev = -1;
-
- for (;;) {
- if(i >= 1900) {
- break;
- }
-
- bool isResponse;
- int timestamp = *((uint32_t *)(got+i));
- if (timestamp & 0x80000000) {
- timestamp &= 0x7fffffff;
- isResponse = 1;
- } else {
- isResponse = 0;
- }
-
-
- int metric = 0;
-
- int parityBits = *((uint32_t *)(got+i+4));
- // 4 bytes of additional information...
- // maximum of 32 additional parity bit information
- //
- // TODO:
- // at each quarter bit period we can send power level (16 levels)
- // or each half bit period in 256 levels.
-
-
- int len = got[i+8];
-
- if (len > 100) {
- break;
- }
- if (i + len >= 1900) {
- break;
- }
-
- uint8_t *frame = (got+i+9);
-
- // Break and stick with current result if buffer was not completely full
- if (frame[0] == 0x44 && frame[1] == 0x44 && frame[3] == 0x44) { break; }
-
- char line[1000] = "";
- int j;
- for (j = 0; j < len; j++) {
- int oddparity = 0x01;
- int k;
-
- for (k=0;k<8;k++) {
- oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01);
- }
-
- //if((parityBits >> (len - j - 1)) & 0x01) {
- if (isResponse && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) {
- sprintf(line+(j*4), "%02x! ", frame[j]);
- }
- else {
- sprintf(line+(j*4), "%02x ", frame[j]);
- }
- }
-
- char *crc;
- crc = "";
- if (len > 2) {
- uint8_t b1, b2;
- for (j = 0; j < (len - 1); j++) {
- // gives problems... search for the reason..
- /*if(frame[j] == 0xAA) {
- switch(frame[j+1]) {
- case 0x01:
- crc = "[1] Two drops close after each other";
- break;
- case 0x02:
- crc = "[2] Potential SOC with a drop in second half of bitperiod";
- break;
- case 0x03:
- crc = "[3] Segment Z after segment X is not possible";
- break;
- case 0x04:
- crc = "[4] Parity bit of a fully received byte was wrong";
- break;
- default:
- crc = "[?] Unknown error";
- break;
- }
- break;
- }*/
- }
-
- if (strlen(crc)==0) {
- if(!isResponse && len == 4) {
- // Rough guess that this is a command from the reader
- // For iClass the command byte is not part of the CRC
- ComputeCrc14443(CRC_ICLASS, &frame[1], len-3, &b1, &b2);
- }
- else {
- // For other data.. CRC might not be applicable (UPDATE commands etc.)
- ComputeCrc14443(CRC_ICLASS, frame, len-2, &b1, &b2);
- }
- //printf("%1x %1x",(unsigned)b1,(unsigned)b2);
- if (b1 != frame[len-2] || b2 != frame[len-1]) {
- crc = (isResponse & (len < 8)) ? "" : " !crc";
- } else {
- crc = "";
- }
- }
- } else {
- crc = ""; // SHORT
- }
-
- char metricString[100];
- if (isResponse) {
- sprintf(metricString, "%3d", metric);
- } else {
- strcpy(metricString, " ");
- }
-
- PrintAndLog(" +%7d: %s: %s %s %s",
- (prev < 0 ? 0 : (timestamp - prev)),
- metricString,
- (isResponse ? "TAG" : " "), line, crc);
-
- prev = timestamp;
- i += (len + 9);
- }
- return 0;
-}
-
-/*void iso14a_set_timeout(uint32_t timeout) {
- UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_SET_TIMEOUT, 0, timeout}};
- SendCommand(&c);
-}*/
+int CmdHFiClass_WriteBlock(const char *Cmd) {
+ uint8_t blockno=0;
+ uint8_t bldata[8]={0,0,0,0,0,0,0,0};
+ uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t keyNbr = 0;
+ uint8_t dataLen = 0;
+ char tempStr[50] = {0};
+ bool use_credit_key = false;
+ bool elite = false;
+ bool errors = false;
+ uint8_t cmdp = 0;
+ while(param_getchar(Cmd, cmdp) != 0x00)
+ {
+ switch(param_getchar(Cmd, cmdp))
+ {
+ case 'h':
+ case 'H':
+ return usage_hf_iclass_writeblock();
+ case 'b':
+ case 'B':
+ if (param_gethex(Cmd, cmdp+1, &blockno, 2)) {
+ PrintAndLog("Block No must include 2 HEX symbols\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'c':
+ case 'C':
+ use_credit_key = true;
+ cmdp++;
+ break;
+ case 'd':
+ case 'D':
+ if (param_gethex(Cmd, cmdp+1, bldata, 16))
+ {
+ PrintAndLog("KEY must include 16 HEX symbols\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'e':
+ case 'E':
+ elite = true;
+ cmdp++;
+ break;
+ case 'k':
+ case 'K':
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, KEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(KEY, iClass_Key_Table[keyNbr], 8);
+ } else {
+ PrintAndLog("\nERROR: Credit KeyNbr is invalid\n");
+ errors = true;
+ }
+ } else {
+ PrintAndLog("\nERROR: Credit Key is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ default:
+ PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+ errors = true;
+ break;
+ }
+ if(errors) return usage_hf_iclass_writeblock();
+ }
-int CmdHFiClassSnoop(const char *Cmd)
-{
- UsbCommand c = {CMD_SNOOP_ICLASS};
- SendCommand(&c);
- return 0;
+ if (cmdp < 6) return usage_hf_iclass_writeblock();
+ int ans = WriteBlock(blockno, bldata, KEY, use_credit_key, elite, true);
+ ul_switch_off_field();
+ return ans;
}
-int CmdHFiClassSim(const char *Cmd)
-{
- uint8_t simType = 0;
- uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+int usage_hf_iclass_clone(void) {
+ PrintAndLog("Usage: hf iclass clone f <tagfile.bin> b <first block> l <last block> k <KEY> e c");
+ PrintAndLog("Options:");
+ PrintAndLog(" f <filename>: specify a filename to clone from");
+ PrintAndLog(" b <Block> : The first block to clone as 2 hex symbols");
+ PrintAndLog(" l <Last Blk>: Set the Data to write as 16 hex symbols");
+ PrintAndLog(" k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory");
+ PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n");
+ PrintAndLog(" e : If 'e' is specified, elite computations applied to key");
+ PrintAndLog("Samples:");
+ PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 06 l 1A k 1122334455667788 e");
+ PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 05 l 19 k 0");
+ PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 06 l 19 k 0 e");
+ return -1;
+}
- if (strlen(Cmd)<1) {
- PrintAndLog("Usage: hf iclass sim [0 <CSN>] | x");
- PrintAndLog(" options");
- PrintAndLog(" 0 <CSN> simulate the given CSN");
- PrintAndLog(" 1 simulate default CSN");
- PrintAndLog(" 2 iterate CSNs, gather MACs");
- PrintAndLog(" sample: hf iclass sim 0 031FEC8AF7FF12E0");
- PrintAndLog(" sample: hf iclass sim 2");
- return 0;
- }
-
- simType = param_get8(Cmd, 0);
-
- if(simType == 0)
- {
- if (param_gethex(Cmd, 1, CSN, 16)) {
- PrintAndLog("A CSN should consist of 16 HEX symbols");
- return 1;
- }
- PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8));
-
- }
- if(simType > 5)
- {
- PrintAndLog("Undefined simptype %d", simType);
- return 1;
- }
- uint8_t numberOfCSNs=0;
+int CmdHFiClassCloneTag(const char *Cmd) {
+ char filename[FILE_PATH_SIZE];
+ char tempStr[50]={0};
+ uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t keyNbr = 0;
+ uint8_t fileNameLen = 0;
+ uint8_t startblock = 0;
+ uint8_t endblock = 0;
+ uint8_t dataLen = 0;
+ bool use_credit_key = false;
+ bool elite = false;
+ bool errors = false;
+ uint8_t cmdp = 0;
+ while(param_getchar(Cmd, cmdp) != 0x00)
+ {
+ switch(param_getchar(Cmd, cmdp))
+ {
+ case 'h':
+ case 'H':
+ return usage_hf_iclass_clone();
+ case 'b':
+ case 'B':
+ if (param_gethex(Cmd, cmdp+1, &startblock, 2)) {
+ PrintAndLog("Start Block No must include 2 HEX symbols\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'c':
+ case 'C':
+ use_credit_key = true;
+ cmdp++;
+ break;
+ case 'e':
+ case 'E':
+ elite = true;
+ cmdp++;
+ break;
+ case 'f':
+ case 'F':
+ fileNameLen = param_getstr(Cmd, cmdp+1, filename);
+ if (fileNameLen < 1) {
+ PrintAndLog("No filename found after f");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'k':
+ case 'K':
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, KEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(KEY, iClass_Key_Table[keyNbr], 8);
+ } else {
+ PrintAndLog("\nERROR: Credit KeyNbr is invalid\n");
+ errors = true;
+ }
+ } else {
+ PrintAndLog("\nERROR: Credit Key is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'l':
+ case 'L':
+ if (param_gethex(Cmd, cmdp+1, &endblock, 2)) {
+ PrintAndLog("Start Block No must include 2 HEX symbols\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ default:
+ PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+ errors = true;
+ break;
+ }
+ if(errors) return usage_hf_iclass_clone();
+ }
- if(simType == 2)
+ if (cmdp < 8) return usage_hf_iclass_clone();
+
+ FILE *f;
+
+ iclass_block_t tag_data[USB_CMD_DATA_SIZE/12];
+
+ if ((endblock-startblock+1)*12 > USB_CMD_DATA_SIZE) {
+ PrintAndLog("Trying to write too many blocks at once. Max: %d", USB_CMD_DATA_SIZE/8);
+ }
+ // file handling and reading
+ f = fopen(filename,"rb");
+ if(!f) {
+ PrintAndLog("Failed to read from file '%s'", filename);
+ return 1;
+ }
+
+ if (startblock<5) {
+ PrintAndLog("You cannot write key blocks this way. yet... make your start block > 4");
+ return 0;
+ }
+ // now read data from the file from block 6 --- 19
+ // ok we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data
+ // then copy to usbcommand->asbytes; the max is 32 - 6 = 24 block 12 bytes each block 288 bytes then we can only accept to clone 21 blocks at the time,
+ // else we have to create a share memory
+ int i;
+ fseek(f,startblock*8,SEEK_SET);
+ if ( fread(tag_data,sizeof(iclass_block_t),endblock - startblock + 1,f) == 0){
+ PrintAndLog("File reading error.");
+ fclose(f);
+ return 2;
+ }
+
+ uint8_t MAC[4]={0x00,0x00,0x00,0x00};
+ uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+
+ if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, true))
+ return 0;
+
+ UsbCommand w = {CMD_ICLASS_CLONE,{startblock,endblock}};
+ uint8_t *ptr;
+ // calculate all mac for every the block we will write
+ for (i = startblock; i <= endblock; i++){
+ Calc_wb_mac(i,tag_data[i - startblock].d,div_key,MAC);
+ // usb command d start pointer = d + (i - 6) * 12
+ // memcpy(pointer,tag_data[i - 6],8) 8 bytes
+ // memcpy(pointer + 8,mac,sizoof(mac) 4 bytes;
+ // next one
+ ptr = w.d.asBytes + (i - startblock) * 12;
+ memcpy(ptr, &(tag_data[i - startblock].d[0]), 8);
+ memcpy(ptr + 8,MAC, 4);
+ }
+ uint8_t p[12];
+ for (i = 0; i <= endblock - startblock;i++){
+ memcpy(p,w.d.asBytes + (i * 12),12);
+ printf("Block |%02x|",i + startblock);
+ printf(" %02x%02x%02x%02x%02x%02x%02x%02x |",p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]);
+ printf(" MAC |%02x%02x%02x%02x|\n",p[8],p[9],p[10],p[11]);
+ }
+ UsbCommand resp;
+ SendCommand(&w);
+ if (!WaitForResponseTimeout(CMD_ACK,&resp,4500))
{
- UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,63}};
- UsbCommand resp = {0};
+ PrintAndLog("Command execute timeout");
+ return 0;
+ }
+ return 1;
+}
- uint8_t csns1[] ={
- /* Order Simulated CSN HASH1 Recovered key bytes */
- /* 1 */ 0x00,0x0B,0x0F,0xFF,0xF7,0xFF,0x12,0xE0,// 0101000045014545 00,01 45
- /* 2 */ 0x03,0x0B,0x0E,0xFE,0xF7,0xFF,0x12,0xE0,// 0202000045014545 02
- /* 3 */ 0x04,0x0D,0x0D,0xFD,0xF7,0xFF,0x12,0xE0,// 0303000045014545 03
- /* 4 */ 0x04,0x0F,0x0F,0xF7,0xF7,0xFF,0x12,0xE0,// 0901000045014545 09
- /* 5 */ 0x01,0x13,0x10,0xF4,0xF7,0xFF,0x12,0xE0,// 0C00000045014545 0C
- /* 6 */ 0x02,0x14,0x10,0xF2,0xF7,0xFF,0x12,0xE0,// 0E00000045014545 0E
- /* 7 */ 0x05,0x17,0x10,0xEC,0xF7,0xFF,0x12,0xE0,// 1400000045014545 14
- /* 8 */ 0x00,0x6B,0x6F,0xDF,0xF7,0xFF,0x12,0xE0,// 2121000045014545 21
- /* 9 */ 0x03,0x6B,0x6E,0xDE,0xF7,0xFF,0x12,0xE0,// 2222000045014545 22
- /* 10 */ 0x04,0x6D,0x6D,0xDD,0xF7,0xFF,0x12,0xE0,// 2323000045014545 23
- /* 11 */ 0x00,0x4F,0x4B,0x43,0xF7,0xFF,0x12,0xE0,// 3D45000045014545 3D
- /* 12 */ 0x00,0x4B,0x4F,0x3F,0xF7,0xFF,0x12,0xE0,// 4141000045014545 41
- /* 13 */ 0x03,0x4B,0x4E,0x3E,0xF7,0xFF,0x12,0xE0,// 4242000045014545 42
- /* 14 */ 0x04,0x4D,0x4D,0x3D,0xF7,0xFF,0x12,0xE0,// 4343000045014545 43
- /* 15 */ 0x04,0x37,0x37,0x7F,0xF7,0xFF,0x12,0xE0,// 0159000045014545 59
- /* 16 */ 0x00,0x2B,0x2F,0x9F,0xF7,0xFF,0x12,0xE0,// 6161000045014545 61
- /* 17 */ 0x03,0x2B,0x2E,0x9E,0xF7,0xFF,0x12,0xE0,// 6262000045014545 62
- /* 18 */ 0x04,0x2D,0x2D,0x9D,0xF7,0xFF,0x12,0xE0,// 6363000045014545 63
- /* 19 */ 0x00,0x27,0x23,0xBB,0xF7,0xFF,0x12,0xE0,// 456D000045014545 6D
- /* 20 */ 0x02,0x52,0xAA,0x80,0xF7,0xFF,0x12,0xE0,// 0066000045014545 66
- /* 21 */ 0x00,0x5C,0xA6,0x80,0xF7,0xFF,0x12,0xE0,// 006A000045014545 6A
- /* 22 */ 0x01,0x5F,0xA4,0x80,0xF7,0xFF,0x12,0xE0,// 006C000045014545 6C
- /* 23 */ 0x06,0x5E,0xA2,0x80,0xF7,0xFF,0x12,0xE0,// 006E000045014545 6E
- /* 24 */ 0x02,0x0E,0x0E,0xFC,0xF7,0xFF,0x12,0xE0,// 0402000045014545 04
- /* 25 */ 0x05,0x0D,0x0E,0xFA,0xF7,0xFF,0x12,0xE0,// 0602000045014545 06
- /* 26 */ 0x06,0x0F,0x0D,0xF9,0xF7,0xFF,0x12,0xE0,// 0703000045014545 07
- /* 27 */ 0x00,0x01,0x05,0x1D,0xF7,0xFF,0x12,0xE0,// 630B000045014545 0B
- /* 28 */ 0x02,0x07,0x01,0x1D,0xF7,0xFF,0x12,0xE0,// 630F000045014545 0F
- /* 29 */ 0x04,0x7F,0x7F,0xA7,0xF7,0xFF,0x12,0xE0,// 5911000045014545 11
- /* 30 */ 0x04,0x60,0x6E,0xE8,0xF7,0xFF,0x12,0xE0,// 1822000045014545 18
- /*31*/ 0x04,0x77,0x77,0xBF,0xF7,0xFF,0x12,0xE0, // 4119000045014545 19
- /*32*/ 0x00,0x69,0x6D,0xE5,0xF7,0xFF,0x12,0xE0, // 1B23000045014545 1B
- /*33*/ 0x01,0x69,0x6E,0xE2,0xF7,0xFF,0x12,0xE0, // 1E22000045014545 1E
- /*34*/ 0x02,0x6B,0x6D,0xE1,0xF7,0xFF,0x12,0xE0, // 1F23000045014545 1F
- /*35*/ 0x01,0x3F,0x04,0xE0,0xF7,0xFF,0x12,0xE0, // 200C000045014545 20
- /*36*/ 0x02,0x6E,0x6E,0xDC,0xF7,0xFF,0x12,0xE0, // 2422000045014545 24
- /*37*/ 0x05,0x6D,0x6E,0xDA,0xF7,0xFF,0x12,0xE0, // 2622000045014545 26
- /*38*/ 0x06,0x6F,0x6D,0xD9,0xF7,0xFF,0x12,0xE0, // 2723000045014545 27
- /*39*/ 0x01,0x6B,0x68,0xEC,0xF7,0xFF,0x12,0xE0, // 1428000045014545 28
- /*40*/ 0x04,0x6F,0x6F,0xD7,0xF7,0xFF,0x12,0xE0, // 2921000045014545 29
- /*41*/ 0x02,0x66,0x66,0xF4,0xF7,0xFF,0x12,0xE0, // 0C2A000045014545 2A
- /*42*/ 0x00,0x61,0x65,0xFD,0xF7,0xFF,0x12,0xE0, // 032B000045014545 2B
- /*43*/ 0x00,0x62,0x64,0xFE,0xF7,0xFF,0x12,0xE0, // 022C000045014545 2C
- /*44*/ 0x01,0x65,0x62,0xFE,0xF7,0xFF,0x12,0xE0, // 022E000045014545 2E
- /*45*/ 0x02,0x67,0x61,0xFD,0xF7,0xFF,0x12,0xE0, // 032F000045014545 2F
- /*46*/ 0x00,0x5F,0x5B,0x13,0xF7,0xFF,0x12,0xE0, // 6D35000045014545 35
- /*47*/ 0x00,0x44,0x4E,0x48,0xF7,0xFF,0x12,0xE0, // 3842000045014545 38
- /*48*/ 0x00,0x53,0x57,0x27,0xF7,0xFF,0x12,0xE0, // 5939000045014545 39
- /*49*/ 0x00,0x49,0x4D,0x45,0xF7,0xFF,0x12,0xE0, // 3B43000045014545 3B
- /*50*/ 0x01,0x49,0x4E,0x42,0xF7,0xFF,0x12,0xE0, // 3E42000045014545 3E
- /*51*/ 0x02,0x4B,0x4D,0x41,0xF7,0xFF,0x12,0xE0, // 3F43000045014545 3F
- /*52*/ 0x02,0x4E,0x4E,0x3C,0xF7,0xFF,0x12,0xE0, // 4442000045014545 44
- /*53*/ 0x05,0x4D,0x4E,0x3A,0xF7,0xFF,0x12,0xE0, // 4642000045014545 46
- /*54*/ 0x06,0x4F,0x4D,0x39,0xF7,0xFF,0x12,0xE0, // 4743000045014545 47
- /*55*/ 0x01,0x77,0x7C,0xB8,0xF7,0xFF,0x12,0xE0, // 4814000045014545 48
- /*56*/ 0x04,0x4F,0x4F,0x37,0xF7,0xFF,0x12,0xE0, // 4941000045014545 49
- /*57*/ 0x00,0x7A,0x7C,0xB6,0xF7,0xFF,0x12,0xE0, // 4A14000045014545 4A
- /*58*/ 0x00,0x41,0x45,0x5D,0xF7,0xFF,0x12,0xE0, // 234B000045014545 4B
- /*59*/ 0x00,0x42,0x44,0x5E,0xF7,0xFF,0x12,0xE0, // 224C000045014545 4C
- /*60*/ 0x01,0x45,0x42,0x5E,0xF7,0xFF,0x12,0xE0, // 224E000045014545 4E
- /*61*/ 0x02,0x47,0x41,0x5D,0xF7,0xFF,0x12,0xE0, // 234F000045014545 4F
- /*62*/ 0x04,0x7E,0x7C,0xAE,0xF7,0xFF,0x12,0xE0, // 5214000045014545 52
- /*63*/ 0x00,0x57,0x53,0x2B,0xF7,0xFF,0x12,0xE0, // 553D000045014545 55
- };
- memcpy(c.d.asBytes, csns1, sizeof(c.d.asBytes));
+static int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool verbose) {
+ uint8_t MAC[4]={0x00,0x00,0x00,0x00};
+ uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
- SendCommand(&c);
- if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) {
- PrintAndLog("Command timed out");
- return 0;
+ if (!select_and_auth(KEY, MAC, div_key, (keyType==0x18), elite, verbose))
+ return 0;
+
+ UsbCommand resp;
+ UsbCommand w = {CMD_ICLASS_READBLOCK, {blockno}};
+ clearCommandBuffer();
+ SendCommand(&w);
+ if (!WaitForResponseTimeout(CMD_ACK,&resp,4500))
+ {
+ PrintAndLog("Command execute timeout");
+ return 0;
+ }
+ uint8_t isOK = resp.arg[0] & 0xff;
+ if (!isOK) {
+ PrintAndLog("Read Block Failed");
+ return 0;
+ }
+ //data read is stored in: resp.d.asBytes[0-15]
+ if (verbose) PrintAndLog("Block %02X: %s\n",blockno, sprint_hex(resp.d.asBytes,8));
+ return 1;
+}
+
+int usage_hf_iclass_readblock(void) {
+ PrintAndLog("Usage: hf iclass readblk b <Block> k <Key> c e\n");
+ PrintAndLog("Options:");
+ PrintAndLog(" b <Block> : The block number as 2 hex symbols");
+ PrintAndLog(" k <Key> : Access Key as 16 hex symbols or 1 hex to select key from memory");
+ PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n");
+ PrintAndLog(" e : If 'e' is specified, elite computations applied to key");
+ PrintAndLog("Samples:");
+ PrintAndLog(" hf iclass readblk b 06 k 0011223344556677");
+ PrintAndLog(" hf iclass readblk b 1B k 0011223344556677 c");
+ PrintAndLog(" hf iclass readblk b 0A k 0");
+ return 0;
+}
+
+int CmdHFiClass_ReadBlock(const char *Cmd) {
+ uint8_t blockno=0;
+ uint8_t keyType = 0x88; //debit key
+ uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t keyNbr = 0;
+ uint8_t dataLen = 0;
+ char tempStr[50] = {0};
+ bool elite = false;
+ bool errors = false;
+ uint8_t cmdp = 0;
+ while(param_getchar(Cmd, cmdp) != 0x00)
+ {
+ switch(param_getchar(Cmd, cmdp))
+ {
+ case 'h':
+ case 'H':
+ return usage_hf_iclass_readblock();
+ case 'b':
+ case 'B':
+ if (param_gethex(Cmd, cmdp+1, &blockno, 2)) {
+ PrintAndLog("Block No must include 2 HEX symbols\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'c':
+ case 'C':
+ keyType = 0x18;
+ cmdp++;
+ break;
+ case 'e':
+ case 'E':
+ elite = true;
+ cmdp++;
+ break;
+ case 'k':
+ case 'K':
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, KEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(KEY, iClass_Key_Table[keyNbr], 8);
+ } else {
+ PrintAndLog("\nERROR: Credit KeyNbr is invalid\n");
+ errors = true;
+ }
+ } else {
+ PrintAndLog("\nERROR: Credit Key is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ default:
+ PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+ errors = true;
+ break;
}
+ if(errors) return usage_hf_iclass_readblock();
+ }
- uint8_t num_mac_responses_1 = resp.arg[1];
- PrintAndLog("Mac responses, first part : %d MACs obtained (should be 63)", num_mac_responses_1);
-
- UsbCommand c2 = {CMD_SIMULATE_TAG_ICLASS, {simType,63}};
- UsbCommand resp2 = {0};
-
- uint8_t csns2[] ={
- /*64*/ 0x04,0x3C,0x3A,0x74,0xF7,0xFF,0x12,0xE0, // 0C56000045014545 56
- /*65*/ 0x00,0x24,0x2E,0xA8,0xF7,0xFF,0x12,0xE0, // 5862000045014545 58
- /*66*/ 0x00,0x29,0x2D,0xA5,0xF7,0xFF,0x12,0xE0, // 5B63000045014545 5B
- /*67*/ 0x00,0x00,0x02,0x24,0xF7,0xFF,0x12,0xE0, // 5C0E000045014545 5C
- /*68*/ 0x01,0x29,0x2E,0xA2,0xF7,0xFF,0x12,0xE0, // 5E62000045014545 5E
- /*69*/ 0x02,0x2B,0x2D,0xA1,0xF7,0xFF,0x12,0xE0, // 5F63000045014545 5F
- /*70*/ 0x02,0x2E,0x2E,0x9C,0xF7,0xFF,0x12,0xE0, // 6462000045014545 64
- /*71*/ 0x02,0x0A,0x02,0x18,0xF7,0xFF,0x12,0xE0, // 680E000045014545 68
- /*72*/ 0x00,0x03,0x07,0x17,0xF7,0xFF,0x12,0xE0, // 6909000045014545 69
- /*73*/ 0x00,0x21,0x25,0xBD,0xF7,0xFF,0x12,0xE0, // 436B000045014545 6B
- /*74*/ 0x02,0x27,0x21,0xBD,0xF7,0xFF,0x12,0xE0, // 436F000045014545 6F
- /*75*/ 0x04,0x07,0x07,0x0F,0xF7,0xFF,0x12,0xE0, // 7109000045014545 71
- /*76*/ 0x00,0x04,0x0E,0x08,0xF7,0xFF,0x12,0xE0, // 7802000045014545 78
- /*77*/ 0x00,0x33,0x37,0x87,0xF7,0xFF,0x12,0xE0, // 7959000045014545 79
- /*78*/ 0x00,0x09,0x0D,0x05,0xF7,0xFF,0x12,0xE0, // 7B03000045014545 7B
- /*79*/ 0x01,0x09,0x0E,0x02,0xF7,0xFF,0x12,0xE0, // 7E02000045014545 7E
- /*80*/ 0x02,0x0B,0x0D,0x01,0xF7,0xFF,0x12,0xE0, // 7F03000045014545 7F
- /*81*/ 0x00,0x34,0x3E,0x78,0xF7,0xFF,0x12,0xE0, // 0852000045014545 08
- /*82*/ 0x04,0x66,0x64,0xF6,0xF7,0xFF,0x12,0xE0, // 0A2C000045014545 0A
- /*83*/ 0x00,0x3F,0x3B,0x73,0xF7,0xFF,0x12,0xE0, // 0D55000045014545 0D
- /*84*/ 0x03,0x3B,0x3E,0x6E,0xF7,0xFF,0x12,0xE0, // 1252000045014545 12
- /*85*/ 0x00,0x11,0x15,0xED,0xF7,0xFF,0x12,0xE0, // 137B000045014545 13
- /*86*/ 0x00,0x6E,0x68,0xEA,0xF7,0xFF,0x12,0xE0, // 1628000045014545 16
- /*87*/ 0x00,0x6D,0x69,0xE9,0xF7,0xFF,0x12,0xE0, // 1727000045014545 17
- /*88*/ 0x00,0x6A,0x6C,0xE6,0xF7,0xFF,0x12,0xE0, // 1A24000045014545 1A
- /*89*/ 0x00,0x40,0x42,0x64,0xF7,0xFF,0x12,0xE0, // 1C4E000045014545 1C
- /*90*/ 0x00,0x77,0x73,0xCB,0xF7,0xFF,0x12,0xE0, // 351D000045014545 1D
- /*91*/ 0x06,0x6E,0x72,0xD0,0xF7,0xFF,0x12,0xE0, // 301E000045014545 30
- /*92*/ 0x00,0x1B,0x1F,0xCF,0xF7,0xFF,0x12,0xE0, // 3171000045014545 31
- /*93*/ 0x01,0x75,0x72,0xCE,0xF7,0xFF,0x12,0xE0, // 321E000045014545 32
- /*94*/ 0x00,0x71,0x75,0xCD,0xF7,0xFF,0x12,0xE0, // 331B000045014545 33
- /*95*/ 0x00,0x48,0x4A,0x4C,0xF7,0xFF,0x12,0xE0, // 3446000045014545 34
- /*96*/ 0x00,0x4E,0x48,0x4A,0xF7,0xFF,0x12,0xE0, // 3648000045014545 36
- /*97*/ 0x00,0x4D,0x49,0x49,0xF7,0xFF,0x12,0xE0, // 3747000045014545 37
- /*98*/ 0x00,0x4A,0x4C,0x46,0xF7,0xFF,0x12,0xE0, // 3A44000045014545 3A
- /*99*/ 0x00,0x20,0x22,0xC4,0xF7,0xFF,0x12,0xE0, // 3C6E000045014545 3C
- /*100*/ 0x00,0x1C,0x66,0x40,0xF7,0xFF,0x12,0xE0, // 402A000045014545 40
- /*101*/ 0x06,0x4E,0x52,0x30,0xF7,0xFF,0x12,0xE0, // 503E000045014545 50
- /*102*/ 0x00,0x7B,0x7F,0xAF,0xF7,0xFF,0x12,0xE0, // 5111000045014545 51
- /*103*/ 0x00,0x51,0x55,0x2D,0xF7,0xFF,0x12,0xE0, // 533B000045014545 53
- /*104*/ 0x00,0x28,0x2A,0xAC,0xF7,0xFF,0x12,0xE0, // 5466000045014545 54
- /*105*/ 0x02,0x53,0x55,0x29,0xF7,0xFF,0x12,0xE0, // 573B000045014545 57
- /*106*/ 0x00,0x2A,0x2C,0xA6,0xF7,0xFF,0x12,0xE0, // 5A64000045014545 5A
- /*107*/ 0x00,0x7C,0x46,0x20,0xF7,0xFF,0x12,0xE0, // 604A000045014545 60
- /*108*/ 0x02,0x03,0x05,0x19,0xF7,0xFF,0x12,0xE0, // 670B000045014545 67
- /*109*/ 0x01,0x2F,0x34,0x90,0xF7,0xFF,0x12,0xE0, // 705C000045014545 70
- /*110*/ 0x00,0x32,0x34,0x8E,0xF7,0xFF,0x12,0xE0, // 725C000045014545 72
- /*111*/ 0x00,0x31,0x35,0x8D,0xF7,0xFF,0x12,0xE0, // 735B000045014545 73
- /*112*/ 0x00,0x08,0x0A,0x0C,0xF7,0xFF,0x12,0xE0, // 7406000045014545 74
- /*113*/ 0x03,0x37,0x32,0x8A,0xF7,0xFF,0x12,0xE0, // 765E000045014545 76
- /*114*/ 0x00,0x0D,0x09,0x09,0xF7,0xFF,0x12,0xE0, // 7707000045014545 77
- /*115*/ 0x00,0x0A,0x0C,0x06,0xF7,0xFF,0x12,0xE0, // 7A04000045014545 7A
- /*116*/ 0x00,0x60,0x62,0x04,0xF7,0xFF,0x12,0xE0, // 7C2E000045014545 7C
- /*117*/ 0x00,0x07,0x03,0x1B,0xF7,0xFF,0x12,0xE0, // 650D000045014545 65
- /*118*/ 0x00,0x0C,0x16,0xF0,0xF7,0xFF,0x12,0xE0, // 107A000045014545 10
- /*119*/ 0x00,0x6F,0x6B,0xE3,0xF7,0xFF,0x12,0xE0, // 1D25000045014545 25
- /*120*/ 0x00,0x2F,0x2B,0xA3,0xF7,0xFF,0x12,0xE0, // 5D65000045014545 5D
- /*121*/ 0x00,0x47,0x43,0x5B,0xF7,0xFF,0x12,0xE0, // 254D000045014545 4D
- /*122*/ 0x00,0x37,0x33,0x8B,0xF7,0xFF,0x12,0xE0, // 755D000045014545 75
- /*123*/ 0x00,0x1F,0x1B,0xD3,0xF7,0xFF,0x12,0xE0, // 2D75000045014545 2D
- /*124*/ 0x00,0x67,0x63,0xFB,0xF7,0xFF,0x12,0xE0, // 052D000045014545 05
- /*125*/ 0x00,0x0F,0x0B,0x03,0xF7,0xFF,0x12,0xE0, // 7D05000045014545 7D
- /*126*/ 0x00,0x17,0x13,0xEB,0xF7,0xFF,0x12,0xE0, // 157D000045014545 15
- };
-
- memcpy(c2.d.asBytes, csns2, sizeof(c2.d.asBytes));
+ if (cmdp < 4) return usage_hf_iclass_readblock();
- SendCommand(&c);
- if (!WaitForResponseTimeout(CMD_ACK, &resp2, -1)) {
- PrintAndLog("Command timed out");
- return 0;
+ return ReadBlock(KEY, blockno, keyType, elite, true);
+}
+
+int CmdHFiClass_loclass(const char *Cmd) {
+ char opt = param_getchar(Cmd, 0);
+
+ if (strlen(Cmd)<1 || opt == 'h') {
+ PrintAndLog("Usage: hf iclass loclass [options]");
+ PrintAndLog("Options:");
+ PrintAndLog("h Show this help");
+ PrintAndLog("t Perform self-test");
+ PrintAndLog("f <filename> Bruteforce iclass dumpfile");
+ PrintAndLog(" An iclass dumpfile is assumed to consist of an arbitrary number of");
+ PrintAndLog(" malicious CSNs, and their protocol responses");
+ PrintAndLog(" The the binary format of the file is expected to be as follows: ");
+ PrintAndLog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>");
+ PrintAndLog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>");
+ PrintAndLog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>");
+ PrintAndLog(" ... totalling N*24 bytes");
+ return 0;
+ }
+ char fileName[255] = {0};
+ if(opt == 'f')
+ {
+ if(param_getstr(Cmd, 1, fileName) > 0)
+ {
+ return bruteforceFileNoKeys(fileName);
+ }else
+ {
+ PrintAndLog("You must specify a filename");
}
- uint8_t num_mac_responses_2 = resp2.arg[1];
- PrintAndLog("Mac responses, second part : %d MACs obtained (should be 63)", num_mac_responses_2);
- size_t datalen = 126 * 16;
- /*
- * Now, time to dump to file. We'll use this format:
- * <8-byte CSN><8-byte MAC>....
- * So, it should wind up as
- * (63+63) * (8 + 8 ) bytes.
- **/
- void* macs = malloc(datalen);
- uint8_t i = 0;
- while(i < 63)
+ }
+ else if(opt == 't')
+ {
+ int errors = testCipherUtils();
+ errors += testMAC();
+ errors += doKeyTests(0);
+ errors += testElite();
+ if(errors)
{
- memcpy(macs+i*16, csns1+i*8,8);
- memcpy(macs+i*16+8, resp.d.asBytes+i*8,8);
-
- memcpy(macs+i*16+63*16, csns2+i*8,8);
- memcpy(macs+i*16+8+63*16, resp2.d.asBytes+i*8,8);
- i++;
+ prnlog("OBS! There were errors!!!");
}
- /** Now, save to dumpfile **/
- saveFile("iclass_mac_attack", "bin", macs,datalen);
- free(macs);
- }else
+ return errors;
+ }
+
+ return 0;
+}
+
+void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize) {
+ uint8_t blockdata[8];
+ uint8_t mem_config;
+ memcpy(&mem_config, iclass_dump + 13,1);
+ uint8_t maxmemcount;
+ uint8_t filemaxblock = filesize / 8;
+ if (mem_config & 0x80)
+ maxmemcount = 255;
+ else
+ maxmemcount = 31;
+ //PrintAndLog ("endblock: %d, filesize: %d, maxmemcount: %d, filemaxblock: %d", endblock,filesize, maxmemcount, filemaxblock);
+
+ if (startblock == 0)
+ startblock = 6;
+ if ((endblock > maxmemcount) || (endblock == 0))
+ endblock = maxmemcount;
+ if (endblock > filemaxblock)
+ endblock = filemaxblock;
+ int i = startblock;
+ int j;
+ while (i <= endblock){
+ printf("Block |%02X| ",i);
+ memcpy(blockdata,iclass_dump + (i * 8),8);
+ for (j = 0;j < 8;j++)
+ printf("%02X ",blockdata[j]);
+ printf("|\n");
+ i++;
+ }
+}
+
+int usage_hf_iclass_readtagfile() {
+ PrintAndLog("Usage: hf iclass readtagfile <filename> [startblock] [endblock]");
+ return 1;
+}
+
+int CmdHFiClassReadTagFile(const char *Cmd) {
+ int startblock = 0;
+ int endblock = 0;
+ char tempnum[5];
+ FILE *f;
+ char filename[FILE_PATH_SIZE];
+ if (param_getstr(Cmd, 0, filename) < 1)
+ return usage_hf_iclass_readtagfile();
+ if (param_getstr(Cmd,1,(char *)&tempnum) < 1)
+ startblock = 0;
+ else
+ sscanf(tempnum,"%d",&startblock);
+
+ if (param_getstr(Cmd,2,(char *)&tempnum) < 1)
+ endblock = 0;
+ else
+ sscanf(tempnum,"%d",&endblock);
+ // file handling and reading
+ f = fopen(filename,"rb");
+ if(!f) {
+ PrintAndLog("Failed to read from file '%s'", filename);
+ return 1;
+ }
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ uint8_t *dump = malloc(fsize);
+
+
+ size_t bytes_read = fread(dump, 1, fsize, f);
+ fclose(f);
+ uint8_t *csn = dump;
+ printf("CSN [00] | %02X %02X %02X %02X %02X %02X %02X %02X |\n",csn[0],csn[1],csn[2],csn[3],csn[4],csn[5],csn[6],csn[7]);
+ // printIclassDumpInfo(dump);
+ printIclassDumpContents(dump,startblock,endblock,bytes_read);
+ free(dump);
+ return 0;
+}
+
+/*
+uint64_t xorcheck(uint64_t sdiv,uint64_t hdiv) {
+ uint64_t new_div = 0x00;
+ new_div ^= sdiv;
+ new_div ^= hdiv;
+ return new_div;
+}
+
+uint64_t hexarray_to_uint64(uint8_t *key) {
+ char temp[17];
+ uint64_t uint_key;
+ for (int i = 0;i < 8;i++)
+ sprintf(&temp[(i *2)],"%02X",key[i]);
+ temp[16] = '\0';
+ if (sscanf(temp,"%016"llX,&uint_key) < 1)
+ return 0;
+ return uint_key;
+}
+*/
+void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite){
+ uint8_t keytable[128] = {0};
+ uint8_t key_index[8] = {0};
+ if (elite) {
+ uint8_t key_sel[8] = { 0 };
+ uint8_t key_sel_p[8] = { 0 };
+ hash2(KEY, keytable);
+ hash1(CSN, key_index);
+ for(uint8_t i = 0; i < 8 ; i++)
+ key_sel[i] = keytable[key_index[i]] & 0xFF;
+
+ //Permute from iclass format to standard format
+ permutekey_rev(key_sel, key_sel_p);
+ diversifyKey(CSN, key_sel_p, div_key);
+ } else {
+ diversifyKey(CSN, KEY, div_key);
+ }
+}
+
+//when told CSN, oldkey, newkey, if new key is elite (elite), and if old key was elite (oldElite)
+//calculate and return xor_div_key (ready for a key write command)
+//print all div_keys if verbose
+static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, uint8_t *xor_div_key, bool elite, bool oldElite, bool verbose){
+ uint8_t old_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t new_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ //get old div key
+ HFiClassCalcDivKey(CSN, OLDKEY, old_div_key, oldElite);
+ //get new div key
+ HFiClassCalcDivKey(CSN, NEWKEY, new_div_key, elite);
+
+ for (uint8_t i = 0; i < sizeof(old_div_key); i++){
+ xor_div_key[i] = old_div_key[i] ^ new_div_key[i];
+ }
+ if (verbose) {
+ printf("Old Div Key : %s\n",sprint_hex(old_div_key,8));
+ printf("New Div Key : %s\n",sprint_hex(new_div_key,8));
+ printf("Xor Div Key : %s\n",sprint_hex(xor_div_key,8));
+ }
+}
+
+int usage_hf_iclass_calc_newkey(void) {
+ PrintAndLog("HELP : Manage iClass Keys in client memory:\n");
+ PrintAndLog("Usage: hf iclass calc_newkey o <Old key> n <New key> s [csn] e");
+ PrintAndLog(" Options:");
+ PrintAndLog(" o <oldkey> : *specify a key as 16 hex symbols or a key number as 1 symbol");
+ PrintAndLog(" n <newkey> : *specify a key as 16 hex symbols or a key number as 1 symbol");
+ PrintAndLog(" s <csn> : specify a card Serial number to diversify the key (if omitted will attempt to read a csn)");
+ PrintAndLog(" e : specify new key as elite calc");
+ PrintAndLog(" ee : specify old and new key as elite calc");
+ PrintAndLog("Samples:");
+ PrintAndLog(" e key to e key given csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899 s deadbeafdeadbeaf ee");
+ PrintAndLog(" std key to e key read csn: hf iclass calcnewkey o 1122334455667788 n 2233445566778899 e");
+ PrintAndLog(" std to std read csn : hf iclass calcnewkey o 1122334455667788 n 2233445566778899");
+ PrintAndLog("NOTE: * = required\n");
+
+ return 1;
+}
+
+int CmdHFiClassCalcNewKey(const char *Cmd) {
+ uint8_t OLDKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t NEWKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t xor_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t CSN[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t CCNR[12] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
+ uint8_t keyNbr = 0;
+ uint8_t dataLen = 0;
+ char tempStr[50] = {0};
+ bool givenCSN = false;
+ bool oldElite = false;
+ bool elite = false;
+ bool errors = false;
+ uint8_t cmdp = 0;
+ while(param_getchar(Cmd, cmdp) != 0x00)
{
- UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}};
- memcpy(c.d.asBytes, CSN, 8);
- SendCommand(&c);
+ switch(param_getchar(Cmd, cmdp))
+ {
+ case 'h':
+ case 'H':
+ return usage_hf_iclass_calc_newkey();
+ case 'e':
+ case 'E':
+ dataLen = param_getstr(Cmd, cmdp, tempStr);
+ if (dataLen==2)
+ oldElite = true;
+ elite = true;
+ cmdp++;
+ break;
+ case 'n':
+ case 'N':
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, NEWKEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(NEWKEY, iClass_Key_Table[keyNbr], 8);
+ } else {
+ PrintAndLog("\nERROR: NewKey Nbr is invalid\n");
+ errors = true;
+ }
+ } else {
+ PrintAndLog("\nERROR: NewKey is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'o':
+ case 'O':
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) {
+ errors = param_gethex(tempStr, 0, OLDKEY, dataLen);
+ } else if (dataLen == 1) {
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr <= ICLASS_KEYS_MAX) {
+ memcpy(OLDKEY, iClass_Key_Table[keyNbr], 8);
+ } else {
+ PrintAndLog("\nERROR: Credit KeyNbr is invalid\n");
+ errors = true;
+ }
+ } else {
+ PrintAndLog("\nERROR: Credit Key is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 's':
+ case 'S':
+ givenCSN = true;
+ if (param_gethex(Cmd, cmdp+1, CSN, 16))
+ return usage_hf_iclass_calc_newkey();
+ cmdp += 2;
+ break;
+ default:
+ PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+ errors = true;
+ break;
+ }
+ if(errors) return usage_hf_iclass_calc_newkey();
}
- return 0;
+
+ if (cmdp < 4) return usage_hf_iclass_calc_newkey();
+
+ if (!givenCSN)
+ if (!select_only(CSN, CCNR, false, true))
+ return 0;
+
+ HFiClassCalcNewKey(CSN, OLDKEY, NEWKEY, xor_div_key, elite, oldElite, true);
+ return 0;
}
-int CmdHFiClassReader(const char *Cmd)
-{
- uint8_t readerType = 0;
+static int loadKeys(char *filename) {
+ FILE *f;
+ f = fopen(filename,"rb");
+ if(!f) {
+ PrintAndLog("Failed to read from file '%s'", filename);
+ return 0;
+ }
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ uint8_t *dump = malloc(fsize);
+
+ size_t bytes_read = fread(dump, 1, fsize, f);
+ fclose(f);
+ if (bytes_read > ICLASS_KEYS_MAX * 8){
+ PrintAndLog("File is too long to load - bytes: %u", bytes_read);
+ free(dump);
+ return 0;
+ }
+ uint8_t i = 0;
+ for (; i < bytes_read/8; i++){
+ memcpy(iClass_Key_Table[i],dump+(i*8),8);
+ }
+ free(dump);
+ PrintAndLog("%u keys loaded", i);
+ return 1;
+}
- if (strlen(Cmd)<1) {
- PrintAndLog("Usage: hf iclass reader <reader type>");
- PrintAndLog(" sample: hf iclass reader 0");
+static int saveKeys(char *filename) {
+ FILE *f;
+ f = fopen(filename,"wb");
+ if (f == NULL) {
+ printf("error opening file %s\n",filename);
+ return 0;
+ }
+ for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++){
+ if (fwrite(iClass_Key_Table[i],8,1,f) != 1){
+ PrintAndLog("save key failed to write to file: %s", filename);
+ break;
+ }
+ }
+ fclose(f);
return 0;
- }
+}
- readerType = param_get8(Cmd, 0);
- PrintAndLog("--readertype:%02x", readerType);
+static int printKeys(void) {
+ PrintAndLog("");
+ for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++){
+ PrintAndLog("%u: %s",i,sprint_hex(iClass_Key_Table[i],8));
+ }
+ PrintAndLog("");
+ return 0;
+}
- UsbCommand c = {CMD_READER_ICLASS, {readerType}};
- //memcpy(c.d.asBytes, CSN, 8);
- SendCommand(&c);
+int usage_hf_iclass_managekeys(void) {
+ PrintAndLog("HELP : Manage iClass Keys in client memory:\n");
+ PrintAndLog("Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p\n");
+ PrintAndLog(" Options:");
+ PrintAndLog(" n <keynbr> : specify the keyNbr to set in memory");
+ PrintAndLog(" k <key> : set a key in memory");
+ PrintAndLog(" f <filename>: specify a filename to use with load or save operations");
+ PrintAndLog(" s : save keys in memory to file specified by filename");
+ PrintAndLog(" l : load keys to memory from file specified by filename");
+ PrintAndLog(" p : print keys loaded into memory\n");
+ PrintAndLog("Samples:");
+ PrintAndLog(" set key : hf iclass managekeys n 0 k 1122334455667788");
+ PrintAndLog(" save key file: hf iclass managekeys f mykeys.bin s");
+ PrintAndLog(" load key file: hf iclass managekeys f mykeys.bin l");
+ PrintAndLog(" print keys : hf iclass managekeys p\n");
+ return 0;
+}
- /*UsbCommand * resp = WaitForResponseTimeout(CMD_ACK, 1500);
- if (resp != NULL) {
- uint8_t isOK = resp->arg[0] & 0xff;
- PrintAndLog("isOk:%02x", isOK);
- } else {
- PrintAndLog("Command execute timeout");
- }*/
+int CmdHFiClassManageKeys(const char *Cmd) {
+ uint8_t keyNbr = 0;
+ uint8_t dataLen = 0;
+ uint8_t KEY[8] = {0};
+ char filename[FILE_PATH_SIZE];
+ uint8_t fileNameLen = 0;
+ bool errors = false;
+ uint8_t operation = 0;
+ char tempStr[20];
+ uint8_t cmdp = 0;
+
+ while(param_getchar(Cmd, cmdp) != 0x00)
+ {
+ switch(param_getchar(Cmd, cmdp))
+ {
+ case 'h':
+ case 'H':
+ return usage_hf_iclass_managekeys();
+ case 'f':
+ case 'F':
+ fileNameLen = param_getstr(Cmd, cmdp+1, filename);
+ if (fileNameLen < 1) {
+ PrintAndLog("No filename found after f");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'n':
+ case 'N':
+ keyNbr = param_get8(Cmd, cmdp+1);
+ if (keyNbr < 0) {
+ PrintAndLog("Wrong block number");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'k':
+ case 'K':
+ operation += 3; //set key
+ dataLen = param_getstr(Cmd, cmdp+1, tempStr);
+ if (dataLen == 16) { //ul-c or ev1/ntag key length
+ errors = param_gethex(tempStr, 0, KEY, dataLen);
+ } else {
+ PrintAndLog("\nERROR: Key is incorrect length\n");
+ errors = true;
+ }
+ cmdp += 2;
+ break;
+ case 'p':
+ case 'P':
+ operation += 4; //print keys in memory
+ cmdp++;
+ break;
+ case 'l':
+ case 'L':
+ operation += 5; //load keys from file
+ cmdp++;
+ break;
+ case 's':
+ case 'S':
+ operation += 6; //save keys to file
+ cmdp++;
+ break;
+ default:
+ PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp));
+ errors = true;
+ break;
+ }
+ if(errors) return usage_hf_iclass_managekeys();
+ }
+ if (operation == 0){
+ PrintAndLog("no operation specified (load, save, or print)\n");
+ return usage_hf_iclass_managekeys();
+ }
+ if (operation > 6){
+ PrintAndLog("Too many operations specified\n");
+ return usage_hf_iclass_managekeys();
+ }
+ if (operation > 4 && fileNameLen == 0){
+ PrintAndLog("You must enter a filename when loading or saving\n");
+ return usage_hf_iclass_managekeys();
+ }
- return 0;
+ switch (operation){
+ case 3: memcpy(iClass_Key_Table[keyNbr], KEY, 8); return 1;
+ case 4: return printKeys();
+ case 5: return loadKeys(filename);
+ case 6: return saveKeys(filename);
+ break;
+ }
+ return 0;
}
static command_t CommandTable[] =
{
- {"help", CmdHelp, 1, "This help"},
- {"list", CmdHFiClassList, 0, "List iClass history"},
- {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"},
- {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"},
- {"reader", CmdHFiClassReader, 0, "Read an iClass tag"},
- {NULL, NULL, 0, NULL}
+ {"help", CmdHelp, 1, "This help"},
+ {"calcnewkey", CmdHFiClassCalcNewKey, 1, "[options..] Calc Diversified keys (blocks 3 & 4) to write new keys"},
+ {"clone", CmdHFiClassCloneTag, 0, "[options..] Authenticate and Clone from iClass bin file"},
+ {"decrypt", CmdHFiClassDecrypt, 1, "[f <fname>] Decrypt tagdump" },
+ {"dump", CmdHFiClassReader_Dump, 0, "[options..] Authenticate and Dump iClass tag's AA1"},
+ {"eload", CmdHFiClassELoad, 0, "[f <fname>] (experimental) Load data into iClass emulator memory"},
+ {"encryptblk", CmdHFiClassEncryptBlk, 1, "<BlockData> Encrypt given block data"},
+ {"list", CmdHFiClassList, 0, " (Deprecated) List iClass history"},
+ {"loclass", CmdHFiClass_loclass, 1, "[options..] Use loclass to perform bruteforce of reader attack dump"},
+ {"managekeys", CmdHFiClassManageKeys, 1, "[options..] Manage the keys to use with iClass"},
+ {"readblk", CmdHFiClass_ReadBlock, 0, "[options..] Authenticate and Read iClass block"},
+ {"reader",CmdHFiClassReader, 0, "Read an iClass tag"},
+ {"readtagfile", CmdHFiClassReadTagFile, 1, "[options..] Display Content from tagfile"},
+ {"replay", CmdHFiClassReader_Replay, 0, "<mac> Read an iClass tag via Reply Attack"},
+ {"sim", CmdHFiClassSim, 0, "[options..] Simulate iClass tag"},
+ {"snoop", CmdHFiClassSnoop, 0, " Eavesdrop iClass communication"},
+ {"writeblk", CmdHFiClass_WriteBlock, 0, "[options..] Authenticate and Write iClass block"},
+ {NULL, NULL, 0, NULL}
};
int CmdHFiClass(const char *Cmd)
{
- CmdsParse(CommandTable, Cmd);
- return 0;
+ CmdsParse(CommandTable, Cmd);
+ return 0;
}
int CmdHelp(const char *Cmd)
{
- CmdsHelp(CommandTable);
- return 0;
-}
-
-/**
- * @brief checks if a file exists
- * @param filename
- * @return
- */
-int fileExists(const char *filename) {
- struct stat st;
- int result = stat(filename, &st);
- return result == 0;
-}
-/**
- * @brief Utility function to save data to a file. This method takes a preferred name, but if that
- * file already exists, it tries with another name until it finds something suitable.
- * E.g. dumpdata-15.txt
- * @param preferredName
- * @param suffix the file suffix. Leave out the ".".
- * @param data The binary data to write to the file
- * @param datalen the length of the data
- * @return
- */
-int saveFile(const char *preferredName, const char *suffix, const void* data, size_t datalen)
-{
- FILE *f = fopen(preferredName, "wb");
- int size = sizeof(char) * (strlen(preferredName)+strlen(suffix)+5);
- char * fileName = malloc(size);
-
- memset(fileName,0,size);
- int num = 1;
- sprintf(fileName,"%s.%s", preferredName, suffix);
- while(fileExists(fileName))
- {
- sprintf(fileName,"%s-%d.%s", preferredName, num, suffix);
- num++;
- }
- /* We should have a valid filename now, e.g. dumpdata-3.bin */
-
- /*Opening file for writing in binary mode*/
- FILE *fileHandle=fopen(fileName,"wb");
- if(!f) {
- PrintAndLog("Failed to write to file '%s'", fileName);
- return 0;
- }
- fwrite(data, 1, datalen, fileHandle);
- fclose(fileHandle);
- PrintAndLog("Saved data to '%s'", fileName);
-
- free(fileName);
+ CmdsHelp(CommandTable);
return 0;
}