reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compute cryptographic and structural fingerprints for a file.
|
||||
|
||||
Always computes MD5/SHA-1/SHA-256 with stdlib ``hashlib``. If ``pefile`` is available and the
|
||||
input is a PE, also computes imphash and per-section SHA-256. Optional fuzzy hashes (ssdeep,
|
||||
TLSH) are included when their libraries are installed. It reads the file as inert bytes and
|
||||
never executes it.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def crypto_hashes(data: bytes) -> dict:
|
||||
return {
|
||||
"md5": hashlib.md5(data).hexdigest(),
|
||||
"sha1": hashlib.sha1(data).hexdigest(),
|
||||
"sha256": hashlib.sha256(data).hexdigest(),
|
||||
"size": len(data),
|
||||
}
|
||||
|
||||
|
||||
def pe_hashes(path: str) -> dict:
|
||||
try:
|
||||
import pefile # type: ignore
|
||||
except ImportError:
|
||||
return {}
|
||||
try:
|
||||
pe = pefile.PE(path, fast_load=True)
|
||||
pe.parse_data_directories(
|
||||
directories=[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_IMPORT"]])
|
||||
out = {"imphash": pe.get_imphash() or None}
|
||||
sections = {}
|
||||
for sec in pe.sections:
|
||||
name = sec.Name.rstrip(b"\x00").decode("latin-1")
|
||||
sections[name] = hashlib.sha256(sec.get_data()).hexdigest()
|
||||
out["section_sha256"] = sections
|
||||
return out
|
||||
except Exception as exc: # noqa: BLE001 - pefile raises many types
|
||||
return {"pe_error": str(exc)}
|
||||
|
||||
|
||||
def fuzzy_hashes(data: bytes) -> dict:
|
||||
out = {}
|
||||
try:
|
||||
import ssdeep # type: ignore
|
||||
out["ssdeep"] = ssdeep.hash(data)
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
import tlsh # type: ignore
|
||||
out["tlsh"] = tlsh.hash(data)
|
||||
except ImportError:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def fingerprint(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
result = crypto_hashes(data)
|
||||
is_pe = data[:2] == b"MZ"
|
||||
if is_pe:
|
||||
result.update(pe_hashes(path))
|
||||
result.update(fuzzy_hashes(data))
|
||||
return result
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Fingerprint a file")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("hash")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(fingerprint(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user