### Telescope Example Output Source: https://docs.libdebug.org/latest/basics/memory_access An example of the list returned by the telescope method representing a pointer chain. ```python [0x7ffff7fcb200, 0x7ffff7fcb208, 0x7ffff7fcb210, "provola"] ``` -------------------------------- ### Telescope Usage Source: https://docs.libdebug.org/latest/basics/memory_access Example of using telescope to traverse a pointer chain starting from the RSP register. ```python address = d.regs.rsp chain = d.memory.telescope(address, max_depth=5, min_str_len=6, max_str_len=0x100) ``` -------------------------------- ### Install libdebug with pip Source: https://docs.libdebug.org/latest Install libdebug from PyPI using pip. Run this command in a terminal with Python 3 and pip available. ```bash python3 -m pip install libdebug ``` -------------------------------- ### Example usage of asynchronous watchpoints Source: https://docs.libdebug.org/latest/stopping_events/watchpoints Example showing how to define a callback for an asynchronous watchpoint and set it with d.watchpoint(). The callback prints the RAX register and disables the watchpoint after 100 hits. ```python def on_watchpoint_hit(t, bp): print(f"RAX: {t.regs.rax:#x}") if bp.hit_count == 100: print("Hit count reached 100") bp.disable() d.watchpoint(0x11f0, condition="rw", length=8, callback=on_watchpoint_hit, file="binary") ``` -------------------------------- ### DO: Using libdebug debugger.run() with pwntools utilities Source: https://docs.libdebug.org/latest Use this pattern when you need pwntools utilities alongside libdebug. It starts the process with libdebug's debugger.run() and only imports the needed pwntools functions, avoiding the race conditions described in the DON'T example. ```python from libdebug import debugger from pwn import fmtstr_payload, u64 d = debugger("./provola") io = d.run() [...] leak = u64(io.recvline()) value = 0xbadf00d fmtstr = fmtstr_payload(6, {leak: value}) io.sendline(fmtstr.encode()) ``` -------------------------------- ### Install libdebug from source or GitHub Source: https://docs.libdebug.org/latest/development/building_libdebug Build and install libdebug from the repository root, or directly from GitHub using a branch or commit. Replace with the desired branch or commit hash; if omitted, the default branch is used. ```bash python3 -m pip install . ``` ```bash python3 -m pip install git+https://github.com/libdebug/libdebug.git@ ``` -------------------------------- ### Catching all signals example Source: https://docs.libdebug.org/latest/stopping_events/signals Example of catching all signals using the "all" option. The callback prints the signal number and mnemonic from the ThreadContext. ```python from libdebug import debugger def catcher(t, catcher): print(f"Signal {t.signal_number} ({t.signal}) was caught") d = debugger("./test_program") d.run() catcher = d.catch_signal("all") d.cont() d.wait() ``` -------------------------------- ### Editable install of libdebug from local clone Source: https://docs.libdebug.org/latest/development/building_libdebug Install libdebug in editable mode from a local clone so source changes take effect immediately, including compiled C++ extensions. Requires running from the repository root. ```bash python3 -m pip install --no-build-isolation -Ceditable.rebuild=true -ve . ``` -------------------------------- ### Synchronous syscall handling example Source: https://docs.libdebug.org/latest/stopping_events/syscalls Example of synchronous syscall handling. After continuing execution, checks whether the handler was hit on enter or exit and prints the corresponding message. The script prints "open syscall was entered". ```python from libdebug import debugger d = debugger("./test_program") d.run() handler = d.handle_syscall(syscall="open") d.cont() if handler.hit_on_enter(d): print("open syscall was entered") elif handler.hit_on_exit(d): print("open syscall was exited") ``` -------------------------------- ### Memory Find Usage Source: https://docs.libdebug.org/latest/basics/memory_access Examples of searching for a string in libc and an integer value in the stack starting from the RSP register. ```python binsh_string_addr = d.memory.find("/bin/sh", file="libc") value_address = d.memory.find(0x1234, file="stack", start=d.regs.rsp) ``` -------------------------------- ### Asynchronous syscall handler example Source: https://docs.libdebug.org/latest/stopping_events/syscalls Example of asynchronous syscall handling. Defines on_enter and on_exit callbacks that modify the syscall arguments and return value, then registers them for the "open" syscall. ```python def on_enter_open(t, handler): print("entering open") t.syscall_arg0 = 0x1 def on_exit_open(t, handler): print("exiting open") t.syscall_return = 0x0 handler = d.handle_syscall(syscall="open", on_enter=on_enter_open, on_exit=on_exit_open) ``` -------------------------------- ### Recursion loop detection example Source: https://docs.libdebug.org/latest/stopping_events/debugging_flow This example shows how to create two hijack_syscall handlers that swap 'read' and 'write' syscalls with recursive=True, which will cause a RuntimeError due to infinite loop detection. ```python handler = d.hijack_syscall("read", "write", recursive=True) handler = d.hijack_syscall("write", "read", recursive=True) ``` -------------------------------- ### Compile jumpstart executable with gcc Source: https://docs.libdebug.org/latest/development/building_libdebug Manually compile and install the jumpstart executable to resolve the FileNotFoundError for jumpstart. Replace [ERROR_PATH] with the path indicated in the error message. Run from the repository root. ```bash gcc -o [ERROR_PATH] libdebug/ptrace/jumpstart/jumpstart.c -O3 ``` -------------------------------- ### Run libdebug test suite Source: https://docs.libdebug.org/latest Clone the libdebug repository and run the test suite to verify your installation when building from source. Requires git and Python 3. ```bash git clone https://github.com/libdebug/libdebug cd libdebug/test python run_suite.py ``` -------------------------------- ### Find Pointers Usage Source: https://docs.libdebug.org/latest/basics/memory_access Example of searching for pointers from the stack to the heap and printing the results. ```python pointers = d.memory.find_pointers("stack", "heap") for src, dst in pointers: print(f"Heap leak to {dst} found at {src} points") ``` -------------------------------- ### r Source: https://docs.libdebug.org/latest/from_pydoc/generated/debugger/debugger Alias for the `run` method. Starts the process and waits for it to stop. ```APIDOC ## r ### Description Alias for the `run` method. Starts the process and waits for it to stop. ### Method r ### Parameters - **redirect_pipes** (bool) - Required - Whether to hook and redirect the pipes of the process to a PipeManager. ### Returns - **PipeManager | None** - The PipeManager object if pipes are redirected, otherwise None. ``` -------------------------------- ### Asynchronous breakpoint callback example Source: https://docs.libdebug.org/latest/stopping_events/breakpoints Example of an asynchronous breakpoint callback that prints the RAX register and disables the breakpoint after 100 hits. The callback is passed to d.breakpoint() with the file parameter set to "binary". ```python def on_breakpoint_hit(t, bp): print(f"RAX: {t.regs.rax:#x}") if bp.hit_count == 100: print("Hit count reached 100") bp.disable() d.breakpoint(0x11f0, callback=on_breakpoint_hit, file="binary") ``` -------------------------------- ### Install libdebug build dependencies per distribution Source: https://docs.libdebug.org/latest/development/building_libdebug Install the required system dependencies for building libdebug on Ubuntu, Arch Linux, Fedora, Debian, openSUSE, and Alpine Linux. These packages provide Python, compilers, and the ELF/DWARF development headers needed by libdebug. ```bash sudo apt install -y python3 python3-dev g++ libdwarf-dev libelf-dev libiberty-dev ``` ```bash sudo pacman -S base-devel python3 elfutils libdwarf binutils ``` ```bash sudo dnf install -y python3 python3-devel g++ elfutils-devel libdwarf-devel binutils-devel ``` ```bash sudo apt install -y python3 python3-dev g++ libdwarf-dev libelf-dev libiberty-dev ``` ```bash sudo zypper install -y gcc-c++ make python3 python3-devel libelf-devel libdwarf-devel binutils-devel ``` ```bash sudo apk add -y python3 python3-dev py3-pip linux-headers elfutils-dev libdwarf-dev binutils-dev ``` -------------------------------- ### Non-blocking GDB migration example Source: https://docs.libdebug.org/latest/basics/detach_and_gdb Example of using non-blocking GDB migration. After calling `d.gdb(blocking = False)`, you can continue using the pipe interaction while GDB runs. Use `wait_for_gdb()` to wait for the GDB session to finish. Note that executing debugger-related commands in non-blocking mode raises an exception. ```python from libdebug import debugger d = debugger("program") pipe = d.run() # Reach interesting point in the program [...] d.gdb(blocking = False) pipe.sendline(b"dump interpret") with open("dump.bin", "r") as f: pipe.send(f.read()) d.wait_for_gdb() ``` -------------------------------- ### Set terminal with tmux for GDB new process Source: https://docs.libdebug.org/latest/basics/detach_and_gdb Example of setting the terminal command in libcontext for opening GDB in a new process. This is needed when automatic detection fails. Use `libcontext.terminal = ['tmux', 'splitw', '-h']`. ```python from libdebug import libcontext libcontext.terminal = ['tmux', 'splitw', '-h'] ``` -------------------------------- ### __init__ Source: https://docs.libdebug.org/latest/from_pydoc/generated/liblog Initializes the logger. Sets up custom log levels and creates general and component-specific loggers. ```APIDOC ## __init__ ### Description Initializes the logger. Sets up custom log levels and creates the general logger and component-specific loggers for debugger and pipe. ### Method Constructor ### Parameters None ### Returns None ### Source Code ```python def __init__(self: LibLog) -> None: """Initializes the logger.""" if self._initialized: return # Add custom log levels logging.addLevelName(60, "SILENT") logging.SILENT = 60 # General logger self.general_logger = self._setup_logger("libdebug", logging.INFO) # Component-specific loggers self.debugger_logger = self._setup_logger("debugger", logging.SILENT) self.pipe_logger = self._setup_logger("pipe", logging.SILENT) self._initialized = True ``` ``` -------------------------------- ### __init__() Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Initializes the context. Sets default logger levels, symbol level, debuginfod server, and terminal. ```APIDOC ## `__init__()` ### Description Initializes the context. ### Source Code ```python def __init__(self: LibContext) -> None: """Initializes the context.""" if self._initialized: return self._pipe_logger_levels = ["DEBUG", "SILENT"] self._debugger_logger_levels = ["DEBUG", "SILENT"] self._general_logger_levels = ["DEBUG", "INFO", "WARNING", "SILENT"] self._sym_lvl = 5 self._debugger_logger = "SILENT" self._pipe_logger = "SILENT" self._general_logger = "INFO" self._debuginfod_server = "https://debuginfod.elfutils.org/" # Adjust log levels based on command-line arguments if len(sys.argv) > 1: if "debugger" in sys.argv: liblog.debugger_logger.setLevel("DEBUG") self._debugger_logger = "DEBUG" elif "pipe" in sys.argv: liblog.pipe_logger.setLevel("DEBUG") self._pipe_logger = "DEBUG" elif "dbg" in sys.argv: self._set_debug_level_for_all() self._debugger_logger = "DEBUG" self._pipe_logger = "DEBUG" self._general_logger = "DEBUG" self._initialized = True self._terminal = [] ``` ``` -------------------------------- ### Create singleton LibLog instance Source: https://docs.libdebug.org/latest/from_pydoc/generated/liblog Implements the singleton pattern: creates a new instance only if none exists, setting _initialized to False on first creation. Returns the existing instance otherwise. ```python def __new__(cls: type): """Create a new instance of the class if it does not exist yet. Returns: LibLog: the instance of the class. """ if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance ``` -------------------------------- ### Read memory slice by start and end addresses Source: https://docs.libdebug.org/latest/basics/memory_access Access a range of bytes by providing the start and end addresses as integers. ```python d.memory[0x1000:0x1010] ``` -------------------------------- ### Synchronous signal catching example Source: https://docs.libdebug.org/latest/stopping_events/signals Example of synchronous signal catching using the hit_on() method. It registers a catcher for signal 10, continues execution, and checks if the signal was caught. ```python from libdebug import debugger d = debugger("./test_program") d.run() catcher = d.catch_signal(10) d.cont() if catcher.hit_on(d): print("Signal 10 was caught") ``` -------------------------------- ### Create singleton LibContext instance with __new__ Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Implements the singleton pattern for LibContext, returning the existing instance if already created, otherwise creating a new one and marking it as not yet initialized. ```python def __new__(cls: type): """Create a new instance of the class if it does not exist yet. Returns: LibContext: the instance of the class. """ if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance ``` -------------------------------- ### pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) Source: https://docs.libdebug.org/latest/from_pydoc/generated/snapshots/snapshot Pretty print the memory diff. Accepts start and end addresses, and optional parameters for file, word size, and integer mode. ```APIDOC ## pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) ### Description Pretty print the memory diff. ### Method pprint_memory ### Parameters - **start** (int) - Required - The start address of the memory diff. - **end** (int) - Required - The end address of the memory diff. - **file** (str, optional) - The backing file for relative / absolute addressing. Defaults to "hybrid". - **override_word_size** (int, optional) - The word size to use for the diff in place of the ISA word size. Defaults to None. - **integer_mode** (bool, optional) - If True, the diff will be printed as hex integers (system endianness applies). Defaults to False. ### Returns None ### Exceptions - **ValueError** - If the snapshot level is 'base'. ### Example ```python snapshot.pprint_memory(0x1000, 0x2000) ``` ``` -------------------------------- ### __new__ Source: https://docs.libdebug.org/latest/from_pydoc/generated/liblog Create a new instance of the class if it does not exist yet. Returns the singleton instance. ```APIDOC ## __new__ ### Description Create a new instance of the class if it does not exist yet. Returns the singleton instance. ### Method Constructor ### Parameters - **cls** (type) - Required - The class being instantiated. ### Returns - **LibLog** - The instance of the class. ### Source Code ```python def __new__(cls: type): """Create a new instance of the class if it does not exist yet. Returns: LibLog: the instance of the class. """ if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance ``` ``` -------------------------------- ### Example usage of create_snapshot() for thread and process Source: https://docs.libdebug.org/latest/save_states/snapshots Shows how to create a thread snapshot and a process snapshot. The thread snapshot is created with level="full" and name="cool snapshot", while the process snapshot uses level="writable" and name="very cool snapshot". ```python d = debugger("program") my_thread = d.threads[1] # Thread Snapshot ts = my_thread.create_snapshot(level="full", name="cool snapshot") # Process Snapshot ps = d.create_snapshot(level="writable", name="very cool snapshot") ``` -------------------------------- ### Asynchronous signal catchers example Source: https://docs.libdebug.org/latest/stopping_events/signals Example of registering asynchronous signal catchers for SIGUSR1 (signal 10) and SIGINT. The callbacks modify the signal attribute and print messages. The debugger continues and waits. ```python from libdebug import debugger d = debugger("./test_program") d.run() # Define the callback function def catcher_SIGUSR1(t, catcher): t.signal = 0x0 print("Look mum, I'm catching a signal") def catcher_SIGINT(t, catcher): print("Look mum, I'm catching another signal") # Register the signal catchers catcher1 = d.catch_signal(10, callback=catcher_SIGUSR1) catcher2 = d.catch_signal('SIGINT', callback=catcher_SIGINT) d.cont() d.wait() ``` -------------------------------- ### Disable continue to binary entrypoint Source: https://docs.libdebug.org/latest/basics/libdebug101 Sets `continue_to_binary_entrypoint=False` to stop tracing at the loader instead of the binary entrypoint. This is useful when the ELF header is corrupt. ```python from libdebug import debugger debugger = debugger("test", continue_to_binary_entrypoint=False) ``` -------------------------------- ### LibContext class definition with logger and symbol level properties Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Defines the LibContext singleton class that holds global library context, including logger levels, symbol level, and debuginfod server. The __init__ method adjusts logger levels based on command-line arguments (debugger, pipe, dbg) and the sym_lvl setter enforces a range of 0 to 5, raising ValueError otherwise. The debugger_logger setter validates against supported levels and updates the underlying logger. ```python class LibContext: """A class that holds the global context of the library.""" _instance = None _pipe_logger_levels: list[str] _debugger_logger_levels: list[str] _general_logger_levels: list[str] _debuginfod_server: str def __new__(cls: type): """Create a new instance of the class if it does not exist yet. Returns: LibContext: the instance of the class. """ if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self: LibContext) -> None: """Initializes the context.""" if self._initialized: return self._pipe_logger_levels = ["DEBUG", "SILENT"] self._debugger_logger_levels = ["DEBUG", "SILENT"] self._general_logger_levels = ["DEBUG", "INFO", "WARNING", "SILENT"] self._sym_lvl = 5 self._debugger_logger = "SILENT" self._pipe_logger = "SILENT" self._general_logger = "INFO" self._debuginfod_server = "https://debuginfod.elfutils.org/" # Adjust log levels based on command-line arguments if len(sys.argv) > 1: if "debugger" in sys.argv: liblog.debugger_logger.setLevel("DEBUG") self._debugger_logger = "DEBUG" elif "pipe" in sys.argv: liblog.pipe_logger.setLevel("DEBUG") self._pipe_logger = "DEBUG" elif "dbg" in sys.argv: self._set_debug_level_for_all() self._debugger_logger = "DEBUG" self._pipe_logger = "DEBUG" self._general_logger = "DEBUG" self._initialized = True self._terminal = [] def _set_debug_level_for_all(self: LibContext) -> None: """Set the debug level for all the loggers to DEBUG.""" for logger in [ liblog.general_logger, liblog.debugger_logger, liblog.pipe_logger, ]: logger.setLevel("DEBUG") @property def sym_lvl(self: LibContext) -> int: """Property getter for sym_lvl. Returns: _sym_lvl (int): the current symbol level. """ return self._sym_lvl @sym_lvl.setter def sym_lvl(self: LibContext, value: int) -> None: """Property setter for sym_lvl, ensuring it's between 0 and 5.""" if 0 <= value <= 5: self._sym_lvl = value else: raise ValueError("sym_lvl must be between 0 and 5") @property def debugger_logger(self: LibContext) -> str: """Property getter for debugger_logger. Returns: _debugger_logger (str): the current debugger logger level. """ return self._debugger_logger @debugger_logger.setter def debugger_logger(self: LibContext, value: str) -> None: """Property setter for debugger_logger, ensuring it's a supported logging level.""" if value in self._debugger_logger_levels: self._debugger_logger = value liblog.debugger_logger.setLevel(value) else: raise ValueError( f"debugger_logger must be a supported logging level. The supported levels are: {self._debugger_logger_levels}", ) @property def pipe_logger(self: LibContext) -> str: """Property getter for pipe_logger. Returns: _pipe_logger (str): the current pipe logger level. """ return self._pipe_logger ``` -------------------------------- ### Hijacking a syscall example (C and Python) Source: https://docs.libdebug.org/latest/stopping_events/syscalls Example of hijacking a syscall. The C program reads input into a secret buffer; the Python script hijacks the read syscall to write, causing the secret to be leaked to standard output instead of being overwritten. ```c #include char secretBuffer[32] = "The password is 12345678"; int main(int argc, char** argv) { [...] read(0, secretBuffer, 31); [...] return 0; } ``` ```python from libdebug import debugger d = debugger("./test_program") d.run() handler = d.hijack_syscall("read", "write") d.cont() d.wait() out = pipe.recvline() print(out.decode()) ``` -------------------------------- ### pprint_memory function signature Source: https://docs.libdebug.org/latest/save_states/snapshot_diffs Signature of the pprint_memory function for printing memory content diffs within a range. Parameters include start and end addresses, optional file mode (default 'hybrid'), override_word_size, and integer_mode. If start is greater than end, the values are swapped. ```python ts_diff.pprint_memory( start: int, end: int, file: str = "hybrid", override_word_size: int = None, integer_mode: bool = False, ) -> None: ``` -------------------------------- ### platform Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Return the current platform. ```APIDOC ## `platform` property ### Description Return the current platform. ``` -------------------------------- ### Initialize LibContext with default settings and CLI log level adjustments Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Initializes the LibContext singleton, setting default logger levels, symbol level, debuginfod server, and terminal list. Adjusts logger levels based on command-line arguments: 'debugger' sets debugger logger to DEBUG, 'pipe' sets pipe logger to DEBUG, and 'dbg' sets all loggers to DEBUG. ```python def __init__(self: LibContext) -> None: """Initializes the context.""" if self._initialized: return self._pipe_logger_levels = ["DEBUG", "SILENT"] self._debugger_logger_levels = ["DEBUG", "SILENT"] self._general_logger_levels = ["DEBUG", "INFO", "WARNING", "SILENT"] self._sym_lvl = 5 self._debugger_logger = "SILENT" self._pipe_logger = "SILENT" self._general_logger = "INFO" self._debuginfod_server = "https://debuginfod.elfutils.org/" # Adjust log levels based on command-line arguments if len(sys.argv) > 1: if "debugger" in sys.argv: liblog.debugger_logger.setLevel("DEBUG") self._debugger_logger = "DEBUG" elif "pipe" in sys.argv: liblog.pipe_logger.setLevel("DEBUG") self._pipe_logger = "DEBUG" elif "dbg" in sys.argv: self._set_debug_level_for_all() self._debugger_logger = "DEBUG" self._pipe_logger = "DEBUG" self._general_logger = "DEBUG" self._initialized = True self._terminal = [] ``` -------------------------------- ### Debugger.signal Source: https://docs.libdebug.org/latest/from_pydoc/generated/debugger/debugger Property that gets or sets the signal to forward to the main thread. ```APIDOC ## `Debugger.signal` ### Description The signal to be forwarded to the main thread. ### Property `signal` (str | None) - Read-write ### Getter Returns - **str | None** - The signal to be forwarded to the main thread. ### Setter Parameters - **signal** (str | int) - Required - The signal to forward to the main thread. ### Errors - **RuntimeError** - If no threads are available (call `run` or `attach` first). ``` -------------------------------- ### LightweightThreadSnapshot.__init__ method Source: https://docs.libdebug.org/latest/from_pydoc/generated/snapshots/thread/lw_thread_snapshot Constructor for LightweightThreadSnapshot. Initializes the snapshot ID, thread ID, name, and saves registers. The name is set to None if the process snapshot has no name, otherwise it appends the thread ID. ```python def __init__( self: LightweightThreadSnapshot, thread: ThreadContext, process_snapshot: ProcessSnapshot, ) -> None: """Creates a new snapshot object for the given thread. Args: thread (ThreadContext): The thread to take a snapshot of. process_snapshot (ProcessSnapshot): The process snapshot to which the thread belongs. """ # Set id of the snapshot and increment the counter self.snapshot_id = thread._snapshot_count thread.notify_snapshot_taken() # Basic snapshot info self.thread_id = thread.thread_id self.tid = thread.tid # If there is a name, append the thread id if process_snapshot.name is None: self.name = None else: self.name = f"{process_snapshot.name} - Thread {self.tid}" # Get thread registers self._save_regs(thread) self._proc_snapshot = process_snapshot ``` -------------------------------- ### Setting breakpoints and checking hits Source: https://docs.libdebug.org/latest/stopping_events/breakpoints Shows how to set breakpoints at an address and at symbols, then continue execution and check which breakpoint was hit using hit_on(). The file parameter selects the backing file for relative addressing. ```python from libdebug import debugger d = debugger("./test_program") d.run() bp = d.breakpoint(0x10ab, file="binary") bp1 = d.breakpoint("main", file="binary") bp2 = d.breakpoint("printf", file="libc") d.cont() print(f"RAX: {d.regs.rax:#x} at the breakpoint") if bp.hit_on(d): print("Breakpoint at 0x10ab was hit") elif bp1.hit_on(d): print("Breakpoint at main was hit") elif bp2.hit_on(d): print("Breakpoint at printf was hit") ``` -------------------------------- ### pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) Source: https://docs.libdebug.org/latest/from_pydoc/generated/snapshots/snapshot?h= Pretty print the memory diff. ```APIDOC ## pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) ### Description Pretty print the memory diff. ### Method pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) ### Parameters - **start** (int) - Required - The start address of the memory diff. - **end** (int) - Required - The end address of the memory diff. - **file** (str, optional) - The backing file for relative / absolute addressing. Defaults to "hybrid". - **override_word_size** (int, optional) - The word size to use for the diff in place of the ISA word size. Defaults to None. - **integer_mode** (bool, optional) - If True, the diff will be printed as hex integers (system endianness applies). Defaults to False. ### Returns None ### Errors - **ValueError** - If the snapshot level is "base", memory is not available. ``` -------------------------------- ### __init__(thread_id, registers) Source: https://docs.libdebug.org/latest/from_pydoc/generated/state/thread_context Initializes the Thread Context with the given thread ID and register holder. ```APIDOC ## `__init__(thread_id, registers)` ### Description Initializes the Thread Context with the given thread ID and register holder. ### Method __init__ ### Parameters - **thread_id** (int) - Required - The thread ID. - **registers** (RegisterHolder) - Required - The register holder for the thread. ### Returns None ``` -------------------------------- ### MemoryMap dataclass with parse, base, and __repr__ Source: https://docs.libdebug.org/latest/from_pydoc/generated/data/memory_map Defines the MemoryMap dataclass with fields for start, end, permissions, size, offset, and backing_file. Includes a static parse method that converts a /proc/pid/maps string into a MemoryMap, raising ValueError on invalid input. The base property aliases start, and __repr__ returns a hex-formatted string representation. ```python @dataclass class MemoryMap: """A memory map of the target process. Attributes: start (int): The start address of the memory map. You can access it also with the 'base' attribute. end (int): The end address of the memory map. permissions (str): The permissions of the memory map. size (int): The size of the memory map. offset (int): The relative offset of the memory map. backing_file (str): The backing file of the memory map, or the symbolic name of the memory map. """ start: int = 0 end: int = 0 permissions: str = "" size: int = 0 offset: int = 0 """The relative offset of the memory map inside the backing file, if any.""" backing_file: str = "" """The backing file of the memory map, such as 'libc.so.6', or the symbolic name of the memory map, such as '[stack]'.""" @staticmethod def parse(vmap: str) -> MemoryMap: """Parses a memory map from a /proc/pid/maps string representation. Args: vmap (str): The string containing the memory map. Returns: MemoryMap: The parsed memory map. """ try: address, permissions, offset, *_, backing_file = vmap.split(" ", 6) start = int(address.split("-")[0], 16) end = int(address.split("-")[1], 16) size = end - start int_offset = int(offset, 16) backing_file = backing_file.strip() if not backing_file: backing_file = f"anon_{start:x}" except ValueError as e: raise ValueError( f"Invalid memory map: {vmap}. Please specify a valid memory map.", ) from e return MemoryMap(start, end, permissions, size, int_offset, backing_file) @property def base(self: MemoryMap) -> int: """Alias for the start address of the memory map.""" return self.start def __repr__(self: MemoryMap) -> str: """Return the string representation of the memory map.""" return f"MemoryMap(start={hex(self.start)}, end={hex(self.end)}, permissions={self.permissions}, size={hex(self.size)}, offset={hex(self.offset)}, backing_file={self.backing_file})" ``` -------------------------------- ### MemoryMapSnapshotList.__init__ constructor Source: https://docs.libdebug.org/latest/from_pydoc/generated/snapshots/memory/memory_map_snapshot_list Initializes a MemoryMapSnapshotList by calling the parent list constructor with the provided memory maps and storing the process name and full path. The process name and full path are used later by the filter method to resolve the 'binary' alias. ```python def __init__( self: MemoryMapSnapshotList, memory_maps: list[MemoryMapSnapshot], process_name: str, full_process_path: str, ) -> None: """Initializes the MemoryMapSnapshotList.""" super().__init__(memory_maps) self._process_full_path = full_process_path self._process_name = process_name ``` -------------------------------- ### Get terminal property Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Getter for the terminal property. Returns the current terminal stored in _terminal. ```python @property def terminal(self: LibContext) -> list[str]: """Property getter for terminal. Returns: _terminal (str): the current terminal. """ return self._terminal ``` -------------------------------- ### Run libdebug test suite with python3 run_suite.py Source: https://docs.libdebug.org/latest/development/building_libdebug Run the libdebug test suite from the test directory. Replace with one of the available suites: fast, slow, stress, or memory. The fast suite is the default and skips some time-consuming tests. ```bash cd test python3 run_suite.py ``` -------------------------------- ### pprint_memory() function signature Source: https://docs.libdebug.org/latest/quality_of_life/pretty_printing Signature of the pprint_memory() function. It prints the contents of process memory within a given address range. The start and end addresses are required; if start is greater than end, the values are swapped. Optional parameters include file (defaults to "hybrid"), override_word_size (defaults to the ISA register size), and integer_mode (defaults to False). ```python d.pprint_memory( start: int, end: int, file: str = "hybrid", override_word_size: int = None, integer_mode: bool = False, ) -> None: ``` -------------------------------- ### Memory Addressing Modes Source: https://docs.libdebug.org/latest/basics/memory_access Examples of using absolute, hybrid, and relative addressing when accessing memory in libdebug. ```python # Absolute addressing d.memory[0x7ffff7fcb200, 0x10, "absolute"] # Hybrid addressing d.memory[0x1000, 0x10, "hybrid"] # Relative addressing d.memory[0x1000, 0x10, "binary"] d.memory[0x1000, 0x10, "libc"] ``` -------------------------------- ### pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) Source: https://docs.libdebug.org/latest/from_pydoc/generated/snapshots/diff Pretty print the memory diff between two snapshots. ```APIDOC ## pprint_memory(start, end, file='hybrid', override_word_size=None, integer_mode=False) ### Description Pretty print the memory diff between two snapshots. ### Method pprint_memory ### Parameters #### Path Parameters - **start** (int) - Required - The start address of the memory diff. - **end** (int) - Required - The end address of the memory diff. #### Query Parameters - **file** (str) - Optional - The backing file for relative / absolute addressing. Defaults to "hybrid". - **override_word_size** (int) - Optional - The word size to use for the diff in place of the ISA word size. Defaults to None. - **integer_mode** (bool) - Optional - If True, the diff will be printed as hex integers (system endianness applies). Defaults to False. ### Request Example No request body required. ### Response #### Success Response (200) No structured response body; prints the memory diff to stdout. #### Response Example No response body example available. ``` -------------------------------- ### ProcessSnapshot.__init__ Source: https://docs.libdebug.org/latest/from_pydoc/generated/snapshots/process/process_snapshot Creates a new ProcessSnapshot instance for the given process, capturing its state at the time of creation. The snapshot level determines the amount of memory detail saved. ```APIDOC ## __init__(debugger, level='base', name=None) ### Description Creates a new snapshot object for the given process. The `level` parameter controls the depth of memory snapshot: 'base' stores only memory maps, 'writable' and 'full' store memory contents. ### Signature ```python def __init__(self: ProcessSnapshot, debugger: InternalDebugger, level: str = "base", name: str | None = None) -> None ``` ### Parameters - **debugger** (Debugger) - Required - The process debugger to take a snapshot of. - **level** (str) - Optional - The snapshot level: "base", "writable", or "full". Defaults to "base". - **name** (str | None) - Optional - A name associated with the snapshot. Defaults to None. ### Returns None ``` -------------------------------- ### Get debuginfod_server property Source: https://docs.libdebug.org/latest/from_pydoc/generated/utils/libcontext Getter for the debuginfod_server property. Returns the current debuginfod server stored in _debuginfod_server. ```python @property def debuginfod_server(self: LibContext) -> str: """Property getter for debuginfod_server. Returns: _debuginfod_server (str): the current debuginfod server. """ return self._debuginfod_server ``` -------------------------------- ### Source paths for autodetect_fpregs_layout (x86 and AArch64) Source: https://docs.libdebug.org/latest/development/building_libdebug Source files for the autodetect_fpregs_layout executable. Use the x86 file for i386/AMD64 architectures and the AArch64 file for AArch64. The AArch64 file is a dummy because the layout is fixed and does not require autodetection. ```c libdebug/ptrace/native/shared/x86_autodetect_fpregs_layout.c ``` ```c libdebug/ptrace/native/aarch64/aarch64_autodetect_fpregs_layout.c ``` -------------------------------- ### Define the Symbol dataclass with hashing and representation Source: https://docs.libdebug.org/latest/from_pydoc/generated/data/symbol The complete `Symbol` dataclass definition includes fields for the symbol's start and end addresses, name, backing file, reference file, optional build ID, and external status. A custom `__hash__` method uses start, end, name, and backing_file for hashing, while `__repr__` provides a detailed, hex-formatted string for debugging. ```python @dataclass class Symbol: """A symbol in the target process. start (int): The start address of the symbol in the target process. end (int): The end address of the symbol in the target process. name (str): The name of the symbol in the target process. backing_file (str): The backing file of the symbol in the target process. reference_file (str): The file that the symbol's offsets refer to in the target process. reference_build_id (str | None): The build ID of the reference file. is_external (bool): Whether the symbol is external or not. """ start: int end: int name: str backing_file: str reference_file: str reference_build_id: str | None is_external: bool def __hash__(self: Symbol) -> int: """Returns the hash of the symbol.""" return hash((self.start, self.end, self.name, self.backing_file)) def __repr__(self: Symbol) -> str: """Returns the string representation of the symbol.""" return f"Symbol(start={self.start:#x}, end={self.end:#x}, name={self.name}, backing_file={self.backing_file}, reference_file={self.reference_file}, reference_build_id={self.reference_build_id}, is_external={self.is_external})" ```