From 35d5896012840ff2ded77df3b72409d7e414f1f5 Mon Sep 17 00:00:00 2001 From: Ave Date: Mon, 30 Nov 2020 22:27:14 +0300 Subject: [PATCH] scripts: Add mfc_hammerlite and ntag_hammertime --- client/luascripts/mfc_hammerlite.lua | 82 +++++++++++ client/luascripts/ntag_hammertime.lua | 189 ++++++++++++++++++++++++++ 2 files changed, 271 insertions(+) create mode 100644 client/luascripts/mfc_hammerlite.lua create mode 100644 client/luascripts/ntag_hammertime.lua 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)