mirror of
https://github.com/joel16/VitaShell.git
synced 2024-12-04 01:40:54 +00:00
812 lines
19 KiB
C
812 lines
19 KiB
C
/*
|
|
VitaShell
|
|
Copyright (C) 2015-2016, TheFloW
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "main.h"
|
|
#include "init.h"
|
|
#include "file.h"
|
|
#include "utils.h"
|
|
#include "module.h"
|
|
/*
|
|
typedef struct {
|
|
char modname[27];
|
|
uint32_t text_addr;
|
|
uint32_t text_size;
|
|
uint32_t data_addr;
|
|
uint32_t data_size;
|
|
} ModuleInfo;
|
|
*/
|
|
static NidTable *nid_table = NULL;
|
|
static int nids_count = 0;
|
|
|
|
uint32_t decode_arm_inst(uint32_t inst, uint8_t *type) {
|
|
if (type)
|
|
*type = INSTRUCTION_UNKNOWN;
|
|
|
|
if (!(BIT_SET(inst, 31) && BIT_SET(inst, 30) && BIT_SET(inst, 29) && !BIT_SET(inst, 28))) {
|
|
return -1;
|
|
}
|
|
|
|
if (BIT_SET(inst, 27)) {
|
|
if (BIT_SET(inst, 26) && BIT_SET(inst, 25) && BIT_SET(inst, 24)) {
|
|
if (type)
|
|
*type = INSTRUCTION_SYSCALL;
|
|
|
|
return 1;
|
|
}
|
|
} else if (!BIT_SET(inst, 26) && BIT_SET(inst, 25)) {
|
|
if (BIT_SET(inst, 24) && !BIT_SET(inst, 23)) {
|
|
if (!(!BIT_SET(inst, 21) && !BIT_SET(inst, 20))) {
|
|
return -1;
|
|
}
|
|
|
|
if (!(BIT_SET(inst, 15) && BIT_SET(inst, 14) && !BIT_SET(inst, 13) && !BIT_SET(inst, 12))) {
|
|
return -1;
|
|
}
|
|
|
|
if (type) {
|
|
if (BIT_SET(inst, 22)) {
|
|
*type = INSTRUCTION_MOVT;
|
|
} else {
|
|
*type = INSTRUCTION_MOVW;
|
|
}
|
|
}
|
|
|
|
return (((inst << 12) >> 28) << 12) | ((inst << 20) >> 20);
|
|
} else if (!BIT_SET(inst, 24) && !BIT_SET(inst, 23)) {
|
|
if (!(BIT_SET(inst, 22) && !BIT_SET(inst, 21) && !BIT_SET(inst, 20) && BIT_SET(inst, 19) && BIT_SET(inst, 18) && BIT_SET(inst, 17) && BIT_SET(inst, 16))) {
|
|
return -1;
|
|
}
|
|
|
|
if (!(BIT_SET(inst, 15) && BIT_SET(inst, 14) && !BIT_SET(inst, 13) && !BIT_SET(inst, 12))) {
|
|
return -1;
|
|
}
|
|
|
|
if (type)
|
|
*type = INSTRUCTION_ADR;
|
|
|
|
return inst & 0xFFF;
|
|
} else if (BIT_SET(inst, 24) && BIT_SET(inst, 23)) {
|
|
if (!(BIT_SET(inst, 22) && BIT_SET(inst, 21))) {
|
|
return -1;
|
|
}
|
|
|
|
if (type)
|
|
*type = INSTRUCTION_MVN;
|
|
|
|
return 0;
|
|
}
|
|
} else if (!BIT_SET(inst, 26) && !BIT_SET(inst, 25)) {
|
|
if ((inst << 7) >> 11 == 0x12FFF1) {
|
|
if (type)
|
|
*type = INSTRUCTION_BRANCH;
|
|
|
|
return 1;
|
|
} else if ((inst << 7) >> 11 == 0x12FFF3) {
|
|
if (type)
|
|
*type = INSTRUCTION_BRANCH;
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
uint32_t encode_arm_inst(uint8_t type, uint16_t immed, uint16_t reg) {
|
|
switch (type) {
|
|
case INSTRUCTION_MOVW:
|
|
return ((uint32_t)0xE30 << 20) | ((uint32_t)(immed & 0xF000) << 4) | (immed & 0xFFF) | (reg << 12);
|
|
|
|
case INSTRUCTION_MOVT:
|
|
return ((uint32_t)0xE34 << 20) | ((uint32_t)(immed & 0xF000) << 4) | (immed & 0xFFF) | (reg << 12);
|
|
|
|
case INSTRUCTION_SYSCALL:
|
|
return (uint32_t)0xEF000000;
|
|
|
|
case INSTRUCTION_BRANCH:
|
|
return ((uint32_t)0xE12FFF1 << 4) | reg;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void makeSyscallStub(uint32_t address, uint16_t syscall) {
|
|
if (!address)
|
|
return;
|
|
|
|
uvl_unlock_mem();
|
|
|
|
uint32_t *f = (uint32_t *)address;
|
|
f[0] = encode_arm_inst(INSTRUCTION_MOVW, syscall, 12);
|
|
f[1] = encode_arm_inst(INSTRUCTION_SYSCALL, 0, 0);
|
|
f[2] = encode_arm_inst(INSTRUCTION_BRANCH, 0, 14);
|
|
f[3] = encode_arm_inst(INSTRUCTION_UNKNOWN, 0, 0);
|
|
|
|
uvl_lock_mem();
|
|
|
|
uvl_flush_icache(f, 0x10);
|
|
}
|
|
|
|
void makeFunctionStub(uint32_t address, void *function) {
|
|
if (!address)
|
|
return;
|
|
|
|
uvl_unlock_mem();
|
|
|
|
uint32_t *f = (uint32_t *)address;
|
|
f[0] = encode_arm_inst(INSTRUCTION_MOVW, (uint16_t)(int)function, 12);
|
|
f[1] = encode_arm_inst(INSTRUCTION_MOVT, (uint16_t)((int)function >> 16), 12);
|
|
f[2] = encode_arm_inst(INSTRUCTION_BRANCH, 0, 12);
|
|
f[3] = encode_arm_inst(INSTRUCTION_UNKNOWN, 0, 0);
|
|
|
|
uvl_lock_mem();
|
|
|
|
uvl_flush_icache(f, 0x10);
|
|
}
|
|
|
|
void makeStub(uint32_t address, void *function) {
|
|
if ((uint32_t)function < MAX_SYSCALL_VALUE) {
|
|
makeSyscallStub(address, (uint16_t)function);
|
|
} else {
|
|
makeFunctionStub(address, function);
|
|
}
|
|
}
|
|
|
|
void copyStub(uint32_t address, void *function) {
|
|
if (!address)
|
|
return;
|
|
|
|
uvl_unlock_mem();
|
|
|
|
memcpy((void *)address, function, 0x10);
|
|
|
|
uvl_lock_mem();
|
|
|
|
uvl_flush_icache((void *)address, 0x10);
|
|
}
|
|
|
|
void makeResultStub(uint32_t address, int result) {
|
|
if (!address)
|
|
return;
|
|
|
|
uvl_unlock_mem();
|
|
|
|
uint32_t *f = (uint32_t *)address;
|
|
f[0] = encode_arm_inst(INSTRUCTION_MOVW, (uint16_t)(int)result, 0);
|
|
f[1] = encode_arm_inst(INSTRUCTION_MOVT, (uint16_t)((int)result >> 16), 0);
|
|
f[2] = encode_arm_inst(INSTRUCTION_BRANCH, 0, 14);
|
|
f[3] = encode_arm_inst(INSTRUCTION_UNKNOWN, 0, 0);
|
|
|
|
uvl_lock_mem();
|
|
|
|
uvl_flush_icache(f, 0x10);
|
|
}
|
|
|
|
void makeArmDummyFunction0(uint32_t address) {
|
|
if (!address)
|
|
return;
|
|
|
|
uvl_unlock_mem();
|
|
|
|
*(uint32_t *)(address + 0x0) = 0xE3E00000; // mvn a1, #0
|
|
*(uint32_t *)(address + 0x4) = 0xE12FFF1E; // bx lr
|
|
*(uint32_t *)(address + 0x8) = 0xE1A00000; // mov a1, r0
|
|
*(uint32_t *)(address + 0xC) = 0x00000000; // nop
|
|
|
|
uvl_lock_mem();
|
|
|
|
uvl_flush_icache((void *)address, 0x10);
|
|
}
|
|
|
|
void makeThumbDummyFunction0(uint32_t address) {
|
|
if (!address)
|
|
return;
|
|
|
|
uvl_unlock_mem();
|
|
|
|
*(uint16_t *)(address + 0x0) = 0x2000; // movs a1, #0
|
|
*(uint16_t *)(address + 0x2) = 0x4770; // bx lr
|
|
|
|
uvl_lock_mem();
|
|
|
|
uvl_flush_icache((void *)address, 0x4);
|
|
}
|
|
|
|
uint32_t extractFunctionStub(uint32_t address) {
|
|
uint8_t type;
|
|
|
|
uint32_t *f = (uint32_t *)address;
|
|
|
|
if (!f)
|
|
return 0;
|
|
|
|
uint32_t movw = decode_arm_inst(f[0], &type);
|
|
if (type != INSTRUCTION_MOVW)
|
|
return 0;
|
|
|
|
uint32_t movt = decode_arm_inst(f[1], &type);
|
|
if (type != INSTRUCTION_MOVT)
|
|
return 0;
|
|
|
|
uint32_t branch = decode_arm_inst(f[2], &type);
|
|
if (type != INSTRUCTION_BRANCH)
|
|
return 0;
|
|
|
|
uint32_t nop = decode_arm_inst(f[3], &type);
|
|
if (type != INSTRUCTION_UNKNOWN)
|
|
return 0;
|
|
|
|
return (movt << 16) | movw;
|
|
}
|
|
|
|
uint32_t extractSyscallStub(uint32_t address) {
|
|
uint8_t type;
|
|
|
|
uint32_t *f = (uint32_t *)address;
|
|
|
|
if (!f)
|
|
return 0;
|
|
|
|
uint32_t movw = decode_arm_inst(f[0], &type);
|
|
if (type != INSTRUCTION_MOVW)
|
|
return 0;
|
|
|
|
uint32_t syscall = decode_arm_inst(f[1], &type);
|
|
if (type != INSTRUCTION_SYSCALL)
|
|
return 0;
|
|
|
|
uint32_t branch = decode_arm_inst(f[2], &type);
|
|
if (type != INSTRUCTION_BRANCH)
|
|
return 0;
|
|
|
|
uint32_t nop = decode_arm_inst(f[3], &type);
|
|
if (type != INSTRUCTION_UNKNOWN)
|
|
return 0;
|
|
|
|
return movw;
|
|
}
|
|
|
|
uint32_t extractStub(uint32_t address) {
|
|
uint32_t value = extractSyscallStub(address);
|
|
|
|
if (!value)
|
|
value = extractFunctionStub(address);
|
|
|
|
return value;
|
|
}
|
|
|
|
int getModuleInfo(SceUID mod, char modname[27], uint32_t *text_addr, uint32_t *text_size) {
|
|
SceKernelModuleInfo info;
|
|
info.size = sizeof(SceKernelModuleInfo);
|
|
if (sceKernelGetModuleInfo(mod, &info) < 0)
|
|
return 0;
|
|
|
|
if (modname)
|
|
strcpy(modname, info.module_name);
|
|
|
|
if (text_addr)
|
|
*text_addr = (uint32_t)info.segments[0].vaddr;
|
|
|
|
if (text_size)
|
|
*text_size = (uint32_t)info.segments[0].memsz;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int findModuleByName(char *name, uint32_t *text_addr, uint32_t *text_size) {
|
|
SceUID mod_list[MAX_MODULES];
|
|
int mod_count = MAX_MODULES;
|
|
|
|
if (sceKernelGetModuleList(0xFF, mod_list, &mod_count) >= 0) {
|
|
int i;
|
|
for (i = mod_count - 1; i >= 0; i--) {
|
|
SceKernelModuleInfo info;
|
|
info.size = sizeof(SceKernelModuleInfo);
|
|
if (sceKernelGetModuleInfo(mod_list[i], &info) >= 0) {
|
|
if (strcmp(info.module_name, name) == 0) {
|
|
return getModuleInfo(mod_list[i], NULL, text_addr, text_size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int findModuleByAddress(uint32_t address, char modname[27], int *section, uint32_t *text_addr, uint32_t *text_size) {
|
|
SceUID mod_list[MAX_MODULES];
|
|
int mod_count = MAX_MODULES;
|
|
|
|
if (sceKernelGetModuleList(0xFF, mod_list, &mod_count) >= 0) {
|
|
int i;
|
|
for (i = mod_count - 1; i >= 0; i--) {
|
|
SceKernelModuleInfo info;
|
|
info.size = sizeof(SceKernelModuleInfo);
|
|
if (sceKernelGetModuleInfo(mod_list[i], &info) >= 0) {
|
|
int j;
|
|
for (j = 0; j < 4; j++) {
|
|
if (address >= (uint32_t)info.segments[j].vaddr && address < ((uint32_t)info.segments[j].vaddr + (uint32_t)info.segments[j].memsz)) {
|
|
*section = j;
|
|
return getModuleInfo(mod_list[i], modname, text_addr, text_size);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SceModuleInfo *findModuleInfo(char *modname, uint32_t text_addr, uint32_t text_size) {
|
|
SceModuleInfo *mod_info = NULL;
|
|
|
|
uint32_t i;
|
|
for (i = 0; i < text_size; i += 4) {
|
|
if (strcmp((char *)(text_addr + i), modname) == 0) {
|
|
mod_info = (SceModuleInfo *)(text_addr + i - 0x4);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return mod_info;
|
|
}
|
|
|
|
uint32_t findModuleExportByInfo(SceModuleInfo *mod_info, uint32_t text_addr, char *libname, uint32_t nid) {
|
|
if (!mod_info)
|
|
return 0;
|
|
|
|
uint32_t i = mod_info->expTop;
|
|
while (i < mod_info->expBtm) {
|
|
SceExportsTable *export = (SceExportsTable *)(text_addr + i);
|
|
|
|
if (export->lib_name && strcmp(export->lib_name, libname) == 0) {
|
|
int j;
|
|
for (j = 0; j < export->num_functions; j++) {
|
|
if (export->nid_table[j] == nid) {
|
|
return (uint32_t)export->entry_table[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
i += export->size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t findModuleExportByName(char *modname, char *libname, uint32_t nid) {
|
|
uint32_t text_addr = 0, text_size = 0;
|
|
if (findModuleByName(modname, &text_addr, &text_size) == 0)
|
|
return 0;
|
|
|
|
return findModuleExportByInfo(findModuleInfo(modname, text_addr, text_size), text_addr, libname, nid);
|
|
}
|
|
|
|
void convertToImportsTable3xx(SceImportsTable2xx *import_2xx, SceImportsTable3xx *import_3xx) {
|
|
memset(import_3xx, 0, sizeof(SceImportsTable3xx));
|
|
|
|
if (import_2xx->size == sizeof(SceImportsTable2xx)) {
|
|
import_3xx->size = import_2xx->size;
|
|
import_3xx->lib_version = import_2xx->lib_version;
|
|
import_3xx->attribute = import_2xx->attribute;
|
|
import_3xx->num_functions = import_2xx->num_functions;
|
|
import_3xx->num_vars = import_2xx->num_vars;
|
|
import_3xx->module_nid = import_2xx->module_nid;
|
|
import_3xx->lib_name = import_2xx->lib_name;
|
|
import_3xx->func_nid_table = import_2xx->func_nid_table;
|
|
import_3xx->func_entry_table = import_2xx->func_entry_table;
|
|
import_3xx->var_nid_table = import_2xx->var_nid_table;
|
|
import_3xx->var_entry_table = import_2xx->var_entry_table;
|
|
} else if (import_2xx->size == sizeof(SceImportsTable3xx)) {
|
|
memcpy(import_3xx, import_2xx, sizeof(SceImportsTable3xx));
|
|
}
|
|
}
|
|
|
|
uint32_t findModuleImportByInfo(SceModuleInfo *mod_info, uint32_t text_addr, char *libname, uint32_t nid) {
|
|
if (!mod_info)
|
|
return 0;
|
|
|
|
uint32_t i = mod_info->impTop;
|
|
while (i < mod_info->impBtm) {
|
|
SceImportsTable3xx import;
|
|
convertToImportsTable3xx((void *)text_addr + i, &import);
|
|
|
|
if (import.lib_name && strcmp(import.lib_name, libname) == 0) {
|
|
int j;
|
|
for (j = 0; j < import.num_functions; j++) {
|
|
if (import.func_nid_table[j] == nid) {
|
|
return (uint32_t)import.func_entry_table[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
i += import.size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t findModuleImportByUID(SceUID mod, char *libname, uint32_t nid) {
|
|
char modname[27];
|
|
uint32_t text_addr = 0, text_size = 0;
|
|
if (getModuleInfo(mod, modname, &text_addr, &text_size) == 0)
|
|
return 0;
|
|
|
|
return findModuleImportByInfo(findModuleInfo(modname, text_addr, text_size), text_addr, libname, nid);
|
|
}
|
|
|
|
uint32_t findModuleImportByValue(char *modname, char *libname, uint32_t value) {
|
|
uint32_t text_addr = 0, text_size = 0;
|
|
if (findModuleByName(modname, &text_addr, &text_size) == 0)
|
|
return 0;
|
|
|
|
SceModuleInfo *mod_info = findModuleInfo(modname, text_addr, text_size);
|
|
if (!mod_info)
|
|
return 0;
|
|
|
|
uint32_t i = mod_info->impTop;
|
|
while (i < mod_info->impBtm) {
|
|
SceImportsTable3xx import;
|
|
convertToImportsTable3xx((void *)text_addr + i, &import);
|
|
|
|
if (import.lib_name && strcmp(import.lib_name, libname) == 0) {
|
|
int j;
|
|
for (j = 0; j < import.num_functions; j++) {
|
|
if (extractStub((uint32_t)import.func_entry_table[j]) == value) {
|
|
return (uint32_t)import.func_entry_table[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
i += import.size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void duplicateModule(char *name, uint32_t *text_addr, uint32_t *text_size) {
|
|
uint32_t ori_text_addr = 0, ori_text_size = 0;
|
|
findModuleByName(name, &ori_text_addr, &ori_text_size);
|
|
|
|
unsigned int length = ALIGN(ori_text_size, 0x100000);
|
|
uint32_t new_text_addr = (uint32_t)uvl_alloc_code_mem(&length);
|
|
|
|
uvl_unlock_mem();
|
|
memcpy((void *)new_text_addr, (void *)ori_text_addr, ori_text_size);
|
|
uvl_lock_mem();
|
|
uvl_flush_icache((void *)new_text_addr, ori_text_size);
|
|
|
|
if (text_addr)
|
|
*text_addr = new_text_addr;
|
|
|
|
if (text_size)
|
|
*text_size = ori_text_size;
|
|
}
|
|
|
|
int dumpModule(SceUID uid) {
|
|
SceKernelModuleInfo info;
|
|
info.size = sizeof(SceKernelModuleInfo);
|
|
int res = sceKernelGetModuleInfo(uid, &info);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
int i;
|
|
for (i = 0; i < 2; i++) {
|
|
if (info.segments[i].vaddr) {
|
|
char *data = malloc(ALIGN(info.segments[i].memsz, 0x1000));
|
|
memcpy(data, info.segments[i].vaddr, info.segments[i].memsz);
|
|
|
|
if (i == 0 && strncmp(info.path, "os0:us/", 7) == 0) {
|
|
SceModuleInfo *mod_info = findModuleInfo(info.module_name, (uint32_t)info.segments[0].vaddr, info.segments[0].memsz);
|
|
if (mod_info) {
|
|
uint32_t i = mod_info->impTop;
|
|
while (i < mod_info->impBtm) {
|
|
SceImportsTable3xx import;
|
|
convertToImportsTable3xx(info.segments[0].vaddr + i, &import);
|
|
|
|
int count_unknown_syscalls = 0;
|
|
|
|
int j;
|
|
for (j = 0; j < import.num_functions; j++) {
|
|
uint32_t value = extractStub((uint32_t)import.func_entry_table[j]);
|
|
uint32_t nid = getNidByValue(value);
|
|
if (nid == 0)
|
|
nid = count_unknown_syscalls++;
|
|
|
|
// Resolve known NIDs
|
|
*(uint32_t *)((uint32_t)data + (uint32_t)&import.func_nid_table[j] - (uint32_t)info.segments[0].vaddr) = nid;
|
|
}
|
|
|
|
i += import.size;
|
|
}
|
|
}
|
|
}
|
|
|
|
char path[MAX_PATH_LENGTH];
|
|
sprintf(path, "cache0:/modules/%s_0x%08X_%d.bin", info.module_name, (unsigned int)info.segments[i].vaddr, i);
|
|
WriteFile(path, data, info.segments[i].memsz);
|
|
|
|
free(data);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int dumpModules() {
|
|
SceUID mod_list[MAX_MODULES];
|
|
int mod_count = MAX_MODULES;
|
|
|
|
int res = sceKernelGetModuleList(0xFF, mod_list, &mod_count);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
removePath("cache0:/modules", NULL, 0, NULL);
|
|
sceIoMkdir("cache0:/modules", 0777);
|
|
|
|
int i;
|
|
for (i = mod_count - 1; i >= 0; i--) {
|
|
dumpModule(mod_list[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void loadDumpModules() {
|
|
removePath("cache0:/modules", NULL, 0, NULL);
|
|
sceIoMkdir("cache0:/modules", 0777);
|
|
|
|
SceUID dfd = sceIoDopen(sys_external_path);
|
|
if (dfd >= 0) {
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
if (!SCE_S_ISDIR(dir.d_stat.st_mode)) {
|
|
char path[128];
|
|
sprintf(path, "%s%s", sys_external_path, dir.d_name);
|
|
|
|
SceUID mod = sceKernelLoadModule(path, 0, NULL);
|
|
if (mod >= 0)
|
|
dumpModule(mod);
|
|
|
|
sceKernelUnloadModule(mod, 0, NULL);
|
|
}
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
}
|
|
|
|
char webcore_path[128];
|
|
sprintf(webcore_path, "%s%s", data_external_path, "webcore");
|
|
|
|
dfd = sceIoDopen(webcore_path);
|
|
if (dfd >= 0) {
|
|
int res = 0;
|
|
|
|
do {
|
|
SceIoDirent dir;
|
|
memset(&dir, 0, sizeof(SceIoDirent));
|
|
|
|
res = sceIoDread(dfd, &dir);
|
|
if (res > 0) {
|
|
if (!SCE_S_ISDIR(dir.d_stat.st_mode)) {
|
|
char path[128];
|
|
sprintf(path, "%s/%s", webcore_path, dir.d_name);
|
|
|
|
SceUID mod = sceKernelLoadModule(path, 0, NULL);
|
|
if (mod >= 0)
|
|
dumpModule(mod);
|
|
|
|
sceKernelUnloadModule(mod, 0, NULL);
|
|
}
|
|
}
|
|
} while (res > 0);
|
|
|
|
sceIoDclose(dfd);
|
|
}
|
|
}
|
|
|
|
uint32_t getValueByNid(uint32_t nid) {
|
|
int i;
|
|
for (i = 0; i < nids_count; i++) {
|
|
if (nid_table[i].nid == nid) {
|
|
return nid_table[i].value;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t getNidByValue(uint32_t value) {
|
|
int i;
|
|
for (i = 0; i < nids_count; i++) {
|
|
if (nid_table[i].value == value) {
|
|
return nid_table[i].nid;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int addNidValue(uint32_t nid, uint32_t value) {
|
|
switch (nid) {
|
|
case 0x79F8E492:
|
|
case 0x913482A9:
|
|
case 0x935CD196:
|
|
case 0x6C2224BA:
|
|
return 0;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < nids_count; i++) {
|
|
if (nid_table[i].nid == nid) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == nids_count) {
|
|
nid_table[nids_count].nid = nid;
|
|
nid_table[nids_count].value = value;
|
|
nids_count++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void addExportNids(SceModuleInfo *mod_info, uint32_t text_addr) {
|
|
uint32_t i = mod_info->expTop;
|
|
while (i < mod_info->expBtm) {
|
|
SceExportsTable *export = (SceExportsTable *)(text_addr + i);
|
|
|
|
int j;
|
|
for (j = 0; j < export->num_functions; j++) {
|
|
uint32_t value = (uint32_t)export->entry_table[j];
|
|
uint32_t nid = export->nid_table[j];
|
|
addNidValue(nid, value);
|
|
}
|
|
|
|
i += export->size;
|
|
}
|
|
}
|
|
|
|
void addImportNids(SceModuleInfo *mod_info, uint32_t text_addr, uint32_t reload_text_addr, int only_syscalls) {
|
|
uint32_t i = mod_info->impTop;
|
|
while (i < mod_info->impBtm) {
|
|
SceImportsTable3xx import;
|
|
convertToImportsTable3xx((void *)text_addr + i, &import);
|
|
|
|
int j;
|
|
for (j = 0; j < import.num_functions; j++) {
|
|
uint32_t value = extractStub((uint32_t)import.func_entry_table[j]);
|
|
uint32_t nid = *(uint32_t *)(reload_text_addr + (uint32_t)&import.func_nid_table[j] - text_addr);
|
|
|
|
if (only_syscalls && value >= MAX_SYSCALL_VALUE)
|
|
continue;
|
|
|
|
addNidValue(nid, value);
|
|
}
|
|
|
|
i += import.size;
|
|
}
|
|
}
|
|
|
|
void addNids(SceUID uid, int only_syscalls) {
|
|
SceKernelModuleInfo info;
|
|
info.size = sizeof(SceKernelModuleInfo);
|
|
if (sceKernelGetModuleInfo(uid, &info) < 0)
|
|
return;
|
|
|
|
// Can be reloaded, so the imports are not resolved yet, skip
|
|
if (sceKernelUnloadModule(uid, 0, NULL) >= 0)
|
|
return;
|
|
|
|
int reload = 1; // If module cannot be reloaded, so its imports cannot be resolved
|
|
SceUID reload_mod = -1;
|
|
char reload_path[MAX_PATH_LENGTH];
|
|
|
|
char reload_modname[27];
|
|
uint32_t reload_text_addr = 0, reload_text_size = 0;
|
|
SceModuleInfo *reload_mod_info;
|
|
|
|
strcpy(reload_path, info.path);
|
|
|
|
if (strncmp(info.path, "os0:us/", 7) == 0) {
|
|
reload = 0;
|
|
} else if (strncmp(info.path, "ux0:/patch/", 11) == 0) {
|
|
// sprintf(reload_path, "app0:/%s", info.path + 21);
|
|
reload = 0;
|
|
} else if (strncmp(info.path, "vs0:sys/external/", 17) == 0) {
|
|
sprintf(reload_path, "%s%s", sys_external_path, info.path + 17);
|
|
}
|
|
|
|
if (reload) {
|
|
reload_mod = sceKernelLoadModule(reload_path, 0, NULL);
|
|
if (reload_mod < 0)
|
|
return;
|
|
|
|
// Get reload module info (NID unpoisoned, syscall unresolved)
|
|
if(!getModuleInfo(reload_mod, reload_modname, &reload_text_addr, &reload_text_size))
|
|
return;
|
|
|
|
reload_mod_info = findModuleInfo(reload_modname, reload_text_addr, reload_text_size);
|
|
if (!reload_mod_info)
|
|
return;
|
|
}
|
|
|
|
// Get module info (NID poisoned, syscall resolved)
|
|
char modname[27];
|
|
uint32_t text_addr = 0, text_size = 0;
|
|
if(!getModuleInfo(uid, modname, &text_addr, &text_size))
|
|
return;
|
|
|
|
SceModuleInfo *mod_info = findModuleInfo(modname, text_addr, text_size);
|
|
if (!mod_info)
|
|
return;
|
|
|
|
// Add import nids
|
|
if (reload) {
|
|
addImportNids(mod_info, text_addr, reload_text_addr, only_syscalls);
|
|
}
|
|
|
|
// Add export nids
|
|
if (!only_syscalls)
|
|
addExportNids(mod_info, text_addr);
|
|
|
|
// Unload
|
|
if (reload)
|
|
sceKernelUnloadModule(reload_mod, 0, NULL);
|
|
}
|
|
|
|
int setupNidTable() {
|
|
SceUID mod_list[MAX_MODULES];
|
|
int mod_count = MAX_MODULES;
|
|
|
|
// Allocate and clear nid table
|
|
if (!nid_table)
|
|
nid_table = malloc(MAX_NIDS * sizeof(NidTable));
|
|
|
|
memset(nid_table, 0, MAX_NIDS * sizeof(NidTable));
|
|
nids_count = 0;
|
|
|
|
// Add preload exports and imports
|
|
int res = sceKernelGetModuleList(0xFF, mod_list, &mod_count);
|
|
if (res < 0)
|
|
return res;
|
|
|
|
int i;
|
|
for (i = mod_count - 1; i >= 0; i--) {
|
|
addNids(mod_list[i], 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void freeNidTable() {
|
|
if (nid_table) {
|
|
free(nid_table);
|
|
nid_table = NULL;
|
|
}
|
|
|
|
nids_count = 0;
|
|
} |