From d0cdc83e8a1f07ae85e7c6cdd28f0d4d724a2fee Mon Sep 17 00:00:00 2001 From: Sergey Lisov Date: Mon, 21 Aug 2023 01:16:40 +0300 Subject: [PATCH] ps5-kstuff: add "run in foreign process" kekcall Also adds support for arbitrary mprotect & mdbg_call without creds. --- prosper0gdb/r0gdb.c | 25 ++++++++++++ prosper0gdb/r0gdb.h | 3 ++ ps5-kstuff/Makefile | 2 +- ps5-kstuff/Makefile.freebsd | 2 +- ps5-kstuff/kekcalls.txt | 1 + ps5-kstuff/main.c | 8 ++++ ps5-kstuff/structs-ps5.inc | 3 ++ ps5-kstuff/uelf/kekcall.c | 70 +++++++++++++++++++++++++++++----- ps5-kstuff/uelf/main.c | 76 ++++++++++++++++++++++--------------- ps5-kstuff/uelf/utils.c | 6 +-- ps5-kstuff/uelf/utils.h | 1 + 11 files changed, 151 insertions(+), 46 deletions(-) diff --git a/prosper0gdb/r0gdb.c b/prosper0gdb/r0gdb.c index 9ca129a..0713e6a 100644 --- a/prosper0gdb/r0gdb.c +++ b/prosper0gdb/r0gdb.c @@ -696,6 +696,7 @@ void* mmap20(void* addr, size_t sz, int prot, int flags, int fd, off_t offset) static uint64_t sys_write; static uint64_t sys_sigaction; +static uint64_t sys_mdbg_call; static void fix_sigaction_17_9(uint64_t* regs) { @@ -763,6 +764,30 @@ int get_self_auth_info_20(const char* path, void* buf) return 0; } +static void fix_mdbg_call(uint64_t* regs) +{ + SKIP_SCHEDULER + if(regs[0] == sys_write) + regs[0] = sys_mdbg_call; + else if(regs[0] == kdata_base - 0x631ea9) + regs[5] = 1; +} + +int mdbg_call_20(void* a, void* b, void* c) +{ + r0gdb_instrument(0); + int(*p_write)(void*, void*, void*) = dlsym((void*)0x2001, "_write"); + trace_prog = fix_mdbg_call; + if(!sys_write) + kmemcpy(&sys_write, (void*)(kdata_base + 0x1709c0 + 48*SYS_write + 8), 8); + if(!sys_mdbg_call) + kmemcpy(&sys_mdbg_call, (void*)(kdata_base + 0x1709c0 + 48*573 + 8), 8); + set_trace(); + int ans = p_write(a, b, c); + trace_prog = 0; + return ans; +} + static uint64_t fncall_fn = 0; static uint64_t fncall_args[6]; static uint64_t fncall_ans = 0; diff --git a/prosper0gdb/r0gdb.h b/prosper0gdb/r0gdb.h index 83ffa1b..78483dc 100644 --- a/prosper0gdb/r0gdb.h +++ b/prosper0gdb/r0gdb.h @@ -70,6 +70,9 @@ int sigaction20(int sig, const struct sigaction* neww, struct sigaction* oldd); //get_self_auth_info that works int get_self_auth_info_20(const char* path, void* buf); +//mdbg_call that works +int mdbg_call_20(void* a, void* b, void* c); + //kernel function call uint64_t r0gdb_kfncall(uint64_t kfn, ...); diff --git a/ps5-kstuff/Makefile b/ps5-kstuff/Makefile index 066990b..0d1fd4c 100644 --- a/ps5-kstuff/Makefile +++ b/ps5-kstuff/Makefile @@ -33,4 +33,4 @@ uelf/crt.o: uelf/crt.asm yasm uelf/crt.asm -f elf64 -o uelf/crt.o uelf/uelf: uelf/*.c uelf/*.h structs.h uelf/crt.o - gcc -Wl,-Bsymbolic -O0 -g -isystem ../freebsd-headers -nostdinc -nostdlib -mgeneral-regs-only -fno-stack-protector -fPIE -fPIC -shared -ffreestanding -fno-builtin uelf/crt.o uelf/*.c -o uelf/uelf + gcc -Wl,-Bsymbolic -Wl,-gc-sections -ffunction-sections -fdata-sections -O0 -g -isystem ../freebsd-headers -nostdinc -nostdlib -mgeneral-regs-only -fno-stack-protector -fPIE -fPIC -shared -ffreestanding -fno-builtin uelf/crt.o uelf/*.c -o uelf/uelf diff --git a/ps5-kstuff/Makefile.freebsd b/ps5-kstuff/Makefile.freebsd index 8a1e133..9dd26c1 100644 --- a/ps5-kstuff/Makefile.freebsd +++ b/ps5-kstuff/Makefile.freebsd @@ -19,7 +19,7 @@ uelf/crt.o: uelf/crt.asm yasm uelf/crt.asm -f elf64 -o uelf/crt.o uelf/uelf: uelf/*.c uelf/*.h structs.h uelf/crt.o - gcc -Wl,-zmax-page-size=4096 -Wl,-Bsymbolic -O0 -g -DFREEBSD -std=gnu11 -nostdlib -fno-stack-protector -fPIE -fPIC -shared -ffreestanding -fno-builtin uelf/crt.o uelf/*.c -o uelf/uelf + gcc -Wl,-zmax-page-size=4096 -Wl,-Bsymbolic -Wl,-gc-sections -ffunction-sections -fdata-sections -O0 -g -DFREEBSD -std=gnu11 -nostdlib -fno-stack-protector -fPIE -fPIC -shared -ffreestanding -fno-builtin uelf/crt.o uelf/*.c -o uelf/uelf freebsd-loader: uelf/uelf kelf freebsd-loader.c gcc -O0 -g -std=gnu11 freebsd-loader.c -mno-red-zone -o freebsd-loader diff --git a/ps5-kstuff/kekcalls.txt b/ps5-kstuff/kekcalls.txt index 65600f2..8c2da1b 100644 --- a/ps5-kstuff/kekcalls.txt +++ b/ps5-kstuff/kekcalls.txt @@ -7,3 +7,4 @@ Calls: * rax = 0x100000027, rdi = X -> copy dr0-dr3 and dr6-dr7 to user address X * rax = 0x200000027, rdi = X -> copy dr0-dr3 and dr6-dr7 from user address X * rax = 0x300000027, rdi = X -> returns rdmsr(X) +* rax = 0x500000027, rdi = pid, rsi = syscno, rdx = X -> call syscall in foreign process, with args read from user address X diff --git a/ps5-kstuff/main.c b/ps5-kstuff/main.c index 4f26c44..c0634e2 100644 --- a/ps5-kstuff/main.c +++ b/ps5-kstuff/main.c @@ -322,9 +322,12 @@ int main(void* ds, int a, int b, uintptr_t c, uintptr_t d) "loadSelfSegment_epilogue"+zero, "loadSelfSegment_watchpoint"+zero, "loadSelfSegment_watchpoint_lr"+zero, + "mdbg_call_fix"+zero, "mini_syscore_header"+zero, "mov_cr3_rax"+zero, "mov_rdi_cr3"+zero, + "mprotect_fix_start"+zero, + "mprotect_fix_end"+zero, "nop_ret"+zero, ".pcpu"+zero, "pop_all_except_rdi_iret"+zero, @@ -375,9 +378,12 @@ int main(void* ds, int a, int b, uintptr_t c, uintptr_t d) kdata_base - 0x8a54cd, // loadSelfSegment_epilogue kdata_base - 0x2cc918, // loadSelfSegment_watchpoint kdata_base - 0x8a5727, // loadSelfSegment_watchpoint_lr + kdata_base - 0x631ea9, // mdbg_call_fix kdata_base + 0xdc16e8, // mini_syscore_header kdata_base - 0x396f9e, // mov_cr3_rax kdata_base - 0x39700e, // mov_rdi_cr3 + kdata_base - 0x90ac61, // mprotect_fix_start + kdata_base - 0x90ac5b, // mprotect_fix_end kdata_base - 0x28a3a0, // nop_ret 0x1234, // .pcpu kdata_base - 0x9cf8a7, // pop_all_except_rdi_iret @@ -455,6 +461,7 @@ int main(void* ds, int a, int b, uintptr_t c, uintptr_t d) kelf_bases[cpu] = (uint64_t)kelf; kelf_entries[cpu] = (uint64_t)entry; } + r0gdb_wrmsr(0xc0000084, r0gdb_rdmsr(0xc0000084) | 0x100); gdb_remote_syscall("write", 3, 0, (uintptr_t)1, (uintptr_t)"done loading\npatching idt... ", (uintptr_t)29); uint64_t cr3 = r0gdb_read_cr3(); for(int cpu = 0; cpu < NCPUS; cpu++) @@ -482,6 +489,7 @@ int main(void* ds, int a, int b, uintptr_t c, uintptr_t d) struct sigaction sa; sigaction(SIGBUS, 0, &sa); sigaction(SIGTRAP, &sa, 0); + copyin(IDT+16*179+5, "\x89", 1); asm volatile("ud2"); return 0; } diff --git a/ps5-kstuff/structs-ps5.inc b/ps5-kstuff/structs-ps5.inc index 74a2193..53ecb9c 100644 --- a/ps5-kstuff/structs-ps5.inc +++ b/ps5-kstuff/structs-ps5.inc @@ -19,9 +19,12 @@ %define iret_rsp (iret_rip+24) %define iret_ss (iret_rip+32) +%define td_proc 8 %define td_pcb 0x3f8 %define td_retval 0x408 +%define p_pid 0xbc + %define pcb_gsbase 0x48 %define pcb_flags 0x100 diff --git a/ps5-kstuff/uelf/kekcall.c b/ps5-kstuff/uelf/kekcall.c index fd6e7d8..4efe7b5 100644 --- a/ps5-kstuff/uelf/kekcall.c +++ b/ps5-kstuff/uelf/kekcall.c @@ -1,20 +1,24 @@ #include +#include +#include #include "kekcall.h" #include "traps.h" #include "utils.h" extern char syscall_after[]; extern char doreti_iret[]; +extern char nop_ret[]; extern char copyout[]; extern char copyin[]; +extern struct sysent sysents[]; int handle_kekcall(uint64_t* regs, uint64_t* args, uint32_t nr) { if(nr == 1) { - uint64_t stack_frame[13] = { + uint64_t stack_frame[12] = { (uint64_t)doreti_iret, - (uint64_t)syscall_after, regs[CS], regs[EFLAGS], regs[RSP], regs[SS], + (uint64_t)nop_ret, regs[CS], regs[EFLAGS], regs[RSP], regs[SS], }; read_dbgregs(stack_frame+6); if(!get_pcb_dbregs()) @@ -28,24 +32,33 @@ int handle_kekcall(uint64_t* regs, uint64_t* args, uint32_t nr) regs[RSI] = args[RDI]; regs[RDX] = 48; regs[RIP] = (uint64_t)copyout; - return ENOSYS; } else if(nr == 2) { - uint64_t stack_frame[13] = {(uint64_t)doreti_iret, MKTRAP(TRAP_KEKCALL, 1), [12] = regs[RDI]}; + uint64_t stack_frame[14] = {(uint64_t)doreti_iret, MKTRAP(TRAP_KEKCALL, 1), [12] = regs[RDI]}; push_stack(regs, stack_frame, sizeof(stack_frame)); regs[RDI] = args[RDI]; regs[RSI] = regs[RSP] + 48; regs[RDX] = 48; regs[RIP] = (uint64_t)copyin; - return ENOSYS; } else if(nr == 3) { return rdmsr(args[RDI], &args[RAX]) ? 0 : EFAULT; } - else if(nr == 0x42) - regs[CS] |= 1; + //nr 4 reserved for wrmsr + else if(nr == 5) + { + uint64_t stack_frame[16] = {(uint64_t)doreti_iret, MKTRAP(TRAP_KEKCALL, 2)}; + stack_frame[6] = args[RDI]; + stack_frame[7] = args[RSI]; + stack_frame[14] = regs[RDI]; + push_stack(regs, stack_frame, sizeof(stack_frame)); + regs[RDI] = args[RDX]; + regs[RSI] = regs[RSP] + 64; + regs[RDX] = 48; + regs[RIP] = (uint64_t)copyin; + } else if(nr == 0xffffffff) { args[RAX] = 0; @@ -58,13 +71,52 @@ void handle_kekcall_trap(uint64_t* regs, uint32_t trap) { if(trap == 1) { - uint64_t stack_frame[12]; + uint64_t stack_frame[14]; pop_stack(regs, stack_frame, sizeof(stack_frame)); - regs[RIP] = (uint64_t)syscall_after; + regs[RIP] = stack_frame[13]; if((uint32_t)regs[RAX]) return; kpoke64(stack_frame[11]+td_retval, 0); set_pcb_dbregs(); write_dbgregs(stack_frame+5); } + else if(trap == 2) + { + uint64_t stack_frame[15]; + pop_stack(regs, stack_frame, sizeof(stack_frame)); + if((uint32_t)regs[RAX]) + { + pop_stack(regs, regs+RIP, 8); + return; + } + uint32_t pid = stack_frame[5]; + uint32_t sysc_no = stack_frame[6]; + int64_t proc = kpeek64(stack_frame[13]+td_proc); + while(proc < -0x100000000) + proc = kpeek64(proc+8); + while(proc && (uint32_t)kpeek64(proc+p_pid) != pid) + proc = kpeek64(proc); + if(!proc) + { + regs[RAX] = ESRCH; + pop_stack(regs, regs+RIP, 8); + return; + } + regs[RDI] = kpeek64(proc+16); + uint64_t stack_frame_2[14] = {(uint64_t)doreti_iret, MKTRAP(TRAP_KEKCALL, 3), [6] = stack_frame[13], regs[RDI]}; + memcpy(stack_frame_2+8, stack_frame+7, 48); + push_stack(regs, stack_frame_2, sizeof(stack_frame_2)); + regs[RAX] = (uint64_t)&sysents[sysc_no]; + regs[RIP] = kpeek64((uint64_t)&sysents[sysc_no].sy_call); + regs[RSI] = regs[RSP] + 64; + handle_syscall(regs, 0); + } + else if(trap == 3) + { + uint64_t stack_frame[14]; + pop_stack(regs, stack_frame, sizeof(stack_frame)); + if(!(uint32_t)regs[RAX]) + kpoke64(stack_frame[5]+td_retval, kpeek64(stack_frame[6]+td_retval)); + regs[RIP] = stack_frame[13]; + } } diff --git a/ps5-kstuff/uelf/main.c b/ps5-kstuff/uelf/main.c index d931945..a14d436 100644 --- a/ps5-kstuff/uelf/main.c +++ b/ps5-kstuff/uelf/main.c @@ -11,6 +11,7 @@ #include "traps.h" #include "kekcall.h" #include "fself.h" +#include "syscall_fixes.h" int have_error_code; @@ -25,6 +26,44 @@ extern char int1_handler[]; extern char int13_handler[]; extern uint64_t wrmsr_args; +void handle_syscall(uint64_t* regs, int allow_kekcall) +{ +#define IS_PPR(which) (regs[RAX] == (uint64_t)&sysents[SYS_##which]) +#ifdef FREEBSD +#define IS_PS4(which) 0 +#else +#define IS_PS4(which) (regs[RAX] == (uint64_t)&sysents2[SYS_##which]) +#endif +#define IS(which) (IS_PPR(which) || IS_PS4(which)) + if(IS_PPR(getppid) && allow_kekcall) + { + uint64_t args[NREGS] = {0}; + copy_from_kernel(args, regs[RSP]+syscall_rsp_to_regs_stash+8, sizeof(args)); + int err = handle_kekcall(regs, args, args[RAX]>>32); + if(err != ENOSYS) + { + if(!err) + kpoke64(regs[RDI]+td_retval, args[RAX]); + regs[RAX] = err; + pop_stack(regs, regs+RIP, 8); + } + } +#ifndef FREEBSD + else if(IS(execve) + || IS(dynlib_load_prx) + || IS(get_self_auth_info) + || IS(get_sdk_compiled_version) + || IS_PPR(get_ppr_sdk_compiled_version)) + handle_fself_syscall(regs); + else if(IS(mprotect) + || IS_PPR(mdbg_call)) + handle_syscall_fix(regs); +#endif +#undef IS +#undef IS_PS4 +#undef IS_PPR +} + void handle(uint64_t* regs) { if(!(regs[CS] & 3)) @@ -76,37 +115,10 @@ from_userspace: else if(regs[RIP] == (uint64_t)syscall_before) { regs[RAX] |= 0xffffull << 48; -#define IS_PPR(which) (regs[RAX] == (uint64_t)&sysents[SYS_##which]) -#ifdef FREEBSD -#define IS_PS4(which) 0 -#else -#define IS_PS4(which) (regs[RAX] == (uint64_t)&sysents2[SYS_##which]) -#endif -#define IS(which) (IS_PPR(which) || IS_PS4(which)) - if(IS_PPR(getppid)) - { - uint64_t args[NREGS] = {0}; - copy_from_kernel(args, regs[RSP]+syscall_rsp_to_regs_stash, sizeof(args)); - int err = handle_kekcall(regs, args, args[RAX]>>32); - if(err != ENOSYS) - { - if(!err) - kpoke64(regs[RDI]+td_retval, args[RAX]); - regs[RAX] = err; - regs[RIP] = (uint64_t)syscall_after; - } - } -#ifndef FREEBSD - else if(IS(execve) - || IS(dynlib_load_prx) - || IS(get_self_auth_info) - || IS(get_sdk_compiled_version) - || IS_PPR(get_ppr_sdk_compiled_version)) - handle_fself_syscall(regs); -#endif -#undef IS -#undef IS_PS4 -#undef IS_PPR + regs[RSI] = regs[RSP] + syscall_rsp_to_rsi; + push_stack(regs, (const uint64_t[1]){(uint64_t)syscall_after}, 8); + regs[RIP] = kpeek64(regs[RAX]+8); + handle_syscall(regs, 1); } else if(regs[RIP] == (uint64_t)doreti_iret) { @@ -134,6 +146,8 @@ from_userspace: return; else if(handle_fself_parasites(regs)) return; + else if(try_handle_syscall_fix_trap(regs)) + return; #endif else { diff --git a/ps5-kstuff/uelf/utils.c b/ps5-kstuff/uelf/utils.c index faa43d1..b9c790a 100644 --- a/ps5-kstuff/uelf/utils.c +++ b/ps5-kstuff/uelf/utils.c @@ -151,8 +151,7 @@ int wrmsr(uint32_t which, uint64_t value) void start_syscall_with_dbgregs(uint64_t* regs, const uint64_t* dbgregs) { - regs[RSI] = regs[RSP] + syscall_rsp_to_rsi; - uint64_t stack_frame[13] = { + uint64_t stack_frame[12] = { (uint64_t)doreti_iret, MKTRAP(TRAP_UTILS, 1), 0, 0, 0, 0, }; @@ -160,7 +159,6 @@ void start_syscall_with_dbgregs(uint64_t* regs, const uint64_t* dbgregs) push_stack(regs, stack_frame, sizeof(stack_frame)); set_pcb_dbregs(); write_dbgregs(dbgregs); - regs[RIP] = kpeek64(regs[RAX] + 8); } void handle_utils_trap(uint64_t* regs, uint32_t trapno) @@ -170,6 +168,6 @@ void handle_utils_trap(uint64_t* regs, uint32_t trapno) uint64_t stack_frame[12]; pop_stack(regs, stack_frame, sizeof(stack_frame)); write_dbgregs(stack_frame+5); - regs[RIP] = (uint64_t)syscall_after; + regs[RIP] = stack_frame[11]; } } diff --git a/ps5-kstuff/uelf/utils.h b/ps5-kstuff/uelf/utils.h index 78c2bb8..b54d8ea 100644 --- a/ps5-kstuff/uelf/utils.h +++ b/ps5-kstuff/uelf/utils.h @@ -17,6 +17,7 @@ void read_dbgregs(uint64_t* dr); void write_dbgregs(const uint64_t* dr); void start_syscall_with_dbgregs(uint64_t* regs, const uint64_t* dbgregs); void handle_utils_trap(uint64_t* regs, uint32_t trapno); +void handle_syscall(uint64_t* regs, int allow_kekcall); int rdmsr(uint32_t which, uint64_t* ans); int wrmsr(uint32_t which, uint64_t value);