8 #include <psp2kern/types.h> 9 #include <psp2kern/kernel/cpu.h> 10 #include <psp2kern/kernel/sysmem.h> 11 #include <psp2kern/kernel/threadmgr.h> 14 #include "taihen_internal.h" 18 #include "substitute/lib/substitute.h" 30 #define PATCHES_POOL_SIZE 0x10000 33 #define NUM_PROC_MAP_BUCKETS 16 36 #define FUNC_TO_UINTPTR_T(x) (((uintptr_t)(x))&0xFFFFFFFE) 39 #define MEM_SHARED_START ((void*)0xE0000000) 48 static SceUID g_hooks_lock;
51 static SceClass g_taihen_class;
60 static int init_patch(
void *dat) {
64 LOG(
"init of: %p", patch);
75 static int free_patch(
void *dat) {
79 LOG(
"cleanup of: %p", patch);
91 SceKernelMemPoolCreateOpt opt;
94 memset(&opt, 0,
sizeof(opt));
95 opt.size =
sizeof(opt);
97 g_patch_pool = sceKernelMemPoolCreate(
"tai_patches", PATCHES_POOL_SIZE, &opt);
98 LOG(
"sceKernelMemPoolCreate(tai_patches): 0x%08X", g_patch_pool);
99 if (g_patch_pool < 0) {
104 LOG(
"Failed to create proc map.");
105 return TAI_ERROR_SYSTEM;
107 g_hooks_lock = sceKernelCreateMutexForKernel(
"tai_hooks_lock", SCE_KERNEL_MUTEX_ATTR_RECURSIVE, 0, NULL);
108 LOG(
"sceKernelCreateMutexForKernel(tai_hooks_lock): 0x%08X", g_hooks_lock);
109 if (g_hooks_lock < 0) {
112 ret = sceKernelCreateClass(&g_taihen_class,
"taiHENClass", sceKernelGetUidClass(),
sizeof(
tai_patch_t), init_patch, free_patch);
113 LOG(
"sceKernelCreateClass(taiHENClass): 0x%08X", ret);
126 LOG(
"Cleaning up patches subsystem.");
128 sceKernelDeleteMutexForKernel(g_hooks_lock);
129 sceKernelMemPoolDestroy(g_patch_pool);
143 static inline void hex_dump(uintptr_t paddr,
const char *addr,
unsigned int size)
146 for (i = 0; i < (size >> 4); i++)
148 LOG(
"0x%08X: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
150 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8],
151 addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15]
178 vma_align = vma & ~0x1F;
179 len = ((vma + len + 0x1F) & ~0x1F) - vma_align;
180 LOG(
"cache flush: vma %p, vma_align %p, len %x", vma, vma_align, len);
183 sceKernelCpuDcacheFlush((
void *)vma_align, len);
184 sceKernelCpuIcacheAndL2Flush((
void *)vma_align, len);
187 flags = sceKernelCpuDisableInterrupts();
188 sceKernelCpuSaveContext(my_context);
189 ret = sceKernelGetPidContext(pid, &other_context);
191 sceKernelCpuRestoreContext(other_context);
192 asm volatile (
"mrc p15, 0, %0, c3, c0, 0" :
"=r" (dacr));
193 asm volatile (
"mcr p15, 0, %0, c3, c0, 0" ::
"r" (0x15450FC3));
194 sceKernelCpuDcacheFlush((
void *)vma_align, len);
195 sceKernelCpuIcacheAndL2Flush((
void *)vma_align, len);
196 hex_dump(vma_align, (
char *)vma_align, len);
197 asm volatile (
"mcr p15, 0, %0, c3, c0, 0" ::
"r" (dacr));
199 sceKernelCpuRestoreContext(my_context);
200 sceKernelCpuEnableInterrupts(flags);
201 LOG(
"sceKernelSwitchVmaForPid(%d): 0x%08X\n", pid, ret);
203 asm volatile (
"isb" :::
"memory");
212 struct substitute_function_hook *hook;
213 struct substitute_function_hook_record **saved;
229 static int do_hooking(
void *args) {
238 ret = substitute_hook_functions(uargs->hook, 1, uargs->saved, SUBSTITUTE_RELAXED);
240 flags = sceKernelCpuDisableInterrupts();
241 sceKernelCpuSaveContext(my_context);
242 ret = sceKernelGetPidContext(uargs->pid, &other_context);
244 sceKernelCpuRestoreContext(other_context);
245 asm volatile (
"mrc p15, 0, %0, c3, c0, 0" :
"=r" (dacr));
246 asm volatile (
"mcr p15, 0, %0, c3, c0, 0" ::
"r" (0x15450FC3));
247 ret = substitute_hook_functions(uargs->hook, 1, uargs->saved, SUBSTITUTE_RELAXED);
248 asm volatile (
"mcr p15, 0, %0, c3, c0, 0" ::
"r" (dacr));
250 sceKernelCpuRestoreContext(my_context);
251 sceKernelCpuEnableInterrupts(flags);
265 static int do_unhooking(
void *saved) {
266 return substitute_free_hooks((
struct substitute_function_hook_record *)saved, 1);
280 static int tai_hook_function(
struct slab_chain *slab,
void *target_func,
const void *src_func,
void **old,
void **saved) {
282 struct substitute_function_hook hook;
285 if (target_func == src_func) {
286 LOG(
"no hook needed");
290 hook.function = target_func;
291 hook.replacement = (
void *)src_func;
295 LOG(
"Calling substitute_hook_functions");
297 uargs.pid = slab->pid;
299 uargs.saved = (
struct substitute_function_hook_record **)saved;
300 ret = sceKernelRunWithStack(0x4000, do_hooking, &uargs);
302 if (ret != SUBSTITUTE_OK) {
303 LOG(
"libsubstitute error: %s", substitute_strerror(ret));
304 return TAI_ERROR_HOOK_ERROR;
316 static int tai_unhook_function(
void *saved) {
318 LOG(
"Calling substitute_free_hooks");
319 ret = sceKernelRunWithStack(0x4000, do_unhooking, saved);
320 if (ret != SUBSTITUTE_OK) {
321 LOG(
"libsubstitute error: %s", substitute_strerror(ret));
322 return TAI_ERROR_HOOK_ERROR;
341 static int tai_force_memcpy(SceUID dst_pid,
void *dst,
const void *src,
size_t size) {
344 ret = sceKernelCpuUnrestrictedMemcpy(dst, src, size);
345 LOG(
"sceKernelCpuUnrestrictedMemcpy(%p, %p, 0x%08X): 0x%08X", dst, src, size, ret);
347 ret = sceKernelRxMemcpyKernelToUserForPid(dst_pid, (uintptr_t)dst, src, size);
348 LOG(
"sceKernelRxMemcpyKernelToUserForPid(%x, %p, %p, 0x%08X): 0x%08X", dst_pid, dst, src, size, ret);
367 memcpy(dst, src, size);
368 LOG(
"memcpy(%p, %p, 0x%08X)", dst, src, size);
370 ret = sceKernelMemcpyUserToKernelForPid(src_pid, dst, (uintptr_t)src, size);
371 LOG(
"sceKernelMemcpyUserToKernelForPid(%x, %p, %p, 0x%08X): 0x%08X", src_pid, dst, src, size, ret);
392 LOG(
"Adding hook %p to chain %p", item, hooks);
393 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
394 if (hooks->
head == NULL) {
399 item->
u.next = (uintptr_t)NULL;
400 item->
u.old = hooks->
old;
403 LOG(
"Hook failed, do not add to chain");
408 item->
u.next = head->
u.next;
409 item->
u.old = hooks->
old;
411 head->
u.next = slab_getmirror(item->
patch->
slab, item);
412 LOG(
"Added hook to existing chain %p", head);
418 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
439 uintptr_t tmp, *cur_user;
442 LOG(
"Removing hook %p for %p", item, hooks);
443 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
444 if (hooks->
head == item) {
446 tai_unhook_function(hooks->
saved);
450 if (hooks->
head != NULL) {
454 for (cur = &hooks->
head; *cur != NULL; cur = &(*cur)->
next) {
455 (*cur)->u.old = hooks->
old;
470 *cur_user = item->
u.next;
477 cur_user = &(*cur)->
u.next;
484 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
499 SceCreateUidObjOpt opt;
506 LOG(
"Hooking %p to %p for pid %x", hook_func, dest_func, pid);
507 if (hook_func >= MEM_SHARED_START) {
509 return TAI_ERROR_INVALID_KERNEL_ADDR;
511 return TAI_ERROR_NOT_IMPLEMENTED;
517 ret = sceKernelCreateUidObj(&g_taihen_class,
"tai_patch_hook", NULL, (SceObjectBase **)&patch);
519 memset(&opt, 0,
sizeof(opt));
522 ret = sceKernelCreateUidObj(&g_taihen_class,
"tai_patch_hook_user", &opt, (SceObjectBase **)&patch);
524 LOG(
"sceKernelCreateUidObj(tai_patch_hook): 0x%08X, %p", ret, patch);
529 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
533 patch->
addr = FUNC_TO_UINTPTR_T(dest_func);
534 patch->
size = FUNC_SAVE_SIZE;
540 ret = sceKernelDeleteUid(patch->
uid);
541 LOG(
"sceKernelDeleteUid(old): 0x%08X", ret);
542 if (tmp == NULL || tmp->
type != HOOKS) {
544 LOG(
"this hook overlaps an existing hook");
545 ret = TAI_ERROR_PATCH_EXISTS;
549 LOG(
"found existing patch %p, discarding %p", tmp, patch);
554 hook = slab_alloc(patch->
slab, &exe_addr);
559 hook->
u.func = (
void *)hook_func;
562 ret = hooks_add_hook(&patch->data.
hooks, hook);
563 if (ret < 0 && patch->data.hooks.head == NULL) {
564 LOG(
"failed to add hook and patch is now empty, freeing hook %p", hook);
565 slab_free(patch->
slab, hook);
568 sceKernelDeleteUid(patch->
uid);
570 }
else if (ret >= 0) {
572 *p_hook = slab_getmirror(patch->
slab, hook);
577 if (ret < 0 && patch && hook) {
578 LOG(
"freeing hook %p", hook);
579 slab_free(patch->
slab, hook);
582 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
601 ret = sceKernelGetObjForUid(uid, &g_taihen_class, (SceObjectBase **)&patch);
602 LOG(
"sceKernelGetObjForUid(%x): 0x%08X", uid, ret);
606 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
608 for (cur = &patch->data.
hooks.
head; *cur != NULL; cur = &(*cur)->
next) {
609 if (slab_getmirror(slab, *cur) == hook_ref) {
611 LOG(
"Found hook %p for ref %p", hook, hook_ref);
612 ret = hooks_remove_hook(&patch->data.
hooks, hook);
615 slab_free(slab, hook);
617 LOG(
"patch is now empty, freeing it");
619 sceKernelDeleteUid(patch->
uid);
625 LOG(
"Cannot find hook for uid %x ref %p", uid, hook_ref);
626 ret = TAI_ERROR_NOT_FOUND;
628 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
653 LOG(
"Injecting %p with %p for size 0x%08X at pid %x", dest, src, size, pid);
654 ret = sceKernelCreateUidObj(&g_taihen_class,
"tai_patch_inject", NULL, (SceObjectBase **)&patch);
655 LOG(
"sceKernelCreateUidObj(tai_patch_inject): 0x%08X, %p", ret, patch);
660 saved = sceKernelMemPoolAlloc(g_patch_pool, size);
661 LOG(
"sceKernelMemPoolAlloc(g_patch_pool, 0x%08X): %p", size, saved);
663 sceKernelDeleteUid(ret);
664 return TAI_ERROR_MEMORY;
669 LOG(
"Invalid address for memcpy");
670 sceKernelDeleteUid(ret);
671 sceKernelMemPoolFree(g_patch_pool, saved);
672 return TAI_ERROR_INVALID_ARGS;
675 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
676 patch->
type = INJECTION;
679 patch->
addr = (uintptr_t)dest;
686 ret = TAI_ERROR_PATCH_EXISTS;
688 ret = tai_force_memcpy(pid, dest, src, size);
692 sceKernelDeleteUid(patch->
uid);
693 sceKernelMemPoolFree(g_patch_pool, saved);
698 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
719 ret = sceKernelGetObjForUid(uid, &g_taihen_class, (SceObjectBase **)&patch);
720 LOG(
"sceKernelGetObjForUid(%x): 0x%08X", uid, ret);
724 if (patch->
type != INJECTION || patch->
uid != uid) {
725 LOG(
"internal error: trying to free an invalid injection");
726 return TAI_ERROR_SYSTEM;
728 inject = &patch->data.
inject;
729 LOG(
"Releasing injection %p for patch %p", inject, patch);
730 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
732 dest = (
void *)patch->
addr;
733 saved = inject->
saved;
736 LOG(
"internal error, cannot remove patch from proc_map");
737 ret = TAI_ERROR_SYSTEM;
739 ret = tai_force_memcpy(pid, dest, saved, size);
740 sceKernelMemPoolFree(g_patch_pool, saved);
741 sceKernelDeleteUid(patch->
uid);
743 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
767 LOG(
"Calling patches cleanup for pid %x", pid);
768 sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
770 while (patch != NULL) {
772 if (patch->
type == INJECTION) {
773 LOG(
"freeing injection saved data");
774 sceKernelMemPoolFree(g_patch_pool, patch->data.
inject.
saved);
775 }
else if (patch->
type == HOOKS) {
776 LOG(
"freeing hook saved data");
779 LOG(
"deleting patch: %x", patch->
uid);
780 sceKernelDeleteUid(patch->
uid);
784 sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
void * saved
The original data (allocated on inject)
tai_patch_type_t type
Type of patch (hook chain or injection)
tai_proc_map_t * proc_map_alloc(int nbuckets)
Allocates a new map.
struct _tai_patch * patch
The patch containing this injection.
struct _tai_hook_user u
Used by TAI_CONTINUE to find next hook to run.
The actual map in memory.
size_t size
Size of the patch.
struct _tai_patch * next
Next patch in the linked list for this process.
uintptr_t tai_hook_ref_t
Hook information.
int tai_hook_release(SceUID uid, tai_hook_ref_t hook_ref)
Removes a hook and restores original function if chain is empty.
struct _tai_inject inject
Inject data.
struct _tai_hook_list hooks
Hook chain data.
uintptr_t addr
Address being patched.
struct _tai_hook * next
Next hook for this process + address.
int proc_map_remove(tai_proc_map_t *map, tai_patch_t *patch)
Remove a single patch from the map.
void proc_map_free(tai_proc_map_t *map)
Frees a map.
A patch containing either a hook chain or an injection.
int patches_init(void)
Initializes the patch system.
SceUID tai_inject_abs(SceUID pid, void *dest, const void *src, size_t size)
Inserts a raw data injection given an absolute address and PID of the address space.
struct _tai_patch * patch
The patch containing this hook.
struct slab_chain * slab
Slab chain for this process (copied from the owner tai_proc_t)
size_t size
Size of original data.
int tai_try_cleanup_process(SceUID pid)
Called on process exist to force remove private hooks.
void * func
Address of the function to hook.
int proc_map_remove_all_pid(tai_proc_map_t *map, SceUID pid, tai_patch_t **head)
Removes every patch associated with a given pid from the map.
SceUID pid
Process owning this object.
Hook data stored in address space of process to patch.
void * saved
Data saved by libsubstitute to restore the function.
SceUID tai_hook_func_abs(tai_hook_ref_t *p_hook, SceUID pid, void *dest_func, const void *hook_func)
Inserts a hook given an absolute address and PID of the function.
int tai_inject_release(SceUID uid)
Removes an injection and restores the original data.
void * old
A function pointer used to call the original function.
SceUID uid
Kernel object id of this object.
struct _tai_hook * head
The linked list of hooks on this process + address.
int proc_map_try_insert(tai_proc_map_t *map, tai_patch_t *patch, tai_patch_t **existing)
Inserts into the map if no overlap or get patch that completely overlaps.
int tai_memcpy_to_kernel(SceUID src_pid, void *dst, const char *src, size_t size)
Memcpy from a process to kernel.
void cache_flush(SceUID pid, uintptr_t vma, size_t len)
Flush L1 and L2 cache for an address.
void patches_deinit(void)
Cleans up the patch system.