forked from arti/vt1200
dwt: runtime oracle and ground-truth vectors for the dwt_prf challenge transform
This commit is contained in:
1 parent
5645bb00b3
commit
041c82cb73
31 files changed
+7367
No files matched your search
@@ -0,0 +1,95 @@
|
||||
-- oracle_call_test.lua -- minimal proof that the firmware's challenge pipeline
|
||||
-- can be invoked directly from the debugger (no TCP connection).
|
||||
--
|
||||
-- Calls 0x3F5A9620(1) (dwt_prf's allocator/init, Ghidra 0x07EB52C4)
|
||||
-- at frame 1000 and stops at the synthetic return address.
|
||||
local machine = manager.machine
|
||||
local cpu = machine.devices[':maincpu']
|
||||
local dbg = machine.debugger
|
||||
local space = cpu.spaces['program']
|
||||
if not dbg then machine:exit(); return end
|
||||
dbg.visible_cpu = cpu
|
||||
|
||||
local ARG = 0x01800000
|
||||
local STACK = 0x01840000
|
||||
local RET = 0x018c0000
|
||||
local IE = 0x200000
|
||||
local INIT = 0x3F5A9620
|
||||
|
||||
local f = 0
|
||||
local started = false
|
||||
local saved = {}
|
||||
|
||||
local function state(name)
|
||||
local r = cpu.state[name]
|
||||
return r and r.value or nil
|
||||
end
|
||||
|
||||
local function push(v)
|
||||
local sp = state('SP') - 0x20
|
||||
space:write_u32(sp, v)
|
||||
cpu.state['SP'].value = sp
|
||||
end
|
||||
|
||||
local function setup_call(target, args)
|
||||
saved.pc = state('PC')
|
||||
saved.sp = state('SP')
|
||||
saved.a14 = state('A14')
|
||||
saved.st = state('ST')
|
||||
print(string.format("TEST setup target=%08X pc=%08X sp=%08X a14=%08X st=%08X",
|
||||
target, saved.pc, saved.sp, saved.a14, saved.st))
|
||||
cpu.state['SP'].value = STACK
|
||||
if #args > 0 then
|
||||
local a = ARG
|
||||
for i = 1, #args do
|
||||
space:write_u32(a, args[i])
|
||||
a = a + 0x20
|
||||
end
|
||||
push(saved.a14)
|
||||
cpu.state['A14'].value = ARG + #args * 0x20
|
||||
end
|
||||
push(RET)
|
||||
cpu.state['PC'].value = target
|
||||
cpu.state['ST'].value = saved.st & ~IE
|
||||
dbg:command('bclear')
|
||||
dbg:command(string.format('bpset %X', RET))
|
||||
dbg.execution_state = 'run'
|
||||
end
|
||||
|
||||
emu.register_frame_done(function()
|
||||
f = f + 1
|
||||
if f == 1000 and not started then
|
||||
started = true
|
||||
print(string.format("TEST frame=%d @1C0200=%08X", f, space:read_u32(0x1c0200)))
|
||||
setup_call(INIT, { 1 })
|
||||
return
|
||||
end
|
||||
if started and saved.pc then
|
||||
local es = dbg.execution_state
|
||||
if es == 'stop' then
|
||||
local pc = state('PC')
|
||||
print(string.format("TEST stop frame=%d pc=%08X savedpc=%08X", f, pc, saved.pc))
|
||||
print(string.format("TEST @1C0200=%08X @1C0220=%08X @1C0240=%08X @1C0260=%08X",
|
||||
space:read_u32(0x1c0200), space:read_u32(0x1c0220),
|
||||
space:read_u32(0x1c0240), space:read_u32(0x1c0260)))
|
||||
-- dump 16 bytes at the key pointer if it looks sane
|
||||
local kp = space:read_u32(0x1c0260)
|
||||
if kp ~= 0 then
|
||||
local t = {}
|
||||
for i = 0, 15 do
|
||||
t[#t + 1] = string.format("%02X", space:read_u8(kp + i * 8))
|
||||
end
|
||||
print("TEST key[0..15]=" .. table.concat(t, " "))
|
||||
end
|
||||
dbg:command('bclear')
|
||||
cpu.state['PC'].value = saved.pc
|
||||
cpu.state['SP'].value = saved.sp
|
||||
cpu.state['A14'].value = saved.a14
|
||||
cpu.state['ST'].value = saved.st
|
||||
print("TEST restoring and exiting")
|
||||
machine:exit()
|
||||
end
|
||||
end
|
||||
end, "vt1200-oracle-call-test")
|
||||
|
||||
dbg.execution_state = 'run'
|
||||
@@ -0,0 +1,175 @@
|
||||
-- oracle_stage.lua -- intermediate-state capture for ONE 8-byte record of the
|
||||
-- dwt_prf challenge transform.
|
||||
--
|
||||
-- Calls the per-record stage 0x3F5ABA40 directly (Ghidra 0x07EB5748) after the
|
||||
-- same INIT/EXPAND sequence dwt/oracle.lua uses, and stops at two interior
|
||||
-- points:
|
||||
--
|
||||
-- 0x3F5ABB30 immediately after the first font render returns; A13 points
|
||||
-- at the callee's 8-byte local record -> "feistel_in"
|
||||
-- 0x3F5AC620 immediately before the final font render; A13+0..7 holds the
|
||||
-- pre-final-render record -> "pre_final_render"
|
||||
--
|
||||
-- Output line: "<index> <input8> <feistel_in> <pre_final> <output8>"
|
||||
--
|
||||
-- Inputs: VT1200_ORACLE_IN (one 16-hex-char record per line)
|
||||
-- Output: VT1200_ORACLE_OUT
|
||||
-- Env: VT1200_ORACLE_START (default 3000), VT1200_ORACLE_TAG
|
||||
local machine = manager.machine
|
||||
local cpu = machine.devices[':maincpu']
|
||||
local dbg = machine.debugger
|
||||
local space = cpu.spaces['program']
|
||||
if not dbg then machine:exit(); return end
|
||||
dbg.visible_cpu = cpu
|
||||
|
||||
local IN_PATH = os.getenv("VT1200_ORACLE_IN")
|
||||
local OUT_PATH = os.getenv("VT1200_ORACLE_OUT")
|
||||
local START = tonumber(os.getenv("VT1200_ORACLE_START") or "3000")
|
||||
local TAG = os.getenv("VT1200_ORACLE_TAG") or "stage"
|
||||
|
||||
local ARG = 0x01800000
|
||||
local STACK = 0x01840000
|
||||
local CBUF = 0x01880000
|
||||
local RET = 0x018C0000
|
||||
local IE = 0x200000
|
||||
|
||||
local INIT = 0x3F5A9620
|
||||
local EXPAND = 0x3F5AA2C0
|
||||
local RECORD = 0x3F5ABA40
|
||||
local S1 = 0x3F5ABB30
|
||||
local S2 = 0x3F5AC620
|
||||
local SRC = 0x1BE810
|
||||
|
||||
local f, stage, idx = 0, "boot", 1
|
||||
local inputs, out, saved = {}, nil, {}
|
||||
local s1, s2 = nil, nil
|
||||
|
||||
local function state(n)
|
||||
local r = cpu.state[n]
|
||||
return r and r.value or nil
|
||||
end
|
||||
|
||||
local function hexbytes(bit, n)
|
||||
local t = {}
|
||||
for i = 0, n - 1 do
|
||||
t[#t + 1] = string.format("%02X", space:read_u8(bit + i * 8))
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local function push(v)
|
||||
local sp = state('SP') - 0x20
|
||||
space:write_u32(sp, v)
|
||||
cpu.state['SP'].value = sp
|
||||
end
|
||||
|
||||
local function call(target, args, extra_bps)
|
||||
saved.pc, saved.sp, saved.a14, saved.st =
|
||||
state('PC'), state('SP'), state('A14'), state('ST')
|
||||
cpu.state['SP'].value = STACK
|
||||
if #args > 0 then
|
||||
local a = ARG
|
||||
for i = 1, #args do
|
||||
space:write_u32(a, args[i]); a = a + 0x20
|
||||
end
|
||||
push(saved.a14)
|
||||
cpu.state['A14'].value = ARG + #args * 0x20
|
||||
end
|
||||
push(RET)
|
||||
cpu.state['PC'].value = target
|
||||
cpu.state['ST'].value = saved.st & ~IE
|
||||
dbg:command('bclear')
|
||||
dbg:command(string.format('bpset %X', RET))
|
||||
if extra_bps then
|
||||
for _, bp in ipairs(extra_bps) do
|
||||
dbg:command(string.format('bpset %X', bp))
|
||||
end
|
||||
end
|
||||
dbg.execution_state = 'run'
|
||||
end
|
||||
|
||||
local function restore_exit()
|
||||
dbg:command('bclear')
|
||||
cpu.state['PC'].value, cpu.state['SP'].value =
|
||||
saved.pc, saved.sp
|
||||
cpu.state['A14'].value, cpu.state['ST'].value =
|
||||
saved.a14, saved.st
|
||||
if out then out:close() end
|
||||
print(TAG .. ": done")
|
||||
machine:exit()
|
||||
end
|
||||
|
||||
local fh = io.open(IN_PATH, "r")
|
||||
if not fh then print(TAG .. ": no input file"); machine:exit(); return end
|
||||
for line in fh:lines() do
|
||||
line = line:gsub("%s+", "")
|
||||
if #line > 0 and line:sub(1, 1) ~= "#" then
|
||||
if #line ~= 16 then
|
||||
print(TAG .. ": bad record length " .. #line); machine:exit(); return
|
||||
end
|
||||
inputs[#inputs + 1] = line:upper()
|
||||
end
|
||||
end
|
||||
fh:close()
|
||||
out = assert(io.open(OUT_PATH, "w"))
|
||||
print(string.format("%s: %d records", TAG, #inputs))
|
||||
|
||||
local function next_record()
|
||||
if idx > #inputs then restore_exit(); return end
|
||||
local v = inputs[idx]
|
||||
for i = 0, 7 do
|
||||
space:write_u8(CBUF + i * 8, tonumber(v:sub(i * 2 + 1, i * 2 + 2), 16))
|
||||
space:write_u8(CBUF + (8 + i) * 8, 0)
|
||||
end
|
||||
s1, s2 = nil, nil
|
||||
stage = "rec_run"
|
||||
call(RECORD, { CBUF }, { S1, S2 })
|
||||
end
|
||||
|
||||
emu.register_frame_done(function()
|
||||
f = f + 1
|
||||
if stage == "boot" and f >= START then
|
||||
print(string.format("%s: frame %d hijack", TAG, f))
|
||||
stage = "init_run"
|
||||
call(INIT, { 1 })
|
||||
return
|
||||
end
|
||||
if stage == "boot" then return end
|
||||
if dbg.execution_state ~= 'stop' then return end
|
||||
local pc = state('PC')
|
||||
|
||||
if stage == "init_run" then
|
||||
stage = "expand_run"
|
||||
call(EXPAND, { SRC })
|
||||
elseif stage == "expand_run" then
|
||||
stage = "records"
|
||||
next_record()
|
||||
elseif stage == "rec_run" then
|
||||
if pc == S1 then
|
||||
s1 = hexbytes(state('A13'), 8)
|
||||
dbg:command('bclear')
|
||||
dbg:command(string.format('bpset %X', RET))
|
||||
dbg:command(string.format('bpset %X', S2))
|
||||
dbg.execution_state = 'run'
|
||||
elseif pc == S2 then
|
||||
s2 = hexbytes(state('A13'), 8)
|
||||
dbg:command('bclear')
|
||||
dbg:command(string.format('bpset %X', RET))
|
||||
dbg.execution_state = 'run'
|
||||
elseif pc == RET then
|
||||
local res = hexbytes(CBUF, 8)
|
||||
out:write(string.format("%d %s %s %s %s\n", idx, inputs[idx],
|
||||
tostring(s1), tostring(s2), res))
|
||||
out:flush()
|
||||
print(string.format("%s: %d/%d %s -> %s (in=%s pre=%s)",
|
||||
TAG, idx, #inputs, inputs[idx], res, tostring(s1), tostring(s2)))
|
||||
idx = idx + 1
|
||||
next_record()
|
||||
else
|
||||
print(string.format("%s: unexpected stop %08X", TAG, pc))
|
||||
restore_exit()
|
||||
end
|
||||
end
|
||||
end, "vt1200-dwt-stage")
|
||||
|
||||
dbg.execution_state = 'run'
|
||||
Reference in new issue
Block a user