using System; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.IL2CPP.Hook; using BepInEx.IL2CPP.Logging; using BepInEx.IL2CPP.Utils; using BepInEx.Logging; using BepInEx.Preloader.Core; using BepInEx.Preloader.Core.Logging; using HarmonyLib.Public.Patching; using MonoMod.Utils; using UnhollowerBaseLib; using UnhollowerRuntimeLib; using UnityEngine; using Logger = BepInEx.Logging.Logger; namespace BepInEx.IL2CPP { public class IL2CPPChainloader : BaseChainloader { private static RuntimeInvokeDetourDelegate originalInvoke; private static readonly ConfigEntry ConfigUnityLogging = ConfigFile.CoreConfig.Bind( "Logging", "UnityLogListening", true, "Enables showing unity log messages in the BepInEx logging system."); private static readonly ConfigEntry ConfigDiskWriteUnityLog = ConfigFile.CoreConfig.Bind( "Logging.Disk", "WriteUnityLog", false, "Include unity log messages in log file output."); private static FastNativeDetour RuntimeInvokeDetour { get; set; } public static IL2CPPChainloader Instance { get; set; } /// /// Register and add a Unity Component (for example MonoBehaviour) into BepInEx global manager. /// /// Automatically registers the type with Il2Cpp type system if it isn't initialised already. /// /// Type of the component to add. public static T AddUnityComponent() where T : Il2CppObjectBase => AddUnityComponent(typeof(T)).Cast(); /// /// Register and add a Unity Component (for example MonoBehaviour) into BepInEx global manager. /// /// Automatically registers the type with Il2Cpp type system if it isn't initialised already. /// /// Type of the component to add public static Il2CppObjectBase AddUnityComponent(Type t) => Il2CppUtils.AddComponent(t); public override void Initialize(string gameExePath = null) { GeneratedDatabasesUtil.DatabasesLocationOverride = Preloader.IL2CPPUnhollowedPath; PatchManager.ResolvePatcher += IL2CPPDetourMethodPatcher.TryResolve; base.Initialize(gameExePath); Instance = this; ClassInjector.Detour = new UnhollowerDetourHandler(); var gameAssemblyModule = Process.GetCurrentProcess().Modules.Cast() .FirstOrDefault(x => x.ModuleName.Contains("GameAssembly") || x.ModuleName.Contains("UserAssembly")); if (gameAssemblyModule == null) { Logger.LogFatal("Could not locate Il2Cpp game assembly (GameAssembly.dll) or (UserAssembly.dll). The game might be obfuscated or use a yet unsupported build of Unity."); return; } gameAssemblyModule.BaseAddress.TryGetFunction("il2cpp_runtime_invoke", out var runtimeInvokePtr); PreloaderLogger.Log.LogDebug($"Runtime invoke pointer: 0x{runtimeInvokePtr.ToInt64():X}"); RuntimeInvokeDetour = FastNativeDetour.CreateAndApply(runtimeInvokePtr, OnInvokeMethod, out originalInvoke, CallingConvention.Cdecl); PreloaderLogger.Log.LogDebug("Runtime invoke patched"); Logger.LogInfo($"Runtime version: {Environment.Version}"); Logger.LogInfo($"Runtime information: {RuntimeInformation.FrameworkDescription}"); } private static IntPtr OnInvokeMethod(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc) { var methodName = Marshal.PtrToStringAnsi(UnhollowerBaseLib.IL2CPP.il2cpp_method_get_name(method)); var unhook = false; if (methodName == "Internal_ActiveSceneChanged") try { if (ConfigUnityLogging.Value) { Logger.Sources.Add(new IL2CPPUnityLogSource()); Application.CallLogCallback("Test call after applying unity logging hook", "", LogType.Assert, true); } unhook = true; Instance.Execute(); } catch (Exception ex) { Logger.LogFatal("Unable to execute IL2CPP chainloader"); Logger.LogError(ex); } var result = originalInvoke(method, obj, parameters, exc); if (unhook) { RuntimeInvokeDetour.Dispose(); PreloaderLogger.Log.LogDebug("Runtime invoke unpatched"); } return result; } protected override void InitializeLoggers() { base.InitializeLoggers(); if (!ConfigDiskWriteUnityLog.Value) DiskLogListener.BlacklistedSources.Add("Unity"); ChainloaderLogHelper.RewritePreloaderLogs(); Logger.Sources.Add(new IL2CPPLogSource()); } public override BasePlugin LoadPlugin(PluginInfo pluginInfo, Assembly pluginAssembly) { var type = pluginAssembly.GetType(pluginInfo.TypeName); var pluginInstance = (BasePlugin)Activator.CreateInstance(type); pluginInstance.Load(); return pluginInstance; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate IntPtr RuntimeInvokeDetourDelegate(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc); } }