The next step

taiHENkaku is everything you know and love about HENkaku and more. Just like before, you can run your favorite homebrew games, emulators, and tweaks. However, this update brings the addition of plugins as well. Based off of technology similar to Cydia Substrate (specifically thanks to substitute), taiHEN allows developers to write hooks and patches to remix games, system applications, and the kernel.

Getting started

We are still ironing out some issues but you can try an early beta today! Just visit http://beta.henkaku.xyz/ from your PS Vita device to try out the new release. If you wish to wait or go back to the last stable release, just reboot your console and visit the old site, https://henkaku.xyz/.

Please note that currently, the beta is only recommended for developers who wish to test out their own taiHEN plugins. We still recommend the last stable release for everyone else because while we rewrote HENkaku from the ground up to use the new patch system, it does not introduce any new features for the user (and does introduce new instabilities). We hope that developers will use taiHEN to write new mods that would work with taiHENkaku and any future exploits!

Developers

Calling all developers! We need you to create some awesome new tweaks using taiHEN! With the new hooking system, the possibilities are endless: cheats, UI tweaks, screen casting, and more. But we need your help is making it all a reality. Get started by downloading the latest kernel enabled toolchain and the taiHEN library. Check out the API documentation for taiHEN. Join us in the #vitasdk chatroom in FreeNode IRC for help and support.

Building

We will add build instructions for plugins here in the future. Right now, just check out how the HENkaku plugin does it or chat with us in #vitasdk.

Configuration

The configuration that determines the plugins to load and the load order can be found in ux0:tai/config.txt . The format is very simple and self explanatory.

# ignored line starting with # # Kernel plugins are started with taiHEN and are in this section *KERNEL ux0:app/MLCL00001/henkaku.skprx ux0:path/to/another.skprx ux0:tai/plugin3.skprx ux0:data/tai/plugin4.skprx ux0:data/tai/plugin5.skprx # titleid for SceSettings *NPXS10015 ux0:app/MLCL00001/henkaku.suprx ux0:data/tai/some_settings_plugin.suprx # titleid for Package Installer *NPXS10031 ux0:path/to/some_pkg_installer_plgin.suprx # titleid for SceShell is special (does not follow the XXXXYYYYY format) *main ux0:app/MLCL00001/henkaku.skprx ux0:data/tai/shell_plgin.skprx

The key things to note are

# begins a comment, * begins a section, and any other character begins a path. KERNEL is a special section name denoting to load a kernel plugin when taiHEN is started up. All other section names are the title id of the application/game in which to load the plugin at startup. Note that SceShell has a special title id of main . In each section, there is a list of plugin paths that will be loaded in order. Paths can be anywhere but it is recommended that plugins reside in ux0:tai or ux0:data/tai . It is valid to have one plugin in multiple sections but the developer must ensure that the plugin knows which application it is loaded in if it needs to do things differently.

Examples

We hope you will read the API documentation to see the full power of taiHEN and the HENkaku source to see an example usage. However, below are some quick usage examples that demonstrate the power of taiHEN.

Do something on startup

Configure the following user plugin to load on an application named "AppName" with the title id "ABCD01234".

// handle to our hook static tai_hook_ref_t app_start_ref; // our hook for app entry int hook_app_start (SceSize argc, const void *args) { printf ( " hello world!

" ); return TAI_CONTINUE ( int , app_start_ref, argc, args); } // our own plugin entry int module_start (SceSize argc, const void *args) { taiHookFunctionExport (&app_start_ref, // Output a reference " AppName " , // Name of module being hooked TAI_ANY_LIBRARY, // If there's multiple libs exporting this 0x935CD196, // Special NID specifying `module_start` hook_app_start); // Name of the hook function return SCE_KERNEL_START_SUCCESS; }

We can build this as myplugin.suprx and add it to ux0:tai/config.txt under the section *ABCD01234 and it will be loaded when ABCD01234 is started and insert the hook.

Logging Filesystem

The following example will log all file opens from applications. Compile it as kernellog.skprx and add to *KERNEL section in ux0:tai/config.txt .

// handle to our hook static tai_hook_ref_t open_ref; // this function is in kernel space SceUID hook_user_open ( const char *path, int flags, SceMode mode, void *args) { char k_path[ 256 ]; SceUID fd; fd = TAI_CONTINUE (SceUID, open_ref, path, flags, mode, args); // we need to copy the user pointer to kernel space sceKernelStrncpyUserToKernel (k_path, ( uintptr_t )path, 256 ); // do some logging printf ( " opening: %s , res: %x

" , k_path, fd); return fd; } // plugin entry int module_start (SceSize argc, const void *args) { taiHookFunctionExportForKernel (KERNEL_PID, // Kernel process &open_ref, // Output a reference " SceIofilemgr " , // Name of module being hooked TAI_ANY_LIBRARY, // If there's multiple libs exporting this 0xCC67B6FD, // NID specifying `sceIoOpen` hook_user_open); // Name of the hook function return SCE_KERNEL_START_SUCCESS; }

Chain of hooks

Consider one kernel plugin with the code above. Now consider a second kernel plugin as follows.

// handle to our hook static tai_hook_ref_t another_open_ref; // this function is in kernel space SceUID hook_user_open_differently ( const char *path, int flags, SceMode mode, void *args) { char k_path[ 256 ]; SceUID fd; fd = TAI_CONTINUE (SceUID, another_open_ref, path, flags, mode, args); // we need to copy the user pointer to kernel space sceKernelStrncpyUserToKernel (k_path, ( uintptr_t )path, 256 ); // filter out certain paths if ( strcmp (k_path, " ux0:hidden_file.bin " ) == 0 && fd >= 0 ) { sceIoClose (fd); // close the handle fd = SCE_KERNEL_ERROR_NOENT; } return fd; } // another plugin entry int module_start (SceSize argc, const void *args) { taiHookFunctionExportForKernel (KERNEL_PID, // Kernel process &another_open_ref, // Output a reference " SceIofilemgr " , // Name of module being hooked TAI_ANY_LIBRARY, // If there's multiple libs exporting this 0xCC67B6FD, // NID specifying `sceIoOpen` hook_user_open_differently); // Name of the hook function return SCE_KERNEL_START_SUCCESS; }

Now we have both filesystem filtering and logging.

Enabling dynarec

This plugin will be loaded in kernel and changes the return value of a function that does a check to enable dynarec.

// handle to our hook static tai_hook_ref_t some_sysroot_check_hook; // patch function static int some_sysroot_check_patched ( void ) { // It is important that we always call `TAI_CONTINUE` regardless if we need // the return value or not. This ensures other hooks in the chain can run! TAI_CONTINUE ( int , some_sysroot_check_hook); return 1 ; } // plugin entry int module_start (SceSize argc, const void *args) { taiHookFunctionExportForKernel (KERNEL_PID, // Kernel process &some_sysroot_check_hook, // Output a reference " SceSysmem " , // Name of module being hooked 0x3691DA45, // NID specifying `SceSysrootForKernel` 0xF8769E86, // NID of the export function we patch some_sysroot_check_patched); // Name of the hook function return SCE_KERNEL_START_SUCCESS; }

Local file logging

The above examples are all global hooks: every call regardless of origin will be hooked. You can also insert local hooks: a library import from a module. In this example, we have a user plugin loaded with "AppName" that only logs sceIoOpen calls from that application.