mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-03-18 03:00:58 +08:00
FIX: 'script run mfkeys' Script was mistakingly only checked first testkeys block with 85 keys. Since the selection in testkeys list was not taking lua way of ranges, it only sent zeros.
This commit is contained in:
parent
72f6ed45eb
commit
c4c3bedb8a
2 changed files with 169 additions and 155 deletions
|
@ -34,7 +34,7 @@ local _keys = {
|
||||||
'a0478cc39091', -- Key from mfoc
|
'a0478cc39091', -- Key from mfoc
|
||||||
'533cb6c723f6', -- Key from mfoc
|
'533cb6c723f6', -- Key from mfoc
|
||||||
'8fd0a4f256e9', -- Key from mfoc
|
'8fd0a4f256e9', -- Key from mfoc
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Data from: http://pastebin.com/wcTHXLZZ
|
Data from: http://pastebin.com/wcTHXLZZ
|
||||||
--]]
|
--]]
|
||||||
|
@ -44,6 +44,7 @@ local _keys = {
|
||||||
'00000ffe2488', -- RKF SL Key B
|
'00000ffe2488', -- RKF SL Key B
|
||||||
'5c598c9c58b5', -- RKF SL Key B
|
'5c598c9c58b5', -- RKF SL Key B
|
||||||
'e4d2770a89be', -- RKF SL Key B
|
'e4d2770a89be', -- RKF SL Key B
|
||||||
|
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Data from: http://pastebin.com/svGjN30Q
|
Data from: http://pastebin.com/svGjN30Q
|
||||||
|
@ -54,7 +55,7 @@ local _keys = {
|
||||||
'47524f555042', -- RKF JOJO GROUP Key B
|
'47524f555042', -- RKF JOJO GROUP Key B
|
||||||
'505249564141', -- RKF JOJO PRIVA Key A
|
'505249564141', -- RKF JOJO PRIVA Key A
|
||||||
'505249564142', -- RKF JOJO PRIVA Key B
|
'505249564142', -- RKF JOJO PRIVA Key B
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Data from: http://pastebin.com/d7sSetef
|
Data from: http://pastebin.com/d7sSetef
|
||||||
--]]
|
--]]
|
||||||
|
|
|
@ -11,13 +11,11 @@
|
||||||
-- Loads the commands-library
|
-- Loads the commands-library
|
||||||
local cmds = require('commands')
|
local cmds = require('commands')
|
||||||
-- Load the default keys
|
-- Load the default keys
|
||||||
local keys = require('mf_default_keys')
|
local keylist = require('mf_default_keys')
|
||||||
-- Ability to read what card is there
|
-- Ability to read what card is there
|
||||||
local reader = require('read14a')
|
local lib14a = require('read14a')
|
||||||
local getopt = require('getopt')
|
local getopt = require('getopt')
|
||||||
|
local utils = require('utils')
|
||||||
local OR = bit32.bor
|
|
||||||
local LSHIFT = bit32.lshift
|
|
||||||
|
|
||||||
example =[[
|
example =[[
|
||||||
script run mfkeys
|
script run mfkeys
|
||||||
|
@ -25,7 +23,7 @@ example =[[
|
||||||
author = "Iceman"
|
author = "Iceman"
|
||||||
usage = "script run mfkeys"
|
usage = "script run mfkeys"
|
||||||
desc = ("This script implements Mifare check keys. It utilises a large list of default keys (currently %d keys).\
|
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:
|
Arguments:
|
||||||
|
@ -41,104 +39,81 @@ function help()
|
||||||
print("Example usage")
|
print("Example usage")
|
||||||
print(example)
|
print(example)
|
||||||
end
|
end
|
||||||
|
---
|
||||||
--[[This may be moved to a separate library at some point]]
|
-- This is only meant to be used when errors occur
|
||||||
local utils =
|
function oops(err)
|
||||||
{
|
print('ERROR: ',err)
|
||||||
---
|
return nil,err
|
||||||
-- Asks the user for Yes or No
|
end
|
||||||
confirm = function(message, ...)
|
--
|
||||||
local answer
|
-- waits for answer from pm3 device
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local function checkCommand(command)
|
local function checkCommand(command)
|
||||||
|
core.clearCommandBuffer()
|
||||||
--print("Sending this command : " .. tostring(command))
|
local usb = command:getBytes()
|
||||||
local usbcommand = command:getBytes()
|
core.SendCommand(usb)
|
||||||
core.SendCommand(usbcommand)
|
local result = core.WaitForResponseTimeout(cmds.CMD_ACK, TIMEOUT)
|
||||||
local result = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT)
|
|
||||||
if result then
|
if result then
|
||||||
local count,cmd,arg0 = bin.unpack('LL',result)
|
local count, cmd, arg0 = bin.unpack('LL',result)
|
||||||
if(arg0==1) then
|
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)
|
key = data:sub(1,12)
|
||||||
return key
|
return key
|
||||||
else
|
else
|
||||||
--print("Key not found...")
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
else
|
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"
|
return nil, "Timeout while waiting for device to respond"
|
||||||
end
|
end
|
||||||
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.
|
-- 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
|
-- If there's more, we need to split it up
|
||||||
local start, remaining= 1, #keys
|
local arg1 = bit32.bor(bit32.lshift(keytype, 8), blockno)
|
||||||
local packets = {}
|
|
||||||
|
local start, remaining = 1, #testkeys
|
||||||
|
local chunksize = remaining
|
||||||
|
if remaining > 85 then chunksize = 85 end
|
||||||
|
local n = chunksize
|
||||||
|
|
||||||
while remaining > 0 do
|
while remaining > 0 do
|
||||||
local n,data = remaining, nil
|
--print('start', start, 'chunksize', chunksize, 'testkeys kvar', remaining, 'N-index=', n)
|
||||||
if remaining > 85 then n = 85 end
|
|
||||||
local data = table.concat(keys,"",start,n)
|
local d1 = table.concat(testkeys, "", start, n)
|
||||||
print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n))
|
|
||||||
|
print(("Testing block %d, keytype %d, with %d keys"):format(blockno, keytype, chunksize))
|
||||||
local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS,
|
local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS,
|
||||||
arg1 = OR(blockNo, LSHIFT(keyType,8) ),
|
arg1 = arg1,
|
||||||
arg2 = 0,
|
arg2 = 0,
|
||||||
arg3 = n,
|
arg3 = chunksize,
|
||||||
data = data}
|
data = d1}
|
||||||
local status = checkCommand(command)
|
local status = checkCommand(command)
|
||||||
if status then return status, blockNo end
|
if status then return status, blockno end
|
||||||
start = start+n+1
|
start = start + chunksize
|
||||||
remaining = remaining - n
|
remaining = remaining - chunksize
|
||||||
|
|
||||||
|
if remaining < 85 then chunksize = remaining end
|
||||||
|
n = n + chunksize
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- A function to display the results
|
-- A function to display the results
|
||||||
-- TODO: iceman 2016, still screws up output when a key is not found.
|
-- TODO: iceman 2016, still screws up output when a key is not found.
|
||||||
local function displayresults(results)
|
local function display_results(keys)
|
||||||
local sector, blockNo, keyA, keyB,_
|
local sector, keyA, keyB, succA, succB
|
||||||
|
print('')
|
||||||
|
print('|---|----------------|---|----------------|---|')
|
||||||
|
print('|sec|key A |res|key B |res|')
|
||||||
|
print('|---|----------------|---|----------------|---|')
|
||||||
|
|
||||||
print("|---|----------------|---|----------------|---|")
|
for sector = 0, #keys do
|
||||||
print("|sec|key A |res|key B |res|")
|
succA, succB, keyA, keyB = unpack(keys[sector])
|
||||||
print("|---|----------------|---|----------------|---|")
|
print(('|%03d| %s | %s | %s | %s |'):format(sector, keyA, succA, keyB, succB))
|
||||||
|
|
||||||
for sector,_ in pairs(results) do
|
|
||||||
blockNo, keyA, keyB = unpack(_)
|
|
||||||
print(("|%03d| %s | 1 | %s | 1 |"):format(sector, keyA, keyB ))
|
|
||||||
end
|
end
|
||||||
print("|---|----------------|---|----------------|---|")
|
print('|---|----------------|---|----------------|---|')
|
||||||
|
|
||||||
end
|
end
|
||||||
-- A little helper to place an item first in the list
|
-- A little helper to place an item first in the list
|
||||||
local function placeFirst(akey, list)
|
local function placeFirst(akey, list)
|
||||||
|
@ -156,24 +131,47 @@ local function placeFirst(akey, list)
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
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 b, sector
|
||||||
local destination = utils.input("Select a filename to store to", "dumpkeys.bin")
|
|
||||||
local file = io.open(destination, "w")
|
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
|
if file == nil then
|
||||||
print("Could not write to file ", destination)
|
print('Could not write to file ', destination)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local key_a = ""
|
local key_a = ''
|
||||||
local key_b = ""
|
local key_b = ''
|
||||||
|
|
||||||
for sector,_ in pairs(results) do
|
--for sector,_ in pairs(keys) do
|
||||||
blockNo, keyA, keyB = unpack(_)
|
for sector = 0, #keys do
|
||||||
key_a = key_a .. bin.pack("H",keyA);
|
local succA, succB, keyA, keyB = unpack(keys[sector])
|
||||||
key_b = key_b .. bin.pack("H",keyB);
|
key_a = key_a .. bin.pack('H', keyA);
|
||||||
|
key_b = key_b .. bin.pack('H', keyB);
|
||||||
end
|
end
|
||||||
file:write(key_a)
|
file:write(key_a)
|
||||||
file:write(key_b)
|
file:write(key_b)
|
||||||
|
@ -181,82 +179,97 @@ local function dumptofile(results)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local function printkeys()
|
local function printkeys()
|
||||||
for i=0,#keys do
|
for i=1, #keylist do
|
||||||
print(i,keys[i])
|
print(i, keylist[i])
|
||||||
|
|
||||||
end
|
end
|
||||||
print ('Number of keys: '..#keys)
|
print ('Number of keys: '..#keylist)
|
||||||
end
|
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 function main( args)
|
||||||
|
|
||||||
|
local start_time = os.time()
|
||||||
|
local numSectors = 16
|
||||||
|
|
||||||
-- Arguments for the script
|
-- Arguments for the script
|
||||||
for o, a in getopt.getopt(args, 'hp') do
|
for o, a in getopt.getopt(args, 'hp') do
|
||||||
if o == "h" then return help() end
|
if o == "h" then return help() end
|
||||||
if o == "p" then return printkeys() end
|
if o == "p" then return printkeys() end
|
||||||
end
|
end
|
||||||
|
|
||||||
result, err = reader.read1443a()
|
-- identify tag
|
||||||
if not result then
|
tag, err = lib14a.read1443a(false)
|
||||||
print(err)
|
if not tag then return oops(err) end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
print(("Found a %s tag"):format(result.name))
|
-- detect sectors and print taginfo
|
||||||
|
numsectors = taginfo(tag)
|
||||||
core.clearCommandBuffer()
|
|
||||||
local blockNo
|
perform_check(numsectors)
|
||||||
local keyType = 0 -- A=0, B=1
|
|
||||||
local numSectors = 16
|
local end_time = os.time()
|
||||||
|
print('mfkeys - Total execution time: '..os.difftime(end_time, start_time)..' sec')
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
main( args)
|
main( args)
|
||||||
|
|
Loading…
Add table
Reference in a new issue