diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 65f9e8422..a5104d98e 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2040,7 +2040,7 @@ void T55xx_ChkPwds(uint8_t flags) { if (isok != sizeof(counter)) goto OUT; - pwdCount = counter[1] << 8 | counter[0]; + pwdCount = (uint16_t)(counter[1] << 8 | counter[0]); if (pwdCount == 0 || pwdCount == 0xFFFF) goto OUT; diff --git a/client/Makefile b/client/Makefile index a09a033d9..87cf03a19 100644 --- a/client/Makefile +++ b/client/Makefile @@ -205,6 +205,7 @@ CMDSRCS = crapto1/crapto1.c \ cmdhffido.c \ cmdhffelica.c \ cmdhfthinfilm.c \ + cmdhfcryptorf.c \ cmdhflto.c \ cmdhw.c \ cmdlf.c \ @@ -251,7 +252,8 @@ CMDSRCS = crapto1/crapto1.c \ flash.c \ wiegand_formats.c \ wiegand_formatutils.c \ - cardhelper.c + cardhelper.c \ + settings.c cpu_arch = $(shell uname -m) ifneq ($(findstring 86, $(cpu_arch)), ) diff --git a/client/cmdhf.c b/client/cmdhf.c index dfc2a3d47..773ffe431 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -34,6 +34,7 @@ #include "cmdhffido.h" // FIDO authenticators #include "cmdhfthinfilm.h" // Thinfilm #include "cmdhflto.h" // LTO-CM +#include "cmdhfcryptorf.h" // CryptoRF #include "cmdtrace.h" // trace list #include "ui.h" #include "cmddata.h" @@ -149,11 +150,22 @@ int CmdHFSearch(const char *Cmd) { res = PM3_SUCCESS; } } +/* + // 14b and iclass is the longest test (put last) + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for CryptoRF tag..."); + if (IfPm3Iso14443b()) { + if (readHFCryptoRF(false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("CryptoRF tag") "found\n"); + res = PM3_SUCCESS; + } + } +*/ // 14b and iclass is the longest test (put last) PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, "Searching for ISO14443-B tag..."); - if (IfPm3Iso14443a()) { + if (IfPm3Iso14443b()) { if (readHF14B(false) == 1) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") "found\n"); res = PM3_SUCCESS; @@ -281,6 +293,7 @@ static command_t CommandTable[] = { {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, +// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"}, {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / Felica RFIDs... }"}, {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, diff --git a/client/cmdhfcryptorf.c b/client/cmdhfcryptorf.c new file mode 100644 index 000000000..c6d43ff46 --- /dev/null +++ b/client/cmdhfcryptorf.c @@ -0,0 +1,609 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2020 iceman +// +// 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 CryptoRF commands (ISO14443B) +//----------------------------------------------------------------------------- + +#include "cmdhfcryptorf.h" + +#include +#include "fileutils.h" + +#include "cmdparser.h" // command_t +#include "comms.h" // clearCommandBuffer +#include "cmdtrace.h" +#include "crc16.h" +#include "cmdhf14a.h" +#include "protocols.h" // definitions of ISO14B protocol + +#define TIMEOUT 2000 +static int CmdHelp(const char *Cmd); + +static int usage_hf_cryptorf_info(void) { + PrintAndLogEx(NORMAL, "Usage: hf cryptorf info [h] [v]\n" + "Options:\n" + " h this help\n" + " v verbose\n" + "\n" + "Example:\n" + _YELLOW_(" hf cryptorf info") + ); + return PM3_SUCCESS; +} +static int usage_hf_cryptorf_reader(void) { + PrintAndLogEx(NORMAL, "Usage: hf cryptorf reader [h] [v]\n" + "Options:\n" + " h this help\n" + " v verbose\n" + "\n" + "Example:\n" + _YELLOW_(" hf cryptorf reader") + ); + return PM3_SUCCESS; +} +static int usage_hf_cryptorf_sniff(void) { + PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer\n" + "Buffer accessible from command " _YELLOW_("'hf list cryptorf'") "\n" + "Usage: hf cryptorf sniff [h]\n" + "Options:\n" + " h this help\n" + "\n" + "Example:\n" + _YELLOW_(" hf cryptorf sniff") + ); + return PM3_SUCCESS; +} +static int usage_hf_cryptorf_sim(void) { + PrintAndLogEx(NORMAL, "Emulating CryptoRF tag with 4 UID / PUPI\n" + "Usage: hf cryptorf sim [h] [u ]\n" + "Options:\n" + " h this help\n" + " u 4byte UID/PUPI\n" + "\n" + "Example:\n" + _YELLOW_(" hf cryptorf sim") + ); + return PM3_SUCCESS; +} +static int usage_hf_cryptorf_dump(void) { + PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-14443-B tag and save it to file\n" + "\n" + "Usage: hf cryptorf dump [h] [card memory] \n" + "Options:\n" + " h this help\n" + " f filename, if no UID will be used as filename\n" + "\n" + "Examples:\n" + "\thf cryptorf dump\n" + "\thf cryptorf dump f mydump"); + return PM3_SUCCESS; +} +static int usage_hf_cryptorf_eload(void) { + PrintAndLogEx(NORMAL, "It loads a binary dump into emulator memory\n" + "Usage: hf cryptorf eload [f ]\n" + "Options:\n" + " h this help\n" + " f filename, if no UID will be used as filename\n" + "\n" + "Examples:\n" + _YELLOW_(" hf cryptorf eload f filename") + ); + return PM3_SUCCESS; +} +static int usage_hf_cryptorf_esave(void) { + PrintAndLogEx(NORMAL, "It saves bin/eml/json dump file of emulator memory\n" + " Usage: hf cryptorf esave [f ]\n" + "Options:\n" + " h this help\n" + " f filename, if no UID will be used as filename\n" + "\n" + "Examples:\n" + _YELLOW_(" hf cryptorf esave ") + _YELLOW_(" hf cryptorf esave f filename") + ); + return PM3_SUCCESS; +} + +static int switch_off_field_cryptorf(void) { + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdHFCryptoRFList(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdTraceList("14b"); + return PM3_SUCCESS; +} + +static int CmdHFCryptoRFSim(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_hf_cryptorf_sim(); + + uint32_t pupi = 0; + if (cmdp == 'u') { + pupi = param_get32ex(Cmd, 1, 0, 16); + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_SIMULATE, pupi, 0, 0, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdHFCryptoRFSniff(const char *Cmd) { + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_hf_cryptorf_sniff(); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_SNIFF, NULL, 0); + return PM3_SUCCESS; +} + +static bool get_14b_UID(iso14b_card_select_t *card) { + + if (!card) + return false; + + int8_t retry = 3; + PacketResponseNG resp; + + // test for 14b SR + while (retry--) { + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + + uint8_t status = resp.oldarg[0]; + if (status == 0) { + memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + return true; + } + } + } // retry + + // test 14b standard + retry = 3; + while (retry--) { + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + + uint8_t status = resp.oldarg[0]; + if (status == 0) { + memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + return true; + } + } + } // retry + + if (retry <= 0) + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + + return false; +} + +static int CmdHFCryptoRFInfo(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_hf_cryptorf_info(); + + bool verbose = (cmdp == 'v'); + + int res = infoHFCryptoRF(verbose); + if (res != PM3_SUCCESS && verbose) { + PrintAndLogEx(FAILED, "no 14443-B tag found"); + } + return res; +} + +static int CmdHFCryptoRFReader(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_hf_cryptorf_reader(); + + bool verbose = (cmdp == 'v'); + + int res = readHFCryptoRF(verbose); + if (res != PM3_SUCCESS && verbose) { + PrintAndLogEx(FAILED, "no 14443-B tag found"); + } + return res; +} + +// need to write to file +static int CmdHFCryptoRFDump(const char *Cmd) { + + uint8_t fileNameLen = 0; + char filename[FILE_PATH_SIZE] = {0}; + char *fptr = filename; + bool errors = false; + uint8_t cmdp = 0, cardtype = 1; + uint16_t cardsize = 0; + uint8_t blocks = 0; + iso14b_card_select_t card; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_hf_cryptorf_dump(); + case 'f': + fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + cmdp += 2; + break; + default: + if (cmdp == 0) { + cardtype = param_get8ex(Cmd, cmdp, 1, 10); + cmdp++; + } else { + PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + } + + //Validations + if (errors) return usage_hf_cryptorf_dump(); + + switch (cardtype) { + case 2: + cardsize = (512 / 8) + 4; + blocks = 0x0F; + break; + case 1: + default: + cardsize = (4096 / 8) + 4; + blocks = 0x7F; + break; + } + + if (!get_14b_UID(&card)) { + PrintAndLogEx(WARNING, "No tag found."); + return PM3_SUCCESS; + } + + if (fileNameLen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + fptr += sprintf(fptr, "hf-cryptorf-"); + FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); + } + + // detect blocksize from card :) + PrintAndLogEx(NORMAL, "Reading memory from tag UID %s", sprint_hex(card.uid, card.uidlen)); + + uint8_t data[cardsize]; + memset(data, 0, sizeof(data)); + + int blocknum = 0; + uint8_t *recv = NULL; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); + + //select + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (resp.oldarg[0]) { + PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); + goto out; + } + } + + uint8_t req[2] = {ISO14443B_READ_BLK}; + + for (int retry = 0; retry < 5; retry++) { + + req[1] = blocknum; + + clearCommandBuffer(); + SendCommandOLD(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); + + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + uint8_t status = resp.oldarg[0] & 0xFF; + if (status > 0) { + continue; + } + + uint16_t len = (resp.oldarg[1] & 0xFFFF); + recv = resp.data.asBytes; + + if (!check_crc(CRC_14443_B, recv, len)) { + PrintAndLogEx(FAILED, "crc fail, retrying one more time"); + continue; + } + + memcpy(data + (blocknum * 4), resp.data.asBytes, 4); + + if (blocknum == 0xFF) { + //last read. + break; + } + + + retry = 0; + blocknum++; + if (blocknum > blocks) { + // read config block + blocknum = 0xFF; + } + + printf("."); + fflush(stdout); + } + } + + if (blocknum != 0xFF) { + PrintAndLogEx(NORMAL, "\n Dump failed"); + goto out; + } + + PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, "block# | data | ascii"); + PrintAndLogEx(NORMAL, "---------+--------------+----------"); + + for (int i = 0; i <= blocks; i++) { + PrintAndLogEx(NORMAL, + "%3d/0x%02X | %s | %s", + i, + i, + sprint_hex(data + (i * 4), 4), + sprint_ascii(data + (i * 4), 4) + ); + } + + PrintAndLogEx(NORMAL, "\n"); + + + size_t datalen = (blocks + 1) * 4; + saveFileEML(filename, data, datalen, 4); + saveFile(filename, ".bin", data, datalen); +out: + return switch_off_field_cryptorf(); +} + +static int CmdHFCryptoRFELoad(const char *Cmd) { + + size_t datalen = 1024; + char filename[FILE_PATH_SIZE] = {0x00}; + bool errors = false, has_filename = false; + uint8_t cmdp = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h' : + return usage_hf_cryptorf_eload(); + case 'f' : + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + has_filename = true; + cmdp += 2; + break; + default : + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (has_filename == false) + errors = true; + + //Validations + if (errors || strlen(Cmd) == 0) return usage_hf_cryptorf_eload(); + + // set up buffer + uint8_t *data = calloc(datalen, sizeof(uint8_t)); + if (!data) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + if (loadFile_safe(filename, ".bin", (void **)&data, &datalen) != PM3_SUCCESS) { + free(data); + PrintAndLogEx(WARNING, "Error, reading file"); + return PM3_EFILE; + } + + PrintAndLogEx(SUCCESS, "Uploading to emulator memory"); + +/* + // fast push mode + conn.block_after_ACK = true; + + //Send to device + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); + if (bytes_in_packet == bytes_remaining) { + // Disable fast mode on last packet + conn.block_after_ACK = false; + } + clearCommandBuffer(); + SendCommandOLD(CMD_HF_CRYPTORF_EML_MEMSET, bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + } +*/ + free(data); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Done"); + return PM3_SUCCESS; +} + +static int CmdHFCryptoRFESave(const char *Cmd) { + + char filename[FILE_PATH_SIZE] = {0}; + char *fptr = filename; + int fileNameLen = 0; + size_t numofbytes = 1024; + bool errors = false; + uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h' : + return usage_hf_cryptorf_esave(); + case 'f' : + fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); + if (!fileNameLen) + errors = true; + if (fileNameLen > FILE_PATH_SIZE - 5) + fileNameLen = FILE_PATH_SIZE - 5; + cmdp += 2; + break; + default : + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors || strlen(Cmd) == 0) return usage_hf_cryptorf_esave(); + + // set up buffer + uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); + if (!data) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + // download emulator memory + PrintAndLogEx(SUCCESS, "Reading emulator memory..."); + if (!GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(data); + return PM3_ETIMEOUT; + } + + // user supplied filename? + if (fileNameLen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + fptr += sprintf(fptr, "hf-cryptorf-"); + FillFileNameByUID(fptr, data, "-dump", 4); + } + + saveFile(filename, ".bin", data, numofbytes); + //needs to change + saveFileEML(filename, data, numofbytes, 8); + //needs to change + saveFileJSON(filename, jsfRaw, data, numofbytes); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"dump", CmdHFCryptoRFDump, IfPm3Iso14443b, "Read all memory pages of an CryptoRF tag, save to file"}, + {"info", CmdHFCryptoRFInfo, IfPm3Iso14443b, "Tag information"}, + {"list", CmdHFCryptoRFList, AlwaysAvailable, "List ISO 14443B history"}, + {"reader", CmdHFCryptoRFReader, IfPm3Iso14443b, "Act as a CryptoRF reader to identify a tag"}, + {"sim", CmdHFCryptoRFSim, IfPm3Iso14443b, "Fake CryptoRF tag"}, + {"sniff", CmdHFCryptoRFSniff, IfPm3Iso14443b, "Eavesdrop CryptoRF"}, + {"eload", CmdHFCryptoRFELoad, AlwaysAvailable, "Load binary dump to emulator memory"}, + {"esave", CmdHFCryptoRFESave, AlwaysAvailable, "Save emulator memory to binary file"}, + + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFCryptoRF(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} + +// Print extented information about tag. +int infoHFCryptoRF(bool verbose) { + + int res = PM3_ESOFT; + + // 14b get and print UID only (general info) + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + + if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + switch_off_field_cryptorf(); + return false; + } + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + + uint64_t status = resp.oldarg[0]; + + switch (status) { + case 0: + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); + res = PM3_SUCCESS; + break; + case 2: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); + break; + case 3: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); + break; + default: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed"); + break; + } + + return res; +} + +// get and print general info cryptoRF +int readHFCryptoRF(bool verbose) { + + int res = PM3_ESOFT; + + // 14b get and print UID only (general info) + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + + if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + return PM3_ETIMEOUT; + } + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + + uint64_t status = resp.oldarg[0]; + + switch (status) { + case 0: + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); + res = PM3_SUCCESS; + break; + case 2: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); + break; + case 3: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); + break; + default: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed"); + break; + } + return res; +} diff --git a/client/cmdhfcryptorf.h b/client/cmdhfcryptorf.h new file mode 100644 index 000000000..82508e415 --- /dev/null +++ b/client/cmdhfcryptorf.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2020 iceman +// +// 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 CryptoRF commands (ISO14443B) +//----------------------------------------------------------------------------- + +#ifndef CMDHFCRYPTORF_H__ +#define CMDHFCRYPTORF_H__ + +#include "common.h" + +int CmdHFCryptoRF(const char *Cmd); + +int infoHFCryptoRF(bool verbose); +int readHFCryptoRF(bool verbose); +#endif diff --git a/client/cmdhffelica.c b/client/cmdhffelica.c index 84339a05c..dc3b07a1b 100644 --- a/client/cmdhffelica.c +++ b/client/cmdhffelica.c @@ -1847,37 +1847,37 @@ int readFelicaUid(bool verbose) { } static command_t CommandTable[] = { - {"----------- General -----------", CmdHelp, IfPm3Iso14443a, ""}, - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, - {"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"}, - {"sniff", CmdHFFelicaSniff, IfPm3Felica, "Sniff ISO 18092/FeliCa traffic"}, - {"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"}, - {"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from authentication-not-required Service."}, - {"wrunencrypted", CmdHFFelicaWriteWithoutEncryption, IfPm3Felica, "write Block Data to an authentication-not-required Service."}, - {"----------- FeliCa Standard -----------", CmdHelp, IfPm3Iso14443a, ""}, - //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, - {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, - {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, - {"scsvcode", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire Area Code and Service Code."}, - {"rqsyscode", CmdHFFelicaRequestSystemCode, IfPm3Felica, "acquire System Code registered to the card."}, - {"auth1", CmdHFFelicaAuthentication1, IfPm3Felica, "authenticate a card. Start mutual authentication with Auth1"}, - {"auth2", CmdHFFelicaAuthentication2, IfPm3Felica, "allow a card to authenticate a Reader/Writer. Complete mutual authentication"}, - //{"read", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, - //{"write", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to an authentication-required Service."}, - //{"scsvcodev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "verify the existence of Area or Service, and to acquire Key Version."}, - //{"getsysstatus", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire the setup information in System."}, - {"rqspecver", CmdHFFelicaRequestSpecificationVersion, IfPm3Felica, "acquire the version of card OS."}, - {"resetmode", CmdHFFelicaResetMode, IfPm3Felica, "reset Mode to Mode 0."}, - //{"auth1v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "authenticate a card."}, - //{"auth2v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "allow a card to authenticate a Reader/Writer."}, - //{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, - //{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."}, - //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, - {"----------- FeliCa Light -----------", CmdHelp, IfPm3Iso14443a, ""}, - {"litesim", CmdHFFelicaSimLite, IfPm3Felica, " - only reply to poll request"}, - {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, - // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} + {"----------- General -----------", CmdHelp, AlwaysAvailable, ""}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, + {"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"}, + {"sniff", CmdHFFelicaSniff, IfPm3Felica, "Sniff ISO 18092/FeliCa traffic"}, + {"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"}, + {"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from authentication-not-required Service."}, + {"wrunencrypted", CmdHFFelicaWriteWithoutEncryption, IfPm3Felica, "write Block Data to an authentication-not-required Service."}, + {"----------- FeliCa Standard -----------", CmdHelp, AlwaysAvailable, ""}, + //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, + {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, + {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, + {"scsvcode", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire Area Code and Service Code."}, + {"rqsyscode", CmdHFFelicaRequestSystemCode, IfPm3Felica, "acquire System Code registered to the card."}, + {"auth1", CmdHFFelicaAuthentication1, IfPm3Felica, "authenticate a card. Start mutual authentication with Auth1"}, + {"auth2", CmdHFFelicaAuthentication2, IfPm3Felica, "allow a card to authenticate a Reader/Writer. Complete mutual authentication"}, + //{"read", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, + //{"write", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to an authentication-required Service."}, + //{"scsvcodev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "verify the existence of Area or Service, and to acquire Key Version."}, + //{"getsysstatus", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire the setup information in System."}, + {"rqspecver", CmdHFFelicaRequestSpecificationVersion, IfPm3Felica, "acquire the version of card OS."}, + {"resetmode", CmdHFFelicaResetMode, IfPm3Felica, "reset Mode to Mode 0."}, + //{"auth1v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "authenticate a card."}, + //{"auth2v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "allow a card to authenticate a Reader/Writer."}, + //{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, + //{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."}, + //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, + {"----------- FeliCa Light -----------", CmdHelp, AlwaysAvailable, ""}, + {"litesim", CmdHFFelicaSimLite, IfPm3Felica, " - only reply to poll request"}, + {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, + // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} {NULL, NULL, NULL, NULL} }; diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 72a7eaad2..c3042d874 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -2050,7 +2050,7 @@ static int CmdHFiClassReadTagFile(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); - PrintAndLogEx(INFO, "File size %d bytes, file blocks %d (0x%02x)", bytes_read, bytes_read >> 3, bytes_read >> 3); + PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, (uint16_t)(bytes_read >> 3), (uint16_t)(bytes_read >> 3)); PrintAndLogEx(INFO, "Printing blocks from"); PrintAndLogEx(INFO, "start " _YELLOW_("0x%02x") "end " _YELLOW_("0x%02x"), (startblock == 0) ? 6 : startblock, endblock); } diff --git a/client/cmdhflegic.c b/client/cmdhflegic.c index 69df2526c..c6de1379d 100644 --- a/client/cmdhflegic.c +++ b/client/cmdhflegic.c @@ -1013,43 +1013,47 @@ static int CmdLegicDump(const char *Cmd) { saveFile(filename, ".bin", data, readlen); saveFileEML(filename, data, readlen, 8); saveFileJSON(filename, jsfLegic, data, readlen); + free(data); return PM3_SUCCESS; } static int CmdLegicRestore(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0x00}; - size_t fileNlen = 0; - bool errors = false, shall_obsfuscate = false; + bool errors = false, shall_obsfuscate = false, have_filename = false; size_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': + case 'h': { errors = true; break; - case 'f': - 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; + } + case 'f': { + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + break; + } + have_filename = true; cmdp += 2; break; - case 'x': + } + case 'x': { shall_obsfuscate = true; cmdp++; break; - default: + } + default: { PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; + } } } + if (have_filename == false) + errors = true; + //Validations if (errors || cmdp == 0) return usage_legic_restore(); @@ -1076,7 +1080,7 @@ static int CmdLegicRestore(const char *Cmd) { } if (card.cardsize != numofbytes) { - PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%zu != %u]", card.cardsize, numofbytes); + PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%u != %zu]", card.cardsize, numofbytes); free(data); return PM3_EFILE; } @@ -1132,45 +1136,54 @@ static int CmdLegicRestore(const char *Cmd) { static int CmdLegicELoad(const char *Cmd) { size_t numofbytes = 256; - int fileNameLen = 0; char filename[FILE_PATH_SIZE] = {0x00}; - bool errors = false, shall_obsfuscate = false; + bool errors = false, shall_obsfuscate = false, have_filename = false; uint8_t cmdp = 0; while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h' : + case 'h' : { return usage_legic_eload(); - case 'f' : - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (!fileNameLen) - errors = true; - if (fileNameLen > FILE_PATH_SIZE - 5) - fileNameLen = FILE_PATH_SIZE - 5; + } + case 'f' : { + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + break; + } + have_filename = true; cmdp += 2; break; - case 'x': + } + case 'x': { shall_obsfuscate = true; cmdp++; break; - case '0' : + } + case '0' : { numofbytes = 22; cmdp++; break; - case '1' : + } + case '1' : { numofbytes = 256; cmdp++; break; - case '2' : + } + case '2' : { numofbytes = 1024; cmdp++; break; - default : + } + default : { PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; break; + } } } + if (have_filename == false) + errors = true; + //Validations if (errors || strlen(Cmd) == 0) return usage_legic_eload(); @@ -1358,8 +1371,8 @@ static command_t CommandTable[] = { {"sim", CmdLegicSim, IfPm3Legicrf, "Start tag simulator"}, {"wrbl", CmdLegicWrbl, IfPm3Legicrf, "Write data to a LEGIC Prime tag"}, {"crc", CmdLegicCalcCrc, AlwaysAvailable, "Calculate Legic CRC over given bytes"}, - {"eload", CmdLegicELoad, IfPm3Legicrf, "Load binary dump to emulator memory"}, - {"esave", CmdLegicESave, IfPm3Legicrf, "Save emulator memory to binary file"}, + {"eload", CmdLegicELoad, AlwaysAvailable, "Load binary dump to emulator memory"}, + {"esave", CmdLegicESave, AlwaysAvailable, "Save emulator memory to binary file"}, {"wipe", CmdLegicWipe, IfPm3Legicrf, "Wipe a LEGIC Prime tag"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 4c49187ca..e43461591 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -3712,7 +3712,7 @@ int CmdHF14AMfELoad(const char *Cmd) { } } - PrintAndLogEx(INFO, "Copying to emulator memory"); + PrintAndLogEx(INFO, "Uploading to emulator memory"); // fast push mode conn.block_after_ACK = true; diff --git a/client/cmdhfmfu.c b/client/cmdhfmfu.c index 7c204f861..fd3ad5aeb 100644 --- a/client/cmdhfmfu.c +++ b/client/cmdhfmfu.c @@ -2808,6 +2808,7 @@ static int CmdHF14MfuNDEF(const char *Cmd) { if (status == -1) { DropField(); PrintAndLogEx(ERR, "Error: tag didn't answer to READ"); + free(records); return PM3_ESOFT; } } diff --git a/client/emv/emvjson.c b/client/emv/emvjson.c index 75b2e87bf..968b51d51 100644 --- a/client/emv/emvjson.c +++ b/client/emv/emvjson.c @@ -94,6 +94,10 @@ int JsonSaveStr(json_t *root, const char *path, const char *value) { return JsonSaveJsonObject(root, path, json_string(value)); }; +int JsonSaveBoolean(json_t *root, const char *path, bool value) { + return JsonSaveJsonObject(root, path, json_boolean(value)); +} + int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen) { char *msg = sprint_hex_inrow(data, datalen); if (msg && strlen(msg) && msg[strlen(msg) - 1] == ' ') diff --git a/client/emv/emvjson.h b/client/emv/emvjson.h index 54d97ada8..9b1efb034 100644 --- a/client/emv/emvjson.h +++ b/client/emv/emvjson.h @@ -24,6 +24,7 @@ const char *GetApplicationDataName(tlv_tag_t tag); int JsonSaveJsonObject(json_t *root, const char *path, json_t *value); int JsonSaveStr(json_t *root, const char *path, const char *value); +int JsonSaveBoolean(json_t *root, const char *path, bool value); int JsonSaveInt(json_t *root, const char *path, int value); int JsonSaveBufAsHexCompact(json_t *elm, const char *path, uint8_t *data, size_t datalen); int JsonSaveBufAsHex(json_t *elm, const char *path, uint8_t *data, size_t datalen); diff --git a/client/fileutils.c b/client/fileutils.c index ecf47383f..05a7e8949 100644 --- a/client/fileutils.c +++ b/client/fileutils.c @@ -38,6 +38,7 @@ // this define is needed for scandir/alphasort to work #define _GNU_SOURCE #include "fileutils.h" +#include "settings.h" #include #include @@ -425,6 +426,9 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s } } break; + case jsfSettings: + settings_save_callback (root); + break; default: break; } @@ -863,7 +867,9 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_ } *datalen = sptr; } - + if (!strcmp(ctype,"settings")) { + settings_load_callback (root); + } PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), fileName); out: json_decref(root); diff --git a/client/fileutils.h b/client/fileutils.h index 3747e0850..06d3ff20a 100644 --- a/client/fileutils.h +++ b/client/fileutils.h @@ -62,6 +62,7 @@ typedef enum { jsfT55x7, jsfT5555, jsfMfPlusKeys, + jsfSettings, } JSONFileType; typedef enum { diff --git a/client/luascripts/Legic_clone.lua b/client/luascripts/Legic_clone.lua deleted file mode 100644 index 00834c98b..000000000 --- a/client/luascripts/Legic_clone.lua +++ /dev/null @@ -1,543 +0,0 @@ -local utils = require('utils') -local cmds = require('commands') -local getopt = require('getopt') -local ansicolors = require('ansicolors') ---[[ - script to create a clone-dump with new crc - Author: mosci - my Fork: https://github.com/icsom/proxmark3.git - Upstream: https://github.com/Proxmark/proxmark3.git - - 1. read tag-dump, xor byte 22..end with byte 0x05 of the inputfile - 2. write to outfile - 3. set byte 0x05 to newcrc - 4. until byte 0x21 plain like in inputfile - 5. from 0x22..end xored with newcrc - 6. calculate new crc on each segment (needs to know the new MCD & MSN0..2) - - simplest usage: - read a valid legic tag with 'hf legic reader' - save the dump with 'hf legic dump o orig' - place your 'empty' tag on the reader and run 'script run Legic_clone -i orig.bin -w' - you will see some output like: - read 1024 bytes from orig.bin - - place your empty tag onto the PM3 to read and display the MCD & MSN0..2 - the values will be shown below - confirm when ready [y/n] ?y - #db# setting up legic card - #db# MIM 256 card found, reading card ... - #db# Card read, use 'hf legic decode' or - #db# 'data hexsamples 8' to view results - 0b ad c0 de <- !! here you'll see the MCD & MSN of your empty tag, which has to be typed in manually as seen below !! - type in MCD as 2-digit value - e.g.: 00 (default: 79 ) - > 0b - type in MSN0 as 2-digit value - e.g.: 01 (default: 28 ) - > ad - type in MSN1 as 2-digit value - e.g.: 02 (default: d1 ) - > c0 - type in MSN2 as 2-digit value - e.g.: 03 (default: 43 ) - > de - MCD:0b, MSN:ad c0 de, MCC:79 <- this crc is calculated from the MCD & MSN and must match the one on yout empty tag - - wrote 1024 bytes to myLegicClone.hex - enter number of bytes to write? (default: 86 ) - - loaded 1024 samples - #db# setting up legic card - #db# MIM 256 card found, writing 0x00 - 0x01 ... - #db# write successful - ... - #db# setting up legic card - #db# MIM 256 card found, writing 0x56 - 0x01 ... - #db# write successful - proxmark3> - - the default value (number of bytes to write) is calculated over all valid segments and should be ok - just hit enter, wait until write has finished - and your clone should be ready (except there has to be a additional KGH-CRC to be calculated - which credentials are unknown until yet) - - the '-w' switch will only work with my fork - it needs the binary legic_crc8 which is not part of the proxmark3-master-branch - also the ability to write DCF is not possible with the proxmark3-master-branch - but creating dumpfile-clone files will be possible (without valid segment-crc - this has to done manually with) - - - (example) Legic-Prime Layout with 'Kaba Group Header' - +----+----+----+----+----+----+----+----+ - 0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f | - +----+----+----+----+----+----+----+----+ - 0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2| - +----+----+----+----+----+----+----+----+ - 0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1| - +----+----+----+----+----+----+----+----+ - 0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0| - +----+----+----+----+----+----+----+----+ - 0x20|UID1|UID2|kghC| - +----+----+----+ - - MCD= ManufacturerID (1 Byte) - MSN0..2= ManufactureSerialNumber (3 Byte) - MCC= CRC (1 Byte) calculated over MCD,MSN0..2 - DCF= DecrementalField (2 Byte) 'credential' (enduser-Tag) seems to have always DCF-low=0x60 DCF-high=0xea - Bck0..5= Backup (6 Byte) Bck0 'dirty-flag', Bck1..5 SegmentHeader-Backup - BCC= BackupCRC (1 Byte) CRC calculated over Bck1..5 - Seg0..3= SegmentHeader (on MIM 4 Byte ) - SegC= SegmentCRC (1 Byte) calculated over MCD,MSN0..2,Seg0..3 - Stp0..n= Stamp0... (variable length) length = Segment-Len - UserData - 1 - UID0..n= UserDater (variable length - with KGH hex 0x00-0x63 / dec 0-99) length = Segment-Len - WRP - WRC - 1 - kghC= KabaGroupHeader (1 Byte + addr 0x0c must be 0x11) - as seen on this example: addr 0x05..0x08 & 0x0c must have been set to this values - otherwise kghCRC will not be created by a official reader (not accepted) ---]] - -copyright = '' -author = 'Mosci' -version = 'v1.0.2' -desc = [[ -This is a script which creates a clone-dump of a dump from a Legic Prime Tag (MIM256 or MIM1024) -(created with 'hf legic dump f my_dump') -]] -example = [[ - script run legic_clone -i my_dump.bin -o my_clone.bin -c f8 - script run legic_clone -i my_dump.bin -d -s -]] -usage = [[ -script run legic_clone -h -i -o -c -d -s -w -]] -arguments = [[ -required : - -i (file to read data from, must be in binary format (*.bin)) - -optional : - -h - Help text - -o - requires option -c to be given - -c - requires option -o to be given - -d - Display content of found Segments - -s - Display summary at the end - -w - write directly to Tag - a file myLegicClone.bin will be generated also - - e.g.: - hint: using the CRC '00' will result in a plain dump ( -c 00 ) -]] - -local bxor = bit32.bxor - --- we need always 2 digits -local function prepend_zero(s) - if (string.len(s) == 1) then - return '0' .. s - else - if (string.len(s) == 0) then - return '00' - else - return s - end - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, err -end - --- read LEGIC data -local function readlegicdata( offset, length, iv ) - -- Read data - local command = Command:newMIX{ - cmd = cmds.CMD_HF_LEGIC_READER - , arg1 = offset - , arg2 = length - , arg3 = iv - , data = nil - } - local result, err = command:sendMIX() - if not result then return oops(err) end - -- result is a packed data structure, data starts at offset 33 - return result -end - ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end - --- Check availability of file -local function file_check(file_name) - local file_found = io.open(file_name, "r") - if not file_found then - file_found = false - else - file_found = true - end - return file_found -end - ---- xor-wrapper --- xor all from addr 0x22 (start counting from 1 => 23) -local function xorme(hex, xor, index) - if ( index >= 23 ) then - return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) )) - else - return hex - end -end - --- read input-file into array -local function getInputBytes(infile) - local line - local bytes = {} - - local fhi,err = io.open(infile,"rb") - if err then print("OOps ... faild to read from file ".. infile); return false; end - - str = fhi:read("*all") - for c in (str or ''):gmatch'.' do - bytes[#bytes+1] = ('%02x'):format(c:byte()) - end - - fhi:close() - - print("\nread ".. #bytes .." bytes from ".. infile) - return bytes -end - --- write to file -local function writeOutputBytes(bytes, outfile) - local fho,err = io.open(outfile,"wb") - if err then print("OOps ... faild to open output-file ".. outfile); return false; end - - for i = 1, #bytes do - fho:write(string.char(tonumber(bytes[i],16))) - end - fho:close() - print("\nwrote ".. #bytes .." bytes to " .. outfile) - return true -end - --- xore certain bytes -local function xorBytes(inBytes, crc) - local bytes = {} - for index = 1, #inBytes do - bytes[index] = xorme(inBytes[index], crc, index) - end - if (#inBytes == #bytes) then - -- replace crc - bytes[5] = string.sub(crc,-2) - return bytes - else - print("error: byte-count missmatch") - return false - end -end - --- get raw segment-data -function getSegmentData(bytes, start, index) - local raw, len, valid, last, wrp, wrc, rd, crc - local segment = {} - segment[0] = bytes[start]..' '..bytes[start+1]..' '..bytes[start+2]..' '..bytes[start+3] - -- flag = high nibble of byte 1 - segment[1] = string.sub(bytes[start+1],0,1) - - -- valid = bit 6 of byte 1 - segment[2] = tonumber(bit32.extract('0x'..bytes[start+1],6,1),16) - - -- last = bit 7 of byte 1 - segment[3] = tonumber(bit32.extract('0x'..bytes[start+1],7,1),16) - - -- len = (byte 0)+(bit0-3 of byte 1) - segment[4] = tonumber(('%03x'):format(tonumber(bit32.extract('0x'..bytes[start+1],0,3),16)..tonumber(bytes[start],16)),16) - - -- wrp (write proteted) = byte 2 - segment[5] = tonumber(bytes[start+2]) - - -- wrc (write control) - bit 4-6 of byte 3 - segment[6] = tonumber(bit32.extract('0x'..bytes[start+3],4,3),16) - - -- rd (read disabled) - bit 7 of byte 3 - segment[7] = tonumber(bit32.extract('0x'..bytes[start+3],7,1),16) - - -- crc byte 4 - segment[8] = bytes[start+4] - - -- segment index - segment[9] = index - - -- # crc-byte - segment[10] = start+4 - return segment -end - ---- Kaba Group Header --- checks if a segment does have a kghCRC --- returns boolean false if no kgh has being detected or the kghCRC if a kgh was detected -function CheckKgh(bytes, segStart, segEnd) - if (bytes[8]=='9f' and bytes[9]=='ff' and bytes[13]=='11') then - local i - local data = {} - segStart = tonumber(segStart, 10) - segEnd = tonumber(segEnd, 10) - local dataLen = segEnd-segStart-5 - --- gather creadentials for verify - local WRP = bytes[(segStart+2)] - local WRC = ("%02x"):format(tonumber(bit32.extract("0x"..bytes[segStart+3],4,3),16)) - local RD = ("%02x"):format(tonumber(bit32.extract("0x"..bytes[segStart+3],7,1),16)) - local XX = "00" - cmd = bytes[1]..bytes[2]..bytes[3]..bytes[4]..WRP..WRC..RD..XX - for i = (segStart+5), (segStart+5+dataLen-2) do - cmd = cmd..bytes[i] - end - local KGH = ("%02x"):format(utils.Crc8Legic(cmd)) - if (KGH == bytes[segEnd-1]) then - return KGH - else - return false - end - else - return false - end -end - --- get only the addresses of segemnt-crc's and the length of bytes -function getSegmentCrcBytes(bytes) - local start = 23 - local index = 0 - local crcbytes = {} - repeat - seg = getSegmentData(bytes,start,index) - crcbytes[index] = seg[10] - start = start + seg[4] - index = index + 1 - until (seg[3] == 1 or tonumber(seg[9]) == 126 ) - crcbytes[index] = start - return crcbytes -end - --- print segment-data (hf legic info like) -function displaySegments(bytes) - --display segment header(s) - start = 23 - index = '00' - - --repeat until last-flag ist set to 1 or segment-index has reached 126 - repeat - wrc = '' - wrp = '' - pld = '' - Seg = getSegmentData(bytes, start, index) - KGH = CheckKgh(bytes, start, (start+tonumber(Seg[4],10))) - printSegment(Seg) - - -- wrc - if (Seg[6] > 0) then - print("WRC protected area:") - -- length of wrc = wrc - for i=1, Seg[6] do - -- starts at (segment-start + segment-header + segment-crc)-1 - wrc = wrc..bytes[(start+4+1+i)-1]..' ' - end - print(wrc) - elseif (Seg[5] > 0) then - print("Remaining write protected area:") - -- length of wrp = (wrp-wrc) - for i=1, (Seg[5]-Seg[6]) do - -- starts at (segment-start + segment-header + segment-crc + wrc)-1 - wrp = wrp..bytes[(start+4+1+Seg[6]+i)-1]..' ' - end - print(wrp) - end - - -- payload - print("Remaining segment payload:") - --length of payload = segment-len - segment-header - segment-crc - wrp -wrc - for i=1, (Seg[4]-4-1-Seg[5]-Seg[6]) do - -- starts at (segment-start + segment-header + segment-crc + segment-wrp + segemnt-wrc)-1 - pld = pld..bytes[(start+4+1+Seg[5]+Seg[6]+i)-1]..' ' - end - print(pld) - if (KGH) then - print("'Kaba Group Header' detected") - end - start = start+Seg[4] - index = prepend_zero(tonumber(Seg[9])+1) - - until (Seg[3] == 1 or tonumber(Seg[9]) == 126 ) -end - --- print Segment values -function printSegment(SegmentData) - res = "\nSegment "..SegmentData[9]..": " - res = res.. "raw header="..SegmentData[0]..", " - res = res.. "flag="..SegmentData[1].." (valid="..SegmentData[2].." last="..SegmentData[3].."), " - res = res.. "len="..("%04d"):format(SegmentData[4])..", " - res = res.. "WRP="..prepend_zero(SegmentData[5])..", " - res = res.. "WRC="..prepend_zero(SegmentData[6])..", " - res = res.. "RD="..SegmentData[7]..", " - res = res.. "crc="..SegmentData[8] - print(res) -end - --- write clone-data to tag -function writeToTag(plainBytes) - local SegCrcs = {} - local output - local readbytes - if(utils.confirm("\nplace your empty tag onto the PM3 to restore the data of the input file\nthe CRCs will be calculated as needed\n confirm when ready") == false) then - return - end - - readbytes = readlegicdata(0, 4, 0x55) - -- gather MCD & MSN from new Tag - this must be enterd manually - print("\nthese are the MCD MSN0 MSN1 MSN2 from the Tag that has being read:") - - plainBytes[1] = ('%02x'):format(readbytes:byte(33)) - plainBytes[2] = ('%02x'):format(readbytes:byte(34)) - plainBytes[3] = ('%02x'):format(readbytes:byte(35)) - plainBytes[4] = ('%02x'):format(readbytes:byte(36)) - - MCD = plainBytes[1] - MSN0 = plainBytes[2] - MSN1 = plainBytes[3] - MSN2 = plainBytes[4] - -- calculate crc8 over MCD & MSN - cmd = MCD..MSN0..MSN1..MSN2 - MCC = ("%02x"):format(utils.Crc8Legic(cmd)) - print("MCD:"..MCD..", MSN:"..MSN0.." "..MSN1.." "..MSN2..", MCC:"..MCC) - - -- calculate new Segment-CRC for each valid segment - SegCrcs = getSegmentCrcBytes(plainBytes) - for i=0, (#SegCrcs-1) do - -- SegCrcs[i]-4 = address of first byte of segmentHeader (low byte segment-length) - segLen = tonumber(("%1x"):format(tonumber(bit32.extract("0x"..plainBytes[(SegCrcs[i]-3)],0,3),16))..("%02x"):format(tonumber(plainBytes[SegCrcs[i]-4],16)),16) - segStart = (SegCrcs[i]-4) - segEnd = (SegCrcs[i]-4+segLen) - KGH = CheckKgh(plainBytes,segStart,segEnd) - if (KGH) then - print("'Kaba Group Header' detected - re-calculate...") - end - cmd = MCD..MSN0..MSN1..MSN2..plainBytes[SegCrcs[i]-4]..plainBytes[SegCrcs[i]-3]..plainBytes[SegCrcs[i]-2]..plainBytes[SegCrcs[i]-1] - plainBytes[SegCrcs[i]] = ("%02x"):format(utils.Crc8Legic(cmd)) - end - - -- apply MCD & MSN to plain data - plainBytes[1] = MCD - plainBytes[2] = MSN0 - plainBytes[3] = MSN1 - plainBytes[4] = MSN2 - plainBytes[5] = MCC - - -- prepare plainBytes for writing (xor plain data with new MCC) - bytes = xorBytes(plainBytes, MCC) - - -- write data to file - if (writeOutputBytes(bytes, "myLegicClone.bin")) then - -- write pm3-buffer to Tag - cmd = ('hf legic restore f myLegicClone') - core.console(cmd) - end -end - --- main function -function main(args) - -- some variables - local i = 0 - local oldcrc, newcrc, infile, outfile - local bytes = {} - local segments = {} - - -- parse arguments for the script - for o, a in getopt.getopt(args, 'hwsdc:i:o:') do - -- output file - if o == 'o' then - outfile = a - ofs = true - if (file_check(a)) then - local answer = utils.confirm('\nthe output-file '..a..' already exists!\nthis will delete the previous content!\ncontinue?') - if (answer==false) then return oops('quiting') end - end - end - -- input file - if o == 'i' then - infile = a - if (file_check(infile)==false) then - return oops('input file: '..infile..' not found') - else - bytes = getInputBytes(infile) - oldcrc = bytes[5] - ifs = true - if (bytes == false) then return oops('couldnt get input bytes') end - end - i = i+1 - end - -- new crc - if o == 'c' then - newcrc = a:lower() - ncs = true - end - -- display segments switch - if o == 'd' then ds = true; end - -- display summary switch - if o == 's' then ss = true; end - -- write to tag switch - if o == 'w' then ws = true; end - -- help - if o == 'h' then return help() end - end - - if (not ifs) then return oops('option -i is required but missing') end - - -- bytes to plain - bytes = xorBytes(bytes, oldcrc) - - -- show segments (works only on plain bytes) - if (ds) then - print("+------------------------------------------- Segments -------------------------------------------+") - displaySegments(bytes); - end - - if (ofs and ncs) then - -- xor bytes with new crc - newBytes = xorBytes(bytes, newcrc) - -- write output - if (writeOutputBytes(newBytes, outfile)) then - -- show summary if requested - if (ss) then - -- information - res = "\n+-------------------------------------------- Summary -------------------------------------------+" - res = res .."\ncreated clone_dump from\n\t"..infile.." crc: "..oldcrc.."\ndump_file:" - res = res .."\n\t"..outfile.." crc: "..string.sub(newcrc,-2) - res = res .."\nyou may load the new file with: hf legic eload "..outfile - res = res .."\n\nif you don't write to tag immediately ('-w' switch) you will need to recalculate each segmentCRC" - res = res .."\nafter writing this dump to a tag!" - res = res .."\n\na segmentCRC gets calculated over MCD,MSN0..3,Segment-Header0..3" - res = res .."\ne.g. (based on Segment00 of the data from "..infile.."):" - res = res .."\nhf legic crc d "..bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[23]..bytes[24]..bytes[25]..bytes[26].." u "..newcrc.." c 8" - -- this can not be calculated without knowing the new MCD, MSN0..2 - print(res) - end - end - else - if (ss) then - -- show why the output-file was not written - print("\nnew file not written - some arguments are missing ..") - print("output file: ".. (ofs and outfile or "not given")) - print("new crc: ".. (ncs and newcrc or "not given")) - end - end - -- write to tag - if (ws and ( #bytes == 1024 or #bytes == 256)) then - writeToTag(bytes) - end -end - --- call main with arguments -main(args) diff --git a/client/proxmark3.c b/client/proxmark3.c index 5a6ae23a9..0438f81ad 100644 --- a/client/proxmark3.c +++ b/client/proxmark3.c @@ -27,6 +27,7 @@ #include "comms.h" #include "fileutils.h" #include "flash.h" +#include "settings.h" static void showBanner(void) { g_printAndLog = PRINTANDLOG_PRINT; @@ -581,6 +582,12 @@ int main(int argc, char *argv[]) { set_my_executable_path(); set_my_user_directory(); + // Settings Load and Test + // settings_load (); + // settings_save (); + // printf ("Ver : %s\n",mySettings.version); + // End Settings + for (int i = 1; i < argc; i++) { if (argv[i][0] != '-') { diff --git a/client/settings.c b/client/settings.c new file mode 100644 index 000000000..79fa00fd1 --- /dev/null +++ b/client/settings.c @@ -0,0 +1,194 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + +//----------------------------------------------------------------------------- +// Settings Functions +//----------------------------------------------------------------------------- + +#include "settings.h" +#include "comms.h" +#include "emv/emvjson.h" + +// Load all settings into memory (struct) +int settings_load (void) { + // loadFileJson wants these, so pass in place holder values, though not used + // in settings load; + uint8_t dummyData = 0x00; + size_t dummyDL = 0x00; + + // clear all settings + memset (&mySettings,0x00,sizeof(mySettings)); + + if (loadFileJSON(settingsFilename, &dummyData, sizeof(dummyData), &dummyDL) == PM3_SUCCESS) { + printf ("==> Settings Loaded\n"); + mySettings.loaded = true; + } + + + // Test results + /* + bool os_windows_usecolor; + bool os_windows_useansicolor; + int window_xpos; + int window_ypos; + int window_hsize; + int window_wsize; + bool use_emojis + bool use_hints + */ + printf (" Settings Version : [%s]\n", mySettings.version); + printf (" os_windows_usecolor (bool) : [%d]\n", mySettings.os_windows_usecolor); + printf (" os_windows_useAnsicolor (bool) : [%d]\n", mySettings.os_windows_useansicolor); + printf (" window_xpos (int) : [%d]\n", mySettings.window_xpos); + printf (" window_ypos (int) : [%d]\n", mySettings.window_ypos); + printf (" window_hsize (int) : [%d]\n", mySettings.window_hsize); + printf (" window_wsize (int) : [%d]\n", mySettings.window_wsize); + printf (" use emoji (bool) : [%d]\n", mySettings.use_emojis); + printf (" use hints (bool) : [%d]\n", mySettings.use_hints); + return PM3_SUCCESS; +} + +// Save all settings from memory (struct) to file +int settings_save(void) { + // Note sure if backup has value ? + char backupFilename[500]; + + snprintf(backupFilename, sizeof(backupFilename),"%s.bak",settingsFilename); + + if (fileExists (backupFilename)) { + if (remove (backupFilename) != 0) { + PrintAndLogEx (FAILED, "Error - could not delete old settings backup file \"%s\"",backupFilename); + return PM3_ESOFT; + } + } + + if (fileExists (settingsFilename)) { + if (rename (settingsFilename,backupFilename) != 0) { + PrintAndLogEx (FAILED, "Error - could not backup settings file \"%s\" to \"%s\"",settingsFilename,backupFilename); + return PM3_ESOFT; + } + } + + uint8_t dummyData = 0x00; + size_t dummyDL = 0x00; + + if (saveFileJSON(settingsFilename, jsfSettings, &dummyData, dummyDL) == PM3_SUCCESS) + PrintAndLogEx (NORMAL, "settings have been saved to \"%s\"",settingsFilename); + + return PM3_SUCCESS; +} + +void settings_save_callback(json_t *root) { + + printf ("==> Save Settings\n"); + //JsonSaveStr(root, "FileType", "settings"); + //JsonSaveStr (root,"Test1.Test2","test settings"); + /* + "version": "1.0 Nov 2019", + "os.windows.usecolor": true, + "os.windows.useAnsiColor": true, + "window.xpos": 10, + "window.ypos": 10, + "window.hsize": 300, + "window.wsize": 600 + */ + JsonSaveStr (root,"FileType","settings"); + JsonSaveStr (root,"version","1.0 Nov 2019");//mySettings.version); + JsonSaveBoolean (root,"os.windows.useColor", mySettings.os_windows_usecolor); + JsonSaveBoolean (root,"os.windows.useAnsiColor", mySettings.os_windows_useansicolor); + JsonSaveInt (root,"window.xpos", mySettings.window_xpos); + JsonSaveInt (root,"window.ypos", mySettings.window_ypos); + JsonSaveInt (root,"window.hsize", mySettings.window_hsize); + JsonSaveInt (root,"window.wsize", mySettings.window_wsize); + JsonSaveBoolean (root,"client.useEmojis", mySettings.use_emojis); + JsonSaveBoolean (root,"client.useHints", mySettings.use_hints); +} + +void settings_load_callback(json_t *root) { + + json_error_t up_error = {0}; + int b1; + int i1; + const char *s1; + + if (json_unpack_ex(root, &up_error , 0, "{s:s}","version", &s1) == 0) + strncpy (mySettings.version,s1,sizeof (mySettings.version) - 1); + else + strncpy (mySettings.version,"unknown",sizeof (mySettings.version) - 1); + + // os.windows... + if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.windows.useColor",&b1) == 0) + mySettings.os_windows_usecolor = b1; + else // default + mySettings.os_windows_useansicolor = false; + + if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.windows.useAnsiColor",&b1) == 0) + mySettings.os_windows_useansicolor = b1; + else // default + mySettings.os_windows_useansicolor = false; + + // window... + if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.xpos",&i1) == 0) + mySettings.window_xpos = i1; + else // default + mySettings.window_xpos = 0; + if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.ypos",&i1) == 0) + mySettings.window_ypos = i1; + else // default + mySettings.window_ypos = 0; + if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.hsize",&i1) == 0) + mySettings.window_hsize = i1; + else // default + mySettings.window_hsize = 0; + if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.wsize",&i1) == 0) + mySettings.window_wsize = i1; + else // default + mySettings.window_wsize = 0; + + // Use EMOJIS + if (json_unpack_ex(root,&up_error, 0, "{s:b}","client.useEmojis",&b1) == 0) + mySettings.use_emojis = b1; + else // default + mySettings.use_emojis = false; + + // Use Hints + if (json_unpack_ex(root,&up_error, 0, "{s:b}","client.useHints",&b1) == 0) + mySettings.use_hints = b1; + else // default + mySettings.use_hints = false; + +} diff --git a/client/settings.h b/client/settings.h new file mode 100644 index 000000000..4bf8b2a5e --- /dev/null +++ b/client/settings.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2009 Michael Gernoth +// Copyright (C) 2010 iZsh +// +// 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. +//----------------------------------------------------------------------------- +// Settings Functions +//----------------------------------------------------------------------------- +#ifndef settings_h +#define settings_h + +#include "fileutils.h" + +#define settingsFilename "settings.json" + +typedef struct { + bool loaded; + char version[20]; + bool os_windows_usecolor; + bool os_windows_useansicolor; + int window_xpos; + int window_ypos; + int window_hsize; + int window_wsize; + bool use_emojis; + bool use_hints; +} settings_t; + +// Settings struct so as to be available to other modules by including settings.h +settings_t mySettings; + +int settings_load (void); +int settings_save (void); + +void settings_save_callback (json_t *root); +void settings_load_callback (json_t *root); + +#endif