FIX: a solution for the issue "hf mf esave - always saves 4K"

FIX: a solution for the issue "hf eload, esave, cload, save - filepath variable too short"
CHG: minor code clean up.
ADD: AES / CRC16 for lua. (and tnp3xx scripts.)
ADD: tnp3dump.lua  script to dump tnp3xx tags.
ADD: tnp3sim.lua script to let PM3 imitate an tnp3xx tag. Needs to be tested live
This commit is contained in:
iceman1001 2015-01-07 22:00:29 +01:00
commit b915fda392
19 changed files with 1137 additions and 133 deletions

272
client/scripts/tnp3dump.lua Normal file
View file

@ -0,0 +1,272 @@
local cmds = require('commands')
local getopt = require('getopt')
local bin = require('bin')
local lib14a = require('read14a')
local utils = require('utils')
local md5 = require('md5')
local dumplib = require('html_dumplib')
local toyNames = require('default_toys')
example =[[
1. script run tnp3dump
2. script run tnp3dump -n
3. script run tnp3dump -k aabbccddeeff
4. script run tnp3dump -k aabbccddeeff -n
5. script run tnp3dump -o myfile
6. script run tnp3dump -n -o myfile
7. script run tnp3dump -k aabbccddeeff -n -o myfile
]]
author = "Iceman"
usage = "script run tnp3dump -k <key> -n -o <filename>"
desc =[[
This script will try to dump the contents of a Mifare TNP3xxx card.
It will need a valid KeyA in order to find the other keys and decode the card.
Arguments:
-h : this help
-k <key> : Sector 0 Key A.
-n : Use the nested cmd to find all keys
-o : filename for the saved dumps
]]
local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
local DEBUG = false -- the debug flag
local numBlocks = 64
local numSectors = 16
---
-- A debug printout-function
function dbg(args)
if not DEBUG then
return
end
if type(args) == "table" then
local i = 1
while result[i] do
dbg(result[i])
i = i+1
end
else
print("###", args)
end
end
---
-- This is only meant to be used when errors occur
function oops(err)
print("ERROR: ",err)
end
---
-- Usage help
function help()
print(desc)
print("Example usage")
print(example)
end
--
-- Exit message
function ExitMsg(msg)
print( string.rep('--',20) )
print( string.rep('--',20) )
print(msg)
print()
end
local function readdumpkeys(infile)
t = infile:read("*all")
len = string.len(t)
local len,hex = bin.unpack(("H%d"):format(len),t)
return hex
end
local function waitCmd()
local response = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT)
if response then
local count,cmd,arg0 = bin.unpack('LL',response)
if(arg0==1) then
local count,arg1,arg2,data = bin.unpack('LLH511',response,count)
return data:sub(1,32)
else
return nil, "Couldn't read block.."
end
end
return nil, "No response from device"
end
local function computeCrc16(s)
local hash = core.crc16(utils.ConvertHexToAscii(s))
return hash
end
local function reverseCrcBytes(crc)
crc2 = crc:sub(3,4)..crc:sub(1,2)
return tonumber(crc2,16)
end
local function main(args)
print( string.rep('--',20) )
print( string.rep('--',20) )
local keyA
local cmd
local err
local useNested = false
local cmdReadBlockString = 'hf mf rdbl %d A %s'
local input = "dumpkeys.bin"
local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M%S");
-- Arguments for the script
for o, a in getopt.getopt(args, 'hk:no:') do
if o == "h" then return help() end
if o == "k" then keyA = a end
if o == "n" then useNested = true end
if o == "o" then outputTemplate = a end
end
-- validate input args.
keyA = keyA or '4b0b20107ccb'
if #(keyA) ~= 12 then
return oops( string.format('Wrong length of write key (was %d) expected 12', #keyA))
end
-- Turn off Debug
local cmdSetDbgOff = "hf mf dbg 0"
core.console( cmdSetDbgOff)
result, err = lib14a.read1443a(false)
if not result then
return oops(err)
end
core.clearCommandBuffer()
if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx
return oops('This is not a TNP3xxx tag. aborting.')
end
-- Show tag info
print((' Found tag : %s'):format(result.name))
print(('Using keyA : %s'):format(keyA))
--Trying to find the other keys
if useNested then
core.console( ('hf mf nested 1 0 A %s d'):format(keyA) )
end
core.clearCommandBuffer()
-- Loading keyfile
print('Loading dumpkeys.bin')
local hex, err = utils.ReadDumpFile(input)
if not hex then
return oops(err)
end
local akeys = hex:sub(0,12*16)
-- Read block 0
cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 0,arg2 = 0,arg3 = 0, data = keyA}
err = core.SendCommand(cmd:getBytes())
if err then return oops(err) end
local block0, err = waitCmd()
if err then return oops(err) end
-- Read block 1
cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = 1,arg2 = 0,arg3 = 0, data = keyA}
err = core.SendCommand(cmd:getBytes())
if err then return oops(err) end
local block1, err = waitCmd()
if err then return oops(err) end
local key
local pos = 0
local blockNo
local blocks = {}
print('Reading card data')
core.clearCommandBuffer()
-- main loop
io.write('Decrypting blocks > ')
for blockNo = 0, numBlocks-1, 1 do
if core.ukbhit() then
print("aborted by user")
break
end
pos = (math.floor( blockNo / 4 ) * 12)+1
key = akeys:sub(pos, pos + 11 )
cmd = Command:new{cmd = cmds.CMD_MIFARE_READBL, arg1 = blockNo ,arg2 = 0,arg3 = 0, data = key}
local err = core.SendCommand(cmd:getBytes())
if err then return oops(err) end
local blockdata, err = waitCmd()
if err then return oops(err) end
if blockNo%4 ~= 3 then
if blockNo < 8 then
-- Block 0-7 not encrypted
blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata)
else
local base = ('%s%s%02x%s'):format(block0, block1, blockNo, HASHCONSTANT)
local baseStr = utils.ConvertHexToAscii(base)
local md5hash = md5.sumhexa(baseStr)
local aestest = core.aes(md5hash, blockdata)
local hex = utils.ConvertAsciiToBytes(aestest)
hex = utils.ConvertBytesToHex(hex)
-- blocks with zero not encrypted.
if string.find(blockdata, '^0+$') then
blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,blockdata)
else
blocks[blockNo+1] = ('%02d :: %s'):format(blockNo,hex)
io.write( blockNo..',')
end
end
else
-- Sectorblocks, not encrypted
blocks[blockNo+1] = ('%02d :: %s%s'):format(blockNo,key,blockdata:sub(13,32))
end
end
io.write('\n')
core.clearCommandBuffer()
-- Print results
local bindata = {}
local emldata = ''
for _,s in pairs(blocks) do
local slice = s:sub(8,#s)
local str = utils.ConvertBytesToAscii(
utils.ConvertHexToBytes(slice)
)
emldata = emldata..slice..'\n'
for c in (str):gmatch('.') do
bindata[#bindata+1] = c
end
end
-- Write dump to files
if not DEBUG then
local foo = dumplib.SaveAsBinary(bindata, outputTemplate..'.bin')
print(("Wrote a BIN dump to the file %s"):format(foo))
local bar = dumplib.SaveAsText(emldata, outputTemplate..'.eml')
print(("Wrote a EML dump to the file %s"):format(bar))
end
local uid = block0:sub(1,8)
local itemtype = block1:sub(1,4)
local cardid = block1:sub(9,24)
-- Show info
print( string.rep('--',20) )
print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) )
print( (' UID : 0x%s'):format(uid) )
print( (' CARDID : 0x%s'):format(cardid ) )
print( string.rep('--',20) )
end
main(args)

