proxmark3/client/scripts/tnp3sim.lua

473 lines
13 KiB
Lua
Raw Normal View History

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
2015-04-06 04:37:41 +08:00
local band = bit32.band
local bor = bit32.bor
local lshift = bit32.lshift
local rshift = bit32.rshift
local byte = string.byte
local char = string.char
local sub = string.sub
local format = string.format
---
-- 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
2015-04-06 04:37:41 +08:00
local function Num2Card(m, l)
local k = {
0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4A, 0x4B,
0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54,0x56, 0x57, 0x58, 0x59, 0x5A, 0x00
}
local msw = tonumber(utils.SwapEndiannessStr(m,32),16)
local lsw = tonumber(utils.SwapEndiannessStr(l,32),16)
if msw > 0x17ea1 then
return "too big"
end
if msw == 0x17ea1 and lsw > 0x8931fee8 then
return "out of range"
end
local s = ""
local index
for i = 1,10 do
index, msw, lsw = DivideByK( msw, lsw)
if ( index <= 1 ) then
s = char(k[index]) .. s
else
s = char(k[index-1]) .. s
end
print (index-1, msw, lsw)
end
return s
end
--33LRT-LM9Q9
--7, 122, 3474858630
--20, 4, 1008436634
--7, 0, 627182959
--17, 0, 21626998
--16, 0, 745758
--23, 0, 25715
--21, 0, 886
--16, 0, 30
--1, 0, 1
--1, 0, 0
function DivideByK(msw, lsw)
local lowLSW
local highLSW
local remainder = 0
local RADIX = 29
--local num = 0 | band( rshift(msw,16), 0xffff)
local num = band( rshift(msw, 16), 0xffff)
--highLSW = 0 | lshift( (num / RADIX) , 16)
highLSW = lshift( (num / RADIX) , 16)
remainder = num % RADIX
num = bor( lshift(remainder,16), band(msw, 0xffff))
--highLSW |= num / RADIX
highLSW = highLSW or (num / RADIX)
remainder = num % RADIX
num = bor( lshift(remainder,16), ( band(rshift(lsw,16), 0xffff)))
--lowLSW = 0 | (num / RADIX) << 16
lowLSW = 0 or (lshift( (num / RADIX), 16))
remainder = num % RADIX
num = bor( lshift(remainder,16) , band(lsw, 0xffff) )
lowLSW = bor(lowLSW, (num / RADIX))
remainder = num % RADIX
return remainder, highLSW, lowLSW
-- uint num = 0 | (msw >> 16) & 0xffff;
-- highLSW = 0 | (num / RADIX) << 16;
-- remainder = num % RADIX;
-- num = (remainder << 16) | (msw & 0xffff);
-- highLSW |= num / RADIX;
-- remainder = num % RADIX;
-- num = (remainder << 16) | ((lsw >> 16) & 0xffff);
-- lowLSW = 0 | (num / RADIX) << 16;
-- remainder = num % RADIX;
-- num = (remainder << 16) | (lsw & 0xffff);
-- lowLSW |= num / RADIX;
-- remainder = num % RADIX;
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)
-- if not loadFromDump then
-- -- 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))
-- end
-- 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)
2015-04-06 04:37:41 +08:00
local cardidLsw = blocks[1]:sub(9,16)
local cardidMsw = blocks[1]:sub(17,24)
2015-04-06 04:37:41 +08:00
local itemtypename = toyNames[itemtype]
if itemtypename == nil then
itemtypename = toyNames[utils.SwapEndiannessStr(itemtype,16)]
end
-- Show info
print( string.rep('--',20) )
2015-04-06 04:37:41 +08:00
print( (' ITEM TYPE : 0x%s - %s'):format(itemtype, itemtypename) )
print( (' UID : 0x%s'):format(uid) )
2015-04-06 04:37:41 +08:00
print( (' CARDID : 0x%s %s [%s]'):format(
cardidMsw,cardidLsw,
--Num2Card(cardidMsw, cardidLsw))
'')
)
print( string.rep('--',20) )
2015-04-06 04:37:41 +08:00
-- 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()
2015-04-06 04:37:41 +08:00
print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--')
end
end
main(args)