Files

103 lines
3.5 KiB
Lua

-- gate_probe.lua -- Phase 1 of the rdebug exploit attempt.
--
-- * sample the two 16-bit config words at bit 0xAECD0 / 0xAECD2 across boot
-- * catch every write to that region (who writes 0x8070, and when)
-- * log the store instructions and the config-object pointers statically found
--
-- Diagnostic only: reads memory. Does not write anything.
--
-- Breakpoints/watchpoints are armed from the frame-1 callback, not at script
-- load: commands issued before dbg.execution_state='run' do not fire. Debugger
-- printf output requires -debuglog (it goes to ./debug.log, not stdout).
--
-- <mame> vt1200 -rompath <abs>/harness/mame/roms -video none -sound none \
-- -nothrottle -debug -debugger none -debuglog -nvram_directory <abs>/nvram \
-- -autoboot_script <abs>/gate_probe.lua -autoboot_delay 0
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
local space = cpu.spaces['program']
if not dbg then
print("GATE ERROR: debugger not enabled; pass -debug")
machine:exit()
return
end
dbg.visible_cpu = cpu
local function u16(a) return space:read_u16(a) end
local function u32(a) return space:read_u32(a) end
local function u8(a) return space:read_u8(a) end
local function str_at(bitptr, max)
local out = {}
for i = 0, (max or 96) - 1 do
local c = u8(bitptr + i * 8)
if c == 0 then break end
if c >= 32 and c < 127 then out[#out + 1] = string.char(c)
else out[#out + 1] = string.format("\\x%02X", c) end
end
return table.concat(out)
end
local samples = { [0]=true, [1]=true, [10]=true, [50]=true, [100]=true, [200]=true,
[600]=true, [1000]=true, [2000]=true, [3000]=true, [4000]=true, [5000]=true }
local frames = 0
local armed = false
local function arm()
-- Every write into the 8 bytes around the two words. wpset works in the
-- program space's address unit = 1 bit, so length 0x40 covers 8 bytes.
dbg:command('wpset 0xaecc0,0x40,w,,{ printf "WP cfg bit=%08X data=%04X pc=%08X\\n", wpaddr, wpdata, pc ; g }')
local stores = {
{ 0x3F329F00, "STORE aecd0" },
{ 0x3F32A050, "STORE aecd2" },
{ 0x3F32A1A0, "STORE aed51" },
{ 0x3F32A2F0, "STORE aed52" },
{ 0x3F32A440, "STORE aed53" },
{ 0x3F32A590, "STORE aed54" },
}
for _, s in ipairs(stores) do
dbg:command(string.format(
'bpset %08X,,{ printf "%s pc=%%08X a7=%%08X a11=%%08X a8=%%08X a13=%%08X\\n", pc, a7, a11, a8, a13 ; g }',
s[1], s[2]))
end
end
local function dump_items()
print("--- config item cells (values as the boot config code reads them)")
local slots = { 0x155830, 0x1565b0, 0x156c70, 0x157330, 0x1579f0,
0x1580b0, 0x152230, 0x1514b0, 0x11a4b0 }
for _, a in ipairs(slots) do
print(string.format(" @0x%06X = 0x%08X", a, u32(a)))
end
local t = {}
for i = 0, 15 do t[#t + 1] = string.format("%02X", u8(0xaec80 + i * 8)) end
print("--- bytes @bit 0xAEC80: " .. table.concat(t, " "))
print(string.format("--- word @0xAECD0 = 0x%04X, 2-bit fields: [0]=%d [1]=%d [2]=%d [3]=%d",
u16(0xaecd0), (u16(0xaecd0) >> 0) & 3, (u16(0xaecd0) >> 2) & 3,
(u16(0xaecd0) >> 4) & 3, (u16(0xaecd0) >> 6) & 3))
end
emu.register_frame_done(function()
frames = frames + 1
if frames == 1 and not armed then
armed = true
arm()
end
if samples[frames] then
print(string.format("SAMPLE frame %5d @0xAECD0=%04X @0xAECD2=%04X pc=%08X",
frames, u16(0xaecd0), u16(0xaecd2), cpu.state['PC'].value))
end
if frames == 3000 then
dump_items()
end
if frames >= tonumber(os.getenv("PROBE_END") or "5000") then
print(string.format("--- done at frame %d", frames))
machine:exit()
end
end, "vt1200-gate-probe")
dbg.execution_state = 'run'