diff --git a/Patch/Launcher.exe b/Patch/Launcher.exe new file mode 100644 index 0000000..088fd54 Binary files /dev/null and b/Patch/Launcher.exe differ diff --git a/Patch/UnityPlayer.dll b/Patch/UnityPlayer.dll new file mode 100644 index 0000000..dbd1abb Binary files /dev/null and b/Patch/UnityPlayer.dll differ diff --git a/Patch/gadget.config b/Patch/gadget.config new file mode 100644 index 0000000..1db5f4f --- /dev/null +++ b/Patch/gadget.config @@ -0,0 +1,9 @@ +{ + "interaction": { + "type": "script", + "path": "universal_redirect.js", + "parameters": { + "redirectHost": "http://127.0.0.1:8888" + } + } +} \ No newline at end of file diff --git a/Patch/gadget.dll b/Patch/gadget.dll new file mode 100644 index 0000000..9ee9495 Binary files /dev/null and b/Patch/gadget.dll differ diff --git a/Patch/universal_redirect.js b/Patch/universal_redirect.js new file mode 100644 index 0000000..ae6e4d5 --- /dev/null +++ b/Patch/universal_redirect.js @@ -0,0 +1,129 @@ +// 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); + + CensorshipPatch.init(); + + // 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"); + } + else { + console.log("zfbrowser not found"); + } +} + +const CensorshipPatch = { + init() { + const OnTriggerCameraElevationDitherPtr = Helpers.FindMethodImpl("Assembly-CSharp.dll", "RPG.Client", "AdventurePhase", "_OnTriggerCameraElevationDither", 1); + Memory.protect(OnTriggerCameraElevationDitherPtr, 1, "rwx"); + OnTriggerCameraElevationDitherPtr.writeU8(0xC3); // RET + + console.log("OnTriggerCameraElevationDither patched successfully, gameplay quality increased by 200%"); + } +} + +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; \ No newline at end of file diff --git a/Patch/version.dll b/Patch/version.dll new file mode 100644 index 0000000..9413814 Binary files /dev/null and b/Patch/version.dll differ