mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-31 12:05:05 +02:00
Static: the VT1200 GUI is an X11 dix server in ROM plus Xlib clients (SERVER, SESSIONMGR/Terminal Manager, WM, VTE). Internal clients use the local service "xserver" via the display string "0::0"; TCP 6000 is a separate authorised listener. Function table and the 4-call hello-window sequence are in dwt/notes/GUI_APPS.md. Dynamic: a poke+jump payload proved the firmware ABI (strlen=4) and then called XOpenDisplay; it never returns (stage 2, display 0, X-server client watermark unchanged) and the rdebug service dies. Verdict: hard for a self-contained window; shared-RAM UI mutation (opcode 8) is the working route.
64 lines
2.4 KiB
Lua
64 lines
2.4 KiB
Lua
-- probe.lua -- frame snapshots + marker/PC telemetry for the GUI experiments.
|
|
--
|
|
-- MSG_TRIGGER=<path> "<prefix> <count> <stride>" -> PNG snapshots
|
|
-- MSG_DIR=<dir> where PNGs go
|
|
--
|
|
-- Also prints a marker line every 250 frames, so a wedged machine still leaves
|
|
-- evidence of how far the injected payload got:
|
|
-- 01850100 display 01850110 root 01850120 window
|
|
-- 01850130 sync 01850140 stage 01850150 strlen
|
|
local machine = manager.machine
|
|
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 cpu = machine.devices[':maincpu']
|
|
local space = cpu.spaces['program']
|
|
|
|
local frame = 0
|
|
local job = nil
|
|
|
|
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
|
|
print("probe: bad trigger " .. text)
|
|
return
|
|
end
|
|
job = { prefix = prefix, left = tonumber(count), stride = tonumber(stride), next = frame }
|
|
print(string.format("probe: frame %d job %s x%d every %d", frame,
|
|
prefix, job.left, job.stride))
|
|
end
|
|
|
|
local function u32(addr)
|
|
local ok, v = pcall(function() return space:read_u32(addr) end)
|
|
if ok then return v end
|
|
return 0xdeadbeef
|
|
end
|
|
|
|
emu.register_frame_done(function()
|
|
frame = frame + 1
|
|
consume_trigger()
|
|
if job and frame >= job.next and screen then
|
|
local out = string.format("%s/%s_frame%05d.png", dir, job.prefix, frame)
|
|
local ok, err = pcall(function() return screen:snapshot(out) end)
|
|
print(string.format("probe: frame %d %s ok=%s err=%s", frame,
|
|
out, tostring(ok), tostring(err)))
|
|
job.left = job.left - 1
|
|
job.next = frame + job.stride
|
|
if job.left <= 0 then job = nil end
|
|
end
|
|
if frame % 250 == 0 then
|
|
print(string.format("probe: frame %d PC=%08X disp=%08X root=%08X win=%08X sync=%08X stage=%d strlen=%d",
|
|
frame, cpu.state['PC'].value,
|
|
u32(0x01850100), u32(0x01850110), u32(0x01850120),
|
|
u32(0x01850130), u32(0x01850140), u32(0x01850150)))
|
|
end
|
|
end, "vt1200-gui-probe")
|
|
|
|
if machine.debugger then machine.debugger.execution_state = 'run' end
|