From be050195046def3a62274f5dde635e7bbc66de28 Mon Sep 17 00:00:00 2001 From: patryk4815 Date: Wed, 20 Nov 2024 01:32:34 +0100 Subject: [PATCH] Port plist (#2551) * commands/plist.py: port to aglib * commands/plist.py: fix lint * commands/plist.py: Fix types * commands/plist.py: make available from lldb * ptype: implement compare for LDDBType and GDBType * ptype: fix display value * gdb/GDBValue: fix rethrowing exception * aglib: create new function Value.value_to_human_readable --- pwndbg/commands/__init__.py | 1 + pwndbg/commands/plist.py | 37 ++++++++++++++++++------------------- pwndbg/dbg/__init__.py | 23 +++++++++++++++++++++++ pwndbg/dbg/gdb.py | 19 ++++++++++++++++++- pwndbg/dbg/lldb/__init__.py | 11 +++++++++++ 5 files changed, 71 insertions(+), 20 deletions(-) diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py index 65bfef5c6..6153c3b1f 100644 --- a/pwndbg/commands/__init__.py +++ b/pwndbg/commands/__init__.py @@ -757,6 +757,7 @@ def load_commands() -> None: import pwndbg.commands.p2p import pwndbg.commands.patch import pwndbg.commands.pie + import pwndbg.commands.plist import pwndbg.commands.probeleak import pwndbg.commands.retaddr import pwndbg.commands.search diff --git a/pwndbg/commands/plist.py b/pwndbg/commands/plist.py index 05c0ffd26..056411015 100644 --- a/pwndbg/commands/plist.py +++ b/pwndbg/commands/plist.py @@ -3,11 +3,10 @@ from __future__ import annotations import argparse from typing import Optional -import gdb - +import pwndbg.aglib.memory import pwndbg.chain import pwndbg.commands -import pwndbg.gdblib.memory +import pwndbg.dbg from pwndbg.color import message parser = argparse.ArgumentParser( @@ -189,17 +188,17 @@ parser.add_argument( @pwndbg.commands.ArgparsedCommand(parser, command_name="plist") def plist( path: str, - next: int, + next: str, sentinel: int, - inner_name: str, - field_name: str, + inner_name: Optional[str], + field_name: Optional[str], offset: int, count: Optional[int] = None, ) -> None: # Have GDB parse the path for us and check if it's valid. try: - first = gdb.parse_and_eval(path) - except gdb.error as e: + first = pwndbg.dbg.selected_frame().evaluate_expression(path) + except pwndbg.dbg_mod.Error as e: print(message.error(f"{e}")) return @@ -219,16 +218,16 @@ def plist( # for the error mesages. sep = "." deref = "" - if first.type.code == gdb.TYPE_CODE_PTR: + if first.type.code == pwndbg.dbg_mod.TypeCode.POINTER: sep = "->" deref = "*" try: first = first.dereference() - except gdb.error as e: + except pwndbg.dbg_mod.Error as e: print(message.error(f"Pointer at {path} could not be dereferenced: {e}")) return - if first.type.code == gdb.TYPE_CODE_PTR: + if first.type.code == pwndbg.dbg_mod.TypeCode.POINTER: print(message.error(f"{path} is not a value or a single pointer to one")) return @@ -247,7 +246,7 @@ def plist( try: inner = first[inner_name] inner_sep = "->" - except gdb.error as e: + except pwndbg.dbg_mod.Error as e: print(message.error(f"Cannot find component {inner_name} in {path}: {e}")) return if inner.is_optimized_out: @@ -265,14 +264,14 @@ def plist( next_ptr = inner[next] next_ptr_loc = inner next_ptr_name = f"{inner_name}.{next}" - except gdb.error as e: + except pwndbg.dbg_mod.Error as e: print(message.error(f"Cannot find component {next_ptr_name} in {path}: {e}")) return if next_ptr.is_optimized_out: print(message.error(f"{path}{sep}{next_ptr_name} has been optimized out")) return - if next_ptr.type.code != gdb.TYPE_CODE_PTR: + if next_ptr.type.code != pwndbg.dbg_mod.TypeCode.POINTER: print(message.error(f"{path}{sep}{next_ptr_name} is not a pointer")) return @@ -282,7 +281,7 @@ def plist( if field_name is not None: try: field = first[field_name] - except gdb.error as e: + except pwndbg.dbg_mod.Error as e: print(message.error(f"Cannot find component {field_name} in {path}: {e}")) return field_type = field.type @@ -409,13 +408,13 @@ def plist( target_type = field_type target_address = address + field_offset - value = pwndbg.gdblib.memory.get_typed_pointer_value(target_type, target_address) + value = pwndbg.aglib.memory.get_typed_pointer_value(target_type, target_address) - symbol = pwndbg.gdblib.symbol.get(target_address) + symbol = pwndbg.dbg.selected_inferior().symbol_name_at_address(target_address) symbol = f"<{symbol}>" if symbol else "" - print(f"{target_address:#x} {symbol}: {value}") - except gdb.error as e: + print(f"{target_address:#x} {symbol}: {value.value_to_human_readable()}") + except pwndbg.dbg_mod.Error as e: print(message.error(f"Cannot dereference {address:#x} for list link #{i + 1}: {e}")) print(message.error("Is the linked list corrupted or is the sentinel value wrong?")) return diff --git a/pwndbg/dbg/__init__.py b/pwndbg/dbg/__init__.py index 9636370e2..ee13fd739 100644 --- a/pwndbg/dbg/__init__.py +++ b/pwndbg/dbg/__init__.py @@ -664,6 +664,12 @@ class Type: # if there is a better debugger-specific way to do this. return [field.name for field in self.fields()] + def __eq__(self, rhs: object) -> bool: + """ + Returns True if types are the same + """ + raise NotImplementedError() + class Value: """ @@ -719,6 +725,23 @@ class Value: """ raise NotImplementedError() + def value_to_human_readable(self) -> str: + """ + Converts a Value to a human-readable string representation. + + The format is similar to what is produced by the `str()` function for gdb.Value, + displaying nested fields and pointers in a user-friendly way. + + **Usage Notes:** + - This function is intended solely for displaying results to the user. + - The output format may differ between debugger implementations (e.g., GDB vs LLDB), + as each debugger may format values differently. For instance: + - GDB might produce: '{\n value = 0,\n inner = {\n next = 0x555555558098 \n }\n}' + - LLDB might produce: '(inner_a_node) *$PWNDBG_CREATED_VALUE_0 = {\n value = 0\n inner = {\n next = 0x0000555555558098\n }\n}' + - As such, this function should not be relied upon for parsing or programmatic use. + """ + raise NotImplementedError() + # This is a GDB implementation detail. def fetch_lazy(self) -> None: """ diff --git a/pwndbg/dbg/gdb.py b/pwndbg/dbg/gdb.py index 0b0ab2a52..9889f1449 100644 --- a/pwndbg/dbg/gdb.py +++ b/pwndbg/dbg/gdb.py @@ -910,6 +910,13 @@ class GDBType(pwndbg.dbg_mod.Type): def __init__(self, inner: gdb.Type): self.inner = inner + @override + def __eq__(self, rhs: object) -> bool: + assert isinstance(rhs, GDBType), "tried to compare GDBType to other type" + other: GDBType = rhs + + return self.inner == other.inner + @property @override def sizeof(self) -> int: @@ -997,7 +1004,17 @@ class GDBValue(pwndbg.dbg_mod.Value): @override def string(self) -> str: - return self.inner.string() + try: + return self.inner.string() + except gdb.error as e: + raise pwndbg.dbg_mod.Error(e) + + @override + def value_to_human_readable(self) -> str: + try: + return str(self.inner) + except gdb.error as e: + raise pwndbg.dbg_mod.Error(e) @override def fetch_lazy(self) -> None: diff --git a/pwndbg/dbg/lldb/__init__.py b/pwndbg/dbg/lldb/__init__.py index d5b2f6a44..c80defd2f 100644 --- a/pwndbg/dbg/lldb/__init__.py +++ b/pwndbg/dbg/lldb/__init__.py @@ -319,6 +319,13 @@ class LLDBType(pwndbg.dbg_mod.Type): def __init__(self, inner: lldb.SBType): self.inner = inner + @override + def __eq__(self, rhs: object) -> bool: + assert isinstance(rhs, LLDBType), "tried to compare LLDBType to other type" + other: LLDBType = rhs + + return self.inner == other.inner + @property @override def sizeof(self) -> int: @@ -480,6 +487,10 @@ class LLDBValue(pwndbg.dbg_mod.Value): return last_str + @override + def value_to_human_readable(self) -> str: + return str(self.inner) + @override def fetch_lazy(self) -> None: # Not needed under LLDB.