Move reverse engineering artifacts under contrib
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Disassemble a PE .text range by RVA and annotate direct branch targets."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
|
||||
from capstone.x86 import X86_OP_IMM, X86_OP_MEM, X86_REG_RIP
|
||||
|
||||
from find_pe_string_xrefs import Section, parse_pe
|
||||
|
||||
|
||||
def find_section(sections: list[Section], rva: int) -> Section:
|
||||
for section in sections:
|
||||
if section.contains_rva(rva):
|
||||
return section
|
||||
raise ValueError(f"RVA 0x{rva:x} is not inside any PE section")
|
||||
|
||||
|
||||
def find_section_or_none(sections: list[Section], rva: int) -> Section | None:
|
||||
for section in sections:
|
||||
if section.contains_rva(rva):
|
||||
return section
|
||||
return None
|
||||
|
||||
|
||||
def parse_rva(text: str) -> int:
|
||||
return int(text, 16 if text.lower().startswith("0x") else 16)
|
||||
|
||||
|
||||
def format_memory_annotation(data: bytes, sections: list[Section], target_rva: int) -> str:
|
||||
section = find_section_or_none(sections, target_rva)
|
||||
if section is None:
|
||||
return f"rip_rva=0x{target_rva:x}"
|
||||
|
||||
offset = section.rva_to_offset(target_rva)
|
||||
if offset < 0 or offset >= len(data):
|
||||
return f"rip_rva=0x{target_rva:x}"
|
||||
|
||||
available = data[offset : min(len(data), offset + 8)]
|
||||
fields = [f"rip_rva=0x{target_rva:x}"]
|
||||
if len(available) >= 4:
|
||||
u32 = struct.unpack_from("<I", available)[0]
|
||||
f32 = struct.unpack_from("<f", available)[0]
|
||||
fields.append(f"u32=0x{u32:08x}")
|
||||
if math.isfinite(f32) and abs(f32) < 1.0e12:
|
||||
fields.append(f"f32={f32:.9g}")
|
||||
if len(available) >= 8:
|
||||
u64 = struct.unpack_from("<Q", available)[0]
|
||||
fields.append(f"u64=0x{u64:016x}")
|
||||
return " ".join(fields)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("pe", type=Path)
|
||||
parser.add_argument("rva", type=parse_rva)
|
||||
parser.add_argument("--size", type=lambda value: int(value, 0), default=0x200)
|
||||
parser.add_argument("--before", type=lambda value: int(value, 0), default=0)
|
||||
args = parser.parse_args()
|
||||
|
||||
data = args.pe.read_bytes()
|
||||
image_base, sections = parse_pe(data)
|
||||
start_rva = max(0, args.rva - args.before)
|
||||
section = find_section(sections, start_rva)
|
||||
end_rva = min(section.virtual_address + section.raw_size, start_rva + args.size)
|
||||
start_offset = section.rva_to_offset(start_rva)
|
||||
code = data[start_offset : start_offset + (end_rva - start_rva)]
|
||||
|
||||
disassembler = Cs(CS_ARCH_X86, CS_MODE_64)
|
||||
disassembler.detail = True
|
||||
|
||||
print(f"image_base=0x{image_base:x} section={section.name} range=0x{start_rva:x}..0x{end_rva:x}")
|
||||
for insn in disassembler.disasm(code, image_base + start_rva):
|
||||
rva = insn.address - image_base
|
||||
annotation = ""
|
||||
if insn.group(1) or insn.group(2): # jump/call
|
||||
for operand in insn.operands:
|
||||
if operand.type == X86_OP_IMM:
|
||||
annotation = f" ; target_rva=0x{operand.imm - image_base:x}"
|
||||
break
|
||||
elif insn.operands:
|
||||
memory_annotations = []
|
||||
for operand in insn.operands:
|
||||
if operand.type == X86_OP_MEM and operand.mem.base == X86_REG_RIP:
|
||||
target_rva = (insn.address + insn.size + operand.mem.disp) - image_base
|
||||
memory_annotations.append(format_memory_annotation(data, sections, target_rva))
|
||||
if memory_annotations:
|
||||
annotation = " ; " + " ; ".join(memory_annotations)
|
||||
marker = ">>" if rva == args.rva else " "
|
||||
print(f"{marker} 0x{rva:08x}: {insn.mnemonic:<8} {insn.op_str}{annotation}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user