pyplugins.wrappers.ptregs_wrap module

ptregs_wrap.py - Architecture-agnostic wrappers for Linux pt_regs structures

This module provides Pythonic, type-annotated wrappers for Linux kernel pt_regs structures across multiple CPU architectures. It enables convenient, architecture-independent access to process register state, such as that captured at system call entry/exit, exceptions, or context switches. The wrappers abstract away the raw struct layout and provide a unified interface for reading/writing registers, extracting syscall arguments, and handling calling conventions.

Overview

The module defines a base PtRegsWrapper class and a set of subclasses for each supported architecture (x86, x86_64, ARM, AArch64, MIPS, PowerPC, LoongArch64, RISC-V, etc). Each subclass knows how to access registers and arguments according to its architecture’s ABI and pt_regs layout. The wrappers can be used with PANDA or other emulation/analysis frameworks that expose pt_regs-like objects.

The module also provides a get_pt_regs_wrapper() factory function to select the correct wrapper for a given architecture.

Typical Usage

Suppose you have a PANDA plugin or other tool that provides a pt_regs struct (e.g., at a syscall, exception, or context switch):

from wrappers.ptregs_wrap import get_pt_regs_wrapper
# Assume 'regs' is a pt_regs struct and 'panda' is a PANDA object
wrapper = get_pt_regs_wrapper(panda, regs, arch_name=panda.arch_name)

# Access registers in an architecture-agnostic way
pc = wrapper.get_pc()
sp = wrapper.get_sp()
retval = wrapper.get_retval()

# Get syscall arguments (handles calling convention automatically)
arg0 = wrapper.get_syscall_arg(0)
arg1 = wrapper.get_syscall_arg(1)
# Or get userland function arguments
user_arg0 = wrapper.get_userland_arg(0)

# Dump all registers as a dictionary
reg_dict = wrapper.dump()

# Coroutine-style argument access (portal):
# get_args_portal and get_arg_portal are generator-based and can yield if a memory read is required (e.g., stack argument).
# Use 'yield from' to drive these coroutines in a portal/coroutine context.
args = yield from wrapper.get_args_portal(3)

The wrappers also support advanced features such as handling 32-bit compatibility mode on x86_64/AArch64, stack argument extraction, and portal-style coroutine memory reads. The get_args_portal and get_arg_portal methods are generator-based and will yield if a memory read is required (such as when reading stack arguments may fail and need to be retried or handled asynchronously).

Classes

  • PtRegsWrapper: Base class for all pt_regs wrappers, provides generic register access and argument extraction.

  • X86PtRegsWrapper, X86_64PtRegsWrapper, ArmPtRegsWrapper, …: Architecture-specific subclasses.

  • PandaMemReadFail: Exception for failed memory reads (for portal/coroutine use).

Functions

  • get_pt_regs_wrapper(panda: Optional[Any], regs: Any, arch_name: Optional[str] = None) -> PtRegsWrapper: Factory to select the correct wrapper for a given architecture.

These wrappers are useful for dynamic analysis, syscall tracing, emulation, and any tool that needs to reason about process register state in a cross-architecture way.

class pyplugins.wrappers.ptregs_wrap.AArch64PtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for AArch64 pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_register(reg_name)[source]

Get register value by name, handling AArch32 compatibility mode if needed

Parameters:

reg_name (str)

Return type:

int | None

get_syscall_arg(num)[source]

Get AArch64 syscall argument, considering compatibility mode

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number, considering compatibility mode. In AArch64, the syscall number is in syscallno. In AArch32 mode, use ARM implementation.

Return type:

int | None

get_userland_arg(num)[source]

Get AArch64 userland argument, considering compatibility mode

Parameters:

num (int)

Return type:

int | None

i = 30
in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

set_register(reg_name, value)[source]

Set register value by name, handling AArch32 compatibility mode if needed

Parameters:
  • reg_name (str)

  • value (int)

Return type:

bool

class pyplugins.wrappers.ptregs_wrap.ArmPtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for ARM pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_syscall_arg(num)[source]

Get ARM syscall argument

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number from r7 register

Return type:

int | None

get_userland_arg(num)[source]

Get ARM userland argument

Parameters:

num (int)

Return type:

int | None

in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

class pyplugins.wrappers.ptregs_wrap.LoongArch64PtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for LoongArch64 pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

