Cleanup emulator register reads (#3489)

pull/3111/merge
OBarronCS 2 days ago committed by GitHub
parent d3bf95f3ba
commit 98cd6d9add
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -212,10 +212,10 @@ class DisassemblyAssistant:
# Ensure emulator's program counter is at the correct location. # Ensure emulator's program counter is at the correct location.
# This occurs very rarely - observed sometimes when the remote is stalling, ctrl-c, and for some reason emulator returns PC=0. # This occurs very rarely - observed sometimes when the remote is stalling, ctrl-c, and for some reason emulator returns PC=0.
if emu: if emu:
if emu.pc != instruction.address: if emu.pc() != instruction.address:
if DEBUG_ENHANCEMENT: if DEBUG_ENHANCEMENT:
print( print(
f"Program counter and emu.pc do not line up: {hex(pwndbg.aglib.regs.pc)=} {hex(emu.pc)=}" f"Program counter and emu.pc do not line up: {hex(pwndbg.aglib.regs.pc)=} {hex(emu.pc())=}"
) )
emu = jump_emu = None emu = jump_emu = None
@ -716,7 +716,7 @@ class DisassemblyAssistant:
# 1. Only use it to determine non-call's (`nexti` should step over calls) # 1. Only use it to determine non-call's (`nexti` should step over calls)
# 2. Make sure we haven't manually set .condition to False (which should override the emulators prediction) # 2. Make sure we haven't manually set .condition to False (which should override the emulators prediction)
if not instruction.call_like and instruction.condition != InstructionCondition.FALSE: if not instruction.call_like and instruction.condition != InstructionCondition.FALSE:
next_addr = jump_emu.pc next_addr = jump_emu.pc()
# Handle edge case - if the target happens to be the next address in memory and it's a jump, we need this variable # Handle edge case - if the target happens to be the next address in memory and it's a jump, we need this variable
# so the disasm output is accurate. # so the disasm output is accurate.

@ -466,7 +466,7 @@ def near(
# Upon execution the previous instruction, the Thumb mode bit may have changed. # Upon execution the previous instruction, the Thumb mode bit may have changed.
# This means we know whether the next instruction executed will be Thumb or not. # This means we know whether the next instruction executed will be Thumb or not.
# This returns None in the case the Thumb bit is not relevent. # This returns None in the case the Thumb bit is not relevent.
last_emulated_thumb_bit_value = emulated_arm_mode_cache[emu.pc] = ( last_emulated_thumb_bit_value = emulated_arm_mode_cache[emu.pc()] = (
emu.read_thumb_bit() emu.read_thumb_bit()
) )

@ -273,7 +273,7 @@ class Emulator:
debug(DEBUG_INIT, "# Setting TLB mode to virtual") debug(DEBUG_INIT, "# Setting TLB mode to virtual")
self.uc.ctl_set_tlb_mode(U.UC_TLB_VIRTUAL) # type: ignore[attr-defined] self.uc.ctl_set_tlb_mode(U.UC_TLB_VIRTUAL) # type: ignore[attr-defined]
self.regs: pwndbg.lib.regs.RegisterSet = pwndbg.aglib.regs.current self.reg_set: pwndbg.lib.regs.RegisterSet = pwndbg.aglib.regs.current
# Whether the emulator is allowed to emulate instructions # Whether the emulator is allowed to emulate instructions
# There are cases when the emulator is incorrect or we want to disable it for certain instruction types, # There are cases when the emulator is incorrect or we want to disable it for certain instruction types,
@ -292,7 +292,7 @@ class Emulator:
self.last_single_step_result = InstructionExecutedResult(None, None) self.last_single_step_result = InstructionExecutedResult(None, None)
# Initialize the register state # Initialize the register state
for emu_reg in self.regs.emulated_regs_order: for emu_reg in self.reg_set.emulated_regs_order:
reg = emu_reg.name reg = emu_reg.name
enum = self.get_reg_enum(reg) enum = self.get_reg_enum(reg)
@ -344,6 +344,9 @@ class Emulator:
return None return None
# raise AttributeError(f"AttributeError: {self!r} object has no register {name!r}") # raise AttributeError(f"AttributeError: {self!r} object has no register {name!r}")
def pc(self) -> int:
return self.read_register(self.reg_set.pc)
# Read size worth of memory, return None on error # Read size worth of memory, return None on error
def read_memory(self, address: int, size: int) -> bytes | None: def read_memory(self, address: int, size: int) -> bytes | None:
# Don't attempt if the address is not mapped on the host process # Don't attempt if the address is not mapped on the host process
@ -587,18 +590,10 @@ class Emulator:
return sz[:max_string_len] + "..." return sz[:max_string_len] + "..."
def __getattr__(self, name: str):
reg = self.get_reg_enum(name)
if reg:
return self.uc.reg_read(reg)
raise AttributeError(f"AttributeError: {self!r} object has no attribute {name!r}")
def update_pc(self, pc=None) -> None: def update_pc(self, pc=None) -> None:
if pc is None: if pc is None:
pc = pwndbg.aglib.regs.pc pc = pwndbg.aglib.regs.pc
self.uc.reg_write(self.get_reg_enum(self.regs.pc), pc) self.uc.reg_write(self.get_reg_enum(self.reg_set.pc), pc)
def read_thumb_bit(self) -> int: def read_thumb_bit(self) -> int:
""" """
@ -612,10 +607,10 @@ class Emulator:
Mimics the `read_thumb_bit` function defined in aglib/arch.py Mimics the `read_thumb_bit` function defined in aglib/arch.py
""" """
if self.arch == "arm": if self.arch == "arm":
if (cpsr := self.cpsr) is not None: if (cpsr := self.read_register("cpsr")) is not None:
return (cpsr >> 5) & 1 return (cpsr >> 5) & 1
elif self.arch == "armcm": elif self.arch == "armcm":
if (xpsr := self.xpsr) is not None: if (xpsr := self.read_register("xpsr")) is not None:
return (xpsr >> 24) & 1 return (xpsr >> 24) & 1
return 0 return 0
@ -717,35 +712,15 @@ class Emulator:
Also supports general registers like 'sp' and 'pc'. Also supports general registers like 'sp' and 'pc'.
""" """
if not self.regs: if not self.reg_set:
return None return None
# If we're looking for an exact register ('eax', 'ebp', 'r0') then # Look up the Unicorn enum for an exact register ('eax', 'ebp', 'r0')
# we can look those up easily. # This does not handle aliases, such as "sp" or "pc"
#
# 'eax' ==> enum
#
# if reg in self.regs.all:
e = self.const_regs.get(reg.upper(), None) e = self.const_regs.get(reg.upper(), None)
if e is not None: if e is not None:
return e return e
# If we're looking for an abstract register which *is* accounted for,
# we can also do an indirect lookup.
#
# 'pc' ==> 'eip' ==> enum
#
if hasattr(self.regs, reg):
return self.get_reg_enum(getattr(self.regs, reg))
# If we're looking for an abstract register which does not exist on
# the RegisterSet objects, we need to do an indirect lookup.
#
# 'sp' ==> 'stack' ==> 'esp' ==> enum
#
elif reg == "sp":
return self.get_reg_enum(self.regs.stack)
return None return None
def hook_add(self, *a, **kw): def hook_add(self, *a, **kw):
@ -769,7 +744,7 @@ class Emulator:
def emulate_with_hook(self, hook, count=512) -> None: def emulate_with_hook(self, hook, count=512) -> None:
ident = self.hook_add(U.UC_HOOK_CODE, hook) ident = self.hook_add(U.UC_HOOK_CODE, hook)
pc: int = self.pc pc: int = self.pc()
# Unicorn appears to disregard the UC_MODE_THUMB mode passed into the constructor, and instead # Unicorn appears to disregard the UC_MODE_THUMB mode passed into the constructor, and instead
# determines Thumb mode based on the PC that is passed to the `emu_start` function # determines Thumb mode based on the PC that is passed to the `emu_start` function
# https://github.com/unicorn-engine/unicorn/issues/391 # https://github.com/unicorn-engine/unicorn/issues/391
@ -884,7 +859,7 @@ class Emulator:
self.last_single_step_result = InstructionExecutedResult(None, None) self.last_single_step_result = InstructionExecutedResult(None, None)
pc = pc or self.pc pc = pc or self.pc()
if instruction is None: if instruction is None:
instruction = pwndbg.aglib.disasm.disassembly.one_raw(pc) instruction = pwndbg.aglib.disasm.disassembly.one_raw(pc)
@ -912,7 +887,7 @@ class Emulator:
# If above call does not throw an Exception, we successfully executed the instruction # If above call does not throw an Exception, we successfully executed the instruction
self.last_pc = pc self.last_pc = pc
debug(DEBUG_EXECUTING, "Unicorn now at pc=%#x", self.pc) debug(DEBUG_EXECUTING, "Unicorn now at pc=%#x", self.pc())
except U.unicorn.UcError: except U.unicorn.UcError:
debug(DEBUG_EXECUTING, "Emulator failed to execute instruction") debug(DEBUG_EXECUTING, "Emulator failed to execute instruction")
self.last_single_step_result = InstructionExecutedResult(None, None) self.last_single_step_result = InstructionExecutedResult(None, None)
@ -940,10 +915,10 @@ class Emulator:
# For debugging # For debugging
def dumpregs(self) -> None: def dumpregs(self) -> None:
for reg in ( for reg in (
list(self.regs.retaddr) list(self.reg_set.retaddr)
+ list(self.regs.misc) + list(self.reg_set.misc)
+ list(self.regs.common) + list(self.reg_set.common)
+ list(self.regs.flags) + list(self.reg_set.flags)
): ):
enum = self.get_reg_enum(reg) enum = self.get_reg_enum(reg)
@ -960,4 +935,4 @@ class Emulator:
debug(DEBUG_TRACE, "# trace_hook: %#-8x %r", (address, data)) debug(DEBUG_TRACE, "# trace_hook: %#-8x %r", (address, data))
def __repr__(self) -> str: def __repr__(self) -> str:
return f"Valid: {self.valid}, PC: {self.pc:#x}" return f"Valid: {self.valid}, PC: {self.pc():#x}"

Loading…
Cancel
Save