forked from yifanlu/taiHEN
-
Notifications
You must be signed in to change notification settings - Fork 5
/
hen.c
589 lines (541 loc) · 20.6 KB
/
hen.c
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
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
/* hen.c -- kernel signature patches
*
* Copyright (C) 2016 Yifan Lu
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
#include <psp2kern/ctrl.h>
#include <psp2kern/types.h>
#include <psp2kern/io/stat.h>
#include <psp2kern/kernel/sysmem.h>
#include <string.h>
#include "error.h"
#include "hen.h"
#include "module.h"
#include "patches.h"
#include "taihen_internal.h"
/** The Vita supports a max of 8 segments for ET_SCE_RELEXEC type */
#define MAX_SEGMENTS 8
/** Should be same on all current firmware, but this may change. */
#define OFFSET_PATCH_ARG 168
/*
* S/ELF header
*/
/** @{ */
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef void * Elf32_Addr;
typedef size_t Elf32_Off;
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* ident bytes */
Elf32_Half e_type; /* file type */
Elf32_Half e_machine; /* target machine */
Elf32_Word e_version; /* file version */
Elf32_Addr e_entry; /* start address */
Elf32_Off e_phoff; /* phdr file offset */
Elf32_Off e_shoff; /* shdr file offset */
Elf32_Word e_flags; /* file flags */
Elf32_Half e_ehsize; /* sizeof ehdr */
Elf32_Half e_phentsize; /* sizeof phdr */
Elf32_Half e_phnum; /* number phdrs */
Elf32_Half e_shentsize; /* sizeof shdr */
Elf32_Half e_shnum; /* number shdrs */
Elf32_Half e_shstrndx; /* shdr string index */
} __attribute__((packed)) Elf32_Ehdr;
typedef struct {
uint32_t magic; /* 53434500 = SCE\0 */
uint32_t version; /* header version 3*/
uint16_t sdk_type; /* */
uint16_t header_type; /* 1 self, 2 unknown, 3 pkg */
uint32_t metadata_offset; /* metadata offset */
uint64_t header_len; /* self header length */
uint64_t elf_filesize; /* ELF file length */
uint64_t self_filesize; /* SELF file length */
uint64_t unknown; /* UNKNOWN */
uint64_t self_offset; /* SELF offset */
uint64_t appinfo_offset; /* app info offset */
uint64_t elf_offset; /* ELF #1 offset */
uint64_t phdr_offset; /* program header offset */
uint64_t shdr_offset; /* section header offset */
uint64_t section_info_offset; /* section info offset */
uint64_t sceversion_offset; /* version offset */
uint64_t controlinfo_offset; /* control info offset */
uint64_t controlinfo_size; /* control info size */
uint64_t padding;
} __attribute__((packed)) self_header_t;
typedef struct {
uint64_t offset;
uint64_t size;
uint32_t compressed; // 2=compressed
uint32_t unknown1;
uint32_t encrypted; // 1=encrypted
uint32_t unknown2;
} __attribute__((packed)) self_section_info_t;
/** @} */
/** Hook reference to `parse_headers` */
static tai_hook_ref_t g_parse_headers_hook;
/** Hook reference to `setup_buffer` */
static tai_hook_ref_t g_setup_buffer_hook;
/** Hook reference to `decrypt_buffer` */
static tai_hook_ref_t g_decrypt_buffer_hook;
/** Hook reference to `decrypt_buffer` */
static tai_hook_ref_t g_rif_check_vita_hook;
/** Hook reference to `decrypt_buffer` */
static tai_hook_ref_t g_rif_check_psp_hook;
/** Hook reference to `rif_get_info` */
static tai_hook_ref_t g_rif_get_info_hook;
/** Hook reference to check in `sceNpDrmPackageCheck` */
static tai_hook_ref_t g_package_check_hook;
/** Hook reference to check in `sceNpDrmPackageCheck` */
static tai_hook_ref_t g_package_check_2_hook;
/** Hook reference to `start_preloaded_modules */
static tai_hook_ref_t g_start_preloaded_modules_hook;
/** Hook reference to `nid_poison` */
static tai_hook_ref_t g_nid_poison_hook;
/** Hook reference to `unload_process` */
static tai_hook_ref_t g_unload_process_hook;
/** Hook reference to `is_cex` */
static tai_hook_ref_t g_is_cex_hook;
/** References to the hooks */
static SceUID g_hooks[12];
/** Is the current decryption a homebrew? */
static int g_is_homebrew;
/** Cache of segment info entries from SELF header */
static self_section_info_t g_seg_info[MAX_SEGMENTS];
/**
* @brief Patch for parsing SELF headers
*
* @param[in] ctx The decrypt context
* @param[in] headers The SELF header buffer
* @param[in] len The header length
* @param args The arguments
*
* @return Zero on success, < 0 on error
*/
static int parse_headers_patched(int ctx, const void *headers, size_t len, void *args) {
self_header_t *self;
Elf32_Ehdr *elf;
int ret;
int num_segs;
memset(&g_seg_info, 0, sizeof(g_seg_info));
if (len >= sizeof(self_header_t) && len >= sizeof(Elf32_Ehdr)) {
self = (self_header_t *)headers;
if (self->elf_offset <= len - sizeof(Elf32_Ehdr)) {
elf = (Elf32_Ehdr *)(headers + self->elf_offset);
num_segs = elf->e_phnum;
if (num_segs <= MAX_SEGMENTS &&
self->section_info_offset < self->section_info_offset + num_segs * sizeof(self_section_info_t) &&
self->section_info_offset + num_segs * sizeof(self_section_info_t) < len
) {
memcpy(&g_seg_info, headers + self->section_info_offset, num_segs * sizeof(self_section_info_t));
}
}
}
ret = TAI_CONTINUE(int, g_parse_headers_hook, ctx, headers, len, args);
if (ctx == 1) { // as of 3.60, only one decrypt context exists
if (ret < 0) {
g_is_homebrew = 1;
// we only do this patch if another hook hasn't modified it already
if (*(uint32_t *)(args + OFFSET_PATCH_ARG) == 0) {
*(uint32_t *)(args + OFFSET_PATCH_ARG) = 0x20;
}
ret = 0;
} else {
g_is_homebrew = 0;
}
LOG("parse ret %x, decrypt is homebrew? %d", ret, g_is_homebrew);
}
return ret;
}
/**
* @brief Patch for setting up decrypt buffer
*
* @param[in] ctx The decrypt context
* @param[in] segidx The ELF segment index to decrypt
*
* @return 1 for non-compressed, 2 for compressed buffer, < 0 on error
*/
static int setup_buffer_patched(int ctx, int segidx) {
int ret;
ret = TAI_CONTINUE(int, g_setup_buffer_hook, ctx, segidx);
if (ctx == 1 && g_is_homebrew && segidx < MAX_SEGMENTS) {
ret = g_seg_info[segidx].compressed;
LOG("segidx %d, compression type: %d", segidx, ret);
}
return ret;
}
/**
* @brief Patch for decrypting a buffer
*
* @param[in] ctx The decrypt context
* @param[inout] buffer The encrypted buffer to decrypt in place
* @param[in] len The length of the buffer
*
* @return Zero on success, < 0 on error
*/
static int decrypt_buffer_patched(int ctx, void *buffer, size_t len) {
int ret;
ret = TAI_CONTINUE(int, g_decrypt_buffer_hook, ctx, buffer, len);
if (ctx == 1 && g_is_homebrew) {
LOG("patching decrypt buffer bypass");
ret = 0;
}
return ret;
}
/**
* @brief Patch for some rif checking
*
* @param[in] a1 Unknown
* @param[in] a2 Unknown
* @param[in] a3 Unknown
* @param[in] a4 Unknown
* @param[in] a5 Unknown
* @param[in] a6 Unknown
*
* @return Unknown
*/
static int rif_check_vita_patched(int a1, int a2, int a3, int a4, int a5, int a6) {
int ret;
ret = TAI_CONTINUE(int, g_rif_check_vita_hook, a1, a2, a3, a4, a5, a6);
if (ret == 0x80870003) {
LOG("patched rif check return: %x => 0", ret);
ret = 0;
}
return ret;
}
/**
* @brief Patch for some rif checking used by SceCompat
*
* @param[in] a1 Unknown
* @param[in] a2 Unknown
* @param[in] a3 Unknown
* @param[in] a4 Unknown
* @param[in] a5 Unknown
*
* @return Unknown
*/
static int rif_check_psp_patched(int a1, int a2, int a3, int a4, int a5) {
int ret;
ret = TAI_CONTINUE(int, g_rif_check_psp_hook, a1, a2, a3, a4, a5);
if (ret == 0x80870003) {
LOG("patched rif check return: %x => 0", ret);
ret = 0;
}
return ret;
}
/**
* @brief Patch for reading rif
*
* @param[in] a1 Unknown
* @param[in] a2 Unknown
* @param[in] a3 Unknown
* @param[in] a4 Unknown
* @param[in] a5 Unknown
* @param[in] a6 Unknown
* @param[in] a7 Unknown
* @param[in] a8 Unknown
* @param[in] a9 Unknown
* @param[in] a10 Unknown
* @param[in] a11 Unknown
* @param[in] a12 Unknown
*
* @return Unknown
*/
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) {
int ret;
ret = TAI_CONTINUE(int, g_rif_get_info_hook, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
if (ret == 0x80870003) {
LOG("patched rif check return: %x => 0", ret);
ret = 0;
}
return ret;
}
/**
* @brief Patch for checking if a fpkg support is enabled
*
* @return Zero if valid, < 0 on error
*/
static int package_check_patched(void) {
int ret;
ret = TAI_CONTINUE(int, g_package_check_hook);
LOG("patching fpkg enabled: %x => 1", ret);
return 1;
}
/**
* @brief Patch for checking if a fpkg support is enabled
*
* @return Zero if valid, < 0 on error
*/
static int package_check_2_patched(void) {
int ret;
ret = TAI_CONTINUE(int, g_package_check_2_hook);
LOG("patching fpkg enabled: %x => 1", ret);
return 1;
}
/**
* @brief Patch to bypass 0x8080004C error on 3.60+
*
* @return 1 if CEX, 0 otherwise
*/
static int is_cex_patched(void) {
TAI_CONTINUE(int, g_is_cex_hook);
return 0;
}
/**
* @brief Patch to disable NID poisoning
*
* This function is actually a memset of 32-bit width.
*
* @param[in] dst The destination in user address space
* @param[in] set What to write
* @param[in] len The length
*
* @return Zero
*/
static int nid_poison_patched(uintptr_t dst, int set, size_t len) {
// we're breaking the rules here. we don't call TAI_CONTINUE
// because we do not want the effect to take place
//LOG("patched out nid poison request: %p, %x, len: %x", dst, set, len);
return 0;
}
/**
* @brief Patch for unloading a process
*
* @param[in] pid The process being unloaded
*
* @return Zero on success, < 0 on error
*/
static int unload_process_patched(SceUID pid) {
int ret;
LOG("unloading pid:%x", pid);
ret = tai_try_cleanup_process(pid);
LOG("cleanup: %x", ret);
ret = TAI_CONTINUE(int, g_unload_process_hook, pid);
LOG("process unloaded: %x", ret);
return ret;
}
/**
* @brief Patch for loading a process
*
* This event happens after the default list of modules are loaded
* for a process. We used to hook at that point, but because of a
* SCE bug, we cannot have > 15 preloaded modules. Instead now we
* hook at the point those preloaded modules start.
*
* If the user hold the L button while starting taiHEN or rebuilt database,
* plugins will be skipped.
*
* @param[in] pid The process being started
*
* @return Zero on success, < 0 on error
*/
static int start_preloaded_modules_patched(SceUID pid) {
SceCtrlData ctrl;
SceIoStat stat;
int ret;
char titleid[32];
LOG("starting all default modules for %x...", pid);
ret = TAI_CONTINUE(int, g_start_preloaded_modules_hook, pid);
ksceCtrlPeekBufferPositive(0, &ctrl, 1);
LOG("buttons held: 0x%08X", ctrl.buttons);
if (ctrl.buttons & (SCE_CTRL_LTRIGGER | SCE_CTRL_L1) ||
ksceIoGetstat("ur0:shell/db/dbr.db-err", &stat) >= 0) {
LOG("skipping plugin loading");
return ret;
}
ksceKernelGetProcessTitleId(pid, titleid, 32);
LOG("title started: %s, pid: %x, loading plugins...", titleid, pid);
taiLoadPluginsForTitleForKernel(pid, titleid, 0);
return ret;
}
/**
* @brief Add kernel patches to disable SELF signature checks
*
* @return Zero on success, < 0 on error
*/
int hen_add_patches(void) {
memset(g_hooks, 0, sizeof(g_hooks));
g_hooks[0] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_parse_headers_hook,
"SceKernelModulemgr",
0x7ABF5135, // SceSblAuthMgrForKernel
0xF3411881,
parse_headers_patched);
if (g_hooks[0] < 0) goto fail;
LOG("parse_headers_patched added");
g_hooks[1] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_setup_buffer_hook,
"SceKernelModulemgr",
0x7ABF5135, // SceSblAuthMgrForKernel
0x89CCDA2C,
setup_buffer_patched);
if (g_hooks[1] < 0) goto fail;
LOG("setup_buffer_patched added");
g_hooks[2] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_decrypt_buffer_hook,
"SceKernelModulemgr",
0x7ABF5135, // SceSblAuthMgrForKernel
0xBC422443,
decrypt_buffer_patched);
if (g_hooks[2] < 0) goto fail;
LOG("decrypt_buffer_patched added");
g_hooks[3] = taiHookFunctionExportForKernel(KERNEL_PID,
&g_rif_check_vita_hook,
"SceNpDrm",
0xD84DC44A, // SceNpDrmForDriver
0x723322B5,
rif_check_vita_patched);
if (g_hooks[3] < 0) goto fail;
LOG("rif_check_vita added");
g_hooks[4] = taiHookFunctionExportForKernel(KERNEL_PID,
&g_rif_check_psp_hook,
"SceNpDrm",
0xD84DC44A, // SceNpDrmForDriver
0xDACB71F4,
rif_check_psp_patched);
if (g_hooks[4] < 0) goto fail;
LOG("rif_check_psp added");
g_hooks[5] = taiHookFunctionExportForKernel(KERNEL_PID,
&g_rif_get_info_hook,
"SceNpDrm",
0xD84DC44A, // SceNpDrmForDriver
0xDB406EAE,
rif_get_info_patched);
if (g_hooks[5] < 0) goto fail;
LOG("rif_get_info added");
g_hooks[6] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_package_check_hook,
"SceNpDrm",
0xFD00C69A, // SceSblAIMgrForDriver
0xD78B04A2,
package_check_patched);
LOG("package_check added");
if (g_hooks[6] < 0) goto fail;
g_hooks[7] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_package_check_2_hook,
"SceNpDrm",
0xFD00C69A, // SceSblAIMgrForDriver
0xF4B98F66,
package_check_2_patched);
LOG("package_check_2 added");
if (g_hooks[7] < 0) goto fail;
g_hooks[8] = taiHookFunctionExportForKernel(KERNEL_PID,
&g_start_preloaded_modules_hook,
"SceKernelModulemgr",
0xC445FA63, // SceModulemgrForKernel
0x432DCC7A,
start_preloaded_modules_patched);
if (g_hooks[8] < 0) {
g_hooks[8] = taiHookFunctionExportForKernel(KERNEL_PID,
&g_start_preloaded_modules_hook,
"SceKernelModulemgr",
0x92C9FFC2, // SceModulemgrForKernel
0x998C7AE9,
start_preloaded_modules_patched);
}
if (g_hooks[8] < 0) goto fail;
LOG("start_preloaded_modules_patched added");
g_hooks[9] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_nid_poison_hook,
"SceKernelModulemgr",
0x63A519E5, // SceSysmemForKernel
0xECF9435A,
nid_poison_patched);
if (g_hooks[9] < 0) {
g_hooks[9] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_nid_poison_hook,
"SceKernelModulemgr",
0x02451F0F, // SceSysmemForKernel
0xFCB5745A,
nid_poison_patched);
}
if (g_hooks[9] < 0) goto fail;
LOG("nid_poison_patched added");
g_hooks[10] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_unload_process_hook,
"SceProcessmgr",
0xC445FA63, // SceModulemgrForKernel
0x0E33258E,
unload_process_patched);
if (g_hooks[10] < 0) {
g_hooks[10] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_unload_process_hook,
"SceProcessmgr",
0x92C9FFC2, // SceModulemgrForKernel
0xE71530D7,
unload_process_patched);
}
if (g_hooks[10] < 0) goto fail;
LOG("unload_process_patched added");
g_hooks[11] = taiHookFunctionImportForKernel(KERNEL_PID,
&g_is_cex_hook,
"SceAppMgr",
0xFD00C69A, // SceSblAIMgrForDriver
0xD78B04A2,
is_cex_patched);
if (g_hooks[11] < 0) goto fail;
LOG("is_cex_patched added");
return TAI_SUCCESS;
fail:
if (g_hooks[0] >= 0) {
taiHookReleaseForKernel(g_hooks[0], g_parse_headers_hook);
}
if (g_hooks[1] >= 0) {
taiHookReleaseForKernel(g_hooks[1], g_setup_buffer_hook);
}
if (g_hooks[2] >= 0) {
taiHookReleaseForKernel(g_hooks[2], g_decrypt_buffer_hook);
}
if (g_hooks[3] >= 0) {
taiHookReleaseForKernel(g_hooks[3], g_rif_check_vita_hook);
}
if (g_hooks[4] >= 0) {
taiHookReleaseForKernel(g_hooks[4], g_rif_check_psp_hook);
}
if (g_hooks[5] >= 0) {
taiHookReleaseForKernel(g_hooks[5], g_rif_get_info_hook);
}
if (g_hooks[6] >= 0) {
taiHookReleaseForKernel(g_hooks[6], g_package_check_hook);
}
if (g_hooks[7] >= 0) {
taiHookReleaseForKernel(g_hooks[7], g_package_check_2_hook);
}
if (g_hooks[8] >= 0) {
taiHookReleaseForKernel(g_hooks[8], g_start_preloaded_modules_hook);
}
if (g_hooks[9] >= 0) {
taiHookReleaseForKernel(g_hooks[9], g_nid_poison_hook);
}
if (g_hooks[10] >= 0) {
taiHookReleaseForKernel(g_hooks[10], g_unload_process_hook);
}
if (g_hooks[11] >= 0) {
taiHookReleaseForKernel(g_hooks[11], g_is_cex_hook);
}
return TAI_ERROR_SYSTEM;
}
/**
* @brief Removes the kernel patches for SELF loading
*
* @return Zero on success, < 0 on error
*/
int hen_remove_patches(void) {
int ret;
ret = taiHookReleaseForKernel(g_hooks[0], g_parse_headers_hook);
ret |= taiHookReleaseForKernel(g_hooks[1], g_setup_buffer_hook);
ret |= taiHookReleaseForKernel(g_hooks[2], g_decrypt_buffer_hook);
ret |= taiHookReleaseForKernel(g_hooks[3], g_rif_check_vita_hook);
ret |= taiHookReleaseForKernel(g_hooks[4], g_rif_check_psp_hook);
ret |= taiHookReleaseForKernel(g_hooks[5], g_rif_get_info_hook);
ret |= taiHookReleaseForKernel(g_hooks[6], g_package_check_hook);
ret |= taiHookReleaseForKernel(g_hooks[7], g_package_check_2_hook);
ret |= taiHookReleaseForKernel(g_hooks[8], g_start_preloaded_modules_hook);
ret |= taiHookReleaseForKernel(g_hooks[9], g_nid_poison_hook);
ret |= taiHookReleaseForKernel(g_hooks[10], g_unload_process_hook);
ret |= taiHookReleaseForKernel(g_hooks[11], g_is_cex_hook);
return ret;
}