field = 'csr_estat'
get_syscall_arg(num)[source]

Get LoongArch64 syscall argument

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number from a7 register

Return type:

int | None

get_userland_arg(num)[source]

Get LoongArch64 userland argument

Parameters:

num (int)

Return type:

int | None

idx = 31
in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

name = 's8'
class pyplugins.wrappers.ptregs_wrap.Mips64PtRegsWrapper(obj, panda=None)[source]

Bases: MipsPtRegsWrapper

Wrapper for MIPS64 pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_syscall_arg(num)[source]

Get MIPS64 syscall argument

Parameters:

num (int)

Return type:

int | None

get_userland_arg(num)[source]

Get MIPS64 userland argument

Parameters:

num (int)

Return type:

int | None

class pyplugins.wrappers.ptregs_wrap.MipsPtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for MIPS pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_syscall_arg(num)[source]

Get MIPS syscall argument

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number from v0 register

Return type:

int | None

get_userland_arg(num)[source]

Get MIPS userland argument

Parameters:

num (int)

Return type:

int | None

idx = 31
in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

name = 'ra'
exception pyplugins.wrappers.ptregs_wrap.PandaMemReadFail(addr, size)[source]

Bases: Exception

Exception for failed memory reads, used for portal/coroutine use-cases.

Attributes:

addr (int): The address that failed to read. size (int): The size of the attempted read.

Parameters:
  • addr (int)

  • size (int)

Return type:

None

class pyplugins.wrappers.ptregs_wrap.PowerPC64PtRegsWrapper(obj, panda=None)[source]

Bases: PowerPCPtRegsWrapper

Wrapper for PowerPC64 pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

class pyplugins.wrappers.ptregs_wrap.PowerPCPtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for PowerPC pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_syscall_arg(num)[source]

Get PowerPC syscall argument

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number from r0 register

Return type:

int | None

get_userland_arg(num)[source]

Get PowerPC userland argument

Parameters:

num (int)

Return type:

int | None

i = 31
in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

reg = 'result'
class pyplugins.wrappers.ptregs_wrap.PtRegsWrapper(obj, panda=None)[source]

Bases: Wrapper

Base class for pt_regs wrappers across different architectures.

Args:

obj: The pt_regs structure to wrap. panda: Optional PANDA object for memory reading.

Parameters:
  • obj (Any)

  • panda (Any | None)

property REG_NAMES: List[str]

Returns a list of all valid register names for this architecture.

dump()[source]

Dump all registers to a dictionary.

Return type:

Dict[str, int | None]

get_arg(num, convention=None)[source]

Get function argument based on calling convention.

Args:

num: Argument number (0-based) convention: Calling convention (‘syscall’ or ‘userland’)

Returns:

The value of the requested argument

Parameters:
  • num (int)

  • convention (str | None)

Return type:

int | None

get_arg_portal(num, convention=None)[source]

Coroutine/generator version of get_arg for portal/coroutine use.

Args:

num: Argument number (0-based) convention: Calling convention (‘syscall’ or ‘userland’)

Returns:

The value of the requested argument (or None if unavailable).

Parameters:
  • num (int)

  • convention (str | None)

Return type:

Generator[int | None, Any, int | None]

get_args(count, convention=None)[source]

Get a list of function arguments according to the calling convention.

Args:

count: Number of arguments to retrieve. convention: Calling convention (‘syscall’ or ‘userland’).

Returns:

List of argument values (may include None if unavailable).

Parameters:
  • count (int)

  • convention (str | None)

Return type:

List[int | None]

get_args_portal(count, convention=None)[source]

Coroutine/generator version of get_args for portal/coroutine use.

Args:

count: Number of arguments to retrieve. convention: Calling convention (‘syscall’ or ‘userland’).

Returns:

List of argument values (may include None if unavailable).

Parameters:
  • count (int)

  • convention (str | None)

Return type:

Generator[int | None, Any, List[int | None]]

get_pc()[source]

Get program counter.

Return type:

int | None

get_register(reg_name)[source]

Get register value by name (Optimized).

Parameters:

reg_name (str)

Return type:

int | None

get_retaddr()[source]

Get the return address (best guess for this architecture). Supports both generator and non-generator implementations in subclasses.

Return type:

int | None

get_retaddr_portal()[source]

Coroutine/generator version of get_retaddr for portal/coroutine use.

Return type:

Generator[int | None, Any, int | None]

