"""Scan a binary blob for MZ markers, validate as PE, dump each as separate file."""
import struct, sys, pathlib

blob_path = pathlib.Path(sys.argv[1])
out_dir = pathlib.Path(sys.argv[2])
out_dir.mkdir(parents=True, exist_ok=True)

data = blob_path.read_bytes()

def pe_total_size(buf, off):
    """Compute size of PE image at offset using section table."""
    # MZ at off; PE header offset at off+0x3C
    if off + 0x40 > len(buf):
        return None
    pe_off = struct.unpack_from("<I", buf, off + 0x3C)[0]
    pe_start = off + pe_off
    if pe_start + 24 > len(buf) or buf[pe_start:pe_start+4] != b"PE\x00\x00":
        return None
    num_sections = struct.unpack_from("<H", buf, pe_start + 6)[0]
    optional_header_size = struct.unpack_from("<H", buf, pe_start + 20)[0]
    sections_start = pe_start + 24 + optional_header_size
    last_end = sections_start + num_sections * 40
    for i in range(num_sections):
        s = sections_start + i * 40
        raw_size = struct.unpack_from("<I", buf, s + 16)[0]
        raw_off = struct.unpack_from("<I", buf, s + 20)[0]
        end = off + raw_off + raw_size
        last_end = max(last_end, end)
    return last_end - off

i = 0
n = 0
while True:
    j = data.find(b"MZ", i)
    if j == -1:
        break
    sz = pe_total_size(data, j)
    if sz and 4096 < sz < 5_000_000:
        out = out_dir / f"plain_{j:08x}.dll"
        out.write_bytes(data[j:j+sz])
        print(f"[ok] offset=0x{j:08x} size={sz} -> {out.name}")
        n += 1
        i = j + sz
    else:
        i = j + 2

print(f"\nExtracted {n} plain PE files into {out_dir}")
