Skip to main content

Asynchronous ps4debug implementation in Python.

Project description

PyPS4debug

A modern, asynchronous Python client for interacting with a PlayStation 4 running a compatible debug payload.

This library provides high-level APIs for process inspection, memory access, debugging, and remote code execution over a TCP interface.

Tested with ps4debug v1.0.15 and v1.1.19.


⚠️ Disclaimer

Interacting with system memory and debugging processes can damage your device, cause instability, or lead to permanent hardware failure.

This project does not implement the payload or server running on the target device—it only provides a client for communicating with it.

You assume all responsibility for any damage, data loss, or unintended behavior resulting from the use of this library.


⚠️ Important Notes

  • Code examples in this documentation are not directly runnable.

  • Values such as:

    • IP addresses
    • Process IDs (PID)
    • Memory addresses will differ depending on your environment and target system.

You are expected to adapt examples to your specific setup.


Features

  • Automatic device discovery on local network
  • Async TCP client with connection pooling
  • Process enumeration and inspection
  • Memory read/write and allocation
  • Kernel memory access
  • Remote procedure calls (RPC)
  • ELF and raw payload injection
  • Debugging session management
  • Memory scanning utilities
  • Console output and notifications

Installation

pip install ps4debug

Quick Start

import asyncio
from ps4debug import PS4Debug

async def main():
    # Discover a PS4 running the debug payload
    ps4 = await PS4Debug.discover()

    # Get version info
    version = await ps4.get_version()
    print("Version:", version)

    # List processes
    processes = await ps4.get_processes()
    for proc in processes:
        print(proc)

asyncio.run(main())

Core Concepts

Client

The PS4Debug class is the main entry point. It manages connections and exposes all high-level functionality.

from ps4debug import PS4Debug

ps4 = PS4Debug("192.168.0.10")

Processes

Retrieve and inspect running processes:

processes = await ps4.get_processes()
info = await ps4.get_process_info(pid)
maps = await ps4.get_process_maps(pid)

Payloads

Raw Payload

await ps4.send_payload(payload_bytes)

ELF Injection

await ps4.send_elf(pid, elf_bytes)

Console Interaction

await ps4.print("Hello from Python")
await ps4.notify("Done")

System Control

await ps4.reboot()

kernel_base = await ps4.get_kernel_base()
data = await ps4.read_kernel_memory(address, length)

Memory Access

Raw Memory

data = await ps4.read_memory(pid, address, length)
await ps4.write_memory(pid, address, b"\x90\x90")

Allocated Memory (Recommended)

The preferred way to work with memory is through MemoryContext, which manages allocation and cleanup automatically.

async with ps4.memory(pid, length=1024) as mem:
    await mem.write(b"hello")
    data = await mem.read(5)

Key properties:

  • Memory is allocated on __aenter__
  • Memory is always freed on __aexit__
  • Safe bounds checking is enforced for reads/writes
  • Prevents leaks and invalid memory reuse

Important: Once the context exits, the memory is no longer valid on the target system.


Structured Memory Access

MemoryContext supports structured data using ConstructModel:

value = await mem.read_model(MyModel)
await mem.write_model(my_model_instance)

Memory Views (Typed Access)

MemoryView provides a type-safe interface over memory.

Creating a View

view = ps4.view(pid, address)

Or from an allocation:

view = mem.view()

Reading Values

value = await view.uint32(offset=0x10).get()

Shorthand:

flag = await view.boolean(offset=0x20)

Writing Values

await view.uint32(offset=0x10).set(1337)

Shorthand:

await view.uint32(offset=0x10)(1337)

Offsetting Views

sub = view.offset(by=0x100)
value = await sub.uint16()

Supported Types

  • Integers: int8, uint8, int16, uint16, int32, uint32, int64, uint64
  • Floating point: floating, double
  • Boolean: boolean
  • Raw bytes: bytes(size)
  • Strings: string(length)
  • Structured models: model(MyModel)

Strings

text = await view.string(length=32, offset=0x0)

Or null-terminated:

text = await view.read_variable_text()

Memory Protection

await mem.change_protection(prot)

Executing Code from Allocated Memory

Allocated memory can be used as an execution target:

result = await mem.call(params=my_model, result_model=MyReturnModel)

This is a convenience wrapper around PS4Debug.call() using the allocated address.


Remote Procedure Calls (RPC)

Execute functions inside a target process:

result = await ps4.call(
    pid=pid,
    address=0x12345678,
    params=my_model,
    return_model=MyReturnModel
)

There is a CallRegisters model that can be used to get started. Its values are serialized to 8-byte unsigned integers each.

from ps4debug import CallRegisters

registers = CallRegisters(
    rdi=1,
    rsi=2,
    rdx=3,
    rcx=4,
    r8=5,
    r9=6,
)

Return Model Requirements

Return values can be parsed into a model using a custom base class:

from typing import Annotated
from construct import Int32ul
from pydantic_construct import ConstructModel

class MyReturnModel(ConstructModel):
    number: Annotated[int, Int32ul]

Constraints:

  • The model must inherit from ConstructModel
  • The total size must not exceed 8 bytes (size of the RAX register)
  • If no return model is provided, raw bytes are returned

Debugging

Debugging is exposed through an async context manager returning a DebuggingContext.

async with ps4.debugger(pid) as dbg:
    await dbg.resume_process()

Only one debugging session can be active at a time.


