reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Identify a .NET/managed assembly and surface basic CLR metadata signals.
|
||||
|
||||
Parses the PE far enough to detect the COM descriptor (CLR) data directory and searches for
|
||||
the ``BSJB`` metadata-root signature. Static parsing only; the sample is never executed.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def identify(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
result = {"managed": False, "bsjb_metadata": False, "notes": []}
|
||||
|
||||
if data[:2] != b"MZ":
|
||||
result["notes"].append("not a PE (no MZ)")
|
||||
return result
|
||||
|
||||
e_lfanew = struct.unpack_from("<I", data, 0x3C)[0]
|
||||
if data[e_lfanew:e_lfanew + 4] != b"PE\x00\x00":
|
||||
result["notes"].append("no PE signature")
|
||||
return result
|
||||
|
||||
coff = e_lfanew + 4
|
||||
opt = coff + 20
|
||||
magic = struct.unpack_from("<H", data, opt)[0]
|
||||
pe32_plus = magic == 0x20B
|
||||
# data directories start after the standard+windows optional header fields
|
||||
dd_offset = opt + (112 if pe32_plus else 96)
|
||||
# COM descriptor is data directory index 14
|
||||
com_dir = dd_offset + 14 * 8
|
||||
rva, size = struct.unpack_from("<II", data, com_dir)
|
||||
if rva and size:
|
||||
result["managed"] = True
|
||||
result["notes"].append("CLR runtime header (COM descriptor) present")
|
||||
|
||||
if b"BSJB" in data:
|
||||
result["bsjb_metadata"] = True
|
||||
result["notes"].append("BSJB metadata root signature found")
|
||||
|
||||
if b"mscoree.dll" in data or b"_CorExeMain" in data:
|
||||
result["notes"].append("mscoree / _CorExeMain reference")
|
||||
|
||||
result["managed"] = result["managed"] or result["bsjb_metadata"]
|
||||
return result
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Identify managed .NET assembly")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("identify")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(identify(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user