get_return_address()[source]

Alias for get_retaddr.

Return type:

int | None

get_return_value()[source]

Get return value (typically in a0/r0/rax).

Return type:

int | None

get_retval()[source]

Get return value (alias for get_return_value).

Return type:

int | None

get_sp()[source]

Get stack pointer.

Return type:

int | None

get_syscall_arg(num)[source]

Get syscall argument. Subclasses must implement.

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number. Subclasses must implement.

Return type:

int | None

get_userland_arg(num)[source]

Get userland argument. Subclasses must implement.

Parameters:

num (int)

Return type:

int | None

in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

read_stack_arg(arg_num, word_size=None)[source]

Read a function argument from the stack.

Args:

arg_num: Argument number (0-based). word_size: Word size override (default: based on architecture).

Returns:

The argument value read from the stack.

Parameters:
  • arg_num (int)

  • word_size (int | None)

Return type:

int | None

set_pc(value)[source]

Set program counter.

Parameters:

value (int)

Return type:

None

set_register(reg_name, value)[source]

Set register value by name (Optimized).

Parameters:
  • reg_name (str)

  • value (int)

Return type:

bool

set_retval(value)[source]

Set the return value (typically in a0/r0/rax).

Parameters:

value (int)

Return type:

None

to_bytes()[source]

Pass-through to underlying bound object for serialization.

class pyplugins.wrappers.ptregs_wrap.Riscv32PtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for RISC-V 32-bit pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

abi_name = 't6'
get_syscall_arg(num)[source]

Get RISC-V 32-bit syscall argument

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number from a7 register

Return type:

int | None

get_userland_arg(num)[source]

Get RISC-V 32-bit userland argument

Parameters:

num (int)

Return type:

int | None

in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

x_reg = 'x31'
class pyplugins.wrappers.ptregs_wrap.Riscv64PtRegsWrapper(obj, panda=None)[source]

Bases: Riscv32PtRegsWrapper

Wrapper for RISC-V 64-bit pt_regs - same structure as 32-bit but with 64-bit registers

Parameters:
  • obj (Any)

  • panda (Any | None)

get_userland_arg(num)[source]

Get RISC-V 64-bit userland argument

Parameters:

num (int)

Return type:

int | None

class pyplugins.wrappers.ptregs_wrap.X86PtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for x86 (32-bit) pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_syscall_arg(num)[source]

Get x86 syscall argument

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number from EAX register

Return type:

int | None

get_userland_arg(num)[source]

Get x86 userland argument from stack

Parameters:

num (int)

Return type:

int | None

in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

class pyplugins.wrappers.ptregs_wrap.X86_64PtRegsWrapper(obj, panda=None)[source]

Bases: PtRegsWrapper

Wrapper for x86_64 pt_regs

Parameters:
  • obj (Any)

  • panda (Any | None)

get_register(reg_name)[source]

Get register value by name (Optimized).

Parameters:

reg_name (str)

Return type:

int | None

get_syscall_arg(num)[source]

Get x86_64 syscall argument, considering compatibility mode

Parameters:

num (int)

Return type:

int | None

get_syscall_number()[source]

Get syscall number, considering compatibility mode. In x86_64, the syscall number is in orig_rax. In compatibility mode, use x86 implementation.

Return type:

int | None

get_userland_arg(num)[source]

Get x86_64 userland argument, considering compatibility mode

Parameters:

num (int)

Return type:

int | None

in_kernel()[source]

Returns True if the pt_regs represents kernel mode, False otherwise. Subclasses should override for architecture-specific logic.

Return type:

bool

k = 'rsp'
set_register(reg_name, value)[source]

Set register value by name (Optimized).

Parameters:
  • reg_name (str)

  • value (int)

Return type:

bool

v = 'sp'
pyplugins.wrappers.ptregs_wrap.get_pt_regs_wrapper(panda, regs, arch_name=None)[source]

Factory function to create the appropriate pt_regs wrapper based on architecture.

Args:

panda: PANDA object (may be used to determine architecture if arch_name not provided) regs: The pt_regs structure to wrap arch_name: Architecture name (optional, will be determined from PANDA if not provided)

Returns:

An appropriate PtRegsWrapper subclass instance.

Parameters:
  • panda (Any | None)

  • regs (Any)

  • arch_name (str | None)

Return type:

PtRegsWrapper