48 lines
1.6 KiB
Python
48 lines
1.6 KiB
Python
"""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}")
|