From 85b93a792a9636104197bb574f9cf2ba24e06495 Mon Sep 17 00:00:00 2001 From: OBarronCS <55004530+OBarronCS@users.noreply.github.com> Date: Sat, 11 Oct 2025 14:40:07 -0400 Subject: [PATCH] Manually propagate register values across instructions while disassembling (#2963) * Manually propagate register values across instructions while disassembling, allowing better annotations when emulation is unavailable * Add comment * More comments * Comment * Comment fix * Local variable change * Print register writes in instruction debug print * Clear register set when encountering branch with undetermined result * Update tests * lint * Fix dbg tests * Update dev docs related to reasoning about process state --- docs/contributing/improving-annotations.md | 2 +- pwndbg/aglib/disasm/arch.py | 50 ++++++++++++++++++- pwndbg/aglib/disasm/disassembly.py | 6 +++ pwndbg/aglib/disasm/instruction.py | 47 ++++++++++++++++- pwndbg/aglib/disasm/x86.py | 7 +++ pwndbg/lib/regs.py | 2 +- .../dbg/tests/test_context_commands.py | 4 +- tests/library/dbg/tests/test_emulate.py | 2 +- tests/library/dbg/tests/test_nearpc.py | 32 ++++++------ .../gdb/tests/test_context_commands.py | 4 +- tests/library/gdb/tests/test_emulate.py | 2 +- tests/library/gdb/tests/test_nearpc.py | 32 ++++++------ tests/unit_tests/test_regs.py | 16 +++--- 13 files changed, 156 insertions(+), 50 deletions(-) diff --git a/docs/contributing/improving-annotations.md b/docs/contributing/improving-annotations.md index 929f8ffe0..9824e2bc6 100644 --- a/docs/contributing/improving-annotations.md +++ b/docs/contributing/improving-annotations.md @@ -37,7 +37,7 @@ We go through the enhancement process for the instruction at the program counter ## When to use emulation / reasoning about process state -When possible, we code aims to use emulation as little as possible. If there is information that can be determined statically or without the emulator, then we try to avoid emulation. This is so we can display annotations even when the Unicorn Engine is disabled. For example, say we come to a stop, and are faced with enhancing the following three instructions in the dashboard: +In general, the code aims to be organized in a way as to allow as many features as possible even in the absence of emulation. If there is information that can be determined statically, then we try to expose it as an alternative to emulation. This is so we can display annotations even when the Unicorn Engine is disabled. For example, say we come to a stop, and are faced with enhancing the following three instructions in the dashboard: ```asm 1. lea rax, [rip + 0xd55] diff --git a/pwndbg/aglib/disasm/arch.py b/pwndbg/aglib/disasm/arch.py index 04af00695..aa9d9b076 100644 --- a/pwndbg/aglib/disasm/arch.py +++ b/pwndbg/aglib/disasm/arch.py @@ -29,6 +29,7 @@ from pwndbg.aglib.disasm.instruction import InstructionCondition from pwndbg.aglib.disasm.instruction import PwndbgInstruction from pwndbg.aglib.disasm.instruction import boolean_to_instruction_condition from pwndbg.lib.arch import PWNDBG_SUPPORTED_ARCHITECTURES_TYPE +from pwndbg.lib.regs import PseudoEmulatedRegisterFile # Emulator currently requires GDB, and we only use it here for type checking. if TYPE_CHECKING: @@ -144,8 +145,16 @@ def memory_or_register_assign(left: str, right: str, mem_assign: bool) -> str: class DisassemblyAssistant: architecture: PWNDBG_SUPPORTED_ARCHITECTURES_TYPE + manual_register_values: PseudoEmulatedRegisterFile + + supports_manual_emulation = False + """This feature relies on the Capstone .regs_access() features that not all architectures have reliable support for""" + def __init__(self, architecture: PWNDBG_SUPPORTED_ARCHITECTURES_TYPE) -> None: self.architecture = architecture + self.manual_register_values = PseudoEmulatedRegisterFile( + pwndbg.aglib.regs.current, pwndbg.aglib.arch.ptrsize + ) self.op_handlers: Dict[ int, Callable[[PwndbgInstruction, EnhancedOperand, Emulator], int | None] @@ -253,6 +262,32 @@ class DisassemblyAssistant: if DEBUG_ENHANCEMENT: print("Turned off emulation for call") + # Manually propagate register values so when enhancing the next instruction, we can read from these registers + if self.supports_manual_emulation: + if ( + instruction.call_like + or (set(instruction.groups) & DO_NOT_EMULATE) + or (instruction.jump_like and not instruction.jump_result_is_known) + ): + # Syscalls and functions (which we step over) can clobber registers + # Also, if we encounter a control flow instruction where the result is unknown, + # we need to reset the registers because otherwise it may show annotations for instructions never actually taken. + self.manual_register_values.invalidate_all_registers() + else: + _, regs_written = instruction.cs_insn.regs_access() + + for reg_id in regs_written: + reg_name: str = instruction.cs_insn.reg_name(reg_id) + + # If we determined that this instruction wrote some value to this register, propagate it. + # Otherwise, invalidate the value since we cannot reason about it. + if reg_id in instruction.register_writes: + self.manual_register_values.write_register( + reg_name, instruction.register_writes[reg_id] + ) + else: + self.manual_register_values.invalidate_register(reg_name) + if DEBUG_ENHANCEMENT: print(self.dump(instruction)) print("Done enhancing") @@ -417,6 +452,9 @@ class DisassemblyAssistant: if DEBUG_ENHANCEMENT: print(f"Read value from process register: {pwndbg.aglib.regs[regname]}") return pwndbg.aglib.regs[regname] + elif (reg_value := self.manual_register_values.read_register(regname)) is not None: + # If we manually tracked the value of this register while disassembling, we can read from it. + return reg_value else: return None @@ -506,6 +544,11 @@ class DisassemblyAssistant: address_list = [address] + if read_size is not None and read_size < pwndbg.aglib.arch.ptrsize: + size_type = pwndbg.aglib.typeinfo.get_type(read_size) + else: + size_type = pwndbg.aglib.typeinfo.ppvoid + for _ in range(limit): if address_list.count(address) >= 2: break @@ -513,7 +556,9 @@ class DisassemblyAssistant: page = pwndbg.aglib.vmmap.find(address) if page and not page.write: try: - address = pwndbg.aglib.memory.read_pointer_width(address) + address = int( + pwndbg.aglib.memory.get_typed_pointer_value(size_type, address) + ) address &= pwndbg.aglib.arch.ptrmask address_list.append(address) except pwndbg.dbg_mod.Error: @@ -1011,6 +1056,9 @@ class DisassemblyAssistant: # If we already used emulation, use the result, otherwise take the source operand before_value result = left.after_value or right.before_value if result is not None and result >= 0: + # We have determined the value written to this register - propagate this to future instructions. + instruction.register_writes[left.reg] = result + TELESCOPE_DEPTH = max(0, int(pwndbg.config.disasm_telescope_depth)) telescope_addresses = self._telescope( diff --git a/pwndbg/aglib/disasm/disassembly.py b/pwndbg/aglib/disasm/disassembly.py index 5d459d6ef..be5ef7f63 100644 --- a/pwndbg/aglib/disasm/disassembly.py +++ b/pwndbg/aglib/disasm/disassembly.py @@ -352,6 +352,12 @@ def near( # By using the same assistant for all the instructions disassembled in this pass, we can track and share information across the instructions assistant = pwndbg.aglib.disasm.disassembly.get_disassembly_assistant_for_current_arch() + # Copy register values to the enhancer for use in manual register tracking + if assistant.supports_manual_emulation and address == pc: + for reg in pwndbg.aglib.regs.current.common: + if (reg_value := pwndbg.aglib.regs[reg]) is not None: + assistant.manual_register_values.write_register(reg, reg_value) + # Start at the current instruction using emulation if available. current = one(address, emu, put_cache=True, assistant=assistant) diff --git a/pwndbg/aglib/disasm/instruction.py b/pwndbg/aglib/disasm/instruction.py index 19a9f58be..22d81f331 100644 --- a/pwndbg/aglib/disasm/instruction.py +++ b/pwndbg/aglib/disasm/instruction.py @@ -189,6 +189,7 @@ class PwndbgInstruction(Protocol): causes_branch_delay: bool split: SplitType emulated: bool + register_writes: Dict[int, int] @property def call_like(self) -> bool: ... @@ -208,6 +209,9 @@ class PwndbgInstruction(Protocol): @property def is_conditional_jump_taken(self) -> bool: ... + @property + def jump_result_is_known(self) -> bool: ... + @property def bytes(self) -> bytearray: ... @@ -410,6 +414,12 @@ class PwndbgInstructionImpl(PwndbgInstruction): If the enhancement successfully used emulation for this instruction """ + self.register_writes = {} + """ + Mapping of Capstone register id to integer value. During enhancement, we might manually determine + that an instruction writes some value to a register, and this is stored here. + """ + @property def call_like(self) -> bool: """ @@ -498,6 +508,19 @@ class PwndbgInstructionImpl(PwndbgInstruction): ) ) + @property + def jump_result_is_known(self) -> bool: + """ + True under the following conditions: + - If it's an unconditional jump, we know the target of the jump + - If it's a conditional jump, we know the target of the branch and know whether or not we take it + Otherwise, false + """ + return self.has_jump_target and ( + (self.is_unconditional_jump) + or (self.is_conditional_jump and self.condition != InstructionCondition.UNDETERMINED) + ) + @property def bytes(self) -> bytearray: """ @@ -522,6 +545,11 @@ class PwndbgInstructionImpl(PwndbgInstruction): def __repr__(self) -> str: operands_str = " ".join([repr(op) for op in self.operands]) + hex_register_writes = { + self.cs_insn.reg_name(reg_id): hex(reg_value) + for reg_id, reg_value in self.register_writes.items() + } + info = f"""{self.mnemonic} {self.op_str} at {self.address:#x} (size={self.size}) (arch: {CAPSTONE_ARCH_MAPPING_STRING.get(self.cs_insn._cs.arch,None)}) Bytes: {pwnlib.util.fiddling.enhex(self.bytes)} ID: {self.id}, {self.cs_insn.insn_name()} @@ -543,7 +571,18 @@ class PwndbgInstructionImpl(PwndbgInstruction): Syscall: {self.syscall if self.syscall is not None else ""} {self.syscall_name if self.syscall_name is not None else "N/A"} Causes Delay slot: {self.causes_branch_delay} Split: {SplitType(self.split).name} - Call-like: {self.call_like}""" + Call-like: {self.call_like} + Register writes: {hex_register_writes}""" + + try: + regs_read, regs_written = self.cs_insn.regs_access() + info += f"\n\tCapstone regs read: {[self.cs_insn.reg_name(reg) for reg in regs_read]}" + info += ( + f"\n\tCapstone regs written: {[self.cs_insn.reg_name(reg) for reg in regs_written]}" + ) + except CsError: + # Not all architectures support the .reg_access() API + pass # Hacky, but this is just for debugging if hasattr(self.cs_insn, "cc"): @@ -715,6 +754,8 @@ class ManualPwndbgInstruction(PwndbgInstruction): self.emulated = False + self.register_writes = {} + @property def bytes(self) -> bytearray: # GDB simply doesn't provide us with the raw bytes. @@ -746,6 +787,10 @@ class ManualPwndbgInstruction(PwndbgInstruction): def is_conditional_jump_taken(self) -> bool: return False + @property + def jump_result_is_known(self) -> bool: + return False + @override def op_find(self, op_type: int, position: int) -> EnhancedOperand: # raise NotImplementedError, because if this is called it indicates a bug elsewhere in the codebase. diff --git a/pwndbg/aglib/disasm/x86.py b/pwndbg/aglib/disasm/x86.py index a9b4693d4..da23b397f 100644 --- a/pwndbg/aglib/disasm/x86.py +++ b/pwndbg/aglib/disasm/x86.py @@ -50,6 +50,8 @@ X86_MATH_INSTRUCTIONS = { # This class handles enhancement for x86 and x86_64. This is because Capstone itself # represents both architectures using the same class class X86DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant): + supports_manual_emulation = True + def __init__(self, architecture) -> None: super().__init__(architecture) @@ -156,6 +158,9 @@ class X86DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant): TELESCOPE_DEPTH = max(0, int(pwndbg.config.disasm_telescope_depth)) if right.before_value is not None: + # We have determined the value written to this register - propagate this to future instructions. + instruction.register_writes[left.reg] = right.before_value + telescope_addresses = super()._telescope( right.before_value, TELESCOPE_DEPTH, instruction, emu ) @@ -213,6 +218,8 @@ class X86DisassemblyAssistant(pwndbg.aglib.disasm.arch.DisassemblyAssistant): # If zeroing the register with XOR A, A. Can reason about this no matter where the instruction is if left.type == CS_OP_REG and right.type == CS_OP_REG and left.reg == right.reg: + # We know that 0 is written to this register - propagate this to future instructions. + instruction.register_writes[left.reg] = 0 instruction.annotation = register_assign(left.str, "0") else: self._common_binary_op_annotator( diff --git a/pwndbg/lib/regs.py b/pwndbg/lib/regs.py index a46878ba4..01f0d5d1d 100644 --- a/pwndbg/lib/regs.py +++ b/pwndbg/lib/regs.py @@ -288,7 +288,7 @@ class RegisterSet: yield from self.all -class PsuedoEmulatedRegisterFile: +class PseudoEmulatedRegisterFile: """ This class represents a set of registers that can be written, read, and invalidated. diff --git a/tests/library/dbg/tests/test_context_commands.py b/tests/library/dbg/tests/test_context_commands.py index 241071346..26e8a360e 100644 --- a/tests/library/dbg/tests/test_context_commands.py +++ b/tests/library/dbg/tests/test_context_commands.py @@ -175,7 +175,7 @@ async def test_context_disasm_syscalls_args_display(ctrl: Controller) -> None: " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -224,7 +224,7 @@ async def test_context_disasm_syscalls_args_display_no_emulate(ctrl: Controller) " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" diff --git a/tests/library/dbg/tests/test_emulate.py b/tests/library/dbg/tests/test_emulate.py index e786cae21..0ab20d1ea 100644 --- a/tests/library/dbg/tests/test_emulate.py +++ b/tests/library/dbg/tests/test_emulate.py @@ -74,7 +74,7 @@ async def test_emulate_disasm_loop(ctrl: Controller) -> None: disasm_without_emu_0x400080 = [ " ► 0x400080 <_start> movabs rsi, string RSI => 0x400094 (string) ◂— xor dword ptr [rdx], esi /* '12345' */", - " 0x40008a <_start+10> mov rdi, rsp", + f" 0x40008a <_start+10> mov rdi, rsp RDI => {hex(pwndbg.aglib.regs.rsp)}", " 0x40008d <_start+13> mov ecx, 3 ECX => 3", " 0x400092 <_start+18> rep movsb byte ptr [rdi], byte ptr [rsi]", " 0x400094 xor dword ptr [rdx], esi", diff --git a/tests/library/dbg/tests/test_nearpc.py b/tests/library/dbg/tests/test_nearpc.py index c44c7fa8e..0f2a40328 100644 --- a/tests/library/dbg/tests/test_nearpc.py +++ b/tests/library/dbg/tests/test_nearpc.py @@ -137,7 +137,7 @@ async def test_nearpc_opcode_bytes(ctrl: Controller, opcode_bytes: int) -> None: " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 {} <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b {} <_start+27> int 0x80\n" + " 0x40009b {} <_start+27> int 0x80 \n" " 0x40009d {} add byte ptr [rax], al\n" " 0x40009f {} add byte ptr [rax], al\n" " 0x4000a1 {} add byte ptr [rax], al\n" @@ -166,7 +166,7 @@ async def test_nearpc_opcode_seperator(ctrl: Controller, separator_bytes: int) - " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 {} <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b {} <_start+27> int 0x80\n" + " 0x40009b {} <_start+27> int 0x80 \n" " 0x40009d {} add byte ptr [rax], al\n" " 0x40009f {} add byte ptr [rax], al\n" " 0x4000a1 {} add byte ptr [rax], al\n" @@ -193,9 +193,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: "b+ 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -211,9 +211,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: " ► 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -228,9 +228,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: "b+ 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -245,9 +245,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: " 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -262,9 +262,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: "b+ 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -279,9 +279,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: " 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -296,9 +296,9 @@ async def test_nearpc_highlight_breakpoint(ctrl: Controller) -> None: " 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" " 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" diff --git a/tests/library/gdb/tests/test_context_commands.py b/tests/library/gdb/tests/test_context_commands.py index fe81badfe..e355b5656 100644 --- a/tests/library/gdb/tests/test_context_commands.py +++ b/tests/library/gdb/tests/test_context_commands.py @@ -165,7 +165,7 @@ def test_context_disasm_syscalls_args_display(start_binary): " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -212,7 +212,7 @@ def test_context_disasm_syscalls_args_display_no_emulate(start_binary): " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" diff --git a/tests/library/gdb/tests/test_emulate.py b/tests/library/gdb/tests/test_emulate.py index 2f59a25a2..74c6d3d37 100644 --- a/tests/library/gdb/tests/test_emulate.py +++ b/tests/library/gdb/tests/test_emulate.py @@ -71,7 +71,7 @@ def test_emulate_disasm_loop(start_binary): disasm_without_emu_0x400080 = [ " ► 0x400080 <_start> movabs rsi, string RSI => 0x400094 (string) ◂— xor dword ptr [rdx], esi /* '12345' */", - " 0x40008a <_start+10> mov rdi, rsp", + f" 0x40008a <_start+10> mov rdi, rsp RDI => {hex(pwndbg.aglib.regs.rsp)}", " 0x40008d <_start+13> mov ecx, 3 ECX => 3", " 0x400092 <_start+18> rep movsb byte ptr [rdi], byte ptr [rsi]", " 0x400094 xor dword ptr [rdx], esi", diff --git a/tests/library/gdb/tests/test_nearpc.py b/tests/library/gdb/tests/test_nearpc.py index 62430b0d5..62f758a08 100644 --- a/tests/library/gdb/tests/test_nearpc.py +++ b/tests/library/gdb/tests/test_nearpc.py @@ -135,7 +135,7 @@ def test_nearpc_opcode_bytes(start_binary, opcode_bytes): " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 {} <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b {} <_start+27> int 0x80\n" + " 0x40009b {} <_start+27> int 0x80 \n" " 0x40009d {} add byte ptr [rax], al\n" " 0x40009f {} add byte ptr [rax], al\n" " 0x4000a1 {} add byte ptr [rax], al\n" @@ -161,7 +161,7 @@ def test_nearpc_opcode_seperator(start_binary, separator_bytes): " buf: 0xdeadbeef\n" " nbytes: 0\n" " 0x400096 {} <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b {} <_start+27> int 0x80\n" + " 0x40009b {} <_start+27> int 0x80 \n" " 0x40009d {} add byte ptr [rax], al\n" " 0x40009f {} add byte ptr [rax], al\n" " 0x4000a1 {} add byte ptr [rax], al\n" @@ -196,9 +196,9 @@ def test_nearpc_highlight_breakpoint(start_binary): "b+ 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -214,9 +214,9 @@ def test_nearpc_highlight_breakpoint(start_binary): " ► 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -231,9 +231,9 @@ def test_nearpc_highlight_breakpoint(start_binary): "b+ 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -248,9 +248,9 @@ def test_nearpc_highlight_breakpoint(start_binary): " 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -265,9 +265,9 @@ def test_nearpc_highlight_breakpoint(start_binary): "b+ 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -282,9 +282,9 @@ def test_nearpc_highlight_breakpoint(start_binary): " 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" "b+ 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" @@ -299,9 +299,9 @@ def test_nearpc_highlight_breakpoint(start_binary): " 0x400085 <_start+5> mov edi, 0x1337 EDI => 0x1337\n" " ► 0x40008a <_start+10> mov esi, 0xdeadbeef ESI => 0xdeadbeef\n" " 0x40008f <_start+15> mov ecx, 0x10 ECX => 0x10\n" - " 0x400094 <_start+20> syscall \n" + " 0x400094 <_start+20> syscall \n" " 0x400096 <_start+22> mov eax, 0xa EAX => 0xa\n" - " 0x40009b <_start+27> int 0x80\n" + " 0x40009b <_start+27> int 0x80 \n" " 0x40009d add byte ptr [rax], al\n" " 0x40009f add byte ptr [rax], al\n" " 0x4000a1 add byte ptr [rax], al\n" diff --git a/tests/unit_tests/test_regs.py b/tests/unit_tests/test_regs.py index a158b1dc7..cb0386c86 100644 --- a/tests/unit_tests/test_regs.py +++ b/tests/unit_tests/test_regs.py @@ -1,6 +1,6 @@ from __future__ import annotations -from pwndbg.lib.regs import PsuedoEmulatedRegisterFile +from pwndbg.lib.regs import PseudoEmulatedRegisterFile from pwndbg.lib.regs import aarch64 from pwndbg.lib.regs import amd64 from pwndbg.lib.regs import mips @@ -16,7 +16,7 @@ def test_emulated_register_set_amd64(): AH = top half of AX AL = bottom half of AX """ - new = PsuedoEmulatedRegisterFile(amd64, 8) + new = PseudoEmulatedRegisterFile(amd64, 8) new.write_register("rax", -1) @@ -67,7 +67,7 @@ def test_emulated_register_set_amd64(): def test_emulated_register_set_amd64_more(): - new = PsuedoEmulatedRegisterFile(amd64, 8) + new = PseudoEmulatedRegisterFile(amd64, 8) # Unwritten value should return None assert new.read_register("rbx") is None @@ -141,7 +141,7 @@ def test_emulated_register_set_amd64_more(): def test_emulated_register_set_amd64_invalidate(): - new = PsuedoEmulatedRegisterFile(amd64, 8) + new = PseudoEmulatedRegisterFile(amd64, 8) new.write_register("rax", -1) @@ -183,7 +183,7 @@ def test_emulated_register_set_amd64_invalidate(): def test_emulate_register_file_amd64_sign_extension(): - new = PsuedoEmulatedRegisterFile(amd64, 8) + new = PseudoEmulatedRegisterFile(amd64, 8) # This will sign extend the value to EAX, since the top bit in 0xFF is 1. new.write_register("eax", 0xFF, source_width=1, sign_extend=True) @@ -204,7 +204,7 @@ def test_emulate_register_file_amd64_sign_extension(): def test_emulated_register_set_aarch64(): - new = PsuedoEmulatedRegisterFile(aarch64, 8) + new = PseudoEmulatedRegisterFile(aarch64, 8) new.write_register("w0", 0xFFFF_AABB) @@ -219,13 +219,13 @@ def test_emulated_register_set_aarch64(): def test_emulated_register_set_mips(): - new = PsuedoEmulatedRegisterFile(mips, 4) + new = PseudoEmulatedRegisterFile(mips, 4) new.write_register("v0", 0xFFFF_AABB) assert new.read_register("v0") == 0xFFFF_AABB - new = PsuedoEmulatedRegisterFile(mips, 8) + new = PseudoEmulatedRegisterFile(mips, 8) new.write_register("v0", 0xFF_FFFF_AABB)