#!/usr/bin/env python3 import argparse import ctypes import pathlib import sys REPOSITORY_ROOT = pathlib.Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPOSITORY_ROOT / "capstone_local")) from capstone import CS_ARCH_ARM, CS_ARCH_PPC, CS_MODE_32, CS_MODE_ARM, CS_MODE_BIG_ENDIAN, Cs PROCESS_VM_READ = 0x0010 PROCESS_QUERY_INFORMATION = 0x0400 def read_process_memory(process_id: int, address: int, length: int) -> bytes: kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) kernel32.OpenProcess.argtypes = [ctypes.c_uint32, ctypes.c_bool, ctypes.c_uint32] kernel32.OpenProcess.restype = ctypes.c_void_p kernel32.ReadProcessMemory.argtypes = [ ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t), ] kernel32.ReadProcessMemory.restype = ctypes.c_bool kernel32.CloseHandle.argtypes = [ctypes.c_void_p] kernel32.CloseHandle.restype = ctypes.c_bool process = kernel32.OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, False, process_id) if not process: raise ctypes.WinError(ctypes.get_last_error()) try: buffer = ctypes.create_string_buffer(length) bytes_read = ctypes.c_size_t() if not kernel32.ReadProcessMemory( process, ctypes.c_void_p(address), buffer, length, ctypes.byref(bytes_read) ): raise ctypes.WinError(ctypes.get_last_error()) if bytes_read.value != length: raise RuntimeError(f"short read: requested {length} bytes, got {bytes_read.value}") return buffer.raw finally: kernel32.CloseHandle(process) def main() -> None: parser = argparse.ArgumentParser( description="Disassemble big-endian 32-bit PowerPC code from a live Windows process." ) parser.add_argument("process_id", type=int) parser.add_argument("host_address", type=lambda value: int(value, 0)) parser.add_argument("guest_address", type=lambda value: int(value, 0)) parser.add_argument("length", type=lambda value: int(value, 0)) parser.add_argument("--arch", choices=("ppc", "arm"), default="ppc") args = parser.parse_args() code = read_process_memory(args.process_id, args.host_address, args.length) if args.arch == "arm": disassembler = Cs(CS_ARCH_ARM, CS_MODE_ARM | CS_MODE_BIG_ENDIAN) else: disassembler = Cs(CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN) for instruction in disassembler.disasm(code, args.guest_address): operands = f" {instruction.op_str}" if instruction.op_str else "" print(f"{instruction.address:08x}: {instruction.bytes.hex(' '):11} " f"{instruction.mnemonic}{operands}") if __name__ == "__main__": main()