From e5530ca8f33b5b320d8897a7fb00b70190dbe339 Mon Sep 17 00:00:00 2001 From: OBarronCS <55004530+OBarronCS@users.noreply.github.com> Date: Sun, 13 Jul 2025 19:32:12 -0700 Subject: [PATCH] Correctly disable emulation on selected instructions (#3155) --- pwndbg/aglib/disasm/arch.py | 2 +- pwndbg/emu/emulator.py | 27 +++++----- tests/library/qemu-user/tests/test_aarch64.py | 52 +++++++++++++++++++ 3 files changed, 67 insertions(+), 14 deletions(-) diff --git a/pwndbg/aglib/disasm/arch.py b/pwndbg/aglib/disasm/arch.py index 32dbec633..10b25928b 100644 --- a/pwndbg/aglib/disasm/arch.py +++ b/pwndbg/aglib/disasm/arch.py @@ -330,7 +330,7 @@ class DisassemblyAssistant: ) # Execute the instruction - if jump_emu and None in jump_emu.single_step(): + if jump_emu and None in jump_emu.single_step(instruction=instruction): # This branch is taken if stepping the emulator failed jump_emu = None emu = None diff --git a/pwndbg/emu/emulator.py b/pwndbg/emu/emulator.py index 6d4aa7b04..f3473aac2 100644 --- a/pwndbg/emu/emulator.py +++ b/pwndbg/emu/emulator.py @@ -31,6 +31,7 @@ import pwndbg.integration import pwndbg.lib.memory import pwndbg.lib.regs from pwndbg import color +from pwndbg.aglib.disasm.instruction import PwndbgInstruction from pwndbg.color.syntax_highlight import syntax_highlight if pwndbg.dbg.is_gdblib_available(): @@ -193,7 +194,7 @@ ARM_BANNED_INSTRUCTIONS = { # We stop emulation when hitting these instructions, since they depend on co-processors or other information # unavailable to the emulator BANNED_INSTRUCTIONS = { - "mips": {C.mips.MIPS_INS_RDHWR}, + "mips": {C.mips.MIPS_INS_RDHWR, C.mips.MIPS_INS_ALIAS_RDHWR}, "arm": ARM_BANNED_INSTRUCTIONS, "armcm": ARM_BANNED_INSTRUCTIONS, "aarch64": {C.aarch64.AARCH64_INS_MRS}, @@ -827,7 +828,7 @@ class Emulator: ) self.until_syscall_address = address - def single_step(self, pc=None, check_instruction=False) -> Tuple[int, int]: + def single_step(self, pc=None, instruction: PwndbgInstruction | None = None) -> Tuple[int, int]: """Steps one instruction. Yields: @@ -844,23 +845,23 @@ class Emulator: pc = pc or self.pc - if check_instruction or DEBUG & DEBUG_EXECUTING: - insn = pwndbg.aglib.disasm.disassembly.one_raw(pc) + if instruction is None: + instruction = pwndbg.aglib.disasm.disassembly.one_raw(pc) # If we don't know how to disassemble, bail. - if insn is None: + if instruction is None: debug(DEBUG_EXECUTING, "Can't disassemble instruction at %#x", pc) return self.last_single_step_result - if insn.id in BANNED_INSTRUCTIONS.get(self.arch, {}): - debug(DEBUG_EXECUTING, "Hit illegal instruction at %#x", pc) - return self.last_single_step_result + if instruction.id in BANNED_INSTRUCTIONS.get(self.arch, {}): + debug(DEBUG_EXECUTING, "Hit illegal instruction at %#x", pc) + return self.last_single_step_result - debug( - DEBUG_EXECUTING, - "# Instruction: attempting to single-step at %#x: %s %s", - (pc, insn.mnemonic, insn.op_str), - ) + debug( + DEBUG_EXECUTING, + "# Instruction: attempting to single-step at %#x: %s %s", + (pc, instruction.mnemonic, instruction.op_str), + ) try: self.single_step_hook_hit_count = 0 diff --git a/tests/library/qemu-user/tests/test_aarch64.py b/tests/library/qemu-user/tests/test_aarch64.py index ca9d95d4b..a20428f76 100644 --- a/tests/library/qemu-user/tests/test_aarch64.py +++ b/tests/library/qemu-user/tests/test_aarch64.py @@ -721,6 +721,58 @@ def test_aarch64_shift_instructions(qemu_assembly_run): assert dis == expected +AARCH64_BANNED_INSTRUCTION = f""" +{AARCH64_PREAMBLE} +mrs x3, TPIDR_EL0 +add x2,x3,x4 +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop +""" + + +def test_aarch64_banned_instructions(qemu_assembly_run): + """ + Certain instructions cannot be emulated, since they depend on coprocessors that Unicorn doesn't support, + or system registers that we cannot reliably know the values of. + + This test ensures that we stop emulation in those cases. + + This means that the "add" instruction should show no annotation, + since our emulation should have stopped meaning we cannot reason about that instruction. + """ + qemu_assembly_run(AARCH64_BANNED_INSTRUCTION, "aarch64") + + dis = gdb.execute("context disasm", to_string=True) + dis = pwndbg.color.strip(dis) + + expected = ( + "LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA\n" + "─────────────────────[ DISASM / aarch64 / set emulate on ]──────────────────────\n" + " ► 0x1010120 <_start> mrs x3, TPIDR_EL0\n" + " 0x1010124 <_start+4> add x2, x3, x4\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 + + REFERENCE_BINARY = tests.get_binary("reference-binary.aarch64.out")