mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
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.
154 lines
4.3 KiB
NASM
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
|