Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions payloads/sources/ps4-kpatches-src/500.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/* 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/>. */

// 5.00, 5.01

#include "types.h"
#include "utils.h"

static inline void do_patch(void *kbase);

__attribute__((section (".text.start")))
int kpatch(void *td) {
const u64 xfast_syscall_off = 0x1c0;
void * const kbase = (void *)rdmsr(0xc0000082) - xfast_syscall_off;

do_patch(kbase);

return 0;
}

__attribute__((always_inline))
static inline void do_patch(void *kbase) {
disable_cr0_wp();

// ChendoChap's patches from pOOBs4
write8(kbase, 0xabd, 0xeb); // bcopy
write8(kbase, 0x1ea36d, 0xeb); // bzero
write8(kbase, 0x1ea3b1, 0xeb); // pagezero
write8(kbase, 0x1ea42d, 0xeb); // memcpy
write8(kbase, 0x1ea471, 0xeb); // pagecopy
write8(kbase, 0x1ea60d, 0xeb); // copyin
write8(kbase, 0x1eaa3d, 0xeb); // copyinstr
write8(kbase, 0x1eaafd, 0xeb); // copystr

// 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, 0x493, 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, 0x4c5, 0xeb);
write16(kbase, 0x4bc, 0x00eb);
write16(kbase, 0x4b8, 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, 0x54a7d, 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, 0x1a3af8, 0x04eb);

// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
// call ...
// mov r14, qword [rbp - 0xad0]
// cmp eax, 0x4000000
// jb ... ; patch jb to jmp
write16(kbase, 0x237e2a, 0xe990);
// 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, 0x2b2350, 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, 0x13d510, 0x37);
write8(kbase, 0x13d513, 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 = 0x107c820;
// .sy_narg = 2
write32(kbase, sysent_11_off, 2);
// .sy_call = gadgets['jmp qword ptr [rsi]']
write64(kbase, sysent_11_off + 8, kbase + 0x13460);
// .sy_thrcnt = SY_THR_STATIC
write32(kbase, sysent_11_off + 0x2c, 1);

enable_cr0_wp();
}
158 changes: 158 additions & 0 deletions payloads/sources/ps4-kpatches-src/503.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/* 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/>. */

// 5.03, 5.05, 5.07

#include "types.h"
#include "utils.h"

static inline void do_patch(void *kbase);

__attribute__((section (".text.start")))
int kpatch(void *td) {
const u64 xfast_syscall_off = 0x1c0;
void * const kbase = (void *)rdmsr(0xc0000082) - xfast_syscall_off;

do_patch(kbase);

return 0;
}

__attribute__((always_inline))
static inline void do_patch(void *kbase) {
disable_cr0_wp();

// ChendoChap's patches from pOOBs4
write8(kbase, 0xabd, 0xeb); // bcopy
write8(kbase, 0x1ea47d, 0xeb); // bzero
write8(kbase, 0x1ea4c1, 0xeb); // pagezero
write8(kbase, 0x1ea53d, 0xeb); // memcpy
write8(kbase, 0x1ea581, 0xeb); // pagecopy
write8(kbase, 0x1ea71d, 0xeb); // copyin
write8(kbase, 0x1eab4d, 0xeb); // copyinstr
write8(kbase, 0x1eac0d, 0xeb); // copystr

// 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, 0x493, 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, 0x4c5, 0xeb);
write16(kbase, 0x4bc, 0x00eb);
write16(kbase, 0x4b8, 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, 0x54a7d, 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, 0x1a3c08, 0x04eb);

// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
// call ...
// mov r14, qword [rbp - 0xad0]
// cmp eax, 0x4000000
// jb ... ; patch jb to jmp
write16(kbase, 0x237f3a, 0xe990);
// 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, 0x2b2620, 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, 0x13d620, 0x37);
write8(kbase, 0x13d623, 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 = 0x107c820;
// .sy_narg = 2
write32(kbase, sysent_11_off, 2);
// .sy_call = gadgets['jmp qword ptr [rsi]']
write64(kbase, sysent_11_off + 8, kbase + 0x13460);
// .sy_thrcnt = SY_THR_STATIC
write32(kbase, sysent_11_off + 0x2c, 1);

enable_cr0_wp();
}
Loading