aboutsummaryrefslogtreecommitdiff
path: root/taihen.c
blob: 1e170b79214b72eb554edd4c69bf5b39f1d80fc4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/* taihen.c -- cfw framework for PS Vita
 *
 * Copyright (C) 2016 Yifan Lu
 * Copyright (C) 2021 Reiko Asakura
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <string.h>
#include <scetypes.h>
#include <ctrl.h>
#include <sblaimgr.h>
#include <syscon.h>
#include <kernel/modulemgr.h>
#include <kernel/sysmem.h>
#include <kernel/threadmgr.h>
#include <taihen/parser.h>
#include "error.h"
#include "hen.h"
#include "module.h"
#include "patches.h"
#include "plugin.h"
#include "proc_map.h"
#include "taihen_internal.h"

/** For ordering log entries */
unsigned char log_ctr = 0;

/** Hook reference to `is_safe_mode` */
static tai_hook_ref_t g_is_safe_mode_hook;

/** References to the hooks */
static SceUID g_hooks[1];

/**
 * @brief      Patch to allow USB drive mounting when boot is delayed
 *             when waiting for controller input
 *
 * @return     Always returns 1
 */
static int is_safe_mode_patched(void) {
  TAI_NEXT(is_safe_mode_patched, g_is_safe_mode_hook);
  return 1;
}

/**
 * @brief      Add a hook given an absolute address
 *
 *             If target is the kernel, use KERNEL_PID as `pid`.
 *
 * @param[in]  pid        The pid of the target
 * @param[out] p_hook     A reference that can be used by the hook function
 * @param      dest_func  The function to patch (must be in the target address
 *                        space)
 * @param[in]  hook_func  The hook function (must be in the target address
 *                        space)
 *
 * @return     A tai patch reference on success, < 0 on error
 *             - TAI_ERROR_PATCH_EXISTS if the address is already patched
 *             - TAI_ERROR_HOOK_ERROR if an internal error occurred trying to hook
 *             - TAI_ERROR_INVALID_KERNEL_ADDR if `pid` is kernel and address is in shared memory region
 */
SceUID taiHookFunctionAbs(SceUID pid, tai_hook_ref_t *p_hook, void *dest_func, const void *hook_func) {
  return tai_hook_func_abs(p_hook, pid, dest_func, hook_func);
}

/**
 * @brief      Add a hook to a module function export
 *
 *             If target is the kernel, use KERNEL_PID as `pid`. Since a module
 *             can have two libraries that export the same NID, you can
 *             optionally pass in the library NID of the one to hook. Otherwise,
 *             use `TAI_ANY_LIBRARY` and the first one found will be used.
 *
 * @param[in]  pid          The pid of the target
 * @param[out] p_hook       A reference that can be used by the hook function
 * @param[in]  module       Name of the target module.
 * @param[in]  library_nid  Optional. NID of the target library.
 * @param[in]  func_nid     The function NID. If `library_nid` is
 *                          `TAI_ANY_LIBRARY`, then the first export with the
 *                          NID will be hooked.
 * @param[in]  hook_func    The hook function (must be in the target address
 *                          space)
 *
 * @return     A tai patch reference on success, < 0 on error
 *             - TAI_ERROR_PATCH_EXISTS if the address is already patched
 *             - TAI_ERROR_HOOK_ERROR if an internal error occurred trying to
 *               hook
 *             - TAI_ERROR_INVALID_KERNEL_ADDR if `pid` is kernel and address is
 *               in shared memory region
 *             - TAI_ERROR_INVALID_MODULE if `module` is `TAI_MAIN_MODULE`
 *               and `pid` is kernel
 */
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) {
  int ret;
  uintptr_t func;

  ret = module_get_export_func(pid, module, library_nid, func_nid, &func);
  if (ret < 0) {
    LOG("Failed to find export for %s, NID:0x%08X: 0x%08X", module, func_nid, ret);
    return ret;
  }
  return taiHookFunctionAbs(pid, p_hook, (void *)func, hook_func);
}

