270 lines
8.4 KiB
C
270 lines
8.4 KiB
C
/* Copyright (C) 2024-2025 anonymous
|
|
|
|
This file is part of PSFree.
|
|
|
|
PSFree is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
PSFree 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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
// 8.50, 8.52
|
|
|
|
#include "types.h"
|
|
#include "utils.h"
|
|
|
|
struct kexec_args {
|
|
u64 entry;
|
|
u64 arg1;
|
|
u64 arg2;
|
|
u64 arg3;
|
|
u64 arg4;
|
|
u64 arg5;
|
|
};
|
|
|
|
static inline void restore(void *kbase, struct kexec_args *uap);
|
|
static inline void patch_aio(void *kbase);
|
|
static inline void do_patch(void *kbase);
|
|
|
|
__attribute__((section (".text.start")))
|
|
int kpatch(void *td, struct kexec_args *uap) {
|
|
const u64 xfast_syscall_off = 0x1c0;
|
|
void * const kbase = (void *)rdmsr(0xc0000082) - xfast_syscall_off;
|
|
|
|
do_patch(kbase);
|
|
patch_aio(kbase);
|
|
restore(kbase, uap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
__attribute__((always_inline))
|
|
static inline void restore(void *kbase, struct kexec_args *uap) {
|
|
u8 *pipe = uap->arg1;
|
|
u8 *pipebuf = uap->arg2;
|
|
for (int i = 0; i < 0x18; i++) {
|
|
pipe[i] = pipebuf[i];
|
|
}
|
|
u64 *pktinfo_field = uap->arg3;
|
|
*pktinfo_field = 0;
|
|
u64 *pktinfo_field2 = uap->arg4;
|
|
*pktinfo_field2 = 0;
|
|
|
|
u64 *sysent_661_save = uap->arg5;
|
|
for (int i = 0; i < 0x30; i += 8) {
|
|
write64(kbase, 0x11041b0 + i, sysent_661_save[i / 8]);
|
|
}
|
|
}
|
|
|
|
__attribute__((always_inline))
|
|
static inline void patch_aio(void *kbase) {
|
|
disable_cr0_wp();
|
|
|
|
const u64 aio_off = 0x30221;
|
|
|
|
// patch = {0xeb, 0x48}
|
|
write16(kbase, aio_off, 0x48eb);
|
|
|
|
// offset = 0x42
|
|
// patch = {0xeb, 0x06}
|
|
write16(kbase, aio_off + 0x42, 0x06eb);
|
|
|
|
// offset = 0x4a
|
|
// patch = {0x41, 0x83, 0xbf, 0xa0, 0x04, 0x00, 0x00, 0x00}
|
|
write64(kbase, aio_off + 0x4a, 0x00000004a0bf8341);
|
|
|
|
// offset = 0x58
|
|
// patch = {0x49, 0x8b, 0x87, 0xd0, 0x04, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0x58, 0xd0878b49);
|
|
write16(kbase, aio_off + 0x5c, 0x0004);
|
|
write8(kbase, aio_off + 0x5e, 0x00);
|
|
|
|
// offset = 0x65
|
|
// patch = {0x49, 0x8b, 0xb7, 0xb0, 0x04, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0x65, 0xb0b78b49);
|
|
write16(kbase, aio_off + 0x69, 0x0004);
|
|
write8(kbase, aio_off + 0x6b, 0x00);
|
|
|
|
// offset = 0x7d
|
|
// patch = {0x49, 0x8b, 0x87, 0x40, 0x05, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0x7d, 0x40878b49);
|
|
write16(kbase, aio_off + 0x81, 0x0005);
|
|
write8(kbase, aio_off + 0x83, 0x00);
|
|
|
|
// offset = 0x8a
|
|
// patch = {0x49, 0x8b, 0xb7, 0x20, 0x05, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0x8a, 0x20b78b49);
|
|
write16(kbase, aio_off + 0x8e, 0x0005);
|
|
write8(kbase, aio_off + 0x90, 0x00);
|
|
|
|
// offset = 0xa2
|
|
// patch = {0x49, 0x8d, 0xbf, 0xc0, 0x00, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0xa2, 0xc0bf8d49);
|
|
write16(kbase, aio_off + 0xa6, 0x0000);
|
|
write8(kbase, aio_off + 0xa8, 0x00);
|
|
|
|
// offset = 0xae
|
|
// patch = {0x49, 0x8d, 0xbf, 0xe0, 0x00, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0xae, 0xe0bf8d49);
|
|
write16(kbase, aio_off + 0xb2, 0x0000);
|
|
write8(kbase, aio_off + 0xb4, 0x00);
|
|
|
|
// offset = 0xc1
|
|
// patch = {0x49, 0x8d, 0xbf, 0x00, 0x01, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0xc1, 0x00bf8d49);
|
|
write16(kbase, aio_off + 0xc5, 0x0001);
|
|
write8(kbase, aio_off + 0xc7, 0x00);
|
|
|
|
// offset = 0xcd
|
|
// patch = {0x49, 0x8d, 0xbf, 0x20, 0x01, 0x00, 0x00}
|
|
write32(kbase, aio_off + 0xcd, 0x20bf8d49);
|
|
write16(kbase, aio_off + 0xd1, 0x0001);
|
|
write8(kbase, aio_off + 0xd3, 0x00);
|
|
|
|
// offset = 0xde
|
|
// patch = {0x49, 0x8b, 0xff}
|
|
write16(kbase, aio_off + 0xde, 0x8b49);
|
|
write8(kbase, aio_off + 0xe0, 0xff);
|
|
|
|
enable_cr0_wp();
|
|
}
|
|
|
|
__attribute__((always_inline))
|
|
static inline void do_patch(void *kbase) {
|
|
disable_cr0_wp();
|
|
|
|
// ChendoChap's patches from pOOBs4
|
|
write16(kbase, 0x624674, 0x00eb); // veriPatch
|
|
write8(kbase, 0xacd, 0xeb); // bcopy
|
|
write8(kbase, 0x3a403d, 0xeb); // bzero
|
|
write8(kbase, 0x3a4081, 0xeb); // pagezero
|
|
write8(kbase, 0x3a40fd, 0xeb); // memcpy
|
|
write8(kbase, 0x3a4141, 0xeb); // pagecopy
|
|
write8(kbase, 0x3a42ed, 0xeb); // copyin
|
|
write8(kbase, 0x3a479d, 0xeb); // copyinstr
|
|
write8(kbase, 0x3a486d, 0xeb); // copystr
|
|
|
|
// stop sysVeri from causing a delayed panic on suspend
|
|
write16(kbase, 0x624f5f, 0x00eb);
|
|
|
|
// patch amd64_syscall() to allow calling syscalls everywhere
|
|
// struct syscall_args sa; // initialized already
|
|
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
|
// int is_invalid_syscall = 0
|
|
//
|
|
// // check the calling code if it looks like one of the syscall stubs at a
|
|
// // libkernel library and check if the syscall number correponds to the
|
|
// // proper stub
|
|
// if ((code & 0xff0000000000ffff) != 0x890000000000c0c7
|
|
// || sa.code != (u32)(code >> 0x10)
|
|
// ) {
|
|
// // patch this to " = 0" instead
|
|
// is_invalid_syscall = -1;
|
|
// }
|
|
write32(kbase, 0x490, 0);
|
|
// these code corresponds to the check that ensures that the caller's
|
|
// instruction pointer is inside the libkernel library's memory range
|
|
//
|
|
// // patch the check to always go to the "goto do_syscall;" line
|
|
// void *code = td->td_frame->tf_rip;
|
|
// if (libkernel->start <= code && code < libkernel->end
|
|
// && is_invalid_syscall == 0
|
|
// ) {
|
|
// goto do_syscall;
|
|
// }
|
|
//
|
|
// do_syscall:
|
|
// ...
|
|
// lea rsi, [rbp - 0x78]
|
|
// mov rdi, rbx
|
|
// mov rax, qword [rbp - 0x80]
|
|
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
|
//
|
|
// sy_call() is the function that will execute the requested syscall.
|
|
write8(kbase, 0x4c2, 0xeb);
|
|
write16(kbase, 0x4b9, 0x00eb);
|
|
write16(kbase, 0x4b5, 0x00eb);
|
|
|
|
// patch sys_setuid() to allow freely changing the effective user ID
|
|
// ; PRIV_CRED_SETUID = 50
|
|
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
|
// test eax, eax
|
|
// je ... ; patch je to jmp
|
|
write8(kbase, 0x22f3d6, 0xeb);
|
|
|
|
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
|
//
|
|
// this check is skipped after the patch
|
|
//
|
|
// if ((new_prot & current->max_protection) != new_prot) {
|
|
// vm_map_unlock(map);
|
|
// return (KERN_PROTECTION_FAILURE);
|
|
// }
|
|
write16(kbase, 0x14d6db, 0x04eb);
|
|
|
|
// TODO: Description of this patch. patch sys_dynlib_load_prx()
|
|
write16(kbase, 0x17474, 0xe990);
|
|
|
|
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
|
// call ...
|
|
// mov r14, qword [rbp - 0xad0]
|
|
// cmp eax, 0x4000000
|
|
// jb ... ; patch jb to jmp
|
|
write8(kbase, 0x17c2f, 0xeb);
|
|
// patch called function to always return 0
|
|
//
|
|
// sys_dynlib_dlsym:
|
|
// ...
|
|
// mov edi, 0x10 ; 16
|
|
// call patched_function ; kernel_base + 0x951c0
|
|
// test eax, eax
|
|
// je ...
|
|
// mov rax, qword [rbp - 0xad8]
|
|
// ...
|
|
// patched_function: ; patch to "xor eax, eax; ret"
|
|
// push rbp
|
|
// mov rbp, rsp
|
|
// ...
|
|
write32(kbase, 0x3ad040, 0xc3c03148);
|
|
|
|
// patch sys_mmap() to allow rwx mappings
|
|
// patch maximum cpu mem protection: 0x33 -> 0x37
|
|
// the ps4 added custom protections for their gpu memory accesses
|
|
// GPU X: 0x8 R: 0x10 W: 0x20
|
|
// that's why you see other bits set
|
|
// ref: https://cturt.github.io/ps4-2.html
|
|
write8(kbase, 0x826ea, 0x37);
|
|
write8(kbase, 0x826ed, 0x37);
|
|
|
|
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
|
//
|
|
// struct args {
|
|
// u64 rdi;
|
|
// u64 rsi;
|
|
// u64 rdx;
|
|
// u64 rcx;
|
|
// u64 r8;
|
|
// u64 r9;
|
|
// };
|
|
//
|
|
// int sys_kexec(struct thread td, struct args *uap) {
|
|
// asm("jmp qword ptr [rsi]");
|
|
// }
|
|
const u64 sysent_11_off = 0x10fc7d0;
|
|
// .sy_narg = 2
|
|
write32(kbase, sysent_11_off, 2);
|
|
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
|
write64(kbase, sysent_11_off + 8, kbase + 0xc810d);
|
|
// .sy_thrcnt = SY_THR_STATIC
|
|
write32(kbase, sysent_11_off + 0x2c, 1);
|
|
|
|
enable_cr0_wp();
|
|
}
|