Skip to content

Process API

The Process API is the main interface for dynamic analysis with Pyda. It provides methods for hooking execution, intercepting system calls, and manipulating process state.

Bases: ProcessTube

Obtain a Process via the module-level process function.

Example

Obtain a Process with process().

from pyda import *

# process() returns a Process instance.
p = process()

# p.run() starts the process.
p.run()

The Process class provides the primary API for instrumenting and analyzing target processes. It allows you to set hooks, intercept system calls, and manipulate process state during execution.

This class extends ProcessTube, providing pwntools-compatible I/O operations alongside the dynamic analysis capabilities.

Attributes:

Name Type Description
regs ProcessRegisters

Interface for reading/writing CPU registers

mem ProcessMemory

Interface for reading/writing process memory

maps ProcessMaps

Interface for querying memory mappings

exe_path str

Path to the main executable being analyzed

tid int

Current thread ID (valid during hook execution)

Example

Basic usage pattern:

from pyda import *

# Get process handle
p = process()

# Set up hooks
def my_hook(p):
    print(f"Hit address, RAX={hex(p.regs.rax)}")

p.hook(0x401000, my_hook)

# Start analysis
p.run()

The following options can be passed to process():

Parameters:

Name Type Description Default
io bool

Whether to capture I/O for pwntools compatibility. When True, enables recv/send operations.

False

tid property

Get the current thread ID (starts from 1).

Note

This property is only valid during hook execution or when the process is stopped.

hook(addr, callback, priority=False, later=False)

Register a hook at the specified address.

Hooks are functions called before executing the instruction at the specified program counter.

Note

Multiple hooks can be registered at the same address and will be called in registration order (unless priority=True).

Parameters:

Name Type Description Default
addr int

Memory address where the hook should be installed

required
callback callable

Function to call when the hook is hit. Should accept one argument (the Process instance)

required
priority bool

If True, adds this hook before existing hooks at the same address

False
later bool

If True, registers the hook but doesn't install it until the address is reached for the first time

False
Example
def main_hook(p):
    print(f"Entered main at {hex(p.regs.rip)}")
    print(f"Stack pointer: {hex(p.regs.rsp)}")

p.hook(0x401234, main_hook)

unhook(addr, callback=None, unregister=True)

Remove a hook from the specified address.

Parameters:

Name Type Description Default
addr int

Address where the hook is installed

required
callback callable

Specific callback to remove. If None, removes all hooks at the address

None
unregister bool

Whether to unregister the hook from DynamoRIO if no callbacks remain

True

hook_after_call(addr, callback)

Register a hook that fires when a function call returns.

This is a convenience method that hooks a function entry point and automatically sets up a hook at the return address.

Parameters:

Name Type Description Default
addr int

Address of the function to hook

required
callback callable

Function to call when the function returns

required
Example
def after_malloc(p):
    ptr = p.regs.rax  # Return value in RAX
    print(f"malloc returned: {hex(ptr)}")

p.hook_after_call(libc.symbols['malloc'], after_malloc)
Note

This assumes x86-64 calling convention where return addresses are stored on the stack.

syscall_pre(syscall_num, callback)

Register a pre-syscall hook for the specified system call.

Pre-syscall hooks are called before the system call is executed, allowing inspection and modification of arguments. They can also prevent the syscall from executing by returning False.

Parameters:

Name Type Description Default
syscall_num int

System call number to hook (e.g. 1 for write)

required
callback callable

Function to call before the syscall. Should accept (process, syscall_num) arguments. Can return False to skip the syscall.

required
Example
def block_write(p, syscall_num):
    if p.regs.rdi == 1:  # stdout
        print("Blocking write to stdout")
        p.regs.rax = -1  # Set error return value
        return False  # Skip the syscall

p.syscall_pre(1, block_write)  # Hook SYS_write

Raises:

Type Description
RuntimeError

If called after the process has started running

syscall_post(syscall_num, callback)

Register a post-syscall hook for the specified system call.

Post-syscall hooks are called after the system call completes, allowing inspection of return values and side effects.

Parameters:

Name Type Description Default
syscall_num int

System call number to hook

required
callback callable

Function to call after the syscall. Should accept (process, syscall_num) arguments.

