Skip to content

Commit

Permalink
Added support for finding the "main module" (resolves #57)
Browse files Browse the repository at this point in the history
In module list search, we now search backwards in the list since the list is in last-loaded-first order and we commonly are looking for older modules
  • Loading branch information
yifanlu committed Dec 13, 2016
1 parent eae6d5d commit ec9f230
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 25 deletions.
1 change: 1 addition & 0 deletions error.h
Expand Up @@ -18,5 +18,6 @@
#define TAI_ERROR_USER_MEMORY 0x90010008
#define TAI_ERROR_NOT_ALLOWED 0x90010009
#define TAI_ERROR_STUB_NOT_RESOLVED 0x9001000A
#define TAI_ERROR_INVALID_MODULE 0x9001000B

#endif // TAI_ERROR_HEADER
29 changes: 20 additions & 9 deletions module.c
Expand Up @@ -217,29 +217,40 @@ static int find_int_for_user(SceUID pid, uintptr_t src, uint32_t needle, size_t
*
* If `name` is NULL, then only the NID is used to locate the loaded
* module. If `name` is not NULL then it will be used to lookup the
* loaded module. If NID is not `TAI_ANY_LIBRARY`, then it will be
* used in the lookup too.
* loaded module. If NID is not `TAI_IGNORE_MODULE_NID`, then it
* will be used in the lookup too. If `name` is NULL and NID is
* `TAI_IGNORE_MODULE_NID` then if there is only one main module, it
* will be returned.
*
* @param[in] pid The pid
* @param[in] name The name to lookup. Can be NULL.
* @param[in] nid The nid to lookup. Can be `TAI_ANY_LIBRARY`.
* @param[in] nid The nid to lookup. Can be `TAI_IGNORE_MODULE_NID`.
* @param[out] info The information
*
* @return Zero on success, < 0 on error
* - TAI_ERROR_INVALID_MODULE if both `name` and NID are undefined
* and there are multiple main modules. KERNEL_PID will always
* return this error.
*/
int module_get_by_name_nid(SceUID pid, const char *name, uint32_t nid, tai_module_info_t *info) {
SceUID modlist[MOD_LIST_SIZE];
void *sceinfo;
size_t count;
int ret;
int get_cur;

get_cur = (name == NULL && nid == 0);
count = MOD_LIST_SIZE;
ret = sceKernelGetModuleListForKernel(pid, 0xff, 1, modlist, &count);
ret = sceKernelGetModuleListForKernel(pid, get_cur ? 1 : 0xff, 1, modlist, &count);
LOG("sceKernelGetModuleListForKernel(%x): 0x%08X, count: %d", pid, ret, count);
if (ret < 0) {
return ret;
}
for (int i = 0; i < count; i++) {
if (get_cur && count > 1) {
LOG("Cannot use TAI_MAIN_MODULE since there are multiple main modules");
return TAI_ERROR_INVALID_MODULE;
}
for (int i = count-1; i >= 0; i--) {
ret = sceKernelGetModuleInternal(modlist[i], &sceinfo);
//LOG("sceKernelGetModuleInternal(%x): 0x%08X", modlist[i], ret);
if (ret < 0) {
Expand All @@ -250,11 +261,11 @@ int module_get_by_name_nid(SceUID pid, const char *name, uint32_t nid, tai_modul
continue;
}
if (name != NULL && strncmp(name, info->name, 27) == 0) {
if (nid == TAI_ANY_LIBRARY || info->modid == nid) {
if (nid == TAI_IGNORE_MODULE_NID || info->modid == nid) {
LOG("Found module %s, NID:0x%08X", name, info->modid);
return TAI_SUCCESS;
}
} else if (name == NULL && info->modid == nid) {
} else if (name == NULL && (get_cur || info->modid == nid)) {
LOG("Found module %s, NID:0x%08X", info->name, info->modid);
return TAI_SUCCESS;
}
Expand Down Expand Up @@ -323,7 +334,7 @@ int module_get_export_func(SceUID pid, const char *modname, uint32_t libnid, uin

LOG("Getting export for pid:%x, modname:%s, libnid:%x, funcnid:%x", pid, modname, libnid, funcnid);
info.size = sizeof(info);
if (module_get_by_name_nid(pid, modname, TAI_ANY_LIBRARY, &info) < 0) {
if (module_get_by_name_nid(pid, modname, TAI_IGNORE_MODULE_NID, &info) < 0) {
LOG("Failed to find module: %s", modname);
return TAI_ERROR_NOT_FOUND;
}
Expand Down Expand Up @@ -389,7 +400,7 @@ int module_get_import_func(SceUID pid, const char *modname, uint32_t target_libn

LOG("Getting import for pid:%x, modname:%s, target_libnid:%x, funcnid:%x", pid, modname, target_libnid, funcnid);
info.size = sizeof(info);
if (module_get_by_name_nid(pid, modname, TAI_ANY_LIBRARY, &info) < 0) {
if (module_get_by_name_nid(pid, modname, TAI_IGNORE_MODULE_NID, &info) < 0) {
LOG("Failed to find module: %s", modname);
return TAI_ERROR_NOT_FOUND;
}
Expand Down
33 changes: 26 additions & 7 deletions taihen-user.c
Expand Up @@ -38,13 +38,16 @@
* hook
* - TAI_ERROR_NOT_IMPLEMENTED if address is in shared memory region
* - TAI_ERROR_USER_MEMORY if pointers are incorrect
* - TAI_ERROR_INVALID_MODULE if `TAI_MAIN_MODULE` is specified and
* there are multiple main modules
*/
SceUID taiHookFunctionExportForUser(tai_hook_ref_t *p_hook, tai_hook_args_t *args) {
tai_hook_args_t kargs;
uint32_t func_nid;
const void *hook_func;
uint32_t state;
char k_module[MAX_NAME_LEN];
int main_mod;
tai_hook_ref_t k_ref;
SceUID kid, ret;
SceUID pid;
Expand All @@ -54,8 +57,9 @@ SceUID taiHookFunctionExportForUser(tai_hook_ref_t *p_hook, tai_hook_args_t *arg
sceKernelMemcpyUserToKernel(&kargs, (uintptr_t)args, sizeof(kargs));
if (kargs.size == sizeof(kargs)) {
pid = sceKernelGetProcessId();
if (sceKernelStrncpyUserToKernel(k_module, (uintptr_t)kargs.module, MAX_NAME_LEN) < MAX_NAME_LEN) {
kid = taiHookFunctionExportForKernel(pid, &k_ref, k_module, kargs.library_nid, kargs.func_nid, kargs.hook_func);
main_mod = (kargs.module == TAI_MAIN_MODULE);
if (main_mod || sceKernelStrncpyUserToKernel(k_module, (uintptr_t)kargs.module, MAX_NAME_LEN) < MAX_NAME_LEN) {
kid = taiHookFunctionExportForKernel(pid, &k_ref, main_mod ? kargs.module : k_module, kargs.library_nid, kargs.func_nid, kargs.hook_func);
if (kid >= 0) {
sceKernelMemcpyKernelToUser((uintptr_t)p_hook, &k_ref, sizeof(*p_hook));
ret = sceKernelCreateUserUid(pid, kid);
Expand Down Expand Up @@ -95,11 +99,14 @@ SceUID taiHookFunctionExportForUser(tai_hook_ref_t *p_hook, tai_hook_args_t *arg
* 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 `TAI_MAIN_MODULE` is specified and
* there are multiple main modules
*/
SceUID taiHookFunctionImportForUser(tai_hook_ref_t *p_hook, tai_hook_args_t *args) {
tai_hook_args_t kargs;
uint32_t state;
char k_module[MAX_NAME_LEN];
int main_mod;
tai_hook_ref_t k_ref;
SceUID kid, ret;
SceUID pid;
Expand All @@ -108,8 +115,9 @@ SceUID taiHookFunctionImportForUser(tai_hook_ref_t *p_hook, tai_hook_args_t *arg
sceKernelMemcpyUserToKernel(&kargs, (uintptr_t)args, sizeof(kargs));
if (kargs.size == sizeof(kargs)) {
pid = sceKernelGetProcessId();
if (sceKernelStrncpyUserToKernel(k_module, (uintptr_t)kargs.module, MAX_NAME_LEN) < MAX_NAME_LEN) {
kid = taiHookFunctionImportForKernel(pid, &k_ref, k_module, kargs.library_nid, kargs.func_nid, kargs.hook_func);
main_mod = (kargs.module == TAI_MAIN_MODULE);
if (main_mod || sceKernelStrncpyUserToKernel(k_module, (uintptr_t)kargs.module, MAX_NAME_LEN) < MAX_NAME_LEN) {
kid = taiHookFunctionImportForKernel(pid, &k_ref, main_mod ? kargs.module : k_module, kargs.library_nid, kargs.func_nid, kargs.hook_func);
if (kid >= 0) {
sceKernelMemcpyKernelToUser((uintptr_t)p_hook, &k_ref, sizeof(*p_hook));
ret = sceKernelCreateUserUid(pid, kid);
Expand Down Expand Up @@ -180,16 +188,26 @@ SceUID taiHookFunctionOffsetForUser(tai_hook_ref_t *p_hook, tai_offset_args_t *a
/**
* @brief Gets information on a currently loaded module
*
* You can use the macro `TAI_MAIN_MODULE` for `module` to specify
* the main module. This is usually the module that is loaded first
* and is usually the eboot.bin. This will only work if there is
* only one module loaded in the main memory space. Not all
* processes have this property! Make sure you check the return
* value.
*
* @see taiGetModuleInfoForKernel
*
* @param[in] module The name of the module
* @param[in] module The name of the module or `TAI_MAIN_MODULE`.
* @param[out] info The information to fill
*
* @return Zero on success, < 0 on error
* - TAI_ERROR_USER_MEMORY if `info->size` is too small or large or
* `module` is invalid
* - TAI_ERROR_INVALID_MODULE if `TAI_MAIN_MODULE` is specified and
* there are multiple main modules
*/
int taiGetModuleInfo(const char *module, tai_module_info_t *info) {
int main_mod;
char k_module[MAX_NAME_LEN];
uint32_t state;
SceUID pid;
Expand All @@ -198,10 +216,11 @@ int taiGetModuleInfo(const char *module, tai_module_info_t *info) {

ENTER_SYSCALL(state);
pid = sceKernelGetProcessId();
if (sceKernelStrncpyUserToKernel(k_module, (uintptr_t)module, MAX_NAME_LEN) < MAX_NAME_LEN) {
main_mod = (module == TAI_MAIN_MODULE);
if (main_mod || sceKernelStrncpyUserToKernel(k_module, (uintptr_t)module, MAX_NAME_LEN) < MAX_NAME_LEN) {
sceKernelMemcpyUserToKernel(&k_info, (uintptr_t)info, sizeof(size_t));
if (k_info.size == sizeof(k_info)) {
ret = taiGetModuleInfoForKernel(pid, k_module, &k_info);
ret = taiGetModuleInfoForKernel(pid, main_mod ? module : k_module, &k_info);
sceKernelMemcpyKernelToUser((uintptr_t)info, &k_info, k_info.size);
} else {
ret = TAI_ERROR_USER_MEMORY;
Expand Down
19 changes: 14 additions & 5 deletions taihen.c
Expand Up @@ -54,15 +54,20 @@ SceUID taiHookFunctionAbs(SceUID pid, tai_hook_ref_t *p_hook, void *dest_func, c
* @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 0, then the
* first export with the NID will be hooked.
* @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_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;
Expand Down Expand Up @@ -104,6 +109,8 @@ SceUID taiHookFunctionExportForKernel(SceUID pid, tai_hook_ref_t *p_hook, const
* 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;
Expand Down Expand Up @@ -178,9 +185,11 @@ SceUID taiHookFunctionOffsetForKernel(SceUID pid, tai_hook_ref_t *p_hook, SceUID
* @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_ANY_LIBRARY, info);
return module_get_by_name_nid(pid, module, TAI_IGNORE_MODULE_NID, info);
}

/**
Expand Down
32 changes: 28 additions & 4 deletions taihen.h
Expand Up @@ -36,6 +36,12 @@ extern "C" {
/** Fake library NID indicating that any library NID would match. */
#define TAI_ANY_LIBRARY 0xFFFFFFFF

/** Fake module NID indicating that any module NID would match. */
#define TAI_IGNORE_MODULE_NID 0xFFFFFFFF

/** Fake module name indicating the current process's main module. */
#define TAI_MAIN_MODULE ((void *)0)

/** Functions for calling the syscalls with arguments */
#define HELPER inline static __attribute__((unused))

Expand Down Expand Up @@ -243,14 +249,24 @@ int taiHookRelease(SceUID tai_uid, tai_hook_ref_t hook);
/**
* @brief Helper function for #taiHookFunctionExportForUser
*
* You can use the macro `TAI_MAIN_MODULE` for `module` to specify
* the main module. This is usually the module that is loaded first
* and is usually the eboot.bin. This will only work if there is
* only one module loaded in the main memory space. Not all
* processes have this property! Make sure you check the return
* value.
*
* @see taiHookFunctionExportForUser
*
* @param[out] p_hook A reference that can be used by the hook function
* @param[in] module Name of the target module.
* @param[in] module Name of the target module or `TAI_MAIN_MODULE`.
* @param[in] library_nid Optional. NID of the target library.
* @param[in] func_nid The function NID. If `library_nid` is 0, then the
* first export with the NID will be hooked.
* @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
*
* @return { description_of_the_return_value }
*/
HELPER SceUID taiHookFunctionExport(tai_hook_ref_t *p_hook, const char *module, uint32_t library_nid, uint32_t func_nid, const void *hook_func) {
tai_hook_args_t args;
Expand All @@ -265,11 +281,19 @@ HELPER SceUID taiHookFunctionExport(tai_hook_ref_t *p_hook, const char *module,
/**
* @brief Helper function for #taiHookFunctionImportForUser
*
* You can use the macro `TAI_MAIN_MODULE` for `module` to specify
* the main module. This is usually the module that is loaded first
* and is usually the eboot.bin. This will only work if there is
* only one module loaded in the main memory space. Not all
* processes have this property! Make sure you check the return
* value.
*
* @see taiHookFunctionImportForUser
*
* @param[out] p_hook A reference that can be used by the hook
* function
* @param[in] module Name of the target module.
* @param[in] module Name of the target module or
* `TAI_MAIN_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
Expand Down

0 comments on commit ec9f230

Please sign in to comment.