#include #include "Hook.h" #include "Plugin.h" #include "Log.h" #include "../deps/UnityResolve/UnityResolve.hpp" #include "Il2cppUtils.hpp" #include "Local.h" #include #define DEFINE_HOOK(returnType, name, params) \ using name##_Type = returnType(*) params; \ name##_Type name##_Addr = nullptr; \ name##_Type name##_Orig = nullptr; \ returnType name##_Hook params #define ADD_HOOK(name, addr) \ name##_Addr = reinterpret_cast(addr); \ if (addr) { \ hookInstaller->InstallHook(reinterpret_cast(addr), \ reinterpret_cast(name##_Hook), \ reinterpret_cast(&name##_Orig)); \ GakumasLocal::Log::InfoFmt("ADD_HOOK: %s at %p", #name, addr); \ } \ else GakumasLocal::Log::ErrorFmt("Hook failed: %s is NULL", #name, addr) namespace GakumasLocal::HookMain { using Il2cppString = UnityResolve::UnityType::String; UnityResolve::UnityType::String* environment_get_stacktrace() { /* static auto mtd = Il2cppUtils::GetMethod("mscorlib.dll", "System", "Environment", "get_StackTrace"); return mtd->Invoke();*/ const auto pClass = Il2cppUtils::GetClass("mscorlib.dll", "System.Diagnostics", "StackTrace"); const auto ctor_mtd = Il2cppUtils::GetMethod("mscorlib.dll", "System.Diagnostics", "StackTrace", ".ctor"); const auto toString_mtd = Il2cppUtils::GetMethod("mscorlib.dll", "System.Diagnostics", "StackTrace", "ToString"); const auto klassInstance = pClass->New(); ctor_mtd->Invoke(klassInstance); return toString_mtd->Invoke(klassInstance); } DEFINE_HOOK(void, Internal_LogException, (void* ex, void* obj)) { Internal_LogException_Orig(ex, obj); // Log::LogFmt(ANDROID_LOG_VERBOSE, "UnityLog - Internal_LogException"); } DEFINE_HOOK(void, Internal_Log, (int logType, int logOption, UnityResolve::UnityType::String* content, void* context)) { Internal_Log_Orig(logType, logOption, content, context); // 2022.3.21f1 // Log::LogFmt(ANDROID_LOG_VERBOSE, "UnityLog - Internal_Log: %s", content->ToString().c_str()); } std::unordered_map loadHistory{}; DEFINE_HOOK(void*, AssetBundle_LoadAssetAsync, (void* _this, Il2cppString* name, void* type)) { // Log::InfoFmt("AssetBundle_LoadAssetAsync: %s, type: %s", name->ToString().c_str()); auto ret = AssetBundle_LoadAssetAsync_Orig(_this, name, type); loadHistory.emplace(ret, name->ToString()); return ret; } DEFINE_HOOK(void*, AssetBundleRequest_GetResult, (void* _this)) { auto result = AssetBundleRequest_GetResult_Orig(_this); if (const auto iter = loadHistory.find(_this); iter != loadHistory.end()) { const auto name = iter->second; loadHistory.erase(iter); // const auto assetClass = Il2cppUtils::get_class_from_instance(result); // Log::InfoFmt("AssetBundleRequest_GetResult: %s, type: %s", name.c_str(), static_cast(assetClass)->name); } return result; } DEFINE_HOOK(void*, Resources_Load, (Il2cppString* path, void* systemTypeInstance)) { auto ret = Resources_Load_Orig(path, systemTypeInstance); // if (ret) Log::DebugFmt("Resources_Load: %s, type: %s", path->ToString().c_str(), Il2cppUtils::get_class_from_instance(ret)->name); return ret; } DEFINE_HOOK(void, I18nHelper_SetUpI18n, (void* _this, Il2cppString* lang, Il2cppString* localizationText, int keyComparison)) { // Log::InfoFmt("SetUpI18n lang: %s, key: %d text: %s", lang->ToString().c_str(), keyComparison, localizationText->ToString().c_str()); // TODO 此处为 dump 原文 csv I18nHelper_SetUpI18n_Orig(_this, lang, localizationText, keyComparison); } DEFINE_HOOK(void, I18nHelper_SetValue, (void* _this, Il2cppString* key, Il2cppString* value)) { // Log::InfoFmt("I18nHelper_SetValue: %s - %s", key->ToString().c_str(), value->ToString().c_str()); std::string local; if (Local::GetI18n(key->ToString(), &local)) { I18nHelper_SetValue_Orig(_this, key, UnityResolve::UnityType::String::New(local)); return; } Local::DumpI18nItem(key->ToString(), value->ToString()); I18nHelper_SetValue_Orig(_this, key, value); } void* fontCache = nullptr; void* GetReplaceFont() { static auto CreateFontFromPath = reinterpret_cast( Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Font::Internal_CreateFontFromPath(UnityEngine.Font,System.String)") ); static auto Font_klass = Il2cppUtils::GetClass("UnityEngine.TextRenderingModule.dll", "UnityEngine", "Font"); static auto Font_ctor = Il2cppUtils::GetMethod("UnityEngine.TextRenderingModule.dll", "UnityEngine", "Font", ".ctor"); static auto IsNativeObjectAlive = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll", "UnityEngine", "Object", "IsNativeObjectAlive"); if (fontCache) { if (IsNativeObjectAlive->Invoke(fontCache)) { return fontCache; } } const auto newFont = Font_klass->New(); Font_ctor->Invoke(newFont); static std::string fontName = Local::GetBasePath() / "local-files" / "MO-UDShinGo-SC-Gb4-M.otf"; CreateFontFromPath(newFont, Il2cppString::New(fontName)); fontCache = newFont; return newFont; } std::unordered_set updatedFontPtrs{}; void UpdateFont(void* TMP_Text_this) { static auto get_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "get_font"); static auto set_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "set_font"); static auto set_sourceFontFile = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_FontAsset", "set_sourceFontFile"); static auto UpdateFontAssetData = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_FontAsset", "UpdateFontAssetData"); auto fontAsset = get_font->Invoke(TMP_Text_this); auto newFont = GetReplaceFont(); if (fontAsset && newFont) { set_sourceFontFile->Invoke(fontAsset, newFont); if (!updatedFontPtrs.contains(fontAsset)) { updatedFontPtrs.emplace(fontAsset); UpdateFontAssetData->Invoke(fontAsset); } } set_font->Invoke(TMP_Text_this, fontAsset); } DEFINE_HOOK(void, TMP_Text_set_text, (void* _this, UnityResolve::UnityType::String* text)) { // Log::DebugFmt("TMP_Text_set_text: %s", text->ToString().c_str()); std::string transText; if (Local::GetGenericText(text->ToString(), &transText)) { return TMP_Text_set_text_Orig(_this, UnityResolve::UnityType::String::New(transText)); } // TMP_Text_set_text_Orig(_this, UnityResolve::UnityType::String::New("[TS]" + text->ToString())); TMP_Text_set_text_Orig(_this, text); static auto set_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "set_font"); //auto font = GetReplaceFontAsset(); //set_font->Invoke(_this, font); UpdateFont(_this); } DEFINE_HOOK(void, TextMeshProUGUI_Awake, (void* _this, void* method)) { // Log::InfoFmt("TextMeshProUGUI_Awake at %p, _this at %p", TextMeshProUGUI_Awake_Orig, _this); const auto TMP_Text_klass = Il2cppUtils::GetClass("Unity.TextMeshPro.dll", "TMPro", "TMP_Text"); const auto get_Text_method = TMP_Text_klass->Get("get_text"); const auto set_Text_method = TMP_Text_klass->Get("set_text"); const auto currText = get_Text_method->Invoke(_this); if (currText) { //Log::InfoFmt("TextMeshProUGUI_Awake: %s", currText->ToString().c_str()); std::string transText; if (Local::GetGenericText(currText->ToString(), &transText)) { TMP_Text_set_text_Orig(_this, UnityResolve::UnityType::String::New(transText)); } } // set_font->Invoke(_this, font); UpdateFont(_this); TextMeshProUGUI_Awake_Orig(_this, method); } DEFINE_HOOK(void, UI_Text_set_text, (void* _this, Il2cppString* value)) { // UI_Text_set_text_Orig(_this, Il2cppString::New("[US]" + value->ToString())); UI_Text_set_text_Orig(_this, value); static auto set_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "set_font"); auto newFont = GetReplaceFont(); set_font->Invoke(_this, newFont); } DEFINE_HOOK(Il2cppString*, OctoCaching_GetResourceFileName, (void* data, void* method)) { auto ret = OctoCaching_GetResourceFileName_Orig(data, method); //Log::DebugFmt("OctoCaching_GetResourceFileName: %s", ret->ToString().c_str()); return ret; } DEFINE_HOOK(void, OctoResourceLoader_LoadFromCacheOrDownload, (void* _this, Il2cppString* resourceName, void* onComplete, void* onProgress, void* method)) { Log::DebugFmt("OctoResourceLoader_LoadFromCacheOrDownload: %s\n", resourceName->ToString().c_str()); std::string replaceStr; if (Local::GetResourceText(resourceName->ToString(), &replaceStr)) { const auto onComplete_klass = Il2cppUtils::get_class_from_instance(onComplete); const auto onComplete_invoke_mtd = UnityResolve::Invoke( "il2cpp_class_get_method_from_name", onComplete_klass, "Invoke", 2); if (onComplete_invoke_mtd) { const auto onComplete_invoke = reinterpret_cast( onComplete_invoke_mtd->methodPointer ); onComplete_invoke(onComplete, UnityResolve::UnityType::String::New(replaceStr), NULL); return; } } return OctoResourceLoader_LoadFromCacheOrDownload_Orig(_this, resourceName, onComplete, onProgress, method); } DEFINE_HOOK(void, OnDownloadProgress_Invoke, (void* _this, Il2cppString* name, uint64_t receivedLength, uint64_t contentLength)) { Log::DebugFmt("OnDownloadProgress_Invoke: %s, %lu/%lu", name->ToString().c_str(), receivedLength, contentLength); OnDownloadProgress_Invoke_Orig(_this, name, receivedLength, contentLength); } // UnHooked DEFINE_HOOK(UnityResolve::UnityType::String*, UI_I18n_GetOrDefault, (void* _this, UnityResolve::UnityType::String* key, UnityResolve::UnityType::String* defaultKey, void* method)) { auto ret = UI_I18n_GetOrDefault_Orig(_this, key, defaultKey, method); // Log::DebugFmt("UI_I18n_GetOrDefault: key: %s, default: %s, result: %s", key->ToString().c_str(), defaultKey->ToString().c_str(), ret->ToString().c_str()); return ret; // return UnityResolve::UnityType::String::New("[I18]" + ret->ToString()); } void StartInjectFunctions() { const auto hookInstaller = Plugin::GetInstance().GetHookInstaller(); UnityResolve::Init(xdl_open(hookInstaller->m_il2cppLibraryPath.c_str(), RTLD_NOW), UnityResolve::Mode::Il2Cpp); ADD_HOOK(AssetBundle_LoadAssetAsync, Il2cppUtils::il2cpp_resolve_icall( "UnityEngine.AssetBundle::LoadAssetAsync_Internal(System.String,System.Type)")); ADD_HOOK(AssetBundleRequest_GetResult, Il2cppUtils::il2cpp_resolve_icall( "UnityEngine.AssetBundleRequest::GetResult()")); ADD_HOOK(Resources_Load, Il2cppUtils::il2cpp_resolve_icall( "UnityEngine.ResourcesAPIInternal::Load(System.String,System.Type)")); ADD_HOOK(I18nHelper_SetUpI18n, Il2cppUtils::GetMethodPointer("quaunity-ui.Runtime.dll", "Qua.UI", "I18nHelper", "SetUpI18n")); ADD_HOOK(I18nHelper_SetValue, Il2cppUtils::GetMethodPointer("quaunity-ui.Runtime.dll", "Qua.UI", "I18n", "SetValue")); //ADD_HOOK(UI_I18n_GetOrDefault, Il2cppUtils::GetMethodPointer("quaunity-ui.Runtime.dll", "Qua.UI", // "I18n", "GetOrDefault")); ADD_HOOK(TextMeshProUGUI_Awake, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro", "TextMeshProUGUI", "Awake")); ADD_HOOK(TMP_Text_set_text, Il2cppUtils::GetMethodPointer("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "set_text")); ADD_HOOK(UI_Text_set_text, Il2cppUtils::GetMethodPointer("UnityEngine.UI.dll", "UnityEngine.UI", "Text", "set_text")); ADD_HOOK(OctoCaching_GetResourceFileName, Il2cppUtils::GetMethodPointer("Octo.dll", "Octo.Caching", "OctoCaching", "GetResourceFileName")); ADD_HOOK(OctoResourceLoader_LoadFromCacheOrDownload, Il2cppUtils::GetMethodPointer("Octo.dll", "Octo.Loader", "OctoResourceLoader", "LoadFromCacheOrDownload", {"System.String", "System.Action", "Octo.OnDownloadProgress"})); ADD_HOOK(OnDownloadProgress_Invoke, Il2cppUtils::GetMethodPointer("Octo.dll", "Octo", "OnDownloadProgress", "Invoke")); ADD_HOOK(Internal_LogException, Il2cppUtils::il2cpp_resolve_icall( "UnityEngine.DebugLogHandler::Internal_LogException(System.Exception,UnityEngine.Object)")); ADD_HOOK(Internal_Log, Il2cppUtils::il2cpp_resolve_icall( "UnityEngine.DebugLogHandler::Internal_Log(UnityEngine.LogType,UnityEngine.LogOption,System.String,UnityEngine.Object)")); } // 77 2640 5000 DEFINE_HOOK(int, il2cpp_init, (const char* domain_name)) { const auto ret = il2cpp_init_Orig(domain_name); // InjectFunctions(); Log::Info("Start init plugin..."); StartInjectFunctions(); Local::LoadData(); Log::Info("Plugin init finished."); return ret; } } namespace GakumasLocal::Hook { void Install() { const auto hookInstaller = Plugin::GetInstance().GetHookInstaller(); Log::Info("Installing hook"); ADD_HOOK(HookMain::il2cpp_init, Plugin::GetInstance().GetHookInstaller()->LookupSymbol("il2cpp_init")); Log::Info("Hook installed"); } }