/**
 * @brief      Add a hook to a module function import
 *
 *             If target is the kernel, use KERNEL_PID as `pid`. This will let
 *             you hook calls from one module to another without having to hook
 *             all calls to that module.
 *
 * @param[in]  pid                 The pid of the target
 * @param[out] p_hook              A reference that can be used by the hook
 *                                 function
 * @param[in]  module              Name of the target module.
 * @param[in]  import_library_nid  The imported library from the target module
 * @param[in]  import_func_nid     The function NID of the import
 * @param[in]  hook_func           The hook function (must be in the target
 *                                 address space)
 *
 * @return     A tai patch reference on success, < 0 on error
 *             - TAI_ERROR_PATCH_EXISTS if the address is already patched
 *             - TAI_ERROR_HOOK_ERROR if an internal error occurred trying to
 *               hook
 *             - TAI_ERROR_INVALID_KERNEL_ADDR if `pid` is kernel and address is
 *               in shared memory region
 *             - TAI_ERROR_STUB_NOT_RESOLVED if the import has not been resolved
 *               yet. You should hook `sceKernelLoadStartModule`,
 *               `sceSysmoduleLoadModule` or whatever the application uses to
 *               start the imported module and add this hook after the module is
 *               loaded. Be sure to also hook module unloading to remove the
 *               hook BEFORE the imported module is unloaded!
 *             - TAI_ERROR_INVALID_MODULE if `module` is `TAI_MAIN_MODULE`
 *               and `pid` is kernel
 */
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) {
  int ret;
  uintptr_t stubptr;
  uint32_t stub[3];

  ret = module_get_import_func(pid, module, import_library_nid, import_func_nid, &stubptr);
  if (ret < 0) {
    LOG("Failed to find stub for %s, NID:0x%08X: 0x%08X", module, import_func_nid, ret);
    return ret;
  }
  ret = tai_memcpy_to_kernel(pid, stub, (const void *)(stubptr & ~1), sizeof(stub));
  if (ret < 0) {
    LOG("Failed to read stub %p, %x", stubptr, ret);
    return ret;
  }
  // FIXME: find a better way to do this
  if (stub[0] == 0xE24FC008) {
    LOG("stub for %p has not been resolved yet!", import_func_nid);
    return TAI_ERROR_STUB_NOT_RESOLVED;
  }
  return taiHookFunctionAbs(pid, p_hook, (void *)stubptr, hook_func);
}

/**
 * @brief      Add a hook to a module manually with an offset
 *
 *             If target is the kernel, use KERNEL_PID as `pid`. The caller is
 *             responsible for checking that the module is of the correct
 *             version!
 *
 * @param[in]  pid        The pid of the target
 * @param[out] p_hook     A reference that can be used by the hook function
 * @param[in]  modid      The module UID from `taiGetModuleInfoForKernel`
 * @param[in]  segidx     The ELF segment index containing the function to patch
 * @param[in]  offset     The offset from the start of the segment
 * @param[in]  thumb      Set to 1 if this is a Thumb function
 * @param[in]  hook_func  The hook function (must be in the target address
 *                        space)
 *
 * @return     A tai patch reference on success, < 0 on error
 *             - TAI_ERROR_PATCH_EXISTS if the address is already patched
 *             - TAI_ERROR_HOOK_ERROR if an internal error occurred trying to hook
 *             - TAI_ERROR_INVALID_KERNEL_ADDR if `pid` is kernel and address is in shared memory region
 */
SceUID taiHookFunctionOffsetForKernel(SceUID pid, tai_hook_ref_t *p_hook, SceUID modid, int segidx, uint32_t offset, int thumb, const void *hook_func) {
  int ret;
  uintptr_t addr;

  ret = module_get_offset(pid, modid, segidx, offset, &addr);
  if (ret < 0) {
    LOG("Failed to find offset for mod:%x, segidx:%d, offset:0x%08X: 0x%08X", modid, segidx, offset, ret);
    return ret;
  }
  if (thumb) {
    addr = addr | 1;
  }
  return taiHookFunctionAbs(pid, p_hook, (void *)addr, hook_func);
}

/**
 * @brief      Gets information on a currently loaded module
 *
 *             You should use this before calling
 *             `taiHookFunctionOffsetForKernel` in order to check that the
 *             module you wish to hook is currently loaded and that the module
 *             NID matches. The module NID changes in each version of the
 *             module.
 *
 * @param[in]  pid     The pid of the _caller_ (kernel should set to KERNEL_PID)
 * @param[in]  module  The name of the module
 * @param[out] info    The information to fill
 *
 * @return     Zero on success, < 0 on error
 *             - TAI_ERROR_INVALID_MODULE if `module` is `TAI_MAIN_MODULE`
 *               and `pid` is kernel
 */
