Files
ps5debug-NG/debugger/source/proc.c
T
OpenSourcereR-dev 6b3cc3a216 Fix per-firmware proc appinfo offsets (name/path/titleid/contentid)
The struct proc name/path/titleid/contentid offsets were hardcoded to the
4.x layout with no firmware gating, so on 6.x and newer they read empty or
garbage: PROC_INFO/PROC_LIST returned blank names and the installer failed
with "SceShellCore not found" on 6.x.

Restore the original's per-firmware selection (three offset sets across
FW 3.x-8.x) behind a shared proc_get_field_offsets() helper that switches on
kernel_get_fw_version(); unsupported firmwares report unknown rather than
emitting wrong offsets. Wired into find_pid, proc_info_handle/proc_list, and
console_foreground_app_handle.
2026-05-31 02:16:51 +00:00

907 lines
27 KiB
C

// SPDX-License-Identifier: GPL-3.0-only
#include "protocol.h"
#include "sdk_shim.h"
#include "net.h"
#include "proc.h"
#include "kdbg.h"
#include "kern_rw_fast.h"
#include "proc_field_offsets.h"
#include "Zydis.h"
#define PROC_NEXT_OFFSET 0x00
#define PROC_VMSPACE_OFFSET 0x200
#define PROC_SELFINFO_NAME_SIZE 32
static void copy_cstr_from_buf(const uint8_t *src, char *out, size_t max) {
if (max == 0) return;
size_t i;
for (i = 0; i < max - 1; i++) {
out[i] = (char)src[i];
if (src[i] == 0) {
memset(out + i, 0, max - i);
return;
}
}
out[max - 1] = 0;
}
#define DISASM_READ_CHUNK 0x10000
static void disasm_fill_entry(struct disasm_instr_entry *out,
uint64_t addr,
const ZydisDecodedInstruction *insn,
const ZydisDecodedOperand *operands) {
memset(out, 0, sizeof(*out));
out->addr = addr;
out->length = insn->length;
out->mnemonic_lo = (uint8_t)(insn->mnemonic & 0xFF);
switch (insn->meta.category) {
case ZYDIS_CATEGORY_CALL: out->kind |= 0x01; break;
case ZYDIS_CATEGORY_RET: out->kind |= 0x02; break;
case ZYDIS_CATEGORY_UNCOND_BR: out->kind |= 0x04; break;
case ZYDIS_CATEGORY_COND_BR: out->kind |= 0x08; break;
default: break;
}
for (ZyanU8 i = 0; i < insn->operand_count_visible; i++) {
const ZydisDecodedOperand *op = &operands[i];
if (op->type != ZYDIS_OPERAND_TYPE_MEMORY) continue;
out->kind |= 0x10;
if (op->actions & ZYDIS_OPERAND_ACTION_MASK_READ) out->kind |= 0x40;
if (op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE) out->kind |= 0x80;
out->mem_base_reg = (uint8_t)(op->mem.base & 0xFF);
out->mem_index_reg = (uint8_t)(op->mem.index & 0xFF);
out->mem_scale = op->mem.scale;
out->mem_disp = op->mem.disp.has_displacement ? op->mem.disp.value : 0;
if (op->mem.base == ZYDIS_REGISTER_RIP) {
out->kind |= 0x20;
out->rip_rel_target = addr + insn->length + (uint64_t)op->mem.disp.value;
}
break;
}
}
typedef void (*disasm_emit_fn)(uint64_t addr,
const ZydisDecodedInstruction *insn,
const ZydisDecodedOperand *operands,
void *ctx);
static uint64_t disasm_iterate(uint32_t pid, uint64_t start, uint64_t length,
uint64_t max_instrs, disasm_emit_fn emit, void *ctx) {
void *chunk = net_alloc_buffer(DISASM_READ_CHUNK);
if (!chunk) return 0;
ZydisDecoder decoder;
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_STACK_WIDTH_64);
ZydisDecodedInstruction insn;
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT];
uint64_t emitted = 0;
uint64_t off = 0;
while (off < length && emitted < max_instrs) {
uint64_t remaining = length - off;
uint32_t read_len = remaining > DISASM_READ_CHUNK ? DISASM_READ_CHUNK : (uint32_t)remaining;
(void)sys_proc_rw_w0((uint64_t)pid, start + off, (uint64_t)read_len, chunk, 0);
uint32_t pos = 0;
while (pos < read_len && emitted < max_instrs) {
uint32_t avail = read_len - pos;
ZyanStatus r = ZydisDecoderDecodeFull(&decoder,
(uint8_t *)chunk + pos, avail,
&insn, operands);
if (!ZYAN_SUCCESS(r)) {
pos++;
continue;
}
emit(start + off + pos, &insn, operands, ctx);
emitted++;
pos += insn.length;
if (pos + ZYDIS_MAX_INSTRUCTION_LENGTH > read_len && remaining > read_len) {
break;
}
}
off += (pos > 0 ? pos : 1);
}
free(chunk);
return emitted;
}
struct disasm_ctx {
int fd;
uint8_t *out_buf;
uint32_t out_cap;
uint32_t out_used;
};
static void disasm_emit_record(uint64_t addr,
const ZydisDecodedInstruction *insn,
const ZydisDecodedOperand *operands,
void *ctx_) {
struct disasm_ctx *ctx = (struct disasm_ctx *)ctx_;
if (ctx->out_used + DISASM_INSTR_ENTRY_SIZE > ctx->out_cap) {
net_send_all(ctx->fd, ctx->out_buf, (int)ctx->out_used);
ctx->out_used = 0;
}
struct disasm_instr_entry *e = (struct disasm_instr_entry *)(ctx->out_buf + ctx->out_used);
disasm_fill_entry(e, addr, insn, operands);
ctx->out_used += DISASM_INSTR_ENTRY_SIZE;
}
int proc_disasm_region_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_disasm_packet *dp = (struct cmd_proc_disasm_packet *)packet->data;
if (!dp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
if (dp->max_entries == 0 || dp->max_entries > 1000000) {
net_send_int32(fd, CMD_ERROR);
return 1;
}
struct disasm_ctx ctx;
ctx.fd = fd;
ctx.out_cap = 0x10000;
ctx.out_used = 0;
ctx.out_buf = (uint8_t *)net_alloc_buffer(ctx.out_cap);
if (!ctx.out_buf) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
(void)disasm_iterate(dp->pid, dp->address, dp->length,
dp->max_entries, disasm_emit_record, &ctx);
if (ctx.out_used > 0) {
net_send_all(fd, ctx.out_buf, (int)ctx.out_used);
}
uint8_t sentinel[DISASM_INSTR_ENTRY_SIZE];
memset(sentinel, 0xFF, sizeof(sentinel));
net_send_all(fd, sentinel, sizeof(sentinel));
free(ctx.out_buf);
return 0;
}
struct xrefs_ctx {
int fd;
uint64_t *buf;
uint32_t cap;
uint32_t used;
};
static void xrefs_emit(uint64_t addr,
const ZydisDecodedInstruction *insn,
const ZydisDecodedOperand *operands,
void *ctx_) {
struct xrefs_ctx *ctx = (struct xrefs_ctx *)ctx_;
for (ZyanU8 i = 0; i < insn->operand_count_visible; i++) {
const ZydisDecodedOperand *op = &operands[i];
if (op->type != ZYDIS_OPERAND_TYPE_MEMORY) continue;
if (op->mem.base != ZYDIS_REGISTER_RIP) break;
uint64_t target = addr + insn->length + (uint64_t)op->mem.disp.value;
if (ctx->used + 1 > ctx->cap) {
net_send_all(ctx->fd, ctx->buf, (int)(ctx->used * sizeof(uint64_t)));
ctx->used = 0;
}
ctx->buf[ctx->used++] = target;
break;
}
}
int proc_extract_code_xrefs_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_disasm_packet *dp = (struct cmd_proc_disasm_packet *)packet->data;
if (!dp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct xrefs_ctx ctx;
ctx.fd = fd;
ctx.cap = 0x2000;
ctx.used = 0;
ctx.buf = (uint64_t *)net_alloc_buffer(ctx.cap * sizeof(uint64_t));
if (!ctx.buf) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
uint64_t max_instrs = dp->max_entries ? dp->max_entries : 10000000;
(void)disasm_iterate(dp->pid, dp->address, dp->length,
max_instrs, xrefs_emit, &ctx);
if (ctx.used > 0) {
net_send_all(fd, ctx.buf, (int)(ctx.used * sizeof(uint64_t)));
}
uint64_t sentinel = 0xFFFFFFFFFFFFFFFFULL;
net_send_all(fd, &sentinel, sizeof(uint64_t));
free(ctx.buf);
return 0;
}
struct xrefs_to_ctx {
int fd;
uint64_t target;
uint64_t *buf;
uint32_t cap;
uint32_t used;
};
static void xrefs_to_emit(uint64_t addr,
const ZydisDecodedInstruction *insn,
const ZydisDecodedOperand *operands,
void *ctx_) {
struct xrefs_to_ctx *ctx = (struct xrefs_to_ctx *)ctx_;
for (ZyanU8 i = 0; i < insn->operand_count_visible; i++) {
const ZydisDecodedOperand *op = &operands[i];
if (op->type != ZYDIS_OPERAND_TYPE_MEMORY) continue;
if (op->mem.base != ZYDIS_REGISTER_RIP) break;
uint64_t resolved = addr + insn->length + (uint64_t)op->mem.disp.value;
if (resolved == ctx->target) {
if (ctx->used + 1 > ctx->cap) {
net_send_all(ctx->fd, ctx->buf, (int)(ctx->used * sizeof(uint64_t)));
ctx->used = 0;
}
ctx->buf[ctx->used++] = addr;
}
break;
}
}
int proc_find_xrefs_to_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_xrefs_to_packet *xp = (struct cmd_proc_xrefs_to_packet *)packet->data;
if (!xp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct xrefs_to_ctx ctx;
ctx.fd = fd;
ctx.target = xp->target_address;
ctx.cap = 0x2000;
ctx.used = 0;
ctx.buf = (uint64_t *)net_alloc_buffer(ctx.cap * sizeof(uint64_t));
if (!ctx.buf) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
(void)disasm_iterate(xp->pid, xp->scan_address, xp->scan_length,
10000000, xrefs_to_emit, &ctx);
if (ctx.used > 0) {
net_send_all(fd, ctx.buf, (int)(ctx.used * sizeof(uint64_t)));
}
uint64_t sentinel = 0xFFFFFFFFFFFFFFFFULL;
net_send_all(fd, &sentinel, sizeof(uint64_t));
free(ctx.buf);
return 0;
}
struct proc_list_wire_entry {
char name[32];
int32_t pid;
} __attribute__((packed));
int proc_list_handle(int fd, struct cmd_packet *packet) {
(void)packet;
intptr_t allproc_head = (intptr_t)KERNEL_ADDRESS_ALLPROC;
intptr_t kproc = 0;
if (kernel_copyout_fast(allproc_head, &kproc, sizeof(kproc)) != 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint32_t count = 0;
intptr_t cur = kproc;
while (cur != 0 && count < 0x1000) {
count++;
intptr_t next = 0;
if (kernel_copyout_fast(cur + PROC_NEXT_OFFSET, &next, sizeof(next)) != 0) break;
cur = next;
}
if (count == 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint32_t length = count * (uint32_t)sizeof(struct proc_list_wire_entry);
struct proc_list_wire_entry *entries = (struct proc_list_wire_entry *)net_alloc_buffer(length);
if (!entries) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
memset(entries, 0, length);
struct proc_field_offsets off;
proc_get_field_offsets(&off);
cur = kproc;
for (uint32_t i = 0; i < count && cur != 0; i++) {
if (off.known)
kernel_copyout_fast(cur + off.name, entries[i].name, PROC_SELFINFO_NAME_SIZE);
size_t nlen = 0;
while (nlen < PROC_SELFINFO_NAME_SIZE && entries[i].name[nlen] != 0) nlen++;
if (nlen < PROC_SELFINFO_NAME_SIZE) {
memset(entries[i].name + nlen, 0, PROC_SELFINFO_NAME_SIZE - nlen);
}
kernel_copyout_fast(cur + KERNEL_OFFSET_PROC_P_PID, &entries[i].pid, sizeof(entries[i].pid));
intptr_t next = 0;
kernel_copyout_fast(cur + PROC_NEXT_OFFSET, &next, sizeof(next));
cur = next;
}
net_send_int32(fd, CMD_SUCCESS);
uint64_t count64 = count;
net_send_all(fd, &count64, sizeof(uint32_t));
net_send_all(fd, entries, (int)length);
free(entries);
return 0;
}
void proc_read_mem(uint32_t pid, uint64_t addr, uint64_t len, void *buf) {
if (proc_aux_range_contains(pid, addr, len) &&
proc_ptwalk_read(pid, addr, len, buf) == 0)
return;
sys_proc_rw_w0((uint64_t)pid, addr, len, buf, 0);
}
int proc_read_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_read_packet *rp = (struct cmd_proc_read_packet *)packet->data;
if (!rp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
void *data = net_alloc_buffer(0x10000);
if (!data) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
uint64_t length = rp->length;
uint64_t address = rp->address;
while (length > 0x10000) {
memset(data, 0, 0x10000);
proc_read_mem(rp->pid, address, 0x10000, data);
net_send_all(fd, data, 0x10000);
address += 0x10000;
length -= 0x10000;
}
if (length > 0) {
memset(data, 0, (size_t)length);
proc_read_mem(rp->pid, address, length, data);
net_send_all(fd, data, (int)length);
}
free(data);
return 0;
}
#define PROC_RS_USR_MIN 0x10000ULL
#define PROC_RS_USR_MAX 0x0000800000000000ULL
static inline int proc_rs_addr_ok(uint64_t a, uint64_t len) {
if (len == 0) return 0;
if (a < PROC_RS_USR_MIN || a >= PROC_RS_USR_MAX) return 0;
if (a + len < a) return 0;
if (a + len > PROC_RS_USR_MAX) return 0;
return 1;
}
int proc_read_stack_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_read_stack_packet *sp =
(struct cmd_proc_read_stack_packet *)packet->data;
if (!sp) { net_send_int32(fd, CMD_DATA_NULL); return 1; }
uint32_t pid = sp->pid;
uint64_t rbp = sp->rbp;
uint64_t rsp = sp->rsp;
uint32_t depth = sp->depth;
if (depth == 0) depth = 1;
if (depth > CMD_PROC_READ_STACK_MAX_DEPTH) depth = CMD_PROC_READ_STACK_MAX_DEPTH;
uint64_t cap = 4 + (uint64_t)depth *
(32 + 12 + CMD_PROC_READ_STACK_LOCALS_CAP + CMD_PROC_READ_STACK_CODE_LEN);
uint8_t *buf = (uint8_t *)net_alloc_buffer(cap);
if (!buf) { net_send_int32(fd, CMD_DATA_NULL); return 1; }
uint8_t code_scratch[CMD_PROC_READ_STACK_CODE_LEN];
uint32_t off = 4;
uint32_t n_frames = 0;
uint64_t cur_rbp = rbp, cur_rsp = rsp;
for (uint32_t i = 0; i < depth; i++) {
if (!proc_rs_addr_ok(cur_rbp, 16)) break;
uint64_t pair[2] = { 0, 0 };
sys_proc_rw_w0((uint64_t)pid, cur_rbp, 16, pair, 0);
uint64_t saved_rbp = pair[0];
uint64_t ret_addr = pair[1];
int64_t fs = (int64_t)cur_rbp - (int64_t)cur_rsp + 8;
uint32_t flags = 0;
uint32_t locals_len = 0;
if (fs <= 0 || (uint64_t)fs > CMD_PROC_READ_STACK_LOCALS_CAP
|| !proc_rs_addr_ok(cur_rsp, (uint64_t)fs)) {
flags |= 1u;
} else {
locals_len = (uint32_t)fs;
}
uint64_t code_addr = ret_addr - CMD_PROC_READ_STACK_CODE_OFF;
uint32_t code_len = proc_rs_addr_ok(code_addr, CMD_PROC_READ_STACK_CODE_LEN)
? CMD_PROC_READ_STACK_CODE_LEN : 0u;
if (off + 32 + 12 + locals_len + code_len > cap) break;
memcpy(buf + off, &cur_rbp, 8); off += 8;
memcpy(buf + off, &cur_rsp, 8); off += 8;
memcpy(buf + off, &saved_rbp, 8); off += 8;
memcpy(buf + off, &ret_addr, 8); off += 8;
memcpy(buf + off, &flags, 4); off += 4;
memcpy(buf + off, &locals_len, 4); off += 4;
memcpy(buf + off, &code_len, 4); off += 4;
if (locals_len) {
memset(buf + off, 0, locals_len);
sys_proc_rw_w0((uint64_t)pid, cur_rsp, locals_len, buf + off, 0);
off += locals_len;
}
if (code_len) {
memset(code_scratch, 0, sizeof(code_scratch));
sys_proc_rw_w0((uint64_t)pid, code_addr, code_len, code_scratch, 0);
memcpy(buf + off, code_scratch, code_len);
off += code_len;
}
n_frames++;
if (saved_rbp == 0) break;
cur_rsp = cur_rbp + 8;
cur_rbp = saved_rbp;
}
memcpy(buf, &n_frames, 4);
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &off, 4);
net_send_all(fd, buf, (int)off);
free(buf);
return 0;
}
int proc_write_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_write_packet *wp = (struct cmd_proc_write_packet *)packet->data;
if (!wp) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
void *data = net_alloc_buffer(0x10000);
if (!data) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
uint64_t length = wp->length;
uint64_t address = wp->address;
while (length > 0x10000) {
net_recv_all(fd, data, 0x10000, 1);
sys_proc_rw_w1((uint64_t)wp->pid, address, 0x10000, data, 0);
address += 0x10000;
length -= 0x10000;
}
if (length > 0) {
net_recv_all(fd, data, (int)length, 1);
sys_proc_rw_w1((uint64_t)wp->pid, address, length, data, 0);
}
net_send_int32(fd, CMD_SUCCESS);
free(data);
return 0;
}
int proc_maps_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_maps_packet *mp = (struct cmd_proc_maps_packet *)packet->data;
if (!mp) {
net_send_int32(fd, CMD_ERROR);
return 1;
}
void *maps = NULL;
int count = 0;
if (sys_proc_vm_map(mp->pid, &maps, &count) != 0) {
sceKernelUsleep(10000);
for (int retries = 21; retries > 0; retries--) {
if (sys_proc_vm_map(mp->pid, &maps, &count) == 0) break;
sceKernelUsleep(10000);
if (retries == 1) {
net_send_int32(fd, CMD_ERROR);
return 1;
}
}
}
if (count == 0) {
net_send_int32(fd, CMD_ERROR);
if (maps) free(maps);
return 1;
}
if (maps == NULL) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
void *aug = NULL;
int aug_count = 0;
if (proc_ptwalk_augment(mp->pid, (struct proc_vm_map_entry *)maps, count,
(struct proc_vm_map_entry **)&aug, &aug_count) == 0
&& aug != NULL && aug_count > 0) {
free(maps);
maps = aug;
count = aug_count;
}
net_send_int32(fd, CMD_SUCCESS);
uint32_t count32 = (uint32_t)count;
net_send_all(fd, &count32, sizeof(count32));
net_send_all(fd, maps, count * (int)sizeof(struct proc_vm_map_entry));
free(maps);
return 0;
}
int proc_install_handle(int fd, struct cmd_packet *packet) {
if (!packet->data) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
uint64_t resp_rpcstub = 0;
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &resp_rpcstub, 8);
return 0;
}
int proc_call_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_call_packet *cp;
struct sys_proc_call_args args;
struct cmd_proc_call_response resp;
cp = (struct cmd_proc_call_packet *)packet->data;
if (cp) {
args.pid = cp->pid;
args.rpcstub = cp->rpcstub;
args.rax = 0;
args.rip = cp->rpc_rip;
args.rdi = cp->rpc_rdi;
args.rsi = cp->rpc_rsi;
args.rdx = cp->rpc_rdx;
args.rcx = cp->rpc_rcx;
args.r8 = cp->rpc_r8;
args.r9 = cp->rpc_r9;
if (sys_proc_cmd(cp->pid, SYS_PROC_CALL, &args)) return -1;
resp.pid = cp->pid;
resp.rpc_rax = args.rax;
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &resp, (int)sizeof(resp));
return 0;
}
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
int proc_elf_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_elf_packet *ep;
struct sys_proc_elf_args args;
void *elf;
ep = (struct cmd_proc_elf_packet *)packet->data;
if (!ep) {
net_send_int32(fd, CMD_ERROR);
return 1;
}
elf = net_alloc_buffer(ep->length);
if (!elf) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
net_recv_all(fd, elf, ep->length, 1);
args.elf = elf;
args.length = ep->length;
if (sys_proc_cmd(ep->pid, SYS_PROC_ELF, &args)) {
free(elf);
net_send_int32(fd, CMD_ERROR);
return 1;
}
free(elf);
net_send_int32(fd, CMD_SUCCESS);
return 0;
}
int proc_elf_rpc_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_elf_rpc_packet *ep;
struct sys_proc_elf_rpc_args args;
struct cmd_proc_elf_rpc_response resp;
void *elf;
ep = (struct cmd_proc_elf_rpc_packet *)packet->data;
if (!ep) {
net_send_int32(fd, CMD_ERROR);
return 1;
}
elf = net_alloc_buffer(ep->length);
if (!elf) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
net_send_int32(fd, CMD_SUCCESS);
net_recv_all(fd, elf, ep->length, 1);
args.elf = elf;
args.length = ep->length;
args.entry = 0;
if (sys_proc_cmd(ep->pid, SYS_PROC_ELF_RPC, &args)) {
free(elf);
net_send_int32(fd, CMD_ERROR);
return 1;
}
free(elf);
resp.entry = args.entry;
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &resp, (int)sizeof(resp));
return 0;
}
struct cmd_proc_protect_packet {
uint32_t pid;
uint64_t address;
uint32_t length;
uint32_t prot;
} __attribute__((packed));
int proc_protect_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_protect_packet *pp;
struct sys_proc_protect_args args;
pp = (struct cmd_proc_protect_packet *)packet->data;
if (pp) {
args.address = pp->address;
args.length = pp->length;
args.prot = pp->prot;
int rc = sys_proc_cmd(pp->pid, SYS_PROC_PROTECT, &args);
net_send_int32(fd, rc == 0 ? CMD_SUCCESS : CMD_ERROR);
return 0;
}
net_send_int32(fd, CMD_DATA_NULL);
return 0;
}
int proc_info_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_info_packet *ip = (struct cmd_proc_info_packet *)packet->data;
if (!ip) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
if ((int32_t)ip->pid <= 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct proc_field_offsets off;
if (proc_get_field_offsets(&off) != 0) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
void *proc_buf = kernel_get_proc_struct_fast(ip->pid);
if (!proc_buf) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
struct cmd_proc_info_response resp;
memset(&resp, 0, sizeof(resp));
resp.pid = ip->pid;
const uint8_t *p = (const uint8_t *)proc_buf;
memcpy(resp.name, p + off.name, PROC_SELFINFO_NAME_SIZE);
copy_cstr_from_buf(p + off.path, resp.path, sizeof(resp.path));
copy_cstr_from_buf(p + off.titleid, resp.titleid, sizeof(resp.titleid));
copy_cstr_from_buf(p + off.contentid, resp.contentid, sizeof(resp.contentid));
free(proc_buf);
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &resp, sizeof(resp));
return 0;
}
int proc_alloc_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_alloc_packet *ap;
struct sys_proc_alloc_args args;
struct cmd_proc_alloc_response resp;
ap = (struct cmd_proc_alloc_packet *)packet->data;
if (ap) {
args.length = ap->length;
if (sys_proc_cmd(ap->pid, SYS_PROC_ALLOC, &args) != 0) {
net_send_int32(fd, CMD_ERROR);
return 0;
}
resp.address = args.address;
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &resp, sizeof(resp));
return 0;
}
net_send_int32(fd, CMD_DATA_NULL);
return 0;
}
int proc_free_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_free_packet *fp;
struct sys_proc_free_args args;
fp = (struct cmd_proc_free_packet *)packet->data;
if (fp) {
args.address = fp->address;
args.length = fp->length;
sys_proc_cmd(fp->pid, SYS_PROC_FREE, &args);
net_send_int32(fd, CMD_SUCCESS);
return 0;
}
net_send_int32(fd, CMD_DATA_NULL);
return 0;
}
int proc_unknown_d_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_unknown_d_packet *up =
(struct cmd_proc_unknown_d_packet *)packet->data;
if (!up) {
net_send_int32(fd, CMD_DATA_NULL);
return 1;
}
void *maps = NULL;
int count = 0;
int64_t result = 0;
int ok = (sys_proc_vm_map(up->pid, &maps, &count) == 0
&& count > 0 && maps);
if (ok) {
struct proc_vm_map_entry *first = (struct proc_vm_map_entry *)maps;
result = (int64_t)first->start;
}
if (maps) free(maps);
if (!ok) {
net_send_int32(fd, CMD_ERROR);
return 0;
}
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &result, 8);
return 0;
}
int proc_alloc_hinted_handle(int fd, struct cmd_packet *packet) {
struct cmd_proc_alloc_hinted_packet *ap;
struct sys_proc_alloc_args args;
struct cmd_proc_alloc_response resp;
ap = (struct cmd_proc_alloc_hinted_packet *)packet->data;
if (ap) {
args.address = ap->hint;
args.length = ap->length;
int rc = sys_proc_cmd(ap->pid, SYS_PROC_ALLOC_HINTED, &args);
resp.address = args.address;
if (rc != 0) {
net_send_int32(fd, CMD_ERROR);
net_send_all(fd, &resp, sizeof(resp));
return 0;
}
net_send_int32(fd, CMD_SUCCESS);
net_send_all(fd, &resp, sizeof(resp));
return 0;
}
net_send_int32(fd, CMD_DATA_NULL);
return 0;
}
int proc_handle(int fd, struct cmd_packet *packet, unsigned char client_idx) {
(void)client_idx;
uint32_t cmd = packet->cmd;
switch (cmd) {
case 0xBDAA0001u: return proc_list_handle(fd, packet);
case 0xBDAA0002u: return proc_read_handle(fd, packet);
case 0xBDAA0023u: return proc_read_stack_handle(fd, packet);
case 0xBDAA0024u: return proc_assemble_handle(fd, packet);
case 0xBDAA0003u: return proc_write_handle(fd, packet);
case 0xBDAA0004u: return proc_maps_handle(fd, packet);
case 0xBDAA0005u: return proc_install_handle(fd, packet);
case 0xBDAA0006u: return proc_call_handle(fd, packet);
case 0xBDAA0007u: return proc_elf_handle(fd, packet);
case 0xBDAA0008u: return proc_protect_handle(fd, packet);
case 0xBDAA0009u: return proc_scan_handle(fd, packet);
case 0xBDAA000Au: return proc_info_handle(fd, packet);
case 0xBDAA000Bu: return proc_alloc_handle(fd, packet);
case 0xBDAA000Cu: return proc_free_handle(fd, packet);
case 0xBDAA000Du: return proc_unknown_d_handle(fd, packet);
case 0xBDAA000Eu: return proc_alloc_hinted_handle(fd, packet);
case 0xBDAA0010u: return proc_elf_rpc_handle(fd, packet);
case 0xBDAA0020u: return proc_disasm_region_handle(fd, packet);
case 0xBDAA0021u: return proc_extract_code_xrefs_handle(fd, packet);
case 0xBDAA0022u: return proc_find_xrefs_to_handle(fd, packet);
case 0xBDAA0501u: return proc_scan_aob_handle(fd, packet);
case 0xBDAA0502u: return proc_scan_aob_multi_handle(fd, packet);
case 0xBDAACCFFu: return proc_auth_handle(fd, packet);
case 0xBDAACC01u: return proc_scan_start_handle(fd, packet);
case 0xBDAACC02u: return proc_scan_count_handle(fd, packet);
case 0xBDAACC03u: return proc_scan_get_handle(fd, packet);
}
net_send_int32(fd, CMD_ERROR);
return 0;
}