dwt/gui — injected X window (working)
Investigation of the VT1200 GUI architecture and whether an injected payload can
create its own window. Read dwt/notes/GUI_APPS.md first (especially the new
§10); this directory is the reproducible part.
Result: yes. The payload must run in a process created by the firmware's own
create_process (not in the rdebug dispatcher), and it must keep the Display *
in a callee-saved register rather than reloading it from a RAM cell. The final
breakpoint-free run shows the window at frame 2838:
evidence/xhello_window_frame2838.png (black 400x200 window at 100,100, framed
by the WM).
| file | what it is |
|---|---|
xhello.asm |
the window payload: ABI check (strlen("0::0")==4), XOpenDisplay, root lookup, XCreateSimpleWindow, XMapWindow, XSync; Display in a11, window in a10, setf re-established after every call |
xlaunch.asm |
128-byte launcher: create_process(xhello, name, 0x6000, 1, 0) — the Telnet handler's call shape |
build.sh |
TI SDK (GSPA/GSPLNK under DOSBox) wrapper; writes build/, scratch in work/ |
probe_win.lua |
breakpoint-free final probe: marker log + PNG snapshots on a trigger file |
probe.lua / probe2.lua / probe3.lua |
earlier probes (marker/PC telemetry; probe3 targets the mis-identified 3F58B800) |
trace_accept.lua |
instrumented run: service attach/lookup, server accept callback, 12-byte setup read, setup-reply write, Xlib read, process create |
build/xhello.bin |
assembled payload, linked at bit 0x01840000 |
build/xlaunch.bin |
assembled launcher, linked at bit 0x01860000 |
evidence/xhello_window_frame2838.png |
the window, after a run with no breakpoints |
evidence/transport_evidence.txt |
quoted accept/read/reply lines from the instrumented runs |
run12/debug.log |
full instrumented trace of the successful fresh-process run |
run13/ |
final run: mame.log, snapshots, private cfg/nvram |
run/ |
the earlier failing experiment (rdebug context, stage 2) |
Build
./dwt/gui/build.sh dwt/gui/xhello.asm 01840000 # 390 bytes
./dwt/gui/build.sh dwt/gui/xlaunch.asm 01860000 # 128 bytes
Run
The payload needs the OS, i.e. VT1200_SKIP_LEVEL1=1 (documented divergence),
a BOOTP/TFTP server and the LANCE attached. MAME's network device is enabled by
the cfg file, not the command line — without
<network><device tag=":lance" interface="0" .../></network> in
vt1200.cfg the taptun provider is never opened (the tap stays NO-CARRIER).
RUN=$PWD/dwt/gui/run13
mkdir -p $RUN/cfg $RUN/nvram
cp harness/mame/cfg-net/vt1200.cfg $RUN/cfg/vt1200.cfg
sudo python3 -u harness/serve.py 192.168.99.1 --bootp &
VT1200_SKIP_LEVEL1=1 MSG_TRIGGER=$RUN/trigger MSG_DIR=$RUN \
/home/arti/code/k-space/mame0289/vt1200 vt1200 \
-rompath harness/mame/roms -sound none -skip_gameinfo \
-networkprovider taptun -video none -nothrottle \
-cfg_directory $RUN/cfg -nvram_directory $RUN/nvram \
-debug -debugger none -autoboot_script $PWD/dwt/gui/probe_win.lua \
-autoboot_delay 0
# once 192.168.99.2:771 answers (rdebug creates a fresh process per connection)
python3 dwt/visual/inject.py --host 192.168.99.2 \
--poke-file 0x01840000 --file dwt/gui/build/xhello.bin
python3 dwt/visual/inject.py --host 192.168.99.2 \
--poke-file 0x01860000 --file dwt/gui/build/xlaunch.bin \
--jump 0x01860000 --jump-wait 8
# snapshots: write "<prefix> <count> <stride>" into $RUN/trigger
echo 'xhello 6 60' > $RUN/trigger
Markers are diagnostic cells at bit 0x01890100 (display), 01890110 (root),
01890120 (window), 01890130 (sync), 01890140 (stage), 01890160
(strlen). In the successful run the stage reaches 6 and strlen is 4.
Do not call Xlib in the rdebug process: the service attach and the server's accept callback succeed, but the server never reads the 12-byte setup, and Xlib waits forever. See §10 of the note for the trace and the remaining open question.