PS5 debugger payload, inspired by Ctn's ps5debug 1.0b5 and wire-compatible with it. Runs the wire-protocol server inside SceShellCore via an umtx-loaded installer that injects the embedded debugger ELF. Features: process scan/RPC/ELF-inject, on-console scan, full debugger (breakpoints, watchpoints, single-step, regs/FP/DR access), kernel R/W, server-side Zydis disassembly, on-console Keystone assembler (0xBDAA0024), foreground-app metadata (0xBDDD0006), server-side RBP stack walk (CMD_PROC_READ_STACK), klog forwarder (TCP 3232), rest-mode survival and a lot more. Supports FW families 3.x / 4.0 / 4.x (incl. 4.51) / 5.x / 6.x / 7.x / 7.5x / 8.x via the kpatch routine in installer/source/main.c. GPL-3.0-only.
46 KiB
ps5debug-NG - Wire Protocol Reference
Developer-facing reference for every command exposed by the debugger payload.
All field offsets, sizes, opcodes, and behaviors below are taken directly from
the sources under debugger/, installer/, and common/ in this tree, with
file:line citations. Nothing is summarized from external documentation.
1. Wire protocol fundamentals
1.1 Core constants
| Symbol | Value | Source |
|---|---|---|
PACKET_VERSION |
"1.3" |
protocol.h:11 |
PACKET_BRANDING |
"ps5debug-NG by OSR v1.2.2" |
meta.c (handle_branding) |
PACKET_MAGIC |
0xFFAABBCC |
protocol.h:13 |
BROADCAST_MAGIC |
0xFFFFAAAA |
server.h:19 |
1.2 Ports
| Port | Protocol | Role | Source |
|---|---|---|---|
| 744 | TCP | Main command server | main.c |
| 755 | TCP | Asynchronous debug-interrupt channel (server→client) | debug.h |
| 3232 | TCP | Kernel-log forwarder (server→client, read-only) | main.c |
| 1010 | UDP | Discovery broadcast | main.c |
1.3 Byte order
All multi-byte integers are little-endian (native x86-64 layout). __attribute__((packed))
is used on every wire struct, so there is no implicit padding between fields.
1.4 Packet framing
Every request on port 744 begins with a fixed 12-byte header. void *data in the
C struct is NOT on the wire - it is a client-side pointer filled in by the server
after the variable-length body is received (protocol.h:104-110, server.c:167-185):
struct cmd_packet { // 12 bytes on the wire
uint32_t magic; // must equal PACKET_MAGIC (0xFFAABBCC)
uint32_t cmd; // command ID (see §4)
uint32_t datalen; // length of request body that follows the header
void *data; // NOT transmitted; server-side pointer only
};
#define CMD_PACKET_SIZE 12
Framing loop (server.c:102-203):
- Server blocking-reads 12 bytes →
cmd_packetheader. - Validates
magic == PACKET_MAGIC; on mismatch the packet is silently dropped and the loop continues (no error response). - If
datalen > 0, server allocates a buffer vianet_alloc_buffer(datalen)and reads exactlydatalenbytes into it. - Dispatches to the appropriate handler; the handler reads any additional trailing data (beyond the fixed request struct) directly from the socket.
- Handler replies with
CMD_SUCCESS(or an error code from §1.6) vianet_send_int32, optionally followed by response payload.
1.5 Dispatch routing (meta.c, cmd_handler)
cmd_handler inlines five infrequent commands, then routes everything else by
testing the command against VALID_*_CMD macros (full-opcode match, no
middle-byte shortcut):
| Namespace | Predicate | Dispatch target | File |
|---|---|---|---|
| Info/ping | n/a (inlined) | cmd_handler |
meta.c (5 cases) |
| Process | VALID_PROC_CMD |
proc_handle |
proc.c |
| Debug | VALID_DEBUG_CMD |
debug_handle |
debug.c |
| Kernel R/W | VALID_KERN_CMD |
kern_handle |
kern.c |
| Console | VALID_CONSOLE_CMD |
console_handle |
console.c |
VALID_CMD(cmd) additionally requires the top byte to be 0xBD
(common/include/protocol.h).
Special case: if cmd == CMD_DEBUG_ATTACH, the per-connection handler is
invoked with the active client slot so it can attach debug state to the
specific TCP connection.
1.6 Status codes (common/include/protocol.h)
| Code | Value | Meaning |
|---|---|---|
CMD_SUCCESS |
0x80000000 |
Command succeeded; response payload may follow. |
CMD_ERROR |
0xF0000001 |
Generic failure. |
CMD_TOO_MUCH_DATA |
0xF0000002 |
Client requested more than the server will send. |
CMD_DATA_NULL |
0xF0000003 |
NULL/missing packet body or allocation failure. |
CMD_ALREADY_DEBUG |
0xF0000004 |
A debug session is already active. |
CMD_INVALID_INDEX |
0xF0000005 |
Breakpoint/watchpoint index out of range. |
CMD_FATAL_STATUS(s) |
macro | ((s >> 28) == 15) - any 0xFxxxxxxx is fatal. |
PS5-specific wire quirk: bit-pair-swapped status words. The server's
net_send_int32 helper (used to transmit uint32_t status codes back to the
client) writes the value with its even/odd bit positions swapped. Clients must
un-swap incoming status uint32_ts before comparing to the table above:
static inline uint32_t bitswap32(uint32_t x) {
return ((x << 1) & 0xAAAAAAAAu) | ((x >> 1) & 0x55555555u);
}
The swap is an involution (its own inverse). Subsequent response payload bytes
(structs, strings, raw memory) are sent without the swap - only the
uint32_t status word is affected. Payload-length prefixes inside response
bodies (e.g. the uint32_t length before a branding/version string) are also
sent raw; the swap is gated specifically to net_send_int32.
1.7 Discovery (server.c:235-306)
A UDP server on port 1010 accepts a single 4-byte datagram. If the payload equals
BROADCAST_MAGIC (0xFFFFAAAA), the server echoes the same 4 bytes back to the
sender. No other payload is accepted; there is no authentication on this channel.
1.8 Authentication (proc.c:1862-1900)
A single connection-scoped flag word g_proc_auth_bits gates the scan family.
Bit 1 (0x02) is the scan-enabled bit; all CMD_PROC_SCAN* handlers check
it on entry (proc.c:1904-1905, 1691, 1860, etc.).
CMD_PROC_AUTH protocol:
- Client sends a
struct cmd_proc_auth_packet:uint32_t magic- must equalCMD_PROC_AUTH_MAGIC(0xBB40E64D).uint32_t flags- requested bits to OR intog_proc_auth_bitson success.
- Server generates a 64-byte challenge and sends:
uint16_t challenge_length = 64followed by the 64 bytes of challenge. - Client XORs the challenge against a keystream (derived client-side; mechanism intentionally undocumented) and sends 64 response bytes back.
- On match, server OR-s the requested flags into
g_proc_auth_bitsand repliesCMD_SUCCESS; otherwiseCMD_DATA_NULL.
State is per-connection: disconnecting resets it. There is no server-wide authentication on other command families - reads/writes/debug/kernel RW are unauthenticated.
1.9 Asynchronous interrupt channel (port 755)
When a client issues CMD_DEBUG_ATTACH, the server also opens a TCP connection
outbound to that client's IP on port 755 (debug.c:25). The client must be
listening on 755 before sending CMD_DEBUG_ATTACH.
Interrupts (hit breakpoints/watchpoints, single-step completions) are delivered
asynchronously as fixed-size packets of DEBUG_INTERRUPT_PACKET_SIZE = 0x4A0
bytes (1184) on this channel (debug.h:105-113):
struct debug_interrupt_packet { // 0x4A0 bytes
uint32_t lwpid;
uint32_t status;
char tdname[40];
struct __reg64 reg64; // GP registers; 184 bytes
struct savefpu_ymm savefpu; // FPU+YMM; 512 bytes
struct __dbreg64 dbreg64; // 16× uint64_t = 128 bytes
};
Note: the struct is packed, so savefpu is not cache-line aligned - the
compiler emits warnings for this (observed during build) but the layout is
stable.
2. Command reference
58 total commands. Every CMD_* macro defined in common/include/protocol.h
has a handler, plus the raw-literal opcodes 0xBDAA0024 (assemble) and
0xBDDD0006 (foreground app).
2.1 Info & ping (0xBD000xxx, 0xBDAACC06)
All five are handled inline in cmd_handler (server.c:52-71).
CMD_VERSION = 0xBD000001
- Request body: none.
- Response:
uint32_t length, thenlengthbytes ofPACKET_VERSION("1.3").
CMD_FW_VERSION = 0xBD000500
- Request body: none.
- Response:
uint16_tfirmware version (BCD, e.g.0x505= 5.05).
CMD_BRANDING = 0xBD000501
- Request body: none.
- Response:
uint32_t length, thenlengthbytes ofPACKET_BRANDING.
CMD_PLATFORM_ID = 0xBD000502
- Request body: none.
- Response:
uint16_tplatform tag - hardcoded5(PS5). Clients display this as the console version (e.g. logged as "Console PS5"). The wire-protocol version reported byCMD_VERSIONis the server protocol revision (currently1.3); this command is the platform discriminator.
CMD_PROC_NOP = 0xBDAACC06
- Request body: none.
- Response:
CMD_SUCCESS. Keepalive / liveness probe.
2.2 Process commands (0xBDAAxxxx) - dispatched by proc_handle (proc.c:2268-2295)
CMD_PROC_LIST = 0xBDAA0001 (proc.c:330-358)
- Request body: none.
- Response:
CMD_SUCCESS,uint32_t num, thennum × struct proc_list_entry:struct proc_list_entry { char p_comm[32]; int pid; }; // 36 bytes each
CMD_PROC_READ = 0xBDAA0002 (proc.c:360-397)
- Request body:
struct cmd_proc_read_packet(16 bytes). - Response:
CMD_SUCCESS, then up tolengthbytes in 64 KiB chunks.
CMD_PROC_WRITE = 0xBDAA0003 (proc.c:399-432)
- Request body:
struct cmd_proc_write_packet(16 bytes). - Trailing data:
lengthbytes to write. - Response:
CMD_SUCCESS.
CMD_PROC_MAPS = 0xBDAA0004 (proc.c:434-477)
- Request body:
struct cmd_proc_maps_packet(4 bytes, justpid). - Response:
CMD_SUCCESS,uint32_t num, thennum × struct proc_vm_map_entry({uint64_t start; uint64_t end; uint64_t offset; uint64_t prot; char name[32];}= 64 bytes each).
CMD_PROC_INTALL = 0xBDAA0005 (proc.c:479-505)
Note the literal spelling INTALL (typo in source, preserved for compat).
- Request body:
struct cmd_proc_install_packet(4 bytes, justpid). - Response:
CMD_SUCCESS,struct cmd_proc_install_response(uint64_t rpcstub, 8 bytes). - Effect: injects the RPC stub code + per-process thread into the target. The
returned
rpcstubis the stub header address and must be supplied back on every subsequentCMD_PROC_CALL.
CMD_PROC_CALL = 0xBDAA0006 (proc.c:507-540)
- Request body:
struct cmd_proc_call_packet(68 bytes):uint32_t pid; uint64_t rpcstub; // from CMD_PROC_INTALL response uint64_t rpc_rip; // target function address uint64_t rpc_rdi, rpc_rsi, rpc_rdx, rpc_rcx, rpc_r8, rpc_r9; // SysV args - Response:
CMD_SUCCESS,struct cmd_proc_call_response({uint32_t pid; uint64_t rpc_rax;}, 12 bytes).
CMD_PROC_ELF = 0xBDAA0007 (proc.c:542-579)
- Request body:
struct cmd_proc_elf_packet({uint32_t pid; uint32_t length;}, 8 bytes). - Trailing data:
lengthbytes of ELF image. - Response:
CMD_SUCCESSor error.
CMD_PROC_PROTECT = 0xBDAA0008 (proc.c:618-640)
- Request body:
struct cmd_proc_protect_packet(20 bytes):{uint32_t pid; uint64_t address; uint32_t length; uint32_t newprot;}. - Response:
CMD_SUCCESS.
CMD_PROC_SCAN = 0xBDAA0009 (proc.c:1401-1485) - requires auth bit 1
Legacy single-pass scan; superseded by the SCAN_START/COUNT/GET trio.
- Request body:
struct cmd_proc_scan_packet(10 bytes). - Trailing data:
lenDatabytes of comparison value(s). - Response:
uint32_t num, thennum × uint64_taddresses.
CMD_PROC_INFO = 0xBDAA000A (proc.c:1487-1511)
- Request body:
struct cmd_proc_info_packet(4 bytes,pid). - Response:
CMD_SUCCESS,struct cmd_proc_info_response(188 bytes):{uint32_t pid; char name[40]; char path[64]; char titleid[16]; char contentid[64];}.
CMD_PROC_ALLOC = 0xBDAA000B (proc.c:1513-1534)
- Request body:
struct cmd_proc_alloc_packet(8 bytes,{pid, length}). - Response:
CMD_SUCCESS,struct cmd_proc_alloc_response(uint64_t address, 8 bytes).
CMD_PROC_FREE = 0xBDAA000C (proc.c:1536-1554)
- Request body:
struct cmd_proc_free_packet(16 bytes,{pid, address, length}). - Response:
CMD_SUCCESS.
CMD_PROC_ALLOC_HINTED = 0xBDAA000E (proc.c:1557-1580)
- Request body:
struct cmd_proc_alloc_hinted_packet(16 bytes,{pid, hint, length}). - Response:
CMD_SUCCESS,struct cmd_proc_alloc_response(uint64_t address).
CMD_PROC_ELF_RPC = 0xBDAA0010 (proc.c:581-616)
Like CMD_PROC_ELF but returns the entry address instead of jumping into it.
- Request body:
struct cmd_proc_elf_rpc_packet(8 bytes,{pid, length}). - Trailing data:
lengthbytes of ELF image. - Response:
CMD_SUCCESS,struct cmd_proc_elf_rpc_response(uint64_t entry, 8 bytes).
CMD_PROC_DISASM_REGION = 0xBDAA0020 (proc.c:145-184)
- Request body:
struct cmd_proc_disasm_packet(20 bytes,{pid, address, length, max_entries}). - Response:
CMD_SUCCESS, a stream ofstruct disasm_instr_entry(32 bytes each), terminated by a sentinel entry with all fields0xFF. - Backend: Zydis amalgamation, compiled with
ZYAN_NO_LIBC.
disasm_instr_entry layout (protocol.h:143-154):
struct disasm_instr_entry { // 32 bytes
uint64_t addr; // instruction start
uint64_t rip_rel_target; // resolved absolute addr if MEM_IS_RIP_REL; else 0
int64_t mem_disp; // [base + index*scale + disp]
uint8_t length;
uint8_t kind; // bitmask below
uint8_t mem_base_reg; // ZydisRegister low byte; 0 if none
uint8_t mem_index_reg;
uint8_t mem_scale; // 1/2/4/8; 0 if none
uint8_t mnemonic_lo; // ZydisMnemonic low byte (category hint)
uint16_t pad;
};
Kind bitmask (protocol.h:139-142):
| Bit | Meaning |
|---|---|
| 0x01 | CALL |
| 0x02 | RET |
| 0x04 | Unconditional JMP |
| 0x08 | Conditional branch |
| 0x10 | Instruction has a memory operand |
| 0x20 | Memory operand is RIP-relative |
| 0x40 | Memory read |
| 0x80 | Memory write |
CMD_PROC_EXTRACT_CODE_XREFS = 0xBDAA0021 (proc.c:220-294)
- Request body:
struct cmd_proc_disasm_packet(20 bytes). - Response:
CMD_SUCCESS,uint32_t num_targets,num_targets × uint64_t(deduplicated RIP-relative branch/call targets inside the scanned range).
CMD_PROC_FIND_XREFS_TO = 0xBDAA0022 (proc.c:296-328)
- Request body:
struct cmd_proc_xrefs_to_packet(24 bytes):{uint32_t pid; uint64_t scan_address; uint32_t scan_length; uint64_t target_address;}. - Response:
CMD_SUCCESS,uint32_t num, thennum × (uint64_t address, uint8_t kind)- the
kindbyte reuses thedisasm_instr_entry.kindbitmask.
- the
CMD_PROC_READ_STACK = 0xBDAA0023 (proc.c)
Server-side RBP-chain walk. The server walks the frame chain itself and bundles
every frame's data (saved RBP, return address, frame-local bytes, and a code
window around the return address) into one response, so clients avoid paying
~4 TCP round-trips per stack frame. Clients can feature-detect this command via
the CMD_BRANDING version string (added in v1.2.2).
- Request body:
struct cmd_proc_read_stack_packet(24 bytes):{uint32_t pid; uint64_t rbp; uint64_t rsp; uint32_t depth;}rbp/rspdescribe the local (top) frame;depthcaps frames returned (incl. local).
- Response:
CMD_SUCCESS,uint32_t bundle_len, thenbundle_lenbytes:u32 n_frames per frame { u64 rbp; u64 rsp; u64 saved_rbp; u64 ret_addr; u32 flags; // bit0: frame-locals omitted (oversized/invalid) - read rsp..rbp yourself u32 frame_locals_len; u32 code_len; // bytes of code at (ret_addr - 10); 0 if unavailable u8 frame_locals[frame_locals_len]; u8 code[code_len]; }- Caps:
CMD_PROC_READ_STACK_MAX_DEPTH = 64,CMD_PROC_READ_STACK_LOCALS_CAP = 0x1000per frame,CMD_PROC_READ_STACK_CODE_OFF = 10,CMD_PROC_READ_STACK_CODE_LEN = 200.
- Caps:
0xBDAA0024 - assemble x86-64 (proc.c, proc_assemble_handle)
Assemble x86-64 text into machine bytes using the Keystone (LLVM-MC) assembler
embedded in the payload. Pure userspace - needs no attached process, no
elevation. The on-console equivalent of what Reaper Studio does with its
client-side keystone.dll when applying VarType.ASM patches.
The bare opcode is intentionally NOT given a CMD_* macro - it's a raw
literal in proc_handle's switch - so the published CMD_* set (which some
clients enumerate) stays unchanged. Clients that need it should send the literal.
- Request body:
struct cmd_proc_assemble_packet(12 bytes):{uint64_t base_addr; uint32_t ks_opt_syntax;}base_addris what Keystone resolves PC-relative operands against.ks_opt_syntax= 0 keeps the engine default (Intel); pass a KeystoneKS_OPT_SYNTAX_*value (1=Intel, 2=ATT, 3=NASM, 4=MASM, 5=GAS) to switch.
- Trailing data:
(datalen - 12)bytes of asm text. NUL-termination not required. - Response on success:
CMD_SUCCESS,struct cmd_proc_assemble_ok(8 bytes,{uint32_t byte_len; uint32_t insn_count;}), thenbyte_lenbytes of machine code. - Response on assembler error:
CMD_ERROR,struct cmd_proc_assemble_err(8 bytes,{uint32_t ks_errno; uint32_t msg_len;}), thenmsg_lenbytes of the human-readable Keystone error (ks_strerror). - Caveats: Keystone here was built with
LLVM_ENABLE_THREADS=OFF; the payload's command dispatcher serialises clients so this is fine for the current usage, but concurrent assembles would need an external mutex.
CMD_PROC_SCAN_AOB = 0xBDAA0501 (proc.c:1582-1691) - requires auth bit 1
- Request body:
struct cmd_proc_scan_aob_packet(22 bytes):{pid, address, length, max_matches(u8), stop_flag(u8), pattern_length(u32)}. - Trailing data:
pattern_lengthbytes. Each byte is either an exact value or the wildcard marker0x??(see handler at proc.c:1638). - Response:
uint32_t num, thennum × uint64_taddresses.
CMD_PROC_SCAN_AOB_MULTI = 0xBDAA0502 (proc.c:1693-1860) - requires auth bit 1
- Request body:
struct cmd_proc_scan_aob_multi_packet(21 bytes):{pid, address, length, stop_flag(u8), patterns_length(u32)}. - Trailing data:
patterns_lengthbytes of concatenated patterns; each pattern is prefixed by its ownuint8_t lengthbyte. - Response:
uint32_t num_patterns, then for each pattern:uint32_t num,num × uint64_t.
CMD_PROC_AUTH = 0xBDAACCFF (proc.c:1862-1900)
See §1.8.
CMD_PROC_SCAN_START = 0xBDAACC01 (proc.c:1922-2044) - requires auth bit 1
- Request body:
struct cmd_proc_scan_start_packet(23 bytes):{pid, address, length, valueType(u8), compareType(u8), alignment(u8), lenData(u32)}. - Trailing data:
lenDatabytes of seed value(s). - Response:
CMD_SUCCESS. Server now holds a scan session keyed onpid.
CMD_PROC_SCAN_COUNT = 0xBDAACC02 (proc.c:2046-2221) - requires auth bit 1
Narrows the current scan set by applying a second comparison.
- Request body:
struct cmd_proc_scan_count_packet(18 bytes):{pid, base_address, valueType(u8), compareType(u8), lenData(u32)}. - Trailing data:
lenDatabytes. - Response:
uint32_t num_results(the new cardinality).
CMD_PROC_SCAN_GET = 0xBDAACC03 (proc.c:2223-2265) - requires auth bit 1
- Request body:
struct cmd_proc_scan_get_packet(8 bytes,{pid, count}). - Response:
CMD_SUCCESS,uint32_t actual, thenactual × uint64_t.
For the enum values used by valueType and compareType, see §7.
2.3 Debug commands (0xBDBBxxxx) - dispatched by debug_handle (kern.c:779-803)
All debug commands (except ATTACH, see §1.5) require an already-attached
debug session. The dispatcher returns early with no response if none exists.
CMD_DEBUG_ATTACH = 0xBDBB0001 (kern.c:98-166, via server.c:188)
- Request body:
struct cmd_debug_attach_packet(8 bytes,{pid, client_ip}). - Response:
CMD_SUCCESS, orCMD_ALREADY_DEBUG/CMD_ERROR. - Side effect: server opens outbound TCP to client on port 755 (see §1.9)
and
ptrace(PT_ATTACH)-es the target.
debug_attach_handle at kern.c:92-96 is a stub that always replies CMD_ERROR
and returns 1; it exists so the switch at kern.c:784 is complete, but is
unreachable in normal flow because server.c:188 intercepts CMD_DEBUG_ATTACH
before cmd_handler runs and routes to debug_attach_handle_svc instead.
CMD_DEBUG_DETACH = 0xBDBB0002 (kern.c:216-461)
- Request body: none.
- Response:
CMD_SUCCESS. Clears all breakpoints/watchpoints, resumes target, closes port 755 connection.
CMD_DEBUG_SET_BREAKPOINT = 0xBDBB0003 (kern.c:463-501)
- Request body:
struct cmd_debug_breakpt_packet(16 bytes,{index, enabled, address}). - Response:
CMD_SUCCESSorCMD_INVALID_INDEX. - Backend: software breakpoint - writes
0xCCataddress, saves original byte.MAX_BREAKPOINTS = 30(protocol.h:438).
CMD_DEBUG_SET_WATCHPOINT = 0xBDBB0004 (kern.c:504-568)
- Request body:
struct cmd_debug_watchpt_packet(24 bytes):{index, enabled, length, breaktype, address}. - Response:
CMD_SUCCESSorCMD_INVALID_INDEX. - Backend: hardware breakpoint via DR0-DR3 / DR7.
MAX_WATCHPOINTS = 4(protocol.h:439).breaktypeusesDBREG_DR7_*encoding (debug.h:119-126):EXEC=0,WRONLY=1,RDWR=3; length1/2/4/8viaDBREG_DR7_LEN_*.
CMD_DEBUG_GET_THREAD_LIST = 0xBDBB0005 (kern.c:570-604)
- Request body: none.
- Response:
CMD_SUCCESS,uint32_t num,num × uint32_t lwpid.
CMD_DEBUG_SUSPEND_THREAD = 0xBDBB0006 (kern.c:606-614)
- Request body:
struct cmd_debug_stopthr_packet(4 bytes,lwpid). - Response:
CMD_SUCCESS.
CMD_DEBUG_RESUME_THREAD = 0xBDBB0007 (kern.c:616-624)
- Request body:
struct cmd_debug_resumethr_packet(4 bytes,lwpid). - Response:
CMD_SUCCESS.
CMD_DEBUG_GETREGS = 0xBDBB0008 (kern.c:626-639)
- Request body:
struct cmd_debug_getregs_packet(4 bytes,lwpid). - Response:
CMD_SUCCESS,struct __reg64(184 bytes; debug.h:11-38).
CMD_DEBUG_SETREGS = 0xBDBB0009 (kern.c:641-655)
- Request body:
struct cmd_debug_setregs_packet(8 bytes,{lwpid, length}). - Trailing data:
lengthbytes - must besizeof(struct __reg64). - Response:
CMD_SUCCESS.
CMD_DEBUG_GETFPREGS = 0xBDBB000A (kern.c:657-670)
- Request body:
struct cmd_debug_getregs_packet(4 bytes,lwpid). - Response:
CMD_SUCCESS,struct savefpu_ymm(512 bytes; debug.h:88-97).
CMD_DEBUG_SETFPREGS = 0xBDBB000B (kern.c:672-686)
- Request body:
struct cmd_debug_setregs_packet(8 bytes). - Trailing data:
lengthbytes -sizeof(struct savefpu_ymm). - Response:
CMD_SUCCESS.
CMD_DEBUG_GETDBREGS = 0xBDBB000C (kern.c:688-701)
- Request body:
struct cmd_debug_getregs_packet(4 bytes). - Response:
CMD_SUCCESS,struct __dbreg64(128 bytes; 16 × uint64_t).
CMD_DEBUG_SETDBREGS = 0xBDBB000D (kern.c:703-717)
- Request body:
struct cmd_debug_setregs_packet(8 bytes). - Trailing data:
lengthbytes (sizeof(struct __dbreg64)). - Response:
CMD_SUCCESS.
CMD_DEBUG_CONTINUE = 0xBDBB0010 (kern.c:719-729)
- Request body:
struct cmd_debug_stopgo_packet(4 bytes,stop). stop == 0→ resume all threads.stop == 1→ enter single-step / halted mode.- Response:
CMD_SUCCESS.
CMD_DEBUG_THREAD_INFO = 0xBDBB0011 (kern.c:731-744)
- Request body:
struct cmd_debug_thrinfo_packet(4 bytes,lwpid). - Response:
CMD_SUCCESS,struct cmd_debug_thrinfo_response(40 bytes,{lwpid, priority, name[32]}).
CMD_DEBUG_STEP = 0xBDBB0012 (kern.c:746-756)
- Request body: none.
- Response:
CMD_SUCCESS. Single-step all threads of the debugged process.
CMD_DEBUG_STEP_THREAD = 0xBDBB0013 (kern.c:758-776)
- Request body:
struct cmd_debug_stopthr_packet(4 bytes,lwpid). - Response:
CMD_SUCCESS.
CMD_DEBUG_PROCESS_STOP = 0xBDBB0500 (kern.c:54-89)
- Request body: 5 bytes of raw data (no named struct):
u32 pid; u8 state;-state ∈ {0,1,2}maps to{resume, stop, kill}(kern.c:61-77). ReturnsCMD_ERRORifstate > 2orpid == 0. - Response:
CMD_SUCCESS, orCMD_DATA_NULL, orCMD_ERROR. - Behavior: If a debug session is active (
g_debugging != 0), stashes a pending signal intog_pending_signalfor the next event dispatch (0 → 0,1 → SIGSTOP(17),2 → SIGKILL(9)). Otherwise callskill(pid, sig)directly withSIGSTOP(19 - FreeBSD SIGSTOP),SIGTSTP(17), orSIGKILL(9).
2.4 Kernel R/W commands (0xBDCCxxxx) - dispatched by kern_handle (console.c:60-67)
CMD_KERN_BASE = 0xBDCC0001 (console.c:7-13)
- Request body: none.
- Response:
CMD_SUCCESS,uint64_tkernel base.
CMD_KERN_READ = 0xBDCC0002 (console.c:16-35)
- Request body:
struct cmd_kern_read_packet(12 bytes,{address, length}). - Response:
CMD_SUCCESS, thenlengthbytes of kernel memory.
CMD_KERN_WRITE = 0xBDCC0003 (console.c:38-57)
- Request body:
struct cmd_kern_write_packet(12 bytes,{address, length}). - Trailing data:
lengthbytes to write. - Response pattern (unique - note the two status messages):
CMD_SUCCESSsent before the server receives the write payload (console.c:51).CMD_SUCCESSsent after the write completes (console.c:54).
2.5 Console commands (0xBDDDxxxx) - dispatched by console_handle (debug.c:121-140)
CMD_CONSOLE_REBOOT = 0xBDDD0001 (debug.c:110-118)
- Request body: none.
- Response: none - connection is closed; system reboots via
syscall(112, 1, 0).
CMD_CONSOLE_END = 0xBDDD0002 (debug.c:133-136)
- Request body: none.
- Response: none - handler returns
1, which tears down the connection.
CMD_CONSOLE_PRINT = 0xBDDD0003 (debug.c:58-82)
- Request body:
uint32_t length, thenlengthbytes of UTF-8 text. - Response:
CMD_SUCCESS.
CMD_CONSOLE_NOTIFY = 0xBDDD0004 (debug.c:85-107)
- Request body:
struct cmd_console_notify_packet(8 bytes,{messageType, length}). - Trailing data:
lengthbytes of UTF-8 text (must include trailing\0). - Response:
CMD_SUCCESS. - Effect: calls
sceSysUtilSendSystemNotificationWithText(messageType, text);messageType = 222is the generic icon used by main.c:39.
CMD_CONSOLE_INFO = 0xBDDD0005 (debug.c:129-132)
- Request body: none.
- Response:
CMD_SUCCESS. No side effect (ping variant).
0xBDDD0006 - foreground-app metadata (console.c, console_foreground_app_handle)
Identify the currently-foregrounded game and return its metadata (pid, titleid,
contentid, process name, and version). Resolves the version from the title's
param.sfo. Raw literal in console_handle's switch - no CMD_* macro
(same rationale as 0xBDAA0024).
- Request body: none.
- Response:
CMD_SUCCESS,struct cmd_console_foreground_app_response(140 bytes):u32 pid; char titleid[16]; char contentid[64]; char name[40]; char app_ver[16]; // "XX.YY" version string from param.sfo - Behaviour: if there is no foreground game, all fields are zeroed (
pid==0). - Version selection: when both
APP_VERandVERSIONare present inparam.sfo, the server returns the lexically larger of the two so that packer-mistakes (where the bundledVERSIONovertakesAPP_VER) still yield the higher-precedence version string.
3. In-process kernel/target primitives
Unlike PS4-class debuggers that ship a separate kernel module, ps5debug-NG runs the entire wire-protocol server inside SceShellCore (after the umtx-loaded installer injects the embedded debugger ELF as a SceShellCore-internal thread). All "kernel-side" operations are therefore performed from userland using primitives supplied by ps5-payload-sdk and short kernel patches applied at installer time:
| Concern | How ps5debug-NG implements it |
|---|---|
Process enumeration (CMD_PROC_LIST) |
sysctl(KERN_PROC, KERN_PROC_PROC) from inside SceShellCore. |
| Target memory R/W | mdbg_call syscalls + the SDK's kernel_copyin / kernel_copyout. |
Target mprotect / alloc / free |
mdbg_call PT_TRACE_ME-tainted variant + KEX-state shim from installer. |
| Kernel base / kernel R/W | kernel_get_root and kernel_copyin/out (toggles CR0.WP internally). |
RPC stub install + call (CMD_PROC_INSTALL / CMD_PROC_CALL) |
In-target thread creation through scePthreadCreate after the installer caches the libkernel.sprx symbol offsets. |
ELF inject (CMD_PROC_ELF, CMD_PROC_ELF_RPC) |
Self-relocating loader inside the debugger; mmap target pages via remote-mmap RPC stub; jump or return entry. |
| Console reboot / notify / print | sceSysUtilSendSystemNotificationWithText, kern_reboot, klog_printf. |
| Jailbreak (uid=0, escape prison) | Performed by the installer before injecting the debugger; not reachable via the wire protocol. |
The installer's role is bootstrap-only: find SceShellCore, apply the FW-specific
kpatch (see installer/source/main.c), set up the
target's KEX state and syscall-origin filter, then call the SCE-side
inject_remote_thread_create primitive to hand control off to the embedded
debugger. After that the installer thread exits and the debugger services TCP.
4. Full command ID table
| Hex | Name | Handler | Auth? |
|---|---|---|---|
0xBD000001 |
CMD_VERSION |
inline (server.c:53) | |
0xBD000500 |
CMD_FW_VERSION |
inline (server.c:55) | |
0xBD000501 |
CMD_BRANDING |
inline (server.c:59) | |
0xBD000502 |
CMD_PLATFORM_ID |
inline (server.c:64) | |
0xBDAA0001 |
CMD_PROC_LIST |
proc_list_handle |
|
0xBDAA0002 |
CMD_PROC_READ |
proc_read_handle |
|
0xBDAA0003 |
CMD_PROC_WRITE |
proc_write_handle |
|
0xBDAA0004 |
CMD_PROC_MAPS |
proc_maps_handle |
|
0xBDAA0005 |
CMD_PROC_INTALL |
proc_install_handle |
|
0xBDAA0006 |
CMD_PROC_CALL |
proc_call_handle |
|
0xBDAA0007 |
CMD_PROC_ELF |
proc_elf_handle |
|
0xBDAA0008 |
CMD_PROC_PROTECT |
proc_protect_handle |
|
0xBDAA0009 |
CMD_PROC_SCAN |
proc_scan_handle |
bit 1 |
0xBDAA000A |
CMD_PROC_INFO |
proc_info_handle |
|
0xBDAA000B |
CMD_PROC_ALLOC |
proc_alloc_handle |
|
0xBDAA000C |
CMD_PROC_FREE |
proc_free_handle |
|
0xBDAA000E |
CMD_PROC_ALLOC_HINTED |
proc_alloc_hinted_handle |
|
0xBDAA0010 |
CMD_PROC_ELF_RPC |
proc_elf_rpc_handle |
|
0xBDAA0020 |
CMD_PROC_DISASM_REGION |
proc_disasm_region_handle |
|
0xBDAA0021 |
CMD_PROC_EXTRACT_CODE_XREFS |
proc_extract_code_xrefs_handle |
|
0xBDAA0022 |
CMD_PROC_FIND_XREFS_TO |
proc_find_xrefs_to_handle |
|
0xBDAA0023 |
CMD_PROC_READ_STACK |
proc_read_stack_handle |
|
0xBDAA0024 |
(raw literal, no macro) | proc_assemble_handle |
|
0xBDAA0501 |
CMD_PROC_SCAN_AOB |
proc_scan_aob_handle |
bit 1 |
0xBDAA0502 |
CMD_PROC_SCAN_AOB_MULTI |
proc_scan_aob_multi_handle |
bit 1 |
0xBDAACCFF |
CMD_PROC_AUTH |
proc_auth_handle |
|
0xBDAACC01 |
CMD_PROC_SCAN_START |
proc_scan_start_handle |
bit 1 |
0xBDAACC02 |
CMD_PROC_SCAN_COUNT |
proc_scan_count_handle |
bit 1 |
0xBDAACC03 |
CMD_PROC_SCAN_GET |
proc_scan_get_handle |
bit 1 |
0xBDAACC06 |
CMD_PROC_NOP |
inline (server.c:68) | |
0xBDBB0001 |
CMD_DEBUG_ATTACH |
debug_attach_handle_svc |
|
0xBDBB0002 |
CMD_DEBUG_DETACH |
debug_detach_handle |
|
0xBDBB0003 |
CMD_DEBUG_SET_BREAKPOINT |
debug_set_breakpoint_handle |
|
0xBDBB0004 |
CMD_DEBUG_SET_WATCHPOINT |
debug_set_watchpoint_handle |
|
0xBDBB0005 |
CMD_DEBUG_GET_THREAD_LIST |
debug_get_thread_list_handle |
|
0xBDBB0006 |
CMD_DEBUG_SUSPEND_THREAD |
debug_suspend_thread_handle |
|
0xBDBB0007 |
CMD_DEBUG_RESUME_THREAD |
debug_resume_thread_handle |
|
0xBDBB0008 |
CMD_DEBUG_GETREGS |
debug_getregs_handle |
|
0xBDBB0009 |
CMD_DEBUG_SETREGS |
debug_setregs_handle |
|
0xBDBB000A |
CMD_DEBUG_GETFPREGS |
debug_getfpregs_handle |
|
0xBDBB000B |
CMD_DEBUG_SETFPREGS |
debug_setfpregs_handle |
|
0xBDBB000C |
CMD_DEBUG_GETDBREGS |
debug_getdbregs_handle |
|
0xBDBB000D |
CMD_DEBUG_SETDBREGS |
debug_setdbregs_handle |
|
0xBDBB0010 |
CMD_DEBUG_CONTINUE |
debug_continue_handle |
|
0xBDBB0011 |
CMD_DEBUG_THREAD_INFO |
debug_thread_info_handle |
|
0xBDBB0012 |
CMD_DEBUG_STEP |
debug_step_handle |
|
0xBDBB0013 |
CMD_DEBUG_STEP_THREAD |
debug_step_thread_handle |
|
0xBDBB0500 |
CMD_DEBUG_PROCESS_STOP |
debug_process_stop_handle |
|
0xBDCC0001 |
CMD_KERN_BASE |
kern_base_handle |
|
0xBDCC0002 |
CMD_KERN_READ |
kern_read_handle |
|
0xBDCC0003 |
CMD_KERN_WRITE |
kern_write_handle |
|
0xBDDD0001 |
CMD_CONSOLE_REBOOT |
console_reboot_handle |
|
0xBDDD0002 |
CMD_CONSOLE_END |
inline (debug.c:133) | |
0xBDDD0003 |
CMD_CONSOLE_PRINT |
console_print_handle |
|
0xBDDD0004 |
CMD_CONSOLE_NOTIFY |
console_notify_handle |
|
0xBDDD0005 |
CMD_CONSOLE_INFO |
inline (debug.c:129) | |
0xBDDD0006 |
(raw literal, no macro) | console_foreground_app_handle |
5. Request packet appendix
All structs are __attribute__((packed)). Sizes match the CMD_*_PACKET_SIZE macros.
| Struct | Size | Fields |
|---|---|---|
cmd_packet (wire portion) |
12 | u32 magic; u32 cmd; u32 datalen; |
cmd_proc_read_packet |
16 | u32 pid; u64 address; u32 length; |
cmd_proc_write_packet |
16 | u32 pid; u64 address; u32 length; |
cmd_proc_maps_packet |
4 | u32 pid; |
cmd_proc_install_packet |
4 | u32 pid; |
cmd_proc_call_packet |
68 | u32 pid; u64 rpcstub,rpc_rip,rpc_rdi,rpc_rsi,rpc_rdx,rpc_rcx,rpc_r8,rpc_r9; |
cmd_proc_elf_packet |
8 | u32 pid; u32 length; |
cmd_proc_elf_rpc_packet |
8 | u32 pid; u32 length; |
cmd_proc_protect_packet |
20 | u32 pid; u64 address; u32 length; u32 newprot; |
cmd_proc_scan_packet |
10 | u32 pid; u8 valueType, compareType; u32 lenData; |
cmd_proc_scan_start_packet |
23 | u32 pid; u64 address; u32 length; u8 valueType, compareType, alignment; u32 lenData; |
cmd_proc_scan_count_packet |
18 | u32 pid; u64 base_address; u8 valueType, compareType; u32 lenData; |
cmd_proc_scan_aob_packet |
22 | u32 pid; u64 address; u32 length; u8 max_matches, stop_flag; u32 pattern_length; |
cmd_proc_scan_aob_multi_packet |
21 | u32 pid; u64 address; u32 length; u8 stop_flag; u32 patterns_length; |
cmd_proc_auth_packet |
8 | u32 magic (0xBB40E64D); u32 flags; |
cmd_proc_scan_get_packet |
8 | u32 pid; u32 count; |
cmd_proc_info_packet |
4 | u32 pid; |
cmd_proc_alloc_packet |
8 | u32 pid; u32 length; |
cmd_proc_alloc_hinted_packet |
16 | u32 pid; u64 hint; u32 length; |
cmd_proc_free_packet |
16 | u32 pid; u64 address; u32 length; |
cmd_proc_disasm_packet |
20 | u32 pid; u64 address; u32 length; u32 max_entries; |
cmd_proc_xrefs_to_packet |
24 | u32 pid; u64 scan_address; u32 scan_length; u64 target_address; |
cmd_proc_read_stack_packet |
24 | u32 pid; u64 rbp; u64 rsp; u32 depth; |
cmd_proc_assemble_packet |
12 | u64 base_addr; u32 ks_opt_syntax; (followed by asm text on the wire) |
cmd_debug_attach_packet |
8 | u32 pid; u32 client_ip; |
cmd_debug_breakpt_packet |
16 | u32 index, enabled; u64 address; |
cmd_debug_watchpt_packet |
24 | u32 index, enabled, length, breaktype; u64 address; |
cmd_debug_stopthr_packet |
4 | u32 lwpid; |
cmd_debug_resumethr_packet |
4 | u32 lwpid; |
cmd_debug_getregs_packet |
4 | u32 lwpid; |
cmd_debug_setregs_packet |
8 | u32 lwpid; u32 length; |
cmd_debug_stopgo_packet |
4 | u32 stop; |
cmd_debug_thrinfo_packet |
4 | u32 lwpid; |
cmd_kern_read_packet |
12 | u64 address; u32 length; |
cmd_kern_write_packet |
12 | u64 address; u32 length; |
cmd_console_notify_packet |
8 | u32 messageType; u32 length; |
6. Response packet appendix
| Struct | Size | Fields |
|---|---|---|
cmd_proc_install_response |
8 | u64 rpcstub; |
cmd_proc_call_response |
12 | u32 pid; u64 rpc_rax; |
cmd_proc_elf_rpc_response |
8 | u64 entry; |
cmd_proc_alloc_response |
8 | u64 address; |
cmd_proc_info_response |
188 | u32 pid; char name[40], path[64], titleid[16], contentid[64]; |
cmd_proc_assemble_ok |
8 | u32 byte_len; u32 insn_count; (followed by byte_len machine bytes) |
cmd_proc_assemble_err |
8 | u32 ks_errno; u32 msg_len; (followed by msg_len chars of ks_strerror) |
cmd_console_foreground_app_response |
132 | u32 pid; char titleid[16], contentid[64], name[40], app_ver[8]; |
cmd_debug_thrinfo_response |
40 | u32 lwpid, priority; char name[32]; |
disasm_instr_entry (streamed) |
32 | see §2.2 |
7. Enum reference
7.1 cmd_proc_scan_valuetype (protocol.h:221-234)
| Value | Name |
|---|---|
| 0 | valTypeUInt8 |
| 1 | valTypeInt8 |
| 2 | valTypeUInt16 |
| 3 | valTypeInt16 |
| 4 | valTypeUInt32 |
| 5 | valTypeInt32 |
| 6 | valTypeUInt64 |
| 7 | valTypeInt64 |
| 8 | valTypeFloat |
| 9 | valTypeDouble |
| 10 | valTypeArrBytes |
| 11 | valTypeString |
7.2 cmd_proc_scan_comparetype (protocol.h:236-252)
| Value | Name |
|---|---|
| 0 | cmpTypeExactValue |
| 1 | cmpTypeFuzzyValue |
| 2 | cmpTypeBiggerThan |
| 3 | cmpTypeSmallerThan |
| 4 | cmpTypeValueBetween |
| 5 | cmpTypeIncreasedValue |
| 6 | cmpTypeIncreasedValueBy |
| 7 | cmpTypeDecreasedValue |
| 8 | cmpTypeDecreasedValueBy |
| 9 | cmpTypeChangedValue |
| 10 | cmpTypeUnchangedValue |
| 11 | cmpTypeUnknownInitialValue |
| 12 | cmpTypeUnknownInitialLowValue |
7.3 DR7 encoding for hardware watchpoints (debug.h:119-126)
breaktype in cmd_debug_watchpt_packet:
| Value | Name |
|---|---|
| 0 | DBREG_DR7_EXEC |
| 1 | DBREG_DR7_WRONLY |
| 3 | DBREG_DR7_RDWR |
length encoding (reused as DR7 length field):
| Value | Bytes |
|---|---|
| 0 | 1 |
| 1 | 2 |
| 2 | 8 |
| 3 | 4 |
8. Response framing patterns
Four reply shapes cover every handler:
- Fixed status:
uint32_t status(usuallyCMD_SUCCESS). Used by writes, breakpoint/watchpoint management, thread suspend/resume, etc. - Status + fixed struct:
uint32_t statusthen astruct cmd_*_responseof known size. Used by info/install/call/alloc. - Status + count + array:
uint32_t status,uint32_t count,count × element. Used by list, thread list, maps, scan results. - Streamed with sentinel:
uint32_t status, then a sequence of fixed-size records terminated by an all-0xFFsentinel. OnlyCMD_PROC_DISASM_REGION.
The CMD_KERN_WRITE handler is the only exception: it sends CMD_SUCCESS
twice (before and after the data phase). Clients must consume both u32s.
9. Known source-code oddities
Documented here so a developer doesn't mistake them for bugs:
CMD_PROC_INTALLis misspelled (missingS). Preserved on the wire.debug_attach_handle(kern.c:92-96) is a stub that always returnsCMD_ERROR; the real attach path isdebug_attach_handle_svc, whichserver.c:188interceptsCMD_DEBUG_ATTACHto call directly. Both declarations remain ininclude/kern.h.struct debug_interrupt_packetispacked- the compiler warns aboutsavefpubeing offset 224 instead of a 64-byte boundary. The layout is intentional; clients rely onDEBUG_INTERRUPT_PACKET_SIZE = 0x4A0exactly.SYS_CONSOLE_CMD_JAILBREAKhas no correspondingCMD_*opcode: it is invoked locally by the payload at startup (main.c:22) and is not reachable over the network.- The disassembler amalgamation (
third_party/zydis/Zydis.c) is compiled with-O3 -DNDEBUG -w -DZYAN_NO_LIBC; the rest of the debugger is-O2. This is a deliberate Makefile override (debugger/Makefile:45-46).
This document reflects the ps5debug-NG v1.2.2 payload (see
debugger/source/meta.c:27). Citations elsewhere may point at line numbers
that drift as the source evolves - when in doubt, the source under
common/include/protocol.h, debugger/source/, and installer/source/ is
the authoritative reference.