function main(stage, parameters) { const redirectHost = parameters.redirectHost || "http://127.0.0.1:8899"; const setUrl = Resolve.Method("UnityEngine.UnityWebRequestModule.dll", "UnityEngine.Networking", "UnityWebRequest", "set_url", 1); const uriCtor = Resolve.Method("System.dll", "System", "Uri", ".ctor", 1); if (setUrl) Interceptor.attach(setUrl, new UrlRewriter(redirectHost)); if (uriCtor) Interceptor.attach(uriCtor, new UrlRewriter(redirectHost)); const browserLoad = Resolve.Method("ZFBrowser.dll", "ZenFulcrum.EmbeddedBrowser", "Browser", "LoadURL", 2); if (browserLoad) { Interceptor.attach(browserLoad, { onEnter(args) { const url = args[1].readManagedString(); const prefix = url.split('/', 3).join('/'); args[1] = Il2Cpp.NewString(url.replace(prefix, redirectHost)); } }); } console.log("[Yuanshen] dispatch redirect active -> " + redirectHost); if (parameters.levelProbe === true) attachLevelProbe(); if (parameters.debugMenu !== false) enableDebugMenu(); } const GlobalVars = { cctor: 0x4BE8CA0, typeInfo: 0xBE814B8, staticFields: 0xA0, debugMenu: 0xF8 }; function enableDebugMenu() { const base = Module.findBaseAddress("UserAssembly.dll"); if (base === null) { console.log("[Yuanshen] debug menu: UserAssembly.dll not loaded"); return; } Interceptor.attach(base.add(GlobalVars.cctor), { onLeave() { const klass = base.add(GlobalVars.typeInfo).readPointer(); if (klass.isNull()) return; const statics = klass.add(GlobalVars.staticFields).readPointer(); if (statics.isNull()) return; statics.add(GlobalVars.debugMenu).writeU8(1); console.log("[Yuanshen] debug menu enabled - Ctrl+` opens it, ` alone opens GM talk"); } }); } const LevelProbe = { "MonsterDataItem.UpdateNormalProp": 0x15D9000, "LCBaseCombat.UpdateCombatProp": 0x21A2720, "LCMonsterCombat.ChangeLevel": 0x2586740, "VCCombatBillboardPlugin.ChangeLevel": 0x21D4AD0, "VCCombatBillboardPlugin.InitCombatUI": 0x21D2A30 }; function attachLevelProbe() { const base = Module.findBaseAddress("UserAssembly.dll"); if (base === null) { console.log("[Yuanshen] level probe: UserAssembly.dll not loaded"); return; } for (const name in LevelProbe) { Interceptor.attach(base.add(LevelProbe[name]), { onEnter(args) { console.log("[level] " + name + " a1=" + args[1].toInt32() + " a2=" + args[2].toInt32() + " a3=" + args[3].toInt32()); } }); } console.log("[Yuanshen] level probe attached at " + base); } function UrlRewriter(redirectHost) { this.onEnter = function (args) { const url = args[1].readManagedString(); if (!url.startsWith("http://") && !url.startsWith("https://")) return; if (url.includes(redirectHost.replace(/^https?:\/\//, ''))) return; const prefix = url.split('/', 3).join('/'); args[1] = Il2Cpp.NewString(url.replace(prefix, redirectHost)); }; } const Il2Cpp = { NewString(value) { const buffer = Memory.allocUtf16String(value); return this.Api('il2cpp_string_new_utf16', 'pointer', ['pointer', 'int'], [buffer, value.length]); }, Api(name, ret, argTypes, args) { const fn = new NativeFunction(Module.findExportByName(null, name), ret, argTypes); return fn.apply(fn, args); } }; const Resolve = { Method(moduleName, namespace, className, methodName, argCount) { const image = this.Image(moduleName); if (image.isNull()) return null; const klass = Il2Cpp.Api('il2cpp_class_from_name', 'pointer', ['pointer', 'pointer', 'pointer'], [image, Memory.allocUtf8String(namespace), Memory.allocUtf8String(className)]); if (klass.isNull()) return null; const method = Il2Cpp.Api('il2cpp_class_get_method_from_name', 'pointer', ['pointer', 'pointer', 'int'], [klass, Memory.allocUtf8String(methodName), argCount]); if (method.isNull()) return null; return method.readPointer(); }, Image(name) { const domain = Il2Cpp.Api('il2cpp_domain_get', 'pointer', [], []); const sizeOut = Memory.alloc(8); const assemblies = Il2Cpp.Api('il2cpp_domain_get_assemblies', 'pointer', ['pointer', 'pointer'], [domain, sizeOut]); const size = sizeOut.readU64(); for (let i = 0; i < size; i++) { const assembly = assemblies.add(i * 8).readPointer(); const image = Il2Cpp.Api('il2cpp_assembly_get_image', 'pointer', ['pointer'], [assembly]); const imageName = Il2Cpp.Api('il2cpp_image_get_name', 'pointer', ['pointer'], [image]); if (imageName.readUtf8String() === name) return image; } return NULL; } }; NativePointer.prototype.readManagedString = function () { const length = this.add(16).readInt(); return this.add(20).readUtf16String(length); }; rpc.exports.init = main;