proxmark3/client/scripts/tnp3sim.lua
2015-05-13 21:20:23 -04:00

490 lines
13 KiB
Lua
Raw Permalink Blame 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 toys = 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 = false -- the debug flag
local RANDOM = '20436F707972696768742028432920323031302041637469766973696F6E2E20416C6C205269676874732052657365727665642E20'
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
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 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], _ , RANDOM)
local baseStr = utils.ConvertHexToAscii(base)
local key = md5.sumhexa(baseStr)
local enc = core.aes128_encrypt(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 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)
-- 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')
ValidateCheckSums(blocks)
end
--
print( string.rep('--',20) )
print(' Gathering info')
local uid = blocks[0]:sub(1,8)
local toytype = blocks[1]:sub(1,4)
local cardidLsw = blocks[1]:sub(9,16)
local cardidMsw = blocks[1]:sub(17,24)
local subtype = blocks[1]:sub(25,28)
-- Show info
print( string.rep('--',20) )
local item = toys.Find( toytype, subtype)
if item then
local itemStr = ('%s - %s (%s)'):format(item[6],item[5], item[4])
print(' ITEM TYPE : '..itemStr )
else
print( (' ITEM TYPE : 0x%s 0x%s'):format(toytype, subtype) )
end
print( (' UID : 0x%s'):format(uid) )
print( (' CARDID : 0x%s %s [%s]'):format(
cardidMsw,cardidLsw,
--Num2Card(cardidMsw, cardidLsw))
'')
)
print( string.rep('--',20) )
-- Experience should be:
local experience = blocks[8]:sub(1,6)
print(('Experience : %d'):format(utils.SwapEndianness(experience,16)))
local money = blocks[8]:sub(7,10)
print(('Money : %d'):format(utils.SwapEndianness(money,16)))
--
-- Sequence number
local seqnum = blocks[8]:sub(18,19)
print(('Sequence number : %d'):format( tonumber(seqnum,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)))
local level = blocks[13]:sub(27,28)
print(('LEVEL : %d'):format( tonumber(level,16)))
--h<>lsa: 667 029b
--local health = blocks[]:sub();
--print(('Health : %d'):format( tonumber(health,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)))
-- Character Name
local name1 = blocks[10]:sub(1,32)
local name2 = blocks[12]:sub(1,32)
print('Custom name : '..utils.ConvertHexToAscii(name1..name2))
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 u '..uid..'\" <--')
end
end
main(args)