taiHEN  1.0
CFW framework for PS Vita
patches.c
1 /* patches.c -- main patch system
2  *
3  * Copyright (C) 2016 Yifan Lu
4  *
5  * This software may be modified and distributed under the terms
6  * of the MIT license. See the LICENSE file for details.
7  */
8 #include <psp2kern/types.h>
9 #include <psp2kern/kernel/cpu.h>
10 #include <psp2kern/kernel/sysmem.h>
11 #include <psp2kern/kernel/threadmgr.h>
12 #include <string.h>
13 #include "error.h"
14 #include "taihen_internal.h"
15 #include "patches.h"
16 #include "proc_map.h"
17 #include "slab.h"
18 #include "substitute/lib/substitute.h"
19 
30 #define PATCHES_POOL_SIZE 0x10000
31 
33 #define NUM_PROC_MAP_BUCKETS 16
34 
36 #define FUNC_TO_UINTPTR_T(x) (((uintptr_t)(x))&0xFFFFFFFE)
37 
39 #define MEM_SHARED_START ((void*)0xE0000000)
40 
42 SceUID g_patch_pool;
43 
45 static tai_proc_map_t *g_map;
46 
48 static SceUID g_hooks_lock;
49 
51 static SceClass g_taihen_class;
52 
60 static int init_patch(void *dat) {
61  tai_patch_t *patch;
62 
63  patch = (tai_patch_t *)dat;
64  LOG("init of: %p", patch);
65  return 0;
66 }
67 
75 static int free_patch(void *dat) {
76  tai_patch_t *patch;
77 
78  patch = (tai_patch_t *)dat;
79  LOG("cleanup of: %p", patch);
80  return 0;
81 }
82 
90 int patches_init(void) {
91  SceKernelMemPoolCreateOpt opt;
92  int ret;
93 
94  memset(&opt, 0, sizeof(opt));
95  opt.size = sizeof(opt);
96  opt.uselock = 1;
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) {
100  return g_patch_pool;
101  }
102  g_map = proc_map_alloc(NUM_PROC_MAP_BUCKETS);
103  if (g_map == NULL) {
104  LOG("Failed to create proc map.");
105  return TAI_ERROR_SYSTEM;
106  }
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) {
110  return g_hooks_lock;
111  }
112  ret = sceKernelCreateClass(&g_taihen_class, "taiHENClass", sceKernelGetUidClass(), sizeof(tai_patch_t), init_patch, free_patch);
113  LOG("sceKernelCreateClass(taiHENClass): 0x%08X", ret);
114  if (ret < 0) {
115  return ret;
116  }
117  return TAI_SUCCESS;
118 }
119 
125 void patches_deinit(void) {
126  LOG("Cleaning up patches subsystem.");
127  // TODO: Find out how to clean up class
128  sceKernelDeleteMutexForKernel(g_hooks_lock);
129  sceKernelMemPoolDestroy(g_patch_pool);
130  proc_map_free(g_map);
131  g_map = NULL;
132  g_patch_pool = 0;
133  g_hooks_lock = 0;
134 }
135 
143 static inline void hex_dump(uintptr_t paddr, const char *addr, unsigned int size)
144 {
145  unsigned int i;
146  for (i = 0; i < (size >> 4); i++)
147  {
148  LOG("0x%08X: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n",
149  paddr,
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]
152  );
153  paddr += 0x10;
154  addr += 0x10;
155  }
156 }
157 
158 #ifdef __arm__
159 
170 void cache_flush(SceUID pid, uintptr_t vma, size_t len) {
171  uintptr_t vma_align;
172  int flags;
173  int my_context[3];
174  int ret;
175  int *other_context;
176  int dacr;
177 
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);
181 
182  if (pid == KERNEL_PID) {
183  sceKernelCpuDcacheFlush((void *)vma_align, len);
184  sceKernelCpuIcacheAndL2Flush((void *)vma_align, len);
185  } else {
186  // TODO: Take care of SHARED_PID
187  flags = sceKernelCpuDisableInterrupts();
188  sceKernelCpuSaveContext(my_context);
189  ret = sceKernelGetPidContext(pid, &other_context);
190  if (ret >= 0) {
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));
198  }
199  sceKernelCpuRestoreContext(my_context);
200  sceKernelCpuEnableInterrupts(flags);
201  LOG("sceKernelSwitchVmaForPid(%d): 0x%08X\n", pid, ret);
202  }
203  asm volatile ("isb" ::: "memory");
204 }
205 #endif
206 
210 struct hook_args {
211  SceUID pid;
212  struct substitute_function_hook *hook;
213  struct substitute_function_hook_record **saved;
214 };
215 
229 static int do_hooking(void *args) {
230  int flags;
231  int ret;
232  int my_context[3];
233  int *other_context;
234  int dacr;
235  struct hook_args *uargs = (struct hook_args *)args;
236 
237  if (uargs->pid == KERNEL_PID) {
238  ret = substitute_hook_functions(uargs->hook, 1, uargs->saved, SUBSTITUTE_RELAXED);
239  } else {
240  flags = sceKernelCpuDisableInterrupts();
241  sceKernelCpuSaveContext(my_context);
242  ret = sceKernelGetPidContext(uargs->pid, &other_context);
243  if (ret >= 0) {
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));
249  }
250  sceKernelCpuRestoreContext(my_context);
251  sceKernelCpuEnableInterrupts(flags);
252  }
253  return ret;
254 }
255 
265 static int do_unhooking(void *saved) {
266  return substitute_free_hooks((struct substitute_function_hook_record *)saved, 1);
267 }
268 
280 static int tai_hook_function(struct slab_chain *slab, void *target_func, const void *src_func, void **old, void **saved) {
281  struct hook_args uargs;
282  struct substitute_function_hook hook;
283  int ret;
284 
285  if (target_func == src_func) {
286  LOG("no hook needed");
287  return TAI_SUCCESS; // no need for hook
288  }
289 
290  hook.function = target_func;
291  hook.replacement = (void *)src_func;
292  hook.old_ptr = old;
293  hook.options = 0;
294  hook.opt = slab;
295  LOG("Calling substitute_hook_functions");
296  // TODO: Take care of SHARED_PID
297  uargs.pid = slab->pid;
298  uargs.hook = &hook;
299  uargs.saved = (struct substitute_function_hook_record **)saved;
300  ret = sceKernelRunWithStack(0x4000, do_hooking, &uargs);
301  LOG("Done hooking");
302  if (ret != SUBSTITUTE_OK) {
303  LOG("libsubstitute error: %s", substitute_strerror(ret));
304  return TAI_ERROR_HOOK_ERROR;
305  }
306  return TAI_SUCCESS;
307 }
308 
316 static int tai_unhook_function(void *saved) {
317  int ret;
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;
323  }
324  return TAI_SUCCESS;
325 }
326 
341 static int tai_force_memcpy(SceUID dst_pid, void *dst, const void *src, size_t size) {
342  int ret;
343  if (dst_pid == KERNEL_PID) {
344  ret = sceKernelCpuUnrestrictedMemcpy(dst, src, size);
345  LOG("sceKernelCpuUnrestrictedMemcpy(%p, %p, 0x%08X): 0x%08X", dst, src, size, ret);
346  } else {
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);
349  }
350  cache_flush(dst_pid, (uintptr_t)dst, size);
351  return ret;
352 }
353 
364 int tai_memcpy_to_kernel(SceUID src_pid, void *dst, const char *src, size_t size) {
365  int ret;
366  if (src_pid == KERNEL_PID) {
367  memcpy(dst, src, size);
368  LOG("memcpy(%p, %p, 0x%08X)", dst, src, size);
369  } else {
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);
372  }
373  return 0;
374 }
375 
388 static int hooks_add_hook(tai_hook_list_t *hooks, tai_hook_t *item) {
389  tai_hook_t *head;
390  int ret;
391 
392  LOG("Adding hook %p to chain %p", item, hooks);
393  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
394  if (hooks->head == NULL) { // first hook for this list
395  ret = tai_hook_function(item->patch->slab, hooks->func, item->u.func, &hooks->old, &hooks->saved);
396  if (ret >= 0) {
397  hooks->head = item;
398  item->next = NULL;
399  item->u.next = (uintptr_t)NULL;
400  item->u.old = hooks->old;
401  cache_flush(item->patch->pid, slab_getmirror(item->patch->slab, item), sizeof(tai_hook_t));
402  } else {
403  LOG("Hook failed, do not add to chain");
404  }
405  } else {
406  head = hooks->head;
407  item->next = head->next;
408  item->u.next = head->u.next;
409  item->u.old = hooks->old;
410  head->next = item;
411  head->u.next = slab_getmirror(item->patch->slab, item);
412  LOG("Added hook to existing chain %p", head);
413  // flush cache for head + item, which were modified
414  cache_flush(item->patch->pid, slab_getmirror(item->patch->slab, head), sizeof(tai_hook_t));
415  cache_flush(item->patch->pid, head->u.next, sizeof(tai_hook_t));
416  ret = 1;
417  }
418  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
419 
420  return ret;
421 }
422 
437 static int hooks_remove_hook(tai_hook_list_t *hooks, tai_hook_t *item) {
438  tai_hook_t **cur;
439  uintptr_t tmp, *cur_user;
440  int ret;
441 
442  LOG("Removing hook %p for %p", item, hooks);
443  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
444  if (hooks->head == item) { // first hook for this list
445  // we must remove the patch
446  tai_unhook_function(hooks->saved);
447  hooks->saved = NULL;
448  // set head to the next item
449  hooks->head = item->next;
450  if (hooks->head != NULL) {
451  // add a patch to the new head
452  ret = tai_hook_function(item->patch->slab, hooks->func, hooks->head->u.func, &hooks->old, &hooks->saved);
453  // update the old pointers
454  for (cur = &hooks->head; *cur != NULL; cur = &(*cur)->next) {
455  (*cur)->u.old = hooks->old;
456  }
457  // clear cache of mirror for the last item since it uses the old pointer
458  cache_flush(item->patch->pid, (uintptr_t)cur - offsetof(tai_hook_t, next), sizeof(tai_hook_t));
459  } else {
460  ret = 0;
461  }
462  } else {
463  cur = &hooks->head;
464  cur_user = &tmp;
465  ret = -1;
466  while (1) {
467  if (*cur) {
468  if (*cur == item) {
469  *cur = item->next; // remove from list
470  *cur_user = item->u.next;
471  // clear cache since pointers were changed
472  cache_flush(item->patch->pid, (uintptr_t)cur - offsetof(tai_hook_t, next), sizeof(tai_hook_t));
473  ret = 0;
474  break;
475  } else {
476  cur = &(*cur)->next;
477  cur_user = &(*cur)->u.next;
478  }
479  } else {
480  break;
481  }
482  }
483  }
484  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
485  return ret;
486 }
487 
498 SceUID tai_hook_func_abs(tai_hook_ref_t *p_hook, SceUID pid, void *dest_func, const void *hook_func) {
499  SceCreateUidObjOpt opt;
500  tai_patch_t *patch, *tmp;
501  tai_hook_t *hook;
502  int ret;
503  struct slab_chain *slab;
504  uintptr_t exe_addr;
505 
506  LOG("Hooking %p to %p for pid %x", hook_func, dest_func, pid);
507  if (hook_func >= MEM_SHARED_START) {
508  if (pid == KERNEL_PID) {
509  return TAI_ERROR_INVALID_KERNEL_ADDR; // invalid hook address
510  } else {
511  return TAI_ERROR_NOT_IMPLEMENTED; // TODO: add support for this
512  }
513  }
514 
515  hook = NULL;
516  if (pid == KERNEL_PID) {
517  ret = sceKernelCreateUidObj(&g_taihen_class, "tai_patch_hook", NULL, (SceObjectBase **)&patch);
518  } else {
519  memset(&opt, 0, sizeof(opt));
520  opt.flags = 8;
521  opt.pid = pid;
522  ret = sceKernelCreateUidObj(&g_taihen_class, "tai_patch_hook_user", &opt, (SceObjectBase **)&patch);
523  }
524  LOG("sceKernelCreateUidObj(tai_patch_hook): 0x%08X, %p", ret, patch);
525  if (ret < 0) {
526  return ret;
527  }
528 
529  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
530  patch->type = HOOKS;
531  patch->uid = ret;
532  patch->pid = pid;
533  patch->addr = FUNC_TO_UINTPTR_T(dest_func);
534  patch->size = FUNC_SAVE_SIZE;
535  patch->next = NULL;
536  patch->data.hooks.func = dest_func;
537  patch->data.hooks.saved = NULL;
538  patch->data.hooks.head = NULL;
539  if (proc_map_try_insert(g_map, patch, &tmp) < 1) {
540  ret = sceKernelDeleteUid(patch->uid);
541  LOG("sceKernelDeleteUid(old): 0x%08X", ret);
542  if (tmp == NULL || tmp->type != HOOKS) {
543  // error
544  LOG("this hook overlaps an existing hook");
545  ret = TAI_ERROR_PATCH_EXISTS;
546  goto err;
547  } else {
548  // we have an existing patch
549  LOG("found existing patch %p, discarding %p", tmp, patch);
550  patch = tmp;
551  }
552  }
553 
554  hook = slab_alloc(patch->slab, &exe_addr);
555  if (hook == NULL) {
556  ret = -1;
557  goto err;
558  }
559  hook->u.func = (void *)hook_func;
560  hook->patch = patch;
561 
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);
566  hook = NULL;
567  proc_map_remove(g_map, patch);
568  sceKernelDeleteUid(patch->uid);
569  patch = NULL;
570  } else if (ret >= 0) {
571  ret = patch->uid;
572  *p_hook = slab_getmirror(patch->slab, hook);
573  }
574 
575 err:
576  // error and we have allocated a hook
577  if (ret < 0 && patch && hook) {
578  LOG("freeing hook %p", hook);
579  slab_free(patch->slab, hook);
580  }
581 
582  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
583 
584  return ret;
585 }
586 
595 int tai_hook_release(SceUID uid, tai_hook_ref_t hook_ref) {
596  tai_hook_t **cur, *hook;
597  tai_patch_t *patch;
598  struct slab_chain *slab;
599  int ret;
600 
601  ret = sceKernelGetObjForUid(uid, &g_taihen_class, (SceObjectBase **)&patch);
602  LOG("sceKernelGetObjForUid(%x): 0x%08X", uid, ret);
603  if (ret < 0) {
604  return ret;
605  }
606  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
607  slab = patch->slab;
608  for (cur = &patch->data.hooks.head; *cur != NULL; cur = &(*cur)->next) {
609  if (slab_getmirror(slab, *cur) == hook_ref) {
610  hook = *cur;
611  LOG("Found hook %p for ref %p", hook, hook_ref);
612  ret = hooks_remove_hook(&patch->data.hooks, hook);
613  *cur = hook->next;
614  LOG("freeing hook");
615  slab_free(slab, hook);
616  if (patch->data.hooks.head == NULL) {
617  LOG("patch is now empty, freeing it");
618  proc_map_remove(g_map, patch);
619  sceKernelDeleteUid(patch->uid);
620  }
621  ret = TAI_SUCCESS;
622  goto end;
623  }
624  }
625  LOG("Cannot find hook for uid %x ref %p", uid, hook_ref);
626  ret = TAI_ERROR_NOT_FOUND;
627 end:
628  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
629 
630  return ret;
631 }
632 
646 SceUID tai_inject_abs(SceUID pid, void *dest, const void *src, size_t size) {
647  tai_patch_t *patch, *tmp;
648  void *saved;
649  int ret;
650 
651  // TODO: Check that dest is not inside our slab structure... that could corrupt kernel code
652 
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);
656  if (ret < 0) {
657  return ret;
658  }
659 
660  saved = sceKernelMemPoolAlloc(g_patch_pool, size);
661  LOG("sceKernelMemPoolAlloc(g_patch_pool, 0x%08X): %p", size, saved);
662  if (saved == NULL) {
663  sceKernelDeleteUid(ret);
664  return TAI_ERROR_MEMORY;
665  }
666 
667  // try to save old data
668  if (tai_memcpy_to_kernel(pid, saved, dest, size) < 0) {
669  LOG("Invalid address for memcpy");
670  sceKernelDeleteUid(ret);
671  sceKernelMemPoolFree(g_patch_pool, saved);
672  return TAI_ERROR_INVALID_ARGS;
673  }
674 
675  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
676  patch->type = INJECTION;
677  patch->uid = ret;
678  patch->pid = pid;
679  patch->addr = (uintptr_t)dest;
680  patch->size = size;
681  patch->next = NULL;
682  patch->data.inject.saved = saved;
683  patch->data.inject.size = size;
684  patch->data.inject.patch = patch;
685  if (proc_map_try_insert(g_map, patch, &tmp) < 1) {
686  ret = TAI_ERROR_PATCH_EXISTS;
687  } else {
688  ret = tai_force_memcpy(pid, dest, src, size);
689  }
690 
691  if (ret < 0) {
692  sceKernelDeleteUid(patch->uid);
693  sceKernelMemPoolFree(g_patch_pool, saved);
694  } else {
695  ret = patch->uid;
696  }
697 
698  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
699 
700  return ret;
701 }
702 
710 int tai_inject_release(SceUID uid) {
711  tai_inject_t *inject;
712  tai_patch_t *patch;
713  void *saved;
714  void *dest;
715  size_t size;
716  int ret;
717  SceUID pid;
718 
719  ret = sceKernelGetObjForUid(uid, &g_taihen_class, (SceObjectBase **)&patch);
720  LOG("sceKernelGetObjForUid(%x): 0x%08X", uid, ret);
721  if (ret < 0) {
722  return ret;
723  }
724  if (patch->type != INJECTION || patch->uid != uid) {
725  LOG("internal error: trying to free an invalid injection");
726  return TAI_ERROR_SYSTEM;
727  }
728  inject = &patch->data.inject;
729  LOG("Releasing injection %p for patch %p", inject, patch);
730  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
731  pid = patch->pid;
732  dest = (void *)patch->addr;
733  saved = inject->saved;
734  size = inject->size;
735  if (!proc_map_remove(g_map, patch)) {
736  LOG("internal error, cannot remove patch from proc_map");
737  ret = TAI_ERROR_SYSTEM;
738  } else {
739  ret = tai_force_memcpy(pid, dest, saved, size);
740  sceKernelMemPoolFree(g_patch_pool, saved);
741  sceKernelDeleteUid(patch->uid);
742  }
743  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
744 
745  return ret;
746 }
747 
765 int tai_try_cleanup_process(SceUID pid) {
766  tai_patch_t *patch, *next;
767  LOG("Calling patches cleanup for pid %x", pid);
768  sceKernelLockMutexForKernel(g_hooks_lock, 1, NULL);
769  if (proc_map_remove_all_pid(g_map, pid, &patch) > 0) {
770  while (patch != NULL) {
771  next = patch->next;
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");
777  free(patch->data.hooks.saved);
778  }
779  LOG("deleting patch: %x", patch->uid);
780  sceKernelDeleteUid(patch->uid);
781  patch = next;
782  }
783  }
784  sceKernelUnlockMutexForKernel(g_hooks_lock, 1);
785  return 0;
786 }
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.
Definition: proc_map.c:69
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.
Definition: proc_map.h:23
size_t size
Size of the patch.
Used by do_hooking
Definition: patches.c:210
struct _tai_patch * next
Next patch in the linked list for this process.
uintptr_t tai_hook_ref_t
Hook information.
Definition: taihen.h:215
int tai_hook_release(SceUID uid, tai_hook_ref_t hook_ref)
Removes a hook and restores original function if chain is empty.
Definition: patches.c:595
struct _tai_inject inject
Inject data.
#define KERNEL_PID
Definition: taihen.h:34
A chain of hooks.
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.
Definition: proc_map.c:233
void proc_map_free(tai_proc_map_t *map)
Frees a map.
Definition: proc_map.c:89
A patch containing either a hook chain or an injection.
int patches_init(void)
Initializes the patch system.
Definition: patches.c:90
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.
Definition: patches.c:646
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.
Definition: patches.c:765
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.
Definition: proc_map.c:202
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.
Definition: patches.c:498
int tai_inject_release(SceUID uid)
Removes an injection and restores the original data.
Definition: patches.c:710
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.
Definition: proc_map.c:114
Injection data.
int tai_memcpy_to_kernel(SceUID src_pid, void *dst, const char *src, size_t size)
Memcpy from a process to kernel.
Definition: patches.c:364
void cache_flush(SceUID pid, uintptr_t vma, size_t len)
Flush L1 and L2 cache for an address.
Definition: patches.c:170
void patches_deinit(void)
Cleans up the patch system.
Definition: patches.c:125