Breakpoints

Breakpoints are managed using indexed slots.

index = await dbg.add_breakpoint(address=0x12345678, callback=lambda event: ...)

You can also configure them manually:

await dbg.set_breakpoint(index, address, callback, enabled=True)
  • Limited number of breakpoint slots
  • Managed internally via index and address mapping

Breakpoint Callbacks

Callbacks are async functions triggered when a breakpoint is hit:

async def on_break(event):
    print("Breakpoint hit at", hex(event.interrupt.regs.rip))
    event.resume = True  # resume execution automatically

await dbg.add_breakpoint(address, on_break)

You can also register a global callback:

dbg.register_callback(global_handler)

Execution order:

  1. Global callback
  2. Breakpoint-specific callback

Watchpoints

Hardware watchpoints can be configured:

await dbg.set_watchpoint(index, address, enabled=True)
  • Supports read/write monitoring
  • Limited hardware slots

Process Control

await dbg.stop_process()
await dbg.resume_process()
await dbg.kill_process()

Threads

threads = await dbg.get_threads()
info = await dbg.get_thread_info(thread_id)

Thread-level control exists but may depend on server-side support:

await dbg.stop_thread(thread_id)
await dbg.resume_thread(thread_id)

Registers

General Purpose

regs = await dbg.get_registers(thread_id)
await dbg.set_registers(thread_id, regs)

Floating Point

fp = await dbg.get_fp_registers(thread_id)

Debug Registers

dbg_regs = await dbg.get_debug_registers(thread_id)

Single Stepping

await dbg.single_step()

Executes exactly one instruction.


Event Handling Model

  • Debug events are received over a local TCP server
  • Events are processed asynchronously
  • Callbacks are awaited, the debugger will not continue until all callbacks ran
  • Execution can optionally resume automatically via event.resume = True (default behavior)

Memory Scanning

Memory scanning is performed using a builder-based query system.

PS4Debug.scan returns a local scanner, meaning:

  • Memory is downloaded from the target
  • Scanning happens on the client side

Basic Usage

There are three ways to run scan queries.

Bounded queries

scanner.query() returns a builder object that can run itself.

scanner = ps4.scan(pid)

results = await (
    scanner
    .query()
    .int32()
    .exact(100)
    .execute()
)

Context builder

async with scanner.executor() as q:
    q.int32()
    q.exact(100)

The query will execute when the context is exited.

Manual

If the above two methods don't suit your needs you can simply create a builder.

builder = ScanBuilder()
builder.int32().exact(100)

scanner.execute(builder)

Builder Pattern

The ScanBuilder allows incremental query construction:

builder = scanner.query()

builder.int32().bigger(100).aligned(True)
results = await builder.execute()

Common Scan Types

Exact Match

.int32().exact(1337)

Range

.int32().between(low=100, high=200)

Increased / Decreased

.increased(to=150)
.decreased(to=50)
.changed(None)

Unknown Initial Value

.unknown_initial_value()

Filtering

Memory Bounds

.bounds(start, end)

Module Filtering

.only_module("libSce.*")

Pause Target

.pause(True)

Iterative Scanning

async for addr, value in scanner.query().int32().exact(100).execute_iter():
    print(hex(addr), value)
  • Supports large result sets
  • Updates internal scan state automatically

Scan Lifecycle

Each session tracks:

  • initial values
  • previous values

Subsequent scans operate as refinements rather than full scans.


Error Handling

Operations may raise multiple exception types depending on the failure source:

  • PS4DebugException – protocol-level or server-side failures
  • ValueError, RuntimeError – invalid usage or state
  • asyncio exceptions – timeouts, cancellations
  • Parsing/validation errors from underlying libraries (e.g. model decoding from Pydantic or construct)

Example:

from ps4debug.exceptions import PS4DebugException

try:
    await ps4.reboot()
except PS4DebugException as e:
    print("Operation failed:", e)

License

This project is licensed under the 0BSD License.

You are free to use, modify, and distribute this software with minimal restrictions.


Contributing

The project is considered functionally complete, but improvements and refinements are welcome. Focus areas:

  • Improving API ergonomics
  • Expanding documentation and examples
  • Adding test coverage

Acknowledgements

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ps4debug-1.0.0.tar.gz (30.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ps4debug-1.0.0-py3-none-any.whl (40.7 kB view details)

Uploaded Python 3

File details

Details for the file ps4debug-1.0.0.tar.gz.

File metadata

  • Download URL: ps4debug-1.0.0.tar.gz
  • Upload date:
  • Size: 30.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ps4debug-1.0.0.tar.gz
Algorithm Hash digest
SHA256 cd68bd88a8bfebb8e573cc675d542c32d0442c01bc4c62320f0b43f02d07f094
MD5 65ea879e9116bff4d1668a73266f46bc
BLAKE2b-256 1cf955128a98b034a8f969c1ac83b8ffbfb3e3d2db581a6e17b56b883b02b590

See more details on using hashes here.

File details

Details for the file ps4debug-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: ps4debug-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 40.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for ps4debug-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1c827d823d1fa38c50c88285ee4e6e773ab589a2b1c67ee0e177bbfe4c34a41e
MD5 59d500f3425577aefeb1d458f1cba773
BLAKE2b-256 cbffd4bd687741acfab435a86db32a077e713a8ae847e3572abfadbf9a45c941

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page