text
[RRG-proxmark3.git] / client / luascripts / hf_mfu_amiibo_restore.lua
blobc31db70986ce12c25efe946df02670afc057e6ff
1 local cmds = require('commands')
2 local getopt = require('getopt')
3 local os = require('os')
4 local io = require('io')
5 local bin = require('bin')
6 local utils = require('utils')
7 local ansicolors = require('ansicolors')
8 local amiibo_tools = require('amiibo_tools')
10 copyright = ''
11 author = 'George Talusan'
12 version = 'v0.0.1'
13 desc = [[
14 This script will try to restore a binary datadump of an Amiibo to a blank NTAG215.
15 It will recalculate PWD and PACK if necessary, set the appropriate password and sector lock bytes.
17 NOTE: PyAmiibo must be installed. The helper script pyscripts/amiibo_change_uid.py depends on PyAmiibo.
19 YMMV if a non-blank NTAG215 is provided!
21 example = [[
22 1. script run hf_mfu_amiibo_restore
23 2. script run hf_mfu_amiibo_restore -f myfile -k password
25 usage = [[
26 script run hf_mfu_amiibo_restore [-h] [-f <filename> -k <password>]
28 arguments = [[
29 -h : this help
30 -f : filename for the datadump to read (bin)
31 -k : password of blank NTAG 215 (use `hf mfu info` to find it)
34 local DEBUG = false -- the debug flag
36 local bxor = bit32.bxor
37 local sub = string.sub
38 local format = string.format
40 ---
41 -- A debug printout-function
42 local function dbg(args)
43 if not DEBUG then return end
44 if type(args) == 'table' then
45 local i = 1
46 while result[i] do
47 dbg(result[i])
48 i = i+1
49 end
50 else
51 print('###', args)
52 end
53 end
54 ---
55 -- This is only meant to be used when errors occur
56 local function oops(err)
57 print('ERROR:', err)
58 core.clearCommandBuffer()
59 return nil, err
60 end
61 ---
62 -- Usage help
63 local function help()
64 print(copyright)
65 print(author)
66 print(version)
67 print(desc)
68 print(ansicolors.cyan..'Usage'..ansicolors.reset)
69 print(usage)
70 print(ansicolors.cyan..'Arguments'..ansicolors.reset)
71 print(arguments)
72 print(ansicolors.cyan..'Example usage'..ansicolors.reset)
73 print(example)
74 end
76 -- Exit message
77 local function ExitMsg(msg)
78 print( string.rep('--',20) )
79 print( string.rep('--',20) )
80 print(msg)
81 print()
82 end
84 local function main(args)
85 print( string.rep('--',20) )
86 print( string.rep('--',20) )
88 local result, err, hex
89 local inputTemplate = 'dumpdata.bin'
90 local password
92 for o, a in getopt.getopt(args, 'hf:k:') do
93 if o == 'h' then return help() end
94 if o == 'f' then inputTemplate = a end
95 if o == 'k' then password = a end
96 end
98 print(('Loading data from %s'):format(inputTemplate))
99 hex, err = utils.ReadDumpFile(inputTemplate)
100 if not hex then return oops(err) end
102 if not password or #password ~= 8 then
103 return oops('Expecting 4 byte password (hint: use `hf mfu info` to get it)')
106 -- chomp emu header
107 if #hex == 1192 then
108 hex = hex:sub(113)
111 local amiibo_offset = 0
112 local amiibo_info = hex:sub(amiibo_offset + 169, amiibo_offset + 169 + 15):lower()
113 local amiibo_game = amiibo_info:sub(1, 3)
114 local amiibo_type = amiibo_info:sub(7, 8)
115 local amiibo_series = amiibo_info:sub(13, 14)
117 dbg('raw: '..ansicolors.green..amiibo_info..ansicolors.reset)
118 print('game: '..ansicolors.green..amiibo_tools.db.game_series[("0x%s"):format(amiibo_game)]..ansicolors.reset)
119 print('character: '..ansicolors.green..amiibo_tools.db.amiibos[("0x%s"):format(amiibo_info)].name..ansicolors.reset)
120 print('type: '..ansicolors.green..amiibo_tools.db.types[("0x%s"):format(amiibo_type)]..ansicolors.reset)
121 print('series: '..ansicolors.green..amiibo_tools.db.amiibo_series[("0x%s"):format(amiibo_series)]..ansicolors.reset)
123 local uid = core.ul_read_uid();
124 if uid == nil then
125 return oops("Can't read UID of NTAG215 card. Reposition card and try again.")
128 local tmp = ('%s.bin'):format(os.tmpname())
129 local amiibo_file = io.open(tmp, 'w+b')
130 amiibo_file:write(bin.pack('H', hex))
131 amiibo_file:close()
132 local tmp2 = ('%s.bin'):format(os.tmpname())
134 print('generating new Amiibo binary for NTAG215 '..ansicolors.green..uid)
135 core.clearCommandBuffer()
136 core.console(('script run amiibo_change_uid %s %s %s %s'):format(uid, tmp, tmp2, core.search_file('resources/key_retail', '.bin')))
138 -- let's sanity check the output
139 hex, err = utils.ReadDumpFile(tmp2)
140 if not hex or #hex ~= 1080 then
141 os.remove(tmp)
142 os.remove(tmp2)
143 return oops('There was a problem generating the output Amiibo')
146 core.console(('hf mfu restore -f %s -k %s'):format(tmp2, password))
148 -- re-write some blocks because `hf mfu restore` won't write out blocks 0-3, and PyAmiibo won't give a PACK/PWD
149 local pwd, pack = core.keygen_algo_b(uid)
150 core.console(('hf mfu wrbl -b 3 -d F110FFEE -k %s'):format(password)) -- CC?
151 core.console(('hf mfu wrbl -b 134 -d %04X0000 -k %s'):format(pack, password)) -- PACK/RFUI
152 core.console(('hf mfu wrbl -b 133 -d %08X -k %s'):format(pwd, password)) -- PWD
153 core.console(('hf mfu wrbl -b 131 -d 00000004 -k %08X'):format(pwd)) -- CFG0
154 core.console(('hf mfu wrbl -b 132 -d 5F000000 -k %08X'):format(pwd)) -- CFG1
156 local lock_bytes = hex:sub(17, 24)
157 dbg('lock_bytes: '..lock_bytes)
158 core.console(('hf mfu wrbl -b 2 -d %s -k %08X'):format(lock_bytes, pwd)) -- BCC1/static lock
159 core.console(('hf mfu wrbl -b 130 -d 01000FBD -k %08X'):format(pwd)) -- dynamic lock/RFUI
161 os.remove(tmp)
162 os.remove(tmp2)
164 main(args)