Enable cross-architecture instruction patching (#3419)

* Enable cross-architecture instruction patching. Use Zig with the patch command, Capstone to disassemble in patch-list.

* remove old comments

* update comment

* rename variable

* Replace pc with . Mark the test as xfail given the qemu-version in CI doesn't support GDB writing to process memory
pull/3422/head^2
OBarronCS 3 weeks ago committed by GitHub
parent aae7ce5324
commit fcaebb752e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -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)

@ -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 <SYS_exit>\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")

Loading…
Cancel
Save