355
client/scripts/tnp3sim.lua Normal file
View file

@ -0,0 +1,355 @@
local cmds = require('commands')
local getopt = require('getopt')
local bin = require('bin')
local lib14a = require('read14a')
local utils = require('utils')
local md5 = require('md5')
local toyNames = require('default_toys')
example =[[
1. script run tnp3sim
2. script run tnp3sim -m
3. script run tnp3sim -m -i myfile
]]
author = "Iceman"
usage = "script run tnp3sim -h -m -i <filename>"
desc =[[
This script will try to load a binary datadump of a Mifare TNP3xxx card.
It vill try to validate all checksums and view some information stored in the dump
For an experimental mode, it tries to manipulate some data.
At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim"
Arguments:
-h : this help
-m : Maxed out items (experimental)
-i : filename for the datadump to read (bin)
]]
local TIMEOUT = 2000 -- Shouldn't take longer than 2 seconds
local DEBUG = true -- the debug flag
---
-- A debug printout-function
function dbg(args)
if not DEBUG then
return
end
if type(args) == "table" then
local i = 1
while result[i] do
dbg(result[i])
i = i+1
end
else
print("###", args)
end
end
---
-- This is only meant to be used when errors occur
function oops(err)
print("ERROR: ",err)
end
---
-- Usage help
function help()
print(desc)
print("Example usage")
print(example)
end
--
-- Exit message
function ExitMsg(msg)
print( string.rep('--',20) )
print( string.rep('--',20) )
print(msg)
print()
end
local function writedumpfile(infile)
t = infile:read("*all")
len = string.len(t)
local len,hex = bin.unpack(("H%d"):format(len),t)
return hex
end
-- blocks with data
-- there are two dataareas, in block 8 or block 36, ( 1==8 ,
-- checksum type = 0, 1, 2, 3
local function GetCheckSum(blocks, dataarea, chksumtype)
local crc
local area = 36
if dataarea == 1 then
area = 8
end
if chksumtype == 0 then
crc = blocks[1]:sub(29,32)
elseif chksumtype == 1 then
crc = blocks[area]:sub(29,32)
elseif chksumtype == 2 then
crc = blocks[area]:sub(25,28)
elseif chksumtype == 3 then
crc = blocks[area]:sub(21,24)
end
return utils.SwapEndianness(crc,16)
end
local function SetCheckSum(blocks, chksumtype)
if blocks == nil then return nil, 'Argument \"blocks\" nil' end
local newcrc
local area1 = 8
local area2 = 36
if chksumtype == 0 then
newcrc = ('%04X'):format(CalcCheckSum(blocks,1,0))
blocks[1] = blocks[1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
elseif chksumtype == 1 then
newcrc = ('%04X'):format(CalcCheckSum(blocks,1,1))
blocks[area1] = blocks[area1]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
newcrc = ('%04X'):format(CalcCheckSum(blocks,2,1))
blocks[area2] = blocks[area2]:sub(1,28)..newcrc:sub(3,4)..newcrc:sub(1,2)
elseif chksumtype == 2 then
newcrc = ('%04X'):format(CalcCheckSum(blocks,1,2))
blocks[area1] = blocks[area1]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(29,32)
newcrc = ('%04X'):format(CalcCheckSum(blocks,2,2))
blocks[area2] = blocks[area2]:sub(1,24)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(29,32)
elseif chksumtype == 3 then
newcrc = ('%04X'):format(CalcCheckSum(blocks,1,3))
blocks[area1] = blocks[area1]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area1]:sub(25,32)
newcrc = ('%04X'):format(CalcCheckSum(blocks,2,3))
blocks[area2] = blocks[area2]:sub(1,20)..newcrc:sub(3,4)..newcrc:sub(1,2)..blocks[area2]:sub(25,32)
end
end
function CalcCheckSum(blocks, dataarea, chksumtype)
local area = 36
if dataarea == 1 then
area = 8
end
if chksumtype == 0 then
data = blocks[0]..blocks[1]:sub(1,28)
elseif chksumtype == 1 then
data = blocks[area]:sub(1,28)..'0500'
elseif chksumtype == 2 then
data = blocks[area+1]..blocks[area+2]..blocks[area+4]
elseif chksumtype == 3 then
data = blocks[area+5]..blocks[area+6]..blocks[area+8]..string.rep('00',0xe0)
end
return utils.Crc16(data)
end
local function ValidateCheckSums(blocks)
local isOk, crc, calc
-- Checksum Type 0
crc = GetCheckSum(blocks,1,0)
calc = CalcCheckSum(blocks, 1, 0)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 0 : %04x = %04x -- %s\n'):format(crc,calc,isOk))
-- Checksum Type 1 (DATAAREAHEADER 1)
crc = GetCheckSum(blocks,1,1)
calc = CalcCheckSum(blocks,1,1)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 1 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
-- Checksum Type 1 (DATAAREAHEADER 2)
crc = GetCheckSum(blocks,2,1)
calc = CalcCheckSum(blocks,2,1)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 1 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
-- Checksum Type 2 (DATAAREA 1)
crc = GetCheckSum(blocks,1,2)
calc = CalcCheckSum(blocks,1,2)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 2 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
-- Checksum Type 2 (DATAAREA 2)
crc = GetCheckSum(blocks,2,2)
calc = CalcCheckSum(blocks,2,2)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 2 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
-- Checksum Type 3 (DATAAREA 1)
crc = GetCheckSum(blocks,1,3)
calc = CalcCheckSum(blocks,1,3)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 3 area 1: %04x = %04x -- %s\n'):format(crc,calc,isOk))
-- Checksum Type 3 (DATAAREA 2)
crc = GetCheckSum(blocks,2,3)
calc = CalcCheckSum(blocks,2,3)
if crc == calc then isOk='Ok' else isOk = 'Error' end
io.write( ('TYPE 3 area 2: %04x = %04x -- %s\n'):format(crc,calc,isOk))
end
local function LoadEmulator(blocks)
local HASHCONSTANT = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
local cmd
local blockdata
for _,b in pairs(blocks) do
blockdata = b
if _%4 ~= 3 then
if (_ >= 8 and _<=21) or (_ >= 36 and _<=49) then
local base = ('%s%s%02x%s'):format(blocks[0], blocks[1], _ , HASHCONSTANT)
local baseStr = utils.ConvertHexToAscii(base)
local key = md5.sumhexa(baseStr)
local enc = core.aes(key, blockdata)
local hex = utils.ConvertAsciiToBytes(enc)
hex = utils.ConvertBytesToHex(hex)
blockdata = hex
io.write( _..',')
end
end
cmd = Command:new{cmd = cmds.CMD_MIFARE_EML_MEMSET, arg1 = _ ,arg2 = 1,arg3 = 0, data = blockdata}
local err = core.SendCommand(cmd:getBytes())
if err then
return err
end
end
io.write('\n')
end
local function main(args)
print( string.rep('--',20) )
print( string.rep('--',20) )
local result, err, hex
local maxed = false
local inputTemplate = "dumpdata.bin"
local outputTemplate = os.date("toydump_%Y-%m-%d_%H%M");
-- Arguments for the script
for o, a in getopt.getopt(args, 'hmi:o:') do
if o == "h" then return help() end
if o == "m" then maxed = true end
if o == "o" then outputTemplate = a end
if o == "i" then inputTemplate = a end
end
-- Turn off Debug
local cmdSetDbgOff = "hf mf dbg 0"
core.console( cmdSetDbgOff)
-- Look for tag present on reader,
result, err = lib14a.read1443a(false)
if not result then return oops(err) end
core.clearCommandBuffer()
if 0x01 ~= result.sak then -- NXP MIFARE TNP3xxx
return oops('This is not a TNP3xxx tag. aborting.')
end
-- Show tag info
print((' Found tag : %s'):format(result.name))
-- Load dump.bin file
print( (' Load data from %s'):format(inputTemplate))
hex, err = utils.ReadDumpFile(inputTemplate)
if not hex then return oops(err) end
local blocks = {}
local blockindex = 0
for i = 1, #hex, 32 do
blocks[blockindex] = hex:sub(i,i+31)
blockindex = blockindex + 1
end
if DEBUG then
print('Validating checksums in the loaded datadump')
ValidateCheckSums(blocks)
end
--
print( string.rep('--',20) )
print(' Gathering info')
local uid = blocks[0]:sub(1,8)
local itemtype = blocks[1]:sub(1,4)
local cardid = blocks[1]:sub(9,24)
-- Show info
print( string.rep('--',20) )
print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, toyNames[itemtype]) )
print( (' UID : 0x%s'):format(uid) )
print( (' CARDID : 0x%s'):format(cardid ) )
print( string.rep('--',20) )
-- lets do something.
--
local experience = blocks[8]:sub(1,6)
print(('Experience : %d'):format(utils.SwapEndianness(experience,24)))
local money = blocks[8]:sub(7,10)
print(('Money : %d'):format(utils.SwapEndianness(money,16)))
local fairy = blocks[9]:sub(1,8)
--FD0F = Left, FF0F = Right
local path = 'not choosen'
if fairy:sub(2,2) == 'D' then
path = 'Left'
elseif fairy:sub(2,2) == 'F' then
path = 'Right'
end
print(('Fairy : %d [Path: %s] '):format(utils.SwapEndianness(fairy,24),path))
local hat = blocks[9]:sub(8,11)
print(('Hat : %d'):format(utils.SwapEndianness(hat,16)))
--0x0D 0x29 0x0A 0x02 16-bit hero points value. Maximum 100.
local heropoints = blocks[13]:sub(20,23)
print(('Hero points : %d'):format(utils.SwapEndianness(heropoints,16)))
--0x10 0x2C 0x0C 0x04 32 bit flag value indicating heroic challenges completed.
local challenges = blocks[16]:sub(25,32)
print(('Finished hero challenges : %d'):format(utils.SwapEndianness(challenges,32)))
if maxed then
print('Lets try to max out some values')
-- max out money, experience
--print (blocks[8])
blocks[8] = 'FFFFFF'..'FFFF'..blocks[8]:sub(11,32)
blocks[36] = 'FFFFFF'..'FFFF'..blocks[36]:sub(11,32)
--print (blocks[8])
-- max out hero challenges
--print (blocks[16])
blocks[16] = blocks[16]:sub(1,24)..'FFFFFFFF'
blocks[44] = blocks[44]:sub(1,24)..'FFFFFFFF'
--print (blocks[16])
-- max out heropoints
--print (blocks[13])
blocks[13] = blocks[13]:sub(1,19)..'0064'..blocks[13]:sub(24,32)
blocks[41] = blocks[41]:sub(1,19)..'0064'..blocks[41]:sub(24,32)
--print (blocks[13])
-- Update Checksums
print('Updating all checksums')
SetCheckSum(blocks, 3)
SetCheckSum(blocks, 2)
SetCheckSum(blocks, 1)
SetCheckSum(blocks, 0)
print('Validating all checksums')
ValidateCheckSums(blocks)
end
--Load dumpdata to emulator memory
if DEBUG then
print('Sending dumpdata to emulator memory')
err = LoadEmulator(blocks)
if err then return oops(err) end
core.clearCommandBuffer()
print('The simulation is now prepared.\n --> run \"hf mf sim 5 '..uid..'\" <--')
end
end
main(args)