Move reverse engineering artifacts under contrib
This commit is contained in:
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Find simple direct x64 call/jump xrefs to code RVAs in a PE file."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
from find_pe_string_xrefs import Section, parse_pe
|
||||
|
||||
|
||||
def iter_rel32_xrefs(data: bytes, section: Section, image_base: int, target_vas: set[int]) -> dict[int, list[tuple[int, str]]]:
|
||||
hits: dict[int, list[tuple[int, str]]] = {target_va: [] for target_va in target_vas}
|
||||
start = section.raw_offset
|
||||
end = min(len(data), section.raw_offset + section.raw_size)
|
||||
|
||||
opcodes = {
|
||||
0xE8: "call",
|
||||
0xE9: "jmp",
|
||||
}
|
||||
for offset in range(start, max(start, end - 5)):
|
||||
opcode = data[offset]
|
||||
mnemonic = opcodes.get(opcode)
|
||||
if mnemonic is None:
|
||||
continue
|
||||
|
||||
disp = struct.unpack_from("<i", data, offset + 1)[0]
|
||||
instr_rva = section.virtual_address + (offset - section.raw_offset)
|
||||
target_va = image_base + instr_rva + 5 + disp
|
||||
if target_va in target_vas:
|
||||
hits[target_va].append((instr_rva, mnemonic))
|
||||
|
||||
# Common tail-call form: ff 25 <riprel32> imports/thunks are not resolved
|
||||
# here. This helper intentionally stays small and deterministic.
|
||||
return hits
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("pe", type=Path)
|
||||
parser.add_argument("targets", nargs="+", help="name=rva_hex pairs")
|
||||
args = parser.parse_args()
|
||||
|
||||
data = args.pe.read_bytes()
|
||||
image_base, sections = parse_pe(data)
|
||||
code_sections = [section for section in sections if section.is_code]
|
||||
|
||||
targets: list[tuple[str, int, int]] = []
|
||||
target_vas: set[int] = set()
|
||||
for item in args.targets:
|
||||
name, _, rva_text = item.partition("=")
|
||||
if not name or not rva_text:
|
||||
raise ValueError(f"target must be name=rva_hex: {item}")
|
||||
target_rva = int(rva_text, 16)
|
||||
target_va = image_base + target_rva
|
||||
targets.append((name, target_rva, target_va))
|
||||
target_vas.add(target_va)
|
||||
|
||||
xrefs: dict[int, list[tuple[int, str]]] = {target_va: [] for target_va in target_vas}
|
||||
for section in code_sections:
|
||||
section_hits = iter_rel32_xrefs(data, section, image_base, target_vas)
|
||||
for target_va, hits in section_hits.items():
|
||||
xrefs[target_va].extend(hits)
|
||||
|
||||
print(f"image_base=0x{image_base:x}")
|
||||
for name, target_rva, target_va in targets:
|
||||
all_hits = sorted(xrefs[target_va])
|
||||
print(f"{name}: target_rva=0x{target_rva:x} direct_xrefs={len(all_hits)}")
|
||||
for instr_rva, mnemonic in all_hits[:64]:
|
||||
print(f" {mnemonic}_rva=0x{instr_rva:x}")
|
||||
if len(all_hits) > 64:
|
||||
print(f" ... {len(all_hits) - 64} more")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user