// entry point function main(stage, parameters) { if (!parameters.redirectHost) { console.log("redirectHost parameter not specified!\nEdit your gadget.config and specify redirectHost in \"parameters\" section."); return; } // Redirect http requests const UnityWebRequestSetUrlPtr = Helpers.FindMethodImpl("UnityEngine.UnityWebRequestModule.dll", "UnityEngine.Networking", "UnityWebRequest", "set_url", 1); const UriCtorPtr = Helpers.FindMethodImpl("System.dll", "System", "Uri", ".ctor", 1); console.log("Found UnityWebRequest::set_url at " + UnityWebRequestSetUrlPtr); console.log("Found Uri::ctor at " + UriCtorPtr); RedirectCallback.targetHost = parameters.redirectHost; Interceptor.attach(UnityWebRequestSetUrlPtr, RedirectCallback); Interceptor.attach(UriCtorPtr, RedirectCallback); console.log("Attached successfully, will redirect all requests to: " + parameters.redirectHost); // Hook ZFGameBrowser to redirect in-game WebView const BrowserLoadURL = Helpers.FindMethodImpl("ZFBrowser.dll", "ZenFulcrum.EmbeddedBrowser", "Browser", "LoadURL", 2); if (BrowserLoadURL) { Interceptor.attach(BrowserLoadURL, { onEnter(args) { var requestUrl = args[1].readCSharpString(); var prefix = requestUrl.split('/', 3).join('/'); args[1] = Il2cppApiWrap.AllocateString(requestUrl.replace(prefix, parameters.redirectHost)); console.log("EmbeddedBrowser redirected: " + args[1].readCSharpString()); } }); console.log("Successfully hooked EmbeddedBrowser"); } } const RedirectCallback = { onEnter(args) { var requestUrl = args[1].readCSharpString(); if (requestUrl.startsWith("http://") || requestUrl.startsWith("https://")) { if (requestUrl.includes(RedirectCallback.targetHost)) return; // already redirected var prefix = requestUrl.split('/', 3).join('/'); args[1] = Il2cppApiWrap.AllocateString(requestUrl.replace(prefix, RedirectCallback.targetHost)); console.log("redirected: " + args[1].readCSharpString()); } } }; const Helpers = { FindMethodImpl(moduleName, namespace, className, methodName, argCount) { const module = Il2cppApiWrap.GetImageByName(moduleName); if (module.equals(ptr(0))) return null; const il2cppClass = Il2cppApiWrap.GetClassByName(module, namespace, className); if (il2cppClass.equals(ptr(0))) return null; const il2cppMethod = Il2cppApiWrap.GetClassMethodByName(il2cppClass, methodName, argCount); if (il2cppMethod.equals(ptr(0))) return null; return il2cppMethod.readPointer(); } } const Il2cppApiWrap = { AllocateString(value) { const pValue = Memory.allocUtf16String(value); return this.CallApiFunction('il2cpp_string_new_utf16', 'pointer', ['pointer', 'int'], [pValue, value.length]); }, GetClassMethodByName(il2cppClass, name, argsCount) { const pName = Memory.allocUtf8String(name); return this.CallApiFunction('il2cpp_class_get_method_from_name', 'pointer', ['pointer', 'pointer', 'int'], [il2cppClass, pName, argsCount]); }, GetClassByName(il2cppImage, namespace, name) { const pNamespace = Memory.allocUtf8String(namespace); const pName = Memory.allocUtf8String(name); return this.CallApiFunction('il2cpp_class_from_name', 'pointer', ['pointer', 'pointer', 'pointer'], [il2cppImage, pNamespace, pName]); }, GetImageByName(name) { const domain = this.CallApiFunction('il2cpp_domain_get', 'pointer', [], []); const sizeOut = Memory.alloc(8); const assemblies = this.CallApiFunction('il2cpp_domain_get_assemblies', 'pointer', ['pointer', 'pointer'], [domain, sizeOut]); const size = sizeOut.readU64(); for (var i = 0; i < size; i++) { const assembly = assemblies.add(i * 8).readPointer(); const il2cppImage = this.CallApiFunction('il2cpp_assembly_get_image', 'pointer', ['pointer'], [assembly]); const imageName = this.CallApiFunction('il2cpp_image_get_name', 'pointer', ['pointer'], [il2cppImage]); if (imageName.readUtf8String() == name) { return il2cppImage; } } return null; }, CallApiFunction(name, rettype, argTypes, args) { const nativeFunction = new NativeFunction(Module.findExportByName(null, name), rettype, argTypes); return nativeFunction.apply(nativeFunction, args); } } NativePointer.prototype.readCSharpString = function() { var length = this.add(16).readInt(); return this.add(20).readUtf16String(length); } rpc.exports.init = main;