/* Copyright (C) 2024 John Törnblom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "pt.h" static int sys_ptrace(int request, pid_t pid, caddr_t addr, int data) { uint8_t privcaps[16] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; pid_t mypid = getpid(); uint8_t caps[16]; uint64_t authid; int ret; if(!(authid=kernel_get_ucred_authid(mypid))) { return -1; } if(kernel_get_ucred_caps(mypid, caps)) { return -1; } if(kernel_set_ucred_authid(mypid, 0x4800000000010003l)) { return -1; } if(kernel_set_ucred_caps(mypid, privcaps)) { return -1; } ret = (int)syscall(SYS_ptrace, request, pid, addr, data); if(kernel_set_ucred_authid(mypid, authid)) { return -1; } if(kernel_set_ucred_caps(mypid, caps)) { return -1; } return ret; } intptr_t pt_resolve(pid_t pid, const char* nid) { intptr_t addr; if((addr=kernel_dynlib_resolve(pid, 0x1, nid))) { return addr; } return kernel_dynlib_resolve(pid, 0x2001, nid); } int pt_trace_me(void) { return sys_ptrace(PT_TRACE_ME, 0, 0, 0); } int pt_attach(pid_t pid) { if(sys_ptrace(PT_ATTACH, pid, 0, 0) == -1) { return -1; } if(waitpid(pid, 0, 0) == -1) { return -1; } return 0; } int pt_detach(pid_t pid, int sig) { if(sys_ptrace(PT_DETACH, pid, 0, sig) == -1) { return -1; } return 0; } int pt_follow_fork(pid_t pid) { if(sys_ptrace(PT_FOLLOW_FORK, pid, NULL, 1) == -1) { return -1; } if(sys_ptrace(PT_LWP_EVENTS, pid, NULL, 1) == -1) { return -1; } return 0; } int pt_follow_exec(pid_t pid) { if(sys_ptrace(PT_LWP_EVENTS, pid, NULL, 1) == -1) { return -1; } return 0; } pid_t pt_await_child(pid_t pid) { struct ptrace_lwpinfo lwpinfo; memset(&lwpinfo, 0, sizeof(lwpinfo)); while(!(lwpinfo.pl_flags & PL_FLAG_FORKED)) { if(waitpid(pid, NULL, 0) == -1) { return -1; } if(sys_ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) == -1) { return -1; } } if(waitpid(lwpinfo.pl_child_pid, NULL, 0) == -1) { return -1; } return lwpinfo.pl_child_pid; } int pt_await_exec(pid_t pid) { struct ptrace_lwpinfo lwpinfo; memset(&lwpinfo, 0, sizeof(lwpinfo)); while(!(lwpinfo.pl_flags & PL_FLAG_EXEC)) { if(waitpid(pid, NULL, 0) == -1) { return -1; } if(sys_ptrace(PT_LWPINFO, pid, (caddr_t)&lwpinfo, sizeof(lwpinfo)) == -1) { return -1; } } return 0; } int pt_step(int pid) { if(sys_ptrace(PT_STEP, pid, (caddr_t)1, 0)) { return -1; } if(waitpid(pid, 0, 0) < 0) { return -1; } return 0; } int pt_continue(pid_t pid, int sig) { if(sys_ptrace(PT_CONTINUE, pid, (caddr_t)1, sig) == -1) { return -1; } return 0; } int pt_getint(pid_t pid, intptr_t addr) { return sys_ptrace(PT_READ_D, pid, (caddr_t)addr, 0); } int pt_getregs(pid_t pid, struct reg *r) { return sys_ptrace(PT_GETREGS, pid, (caddr_t)r, 0); } int pt_setregs(pid_t pid, const struct reg *r) { return sys_ptrace(PT_SETREGS, pid, (caddr_t)r, 0); } static long pt_call(pid_t pid, intptr_t addr, ...) { struct reg jmp_reg; struct reg bak_reg; va_list ap; if(pt_getregs(pid, &bak_reg)) { return -1; } memcpy(&jmp_reg, &bak_reg, sizeof(jmp_reg)); jmp_reg.r_rip = addr; va_start(ap, addr); jmp_reg.r_rdi = va_arg(ap, int64_t); jmp_reg.r_rsi = va_arg(ap, int64_t); jmp_reg.r_rdx = va_arg(ap, int64_t); jmp_reg.r_rcx = va_arg(ap, int64_t); jmp_reg.r_r8 = va_arg(ap, int64_t); jmp_reg.r_r9 = va_arg(ap, int64_t); va_end(ap); if(pt_setregs(pid, &jmp_reg)) { return -1; } // single step until the function returns while(jmp_reg.r_rsp <= bak_reg.r_rsp) { if(pt_step(pid)) { return -1; } if(pt_getregs(pid, &jmp_reg)) { return -1; } } // restore registers if(pt_setregs(pid, &bak_reg)) { return -1; } return jmp_reg.r_rax; } long pt_syscall(pid_t pid, int sysno, ...) { intptr_t addr = pt_resolve(pid, "HoLVWNanBBc"); struct reg jmp_reg; struct reg bak_reg; va_list ap; if(!addr) { return -1; } else { addr += 0xa; } if(pt_getregs(pid, &bak_reg)) { return -1; } memcpy(&jmp_reg, &bak_reg, sizeof(jmp_reg)); jmp_reg.r_rip = addr; jmp_reg.r_rax = sysno; va_start(ap, sysno); jmp_reg.r_rdi = va_arg(ap, int64_t); jmp_reg.r_rsi = va_arg(ap, int64_t); jmp_reg.r_rdx = va_arg(ap, int64_t); jmp_reg.r_r10 = va_arg(ap, int64_t); jmp_reg.r_r8 = va_arg(ap, int64_t); jmp_reg.r_r9 = va_arg(ap, int64_t); va_end(ap); if(pt_setregs(pid, &jmp_reg)) { return -1; } // single step until the function returns while(jmp_reg.r_rsp <= bak_reg.r_rsp) { if(pt_step(pid)) { return -1; } if(pt_getregs(pid, &jmp_reg)) { return -1; } } // restore registers if(pt_setregs(pid, &bak_reg)) { return -1; } return jmp_reg.r_rax; } int pt_jitshm_create(pid_t pid, intptr_t name, size_t size, int flags) { return (int)pt_syscall(pid, 0x215, name, size, flags); } int pt_jitshm_alias(pid_t pid, int fd, int flags) { return (int)pt_syscall(pid, 0x216, fd, flags); } intptr_t pt_mmap(pid_t pid, intptr_t addr, size_t len, int prot, int flags, int fd, off_t off) { return pt_syscall(pid, SYS_mmap, addr, len, prot, flags, fd, off); } int pt_msync(pid_t pid, intptr_t addr, size_t len, int flags) { return (int)pt_syscall(pid, SYS_msync, addr, len, flags); } int pt_munmap(pid_t pid, intptr_t addr, size_t len) { return (int)pt_syscall(pid, SYS_munmap, addr, len); } int pt_mprotect(pid_t pid, intptr_t addr, size_t len, int prot) { return (int)pt_syscall(pid, SYS_mprotect, addr, len, prot); } int pt_socket(pid_t pid, int domain, int type, int protocol) { return (int)pt_syscall(pid, SYS_socket, domain, type, protocol); } int pt_setsockopt(pid_t pid, int fd, int level, int optname, intptr_t optval, socklen_t optlen) { return (int)pt_syscall(pid, SYS_setsockopt, fd, level, optname, optval, optlen, 0); } int pt_bind(pid_t pid, int sockfd, intptr_t addr, socklen_t addrlen) { return (int)pt_syscall(pid, SYS_bind, sockfd, addr, addrlen); } ssize_t pt_recvmsg(pid_t pid, int fd, intptr_t msg, int flags) { return (int)pt_syscall(pid, SYS_recvmsg, fd, msg, flags); } int pt_close(pid_t pid, int fd) { return (int)pt_syscall(pid, SYS_close, fd); } int pt_dup2(pid_t pid, int oldfd, int newfd) { return (int)pt_syscall(pid, SYS_dup2, oldfd, newfd); } int pt_rdup(pid_t pid, pid_t other_pid, int fd) { return (int)pt_syscall(pid, 0x25b, other_pid, fd); } int pt_pipe(pid_t pid, intptr_t pipefd) { intptr_t faddr = pt_resolve(pid, "-Jp7F+pXxNg"); return (int)pt_call(pid, faddr, pipefd); } void pt_perror(pid_t pid, const char *s) { intptr_t faddr = pt_resolve(pid, "9BcDykPmo1I"); intptr_t addr = pt_call(pid, faddr); int err = pt_getint(pid, addr); char buf[255]; strcpy(buf, s); strcat(buf, ": "); strcat(buf, strerror(err)); puts(buf); } intptr_t pt_sceKernelGetProcParam(pid_t pid) { intptr_t faddr = pt_resolve(pid, "959qrazPIrg"); return pt_call(pid, faddr); } intptr_t pt_getargv(pid_t pid) { intptr_t faddr = pt_resolve(pid, "FJmglmTMdr4"); return pt_call(pid, faddr); }