mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
add hf_mfu_amiibo_restore to restore an Amiibo dump to a blank NTAG215
This commit is contained in:
parent
96f977ed8a
commit
d89ed735b0
5 changed files with 2913 additions and 2653 deletions
2648
client/lualibs/amiibo_tools.lua
Normal file
2648
client/lualibs/amiibo_tools.lua
Normal file
File diff suppressed because it is too large
Load diff
164
client/luascripts/hf_mfu_amiibo_restore.lua
Normal file
164
client/luascripts/hf_mfu_amiibo_restore.lua
Normal file
|
@ -0,0 +1,164 @@
|
|||
local cmds = require('commands')
|
||||
local getopt = require('getopt')
|
||||
local os = require('os')
|
||||
local io = require('io')
|
||||
local bin = require('bin')
|
||||
local utils = require('utils')
|
||||
local ansicolors = require('ansicolors')
|
||||
local amiibo_tools = require('amiibo_tools')
|
||||
|
||||
copyright = ''
|
||||
author = 'George Talusan'
|
||||
version = 'v0.0.1'
|
||||
desc = [[
|
||||
This script will try to restore a binary datadump of an Amiibo to a blank NTAG215.
|
||||
It will recalculate PWD and PACK if necessary, set the appropriate password and sector lock bytes.
|
||||
|
||||
NOTE: PyAmiibo must be installed. The helper script pyscripts/amiibo_change_uid.py depends on PyAmiibo.
|
||||
|
||||
YMMV if a non-blank NTAG215 is provided!
|
||||
]]
|
||||
example = [[
|
||||
1. script run hf_mfu_amiibo_restore
|
||||
2. script run hf_mfu_amiibo_restore -f myfile -k password
|
||||
]]
|
||||
usage = [[
|
||||
script run hf_mfu_amiibo_restore [-h] [-f <filename> -k <password>]
|
||||
]]
|
||||
arguments = [[
|
||||
-h : this help
|
||||
-f : filename for the datadump to read (bin)
|
||||
-k : password of blank NTAG 215 (use `hf mfu info` to find it)
|
||||
]]
|
||||
|
||||
local DEBUG = false -- the debug flag
|
||||
|
||||
local bxor = bit32.bxor
|
||||
local sub = string.sub
|
||||
local format = string.format
|
||||
|
||||
---
|
||||
-- A debug printout-function
|
||||
local 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
|
||||
local function oops(err)
|
||||
print('ERROR:', err)
|
||||
core.clearCommandBuffer()
|
||||
return nil, err
|
||||
end
|
||||
---
|
||||
-- Usage help
|
||||
local function help()
|
||||
print(copyright)
|
||||
print(author)
|
||||
print(version)
|
||||
print(desc)
|
||||
print(ansicolors.cyan..'Usage'..ansicolors.reset)
|
||||
print(usage)
|
||||
print(ansicolors.cyan..'Arguments'..ansicolors.reset)
|
||||
print(arguments)
|
||||
print(ansicolors.cyan..'Example usage'..ansicolors.reset)
|
||||
print(example)
|
||||
end
|
||||
--
|
||||
-- Exit message
|
||||
local function ExitMsg(msg)
|
||||
print( string.rep('--',20) )
|
||||
print( string.rep('--',20) )
|
||||
print(msg)
|
||||
print()
|
||||
end
|
||||
|
||||
local function main(args)
|
||||
print( string.rep('--',20) )
|
||||
print( string.rep('--',20) )
|
||||
|
||||
local result, err, hex
|
||||
local inputTemplate = 'dumpdata.bin'
|
||||
local password
|
||||
|
||||
for o, a in getopt.getopt(args, 'hf:k:') do
|
||||
if o == 'h' then return help() end
|
||||
if o == 'f' then inputTemplate = a end
|
||||
if o == 'k' then password = a end
|
||||
end
|
||||
|
||||
print(('Loading data from %s'):format(inputTemplate))
|
||||
hex, err = utils.ReadDumpFile(inputTemplate)
|
||||
if not hex then return oops(err) end
|
||||
|
||||
if not password or #password ~= 8 then
|
||||
return oops('Expecting 4 byte password (hint: use `hf mfu info` to get it)')
|
||||
end
|
||||
|
||||
-- chomp emu header
|
||||
if #hex == 1192 then
|
||||
hex = hex:sub(112)
|
||||
end
|
||||
|
||||
local amiibo_offset = 0
|
||||
local amiibo_info = hex:sub(amiibo_offset + 169, amiibo_offset + 169 + 15):lower()
|
||||
local amiibo_game = amiibo_info:sub(1, 3)
|
||||
local amiibo_type = amiibo_info:sub(7, 8)
|
||||
local amiibo_series = amiibo_info:sub(13, 14)
|
||||
|
||||
dbg('raw: '..ansicolors.green..amiibo_info..ansicolors.reset)
|
||||
print('game: '..ansicolors.green..amiibo_tools.db.game_series[("0x%s"):format(amiibo_game)]..ansicolors.reset)
|
||||
print('character: '..ansicolors.green..amiibo_tools.db.amiibos[("0x%s"):format(amiibo_info)].name..ansicolors.reset)
|
||||
print('type: '..ansicolors.green..amiibo_tools.db.types[("0x%s"):format(amiibo_type)]..ansicolors.reset)
|
||||
print('series: '..ansicolors.green..amiibo_tools.db.amiibo_series[("0x%s"):format(amiibo_series)]..ansicolors.reset)
|
||||
|
||||
local uid = core.ul_read_uid();
|
||||
if uid == nil then
|
||||
return oops("Can't read UID of NTAG215 card. Reposition card and try again.")
|
||||
end
|
||||
|
||||
local tmp = ('%s.bin'):format(os.tmpname())
|
||||
local amiibo_file = io.open(tmp, 'w+b')
|
||||
amiibo_file:write(bin.pack('H', hex))
|
||||
amiibo_file:close()
|
||||
local tmp2 = ('%s.bin'):format(os.tmpname())
|
||||
|
||||
print('generating new Amiibo binary for NTAG215 '..ansicolors.green..uid)
|
||||
core.clearCommandBuffer()
|
||||
core.console(('script run amiibo_change_uid %s %s %s %sresources/key_retail.bin'):format(uid, tmp, tmp2, core.ewd()))
|
||||
|
||||
-- let's sanity check the output
|
||||
hex, err = utils.ReadDumpFile(tmp2)
|
||||
if not hex or #hex ~= 1080 then
|
||||
os.remove(tmp)
|
||||
os.remove(tmp2)
|
||||
return oops('There was a problem generating the output Amiibo')
|
||||
end
|
||||
|
||||
core.console(('hf mfu restore -f %s -k %s'):format(tmp2, password))
|
||||
|
||||
-- re-write some blocks because `hf mfu restore` won't write out blocks 0-3, and PyAmiibo won't give a PACK/PWD
|
||||
local pwd, pack = core.keygen_algo_b(uid)
|
||||
core.console(('hf mfu wrbl -b 3 -d F110FFEE -k %s'):format(password)) -- CC?
|
||||
core.console(('hf mfu wrbl -b 134 -d %04X0000 -k %s'):format(pack, password)) -- PACK/RFUI
|
||||
core.console(('hf mfu wrbl -b 133 -d %08X -k %s'):format(pwd, password)) -- PWD
|
||||
core.console(('hf mfu wrbl -b 131 -d 00000004 -k %08X'):format(pwd)) -- CFG0
|
||||
core.console(('hf mfu wrbl -b 132 -d 5F000000 -k %08X'):format(pwd)) -- CFG1
|
||||
|
||||
local lock_bytes = hex:sub(17, 24)
|
||||
dbg('lock_bytes: '..lock_bytes)
|
||||
core.console(('hf mfu wrbl -b 2 -d %s -k %08X'):format(lock_bytes, pwd)) -- BCC1/static lock
|
||||
core.console(('hf mfu wrbl -b 130 -d 01000FBD -k %08X'):format(pwd)) -- dynamic lock/RFUI
|
||||
|
||||
os.remove(tmp)
|
||||
os.remove(tmp2)
|
||||
end
|
||||
main(args)
|
File diff suppressed because it is too large
Load diff
41
client/pyscripts/amiibo_change_uid.py
Normal file
41
client/pyscripts/amiibo_change_uid.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
import sys
|
||||
from amiibo import AmiiboDump, AmiiboMasterKey
|
||||
|
||||
def main():
|
||||
if(len(sys.argv) != 5):
|
||||
print("""
|
||||
\t{0} - helper script for integrating with PyAmiibo
|
||||
|
||||
Usage: {0} <uid> <infile> <outfile> <keyfile>
|
||||
|
||||
Example:
|
||||
|
||||
\t{0} 04123456789ABC my_amiibo_original.bin my_amiibo_with_new_uid.bin keyfile.bin
|
||||
|
||||
\n""".format(sys.argv[0]))
|
||||
return 1
|
||||
|
||||
uid = sys.argv[1]
|
||||
infile = sys.argv[2]
|
||||
outfile = sys.argv[3]
|
||||
keyfile = sys.argv[4]
|
||||
|
||||
if len(uid) != 14:
|
||||
print('expecting 7 byte UID')
|
||||
return 1
|
||||
|
||||
with open(keyfile, 'rb') as keybin:
|
||||
master_key = AmiiboMasterKey.from_combined_bin(keybin.read())
|
||||
|
||||
with open(infile, 'rb') as fin, open(outfile, 'wb') as fout:
|
||||
dump = AmiiboDump(master_key, fin.read(), is_locked=True)
|
||||
dump.unlock()
|
||||
dump.uid_hex = uid
|
||||
dump.lock()
|
||||
fout.write(dump.data)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
@ -886,6 +886,39 @@ static int l_detect_prng(lua_State *L) {
|
|||
lua_pushinteger(L, res);
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* @brief l_keygen_algoB is a function to calculate pwd/pack using UID, by algo B
|
||||
* @param L
|
||||
* @return
|
||||
*/
|
||||
static int l_keygen_algoB(lua_State *L) {
|
||||
//Check number of arguments
|
||||
int n = lua_gettop(L);
|
||||
if (n != 1) {
|
||||
return returnToLuaWithError(L, "Only UID");
|
||||
}
|
||||
|
||||
size_t size;
|
||||
uint32_t tmp;
|
||||
const char *p_uid = luaL_checklstring(L, 1, &size);
|
||||
if (size != 14)
|
||||
return returnToLuaWithError(L, "Wrong size of UID, got %d bytes, expected 14", (int) size);
|
||||
|
||||
uint8_t uid[7] = {0, 0, 0, 0, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < 14; i += 2) {
|
||||
sscanf(&p_uid[i], "%02x", &tmp);
|
||||
uid[i / 2] = tmp & 0xFF;
|
||||
}
|
||||
|
||||
uint32_t pwd = ul_ev1_pwdgenB(uid);
|
||||
uint16_t pack = ul_ev1_packgenB(uid);
|
||||
|
||||
lua_pushunsigned(L, pwd);
|
||||
lua_pushunsigned(L, pack);
|
||||
return 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief l_keygen_algoD is a function to calculate pwd/pack using UID, by algo D
|
||||
* @param L
|
||||
|
@ -1196,6 +1229,19 @@ static int l_ndefparse(lua_State *L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int l_ul_read_uid(lua_State *L) {
|
||||
uint8_t uid[7] = { 0, 0, 0, 0, 0, 0, 0 };
|
||||
int res = ul_read_uid(uid);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return returnToLuaWithError(L, "Failed to read Ultralight/NTAG UID");
|
||||
}
|
||||
char buf[15];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
snprintf(buf, sizeof(buf), "%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]);
|
||||
lua_pushstring(L, buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_remark(lua_State *L) {
|
||||
//Check number of arguments
|
||||
int n = lua_gettop(L);
|
||||
|
@ -1340,7 +1386,7 @@ int set_pm3_libraries(lua_State *L) {
|
|||
{"hardnested", l_hardnested},
|
||||
{"detect_prng", l_detect_prng},
|
||||
// {"keygen.algoA", l_keygen_algoA},
|
||||
// {"keygen.algoB", l_keygen_algoB},
|
||||
{"keygen_algo_b", l_keygen_algoB},
|
||||
// {"keygen.algoC", l_keygen_algoC},
|
||||
{"keygen_algo_d", l_keygen_algoD},
|
||||
{"t55xx_readblock", l_T55xx_readblock},
|
||||
|
@ -1354,6 +1400,7 @@ int set_pm3_libraries(lua_State *L) {
|
|||
{"rem", l_remark},
|
||||
{"em4x05_read", l_em4x05_read},
|
||||
{"em4x50_read", l_em4x50_read},
|
||||
{"ul_read_uid", l_ul_read_uid},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue