Files
ps5debug-NG/debugger/source/main.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

376 lines
10 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
#include "protocol.h"
#include "sdk_shim.h"
#include "net.h"
extern int cmd_handler(int fd, struct cmd_packet *packet, unsigned char client_idx);
extern void print_ascii_banner(void);
extern void (*__init_array_start[])(void) __attribute__((weak));
extern void (*__init_array_end[])(void) __attribute__((weak));
static void run_init_array_once(void)
{
static int done = 0;
if (done) return;
done = 1;
if (!__init_array_start) return;
for (void (**p)(void) = __init_array_start; p < __init_array_end; ++p)
if (*p) (*p)();
}
extern void server_state_init(void);
extern void *alloc_client(void);
extern void free_client(void *svc);
extern int dispatch_debug_events(void);
extern void *klog_server_thread(void *arg);
extern int g_klog_listen_fd;
#include "debug_state.h"
struct server_client {
uint32_t active;
int fd;
uint32_t debugging;
uint8_t client_addr[16];
uint8_t pad_1c[4];
uint8_t dbgctx[0x358];
};
extern void *curdbgcli;
extern void *curdbgctx;
extern uint32_t g_debug_attached;
extern void *g_server_mutex;
extern void *g_proc_rw_mutex;
#define SERVER_PORT 744
#define SERVER_MAXCLIENTS 12
extern struct server_client g_clients[SERVER_MAXCLIENTS];
#define PACKET_MAGIC 0xFFAABBCCu
static void notify(const char *msg) {
typedef struct { char useless1[45]; char message[3075]; } req_t;
req_t req;
memset(&req, 0, sizeof(req));
strncpy(req.message, msg, sizeof(req.message) - 1);
sceKernelSendNotificationRequest(0, &req, sizeof(req), 0);
}
void configure_socket(int fd) {
int32_t one = 1;
setsockopt(fd, 6, 1, &one, 4);
one = 1;
setsockopt(fd, 0xFFFF, 4, &one, 4);
}
static int handle_client(struct server_client *svc) {
int fd = svc->fd;
struct timeval tv;
fd_set sfd;
while (1) {
tv.tv_sec = 0;
tv.tv_usec = 1000;
FD_ZERO(&sfd);
FD_SET(fd, &sfd);
errno = 0;
select(fd + 1, &sfd, NULL, NULL, &tv);
if (FD_ISSET(fd, &sfd)) {
struct cmd_packet packet;
memset(&packet, 0, 12);
if (net_recv_all(fd, &packet, 12, 1) < 0) return 0;
if (errno == ECONNRESET) return 0;
if (packet.magic != PACKET_MAGIC) continue;
packet.data = NULL;
void *data = NULL;
if (packet.datalen != 0) {
if (packet.datalen > 0x100000) {
net_send_int32(fd, 0xF0000002u );
continue;
}
data = malloc(packet.datalen);
if (!data) {
net_send_int32(fd, CMD_DATA_NULL);
continue;
}
if (net_recv_all(fd, data, (int)packet.datalen, 1) < 0) {
free(data);
return 0;
}
packet.data = data;
}
if (g_debug_attached == 0 && packet.cmd == CMD_DEBUG_ATTACH) {
curdbgcli = svc;
curdbgctx = (void *)((char *)svc + 0x20);
}
unsigned char client_idx = (unsigned char)((svc->active - 1) & 0xFFu);
int rc = cmd_handler(fd, &packet, client_idx);
if (data) free(data);
if (rc != 0) return rc;
if (svc->debugging && g_stopgo_resume_signal != 0xFFFFFFFFu) {
scePthreadMutexLock(&g_proc_rw_mutex);
scePthreadMutexLock(&g_server_mutex);
int dde_rc = dispatch_debug_events();
scePthreadMutexUnlock(&g_server_mutex);
scePthreadMutexUnlock(&g_proc_rw_mutex);
if (dde_rc != 0) return 0;
}
continue;
}
if (svc->debugging) {
scePthreadMutexLock(&g_proc_rw_mutex);
scePthreadMutexLock(&g_server_mutex);
int dde_rc = dispatch_debug_events();
scePthreadMutexUnlock(&g_server_mutex);
scePthreadMutexUnlock(&g_proc_rw_mutex);
if (dde_rc != 0) return 0;
}
if (errno == ECONNRESET) return 0;
sceKernelUsleep(svc->debugging ? 250 : 10000);
}
}
static void *client_thread_main(void *arg) {
struct server_client *svc = (struct server_client *)arg;
handle_client(svc);
free_client(svc);
return NULL;
}
struct pld_sockaddr_in {
uint8_t sin_len;
uint8_t sin_family;
uint16_t sin_port;
uint32_t sin_addr;
uint8_t sin_zero[8];
};
static int g_bcast_fd = -1;
static void *broadcast_thread(void *arg) {
(void)arg;
struct pld_sockaddr_in sa;
sa.sin_len = 0x10;
sa.sin_family = 2;
sa.sin_port = sceNetHtons(0x3F2);
sa.sin_addr = 0;
memset(&sa.sin_zero[2], 0, 6);
g_bcast_fd = sceNetSocket((const char *)0, 2, 2, 0);
if (g_bcast_fd < 0) return (void *)0;
int32_t one = 1;
sceNetSetsockopt(g_bcast_fd, 0xFFFF, 0x20, &one, 4);
one = 1;
sceNetSetsockopt(g_bcast_fd, 0xFFFF, 4, &one, 4);
one = 1;
sceNetSetsockopt(g_bcast_fd, 6, 1, &one, 4);
errno = 0;
if (sceNetBind(g_bcast_fd, &sa, 0x10) != 0) return (void *)0;
int local_fd = g_bcast_fd;
for (;;) {
scePthreadYield();
struct pld_sockaddr_in peer;
uint32_t peer_len = 0x10;
uint32_t magic = 0;
int n = sceNetRecvfrom(local_fd, &magic, 4, 0, &peer, &peer_len);
if (n < 0) {
int err = *sceNetErrnoLoc();
if (err == 0xA3 || err == EBADF) return (void *)0;
} else if (magic == 0xFFFFAAAAu) {
sceNetSendto(local_fd, &magic, 4, 0, &peer, peer_len);
}
sceKernelSleep(1);
}
}
static int start_server(void) {
{
ScePthread bcast_tid;
if (scePthreadCreate(&bcast_tid, NULL, broadcast_thread,
NULL, "ps5debug_bcast") == 0) {
scePthreadDetach(bcast_tid);
}
}
int srv = sceNetSocket((const char *)0, 2 , 1 , 0);
if (srv < 0) {
notify("ps5debug: sceNetSocket() failed");
return 1;
}
int32_t one = 1;
sceNetSetsockopt(srv, 0xFFFF, 4, &one, 4);
one = 1;
sceNetSetsockopt(srv, 6, 1, &one, 4);
struct pld_sockaddr_in sa;
sa.sin_len = 0x10;
sa.sin_family = 2;
sa.sin_port = sceNetHtons(SERVER_PORT);
sa.sin_addr = 0;
memset(&sa.sin_zero[2], 0, 6);
if (sceNetBind(srv, &sa, 0x10) != 0) {
notify("ps5debug: sceNetBind(744) failed");
sceNetSocketClose(srv);
return 1;
}
if (sceNetListen(srv, 8) != 0) {
notify("ps5debug: sceNetListen() failed");
sceNetSocketClose(srv);
return 1;
}
while (1) {
struct pld_sockaddr_in cli;
uint32_t cli_len = 0x10;
int fd = sceNetAccept(srv, &cli, &cli_len);
if (fd < 0) {
int err = *sceNetErrnoLoc();
if (err == 0xA3) goto teardown;
continue;
}
if (fd == 0) continue;
int32_t one_a = 1;
sceNetSetsockopt(fd, 6, 1, &one_a, 4);
struct server_client *slot = (struct server_client *)alloc_client();
if (!slot) {
sceNetSocketClose(fd);
continue;
}
slot->fd = fd;
slot->debugging = 0;
memcpy(slot->client_addr, &cli, sizeof(cli) > 16 ? 16 : sizeof(cli));
memset(slot->dbgctx, 0, sizeof(slot->dbgctx));
ScePthread thr;
if (scePthreadCreate(&thr, NULL, client_thread_main,
slot, "ps5debug_client") == 0) {
scePthreadDetach(thr);
} else {
handle_client(slot);
free_client(slot);
}
}
teardown:
for (int i = 0; i < SERVER_MAXCLIENTS; i++) {
if (g_clients[i].active != 0) {
g_clients[i].active = 0;
close(g_clients[i].fd);
}
}
sceNetSocketClose(srv);
if (g_bcast_fd >= 0) {
sceNetSocketAbort(g_bcast_fd, 3);
sceNetSocketClose(g_bcast_fd);
}
if (g_klog_listen_fd >= 0) {
sceNetSocketAbort(g_klog_listen_fd, 3);
}
return 0;
}
static void debugger_server_loop(void) {
static const char disconnect_msg[] = "ps5debug-NG disconnected.";
static const char loaded_msg[] = "ps5debug-NG by OSR v1.2.2 loaded!\n"
"Coded by OpenSourcereR\n"
"Special thanks to\n"
"golden, Ctn & SiSTRo! \xE2\x9D\xA4\n";
char ip_buf[16];
uint32_t retry = 0;
for (;;) {
memset(ip_buf, 0, 0x10);
net_get_ip_address(ip_buf);
if (strlen(ip_buf) > 4) {
{
ScePthread klog_tid;
if (scePthreadCreate(&klog_tid, NULL, klog_server_thread,
NULL, "ps5debug_klog") == 0) {
scePthreadDetach(klog_tid);
}
}
g_stopgo_resume_signal = 0xFFFFFFFFu;
g_stopgo_resume_pid = 0;
notify(loaded_msg);
(void)start_server();
retry = 0;
continue;
}
uint32_t next = retry + 1;
if (retry == 0) {
notify(disconnect_msg);
sceKernelSleep(2);
} else if (next > 99) {
sceKernelSleep(1000);
} else {
sceKernelSleep(2);
}
retry = next;
}
}
extern int hijack_mode(void);
static void *debugger_server_loop_thread(void *arg) {
(void)arg;
debugger_server_loop();
return NULL;
}
int main(int argc, char *argv[]) {
(void)argc; (void)argv;
run_init_array_once();
print_ascii_banner();
server_state_init();
if (hijack_mode()) {
ScePthread tid;
if (scePthreadCreate(&tid, NULL, debugger_server_loop_thread,
NULL, "ps5debug_main") == 0) {
scePthreadDetach(tid);
}
return 0;
}
debugger_server_loop();
return 0;
}