required
Example
def log_write_result(p, syscall_num):
    bytes_written = p.regs.rax
    print(f"write() returned {bytes_written}")

p.syscall_post(1, log_write_result)

Raises:

Type Description
RuntimeError

If called after the process has started running

set_thread_entry(callback)

Register a callback for when new threads are created.

Parameters:

Name Type Description Default
callback callable

Function to call when a new thread starts. Should accept one argument (the Process instance).

required
Example
def new_thread(p):
    print(f"New thread created: TID {p.tid}")

p.set_thread_entry(new_thread)

on_module_load(callback)

Register a callback for when new modules are loaded.

Parameters:

Name Type Description Default
callback callable

Function to call when a module loads. Should accept appropriate arguments from DynamoRIO.

required

read(addr, size)

Read memory from the target process. Returns bytes.

Parameters:

Name Type Description Default
addr int

Memory address to read from

required
size int

Number of bytes to read

required
Example
# Read 16 bytes from the stack
stack_data = p.read(p.regs.rsp, 16)

write(addr, data)

Write data to the target process memory.

Parameters:

Name Type Description Default
addr int

Memory address to write to

required
data bytes

Data to write

required
Example
# Write a string to memory
p.write(0x401000, b"Hello World\x00")

run()

Start or resume execution of the target process.

This method begins execution and will run until the process exits or encounters a blocking condition (like waiting for I/O).

Example
p = process()
p.hook(0x401000, my_hook)
p.run()  # Start execution

run_until(addr)

Run the process until it reaches the specified address.

Parameters:

Name Type Description Default
addr int

Address to run until

required
Example
# Run until we hit main
p.run_until(0x401234)
print(f"Stopped at main, RAX={hex(p.regs.rax)}")

run_from_to(start, end)

Jump to a specific address and run until another address is reached.

Parameters:

Name Type Description Default
start int

Address to jump to

required
end int

Address to run until

required
Note

This cannot be used from within hooks.

callable(addr)

Create a callable that executes instrumented target code.

Returns a function that, when called, will execute the function at the specified address with the given arguments, following standard calling conventions.

Parameters:

Name Type Description Default
addr int

Address of the function to call

required
Example
# Create a callable for malloc
malloc = p.callable(libc.symbols['malloc'])

# Call malloc(100) 
p.run_until(main_addr)  # Establish stack first
ptr = malloc(100)
Note
  • Cannot be used from within hooks
  • Requires the process to have a valid stack
  • Currently supports up to 6 arguments (x86-64 limitation)

backtrace()

Get a string representation of the current call stack.

Example
def crash_hook(p):
    print("Crash detected!")
    print("Backtrace:")
    print(p.backtrace())

backtrace_cpp(short=False)

Get a C++ demangled backtrace of the current call stack.

Parameters:

Name Type Description Default
short bool

If True, truncate long symbol names

False

Memory and Register Interfaces

pyda.proc.ProcessRegisters(p)

Interface for reading and writing CPU registers.

Provides convenient access to CPU registers using both attribute and dictionary-style syntax.

Example
# Reading registers
rax_val = p.regs.rax
rsp_val = p.regs['rsp']

# Writing registers  
p.regs.rax = 0x1337
p.regs['rbx'] = 0x2000

__getitem__(name)

__setitem__(name, value)

has_reg(name)

Check if a register name is valid.

pyda.proc.ProcessMemory(p)

Interface for reading and writing process memory.

Provides convenient slice-based access to target process memory.

Example
# Read single byte
byte_val = p.mem[0x401000]

# Read range of bytes
data = p.mem[0x401000:0x401010]  # 16 bytes

# Write data
p.mem[0x401000:0x401004] = b"\x41\x41\x41\x41"

__getitem__(key)

pyda.proc.ProcessMaps(p)

Interface for querying process memory mappings.

Example
# Get base address of main executable
main_base = p.maps[p.exe_path].base

# Get libc base
libc_base = p.maps["libc.so.6"].base

__getitem__(key)

pyda.proc.Map(vaddr: int, size: int, path: str, perms: int) dataclass

Represents a memory mapping in the target process.

base property

Base address of this mapping.

start property

Start address (same as base).

end property

End address of this mapping.

executable property

True if mapping has execute permissions.

writable property

True if mapping has write permissions.

readable property

True if mapping has read permissions.