diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8338eb724..68af6b38b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,6 +37,19 @@ jobs: # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed libssl-dev + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools + python3 -m pip install ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + - name: Checkout repository uses: actions/checkout@v2 @@ -50,21 +63,14 @@ jobs: # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + - name: make clean + run: make clean - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release + - name: Build + env: + V: 1 + PLATFORM_EXTRAS: BTADDON + run: make - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f76c3c16..a339b528e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Changed `hf legic *` - now uses NG instead (@iceman1001) + - Added `hf legic view` - view contents of LEGIC Prime dump files (@iceman1001) - Changed `hf mfu restore` - now takes bin/json as dump files (@iceman1001) - Added `hf mfu view` - view contents of MFU dump files (@iceman1001) - Changed `hf_mf_uidbruteforce` - added support for S70, enhance UID length management (@cactuschibre) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 22871a00d..afe10694c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1285,11 +1285,13 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_LEGIC_WRITER: { - LegicRfWriter(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); + legic_packet_t *payload = (legic_packet_t *) packet->data.asBytes; + LegicRfWriter(payload->offset, payload->len, payload->iv, payload->data); break; } case CMD_HF_LEGIC_READER: { - LegicRfReader(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); + legic_packet_t *payload = (legic_packet_t *) packet->data.asBytes; + LegicRfReader(payload->offset, payload->len, payload->iv); break; } case CMD_HF_LEGIC_INFO: { @@ -1302,10 +1304,9 @@ static void PacketReceived(PacketCommandNG *packet) { // involved in dealing with emulator memory. But if it is called later, it might // destroy the Emulator Memory. //----------------------------------------------------------------------------- - // arg0 = offset - // arg1 = num of bytes FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - emlSet(packet->data.asBytes, packet->oldarg[0], packet->oldarg[1]); + legic_packet_t *payload = (legic_packet_t *) packet->data.asBytes; + emlSet(payload->data, payload->offset, payload->len); break; } #endif diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 840fd74f9..637d22fee 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -422,7 +422,7 @@ void LegicRfInfo(void) { // establish shared secret and detect card type uint8_t card_type = setup_phase(0x01); if (init_card(card_type, &card) != PM3_SUCCESS) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_INFO, PM3_EINIT, NULL, 0); goto OUT; } @@ -430,7 +430,7 @@ void LegicRfInfo(void) { for (uint8_t i = 0; i < sizeof(card.uid); ++i) { int16_t byte = read_byte(i, card.cmdsize); if (byte == -1) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_INFO, PM3_EFAILED, NULL, 0); goto OUT; } card.uid[i] = byte & 0xFF; @@ -440,12 +440,12 @@ void LegicRfInfo(void) { int16_t mcc = read_byte(4, card.cmdsize); int16_t calc_mcc = CRC8Legic(card.uid, 4); if (mcc != calc_mcc) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_INFO, PM3_ESOFT, NULL, 0); goto OUT; } // OK - reply_mix(CMD_ACK, 1, 0, 0, (uint8_t *)&card, sizeof(legic_card_select_t)); + reply_ng(CMD_HF_LEGIC_INFO, PM3_SUCCESS, (uint8_t *)&card, sizeof(legic_card_select_t)); OUT: switch_off(); @@ -497,7 +497,7 @@ void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { // establish shared secret and detect card type uint8_t card_type = setup_phase(iv); if (init_card(card_type, &card) != PM3_SUCCESS) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_READER, PM3_EINIT, NULL, 0); goto OUT; } @@ -509,7 +509,7 @@ void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { for (uint16_t i = 0; i < len; ++i) { int16_t byte = read_byte(offset + i, card.cmdsize); if (byte == -1) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_READER, PM3_EFAILED, NULL, 0); goto OUT; } legic_mem[i] = byte; @@ -520,7 +520,7 @@ void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { } // OK - reply_mix(CMD_ACK, 1, len, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_READER, PM3_SUCCESS, (uint8_t *)&len, sizeof(len)); OUT: switch_off(); @@ -533,14 +533,14 @@ void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { // uid is not writeable if (offset <= WRITE_LOWERLIMIT) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_WRITER, PM3_EINVARG, NULL, 0); goto OUT; } // establish shared secret and detect card type uint8_t card_type = setup_phase(iv); if (init_card(card_type, &card) != PM3_SUCCESS) { - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_WRITER, PM3_EINIT, NULL, 0); goto OUT; } @@ -553,13 +553,13 @@ void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { while (len-- > 0 && BUTTON_PRESS() == false) { if (write_byte(len + offset, data[len], card.addrsize) == false) { Dbprintf("operation failed | %02X | %02X | %02X", len + offset, len, data[len]); - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_WRITER, PM3_EFAILED, NULL, 0); goto OUT; } } // OK - reply_mix(CMD_ACK, 1, len, 0, 0, 0); + reply_ng(CMD_HF_LEGIC_WRITER, PM3_SUCCESS, (uint8_t *)&len, sizeof(len)); OUT: switch_off(); diff --git a/client/luascripts/hf_legic_clone.lua b/client/luascripts/hf_legic_clone.lua index ae777dd7e..d41046d65 100644 --- a/client/luascripts/hf_legic_clone.lua +++ b/client/luascripts/hf_legic_clone.lua @@ -170,14 +170,9 @@ 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() + local d0 = ('%04X%04X%02X'):format(offset, len, iv) + local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_READER, data = d0} + local result, err = c:sendNG() if not result then return oops(err) end -- result is a packed data structure, data starts at offset 33 return result diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index f00e58407..38abf668b 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -30,7 +30,9 @@ static int CmdHelp(const char *Cmd); -#define MAX_LENGTH 1024 +#define LEGIC_PRIME_MIM22 22 +#define LEGIC_PRIME_MIM256 256 +#define LEGIC_PRIME_MIM1024 1024 static bool legic_xor(uint8_t *data, uint16_t cardsize) { @@ -42,14 +44,14 @@ static bool legic_xor(uint8_t *data, uint16_t cardsize) { uint8_t crc = data[4]; uint32_t calc_crc = CRC8Legic(data, 4); if (crc != calc_crc) { - PrintAndLogEx(INFO, "Crc mismatch, obsfuscation not possible"); + PrintAndLogEx(INFO, "CRC mismatch, obsfuscation not possible"); return false; } for (uint16_t i = 22; i < cardsize; i++) { data[i] ^= crc; } - PrintAndLogEx(SUCCESS, "applying xoring of data done!"); + PrintAndLogEx(SUCCESS, "Applying xoring of data done!"); return true; } @@ -393,33 +395,30 @@ static int CmdLegicRdbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic rdbl", "Read data from a LEGIC Prime tag", - "hf legic rdbl -o 0 -l 16 -> reads from byte[0] 16 bytes(system header)\n" - "hf legic rdbl -o 0 -l 4 --iv 55 -> reads from byte[0] 4 bytes with IV 0x55\n" - "hf legic rdbl -o 0 -l 256 --iv 55 -> reads from byte[0] 256 bytes with IV 0x55"); + "hf legic rdbl -o 0 -l 16 -> read 16 bytes from offset 0 (system header)\n" + "hf legic rdbl -o 0 -l 4 --iv 55 -> read 4 bytes from offset 0\n" + "hf legic rdbl -o 0 -l 256 --iv 55 -> read 256 bytes from offset 0"); void *argtable[] = { arg_param_begin, - arg_int1("o", "offset", "", "offset in data array to start download from"), - arg_int1("l", "length", "", "number of bytes to read"), + arg_int0("o", "offset", "", "offset in data array to start download from"), + arg_int0("l", "length", "", "number of bytes to read"), arg_str0(NULL, "iv", "", "Initialization vector to use. Must be odd and 7bits max"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int offset = arg_get_int_def(ctx, 1, 0); - - int len = arg_get_int_def(ctx, 2, 0); + int len = arg_get_int_def(ctx, 2, 16); int iv_len = 0; - uint8_t iv[1] = {0x01}; // formerly uidcrc - + uint8_t iv[1] = {0x01}; CLIGetHexWithReturn(ctx, 3, iv, &iv_len); - CLIParserFree(ctx); // sanity checks - if (len + offset >= MAX_LENGTH) { - PrintAndLogEx(WARNING, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", MAX_LENGTH, len + offset); + if (len + offset >= LEGIC_PRIME_MIM1024) { + PrintAndLogEx(WARNING, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", LEGIC_PRIME_MIM1024, len + offset); return PM3_EOUTOFBOUND; } @@ -435,9 +434,9 @@ static int CmdLegicRdbl(const char *Cmd) { uint16_t datalen = 0; int status = legic_read_mem(offset, len, iv[0], data, &datalen); if (status == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, " ## | 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------------------------------------------------------"); - print_hex_break(data, datalen, 32); + PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(data, datalen, 16); } free(data); return status; @@ -446,17 +445,31 @@ static int CmdLegicRdbl(const char *Cmd) { static int CmdLegicSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic sim", - "Simulates a LEGIC Prime tag. MIM22, MIM256, MIM1024 types can be emulated", - "hf legic sim -t 0 -> Simulate Type MIM22\n" - "hf legic sim -t 1 -> Simulate Type MIM256 (default)\n" - "hf legic sim -t 2 -> Simulate Type MIM1024"); + "Simulates a LEGIC Prime tag.\n" + "Following types supported (MIM22, MIM256, MIM1024)", + "hf legic sim --22\n" + ); void *argtable[] = { arg_param_begin, - arg_int0("t", "type", "", "Tag type to simulate."), + arg_lit0(NULL, "22", "LEGIC Prime MIM22"), + arg_lit0(NULL, "256", "LEGIC Prime MIM256 (def)"), + arg_lit0(NULL, "1024", "LEGIC Prime MIM1024"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool m1 = arg_get_lit(ctx, 1); + bool m2 = arg_get_lit(ctx, 2); + bool m3 = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + // validations + if (m1 + m2 + m3 > 1) { + PrintAndLogEx(WARNING, "Only specify one LEGIC Prime Type"); + return PM3_EINVARG; + } else if (m1 + m2 + m3 == 0) { + m2 = true; + } struct { uint8_t tagtype; @@ -464,14 +477,12 @@ static int CmdLegicSim(const char *Cmd) { } PACKED payload; payload.send_reply = true; - payload.tagtype = arg_get_int_def(ctx, 1, 1); - - CLIParserFree(ctx); - - if (payload.tagtype > 2) { - PrintAndLogEx(ERR, "Invalid tag type selected."); - return PM3_EINVARG; - } + if (m1) + payload.tagtype = 0; + else if (m2) + payload.tagtype = 1; + else if (m3) + payload.tagtype = 2; clearCommandBuffer(); SendCommandNG(CMD_HF_LEGIC_SIMULATE, (uint8_t *)&payload, sizeof(payload)); @@ -513,15 +524,13 @@ static int CmdLegicWrbl(const char *Cmd) { int offset = arg_get_int_def(ctx, 1, 0); int dlen = 0; - uint8_t data[MAX_LENGTH] = {0}; + uint8_t data[LEGIC_PRIME_MIM1024] = {0}; CLIGetHexWithReturn(ctx, 2, data, &dlen); bool autoconfirm = arg_get_lit(ctx, 3); CLIParserFree(ctx); - uint32_t IV = 0x55; - // OUT-OF-BOUNDS checks // UID 4+1 bytes can't be written to. if (offset < 5) { @@ -558,27 +567,34 @@ static int CmdLegicWrbl(const char *Cmd) { } } + uint32_t IV = 0x55; legic_chk_iv(&IV); - PrintAndLogEx(SUCCESS, "Writing to tag"); + PrintAndLogEx(SUCCESS, "Writing to tag to offset %i", offset); + + legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + dlen); + payload->offset = (offset & 0xFFFF); + payload->iv = (IV & 0x7F); + payload->len = dlen; + memcpy(payload->data, data, dlen); PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_LEGIC_WRITER, offset, dlen, IV, data, dlen); + SendCommandNG(CMD_HF_LEGIC_WRITER, (uint8_t *)payload, sizeof(legic_packet_t) + dlen); + free(payload); uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER, &resp, 2000) == false) { ++timeout; PrintAndLogEx(NORMAL, "." NOLF); - if (timeout > 7) { + if (timeout > 10) { PrintAndLogEx(WARNING, "\ncommand execution time out"); return PM3_ETIMEOUT; } } PrintAndLogEx(NORMAL, ""); - uint8_t isOK = resp.oldarg[0] & 0xFF; - if (!isOK) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Failed writing tag"); return PM3_ERFTRANS; } @@ -633,12 +649,17 @@ int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uin legic_chk_iv(&iv); + legic_packet_t *payload = calloc(1, sizeof(legic_packet_t)); + payload->offset = (offset & 0xFFFF); + payload->iv = iv; + payload->len = len; + clearCommandBuffer(); - SendCommandMIX(CMD_HF_LEGIC_READER, offset, len, iv, NULL, 0); + SendCommandNG(CMD_HF_LEGIC_READER, (uint8_t *)payload, sizeof(legic_packet_t)); PacketResponseNG resp; uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 1000)) { + while (WaitForResponseTimeout(CMD_HF_LEGIC_READER, &resp, 1000) == false) { ++timeout; PrintAndLogEx(NORMAL, "." NOLF); if (timeout > 14) { @@ -648,9 +669,9 @@ int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uin } PrintAndLogEx(NORMAL, ""); - uint8_t isOK = resp.oldarg[0] & 0xFF; - *outlen = resp.oldarg[1]; - if (!isOK) { + + *outlen = resp.data.asDwords[0]; + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Failed reading tag"); return PM3_ESOFT; } @@ -659,7 +680,7 @@ int legic_read_mem(uint32_t offset, uint32_t len, uint32_t iv, uint8_t *out, uin PrintAndLogEx(WARNING, "Fail, only managed to read %u bytes", *outlen); // copy data from device - if (!GetFromDevice(BIG_BUF_EML, out, *outlen, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, out, *outlen, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); return PM3_ETIMEOUT; } @@ -671,11 +692,11 @@ int legic_print_type(uint32_t tagtype, uint8_t spaces) { spc[10] = 0x00; char *spacer = spc + (10 - spaces); - if (tagtype == 22) + if (tagtype == LEGIC_PRIME_MIM22) PrintAndLogEx(SUCCESS, "%sTYPE: " _YELLOW_("MIM%d card (outdated)"), spacer, tagtype); - else if (tagtype == 256) + else if (tagtype == LEGIC_PRIME_MIM256) PrintAndLogEx(SUCCESS, "%sTYPE: " _YELLOW_("MIM%d card (234 bytes)"), spacer, tagtype); - else if (tagtype == 1024) + else if (tagtype == LEGIC_PRIME_MIM1024) PrintAndLogEx(SUCCESS, "%sTYPE: " _YELLOW_("MIM%d card (1002 bytes)"), spacer, tagtype); else PrintAndLogEx(INFO, "%sTYPE: " _YELLOW_("Unknown %06x"), spacer, tagtype); @@ -683,21 +704,22 @@ int legic_print_type(uint32_t tagtype, uint8_t spaces) { } int legic_get_type(legic_card_select_t *card) { - if (card == NULL) return PM3_EINVARG; + if (card == NULL) + return PM3_EINVARG; clearCommandBuffer(); SendCommandNG(CMD_HF_LEGIC_INFO, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) + if (WaitForResponseTimeout(CMD_HF_LEGIC_INFO, &resp, 1500) == false) return PM3_ETIMEOUT; - uint8_t isOK = resp.oldarg[0] & 0xFF; - if (!isOK) + if (resp.status != PM3_SUCCESS) return PM3_ESOFT; memcpy(card, resp.data.asBytes, sizeof(legic_card_select_t)); return PM3_SUCCESS; } + void legic_chk_iv(uint32_t *iv) { if ((*iv & 0x7F) != *iv) { *iv &= 0x7F; @@ -709,20 +731,30 @@ void legic_chk_iv(uint32_t *iv) { PrintAndLogEx(INFO, "LSB of IV must be SET %u", *iv); } } + void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { + // fast push mode g_conn.block_after_ACK = true; - for (size_t i = offset; i < numofbytes; i += PM3_CMD_DATA_SIZE) { + for (size_t i = offset; i < numofbytes; i += (PM3_CMD_DATA_SIZE - sizeof(legic_packet_t))) { - size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE); + size_t len = MIN((numofbytes - i), (PM3_CMD_DATA_SIZE - sizeof(legic_packet_t))); if (len == numofbytes - i) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } + + legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + len); + payload->offset = i; + payload->len = len; + memcpy(payload->data, src + i, len); + clearCommandBuffer(); - SendCommandOLD(CMD_HF_LEGIC_ESET, i, len, 0, src + i, len); + SendCommandNG(CMD_HF_LEGIC_ESET, (uint8_t *)payload, sizeof(legic_packet_t) + len); + free(payload); } } + static int CmdLegicReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic reader", @@ -748,10 +780,10 @@ static int CmdLegicReader(const char *Cmd) { static int CmdLegicDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic dump", - "Read all memory from LEGIC Prime MIM22, MIM256, MIM1024 and saves bin/eml/json dump file\n" - "It autodetects card type.", + "Read all memory from LEGIC Prime tags and saves to (bin/eml/json) dump file\n" + "It autodetects card type (MIM22, MIM256, MIM1024)", "hf legic dump --> use UID as filename\n" - "hf legic dump -f myfile --> use user specified filename\n" + "hf legic dump -f myfile \n" "hf legic dump --de --> use UID as filename and deobfuscate data"); void *argtable[] = { @@ -780,28 +812,32 @@ static int CmdLegicDump(const char *Cmd) { legic_print_type(dumplen, 0); PrintAndLogEx(SUCCESS, "Reading tag memory %d b...", dumplen); + legic_packet_t *payload = calloc(1, sizeof(legic_packet_t)); + payload->offset = 0; + payload->iv = 0x55; + payload->len = dumplen; + clearCommandBuffer(); - SendCommandMIX(CMD_HF_LEGIC_READER, 0x00, dumplen, 0x55, NULL, 0); + SendCommandNG(CMD_HF_LEGIC_READER, (uint8_t *)payload, sizeof(legic_packet_t)); PacketResponseNG resp; uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_HF_LEGIC_READER, &resp, 2000) == false) { ++timeout; PrintAndLogEx(NORMAL, "." NOLF); - if (timeout > 7) { + if (timeout > 10) { PrintAndLogEx(WARNING, "\ncommand execution time out"); return PM3_ETIMEOUT; } } PrintAndLogEx(NORMAL, ""); - uint8_t isOK = resp.oldarg[0] & 0xFF; - if (!isOK) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Failed dumping tag data"); return PM3_ERFTRANS; } - uint16_t readlen = resp.oldarg[1]; + uint16_t readlen = resp.data.asDwords[0]; uint8_t *data = calloc(readlen, sizeof(uint8_t)); if (!data) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); @@ -812,7 +848,7 @@ static int CmdLegicDump(const char *Cmd) { PrintAndLogEx(WARNING, "Fail, only managed to read 0x%02X bytes of 0x%02X", readlen, dumplen); // copy data from device - if (!GetFromDevice(BIG_BUF_EML, data, readlen, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, data, readlen, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(data); return PM3_ETIMEOUT; @@ -846,7 +882,7 @@ static int CmdLegicDump(const char *Cmd) { static int CmdLegicRestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic restore", - "Reads binary file and it autodetects card type and verifies that the file has the same size\n" + "Reads (bin/eml/json) file and it autodetects card type and verifies that the file has the same size\n" "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]", "hf legic restore -f myfile --> use user specified filename\n" "hf legic restore -f myfile --ob --> use UID as filename and obfuscate data"); @@ -876,21 +912,43 @@ static int CmdLegicRestore(const char *Cmd) { legic_print_type(card.cardsize, 0); // set up buffer - uint8_t *data = calloc(card.cardsize, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; + uint8_t *data = NULL; + size_t bytes_read = 0; + int res = 0; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&data, &bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&data, &bytes_read); + break; + } + case JSON: { + data = calloc(LEGIC_PRIME_MIM1024, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + res = loadFileJSON(filename, (void *)data, LEGIC_PRIME_MIM1024, &bytes_read, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + free(data); + return PM3_EINVARG; + } } - size_t numofbytes; - if (loadFile_safe(filename, ".bin", (void **)&data, &numofbytes) != PM3_SUCCESS) { + if (res != PM3_SUCCESS) { free(data); - PrintAndLogEx(WARNING, "Error, reading file"); return PM3_EFILE; } - if (card.cardsize != numofbytes) { - PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%u != %zu]", card.cardsize, numofbytes); + // validation + if (card.cardsize != bytes_read) { + PrintAndLogEx(WARNING, "Fail, filesize and cardsize is not equal. [%u != %zu]", card.cardsize, bytes_read); free(data); return PM3_EFILE; } @@ -911,21 +969,30 @@ static int CmdLegicRestore(const char *Cmd) { // transfer to device PacketResponseNG resp; - for (size_t i = 7; i < numofbytes; i += PM3_CMD_DATA_SIZE) { + // 7 = skip UID bytes and MCC + for (size_t i = 7; i < bytes_read; i += PM3_CMD_DATA_SIZE) { - size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE); - if (len == numofbytes - i) { + size_t len = MIN((bytes_read - i), PM3_CMD_DATA_SIZE); + if (len == bytes_read - i) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } + + legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + len); + payload->offset = i; + payload->iv = 0x55; + payload->len = len; + memcpy(payload->data, data + i, len); + clearCommandBuffer(); - SendCommandOLD(CMD_HF_LEGIC_WRITER, i, len, 0x55, data + i, len); + SendCommandNG(CMD_HF_LEGIC_WRITER, (uint8_t *)payload, sizeof(legic_packet_t) + len); + free(payload); uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER, &resp, 2000) == false) { ++timeout; PrintAndLogEx(NORMAL, "." NOLF); - if (timeout > 7) { + if (timeout > 10) { PrintAndLogEx(WARNING, "\ncommand execution time out"); free(data); return PM3_ETIMEOUT; @@ -933,9 +1000,8 @@ static int CmdLegicRestore(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - uint8_t isOK = resp.oldarg[0] & 0xFF; - if (!isOK) { - PrintAndLogEx(WARNING, "Failed writing tag [msg = %u]", (uint8_t)(resp.oldarg[1] & 0xFF)); + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Failed writing tag"); free(data); return PM3_ERFTRANS; } @@ -950,15 +1016,14 @@ static int CmdLegicRestore(const char *Cmd) { static int CmdLegicELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic eload", - "Loads a LEGIC binary dump into emulator memory", - "hf legic eload -f myfile -t 0 -> Simulate Type MIM22\n" - "hf legic eload -f myfile -t 1 -> Simulate Type MIM256 (default)\n" - "hf legic eload -f myfile -t 2 -> Simulate Type MIM1024"); + "Loads a LEGIC Prime dump file into emulator memory", + "hf legic eload -f myfile\n" + "hf legic eload -f myfile --obfuscate\n" + ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename to restore"), - arg_int0("t", "type", "", "Tag type to simulate."), + arg_str1("f", "file", "", "Filename to load"), arg_lit0(NULL, "obfuscate", "Obfuscate dump data (xor with MCC)"), arg_param_end }; @@ -968,67 +1033,81 @@ static int CmdLegicELoad(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - size_t numofbytes = 0; - - switch (arg_get_int_def(ctx, 2, 1)) { - case 0: - numofbytes = 22; - break; - case 1: - numofbytes = 256; - break; - case 2: - numofbytes = 1024; - break; - default: - PrintAndLogEx(ERR, "Unknown tag type"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - - bool shall_obsfuscate = arg_get_lit(ctx, 3); - + bool shall_obsfuscate = arg_get_lit(ctx, 2); CLIParserFree(ctx); // set up buffer - uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); - if (!data) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return PM3_EMALLOC; + uint8_t *data = NULL; + size_t bytes_read = 0; + int res = 0; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&data, &bytes_read); + break; + } + case EML: { + res = loadFileEML_safe(filename, (void **)&data, &bytes_read); + break; + } + case JSON: { + data = calloc(LEGIC_PRIME_MIM1024, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + res = loadFileJSON(filename, (void *)data, LEGIC_PRIME_MIM1024, &bytes_read, NULL); + break; + } + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/JSON/EML formats allowed"); + free(data); + return PM3_EINVARG; + } } - if (loadFile_safe(filename, ".bin", (void **)&data, &numofbytes) != PM3_SUCCESS) { + if (res != PM3_SUCCESS) { + free(data); + return PM3_EFILE; + } + + // validation + if (bytes_read != LEGIC_PRIME_MIM22 && + bytes_read != LEGIC_PRIME_MIM256 && + bytes_read != LEGIC_PRIME_MIM1024) { + PrintAndLogEx(ERR, "File content error. Read %zu bytes", bytes_read); free(data); - PrintAndLogEx(WARNING, "Error, reading file"); return PM3_EFILE; } if (shall_obsfuscate) { - legic_xor(data, numofbytes); + legic_xor(data, bytes_read); } PrintAndLogEx(SUCCESS, "Uploading to emulator memory"); - legic_seteml(data, 0, numofbytes); + legic_seteml(data, 0, bytes_read); free(data); - PrintAndLogEx(SUCCESS, "Done"); + PrintAndLogEx(SUCCESS, "Done!"); return PM3_SUCCESS; } static int CmdLegicESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic esave", - "Saves bin/eml/json dump file of emulator memory", - "hf legic esave --> uses UID as filename\n" - "hf legic esave -f myfile -t 0 --> Type MIM22\n" - "hf legic esave -f myfile -t 1 --> Type MIM256 (default)\n" - "hf legic esave -f myfile -t 2 --> Type MIM1024"); + "Saves a (bin/eml/json) dump file of emulator memory", + "hf legic esave --> uses UID as filename\n" + "hf legic esave -f myfile --22\n" + "hf legic esave -f myfile --22 --de\n" + ); void *argtable[] = { arg_param_begin, arg_str0("f", "file", "", "Filename to save"), - arg_int0("t", "type", "", "Tag type"), - arg_lit0(NULL, "deobfuscate", "De-obfuscate dump data (xor with MCC)"), + arg_lit0(NULL, "22", "LEGIC Prime MIM22"), + arg_lit0(NULL, "256", "LEGIC Prime MIM256 (def)"), + arg_lit0(NULL, "1024", "LEGIC Prime MIM1024"), + arg_lit0(NULL, "de", "De-obfuscate dump data (xor with MCC)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1037,38 +1116,38 @@ static int CmdLegicESave(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - size_t numofbytes = 0; + bool m1 = arg_get_lit(ctx, 2); + bool m2 = arg_get_lit(ctx, 3); + bool m3 = arg_get_lit(ctx, 4); + bool shall_deobsfuscate = arg_get_lit(ctx, 5); + CLIParserFree(ctx); - switch (arg_get_int_def(ctx, 2, 1)) { - case 0: - numofbytes = 22; - break; - case 1: - numofbytes = 256; - break; - case 2: - numofbytes = 1024; - break; - default: - PrintAndLogEx(ERR, "Unknown tag type"); - CLIParserFree(ctx); - return PM3_EINVARG; + // validations + if (m1 + m2 + m3 > 1) { + PrintAndLogEx(WARNING, "Only specify one LEGIC Prime Type"); + return PM3_EINVARG; + } else if (m1 + m2 + m3 == 0) { + m2 = true; } - bool shall_deobsfuscate = arg_get_lit(ctx, 3); - - CLIParserFree(ctx); + size_t numofbytes = LEGIC_PRIME_MIM256; + if (m1) + numofbytes = LEGIC_PRIME_MIM22; + else if (m2) + numofbytes = LEGIC_PRIME_MIM256; + else if (m3) + numofbytes = LEGIC_PRIME_MIM1024; // set up buffer uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); - if (!data) { + if (data == NULL) { 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)) { + if (GetFromDevice(BIG_BUF_EML, data, numofbytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(data); return PM3_ETIMEOUT; @@ -1091,6 +1170,65 @@ static int CmdLegicESave(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLegicEView(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic eview", + "It displays emulator memory", + "hf legic eview\n" + "hf legic eview --22\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "22", "LEGIC Prime MIM22"), + arg_lit0(NULL, "256", "LEGIC Prime MIM256 (def)"), + arg_lit0(NULL, "1024", "LEGIC Prime MIM1024"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool m1 = arg_get_lit(ctx, 1); + bool m2 = arg_get_lit(ctx, 2); + bool m3 = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + // validations + if (m1 + m2 + m3 > 1) { + PrintAndLogEx(WARNING, "Only specify one LEGIC Prime Type"); + return PM3_EINVARG; + } else if (m1 + m2 + m3 == 0) { + m2 = true; + } + + size_t bytes = LEGIC_PRIME_MIM256; + if (m1) + bytes = LEGIC_PRIME_MIM22; + else if (m2) + bytes = LEGIC_PRIME_MIM256; + else if (m3) + bytes = LEGIC_PRIME_MIM1024; + + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "downloading emulator memory"); + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(dump); + return PM3_ETIMEOUT; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(dump, bytes, 16); + free(dump); + return PM3_SUCCESS; +} + static int CmdLegicWipe(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic wipe", @@ -1136,14 +1274,22 @@ static int CmdLegicWipe(const char *Cmd) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } + + legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + len); + payload->offset = i; + payload->iv = 0x55; + payload->len = len; + memcpy(payload->data, data + i, len); + clearCommandBuffer(); - SendCommandOLD(CMD_HF_LEGIC_WRITER, i, len, 0x55, data + i, len); + SendCommandNG(CMD_HF_LEGIC_WRITER, (uint8_t *)payload, sizeof(legic_packet_t) + len); + free(payload); uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER, &resp, 2000) == false) { ++timeout; PrintAndLogEx(NORMAL, "." NOLF); - if (timeout > 7) { + if (timeout > 10) { PrintAndLogEx(WARNING, "\ncommand execution time out"); free(data); return PM3_ETIMEOUT; @@ -1151,14 +1297,13 @@ static int CmdLegicWipe(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - uint8_t isOK = resp.oldarg[0] & 0xFF; - if (!isOK) { - PrintAndLogEx(WARNING, "Failed writing tag [msg = %u]", (uint8_t)(resp.oldarg[1] & 0xFF)); + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "failed writing tag"); free(data); return PM3_ERFTRANS; } } - PrintAndLogEx(SUCCESS, "ok\n"); + PrintAndLogEx(SUCCESS, "Done!\n"); free(data); return PM3_SUCCESS; } @@ -1167,20 +1312,85 @@ static int CmdLegicList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf legic", "legic"); } +static int CmdLegicView(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic view", + "Print a LEGIC Prime dump file (bin/eml/json)", + "hf legic view -f hf-legic-01020304-dump.bin" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Filename of dump"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + // reserve memory + uint8_t *dump = NULL; + size_t bytes_read = 0; + int res = 0; + DumpFileType_t dftype = getfiletype(filename); + switch (dftype) { + case BIN: { + res = loadFile_safe(filename, ".bin", (void **)&dump, &bytes_read); + break; + } + case JSON: { + dump = calloc(LEGIC_PRIME_MIM1024, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + res = loadFileJSON(filename, (void *)dump, LEGIC_PRIME_MIM1024, &bytes_read, NULL); + break; + } + case EML: + res = loadFileEML_safe(filename, (void **)&dump, &bytes_read); + break; + case DICTIONARY: { + PrintAndLogEx(ERR, "Error: Only BIN/EML/JSON formats allowed"); + free(dump); + return PM3_EINVARG; + } + } + + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); + free(dump); + return PM3_EFILE; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(dump, bytes_read, 16); + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdLegicList, AlwaysAvailable, "List LEGIC history"}, - {"reader", CmdLegicReader, IfPm3Legicrf, "LEGIC Prime Reader UID and tag info"}, - {"info", CmdLegicInfo, IfPm3Legicrf, "Display deobfuscated and decoded LEGIC Prime tag data"}, {"dump", CmdLegicDump, IfPm3Legicrf, "Dump LEGIC Prime tag to binary file"}, - {"restore", CmdLegicRestore, IfPm3Legicrf, "Restore a dump file onto a LEGIC Prime tag"}, + {"info", CmdLegicInfo, IfPm3Legicrf, "Display deobfuscated and decoded LEGIC Prime tag data"}, + {"list", CmdLegicList, AlwaysAvailable, "List LEGIC history"}, {"rdbl", CmdLegicRdbl, IfPm3Legicrf, "Read bytes from a LEGIC Prime tag"}, - {"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, AlwaysAvailable, "Load binary dump to emulator memory"}, - {"esave", CmdLegicESave, AlwaysAvailable, "Save emulator memory to binary file"}, + {"reader", CmdLegicReader, IfPm3Legicrf, "LEGIC Prime Reader UID and tag info"}, + {"restore", CmdLegicRestore, IfPm3Legicrf, "Restore a dump file onto a LEGIC Prime tag"}, {"wipe", CmdLegicWipe, IfPm3Legicrf, "Wipe a LEGIC Prime tag"}, + {"wrbl", CmdLegicWrbl, IfPm3Legicrf, "Write data to a LEGIC Prime tag"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"}, + {"sim", CmdLegicSim, IfPm3Legicrf, "Start tag simulator"}, + {"eload", CmdLegicELoad, IfPm3Legicrf, "Load binary dump to emulator memory"}, + {"esave", CmdLegicESave, IfPm3Legicrf, "Save emulator memory to binary file"}, + {"eview", CmdLegicEView, IfPm3Legicrf, "View emulator memory"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("utils") " ---------------------"}, + {"crc", CmdLegicCalcCrc, AlwaysAvailable, "Calculate Legic CRC over given bytes"}, + {"view", CmdLegicView, AlwaysAvailable, "Display content from tag dump file"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index dd4636b09..e662e248b 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -4152,7 +4152,6 @@ static int CmdHF14AMfEView(const char *Cmd) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } - memset(dump, 0, bytes); PrintAndLogEx(INFO, "downloading emulator memory"); if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { diff --git a/client/src/cmdhfwaveshare.c b/client/src/cmdhfwaveshare.c index 67709035c..ae2f74582 100644 --- a/client/src/cmdhfwaveshare.c +++ b/client/src/cmdhfwaveshare.c @@ -390,18 +390,18 @@ static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, ui return PM3_ESOFT; } - int16_t *chanR = calloc(width * height, sizeof(int16_t)); + int16_t *chanR = calloc(((size_t)width) * height, sizeof(int16_t)); if (chanR == NULL) { return PM3_EMALLOC; } - int16_t *chanG = calloc(width * height, sizeof(int16_t)); + int16_t *chanG = calloc(((size_t)width) * height, sizeof(int16_t)); if (chanG == NULL) { free(chanR); return PM3_EMALLOC; } - int16_t *chanB = calloc(width * height, sizeof(int16_t)); + int16_t *chanB = calloc(((size_t)width) * height, sizeof(int16_t)); if (chanB == NULL) { free(chanR); free(chanG); @@ -423,14 +423,14 @@ static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, ui if ((model_nr == M1in54B) || (model_nr == M2in13B)) { // for BW+Red screens: - uint8_t *mapBlack = calloc((uint32_t)(width * height), sizeof(uint8_t)); + uint8_t *mapBlack = calloc(((size_t)width) * height, sizeof(uint8_t)); if (mapBlack == NULL) { free(chanR); free(chanG); free(chanB); return PM3_EMALLOC; } - uint8_t *mapRed = calloc((uint32_t)(width * height), sizeof(uint8_t)); + uint8_t *mapRed = calloc(((size_t)width) * height, sizeof(uint8_t)); if (mapRed == NULL) { free(chanR); free(chanG); @@ -490,7 +490,7 @@ static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, ui free(mapRed); } else { // for BW-only screens: - int16_t *chanGrey = calloc(width * height, sizeof(int16_t)); + int16_t *chanGrey = calloc(((size_t)width) * height, sizeof(int16_t)); if (chanGrey == NULL) { free(chanR); free(chanG); @@ -500,7 +500,7 @@ static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, ui rgb_to_gray(chanR, chanG, chanB, width, height, chanGrey); dither_chan_inplace(chanGrey, width, height); - uint8_t *mapBlack = calloc(width * height, sizeof(uint8_t)); + uint8_t *mapBlack = calloc(((size_t)width) * height, sizeof(uint8_t)); if (mapBlack == NULL) { free(chanR); free(chanG); diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 6b1a8b9b8..b228a5c91 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -375,8 +375,8 @@ static int CmdDetectReader(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("L", "LF", "detect low frequence 125/134 kHz"), - arg_lit0("H", "HF", "detect high frequence 13.56 MHZ"), + arg_lit0("L", "LF", "detect low frequency 125/134 kHz"), + arg_lit0("H", "HF", "detect high frequency 13.56 MHZ"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -385,7 +385,7 @@ static int CmdDetectReader(const char *Cmd) { CLIParserFree(ctx); if ((lf + hf) > 1) { - PrintAndLogEx(INFO, "Can only set one frequence"); + PrintAndLogEx(INFO, "Can only set one frequency"); return PM3_EINVARG; } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index f2249d1ee..2ac29b849 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -1239,6 +1239,10 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); } + if (!strcmp(ctype, "legic")) { + JsonLoadBufAsHex(root, "$.raw", udata, maxdatalen, datalen); + } + out: if (callback != NULL) { diff --git a/client/src/loclass/cipherutils.c b/client/src/loclass/cipherutils.c index bcfcbb7b7..eaca50325 100644 --- a/client/src/loclass/cipherutils.c +++ b/client/src/loclass/cipherutils.c @@ -147,9 +147,11 @@ void printarr(const char *name, uint8_t *arr, int len) { char *output = calloc(outsize, sizeof(char)); cx = snprintf(output, outsize, "uint8_t %s[] = {", name); for (i = 0; i < len; i++) { - cx += snprintf(output + cx, outsize - cx, "0x%02x,", *(arr + i)); //5 bytes per byte + if (cx < outsize) + cx += snprintf(output + cx, outsize - cx, "0x%02x,", *(arr + i)); //5 bytes per byte } - snprintf(output + cx, outsize - cx, "};"); + if (cx < outsize) + snprintf(output + cx, outsize - cx, "};"); PrintAndLogEx(INFO, output); free(output); } @@ -165,12 +167,16 @@ void printarr_human_readable(const char *title, uint8_t *arr, int len) { for (i = 0; i < len; i++) { if (i % 16 == 0) { - if (i == 0) - cx += snprintf(output + cx, outsize - cx, "%02x| ", i); - else - cx += snprintf(output + cx, outsize - cx, "\n%02x| ", i); + if (i == 0) { + if (cx < outsize) + cx += snprintf(output + cx, outsize - cx, "%02x| ", i); + } else { + if (cx < outsize) + cx += snprintf(output + cx, outsize - cx, "\n%02x| ", i); + } } - cx += snprintf(output + cx, outsize - cx, "%02x ", *(arr + i)); + if (cx < outsize) + cx += snprintf(output + cx, outsize - cx, "%02x ", *(arr + i)); } PrintAndLogEx(INFO, output); free(output); diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 8c4aa87fd..e58488a87 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -872,7 +872,7 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt uint8_t data[]; } PACKED; - size_t size = blocksCount * blockBtWidth; + size_t size = ((size_t) blocksCount) * blockBtWidth; if (size > (PM3_CMD_DATA_SIZE - sizeof(struct p))) { return PM3_ESOFT; } diff --git a/client/src/pm3line_vocabulory.h b/client/src/pm3line_vocabulory.h index a54adb525..8590b0b26 100644 --- a/client/src/pm3line_vocabulory.h +++ b/client/src/pm3line_vocabulory.h @@ -262,18 +262,20 @@ const static vocabulory_t vocabulory[] = { { 1, "hf iclass permutekey" }, { 1, "hf iclass view" }, { 1, "hf legic help" }, - { 1, "hf legic list" }, - { 0, "hf legic reader" }, - { 0, "hf legic info" }, { 0, "hf legic dump" }, - { 0, "hf legic restore" }, + { 0, "hf legic info" }, + { 1, "hf legic list" }, { 0, "hf legic rdbl" }, - { 0, "hf legic sim" }, - { 0, "hf legic wrbl" }, - { 1, "hf legic crc" }, - { 1, "hf legic eload" }, - { 1, "hf legic esave" }, + { 0, "hf legic reader" }, + { 0, "hf legic restore" }, { 0, "hf legic wipe" }, + { 0, "hf legic wrbl" }, + { 0, "hf legic sim" }, + { 0, "hf legic eload" }, + { 0, "hf legic esave" }, + { 0, "hf legic eview" }, + { 1, "hf legic crc" }, + { 1, "hf legic view" }, { 1, "hf lto help" }, { 0, "hf lto dump" }, { 0, "hf lto restore" }, diff --git a/doc/commands.json b/doc/commands.json index d4b576162..dc72eb427 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -3165,15 +3165,65 @@ ], "usage": "hf legic crc [-h] -d [--mcc ] [-t ]" }, - "hf legic dump": { - "command": "hf legic dump", - "description": "read all memory from legic prime mim22, mim256, mim1024 and saves bin/eml/json dump file it autodetects card type.", + "hf legic eload": { + "command": "hf legic eload", + "description": "loads a legic prime dump file into emulator memory", "notes": [ - "hf legic dump -> use uid as filename", - "hf legic dump -f myfile -> use user specified filename", - "hf legic dump --de -> use uid as filename and deobfuscate data" + "hf legic eload -f myfile", + "hf legic eload -f myfile --obfuscate" ], "offline": false, + "options": [ + "-h, --help this help", + "-f, --file filename to load", + "--obfuscate obfuscate dump data (xor with mcc)" + ], + "usage": "hf legic eload [-h] -f [--obfuscate]" + }, + "hf legic esave": { + "command": "hf legic esave", + "description": "saves a (bin/eml/json) dump file of emulator memory", + "notes": [ + "hf legic esave -> uses uid as filename", + "hf legic esave -f myfile --22", + "hf legic esave -f myfile --22 --de" + ], + "offline": false, + "options": [ + "-h, --help this help", + "-f, --file filename to save", + "--22 legic prime mim22", + "--256 legic prime mim256 (def)", + "--1024 legic prime mim1024", + "--de de-obfuscate dump data (xor with mcc)" + ], + "usage": "hf legic esave [-h] [-f ] [--22] [--256] [--1024] [--de]" + }, + "hf legic eview": { + "command": "hf legic eview", + "description": "it displays emulator memory", + "notes": [ + "hf legic eview", + "hf legic eview --22" + ], + "offline": false, + "options": [ + "-h, --help this help", + "--22 legic prime mim22", + "--256 legic prime mim256 (def)", + "--1024 legic prime mim1024" + ], + "usage": "hf legic eview [-h] [--22] [--256] [--1024]" + }, + "hf legic help": { + "command": "hf legic help", + "description": "----------- --------------------- operations --------------------- help this help list list legic history ----------- --------------------- simulation --------------------- ----------- --------------------- utils --------------------- crc calculate legic crc over given bytes view display content from tag dump file --------------------------------------------------------------------------------------- hf legic dump available offline: no read all memory from legic prime tags and saves to (bin/eml/json) dump file it autodetects card type (mim22, mim256, mim1024)", + "notes": [ + "hf legic dump -> use uid as filename", + "hf legic dump -f myfile", + "hf legic dump --de -> use uid as filename and deobfuscate data" + ], + "offline": true, "options": [ "-h, --help this help", "-f, --file dump filename", @@ -3181,44 +3231,21 @@ ], "usage": "hf legic dump [-h] [-f ] [--de]" }, - "hf legic eload": { - "command": "hf legic eload", - "description": "loads a legic binary dump into emulator memory", + "hf legic info": { + "command": "hf legic info", + "description": "gets information from a legic prime tag like systemarea, user areas, etc", "notes": [ - "hf legic eload -f myfile -t 0 -> simulate type mim22", - "hf legic eload -f myfile -t 1 -> simulate type mim256 (default)", - "hf legic eload -f myfile -t 2 -> simulate type mim1024" + "hf legic info" ], - "offline": true, + "offline": false, "options": [ - "-h, --help this help", - "-f, --file filename to restore", - "-t, --type tag type to simulate.", - "--obfuscate obfuscate dump data (xor with mcc)" + "-h, --help this help" ], - "usage": "hf legic eload [-h] -f [-t ] [--obfuscate]" + "usage": "hf legic info [-h]" }, - "hf legic esave": { - "command": "hf legic esave", - "description": "saves bin/eml/json dump file of emulator memory", - "notes": [ - "hf legic esave -> uses uid as filename", - "hf legic esave -f myfile -t 0 -> type mim22", - "hf legic esave -f myfile -t 1 -> type mim256 (default)", - "hf legic esave -f myfile -t 2 -> type mim1024" - ], - "offline": true, - "options": [ - "-h, --help this help", - "-f, --file filename to save", - "-t, --type tag type", - "--deobfuscate de-obfuscate dump data (xor with mcc)" - ], - "usage": "hf legic esave [-h] [-f ] [-t ] [--deobfuscate]" - }, - "hf legic help": { - "command": "hf legic help", - "description": "help this help list list legic history crc calculate legic crc over given bytes eload load binary dump to emulator memory esave save emulator memory to binary file --------------------------------------------------------------------------------------- hf legic list available offline: yes alias of `trace list -t legic` with selected protocol data to annotate trace buffer you can load a trace from file (see `trace load -h`) or it be downloaded from device by default it accepts all other arguments of `trace list`. note that some might not be relevant for this specific protocol", + "hf legic list": { + "command": "hf legic list", + "description": "alias of `trace list -t legic` with selected protocol data to annotate trace buffer you can load a trace from file (see `trace load -h`) or it be downloaded from device by default it accepts all other arguments of `trace list`. note that some might not be relevant for this specific protocol", "notes": [ "hf legic list -f -> show frame delay times", "hf legic list -1 -> use trace buffer" @@ -3237,25 +3264,13 @@ ], "usage": "hf legic list [-h1fcrux] [--dict ]" }, - "hf legic info": { - "command": "hf legic info", - "description": "gets information from a legic prime tag like systemarea, user areas, etc", - "notes": [ - "hf legic info" - ], - "offline": false, - "options": [ - "-h, --help this help" - ], - "usage": "hf legic info [-h]" - }, "hf legic rdbl": { "command": "hf legic rdbl", "description": "read data from a legic prime tag", "notes": [ - "hf legic rdbl -o 0 -l 16 -> reads from byte[0] 16 bytes(system header)", - "hf legic rdbl -o 0 -l 4 --iv 55 -> reads from byte[0] 4 bytes with iv 0x55", - "hf legic rdbl -o 0 -l 256 --iv 55 -> reads from byte[0] 256 bytes with iv 0x55" + "hf legic rdbl -o 0 -l 16 -> read 16 bytes from offset 0 (system header)", + "hf legic rdbl -o 0 -l 4 --iv 55 -> read 4 bytes from offset 0", + "hf legic rdbl -o 0 -l 256 --iv 55 -> read 256 bytes from offset 0" ], "offline": false, "options": [ @@ -3264,7 +3279,7 @@ "-l, --length number of bytes to read", "--iv initialization vector to use. must be odd and 7bits max" ], - "usage": "hf legic rdbl [-h] -o -l [--iv ]" + "usage": "hf legic rdbl [-h] [-o ] [-l ] [--iv ]" }, "hf legic reader": { "command": "hf legic reader", @@ -3281,7 +3296,7 @@ }, "hf legic restore": { "command": "hf legic restore", - "description": "reads binary file and it autodetects card type and verifies that the file has the same size then write the data back to card. all bytes except the first 7bytes [uid(4) mcc(1) dcf(2)]", + "description": "reads (bin/eml/json) file and it autodetects card type and verifies that the file has the same size then write the data back to card. all bytes except the first 7bytes [uid(4) mcc(1) dcf(2)]", "notes": [ "hf legic restore -f myfile -> use user specified filename", "hf legic restore -f myfile --ob -> use uid as filename and obfuscate data" @@ -3296,18 +3311,31 @@ }, "hf legic sim": { "command": "hf legic sim", - "description": "simulates a legic prime tag. mim22, mim256, mim1024 types can be emulated", + "description": "simulates a legic prime tag. following types supported (mim22, mim256, mim1024)", "notes": [ - "hf legic sim -t 0 -> simulate type mim22", - "hf legic sim -t 1 -> simulate type mim256 (default)", - "hf legic sim -t 2 -> simulate type mim1024" + "hf legic sim --22" ], "offline": false, "options": [ "-h, --help this help", - "-t, --type tag type to simulate." + "--22 legic prime mim22", + "--256 legic prime mim256 (def)", + "--1024 legic prime mim1024" ], - "usage": "hf legic sim [-h] [-t ]" + "usage": "hf legic sim [-h] [--22] [--256] [--1024]" + }, + "hf legic view": { + "command": "hf legic view", + "description": "print a legic prime dump file (bin/eml/json)", + "notes": [ + "hf legic view -f hf-legic-01020304-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help this help", + "-f, --file filename of dump" + ], + "usage": "hf legic view [-h] -f " }, "hf legic wipe": { "command": "hf legic wipe", @@ -6258,8 +6286,8 @@ "offline": false, "options": [ "-h, --help this help", - "-l, --lf detect low frequence 125/134 khz", - "-h, --hf detect high frequence 13.56 mhz" + "-l, --lf detect low frequency 125/134 khz", + "-h, --hf detect high frequency 13.56 mhz" ], "usage": "hw detectreader [-hlh]" }, @@ -10415,8 +10443,8 @@ } }, "metadata": { - "commands_extracted": 606, + "commands_extracted": 608, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2022-02-13T11:23:34" + "extracted_on": "2022-02-14T20:43:01" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index b695ba5b5..cf1be550e 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -401,18 +401,20 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf legic help `|Y |`This help` -|`hf legic list `|Y |`List LEGIC history` -|`hf legic reader `|N |`LEGIC Prime Reader UID and tag info` -|`hf legic info `|N |`Display deobfuscated and decoded LEGIC Prime tag data` |`hf legic dump `|N |`Dump LEGIC Prime tag to binary file` -|`hf legic restore `|N |`Restore a dump file onto a LEGIC Prime tag` +|`hf legic info `|N |`Display deobfuscated and decoded LEGIC Prime tag data` +|`hf legic list `|Y |`List LEGIC history` |`hf legic rdbl `|N |`Read bytes from a LEGIC Prime tag` -|`hf legic sim `|N |`Start tag simulator` -|`hf legic wrbl `|N |`Write data to a LEGIC Prime tag` -|`hf legic crc `|Y |`Calculate Legic CRC over given bytes` -|`hf legic eload `|Y |`Load binary dump to emulator memory` -|`hf legic esave `|Y |`Save emulator memory to binary file` +|`hf legic reader `|N |`LEGIC Prime Reader UID and tag info` +|`hf legic restore `|N |`Restore a dump file onto a LEGIC Prime tag` |`hf legic wipe `|N |`Wipe a LEGIC Prime tag` +|`hf legic wrbl `|N |`Write data to a LEGIC Prime tag` +|`hf legic sim `|N |`Start tag simulator` +|`hf legic eload `|N |`Load binary dump to emulator memory` +|`hf legic esave `|N |`Save emulator memory to binary file` +|`hf legic eview `|N |`View emulator memory` +|`hf legic crc `|Y |`Calculate Legic CRC over given bytes` +|`hf legic view `|Y |`Display content from tag dump file` ### hf lto diff --git a/include/legic.h b/include/legic.h index 7fb3ff19b..ce816cdf8 100644 --- a/include/legic.h +++ b/include/legic.h @@ -30,7 +30,14 @@ typedef struct { uint8_t cmdsize; uint8_t addrsize; uint16_t cardsize; -} legic_card_select_t; +} PACKED legic_card_select_t; + +typedef struct { + uint16_t offset; + uint16_t len; + uint8_t iv; + uint8_t data[]; +} PACKED legic_packet_t; // iceman: todo : this should be packed diff --git a/tools/fpga_compress/fpga_compress.c b/tools/fpga_compress/fpga_compress.c index 04e0dbc9f..66b0b7d4f 100644 --- a/tools/fpga_compress/fpga_compress.c +++ b/tools/fpga_compress/fpga_compress.c @@ -217,7 +217,7 @@ static int zlib_decompress(FILE *infile, FILE *outfiles[], uint8_t num_outfiles, // seeking for trailing zeroes long offset = 0; long outfilesizes[10] = {0}; - for (uint16_t k = 0; k < *outsize / (FPGA_INTERLEAVE_SIZE * num_outfiles); k++) { + for (long k = 0; k < *outsize / (FPGA_INTERLEAVE_SIZE * num_outfiles); k++) { for (uint16_t j = 0; j < num_outfiles; j++) { for (long i = 0; i < FPGA_INTERLEAVE_SIZE; i++) { if (outbufall[offset + i]) { @@ -234,7 +234,7 @@ static int zlib_decompress(FILE *infile, FILE *outfiles[], uint8_t num_outfiles, total_size += outfilesizes[j]; } offset = 0; - for (uint16_t k = 0; k < *outsize / (FPGA_INTERLEAVE_SIZE * num_outfiles); k++) { + for (long k = 0; k < *outsize / (FPGA_INTERLEAVE_SIZE * num_outfiles); k++) { for (uint16_t j = 0; j < num_outfiles; j++) { if (k * FPGA_INTERLEAVE_SIZE < outfilesizes[j]) { uint16_t chunk = outfilesizes[j] - (k * FPGA_INTERLEAVE_SIZE) < FPGA_INTERLEAVE_SIZE ? outfilesizes[j] - (k * FPGA_INTERLEAVE_SIZE) : FPGA_INTERLEAVE_SIZE; @@ -275,7 +275,7 @@ static int bitparse_find_section(FILE *infile, char section_name, unsigned int * /* Strange section name, abort */ break; } - unsigned int current_length = 0; + uint32_t current_length = 0; int tmp; switch (current_name) { case 'e': @@ -313,7 +313,7 @@ static int bitparse_find_section(FILE *infile, char section_name, unsigned int * break; } - for (uint16_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { + for (uint32_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { (void)fgetc(infile); numbytes++; } @@ -322,7 +322,7 @@ static int bitparse_find_section(FILE *infile, char section_name, unsigned int * } static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len) { - unsigned int fpga_info_len; + uint32_t fpga_info_len; char tempstr[40] = {0x00}; dst[0] = '\0'; @@ -344,7 +344,7 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len strncat(dst, " image built", len - strlen(dst) - 1); if (bitparse_find_section(infile, 'b', &fpga_info_len)) { strncat(dst, " for ", len - strlen(dst) - 1); - for (uint16_t i = 0; i < fpga_info_len; i++) { + for (uint32_t i = 0; i < fpga_info_len; i++) { char c = (char)fgetc(infile); if (i < sizeof(tempstr)) { tempstr[i] = c; @@ -355,7 +355,7 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len if (bitparse_find_section(infile, 'c', &fpga_info_len)) { strncat(dst, " on ", len - strlen(dst) - 1); - for (uint16_t i = 0; i < fpga_info_len; i++) { + for (uint32_t i = 0; i < fpga_info_len; i++) { char c = (char)fgetc(infile); if (i < sizeof(tempstr)) { if (c == '/') c = '-'; @@ -368,7 +368,7 @@ static int FpgaGatherVersion(FILE *infile, char *infile_name, char *dst, int len if (bitparse_find_section(infile, 'd', &fpga_info_len)) { strncat(dst, " at ", len - strlen(dst) - 1); - for (uint16_t i = 0; i < fpga_info_len; i++) { + for (uint32_t i = 0; i < fpga_info_len; i++) { char c = (char)fgetc(infile); if (i < sizeof(tempstr)) { if (c == ' ') c = '0'; @@ -428,10 +428,10 @@ int main(int argc, char **argv) { usage(); return (EXIT_FAILURE); } - int num_output_files = argc - 3; + uint8_t num_output_files = argc - 3; FILE **outfiles = calloc(num_output_files, sizeof(FILE *)); char **outfile_names = calloc(num_output_files, sizeof(char *)); - for (uint16_t i = 0; i < num_output_files; i++) { + for (uint8_t i = 0; i < num_output_files; i++) { outfile_names[i] = argv[i + 3]; outfiles[i] = fopen(outfile_names[i], "wb"); if (outfiles[i] == NULL) { @@ -463,7 +463,7 @@ int main(int argc, char **argv) { } else { // Compress or generate version info bool generate_version_file = false; - int num_input_files = 0; + uint8_t num_input_files = 0; if (!strcmp(argv[1], "-v")) { // generate version info generate_version_file = true; num_input_files = argc - 3; @@ -473,7 +473,7 @@ int main(int argc, char **argv) { FILE **infiles = calloc(num_input_files, sizeof(FILE *)); char **infile_names = calloc(num_input_files, sizeof(char *)); - for (uint16_t i = 0; i < num_input_files; i++) { + for (uint8_t i = 0; i < num_input_files; i++) { infile_names[i] = argv[i + (generate_version_file ? 2 : 1)]; infiles[i] = fopen(infile_names[i], "rb"); if (infiles[i] == NULL) { diff --git a/tools/mfd_aes_brute/brute_key.c b/tools/mfd_aes_brute/brute_key.c index aa6c532fd..41c91f3c7 100644 --- a/tools/mfd_aes_brute/brute_key.c +++ b/tools/mfd_aes_brute/brute_key.c @@ -22,6 +22,8 @@ // makes it ~14% slower //#define SPINNER +#define __STDC_FORMAT_MACROS +#include #include #include #include