; 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