Files
ps5debug-NG/debugger/source/kern_rw_fast.c
T
OpenSourcereR-dev 9c649a44dc Initial public release: ps5debug-NG v1.2.2
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.
2026-05-14 13:44:35 +07:00

313 lines
9.9 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <ps5/payload.h>
#include <ps5/kernel.h>
#include "sdk_shim.h"
#include "kern_rw_fast.h"
static const uint64_t kr_write_setup_const[2] = { 0x0ULL, 0x4000000000000000ULL };
static const uint64_t kr_read_setup_const [2] = { 0x4000000040000000ULL, 0x4000000000000000ULL };
static int kr_rwpair_0;
static int kr_rwpair_1;
static int kr_rwpipe_0;
static int kr_rwpipe_1;
static uint64_t kr_kpipe_addr;
static int kr_initialized = 0;
extern void *kr_fast_mutex;
static __attribute__((noinline)) int kr_lazy_init(void) {
payload_args_t *a = payload_get_args();
if (!a || !a->rwpair || !a->rwpipe || !a->kpipe_addr) return -1;
kr_rwpair_0 = a->rwpair[0];
kr_rwpair_1 = a->rwpair[1];
kr_rwpipe_0 = a->rwpipe[0];
kr_rwpipe_1 = a->rwpipe[1];
kr_kpipe_addr = (uint64_t)a->kpipe_addr;
kr_initialized = 1;
return 0;
}
typedef unsigned __int128 u128_alias __attribute__((may_alias));
__attribute__((target("no-avx")))
static int32_t kernel_copyin_fast_inner(const void *udaddr, intptr_t kaddr, size_t len)
{
uint64_t f[7];
*(u128_alias *)&f[0] = *(const u128_alias *)kr_write_setup_const;
f[4] = kr_kpipe_addr;
*(u128_alias *)&f[5] = 0;
if (__crt_syscall(0x69, kr_rwpair_0, 0x29, 0x2e, &f[4], 0x14) != 0) return -1;
f[2] = 0;
if (__crt_syscall(0x69, kr_rwpair_1, 0x29, 0x2e, &f[0], 0x14) != 0) return -1;
f[0] = (uint64_t)kaddr;
*(u128_alias *)&f[1] = 0;
f[4] = kr_kpipe_addr + 0x10;
*(u128_alias *)&f[5] = 0;
if (__crt_syscall(0x69, kr_rwpair_0, 0x29, 0x2e, &f[4], 0x14) != 0) return -1;
if (__crt_syscall(0x69, kr_rwpair_1, 0x29, 0x2e, &f[0], 0x14) != 0) return -1;
__crt_syscall(4, kr_rwpipe_1, (uint64_t)udaddr, (uint64_t)len);
return 0;
}
int32_t kernel_copyin_fast(const void *udaddr, intptr_t kaddr, size_t len)
{
if (!kr_initialized && kr_lazy_init() != 0) return -1;
scePthreadMutexLock(&kr_fast_mutex);
int32_t rc = kernel_copyin_fast_inner(udaddr, kaddr, len);
scePthreadMutexUnlock(&kr_fast_mutex);
return rc;
}
__attribute__((target("no-avx")))
static int32_t kernel_copyout_fast_inner(intptr_t kaddr, void *udaddr, size_t len)
{
uint64_t f[7];
*(u128_alias *)&f[0] = *(const u128_alias *)kr_read_setup_const;
f[4] = kr_kpipe_addr;
*(u128_alias *)&f[5] = 0;
if (__crt_syscall(0x69, kr_rwpair_0, 0x29, 0x2e, &f[4], 0x14) != 0) return -1;
f[2] = 0;
if (__crt_syscall(0x69, kr_rwpair_1, 0x29, 0x2e, &f[0], 0x14) != 0) return -1;
f[0] = (uint64_t)kaddr;
*(u128_alias *)&f[1] = 0;
f[4] = kr_kpipe_addr + 0x10;
*(u128_alias *)&f[5] = 0;
if (__crt_syscall(0x69, kr_rwpair_0, 0x29, 0x2e, &f[4], 0x14) != 0) return -1;
if (__crt_syscall(0x69, kr_rwpair_1, 0x29, 0x2e, &f[0], 0x14) != 0) return -1;
__crt_syscall(3, kr_rwpipe_0, udaddr, (uint64_t)len);
return 0;
}
int32_t kernel_copyout_fast(intptr_t kaddr, void *udaddr, size_t len)
{
if (!kr_initialized && kr_lazy_init() != 0) return -1;
scePthreadMutexLock(&kr_fast_mutex);
int32_t rc = kernel_copyout_fast_inner(kaddr, udaddr, len);
scePthreadMutexUnlock(&kr_fast_mutex);
return rc;
}
#define KR_PROC_CACHE_N 8
static struct { int32_t pid; uint64_t kproc; } kr_proc_cache[KR_PROC_CACHE_N];
static int kr_proc_cache_next = 0;
void kern_proc_cache_flush(void)
{
for (int i = 0; i < KR_PROC_CACHE_N; i++) { kr_proc_cache[i].pid = 0; kr_proc_cache[i].kproc = 0; }
}
void kern_proc_cache_invalidate(int pid)
{
for (int i = 0; i < KR_PROC_CACHE_N; i++)
if (kr_proc_cache[i].pid == (int32_t)pid) { kr_proc_cache[i].pid = 0; kr_proc_cache[i].kproc = 0; }
}
static intptr_t kr_walk_allproc(pid_t pid)
{
uint64_t cur = 0;
uint64_t next = 0;
uint32_t cur_pid = 0;
if (kernel_copyout_fast((intptr_t)KERNEL_ADDRESS_ALLPROC, &cur, 8) != 0) return 0;
while (cur != 0) {
if (kernel_copyout_fast((intptr_t)(cur + 0xBC), &cur_pid, 4) != 0) return 0;
if ((int32_t)cur_pid == pid) return (intptr_t)cur;
if (kernel_copyout_fast((intptr_t)cur, &next, 8) != 0) return 0;
cur = next;
}
return 0;
}
intptr_t kernel_get_proc_fast(pid_t pid)
{
for (int i = 0; i < KR_PROC_CACHE_N; i++) {
if (kr_proc_cache[i].pid == (int32_t)pid && kr_proc_cache[i].kproc != 0) {
uint32_t v = 0;
if (kernel_copyout_fast((intptr_t)(kr_proc_cache[i].kproc + 0xBC), &v, 4) == 0
&& (int32_t)v == (int32_t)pid)
return (intptr_t)kr_proc_cache[i].kproc;
kr_proc_cache[i].pid = 0; kr_proc_cache[i].kproc = 0;
break;
}
}
intptr_t kp = kr_walk_allproc(pid);
if (kp != 0) {
kr_proc_cache[kr_proc_cache_next].pid = (int32_t)pid;
kr_proc_cache[kr_proc_cache_next].kproc = (uint64_t)kp;
kr_proc_cache_next = (kr_proc_cache_next + 1) % KR_PROC_CACHE_N;
}
return kp;
}
intptr_t kernel_get_proc_ucred_fast(pid_t pid)
{
intptr_t kproc = kernel_get_proc_fast(pid);
if (kproc == 0) return 0;
uint64_t ucred = 0;
if (kernel_copyout_fast((intptr_t)(kproc + 0x40), &ucred, 8) != 0) return 0;
return (intptr_t)ucred;
}
uint64_t kernel_get_ucred_authid_fast(pid_t pid)
{
uint64_t authid = 0;
intptr_t ucred = kernel_get_proc_ucred_fast(pid);
if (ucred == 0) return 0;
if (kernel_copyout_fast(ucred + 0x58, &authid, sizeof(authid)) != 0) return 0;
return authid;
}
int32_t kernel_set_ucred_authid_fast(pid_t pid, uint64_t new_authid)
{
intptr_t ucred = kernel_get_proc_ucred_fast(pid);
if (ucred == 0) return -1;
return kernel_copyin_fast(&new_authid, ucred + 0x58, sizeof(new_authid));
}
int32_t kernel_get_ucred_caps_fast(pid_t pid, uint8_t caps[16])
{
intptr_t ucred = kernel_get_proc_ucred_fast(pid);
if (ucred == 0) return -1;
if (kernel_copyout_fast(ucred + 0x60, caps, 0x10) != 0) return -1;
return 0;
}
int32_t kernel_set_ucred_caps_fast(pid_t pid, const uint8_t caps[16])
{
intptr_t ucred = kernel_get_proc_ucred_fast(pid);
if (ucred == 0) return -1;
if (kernel_copyin_fast(caps, ucred + 0x60, 0x10) != 0) return -1;
return 0;
}
void *kernel_get_proc_struct_fast(uint32_t pid)
{
void *result = (void *)0;
if (pid != 0) {
intptr_t kproc = kernel_get_proc_fast((pid_t)pid);
if (kproc != 0) {
void *buf = malloc(0xC90);
if (buf && kernel_copyout_fast(kproc, buf, 0xC90) == 0)
result = buf;
else if (buf) {
free(buf);
}
}
}
return result;
}
int kern_setup_sprx_dispatch_fast(int pid, int sprx_handle,
void *buf1, void *buf2)
{
(void)kernel_get_proc_fast(pid);
intptr_t kproc = kernel_get_proc_fast(pid);
if (kproc == 0) return 0;
uint64_t kaddr;
if (kernel_copyout_fast((intptr_t)(kproc + 0x3E8), &kaddr, 8) != 0)
return 0;
uint8_t *match = (uint8_t *)buf2 + 0x28;
uint64_t want = (uint64_t)(uint32_t)sprx_handle;
do {
if (kernel_copyout_fast((intptr_t)kaddr, &kaddr, 8) != 0) return 0;
if (kaddr == 0) return 0;
if (kernel_copyout_fast((intptr_t)(kaddr + 0x28), match, 8) != 0)
return 0;
} while (*(uint64_t *)match != want);
if (kernel_copyout_fast((intptr_t)kaddr, buf2, 0x180) != 0) return 0;
int32_t rc = kernel_copyout_fast(
(intptr_t)*(uint64_t *)((uint8_t *)buf2 + 0x148),
buf1, 0x120);
return (int)((uint32_t)~rc >> 31);
}
uint64_t kernel_dynlib_resolve_fast(int pid, int module_sel, const char *nid)
{
uint8_t dispatch_table[0x120] __attribute__((aligned(8)));
uint8_t module_record[0x180] __attribute__((aligned(8)));
uint8_t entry[0x18] __attribute__((aligned(8)));
uint8_t nid_read[0x0C];
if (kern_setup_sprx_dispatch_fast(pid, module_sel,
dispatch_table, module_record) == 0)
return 0;
uint64_t kaddr_table = *(uint64_t *)(dispatch_table + 0x28);
uint64_t table_size = *(uint64_t *)(dispatch_table + 0x30);
uint64_t nid_strbase = *(uint64_t *)(dispatch_table + 0x38);
uint64_t module_base = *(uint64_t *)(module_record + 0x30);
uint64_t end = kaddr_table + table_size;
if (kaddr_table >= end) return 0;
for (uint64_t cur = kaddr_table; cur < end; cur += 0x18) {
if (kernel_copyout_fast((intptr_t)cur, entry, 0x18) != 0) return 0;
uint32_t off = *(uint32_t *)entry;
if (kernel_copyout_fast((intptr_t)((uint64_t)off + nid_strbase),
nid_read, 0x0C) != 0) return 0;
int matched = 0;
for (int i = 0; i <= 10; i++) {
uint8_t want = (uint8_t)nid[i];
uint8_t got = nid_read[i];
if (want != got) { matched = -1; break; }
if (want == 0) { matched = 1; break; }
if (i == 10) { matched = 1; break; }
}
if (matched == 1)
return *(uint64_t *)(entry + 0x08) + module_base;
}
return 0;
}
uint64_t kernel_dynlib_mapbase_addr_by_proc_fast(void *proc, unsigned int sel)
{
uint64_t cur = (uint64_t)proc + 0x3E8;
if (kernel_copyout_fast((intptr_t)cur, &cur, 8) != 0) return (uint64_t)-1;
if (kernel_copyout_fast((intptr_t)cur, &cur, 8) != 0) return (uint64_t)-1;
while (cur != 0) {
uint32_t entry_sel = 0xFFFFFFFFu;
if (kernel_copyout_fast((intptr_t)(cur + 0x28), &entry_sel, 4) != 0)
return (uint64_t)-1;
if (entry_sel == 0xFFFFFFFFu) return (uint64_t)-1;
if (entry_sel == sel) {
uint64_t mapbase = 0;
kernel_copyout_fast((intptr_t)(cur + 0x30), &mapbase, 8);
return mapbase;
}
if (kernel_copyout_fast((intptr_t)cur, &cur, 8) != 0)
return (uint64_t)-1;
}
uint64_t fallback = 0;
kernel_copyout_fast((intptr_t)0x30, &fallback, 8);
return fallback;
}