taiHEN  1.0
CFW framework for PS Vita
hen.c
1 /* hen.c -- kernel signature patches
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/io/fcntl.h>
10 #include <psp2kern/kernel/modulemgr.h>
11 #include <psp2kern/kernel/sysmem.h>
12 #include <string.h>
13 #include <taihen/parser.h>
14 #include "error.h"
15 #include "hen.h"
16 #include "module.h"
17 #include "patches.h"
18 #include "taihen_internal.h"
19 
21 #define MAX_SEGMENTS 8
22 
24 #define OFFSET_PATCH_ARG 168
25 
26 /*
27  * S/ELF header
28  */
30 typedef uint16_t Elf32_Half;
31 typedef uint32_t Elf32_Word;
32 typedef int32_t Elf32_Sword;
33 typedef void * Elf32_Addr;
34 typedef size_t Elf32_Off;
35 
36 #define EI_NIDENT 16
37 
38 typedef struct {
39  unsigned char e_ident[EI_NIDENT]; /* ident bytes */
40  Elf32_Half e_type; /* file type */
41  Elf32_Half e_machine; /* target machine */
42  Elf32_Word e_version; /* file version */
43  Elf32_Addr e_entry; /* start address */
44  Elf32_Off e_phoff; /* phdr file offset */
45  Elf32_Off e_shoff; /* shdr file offset */
46  Elf32_Word e_flags; /* file flags */
47  Elf32_Half e_ehsize; /* sizeof ehdr */
48  Elf32_Half e_phentsize; /* sizeof phdr */
49  Elf32_Half e_phnum; /* number phdrs */
50  Elf32_Half e_shentsize; /* sizeof shdr */
51  Elf32_Half e_shnum; /* number shdrs */
52  Elf32_Half e_shstrndx; /* shdr string index */
53 } __attribute__((packed)) Elf32_Ehdr;
54 
55 typedef struct {
56  uint32_t magic; /* 53434500 = SCE\0 */
57  uint32_t version; /* header version 3*/
58  uint16_t sdk_type; /* */
59  uint16_t header_type; /* 1 self, 2 unknown, 3 pkg */
60  uint32_t metadata_offset; /* metadata offset */
61  uint64_t header_len; /* self header length */
62  uint64_t elf_filesize; /* ELF file length */
63  uint64_t self_filesize; /* SELF file length */
64  uint64_t unknown; /* UNKNOWN */
65  uint64_t self_offset; /* SELF offset */
66  uint64_t appinfo_offset; /* app info offset */
67  uint64_t elf_offset; /* ELF #1 offset */
68  uint64_t phdr_offset; /* program header offset */
69  uint64_t shdr_offset; /* section header offset */
70  uint64_t section_info_offset; /* section info offset */
71  uint64_t sceversion_offset; /* version offset */
72  uint64_t controlinfo_offset; /* control info offset */
73  uint64_t controlinfo_size; /* control info size */
74  uint64_t padding;
75 } __attribute__((packed)) self_header_t;
76 
77 typedef struct {
78  uint64_t offset;
79  uint64_t size;
80  uint32_t compressed; // 2=compressed
81  uint32_t unknown1;
82  uint32_t encrypted; // 1=encrypted
83  uint32_t unknown2;
84 } __attribute__((packed)) self_section_info_t;
88 static tai_hook_ref_t g_parse_headers_hook;
89 
91 static tai_hook_ref_t g_setup_buffer_hook;
92 
94 static tai_hook_ref_t g_decrypt_buffer_hook;
95 
97 static tai_hook_ref_t g_rif_check_vita_hook;
98 
100 static tai_hook_ref_t g_rif_check_psp_hook;
101 
103 static tai_hook_ref_t g_rif_get_info_hook;
104 
106 static tai_hook_ref_t g_package_check_hook;
107 
109 static tai_hook_ref_t g_package_check_2_hook;
110 
112 static tai_hook_ref_t g_load_user_libs_hook;
113 
115 static tai_hook_ref_t g_nid_poison_hook;
116 
118 static tai_hook_ref_t g_unload_process_hook;
119 
121 static SceUID g_hooks[11];
122 
124 static SceUID g_config_blk;
125 
127 char *g_config = NULL;
128 
130 static int g_is_homebrew;
131 
133 static self_section_info_t g_seg_info[MAX_SEGMENTS];
134 
145 static int parse_headers_patched(int ctx, const void *headers, size_t len, void *args) {
146  self_header_t *self;
147  Elf32_Ehdr *elf;
148  int ret;
149  int num_segs;
150 
151  memset(&g_seg_info, 0, sizeof(g_seg_info));
152  if (len >= sizeof(self_header_t) && len >= sizeof(Elf32_Ehdr)) {
153  self = (self_header_t *)headers;
154  if (self->elf_offset <= len - sizeof(Elf32_Ehdr)) {
155  elf = (Elf32_Ehdr *)(headers + self->elf_offset);
156  num_segs = elf->e_phnum;
157  if (num_segs <= MAX_SEGMENTS &&
158  self->section_info_offset < self->section_info_offset + num_segs * sizeof(self_section_info_t) &&
159  self->section_info_offset + num_segs * sizeof(self_section_info_t) < len
160  ) {
161  memcpy(&g_seg_info, headers + self->section_info_offset, num_segs * sizeof(self_section_info_t));
162  }
163  }
164  }
165  ret = TAI_CONTINUE(int, g_parse_headers_hook, ctx, headers, len, args);
166  if (ctx == 1) { // as of 3.60, only one decrypt context exists
167  if (ret == 0x800f0624 || ret == 0x800f0616 || ret == 0x800f0024 || ((unsigned)ret >= 0x800f0b30 && (unsigned)ret <= 0x800f0b3f)) {
168  g_is_homebrew = 1;
169  // we only do this patch if another hook hasn't modified it already
170  if (*(uint32_t *)(args + OFFSET_PATCH_ARG) == 0) {
171  *(uint32_t *)(args + OFFSET_PATCH_ARG) = 0x20;
172  }
173  ret = 0;
174  } else {
175  g_is_homebrew = 0;
176  }
177  LOG("parse ret %x, decrypt is homebrew? %d", ret, g_is_homebrew);
178  }
179  return ret;
180 }
181 
190 static int setup_buffer_patched(int ctx, int segidx) {
191  int ret;
192 
193  ret = TAI_CONTINUE(int, g_setup_buffer_hook, ctx, segidx);
194  if (ctx == 1 && g_is_homebrew && segidx < MAX_SEGMENTS) {
195  ret = g_seg_info[segidx].compressed;
196  LOG("segidx %d, compression type: %d", segidx, ret);
197  }
198  return ret;
199 }
200 
210 static int decrypt_buffer_patched(int ctx, void *buffer, size_t len) {
211  int ret;
212 
213  ret = TAI_CONTINUE(int, g_decrypt_buffer_hook, ctx, buffer, len);
214  if (ctx == 1 && g_is_homebrew) {
215  LOG("patching decrypt buffer bypass");
216  ret = 0;
217  }
218  return ret;
219 }
220 
233 static int rif_check_vita_patched(int a1, int a2, int a3, int a4, int a5, int a6) {
234  int ret;
235  ret = TAI_CONTINUE(int, g_rif_check_vita_hook, a1, a2, a3, a4, a5, a6);
236  if (ret == 0x80870003) {
237  LOG("patched rif check return: %x => 0", ret);
238  ret = 0;
239  }
240  return ret;
241 }
242 
254 static int rif_check_psp_patched(int a1, int a2, int a3, int a4, int a5) {
255  int ret;
256  ret = TAI_CONTINUE(int, g_rif_check_psp_hook, a1, a2, a3, a4, a5);
257  if (ret == 0x80870003) {
258  LOG("patched rif check return: %x => 0", ret);
259  ret = 0;
260  }
261  return ret;
262 }
263 
282 static int rif_get_info_patched(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12) {
283  int ret;
284  ret = TAI_CONTINUE(int, g_rif_get_info_hook, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
285  if (ret == 0x80870003) {
286  LOG("patched rif check return: %x => 0", ret);
287  ret = 0;
288  }
289  return ret;
290 }
291 
297 static int package_check_patched(void) {
298  int ret;
299  ret = TAI_CONTINUE(int, g_package_check_hook);
300  LOG("patching fpkg enabled: %x => 1", ret);
301  return 1;
302 }
303 
309 static int package_check_2_patched(void) {
310  int ret;
311  ret = TAI_CONTINUE(int, g_package_check_2_hook);
312  LOG("patching fpkg enabled: %x => 1", ret);
313  return 1;
314 }
315 
325 static int load_user_libs_patched(SceUID pid, void *args, int flags) {
326  tai_plugin_load_t param;
327  int ret;
328  SceUID mod;
329  char titleid[32];
330 
331  ret = TAI_CONTINUE(int, g_load_user_libs_hook, pid, args, flags);
332 
333  sceKernelGetProcessTitleIdForKernel(pid, titleid, 32);
334  LOG("title started: %s, pid: %x", titleid, pid);
335 
336  if (g_config) {
337  param.pid = pid;
338  param.flags = 0x8000; // queue for load
339  taihen_config_parse(g_config, titleid, hen_load_plugin, &param);
340  } else {
341  LOG("config not loaded, skipping plugin load");
342  }
343 
344  return ret;
345 }
346 
358 static int nid_poison_patched(uintptr_t dst, int set, size_t len) {
359  // we're breaking the rules here. we don't call TAI_CONTINUE
360  // because we do not want the effect to take place
361  //LOG("patched out nid poison request: %p, %x, len: %x", dst, set, len);
362  return 0;
363 }
364 
372 static int unload_process_patched(SceUID pid) {
373  int ret;
374 
375  LOG("unloading pid:%x", pid);
376  ret = tai_try_cleanup_process(pid);
377  LOG("cleanup: %x", ret);
378  ret = TAI_CONTINUE(int, g_unload_process_hook, pid);
379  LOG("process unloaded: %x", ret);
380 
381  return ret;
382 }
383 
392 int hen_load_config(void) {
393  SceUID fd;
394  SceOff len;
395  int ret;
396  char *config;
397  int rd, total;
398 
399  LOG("opening config %s", TAIHEN_CONFIG_FILE);
400  fd = sceIoOpenForDriver(TAIHEN_CONFIG_FILE, SCE_O_RDONLY, 0);
401  if (fd < 0) {
402  LOG("failed to open config %s", TAIHEN_CONFIG_FILE);
403  return fd;
404  }
405 
406  len = sceIoLseekForDriver(fd, 0, SCE_SEEK_END);
407  if (len < 0) {
408  LOG("failed to seek config");
409  sceIoCloseForDriver(fd);
410  return TAI_ERROR_SYSTEM;
411  }
412 
413  sceIoLseekForDriver(fd, 0, SCE_SEEK_SET);
414 
415  if (!g_config) {
416  LOG("allocating %d bytes for config", (len + 0xfff) & ~0xfff);
417  g_config_blk = sceKernelAllocMemBlockForKernel("tai_config", SCE_KERNEL_MEMBLOCK_TYPE_KERNEL_RW, (len + 0xfff) & ~0xfff, NULL);
418  if (g_config_blk < 0) {
419  LOG("failed to allocate memory: %x", g_config_blk);
420  sceIoCloseForDriver(fd);
421  return g_config_blk;
422  }
423 
424  ret = sceKernelGetMemBlockBaseForKernel(g_config_blk, (void **)&config);
425  if (ret < 0) {
426  LOG("failed to get base for %x: %x", g_config_blk, ret);
427  sceIoCloseForDriver(fd);
428  return ret;
429  }
430  } else {
431  config = g_config;
432  }
433 
434  LOG("reading config to memory");
435  rd = total = 0;
436  while (total < len) {
437  rd = sceIoReadForDriver(fd, config+total, len-total);
438  if (rd < 0) {
439  LOG("failed to read config: rd %x, total %x, len %x", rd, total, len);
440  ret = rd;
441  break;
442  }
443  total += rd;
444  }
445 
446  sceIoCloseForDriver(fd);
447  if (ret < 0) {
448  sceKernelFreeMemBlockForKernel(g_config_blk);
449  return ret;
450  }
451 
452  if ((ret = taihen_config_validate(config)) != 0) {
453  LOG("config parsing failed: %x", ret);
454  sceKernelFreeMemBlockForKernel(g_config_blk);
455  return ret;
456  }
457 
458  g_config = config;
459  return TAI_SUCCESS;
460 }
461 
467 int hen_free_config(void) {
468  if (g_config) {
469  sceKernelFreeMemBlockForKernel(g_config_blk);
470  }
471  return 0;
472 }
473 
482 void hen_load_plugin(const char *path, void *param) {
483  tai_plugin_load_t *load = (tai_plugin_load_t *)param;
484  int ret;
485  int result;
486 
487  if (!g_config) {
488  LOG("no config loaded! skipping plugin load");
489  return;
490  }
491 
492  LOG("pid:%x loading module %s (flags:%x)", load->pid, path, load->flags);
493  if ((load->flags & 0x8000) == 0x8000) {
494  ret = sceKernelLoadModuleForPid(load->pid, path, load->flags, NULL);
495  result = ret;
496  } else {
497  ret = sceKernelLoadStartModuleForPid(load->pid, path, 0, NULL, load->flags, NULL, &result);
498  }
499  LOG("load result: %x", ret);
500 }
501 
507 int hen_add_patches(void) {
508  memset(g_hooks, 0, sizeof(g_hooks));
510  &g_parse_headers_hook,
511  "SceKernelModulemgr",
512  0x7ABF5135, // SceSblAuthMgrForKernel
513  0xF3411881,
514  parse_headers_patched);
515  if (g_hooks[0] < 0) goto fail;
516  LOG("parse_headers_patched added");
518  &g_setup_buffer_hook,
519  "SceKernelModulemgr",
520  0x7ABF5135, // SceSblAuthMgrForKernel
521  0x89CCDA2C,
522  setup_buffer_patched);
523  if (g_hooks[1] < 0) goto fail;
524  LOG("setup_buffer_patched added");
526  &g_decrypt_buffer_hook,
527  "SceKernelModulemgr",
528  0x7ABF5135, // SceSblAuthMgrForKernel
529  0xBC422443,
530  decrypt_buffer_patched);
531  if (g_hooks[2] < 0) goto fail;
532  LOG("decrypt_buffer_patched added");
534  &g_rif_check_vita_hook,
535  "SceNpDrm",
536  0xD84DC44A, // SceNpDrmForDriver
537  0x723322B5,
538  rif_check_vita_patched);
539  if (g_hooks[3] < 0) goto fail;
540  LOG("rif_check_vita added");
542  &g_rif_check_psp_hook,
543  "SceNpDrm",
544  0xD84DC44A, // SceNpDrmForDriver
545  0xDACB71F4,
546  rif_check_psp_patched);
547  if (g_hooks[4] < 0) goto fail;
548  LOG("rif_check_psp added");
550  &g_rif_get_info_hook,
551  "SceNpDrm",
552  0xD84DC44A, // SceNpDrmForDriver
553  0xDB406EAE,
554  rif_get_info_patched);
555  if (g_hooks[5] < 0) goto fail;
556  LOG("rif_get_info added");
558  &g_package_check_hook,
559  "SceNpDrm",
560  0xFD00C69A, // SceSblAIMgrForDriver
561  0xD78B04A2,
562  package_check_patched);
563  LOG("package_check added");
564  if (g_hooks[6] < 0) goto fail;
566  &g_package_check_2_hook,
567  "SceNpDrm",
568  0xFD00C69A, // SceSblAIMgrForDriver
569  0xF4B98F66,
570  package_check_2_patched);
571  LOG("package_check_2 added");
572  if (g_hooks[7] < 0) goto fail;
574  &g_load_user_libs_hook,
575  "SceKernelModulemgr",
576  0xC445FA63, // SceModulemgrForKernel
577  0x3AD26B43,
578  load_user_libs_patched);
579  if (g_hooks[8] < 0) goto fail;
580  LOG("load_user_libs added");
582  &g_nid_poison_hook,
583  "SceKernelModulemgr",
584  0x63A519E5, // SceSysmemForKernel
585  0xECF9435A,
586  nid_poison_patched);
587  if (g_hooks[9] < 0) goto fail;
588  LOG("nid_poison_patched added");
590  &g_unload_process_hook,
591  "SceProcessmgr",
592  0xC445FA63, // SceModulemgrForKernel
593  0x0E33258E,
594  unload_process_patched);
595  if (g_hooks[10] < 0) goto fail;
596  LOG("unload_process_patched added");
597 
598  return TAI_SUCCESS;
599 fail:
600  if (g_hooks[0] >= 0) {
601  taiHookReleaseForKernel(g_hooks[0], g_parse_headers_hook);
602  }
603  if (g_hooks[1] >= 0) {
604  taiHookReleaseForKernel(g_hooks[1], g_setup_buffer_hook);
605  }
606  if (g_hooks[2] >= 0) {
607  taiHookReleaseForKernel(g_hooks[2], g_decrypt_buffer_hook);
608  }
609  if (g_hooks[3] >= 0) {
610  taiHookReleaseForKernel(g_hooks[3], g_rif_check_vita_hook);
611  }
612  if (g_hooks[4] >= 0) {
613  taiHookReleaseForKernel(g_hooks[4], g_rif_check_psp_hook);
614  }
615  if (g_hooks[5] >= 0) {
616  taiHookReleaseForKernel(g_hooks[5], g_rif_get_info_hook);
617  }
618  if (g_hooks[6] >= 0) {
619  taiHookReleaseForKernel(g_hooks[6], g_package_check_hook);
620  }
621  if (g_hooks[7] >= 0) {
622  taiHookReleaseForKernel(g_hooks[7], g_package_check_2_hook);
623  }
624  if (g_hooks[8] >= 0) {
625  taiHookReleaseForKernel(g_hooks[8], g_load_user_libs_hook);
626  }
627  if (g_hooks[9] >= 0) {
628  taiHookReleaseForKernel(g_hooks[9], g_nid_poison_hook);
629  }
630  if (g_hooks[10] >= 0) {
631  taiHookReleaseForKernel(g_hooks[10], g_unload_process_hook);
632  }
633  return TAI_ERROR_SYSTEM;
634 }
635 
642  int ret;
643 
644  ret = taiHookReleaseForKernel(g_hooks[0], g_parse_headers_hook);
645  ret |= taiHookReleaseForKernel(g_hooks[1], g_setup_buffer_hook);
646  ret |= taiHookReleaseForKernel(g_hooks[2], g_decrypt_buffer_hook);
647  ret |= taiHookReleaseForKernel(g_hooks[3], g_rif_check_vita_hook);
648  ret |= taiHookReleaseForKernel(g_hooks[4], g_rif_check_psp_hook);
649  ret |= taiHookReleaseForKernel(g_hooks[5], g_rif_get_info_hook);
650  ret |= taiHookReleaseForKernel(g_hooks[6], g_package_check_hook);
651  ret |= taiHookReleaseForKernel(g_hooks[7], g_package_check_2_hook);
652  ret |= taiHookReleaseForKernel(g_hooks[8], g_load_user_libs_hook);
653  ret |= taiHookReleaseForKernel(g_hooks[9], g_nid_poison_hook);
654  ret |= taiHookReleaseForKernel(g_hooks[10], g_unload_process_hook);
655  return ret;
656 }
SceUID taiHookFunctionExportForKernel(SceUID pid, tai_hook_ref_t *p_hook, const char *module, uint32_t library_nid, uint32_t func_nid, const void *hook_func)
Add a hook to a module function export.
Definition: taihen.c:72
int hen_free_config(void)
Frees tai config file.
Definition: hen.c:467
int taiHookReleaseForKernel(SceUID tai_uid, tai_hook_ref_t hook)
Release a hook.
Definition: taihen.c:204
uintptr_t tai_hook_ref_t
Hook information.
Definition: taihen.h:215
SceUID taiHookFunctionImportForKernel(SceUID pid, tai_hook_ref_t *p_hook, const char *module, uint32_t import_library_nid, uint32_t import_func_nid, const void *hook_func)
Add a hook to a module function import.
Definition: taihen.c:115
void hen_load_plugin(const char *path, void *param)
Callback to config parser to load a plugin.
Definition: hen.c:482
#define KERNEL_PID
Definition: taihen.h:34
SceUID pid
Process to load plugin to.
Definition: hen.h:23
int tai_try_cleanup_process(SceUID pid)
Called on process exist to force remove private hooks.
Definition: patches.c:765
int hen_load_config(void)
Load tai config file.
Definition: hen.c:392
Arguments passed from taiHEN to config parser back to taiHEN.
Definition: hen.h:22
int flags
Flags for loading.
Definition: hen.h:24
int hen_add_patches(void)
Add kernel patches to disable SELF signature checks.
Definition: hen.c:507
#define TAI_CONTINUE(type, hook,...)
Calls the next function in the chain.
Definition: taihen.h:345
int hen_remove_patches(void)
Removes the kernel patches for SELF loading.
Definition: hen.c:641
#define TAIHEN_CONFIG_FILE
Definition: hen.h:17