diff --git a/client/lualibs/mf_default_keys.lua b/client/lualibs/mf_default_keys.lua index 6f7eb46e..bcbb2e32 100644 --- a/client/lualibs/mf_default_keys.lua +++ b/client/lualibs/mf_default_keys.lua @@ -34,7 +34,7 @@ local _keys = { 'a0478cc39091', -- Key from mfoc '533cb6c723f6', -- Key from mfoc '8fd0a4f256e9', -- Key from mfoc - + --[[ Data from: http://pastebin.com/wcTHXLZZ --]] @@ -44,6 +44,7 @@ local _keys = { '00000ffe2488', -- RKF SL Key B '5c598c9c58b5', -- RKF SL Key B 'e4d2770a89be', -- RKF SL Key B + --[[ Data from: http://pastebin.com/svGjN30Q @@ -54,7 +55,7 @@ local _keys = { '47524f555042', -- RKF JOJO GROUP Key B '505249564141', -- RKF JOJO PRIVA Key A '505249564142', -- RKF JOJO PRIVA Key B - + --[[ Data from: http://pastebin.com/d7sSetef --]] diff --git a/client/scripts/mfkeys.lua b/client/scripts/mfkeys.lua index 937c9172..a8d40a50 100644 --- a/client/scripts/mfkeys.lua +++ b/client/scripts/mfkeys.lua @@ -11,13 +11,11 @@ -- Loads the commands-library local cmds = require('commands') -- Load the default keys -local keys = require('mf_default_keys') +local keylist = require('mf_default_keys') -- Ability to read what card is there -local reader = require('read14a') +local lib14a = require('read14a') local getopt = require('getopt') - -local OR = bit32.bor -local LSHIFT = bit32.lshift +local utils = require('utils') example =[[ script run mfkeys @@ -25,7 +23,7 @@ example =[[ author = "Iceman" usage = "script run mfkeys" desc = ("This script implements Mifare check keys. It utilises a large list of default keys (currently %d keys).\ -If you want to add more, just put them inside /lualibs/mf_default_keys.lua\n"):format(#keys) .. +If you want to add more, just put them inside /lualibs/mf_default_keys.lua\n"):format(#keylist) .. [[ Arguments: @@ -41,104 +39,81 @@ function help() print("Example usage") print(example) end - ---[[This may be moved to a separate library at some point]] -local utils = -{ - --- - -- Asks the user for Yes or No - confirm = function(message, ...) - local answer - message = message .. " [y]/[n] ?" - repeat - io.write(message) - io.flush() - answer=io.read() - if answer == 'Y' or answer == "y" then - return true - elseif answer == 'N' or answer == 'n' then - return false - end - until false - end, - --- - -- Asks the user for input - input = function (message , default) - local answer - if default ~= nil then - message = message .. " (default: ".. default.. " )" - end - message = message .." \n > " - io.write(message) - io.flush() - answer=io.read() - if answer == '' then answer = default end - - return answer - end, -} - - +--- +-- This is only meant to be used when errors occur +function oops(err) + print('ERROR: ',err) + return nil,err +end +-- +-- waits for answer from pm3 device local function checkCommand(command) - - --print("Sending this command : " .. tostring(command)) - local usbcommand = command:getBytes() - core.SendCommand(usbcommand) - local result = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT) + core.clearCommandBuffer() + local usb = command:getBytes() + core.SendCommand(usb) + local result = core.WaitForResponseTimeout(cmds.CMD_ACK, TIMEOUT) if result then - local count,cmd,arg0 = bin.unpack('LL',result) + local count, cmd, arg0 = bin.unpack('LL',result) if(arg0==1) then - local count,arg1,arg2,data = bin.unpack('LLH511',result,count) + local count, arg1, arg2, data = bin.unpack('LLH511',result,count) key = data:sub(1,12) return key else - --print("Key not found...") return nil end else - print("Timeout while waiting for response. Increase TIMEOUT in keycheck.lua to wait longer") + print("Timeout while waiting for response. Increase TIMEOUT in mfkeys.lua to wait longer") return nil, "Timeout while waiting for device to respond" end end -function checkBlock(blockNo, keys, keyType) +local function checkBlock(blockno, testkeys, keytype) + -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go. -- If there's more, we need to split it up - local start, remaining= 1, #keys - local packets = {} + local arg1 = bit32.bor(bit32.lshift(keytype, 8), blockno) + + local start, remaining = 1, #testkeys + local chunksize = remaining + if remaining > 85 then chunksize = 85 end + local n = chunksize + while remaining > 0 do - local n,data = remaining, nil - if remaining > 85 then n = 85 end - local data = table.concat(keys,"",start,n) - print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n)) + --print('start', start, 'chunksize', chunksize, 'testkeys kvar', remaining, 'N-index=', n) + + local d1 = table.concat(testkeys, "", start, n) + + print(("Testing block %d, keytype %d, with %d keys"):format(blockno, keytype, chunksize)) local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS, - arg1 = OR(blockNo, LSHIFT(keyType,8) ), + arg1 = arg1, arg2 = 0, - arg3 = n, - data = data} + arg3 = chunksize, + data = d1} local status = checkCommand(command) - if status then return status, blockNo end - start = start+n+1 - remaining = remaining - n + if status then return status, blockno end + start = start + chunksize + remaining = remaining - chunksize + + if remaining < 85 then chunksize = remaining end + n = n + chunksize end return nil end -- A function to display the results -- TODO: iceman 2016, still screws up output when a key is not found. -local function displayresults(results) - local sector, blockNo, keyA, keyB,_ +local function display_results(keys) + local sector, keyA, keyB, succA, succB + print('') + print('|---|----------------|---|----------------|---|') + print('|sec|key A |res|key B |res|') + print('|---|----------------|---|----------------|---|') - print("|---|----------------|---|----------------|---|") - print("|sec|key A |res|key B |res|") - print("|---|----------------|---|----------------|---|") - - for sector,_ in pairs(results) do - blockNo, keyA, keyB = unpack(_) - print(("|%03d| %s | 1 | %s | 1 |"):format(sector, keyA, keyB )) + for sector = 0, #keys do + succA, succB, keyA, keyB = unpack(keys[sector]) + print(('|%03d| %s | %s | %s | %s |'):format(sector, keyA, succA, keyB, succB)) end - print("|---|----------------|---|----------------|---|") - + print('|---|----------------|---|----------------|---|') end -- A little helper to place an item first in the list local function placeFirst(akey, list) @@ -156,24 +131,47 @@ local function placeFirst(akey, list) end return result end -local function dumptofile(results) - local sector, blockNo, keyA, keyB,_ +--[[ +The mifare Classic 1k card has 16 sectors of 4 data blocks each. +The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining +8 sectors consist of 16 data blocks. +--]] +local function get_blockno(s) - if utils.confirm("Do you wish to save the keys to dumpfile?") then - local destination = utils.input("Select a filename to store to", "dumpkeys.bin") - local file = io.open(destination, "w") + local b, sector + + if type(s) == 'string' then + sector = tonumber(s) + else + sector = s + end + + if sector < 32 then + b = sector * 4 + else + b = 32 * 4 + (sector - 32) * 16 +end + return ('%02x'):format(b) +end +-- +-- dumps all keys to file +local function dumptofile(keys) + if utils.confirm('Do you wish to save the keys to dumpfile?') then + local destination = utils.input('Select a filename to store to', 'dumpkeys.bin') + local file = io.open(destination, 'wb') if file == nil then - print("Could not write to file ", destination) + print('Could not write to file ', destination) return end - local key_a = "" - local key_b = "" + local key_a = '' + local key_b = '' - for sector,_ in pairs(results) do - blockNo, keyA, keyB = unpack(_) - key_a = key_a .. bin.pack("H",keyA); - key_b = key_b .. bin.pack("H",keyB); + --for sector,_ in pairs(keys) do + for sector = 0, #keys do + local succA, succB, keyA, keyB = unpack(keys[sector]) + key_a = key_a .. bin.pack('H', keyA); + key_b = key_b .. bin.pack('H', keyB); end file:write(key_a) file:write(key_b) @@ -181,82 +179,97 @@ local function dumptofile(results) end end local function printkeys() - for i=0,#keys do - print(i,keys[i]) - + for i=1, #keylist do + print(i, keylist[i]) end - print ('Number of keys: '..#keys) + print ('Number of keys: '..#keylist) end +local function perform_check(numsectors) + local keyType = 0 -- A=0, B=1 + + -- empty list of found keys + local keys = {} + for i = 0, numsectors-1 do + keys[i] = {0,0,'',''} + end + + for sector = 0, #keys do + -- Check if user aborted + if core.ukbhit() then + print('Aborted by user') + break + end + + local targetblock = tonumber(get_blockno(sector), 16) + + local succA, succB, keyA, keyB = unpack(keys[sector]) + + local keyA = checkBlock(targetblock, keylist, 0) + if keyA then succA = 1; keylist = placeFirst(keyA, keylist) end + keyA = keyA or '------------' + + local keyB = checkBlock(targetblock, keylist, 1) + if keyB then succB = 1; keylist = placeFirst(keyB, keylist) end + keyB = keyB or '------------' + + keys[sector] = {succA, succB, keyA, keyB } + end + + display_results(keys) + + -- save to dumpkeys.bin + dumptofile(keys) +end +-- +-- shows tag information +local function taginfo(tag) + + local sectors = 16 + -- Show tag info + print((' Found tag %s'):format(tag.name)) + + if 0x18 == tag.sak then --NXP MIFARE Classic 4k | Plus 4k + -- MIFARE Classic 4K offers 4096 bytes split into forty sectors, + -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. + sectors = 40 + elseif 0x08 == tag.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k + -- 1K offers 1024 bytes of data storage, split into 16 sector + sectors = 16 + elseif 0x09 == tag.sak then -- NXP MIFARE Mini 0.3k + -- MIFARE Classic mini offers 320 bytes split into five sectors. + sectors = 5 + elseif 0x10 == tag.sak then-- "NXP MIFARE Plus 2k" + sectors = 32 + else + print("I don't know how many sectors there are on this type of card, defaulting to 16") + end + return sectors +end +--- +-- The main entry point local function main( args) + local start_time = os.time() + local numSectors = 16 + -- Arguments for the script for o, a in getopt.getopt(args, 'hp') do if o == "h" then return help() end if o == "p" then return printkeys() end end - result, err = reader.read1443a() - if not result then - print(err) - return - end + -- identify tag + tag, err = lib14a.read1443a(false) + if not tag then return oops(err) end - print(("Found a %s tag"):format(result.name)) - - core.clearCommandBuffer() - local blockNo - local keyType = 0 -- A=0, B=1 - local numSectors = 16 - - if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k - -- IFARE Classic 4K offers 4096 bytes split into forty sectors, - -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors. - numSectors = 40 - elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k - -- 1K offers 1024 bytes of data storage, split into 16 sector - numSectors = 16 - elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k - -- MIFARE Classic mini offers 320 bytes split into five sectors. - numSectors = 5 - elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k" - numSectors = 32 - else - print("I don't know how many sectors there are on this type of card, defaulting to 16") - end - - result = {} - for sector=1,numSectors,1 do - - --[[ - The mifare Classic 1k card has 16 sectors of 4 data blocks each. - The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining - 8 sectors consist of 16 data blocks. - --]] - local blockNo = sector * 4 -1 - - if sector > 32 then - blockNo = 32*4+ (sector-32)*16 -1 - end - - local keyA = checkBlock(blockNo, keys, 0) - if keyA then keys = placeFirst(keyA, keys) end - keyA = keyA or "" - - local keyB = checkBlock(blockNo, keys, 1) - if keyB then keys = placeFirst(keyB, keys) end - keyB = keyB or "" - - result[sector] = {blockNo, keyA, keyB } - - -- Check if user aborted - if core.ukbhit() then - print("Aborted by user") - break - end - end - displayresults(result) - dumptofile(result) + -- detect sectors and print taginfo + numsectors = taginfo(tag) + + perform_check(numsectors) + + local end_time = os.time() + print('mfkeys - Total execution time: '..os.difftime(end_time, start_time)..' sec') end main( args)