⬢ DragonFlyBSD Kernel Audit
DF-3017 / walkzones.py
← back to finding ↓ download raw
#!/usr/bin/env python3
"""gdbstub walker: deref CPU_prvspace[cpu] -> gd -> ZoneAry[zi] zones ->
head chunks; dump a stale devfs_node (Ndev) template and the 320-class
picture.  Writes /tmp/opencode/node_tpl.bin on the host.
usage: walkzones.py [zi]"""
import socket, time, sys

_sock = socket.create_connection(("127.0.0.1", 1235), timeout=10)
_sock.settimeout(2.5)
def rsp(cmd, timeout=2.5):
    pkt = b"$" + cmd + b"#%02x" % (sum(cmd) & 0xFF)
    _sock.sendall(pkt)
    buf = b""
    t0 = time.time()
    while time.time() - t0 < timeout:
        try:
            d = _sock.recv(65536)
        except socket.timeout:
            break
        if not d: raise RuntimeError("eof")
        buf += d
        if b"$" in buf and b"#" in buf[buf.index(b"$")+1:]:
            try: _sock.sendall(b"+")
            except Exception: pass
            i = buf.index(b"$"); j = buf.index(b"#", i+1)
            return buf[i+1:j]
    raise RuntimeError("timeout")
_sock.sendall(b"$qSupported#37"); time.sleep(0.3)
try: _sock.recv(65536)
except Exception: pass
_sock.sendall(b"+")
def rd(a, n):
    b = rsp(b"m%x,%x" % (a, n))
    if b.startswith(b"E"): raise RuntimeError(b.decode())
    return bytes.fromhex(b.decode())
def rq(a): return int.from_bytes(rd(a, 8), "little")
def u32(a): return int.from_bytes(rd(a, 4), "little")

CPU_PRVSPACE = 0xffffffff8113d620   # struct privatespace *[MAXCPU]
GD_SLAB = 0x2f00
ZI = int(sys.argv[1]) if len(sys.argv) > 1 else 22

tpl = None
for cpu in range(6):
    try:
        ps = rq(CPU_PRVSPACE + cpu * 8)
        gd = ps
        cpuid = u32(gd + 0x48)
        z = rq(gd + GD_SLAB + ZI * 16)
        print("cpu%d prvspace=%#x cpuid=%d ZoneAry[%d] head=%#x" %
              (cpu, ps, cpuid, ZI, z))
        n = 0
        while z and n < 4:
            print("  zone %#x zi=%d cpu=%d nfree=%d/%d chunk=%d lch=%#x rch=%#x" %
                  (z, u32(z+0x44), u32(z+4), u32(z+0x28), u32(z+0x2c),
                   u32(z+0x40), rq(z+0x50), rq(z+0x60)))
            c = rq(z + 0x50)
            k = 0
            while c and k < 6:
                try:
                    blk = rd(c, 0x100)
                except Exception:
                    break
                d_dev = int.from_bytes(blk[0:8], "little")
                mp = int.from_bytes(blk[8:16], "little")
                ntype = int.from_bytes(blk[0x38:0x3c], "little")
                uid = int.from_bytes(blk[0x84:0x88], "little")
                print("    chunk %#x d_dev=%#x mp=%#x type=%d uid=%d" %
                      (c, d_dev, mp, ntype, uid))
                if tpl is None and ntype == 4 and (mp >> 48) == 0xffff and uid == 1001:
                    tpl = blk[:232]
                c = int.from_bytes(blk[0:8], "little")  # freelist link at +0
                k += 1
            z = rq(z + 0x10)
            n += 1
    except Exception as e:
        print("cpu%d err %s" % (cpu, e))
    if tpl is not None:
        break
if tpl:
    open("/tmp/opencode/node_tpl.bin", "wb").write(tpl)
    mp = int.from_bytes(tpl[8:16], "little")
    print("[TPL] saved 232B node template, mp=%#x" % mp)
else:
    print("[TPL] none found")

# resume the VM (gdbstub attach halts it)
try:
    _sock.sendall(b"$c#63")
    time.sleep(0.2)
    _sock.close()
except Exception:
    pass