⬢ DragonFlyBSD Kernel Audit
DF-2362 / analyze2362b.py
← back to finding ↓ download raw
#!/usr/bin/env python3 -u
# analyze2362b.py -- properly reconstruct packet round-trips from the raw log.
import re, sys

LOG = sys.argv[1] if len(sys.argv) > 1 else "/tmp/opencode/df2362-raw.log"
raw = open(LOG, "rb").read().decode(errors="replace")
events = []
for m in re.finditer(r"<< |>> ", raw):
    events.append((m.start(), m.end()))
chunks = []
for i, (s, e) in enumerate(events):
    end = events[i + 1][0] if i + 1 < len(events) else len(raw)
    prefix = raw[s:e]
    chunks.append(("in" if prefix == "<< " else "out", raw[e:end]))

def frames(buf):
    out = []
    while True:
        i = buf.find("$")
        if i < 0:
            return out, buf
        m = re.match(r"\$([^#]*)#([0-9a-fA-F]{2})", buf[i:])
        if not m or len(m.group(0)) > len(buf[i:]):
            return out, buf[i:]
        out.append(m.group(1))
        buf = buf[i + len(m.group(0)):]
        # consume a leading '+'/'-' in remainder lazily

rounds = []
recvbuf = ""
pending = None
for d, t in chunks:
    if d == "out":
        if t.startswith("$"):
            m = re.match(r"\$([^#]*)#([0-9a-fA-F]{2})", t)
            if m:
                pending = m.group(1)
        elif t.startswith("+") and pending is not None:
            pass
    else:
        recvbuf += t
        # strip acks
        while recvbuf[:1] in ("+", "-"):
            recvbuf = recvbuf[1:]
        fs, recvbuf = frames(recvbuf)
        for f in fs:
            rounds.append((pending, f))
            pending = None

print("round trips: %d" % len(rounds))
def hexv(c):
    ch = chr(c)
    if ch in "0123456789": return ord(ch) - 48
    if ch in "abcdef": return ord(ch) - 87
    if ch in "ABCDEF": return ord(ch) - 55
    return -1

ROUT = 0xffffffff817a6f20
RIN  = 0xffffffff817a70c0

# locate the big m reads and verify contents
for idx, (s, r) in enumerate(rounds):
    if s and s.startswith("m") and "," in s:
        addr = int(s[1:].split(",")[0], 16)
        n = int(s.split(",")[1], 16)
        if r and len(r) == 2 * n:
            b = bytes.fromhex(r)
            # is this a read of the ROUT tail?
            if addr == ROUT + 400 and n == 0xc0:
                print("[%02d] verify-read of gap: %s" % (idx, b[:40].decode(errors="replace")))

# ---- T1 forensic: find the m<src>,108 overflow
for idx, (s, r) in enumerate(rounds):
    if s and s.endswith(",108") and s.startswith("m") and r and len(r) == 528:
        ov = r
        # the next read(s)
        print("\nT1 overflow reply: len %d, chars[400:416]=%r" % (len(ov), ov[400:416]))
        for j in range(idx + 1, min(idx + 3, len(rounds))):
            s2, r2 = rounds[j]
            if s2 and s2.startswith("m") and r2:
                a2 = int(s2[1:].split(",")[0], 16)
                n2 = int(s2.split(",")[1], 16)
                b2 = bytes.fromhex(r2)
                if a2 == ROUT + 400:
                    print("  first verify read [%d] (%d bytes):" % (j, n2))
                    print("    bytes 0..15  (gap):     %r" % b2[:16])
                    print("    expected ov[400:416]:   %r" % ov[400:416].encode())
                    print("    match: %s" % (b2[:16] == ov[400:416].encode()))
                    print("    bytes 38..111 (Rin22..):%r" % b2[38:112][:20])
                    print("    expected ov[438:458]:   %r" % ov[438:458].encode())
                    print("    match40: %s" % (b2[38:112] == ov[438:528].encode()[:74]))
                    print("    byte@128 (Rout+528):    %02x" % b2[128])
        break

# ---- T3 forensic
print("\n=== T3 ===")
for idx, (s, r) in enumerate(rounds):
    if s and s.startswith("M") and ",fa:" in s and r == "OK":
        data = s.split(":", 1)[1]
        datb = bytes.fromhex(data)
        # following reads
        reads = []
        for j in range(idx + 1, min(idx + 8, len(rounds))):
            s2, r2 = rounds[j]
            if s2 and s2.startswith("m") and r2 and "," in s2:
                a2 = int(s2[1:].split(",")[0], 16)
                n2 = int(s2.split(",")[1], 16)
                if len(r2) == 2 * n2:
                    reads.append((a2, n2, bytes.fromhex(r2)))
        tgt = [x for x in reads if x[0] == 0xffffffff817a6fa0]
        rin = [x for x in reads if x[0] == RIN + 0x180]
        if tgt and rin:
            got = tgt[0][2]
            post = rin[0][2]
            src = bytearray([0]) + post[14:16] + post[16:]
            pred = bytearray(datb)
            i = 0
            while len(pred) < 250 and 2 * i + 1 < len(src):
                pred.append(((hexv(src[2*i]) << 4) + hexv(src[2*i+1])) & 0xff)
                i += 1
            pred = bytes(pred[:250])
            print("pkt3 data %dB, target got %dB" % (len(datb), len(got)))
            print("src head: %s" % src[:12].hex())
            ok = True
            for k in range(min(len(got), len(pred))):
                if got[k] != pred[k]:
                    print("diverge at %d: got %02x pred %02x" % (k, got[k], pred[k]))
                    print(" got  : %s" % got[max(0,k-6):k+10].hex())
                    print(" pred : %s" % pred[max(0,k-6):k+10].hex())
                    kb = k - 188            # index into src bytes
                    print(" src around byte %d: %s" % (kb, src[max(0,kb-2):kb+6].hex()))
                    ok = False
                    break
            if ok and len(got) == len(pred):
                print("FULL MATCH")
        break