Files
vt1200/dwt/gui/trace_accept.lua
T
SDD a94e99579f dwt/gui: injected X window works via create_process
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).
2026-10-18 14:28:58 +03:00

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