Correctly disable emulation on selected instructions (#3155)

pull/3157/head
OBarronCS 5 months ago committed by GitHub
parent c189974445
commit e5530ca8f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

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

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

Loading…
Cancel
Save