mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
The xserver local service is reachable from a payload, but a connect made inside the rdebug process is accepted at the service layer and then never dequeued: the server's accept callback runs (3F34EC50), but no 12-byte X11 setup read follows and Xlib waits in select. create_process(entry,name,stack,1,arg) from a new process fixes it. xlaunch creates the child; the server reads the setup (READ12 ret 0x0C), writes the reply (8 + 0x90), and xhello creates/maps/syncs a 400x200 window. The payload also had to keep Display* in a11 (callee-saved) instead of reloading it from a RAM cell, and re-run SETF after each Xlib call. Final breakpoint-free frame: dwt/gui/evidence/xhello_window_frame2838.png. ROM untouched (b46a3e442ccb86134d2356bd70d22fd2).
193 lines
7.5 KiB
Lua
193 lines
7.5 KiB
Lua
-- trace_accept.lua -- H1/H2/H3 instrumentation for the X local-service connect.
|
|
--
|
|
-- Start with -debuglog: breakpoint printf output goes to ./debug.log (not
|
|
-- stdout). All addresses are bit addresses.
|
|
--
|
|
-- Probe points:
|
|
-- 3F9C2570 XOpenDisplay (Xlib client entry)
|
|
-- 3FD3B3E0 XParseDisplay: call of service attach by name
|
|
-- 3F70B060 service attach by name
|
|
-- 3F70DB80 service table lookup
|
|
-- 3F709840 service register (server side)
|
|
-- 3F34EC50 "xserver" registration callback = local accept/next-connection
|
|
-- 3F58B830 TCP connection accept (the real function, NOT 3F58B800)
|
|
-- 3F3F4680 authorisation check
|
|
-- 3F706A40 create_process
|
|
-- 3FD41B90 XCreateSimpleWindow
|
|
-- 3FD645B0 XMapWindow
|
|
-- 3FD9F6B0 XSync
|
|
--
|
|
-- Also snapshots frames on a trigger file: "<prefix> <count> <stride>".
|
|
local machine = manager.machine
|
|
local cpu = machine.devices[':maincpu']
|
|
local dbg = machine.debugger
|
|
local space = cpu.spaces['program']
|
|
if not dbg then print("trace: no debugger"); machine:exit(); return end
|
|
dbg.visible_cpu = cpu
|
|
|
|
local dir = os.getenv("MSG_DIR") or "."
|
|
local trigger = os.getenv("MSG_TRIGGER") or (dir .. "/trigger")
|
|
local screen = nil
|
|
for _, s in pairs(machine.screens) do screen = s; break end
|
|
|
|
local f = 0
|
|
local job = nil
|
|
local armed = false
|
|
local scanned = false
|
|
|
|
local function u32(a)
|
|
local ok, v = pcall(function() return space:read_u32(a) end)
|
|
if ok then return v end
|
|
return 0xdeadbeef
|
|
end
|
|
|
|
local function u8(a)
|
|
local ok, v = pcall(function() return space:read_u8(a) end)
|
|
if ok then return v end
|
|
return 0
|
|
end
|
|
|
|
local function name_at(p)
|
|
local t = {}
|
|
for k = 0, 15 do
|
|
local c = u8(p + k * 8)
|
|
if c == 0 then break end
|
|
if c >= 0x20 and c < 0x7f then
|
|
t[#t + 1] = string.char(c)
|
|
else
|
|
t[#t + 1] = "."
|
|
end
|
|
end
|
|
return table.concat(t)
|
|
end
|
|
|
|
local function cmd(s)
|
|
local ok, e = pcall(function() dbg:command(s) end)
|
|
if not ok then print("trace: command failed: " .. tostring(e)) end
|
|
end
|
|
|
|
local function consume_trigger()
|
|
local fh = io.open(trigger, "r")
|
|
if not fh then return end
|
|
local text = fh:read("*a") or ""
|
|
fh:close(); os.remove(trigger)
|
|
local prefix, count, stride = text:match("(%S+)%s+(%d+)%s+(%d+)")
|
|
if not prefix then return end
|
|
job = { prefix = prefix, left = tonumber(count), stride = tonumber(stride), next = f }
|
|
print(string.format("trace: frame %d snapshot job %s x%d every %d",
|
|
f, prefix, job.left, job.stride))
|
|
end
|
|
|
|
local function service_table()
|
|
local out = {}
|
|
for i = 0, 31 do
|
|
local p = u32(0x1e8d70 + i * 32)
|
|
if p ~= 0 and p ~= 0xdeadbeef then
|
|
out[#out + 1] = string.format("[%d]%08X:%s", i, p, name_at(p))
|
|
end
|
|
end
|
|
return table.concat(out, " ")
|
|
end
|
|
|
|
local function process_table()
|
|
local out = {}
|
|
local p = u32(0x1b3880)
|
|
local n = 0
|
|
while p ~= 0 and p ~= 0x1b3880 and p ~= 0xdeadbeef and n < 24 do
|
|
out[#out + 1] = string.format("%08X:%s", p, name_at(u32(p + 0xa0)))
|
|
p = u32(p)
|
|
n = n + 1
|
|
end
|
|
return table.concat(out, " ")
|
|
end
|
|
|
|
local function arm()
|
|
armed = true
|
|
-- client side
|
|
cmd('bpset 3F9C2570,,{ printf "TR OPEN entry pc=%08X a14=%08X arg=%08X\\n", pc, a14, d@(a14-32) ; g }')
|
|
cmd('bpset 3F9C27B0,,{ printf "TR DISP-ALLOC a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 3F9C3730,,{ printf "TR DISP-INIT a10=%08X\\n", a10 ; g }')
|
|
cmd('bpset 3F9C6870,,{ printf "TR DISP-RET a10=%08X a8=%08X\\n", a10, a8 ; g }')
|
|
cmd('bpset 3F9C6880,,{ printf "TR DISP-RET2 a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 01840450,,{ printf "TR XH STORE-DISP a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 01840570,,{ printf "TR XH STORE-ROOT a9=%08X\\n", a9 ; g }')
|
|
cmd('bpset 01840840,,{ printf "TR XH STORE-WIN a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 3FD3B3E0,,{ printf "TR PARSE-ATTACH pc=%08X name=%08X\\n", pc, d@(a14-32) ; g }')
|
|
cmd('bpset 3F70B060,,{ printf "TR ATTACH entry pc=%08X nameptr=%08X proc=%08X\\n", pc, d@(a14-32), d@0x1b3900 ; g }')
|
|
cmd('bpset 3F70BB40,,{ printf "TR ATTACH ret a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 3F70DB80,,{ printf "TR LOOKUP entry nameptr=%08X\\n", d@(a14-32) ; g }')
|
|
-- server side
|
|
cmd('bpset 3F709840,,{ printf "TR REGISTER entry nameptr=%08X proc=%08X\\n", d@(a14-32), d@0x1b3900 ; g }')
|
|
cmd('bpset 3F70AC20,,{ printf "TR REGISTER ret a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 3F34EC50,,{ printf "TR ACCEPT-CB entry proc=%08X\\n", d@0x1b3900 ; g }')
|
|
cmd('bpset 3F58B830,,{ printf "TR TCP-ACCEPT entry\\n" ; g }')
|
|
cmd('bpset 3F3F4680,,{ printf "TR AUTH entry\\n" ; g }')
|
|
-- local service: Xlib's setup write and the server's 12-byte setup read
|
|
cmd('bpset 3FD3D980,,{ printf "TR SETUP-WRITE pre len=%08X\\n", d@(a13+4288) ; dump ' .. dir .. '/setup_write.bin,a13+160,128 ; g }')
|
|
cmd('bpset 3F34F190,,{ printf "TR LOCAL-ACCEPT-WORKER entry conn=%08X\\n", a13 ; g }')
|
|
cmd('bpset 3F350170,,{ printf "TR READ12 call frame=%08X conn=%08X len=%08X\\n", a13, d@(a13), d@(a13+416) ; g }')
|
|
cmd('bpset 3F3501A0,,{ printf "TR READ12 ret a8=%08X\\n", a8 ; dump ' .. dir .. '/read12.bin,a13+96,64 ; g }')
|
|
cmd('bpset 3F350330,,{ printf "TR READ12 complete\\n" ; g }')
|
|
cmd('bpset 3F350BE0,,{ printf "TR SERVER-REPLY-WRITE call\\n" ; g }')
|
|
cmd('bpset 3F350C10,,{ printf "TR SERVER-REPLY-WRITE ret a8=%08X\\n", a8 ; g }')
|
|
cmd('bpset 3F9B2470,,{ printf "TR XREAD entry conn=%08X buf=%08X len=%08X\\n", a9, a10, a11 ; g }')
|
|
cmd('bpset 3F9B3020,,{ printf "TR XREAD ret a8=%08X expect=%08X\\n", a8, a11 ; g }')
|
|
cmd('bpset 3F70D150,d@0x1b3900==414F90,{ printf "TR SRV-WRITE entry pc=%08X\\n", pc ; g }')
|
|
cmd('bpset 3F70D990,d@0x1b3900==414F90,{ printf "TR SRV-WRITE ret a8=%08X\\n", a8 ; g }')
|
|
-- process creation
|
|
cmd('bpset 3F706A40,,{ printf "TR CREATE_PROC entry entryarg=%08X nameptr=%08X proc=%08X\\n", d@(a14-32), d@(a14-64), d@0x1b3900 ; g }')
|
|
-- window calls
|
|
cmd('bpset 3FD41B90,,{ printf "TR CreateSimpleWindow\\n" ; g }')
|
|
cmd('bpset 3FD645B0,,{ printf "TR MapWindow\\n" ; g }')
|
|
cmd('bpset 3FD9F6B0,,{ printf "TR XSync\\n" ; g }')
|
|
-- xhello progress is tracked through its relocated markers (0189xxxx);
|
|
-- the code offsets moved when the setf fix was inserted, so no bpsets here.
|
|
print("trace: armed")
|
|
end
|
|
|
|
emu.register_frame_done(function()
|
|
f = f + 1
|
|
if f == 1 and not armed then arm() end
|
|
consume_trigger()
|
|
if job and screen and f >= job.next then
|
|
local out = string.format("%s/%s_frame%05d.png", dir, job.prefix, f)
|
|
pcall(function() return screen:snapshot(out) end)
|
|
print(string.format("trace: frame %d snapshot %s", f, out))
|
|
job.left = job.left - 1
|
|
job.next = f + job.stride
|
|
if job.left <= 0 then job = nil end
|
|
end
|
|
if f % 100 == 0 then
|
|
print(string.format("trace: f=%d PC=%08X st=%d disp=%08X root=%08X win=%08X pcb=%08X tmdisp=%08X strlen=%d",
|
|
f, cpu.state['PC'].value, u32(0x01890140), u32(0x01890100),
|
|
u32(0x01890110), u32(0x01890120), u32(0x01860120), u32(0xb78e0), u32(0x01890160)))
|
|
local d = u32(0x01890100)
|
|
if d ~= 0x55555555 and d ~= 0 and d ~= 0xdeadbeef then
|
|
local s = u32(d + 0x480)
|
|
local r = u32(s + 0x40)
|
|
local td = u32(0xb78e0)
|
|
local ts = u32(td + 0x480)
|
|
local tr = u32(ts + 0x40)
|
|
print(string.format("trace: f=%d disp=%08X screens=%08X root=%08X | tm disp=%08X screens=%08X root=%08X",
|
|
f, d, s, r, td, ts, tr))
|
|
if u32(0x01890140) == 3 and not scanned then
|
|
scanned = true
|
|
local hits = {}
|
|
for a = 0x00400000, 0x01000000, 0x20 do
|
|
if u32(a) == ts then
|
|
hits[#hits + 1] = string.format("%08X", a)
|
|
if #hits >= 8 then break end
|
|
end
|
|
end
|
|
print("trace: heap words equal to tm screens: " .. table.concat(hits, ","))
|
|
end
|
|
end
|
|
end
|
|
if f % 500 == 0 then
|
|
print(string.format("trace: f=%d services %s", f, service_table()))
|
|
print(string.format("trace: f=%d procs %s", f, process_table()))
|
|
end
|
|
end, "vt1200-gui-trace")
|
|
|
|
if machine.debugger then machine.debugger.execution_state = 'run' end
|