/* Copyright (C) 2025 OrionHEN / LightningMods 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, 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; see the file COPYING. If not, see . */ #include "Detour.h" #include "HookedFuncs.hpp" #include "appinst_types.hpp" #include "defs.h" #include "external_symbols.hpp" #include "ipc.hpp" #include "proc.h" #include "ps5/kernel.h" #include "ucred.h" #include "webserver.hpp" #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // Globals (referenced by other shellui translation units) // --------------------------------------------------------------------------- std::string dec_xml_str; std::string UI3_dec; std::string legacy_dec; std::string appsystem_dll; std::string uilib; std::string Sysinfo; std::string display_info; std::string uilib_dll; std::string plugin_xml; std::string remote_play_xml; std::string debug_settings_xml; std::string cheats_xml; MonoImage* pui_img = nullptr; MonoImage* AppSystem_img = nullptr; MonoObject* Game = nullptr; MonoImage* react_common_img = nullptr; bool hooked = false; bool has_hv_bypass = false; bool is_6xx = false; bool is_3xx = false; extern "C" long ptr_syscall = 0; void __syscall() { asm(".intel_syntax noprefix\n" " mov rax, rdi\n" " mov rdi, rsi\n" " mov rsi, rdx\n" " mov rdx, rcx\n" " mov r10, r8\n" " mov r8, r9\n" " mov r9, qword ptr [rsp + 8]\n" " call qword ptr [rip + ptr_syscall]\n" " ret\n"); } void (*OnRender_orig)(MonoObject* instance); MonoObject* rootWidget = nullptr; MonoObject* font = nullptr; void (*Orig_ReloadApp)(MonoString* str) = nullptr; // Defined in prx_install / prx_overlay void ReloadApp(MonoString* str); void OnRender_Hook(MonoObject* instance); void* dialogue_thread(void* arg); int KillAppWithReason_Hook(int appId, int reason); extern ssize_t (*read_orig)(int fd, void* buf, size_t count); extern ssize_t read_hook(int fd, void* buf, size_t count); extern int (*sceAppInstUtilInstallByPackage_orig)(MetaInfo* arg1, SceAppInstallPkgInfo* pkg_info, PlayGoInfo* arg2); int sceAppInstUtilInstallByPackage_hook(MetaInfo* arg1, SceAppInstallPkgInfo* pkg_info, PlayGoInfo* arg2); // --------------------------------------------------------------------------- // Init constants // --------------------------------------------------------------------------- namespace { constexpr int kSyscallInstrOffset = 0x0A; constexpr size_t kMprotectProbeSize = 100; constexpr int kProtRwx = 0x7; // PROT_READ | PROT_WRITE | PROT_EXEC constexpr uint32_t kFw3xxMaxExclusive = 0x4000042; constexpr uint32_t kFw6xxMin = 0x6000000; // Same magnitude as the previous sleep(0x100000) keep-alive. constexpr unsigned kKeepAliveSleepSec = 0x100000; // XOR key material used for version string + toolbox XML (base64 of "SISTR0_I_SEE_YOU"). constexpr const char* kXorKeyB64 = "U0lTVFIwX0lfU0VFX1lPVQ=="; // --------------------------------------------------------------------------- // RAII: restore process authid on every exit path // --------------------------------------------------------------------------- struct AuthIdGuard { pid_t pid; uintptr_t old_authid; bool active = true; AuthIdGuard(pid_t p, uintptr_t auth) : pid(p), old_authid(auth) {} ~AuthIdGuard() { if (active) set_proc_authid(pid, old_authid); } void release() { active = false; } AuthIdGuard(const AuthIdGuard&) = delete; AuthIdGuard& operator=(const AuthIdGuard&) = delete; }; // --------------------------------------------------------------------------- // Detour helpers // --------------------------------------------------------------------------- template bool install_detour(const char* name, uint64_t target, void* hook, Fn& out_orig, bool required) { if (!target) { shellui_log("Hook target missing: %s", name); if (required) notify("Failed to find hook target"); return !required; } void* trampoline = DetourFunction(target, hook); if (!trampoline) { shellui_log("Detour failed: %s", name); if (required) notify("Failed to install hook"); return !required; } out_orig = reinterpret_cast(trampoline); return true; } template bool install_detour_native(const char* name, void* target, void* hook, Fn& out_orig, bool required) { return install_detour(name, reinterpret_cast(target), hook, out_orig, required); } // --------------------------------------------------------------------------- // Mono images used during init // --------------------------------------------------------------------------- struct ShellImages { MonoImage* legacy = nullptr; MonoImage* mscorlib = nullptr; MonoImage* react_pui = nullptr; MonoImage* app_system = nullptr; MonoImage* core = nullptr; MonoImage* capture_menu = nullptr; MonoImage* lnc = nullptr; // optional MonoImage* react_common = nullptr; MonoImage* rn_shell = nullptr; MonoImage* app_install = nullptr; MonoImage* pui = nullptr; }; MonoImage* require_dll(const char* name) { MonoImage* img = getDLLimage(name); if (!img) { shellui_log("Failed to load Mono assembly: %s", name); notify("Failed to load assembly"); } return img; } // --------------------------------------------------------------------------- // Phase 1: native dynlib symbols // --------------------------------------------------------------------------- bool resolve_native_symbols(pid_t pid, void*& out_read, void*& out_sceAppInstUtilInstallByPackage) { // Locals required by KERNEL_DLSYM (writes into a named variable). static int (*sceAppInstUtilInstallByPackage)(MetaInfo*, SceAppInstallPkgInfo*, PlayGoInfo*) = nullptr; static ssize_t (*read)(int, void*, size_t) = nullptr; int appinstaller = get_module_handle(pid, "libSceAppInstUtil.sprx"); KERNEL_DLSYM(appinstaller, sceAppInstUtilInstallByPackage); out_sceAppInstUtilInstallByPackage = reinterpret_cast(sceAppInstUtilInstallByPackage); int libkernelsys = get_module_handle(pid, "libkernel_sys.sprx"); KERNEL_DLSYM(libkernelsys, sceKernelDebugOutText); KERNEL_DLSYM(libkernelsys, sceKernelMkdir); KERNEL_DLSYM(libkernelsys, scePthreadCreate); KERNEL_DLSYM(libkernelsys, sceKernelMprotect); KERNEL_DLSYM(libkernelsys, sceKernelSendNotificationRequest); KERNEL_DLSYM(libkernelsys, sceKernelGetProsperoSystemSwVersion); KERNEL_DLSYM(libkernelsys, sceKernelGetAppInfo); KERNEL_DLSYM(libkernelsys, sceKernelGetProcessName); KERNEL_DLSYM(libkernelsys, read); out_read = reinterpret_cast(read); KERNEL_DLSYM(libSceKernelHandle, sceKernelJitCreateSharedMemory); KERNEL_DLSYM(libSceKernelHandle, sceKernelJitCreateAliasOfSharedMemory); KERNEL_DLSYM(libSceKernelHandle, sceKernelJitMapSharedMemory); KERNEL_DLSYM(libSceKernelHandle, ioctl); KERNEL_DLSYM(libSceKernelHandle, __sys_regmgr_call); // Point ptr_syscall at the syscall instruction inside getpid. // Variable name must be `getpid` for KERNEL_DLSYM's #sym string. { static __attribute__((used)) long getpid = 0; KERNEL_DLSYM(libSceKernelHandle, getpid); ptr_syscall = getpid + kSyscallInstrOffset; } int shellui_util = get_module_handle(pid, "libSceShellUIUtil.sprx"); KERNEL_DLSYM(shellui_util, sceShellUIUtilLaunchByUri); KERNEL_DLSYM(shellui_util, sceShellUIUtilInitialize); int system_service = get_module_handle(pid, "libSceSystemService.sprx"); KERNEL_DLSYM(system_service, sceSystemServiceGetAppIdOfRunningBigApp); KERNEL_DLSYM(system_service, sceSystemServiceGetAppTitleId); { void* sceSystemServiceLaunchApp = nullptr; KERNEL_DLSYM(system_service, sceSystemServiceLaunchApp); if (!sceSystemServiceLaunchApp) shellui_log("Failed to resolve sceSystemServiceLaunchApp"); } int remote_play = get_module_handle(pid, "libSceRemoteplay.sprx"); KERNEL_DLSYM(remote_play, sceRemoteplayNotifyPinCodeError); KERNEL_DLSYM(remote_play, sceRemoteplayInitialize); KERNEL_DLSYM(remote_play, sceRemoteplayGeneratePinCode); KERNEL_DLSYM(remote_play, sceRemoteplayConfirmDeviceRegist); int reg = get_module_handle(pid, "libSceRegMgr.sprx"); KERNEL_DLSYM(reg, sceRegMgrGetInt); return true; } // --------------------------------------------------------------------------- // Phase 2: mono runtime symbols // --------------------------------------------------------------------------- bool resolve_mono_symbols(pid_t pid) { int libmono = get_module_handle(pid, "libmonosgen-2.0.sprx"); KERNEL_DLSYM(libmono, mono_object_to_string); KERNEL_DLSYM(libmono, mono_get_root_domain); KERNEL_DLSYM(libmono, mono_property_get_get_method); KERNEL_DLSYM(libmono, mono_property_get_set_method); KERNEL_DLSYM(libmono, mono_class_get_property_from_name); KERNEL_DLSYM(libmono, mono_class_from_name); KERNEL_DLSYM(libmono, mono_raise_exception); KERNEL_DLSYM(libmono, mono_runtime_invoke); KERNEL_DLSYM(libmono, mono_array_new); KERNEL_DLSYM(libmono, mono_string_new); KERNEL_DLSYM(libmono, mono_jit_set_aot_only); KERNEL_DLSYM(libmono, mono_jit_init_version); KERNEL_DLSYM(libmono, mono_object_new); KERNEL_DLSYM(libmono, mono_object_unbox); KERNEL_DLSYM(libmono, mono_set_dirs); KERNEL_DLSYM(libmono, mono_compile_method); KERNEL_DLSYM(libmono, mono_assembly_get_image); KERNEL_DLSYM(libmono, mono_domain_assembly_open); KERNEL_DLSYM(libmono, mono_get_byte_class); KERNEL_DLSYM(libmono, mono_thread_attach); KERNEL_DLSYM(libmono, mono_object_get_class); KERNEL_DLSYM(libmono, mono_vtable_get_static_field_data); KERNEL_DLSYM(libmono, mono_class_get_method_from_name); KERNEL_DLSYM(libmono, mono_class_get_field_from_name); KERNEL_DLSYM(libmono, mono_aot_get_method); KERNEL_DLSYM(libmono, mono_field_static_set_value); KERNEL_DLSYM(libmono, mono_assembly_setrootdir); KERNEL_DLSYM(libmono, mono_free); KERNEL_DLSYM(libmono, mono_gchandle_new); KERNEL_DLSYM(libmono, mono_image_open_from_data); KERNEL_DLSYM(libmono, mono_runtime_object_init); KERNEL_DLSYM(libmono, mono_domain_get); KERNEL_DLSYM(libmono, mono_assembly_load_from); KERNEL_DLSYM(libmono, mono_method_desc_new); KERNEL_DLSYM(libmono, mono_method_desc_search_in_class); KERNEL_DLSYM(libmono, mono_method_desc_free); KERNEL_DLSYM(libmono, mono_object_new_specific); KERNEL_DLSYM(libmono, mono_thread_detach); KERNEL_DLSYM(libmono, mono_array_addr_with_size); KERNEL_DLSYM(libmono, mono_thread_current); KERNEL_DLSYM(libmono, mono_class_vtable); KERNEL_DLSYM(libmono, mono_domain_unload); KERNEL_DLSYM(libmono, mono_string_to_utf8); const bool ok = mono_object_to_string && mono_get_root_domain && mono_property_get_get_method && mono_property_get_set_method && mono_class_get_property_from_name && mono_class_from_name && mono_runtime_invoke && mono_array_new && mono_string_new && mono_jit_set_aot_only && mono_jit_init_version && mono_object_new && mono_object_unbox && mono_set_dirs && mono_compile_method && mono_assembly_get_image && mono_domain_assembly_open && mono_get_byte_class && mono_thread_attach && mono_object_get_class && mono_vtable_get_static_field_data && mono_class_get_method_from_name && mono_class_get_field_from_name && mono_aot_get_method && mono_field_static_set_value && mono_assembly_setrootdir && mono_free && mono_gchandle_new && mono_image_open_from_data && mono_runtime_object_init && mono_domain_get && mono_assembly_load_from && mono_method_desc_new && mono_method_desc_search_in_class && mono_method_desc_free && mono_object_new_specific && mono_thread_detach && mono_array_addr_with_size && mono_thread_current && mono_class_vtable && mono_domain_unload && mono_string_to_utf8; if (!ok) shellui_log("Failed to resolve mono symbols"); return ok; } // --------------------------------------------------------------------------- // Phase 3: resource / type name strings (kept as base64 at rest) // --------------------------------------------------------------------------- void init_resource_names() { // Manifest resource paths (used by GetManifestResourceStream hook) plugin_xml = base64_decode( "U2NlLlZzaC5TaGVsbFVJLkxlZ2FjeS5zcmMuU2NlLlZzaC5Ta" "GVsbFVJLlNldHRpbmdzLlBsdWdpbnMucGx1Z2lucy54bWw="); cheats_xml = base64_decode( "U2NlLlZzaC5TaGVsbFVJLkxlZ2FjeS5zcmMuU2NlLlZzaC5Ta" "GVsbFVJLlNldHRpbmdzLlBsdWdpbnMuY2hlYXRzLnhtbA=="); remote_play_xml = base64_decode( "U2NlLlZzaC5TaGVsbFVJLkxlZ2FjeS5zcmMuU2NlLlZzaC5TaGVsbFVJLlNldHRpbmdzLlBs" "dWdpbnMucmVtb3RlX3BsYXkueG1s"); debug_settings_xml = base64_decode( "U2NlLlZzaC5TaGVsbFVJLkxlZ2FjeS5zcmMuU2NlLlZzaC5TaGVsbFVJLlNldHRpbmdzLlBs" "dWdpbnMuRGVidWdTZXR0aW5ncy5kYXRhLmRlYnVnX3NldHRpbmdzLnhtbA=="); // Assembly / type names used across hooks legacy_dec = base64_decode("U2NlLlZzaC5TaGVsbFVJLkxlZ2FjeS5kbGw="); // Legacy.dll UI3_dec = base64_decode("U2NlLlZzaC5TaGVsbFVJLlNldHRpbmdzLkNvcmVVSTM="); // Settings.CoreUI3 appsystem_dll = base64_decode("U2NlLlZzaC5TaGVsbFVJLkFwcFN5c3RlbS5kbGw="); uilib = base64_decode("U2NlLlZzaC5VSUxpYg=="); Sysinfo = base64_decode("U3lzdGVtU29mdHdhcmVWZXJzaW9uSW5mbw=="); display_info = base64_decode("c2V0X0Rpc3BsYXlWZXJzaW9u"); uilib_dll = base64_decode( "L3N5c3RlbV9leC9jb21tb25fZXgvbGliL1NjZS5Wc2guVUlMaWIuZGxs"); shellui_log("[GMRS-INIT] expected resource names:"); shellui_log("[GMRS-INIT] debug_settings_xml=\"%s\"", debug_settings_xml.c_str()); shellui_log("[GMRS-INIT] plugin_xml=\"%s\"", plugin_xml.c_str()); shellui_log("[GMRS-INIT] cheats_xml=\"%s\"", cheats_xml.c_str()); shellui_log("[GMRS-INIT] remote_play_xml=\"%s\"", remote_play_xml.c_str()); } // --------------------------------------------------------------------------- // Phase 4: version banner + toolbox XML decrypt // --------------------------------------------------------------------------- bool init_version_string(const OrbisKernelSwVersion& sw) { const char enc_ver[] = "\x30\x44\x0d\x1c\x13\x08\x69\x35\x3d\x44\x0d\x46"; const std::string key = base64_decode(kXorKeyB64); auto dev_ver_bytes = encrypt_decrypt(reinterpret_cast(enc_ver), sizeof(enc_ver) - 1, key); std::string dec_ver(dev_ver_bytes.begin(), dev_ver_bytes.end()); dec_ver += OrionHEN_VERSION; std::string final_ver; #if PUBLIC_TEST == 1 final_ver = dec_ver + "-PUBLIC_TEST (" + sw.version_str + " )"; #elif PRE_RELEASE == 1 final_ver = dec_ver + " PRE_RELEASE (" + sw.version_str + " )"; #else final_ver = dec_ver + " (" + sw.version_str + " )"; #endif shellui_log("Decrypted Version: %s", final_ver.c_str()); if (!SetVersionString(final_ver.c_str())) { shellui_log("Failed to set version string"); return false; } return true; } bool decrypt_toolbox_xml() { const int size = static_cast(reinterpret_cast(&toolbox_end) - reinterpret_cast(&toolbox_start)); shellui_log("[GMRS-INIT] decrypting toolbox XML embed: blob=%d", size); const std::string key = base64_decode(kXorKeyB64); auto decrypted = encrypt_decrypt(toolbox_start, size, key); dec_xml_str.assign(decrypted.begin(), decrypted.end()); shellui_log("[GMRS-INIT] decrypted toolbox XML: full=%zu full_prefix=%.60s", dec_xml_str.size(), dec_xml_str.empty() ? "(empty)" : dec_xml_str.c_str()); if (dec_xml_str.empty() || dec_xml_str.find("system_settings") == std::string::npos) { shellui_log("[GMRS-INIT] WARN: full toolbox XML missing/invalid after decrypt!"); } return true; } // --------------------------------------------------------------------------- // Phase 5: load Mono assemblies // --------------------------------------------------------------------------- bool load_shell_images(ShellImages& out) { out.legacy = require_dll(legacy_dec.c_str()); out.mscorlib = require_dll("mscorlib.dll"); out.react_pui = require_dll("ReactNative.PUI.dll"); out.app_system = require_dll(appsystem_dll.c_str()); out.core = require_dll("Sce.PlayStation.Core.dll"); out.capture_menu = require_dll("Sce.Vsh.ShellUI.CaptureMenu.dll"); // Optional: LaunchApp / KillAppWithReason only when present out.lnc = getDLLimage("Sce.Vsh.LncUtilWrapper.dll"); if (!out.lnc) notify("Failed to get LncUtilWrapper image"); out.react_common = require_dll("ReactNative.Vsh.Common.dll"); out.rn_shell = require_dll("Sce.Vsh.ShellUI.ReactNativeShellApp.dll"); out.app_install = require_dll("Sce.Vsh.AppInstUtilWrapper.dll"); out.pui = require_dll("Sce.PlayStation.PUI.dll"); // Publish images other modules need AppSystem_img = out.app_system; react_common_img = out.react_common; pui_img = out.pui; return out.legacy && out.mscorlib && out.react_pui && out.app_system && out.core && out.capture_menu && out.react_common && out.rn_shell && out.app_install && out.pui; } // --------------------------------------------------------------------------- // Phase 6: resolve Game container scene // --------------------------------------------------------------------------- bool resolve_game_container(MonoImage* app_system) { MonoClass* layer_manager = mono_class_from_name(app_system, "Sce.Vsh.ShellUI.AppSystem", "LayerManager"); if (!layer_manager) { notify("Failed to get LayerManager class"); return false; } MonoMethod* find = mono_class_get_method_from_name(layer_manager, "FindContainerSceneByPath", 1); if (!find) { notify("Failed to get FindContainerSceneByPath method"); return false; } MonoString* path_arg = mono_string_new(mono_domain_get(), "Game"); void* args[1] = {path_arg}; MonoObject* exception = nullptr; Game = mono_runtime_invoke(find, nullptr, args, &exception); if (exception) { notify("Exception occurred while calling FindContainerSceneByPath"); return false; } if (!Game) { notify("Failed to get Game ContainerScene"); return false; } shellui_log("Game ContainerScene: %p", Game); return true; } // --------------------------------------------------------------------------- // Phase 7: install hooks // --------------------------------------------------------------------------- bool install_hooks(const ShellImages& img, void* read_fn) { char probe[kMprotectProbeSize]; has_hv_bypass = (sceKernelMprotect(probe, sizeof(probe), kProtRwx) == 0); shellui_log("has_hv_bypass=%d (mprotect for detours)", has_hv_bypass ? 1 : 0); Patch_Main_thread_Check(img.core); // --- PUI render --- OnRender_orig = reinterpret_cast(DetourFunction( Get_Address_of_Method(img.pui, "Sce.PlayStation.PUI", "Application", "Update", 0), reinterpret_cast(&OnRender_Hook))); // --- Navigation (optional diagnostics) --- { uint64_t addr = Get_Address_of_Method( img.react_pui, "ReactNative.Views.UI3.View", "ReactNavigatorManager", "UpdateNavigationState", 1); if (addr) { ReactNavigatorManager_UpdateNavigationState_Orig = reinterpret_cast( DetourFunction(addr, reinterpret_cast( &ReactNavigatorManager_UpdateNavigationState_Hook))); shellui_log(ReactNavigatorManager_UpdateNavigationState_Orig ? "[DBG-NAV] UpdateNavigationState hooked" : "[DBG-NAV] failed to detour UpdateNavigationState"); } else { shellui_log("[DBG-NAV] UpdateNavigationState not found"); } } // --- Registry (required) --- if (!sceRegMgrGetInt) { notify("Failed to find sceRegMgrGetInt"); return false; } if (!install_detour_native("sceRegMgrGetInt", reinterpret_cast(sceRegMgrGetInt), reinterpret_cast(&sceRegMgrGetInt_hook), sceRegMgrGetInt, true)) return false; // --- libc read (required) --- if (!install_detour_native("read", read_fn, reinterpret_cast(&read_hook), read_orig, true)) return false; // --- Option menu createJson (required) --- { uint64_t addr = Get_Address_of_Method( img.rn_shell, "ReactNative.Modules.ShellUI.HomeUI", "OptionMenu", "createJson", 8); if (!install_detour("OptionMenu.createJson", addr, reinterpret_cast(&createJson_hook), createJson, true)) return false; } // --- DebugSettings.GetModel (optional) --- { uint64_t addr = Get_Address_of_Method( img.rn_shell, "ReactNative.Modules.ShellUI.Settings", "DebugSettingsModule", "GetModel", 2); if (addr) { DebugSettings_GetModel_Orig = reinterpret_cast( DetourFunction(addr, reinterpret_cast(&DebugSettings_GetModel_Hook))); shellui_log(DebugSettings_GetModel_Orig ? "[DBG-GETMODEL] GetModel hooked" : "[DBG-GETMODEL] failed to detour GetModel"); } else { shellui_log("[DBG-GETMODEL] GetModel not found"); } } // --- LncUtil (optional when image missing) --- if (img.lnc) { (void)install_detour( "LncUtilWrapper.LaunchApp", Get_Address_of_Method(img.lnc, "Sce.Vsh.LncUtil", "LncUtilWrapper", "LaunchApp", 4), reinterpret_cast(&LaunchApp), LaunchApp_orig, false); void* kill_tramp = DetourFunction( Get_Address_of_Method(img.lnc, "Sce.Vsh.LncUtil", "LncUtilWrapper", "KillAppWithReason", 2), reinterpret_cast(&KillAppWithReason_Hook)); if (!kill_tramp) notify("Failed to detour KillAppWithReason"); } // --- Impose / GamePad --- (void)install_detour( "LayerManager.UpdateImposeStatusFlag", Get_Address_of_Method(img.app_system, "Sce.Vsh.ShellUI.AppSystem", "LayerManager", "UpdateImposeStatusFlag", 2), reinterpret_cast(&UpdateImposeStatusFlag_hook), UpdateImposeStatusFlag_Orig, false); if (!install_detour("GamePad.GetData", Get_Address_of_Method(img.core, "Sce.PlayStation.Core.Input", "GamePad", "GetData", 1), reinterpret_cast(&GetData_hook), GetData, true)) return false; // --- Settings UI hooks --- (void)install_detour( "SettingsPlugin.CxmlUri", Get_Address_of_Method(img.legacy, UI3_dec.c_str(), "SettingsPlugin", "CxmlUri", 1), reinterpret_cast(&CxmlUri_Hook), CxmlUri, false); // Bundle decrypt; fall back to ioctl detour if the Mono method is unavailable { const uint64_t addr = Get_Address_of_Method( img.react_pui, "ReactNative.PlayStation.Security", "JavaScriptBundleDecryptor", "Decrypt", 5); (void)install_detour("JavaScriptBundleDecryptor.Decrypt", addr, reinterpret_cast(&CallDecrypt), CallDecrypt_orig, false); if (!CallDecrypt_orig) { if (!ioctl) { notify("Failed to find decrypt workaround"); return false; } shellui_log("Found ioctl at %p", ioctl); DetourFunction(reinterpret_cast(ioctl), reinterpret_cast(&ioctl_hook)); shellui_log("Detoured ioctl to ioctl_hook"); } } (void)install_detour( "SettingPage.OnPressed", Get_Address_of_Method(img.legacy, UI3_dec.c_str(), "SettingPage", "OnPressed", 2), reinterpret_cast(&OnPress_Hook), oOnPress, false); // BootHelper.Boot: try 3-arg then 2-arg signature { const uint64_t boot3 = Get_Address_of_Method( img.app_system, "Sce.Vsh.ShellUI.AppSystem", "BootHelper", "Boot", 3); (void)install_detour("BootHelper.Boot(3)", boot3, reinterpret_cast(&uri_boot_hook), boot_orig, false); if (!boot_orig) { const uint64_t boot2 = Get_Address_of_Method( img.app_system, "Sce.Vsh.ShellUI.AppSystem", "BootHelper", "Boot", 2); (void)install_detour("BootHelper.Boot(2)", boot2, reinterpret_cast(&uri_boot_hook_2), boot_orig_2, false); if (!boot_orig_2) notify("failed to detour BootHelper.Boot"); } } // CaptureScreen: 4-arg (old) then 5-arg (new) { const uint64_t cap4 = Get_Address_of_Method( img.capture_menu, "Sce.Vsh.ShellUI.CaptureMenu", "CaptureController", "CaptureScreen", 4); (void)install_detour("CaptureScreen(4)", cap4, reinterpret_cast(&CaptureScreen_old), CaptureScreen_orig_old, false); if (!CaptureScreen_orig_old) { const uint64_t cap5 = Get_Address_of_Method( img.capture_menu, "Sce.Vsh.ShellUI.CaptureMenu", "CaptureController", "CaptureScreen", 5); (void)install_detour("CaptureScreen(5)", cap5, reinterpret_cast(&CaptureScreen_new), CaptureScreen_orig_new, false); if (!CaptureScreen_orig_new) notify("Failed to detour CaptureScreen"); } } (void)install_detour( "EventManager.OnShareButton", Get_Address_of_Method(img.capture_menu, "Sce.Vsh.ShellUI.CaptureMenu", "EventManager", "OnShareButton", 1), reinterpret_cast(&OnShareButton), OnShareButton_orig, false); (void)install_detour( "SettingPage.OnCreating", Get_Address_of_Method(img.legacy, UI3_dec.c_str(), "SettingPage", "OnCreating", 1), reinterpret_cast(&OnPreCreate_Hook), oOnPreCreate, false); if (!install_detour( "SettingsPlugin.GetString", Get_Address_of_Method(img.legacy, UI3_dec.c_str(), "SettingsPlugin", "GetString", 1), reinterpret_cast(&GetString_Hook), oGetString, true)) return false; // GetManifestResourceStream: Assembly on 3.xx, RuntimeAssembly otherwise { const char* klass = is_3xx ? "Assembly" : "RuntimeAssembly"; uint64_t method = Get_Address_of_Method(img.mscorlib, "System.Reflection", klass, "GetManifestResourceStream", 1); if (!method) { notify("Failed to get master address"); return false; } (void)install_detour("GetManifestResourceStream", method, reinterpret_cast(&GetManifestResourceStream_Hook), GetManifestResourceStream_Original, false); } (void)install_detour( "PowerManager.Terminate", Get_Address_of_Method(img.app_system, "Sce.Vsh.ShellUI.AppSystem", "PowerManager", "Terminate", 0), reinterpret_cast(&Terminate), oTerminate, false); return true; } // --------------------------------------------------------------------------- // Phase 8: process query hooks + keep-alive // --------------------------------------------------------------------------- void setup_proc_hooks() { orion_proc_set_sce_hooks( [](int pid, char* name) -> int { return sceKernelGetProcessName ? sceKernelGetProcessName(pid, name) : -1; }, [](pid_t pid, void* info) -> int { return sceKernelGetAppInfo ? sceKernelGetAppInfo(pid, static_cast(info)) : -1; }, []() -> int { return sceSystemServiceGetAppIdOfRunningBigApp ? sceSystemServiceGetAppIdOfRunningBigApp() : -1; }); } void run_keep_alive() { pthread_t thread_id{}; scePthreadCreate(&thread_id, nullptr, dialogue_thread, nullptr, "dialogue_thread"); orion_ready_signal(ORION_READY_TOOLBOX); while (true) { shellui_log("sleeping ...."); sleep(kKeepAliveSleepSec); } } } // namespace // --------------------------------------------------------------------------- // main — orchestration only // --------------------------------------------------------------------------- int main(int argc, char const* argv[]) { (void)argc; (void)argv; if (hooked) return 0; const pid_t pid = getpid(); AuthIdGuard auth(pid, set_ucred_to_debugger()); void* read_fn = nullptr; void* appinst_fn = nullptr; (void)appinst_fn; if (!resolve_native_symbols(pid, read_fn, appinst_fn)) return -1; shellui_log("Starting ShellUI Module ...."); if (!resolve_mono_symbols(pid)) return -1; init_resource_names(); OrbisKernelSwVersion sw{}; sceKernelGetProsperoSystemSwVersion(&sw); is_3xx = (sw.version < kFw3xxMaxExclusive); is_6xx = (sw.version >= kFw6xxMin); shellui_log("System Software Version: %s is_3xx: %s", sw.version_str, is_3xx ? "Yes" : "No"); if (!mono_get_root_domain) return -1; shellui_log("loading settings"); if (!LoadSettings()) { shellui_log("Failed to load settings"); return -1; } shellui_log("Settings loaded successfully"); Root_Domain = mono_get_root_domain(); if (!Root_Domain) { shellui_log("failed to get shellui root domain"); return -1; } shellui_log("Shellui Root Domain: %p", Root_Domain); mono_thread_attach(Root_Domain); if (!init_version_string(sw)) return -1; if (!decrypt_toolbox_xml()) return -1; ShellImages images; if (!load_shell_images(images)) return -1; if (!resolve_game_container(images.app_system)) return -1; shellui_log("Starting hooking..."); if (!install_hooks(images, read_fn)) return -1; shellui_log("Performing Magic ...."); // Drop elevated auth before long-running work set_proc_authid(pid, auth.old_authid); auth.release(); if (g_settings.display_tids) ReloadRNPSApp("NPXS40002"); shellui_log("Performed Magic"); setup_proc_hooks(); hooked = true; run_keep_alive(); return 0; }