int taiGetModuleInfoForKernel(SceUID pid, const char *module, tai_module_info_t *info) {
  return module_get_by_name_nid(pid, module, TAI_IGNORE_MODULE_NID, info);
}

/**
 * @brief      Release a hook
 *
 * @param[in]  tai_uid  The tai patch reference to free
 * @param[in]  hook     The hook to free
 *
 * @return     Zero on success, < 0 on error
 *             - TAI_ERROR_HOOK_ERROR if an internal error occurred trying to restore the function
 */
int taiHookReleaseForKernel(SceUID tai_uid, tai_hook_ref_t hook) {
  return tai_hook_release(tai_uid, hook);
}

/**
 * @brief      Injects data into a process bypassing MMU flags
 *
 * @param[in]  pid   The pid of the target (can be KERNEL_PID)
 * @param      dest  The destination in the process address space
 * @param[in]  src   The source in kernel address space
 * @param[in]  size  The size of the injection in bytes
 *
 * @return     A tai patch reference on success, < 0 on error
 *             - TAI_ERROR_PATCH_EXISTS if the address is already patched
 */
SceUID taiInjectAbsForKernel(SceUID pid, void *dest, const void *src, size_t size) {
  return tai_inject_abs(pid, dest, src, size);
}

/**
 * @brief      Inject data into a process bypassing MMU flags given an offset
 *
 * @param[in]  pid     The pid of the target (can be KERNEL_PID)
 * @param[in]  modid   The module UID from `taiGetModuleInfoForKernel`
 * @param[in]  segidx  Index of the ELF segment containing the data to patch
 * @param[in]  offset  The offset from the start of the segment
 * @param[in]  data    The data in kernel address space
 * @param[in]  size    The size of the injection in bytes
 *
 * @return     A tai patch reference on success, < 0 on error
 *             - TAI_ERROR_PATCH_EXISTS if the address is already patched
 */
SceUID taiInjectDataForKernel(SceUID pid, SceUID modid, int segidx, uint32_t offset, const void *data, size_t size) {
  int ret;
  uintptr_t addr;

  ret = module_get_offset(pid, modid, segidx, offset, &addr);
  if (ret < 0) {
    LOG("Failed to find offset for mod:%x, segidx:%d, offset:0x%08X: 0x%08X", modid, segidx, offset, ret);
    return ret;
  }
  return taiInjectAbsForKernel(pid, (void *)addr, data, size);
}

/**
 * @brief      Release an injection
 *
 * @param[in]  tai_uid  The tai patch reference to free
 *
 * @return     Zero on success, < 0 on error
 */
int taiInjectReleaseForKernel(SceUID tai_uid) {
  return tai_inject_release(tai_uid);
}

/**
 * @brief      Parses the taiHEN config and loads all plugins for a titleid to a
 *             process
 *
 *             `flags` are ignored!
 *
 * @param[in]  pid      The pid to load to
 * @param[in]  titleid  The title to read from the config
 * @param[in]  flags    Ignored.
 *
 * @return     Zero on success, < 0 on error
 *             - TAI_ERROR_SYSTEM if the config file is invalid
 */
int taiLoadPluginsForTitleForKernel(SceUID pid, const char *titleid, int flags) {
  return plugin_load_all(pid, titleid);
}

/**
 * @brief      Reloads config.txt from the default path and optionally loads
 *             kernel plugins.
 *
 *             If `schedule` is set, then if called from a plugin `module_start`
 *             handler, it will schedule a config reload (and optionally load
 *             kernel plugins) after the current config parsing is completed. If
 *             `load_kernel` is set, then load kernel plugins defined in
 *             `*KERNEL` from the config file as well after config is reloaded
 *             successfully.
 *
 * @param[in]  schedule     If blocking, schedule reload until after load is
 *                          complete.
 * @param[in]  load_kernel  Load all kernel plugins defined in config.
 *
 * @return     Zero on success, < 0 on error
 *             - TAI_ERROR_BLOCKING if attempted to call from plugin start and
 *               `schedule` _is not set_.
 */
