Files
SDD 01923ecb69 dwt/tcpip: map the socket API and fetch a file over TFTP into RAM
The firmware's tftp_open hangs in its resolver from the rdebug process, so
the payload speaks RFC 783 over the firmware socket API: socket(2,2,0),
sendto(RRQ), recvfrom(DATA)/ACK into a malloc'd buffer, close. Verified
live: 600 B fetched from 192.168.99.1 into the malloc buffer, server RRQ
logged, and rdebug peek matches probe.bin byte-for-byte.

Notes: dwt/notes/TCPIP.md (function table, ABI, limits) and the
loader/transport hand-off in dwt/notes/LOADER_CROSS.md.
2026-10-18 15:24:34 +03:00

154 lines
4.3 KiB
NASM

; tftp_fetch.asm -- fetch a file over TFTP into a malloc'd buffer using the
; firmware's own client (dwt_tftp.c in ROM).
;
; Linked at bit 01870000h (byte 0x30E000) and executed with the rdebug `jump`
; call. The payload follows the firmware's stack-argument convention:
;
; MOVE A14,-*SP,1 ; reserve the slot the callee restores
; MOVE argN,*A14+,1 ; last argument first, upward via A14
; MOVE arg0,*A14+,1 ; first argument last
; CALLA target
;
; We point A14 at a fixed argument area and restore SP/A14 from the saved
; entry values after every call, so a payload bug cannot walk the caller's
; stack. a9 and a11 are callee-saved by tftp_open/tftp_read/tftp_close and
; hold the fd and the current write pointer across calls.
;
; Firmware entry points (bit addresses; Ghidra = bit >> 3):
; malloc 3F701230h (Ghidra 07EE0246) malloc(bytes) -> bit pointer
; tftp_open 3F5EBC40h (Ghidra 07EBD788) tftp_open(host, filename) -> fd
; tftp_read 3F5ED230h (Ghidra 07EBDA46) tftp_read(fd, buf, bufsize) -> n
; tftp_close 3F5EC860h (Ghidra 07EBD90C) tftp_close(fd)
;
; tftp_read returns the DATA payload length, and 0 once a short (final) block
; has been consumed. tftp_close sends TFTP ERROR 3 if the last block read was
; a full 512 bytes, so the loop must read until it gets 0.
;
; Evidence cells (32-bit, bit addresses; peeked over rdebug):
; 01871000 stage (1 malloc, 2 open, 3 reading, 4 closed, 0FFh failed)
; 01871020 fd
; 01871040 buffer pointer (returned by malloc; a BIT address)
; 01871060 total bytes fetched
; 01871080 last tftp_read return
; 018710A0 marker: 05A5A5A5h when the fetch completed
; 018710C0 tftp_close return scratch
; 01871200 entry SP
; 01871220 entry A14
;
; The fetched bytes are at [01871040] and are compared with the served file.
; The marker cells are deliberately above 01870AD0 (the end of this image):
; writing them into the code was the first, silent failure of this payload.
.text
_start:
setf 16, 0, 0
setf 32, 0, 1
mmtm sp, a0,a1,a2,a3,a4,a5,a6,a7
move sp, @01871200h, 1 ; entry SP
move a14, @01871220h, 1 ; entry A14
movi 1, a0, L
move a0, @01871000h, 1 ; stage = 1
; ---- buf = malloc(010000h) ------------------------------------
move a14, -*sp, 1
movi 01878000h, a14, L ; argument area
movi 010000h, a0, L ; 65536 bytes
move a0, *a14+, 1
calla 3F701230h ; malloc
move @01871200h, sp, 1
move @01871220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @01871040h, 1 ; buffer pointer (bit address)
move a8, a11
move a8, a8
jreq fail
movi 2, a0, L
move a0, @01871000h, 1 ; stage = 2
; ---- fd = tftp_open("192.168.99.1", "dwt/tcpip/probe.bin") -----
movi 01878000h, a14, L ; argument area
movi FNAME, a0, L
move a0, *a14+, 1 ; arg1 = filename
movi HOST, a0, L
move a0, *a14+, 1 ; arg0 = host
calla 3F5EBC40h ; tftp_open
move @01871200h, sp, 1
move @01871220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @01871020h, 1 ; fd
move a8, a9
move a8, a8
jrlt fail
; ---- while ((n = tftp_read(fd, p, 010000h)) > 0) p += n --------
movi 3, a0, L
move a0, @01871000h, 1 ; stage = 3
read_loop:
movi 01878000h, a14, L
movi 010000h, a0, L
move a0, *a14+, 1 ; arg2 = bufsize
move a11, *a14+, 1 ; arg1 = buffer
move a9, *a14+, 1 ; arg0 = fd
calla 3F5ED230h ; tftp_read
move @01871200h, sp, 1
move @01871220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @01871080h, 1 ; last return
move a8, a8
jrle close_it
add a8, a11 ; advance the write pointer
move @01871060h, a1, 1
add a8, a1
move a1, @01871060h, 1 ; total += n
jruc read_loop
close_it:
movi 4, a0, L
move a0, @01871000h, 1 ; stage = 4
movi 01878000h, a14, L
move a9, *a14+, 1 ; arg0 = fd
calla 3F5EC860h ; tftp_close
move @01871200h, sp, 1
move @01871220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @018710C0h, 1
movi 05A5A5A5h, a0, L
move a0, @018710A0h, 1 ; DONE marker
sub a8, a8 ; return 0
jruc epilogue
fail:
movi 0FFh, a0, L
move a0, @01871000h, 1
movi 0FFFFFFFFh, a8, L
epilogue:
mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7
rets
HOST:
.word 03931h ; "192.168.99.1"
.word 02E32h
.word 03631h
.word 02E38h
.word 03939h
.word 0312Eh
.word 00000h
FNAME:
.word 07764h ; "dwt/tcpip/probe.bin"
.word 02F74h
.word 06374h
.word 06970h
.word 02F70h
.word 07270h
.word 0626Fh
.word 02E65h
.word 06962h
.word 0006Eh
.end