diff --git a/pwndbg/commands/patch.py b/pwndbg/commands/patch.py index 0f3c38567..fd92770f4 100644 --- a/pwndbg/commands/patch.py +++ b/pwndbg/commands/patch.py @@ -4,20 +4,19 @@ import argparse from typing import Dict from typing import Tuple -from pwnlib.asm import asm -from pwnlib.asm import disasm - +import pwndbg.aglib.asm import pwndbg.aglib.memory import pwndbg.color.context import pwndbg.color.memory import pwndbg.color.syntax_highlight import pwndbg.commands import pwndbg.lib.cache +from pwndbg.aglib.disasm.disassembly import get_disassembler from pwndbg.color import message from pwndbg.commands import CommandCategory # Keep old patches made so we can revert them -patches: Dict[int, Tuple[bytearray, bytearray]] = {} +patches: Dict[int, Tuple[bytes, bytes]] = {} parser = argparse.ArgumentParser(description="Patches given instruction with given code or bytes.") @@ -29,7 +28,7 @@ parser.add_argument("-q", "--quiet", action="store_true", help="don't print anyt @pwndbg.commands.Command(parser, category=CommandCategory.MISC) @pwndbg.commands.OnlyWhenRunning def patch(address: int, ins: str, quiet: bool) -> None: - new_mem = asm(ins) + new_mem = pwndbg.aglib.asm.asm(ins) old_mem = pwndbg.aglib.memory.read(address, len(new_mem)) @@ -82,8 +81,14 @@ def patch_list() -> None: print(pwndbg.color.context.banner("Patches:")) for addr, (old, new) in patches.items(): - old_insns = disasm(old, byte=False, offset=False) - new_insns = disasm(new, byte=False, offset=False) + cs = get_disassembler(pwndbg.aglib.arch.get_capstone_constants(addr)) + + old_insns = "\n".join( + [f"{x.mnemonic} {x.op_str}".strip() for x in cs.disasm(old, offset=addr)] + ) + new_insns = "\n".join( + [f"{x.mnemonic} {x.op_str}".strip() for x in cs.disasm(new, offset=addr)] + ) colored_addr = pwndbg.color.memory.get(addr) diff --git a/tests/library/qemu_user/tests/test_aarch64.py b/tests/library/qemu_user/tests/test_aarch64.py index aa144cc76..fdb8b864a 100644 --- a/tests/library/qemu_user/tests/test_aarch64.py +++ b/tests/library/qemu_user/tests/test_aarch64.py @@ -1,6 +1,7 @@ from __future__ import annotations import gdb +import pytest from capstone.aarch64_const import AARCH64_INS_BL import pwndbg.aglib.disasm.disassembly @@ -774,6 +775,68 @@ def test_aarch64_banned_instructions(qemu_assembly_run): assert dis == expected +AARCH64_CROSS_ARCH_PATCH_INSTRUCTIONS = f""" +{AARCH64_PREAMBLE} +{AARCH64_GRACEFUL_EXIT} +""" + + +@pytest.mark.xfail( + reason="qemu-user 8.2.2 (version on Ubuntu24.04) does not support GDB writing to memory. This succeeds on newer versions of qemu. Remove the xfail when qemu is upgraded." +) +def test_aarch64_cross_arch_patch(qemu_assembly_run): + """ + Make sure the `patch` command, which delegates to Zig to compile, works + """ + qemu_assembly_run(AARCH64_CROSS_ARCH_PATCH_INSTRUCTIONS, "aarch64") + + dis = gdb.execute("context disasm", to_string=True) + dis = pwndbg.color.strip(dis) + + expected_before = ( + "LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n" + "─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n" + " ► 0x1010120 <_start> mov x0, #0 X0 => 0\n" + " 0x1010124 <_start+4> mov x8, #0x5d X8 => 0x5d\n" + " 0x1010128 <_start+8> svc #0 \n" + " 0x101012c <_start+12> nop \n" + " 0x1010130 <_start+16> nop \n" + " 0x1010134 <_start+20> nop \n" + " 0x1010138 <_start+24> nop \n" + " 0x101013c <_start+28> nop \n" + " 0x1010140 <_start+32> nop \n" + " 0x1010144 <_start+36> nop \n" + " 0x1010148 <_start+40> nop \n" + "────────────────────────────────────────────────────────────────────────────────\n" + ) + + assert dis == expected_before + + gdb.execute("patch $pc 'nop; nop; nop'") + + dis = gdb.execute("context disasm", to_string=True) + dis = pwndbg.color.strip(dis) + + expected_after = ( + "LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n" + "─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n" + " ► 0x1010120 <_start> nop \n" + " 0x1010124 <_start+4> nop \n" + " 0x1010128 <_start+8> nop \n" + " 0x101012c <_start+12> nop \n" + " 0x1010130 <_start+16> nop \n" + " 0x1010134 <_start+20> nop \n" + " 0x1010138 <_start+24> nop \n" + " 0x101013c <_start+28> nop \n" + " 0x1010140 <_start+32> nop \n" + " 0x1010144 <_start+36> nop \n" + " 0x1010148 <_start+40> nop \n" + "────────────────────────────────────────────────────────────────────────────────\n" + ) + + assert dis == expected_after + + REFERENCE_BINARY = get_binary("reference-binary.aarch64.out")