int taiReloadConfigForKernel(int schedule, int load_kernel) {
  int ret;

  ret = plugin_load_config();
  if (ret == TAI_ERROR_BLOCKING && schedule) {
    plugin_delayed_load_config(load_kernel);
    ret = TAI_SUCCESS;
  }
  return ret;
}

/**
 * @brief      Module entry point
 *
 *             This module should be loaded by a kernel exploit. taiHEN expects
 *             the kernel environment to be clean, which means that no outside
 *             hooks and patches which may interfere with taiHEN.
 *
 *             If the user hold the L button while starting taiHEN, kernel
 *             plugins will be skipped.
 *
 * @param[in]  argc  Size of arguments (unused)
 * @param[in]  args  The arguments (unused)
 *
 * @return     Success always
 */
int module_start(SceSize argc, const void *args) {
  SceCtrlData ctrl;
  void *kbl_param;
  int load_plugins = 1;
  int ret;
  LOG("starting taihen...");
  ret = proc_map_init();
  if (ret < 0) {
    LOG("proc map init failed: %x", ret);
    return SCE_KERNEL_START_FAILED;
  }
  ret = patches_init();
  if (ret < 0) {
    LOG("patches init failed: %x", ret);
    return SCE_KERNEL_START_FAILED;
  }
  ret = plugin_init();
  if (ret < 0) {
    LOG("plugin init failed: %x", ret);
    return SCE_KERNEL_START_FAILED;
  }
  ret = hen_add_patches();
  if (ret < 0) {
    LOG("HEN patches failed: %x", ret);
    return SCE_KERNEL_START_FAILED;
  }
  if (sceSblAimgrIsGenuineDolce() && sceKernelSysrootGetShellPid() < 0) {
    kbl_param = sceKernelGetSysrootBuffer();
    if (*(uint32_t*)(kbl_param + 0xC4) == 0xFF1C && *(uint32_t*)(kbl_param + 0xD8) == 0x8) {
      g_hooks[0] = taiHookFunctionImportForKernel(KERNEL_PID, &g_is_safe_mode_hook, "SceUsbServ", 0x2ED7F97A, 0x834439A7, is_safe_mode_patched);
      sceSysconCtrlDolceLED(1);
      for (int i = 0; i <= 10000000; i += 50000) {
        memset(&ctrl, 0, sizeof(ctrl));
        sceCtrlPeekBufferPositive(0, &ctrl, 1);
        if (ctrl.buttons & (SCE_CTRL_L | SCE_CTRL_L1)) {
          load_plugins = 0;
          sceSysconCtrlDolceLED(2);
          sceKernelDelayThread(2000000);
          break;
        }
        sceKernelDelayThread(50000);
      }
      sceSysconCtrlDolceLED(0);
      if (g_hooks[0] >= 0) {
        taiHookReleaseForKernel(g_hooks[0], g_is_safe_mode_hook);
      }
    }
  }
  sceCtrlPeekBufferPositive(0, &ctrl, 1);
  LOG("buttons held: 0x%08X", ctrl.buttons);
  if (ctrl.buttons & (SCE_CTRL_L | SCE_CTRL_L1)) {
    load_plugins = 0;
  }
  if (load_plugins) {
    plugin_load_config();
    plugin_load_all(KERNEL_PID, "KERNEL");
  } else {
    LOG("skipping plugin loading");
  }
  return SCE_KERNEL_START_SUCCESS;
}

/**
 * @brief      Module cleanup
 *
 *             This cleans up the system and removes all hooks and patches. All
 *             handles held by plugins will be invalid after this point! This is
 *             called by the kernel module manager. In usual operation, you
 *             should not unload taiHEN.
 *
 * @param[in]  argc  Size of arguments (unused)
 * @param[in]  args  The arguments (unused)
 *
 * @return     Success always
 */
int module_stop(SceSize argc, const void *args) {
  // TODO: release everything
  hen_remove_patches();
  plugin_deinit();
  patches_deinit();
  proc_map_deinit();
  return SCE_KERNEL_STOP_SUCCESS;
}

/**
 * @brief      Module Exit handler (unused)
 *
 *             This function is currently unused on retail units.
 */
void module_exit(void) {

}