diff --git a/armsrc/Standalone/lf_hidbrute.c b/armsrc/Standalone/lf_hidbrute.c index f0d8cae86..c8d48bb0c 100644 --- a/armsrc/Standalone/lf_hidbrute.c +++ b/armsrc/Standalone/lf_hidbrute.c @@ -95,7 +95,7 @@ void RunMod(void) { WAIT_BUTTON_RELEASED(); - CopyHIDtoT55x7(0, high[selected], low[selected], 0); + CopyHIDtoT55x7(0, high[selected], low[selected], 0, false, false); Dbprintf("[=] cloned %x %x %08x", selected, high[selected], low[selected]); LEDsoff(); diff --git a/armsrc/Standalone/lf_samyrun.c b/armsrc/Standalone/lf_samyrun.c index 9646dede3..497e0c03b 100644 --- a/armsrc/Standalone/lf_samyrun.c +++ b/armsrc/Standalone/lf_samyrun.c @@ -121,7 +121,7 @@ void RunMod(void) { Dbprintf("[=] cloning %x | %x%08x", selected, high[selected], low[selected]); // high2, high, low, no longFMT - CopyHIDtoT55x7(0, high[selected], low[selected], 0); + CopyHIDtoT55x7(0, high[selected], low[selected], 0, false, false); DbpString("[=] cloned done"); diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 0ed5271fc..243f01b81 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -874,7 +874,7 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_LF_HID_CLONE: { lf_hidsim_t *payload = (lf_hidsim_t *)packet->data.asBytes; - CopyHIDtoT55x7(payload->hi2, payload->hi, payload->lo, payload->longFMT); + CopyHIDtoT55x7(payload->hi2, payload->hi, payload->lo, payload->longFMT, payload->Q5, payload->EM); break; } case CMD_LF_IO_WATCH: { diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 2bd5dc279..32c9568d1 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -2190,10 +2190,8 @@ void T55xxWakeUp(uint32_t pwd, uint8_t flags) { reply_ng(CMD_LF_T55XX_WAKEUP, PM3_SUCCESS, NULL, 0); } - /*-------------- Cloning routines -----------*/ static void WriteT55xx(uint32_t *blockdata, uint8_t startblock, uint8_t numblocks) { - t55xx_write_block_t cmd; cmd.pwd = 0; cmd.flags = 0; @@ -2203,11 +2201,18 @@ static void WriteT55xx(uint32_t *blockdata, uint8_t startblock, uint8_t numblock cmd.blockno = i - 1; T55xxWriteBlock((uint8_t *)&cmd); } - } +/* disabled until verified. +static void WriteEM4x05(uint32_t *blockdata, uint8_t startblock, uint8_t numblocks) { + for (uint8_t i = numblocks + startblock; i > startblock; i--) { + EM4xWriteWord(i - 1, blockdata[i - 1], 0, false); + } +} +*/ + // Copy HID id to card and setup block 0 config -void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) { +void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool q5, bool em) { uint32_t data[] = {0, 0, 0, 0, 0, 0, 0}; uint8_t last_block = 0; @@ -2244,11 +2249,21 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) { data[0] = T55x7_BITRATE_RF_50 | T55x7_MODULATION_FSK2a | last_block << T55x7_MAXBLOCK_SHIFT; //TODO add selection of chip for Q5 or T55x7 - // data[0] = T5555_SET_BITRATE(50) | T5555_MODULATION_FSK2 | T5555_INVERT_OUTPUT | last_block << T5555_MAXBLOCK_SHIFT; + if (q5) { + data[0] = T5555_SET_BITRATE(50) | T5555_MODULATION_FSK2 | T5555_INVERT_OUTPUT | last_block << T5555_MAXBLOCK_SHIFT; + } else if (em) { + data[0] = (EM4x05_SET_BITRATE(50) | EM4x05_MODULATION_FSK2 | EM4x05_INVERT | EM4x05_SET_NUM_BLOCKS(last_block)); + } LED_D_ON(); - WriteT55xx(data, 0, last_block + 1); + if (em) { + Dbprintf("Clone HID Prox to EM4x05 is untested and disabled until verified"); + //WriteEM4x05(data, 0, last_block + 1); + } else { + WriteT55xx(data, 0, last_block + 1); + } LED_D_OFF(); + reply_ng(CMD_LF_HID_CLONE, PM3_SUCCESS, NULL, 0); } // clone viking tag to T55xx @@ -2265,7 +2280,12 @@ void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em) { data[2] = bytes_to_num(blocks + 4, 4); // Program the data blocks for supplied ID and the block 0 config - WriteT55xx(data, 0, 3); + if (em) { + Dbprintf("Clone Viking to EM4x05 is untested and disabled until verified"); + //WriteEM4x05(data, 0, 3); + } else { + WriteT55xx(data, 0, 3); + } LED_D_OFF(); reply_ng(CMD_LF_VIKING_CLONE, PM3_SUCCESS, NULL, 0); } diff --git a/armsrc/lfops.h b/armsrc/lfops.h index 703d48b21..82db382d8 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -39,8 +39,7 @@ int lf_awid_watch(int findone, uint32_t *high, uint32_t *low); // Realtime demod int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low); int lf_io_watch(int findone, uint32_t *high, uint32_t *low); -void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT); // Clone an HID card to T5557/T5567 - +void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool q5, bool em); // Clone an HID card to T5557/T5567 void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em); int copy_em410x_to_t55xx(uint8_t card, uint8_t clock, uint32_t id_hi, uint32_t id_lo); diff --git a/client/luascripts/mfc_hammerlite.lua b/client/luascripts/mfc_hammerlite.lua new file mode 100644 index 000000000..c2f34f04c --- /dev/null +++ b/client/luascripts/mfc_hammerlite.lua @@ -0,0 +1,82 @@ +local getopt = require('getopt') +local lib14a = require('read14a') +local cmds = require('commands') +local ansicolors = require('ansicolors') + +copyright = 'Copyright 2020 A. Ozkal, released under GPLv2+.' +author = 'Ave' +version = 'v0.1.1' +desc = [[ +This script writes a bunch of random blocks to a Mifare Classic card + ]] +example = [[ + script run mfc_hammerlite -w 1000 -k FFFFFFFFFFFF +]] +usage = [[ + script run mfc_hammerlite [-h] [-w ] [-k ] +]] +arguments = [[ + -h : This help + -w : Amount of write rounds to be done to each block (optional, default: 100) + -k : A key for the sectors +]] + +local function help() + 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 + +function randhex(len) + result = "" + for i = 1,len,1 + do + -- 48-57 numbers, 65-70 a-f + hex = math.random(0, 15) + if hex >= 10 then + hex = hex + 7 + end + result = result..string.char(48 + hex) + end + return result +end + +--- +-- The main entry point +function main(args) + -- param defaults + loopcount = 100 + verifyevery = 10 + key = "FFFFFFFFFFFF" + + -- Read the parameters + for o, a in getopt.getopt(args, 'hw:k:') do + if o == 'h' then return help() end + if o == 'w' then loopcount = tonumber(a) end + if o == 'k' then key = a end + end + + starttime = os.time() + + for i = 1,loopcount,1 + do + for a = 1,63,1 + do + if ((a + 1) % 4 ~= 0) and a ~= 0 then -- :) + data = randhex(32) + -- core.console('hf mf rdbl '..a..' A FFFFFFFFFFFF') + core.console('hf mf wrbl '..a..' A '..key..' '..data) + end + end + end + + print("Hammering complete.") +end + +main(args) diff --git a/client/luascripts/ntag_hammertime.lua b/client/luascripts/ntag_hammertime.lua new file mode 100644 index 000000000..fc9168ae5 --- /dev/null +++ b/client/luascripts/ntag_hammertime.lua @@ -0,0 +1,189 @@ +local getopt = require('getopt') +local lib14a = require('read14a') +local cmds = require('commands') +local ansicolors = require('ansicolors') + +copyright = 'Copyright 2020 A. Ozkal, released under GPLv2+.' +author = 'Ave' +version = 'v2.1.3' +desc = [[ +This script writes a bunch of random blocks to a NTAG or MFUL card to test its actual write limits + ]] +example = [[ + script run ntag_hammertime -w 1000 -r 50 -z 50 -f 5 -s 4 -e 129 +]] +usage = [[ + script run ntag_hammertime [-h] [-w ] [-r ] [-z ] [-f ] [-s ] [-e ] +]] +arguments = [[ + -h : This help + -w : Amount of write rounds to be done to each block (optional, default: 100) + -r : Verify frequency (reads and checks written values every x rounds, optional, default: 10) + -z : Reselect frequency (reselects card once every x rounds, optional, default: 10) + -f : Maximum consequent fails (read/write) that will trigger a fail state (optional, default: 3) + -s : Block number for writes to be started to (optional, inclusive, decimal, default: 4) + -e : Block number for writes to be ended on (optional, inclusive, decimal, default: 129) +]] + +local function help() + 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 + +function randhex(len) + result = "" + for i = 1,len,1 + do + -- 48-57 numbers, 65-70 a-f + hex = math.random(0, 15) + if hex >= 10 then + hex = hex + 7 + end + result = result..string.char(48 + hex) + end + return result +end + +-- Used to send raw data to the firmware to subsequently forward the data to the card. +-- from mifareplus.lua +local function sendRaw(rawdata, crc, power) + -- print((": %s"):format(rawdata)) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + if crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + if power then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + local command = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, -- Send raw + arg2 = string.len(rawdata) / 2, -- arg2 contains the length, which is half the length of the ASCII-string rawdata + data = rawdata + } + local ignore_response = false + local result, err = command:sendMIX(ignore_response) + if result then + --unpack the first 4 parts of the result as longs, and the last as an extremely long string to later be cut down based on arg1, the number of bytes returned + local count,cmd,arg1,arg2,arg3,data = bin.unpack('LLLLH512',result) + + returned_bytes = string.sub(data, 1, arg1 * 2) + if #returned_bytes > 0 then + -- print((": %s"):format(returned_bytes)) -- need to multiply by 2 because the hex digits are actually two bytes when they are strings + return returned_bytes + else + return nil + end + else + print("Error sending the card raw data.") + return nil + end +end + +local function selectCard(keepField, arg2, attemptCount) + for i = 1,attemptCount,1 + do + if lib14a.read(keepField, arg2) then + return true + end + end + return false +end + +--- +-- The main entry point +function main(args) + failcounter = 0 + + -- param defaults + loopcount = 100 + verifyevery = 10 + reselectevery = 10 + failmax = 3 + blockstart = 4 + blockend = 129 + + -- Read the parameters + for o, a in getopt.getopt(args, 'hw:r:z:f:s:e:') do + if o == 'h' then return help() end + if o == 'w' then loopcount = tonumber(a) end + if o == 'r' then verifyevery = tonumber(a) end + if o == 'z' then reselectevery = tonumber(a) end + if o == 'f' then failmax = tonumber(a) end + if o == 's' then blockstart = tonumber(a) end + if o == 'e' then blockend = tonumber(a) end + end + + starttime = os.time() + + if selectCard(true, false, 3) ~= true then + return print("Select failed.") + end + for i = 1,loopcount,1 + do + for block = blockstart,blockend,1 + do + data = randhex(8) + print(i..": Writing "..data.." to block "..block..".") + blockhex = string.format("%02x", block) + result = sendRaw("A2"..blockhex..data, true, true) + if result then -- if false/nil, that's a fail right there + print(ansicolors.green.."Got "..result.."."..ansicolors.reset) -- We want this to be 0A + failcounter = 0 + else + print(ansicolors.red.."Write FAILED."..ansicolors.reset) + failcounter = failcounter + 1 + goto continue + end + + if i % verifyevery == 0 then + result = sendRaw("30"..blockhex, true, true) + if result then -- if false, that's a fail right there + result = string.sub(result, 0, 8) + if result ~= data then + print(ansicolors.red.."Read IMPROPER, supposed to be "..data..", got "..result.."."..ansicolors.reset) + failcounter = failcounter + 1 + goto continue + else + print(ansicolors.green.."Read matches the write."..ansicolors.reset) + failcounter = 0 + end + else + print(ansicolors.red.."Read FAILED."..ansicolors.reset) + failcounter = failcounter + 1 + goto continue + end + end + ::continue:: + + if failcounter >= failmax then + -- close field + lib14a.read(false, false) + return print(ansicolors.red.."Test failed after "..(os.time() - starttime).." seconds, "..(i*(blockend-blockstart)).." writes and "..math.floor((i*(blockend-blockstart))/verifyevery).." reads."..ansicolors.reset) + end + end + + if i % reselectevery == 0 then + -- reselect + sendRaw("", false, false) + if selectCard(true, false, 3) ~= true then + return print("Reselect failed.") + end + print("Reselected card, current rate: "..(i*(blockend-blockstart))/(os.time() - starttime).." writes/s.") + end + end + + -- close field + lib14a.read(false, false) + print("Successfully completed test in "..(os.time() - starttime).." seconds, did "..(loopcount*(blockend-blockstart)).." writes and "..math.floor((loopcount*(blockend-blockstart))/verifyevery).." reads.") +end + +main(args) diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c index 9b73dda19..dc112913b 100644 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -167,7 +167,7 @@ static int CmdHIDDemod(const char *Cmd) { } // this read is the "normal" read, which download lf signal and tries to demod here. -static int CmdHIDRead(const char *Cmd) { +static int CmdHIDReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hid reader", "read a HID Prox tag", @@ -318,6 +318,8 @@ static int CmdHIDClone(const char *Cmd) { "lf hid clone -r 01f0760643c3 -> HID P10001 40 bit\n" "lf hid clone -r 01400076000c86 -> HID Corporate 48 bit\n" "lf hid clone -w H10301 --fc 118 --cn 1603 -> HID 10301 26 bit\n" + "lf hid clone -w H10301 --fc 118 --cn 1603 --q5 -> HID 10301 26 bit, encode for Q5/T5555 tag\n" + "lf hid clone -w H10301 --fc 118 --cn 1603 --em -> HID 10301 26 bit, encode for EM4305/4469" ); @@ -329,7 +331,8 @@ static int CmdHIDClone(const char *Cmd) { arg_int0("i", NULL, "", "issue level"), arg_int0("o", "oem", "", "OEM code"), arg_strx0("r", "raw", "", "raw bytes"), -// arg_lit0("q", "Q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), + arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -349,9 +352,15 @@ static int CmdHIDClone(const char *Cmd) { char raw[40] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)raw, sizeof(raw), &raw_len); - //bool q5 = arg_get_lit(ctx, 7); + bool q5 = arg_get_lit(ctx, 7); + bool em = arg_get_lit(ctx, 8); CLIParserFree(ctx); + if (q5 && em) { + PrintAndLogEx(FAILED, "Can't specify both Q5 and EM4305 at the same time"); + return PM3_EINVARG; + } + wiegand_message_t packed; memset(&packed, 0, sizeof(wiegand_message_t)); @@ -375,6 +384,17 @@ static int CmdHIDClone(const char *Cmd) { } } + char cardtype[16] = {"T55x7"}; + // Q5 + if (q5) { + snprintf(cardtype, sizeof(cardtype), "Q5/T5555"); + } + + // EM4305 + if (em) { + snprintf(cardtype, sizeof(cardtype), "EM4305/4469"); + } + if (raw_len == 0) { PrintAndLogEx(INFO, "Preparing to clone HID tag"); HIDTryUnpack(&packed, false); @@ -387,11 +407,22 @@ static int CmdHIDClone(const char *Cmd) { payload.hi = packed.Mid; payload.lo = packed.Bot; payload.longFMT = (packed.Mid > 0xFFF); + payload.Q5 = q5; + payload.EM = em; clearCommandBuffer(); SendCommandNG(CMD_LF_HID_CLONE, (uint8_t *)&payload, sizeof(payload)); - PrintAndLogEx(SUCCESS, "Done"); - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf hid read`") " to verify"); + + PacketResponseNG resp; + WaitForResponse(CMD_LF_HID_CLONE, &resp); + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Done"); + } else { + PrintAndLogEx(FAILED, "Failed cloning"); + return resp.status; + } + + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`lf hid reader`") " to verify"); return PM3_SUCCESS; } @@ -561,7 +592,7 @@ static int CmdHIDBrute(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "this help"}, {"demod", CmdHIDDemod, AlwaysAvailable, "demodulate HID Prox tag from the GraphBuffer"}, - {"read", CmdHIDRead, IfPm3Lf, "attempt to read and extract tag data"}, + {"reader", CmdHIDReader, IfPm3Lf, "attempt to read and extract tag data"}, {"clone", CmdHIDClone, IfPm3Lf, "clone HID tag to T55x7"}, {"sim", CmdHIDSim, IfPm3Lf, "simulate HID tag"}, {"brute", CmdHIDBrute, IfPm3Lf, "bruteforce card number against reader"}, diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index efdcafe33..9d6bf90ad 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -238,6 +238,8 @@ typedef struct { uint32_t hi; uint32_t lo; uint8_t longFMT; + bool Q5; + bool EM; } PACKED lf_hidsim_t; // For CMD_LF_FSK_SIMULATE (FSK)