Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of DunGenReferenceFixerFixed v0.0.2
BepInEx/patchers/mariusvn-DunGenReferenceFixerFixed/DunGenReferenceFixerFixed.dll
Decompiled 12 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using DunGenReferenceFixer.Patches.Native; using DunGenReferenceFixer.Utilities.Native; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("DunGenReferenceFixer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("DunGenReferenceFixer")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("ee30c7a9-db89-425a-acff-2202e901ce36")] [assembly: AssemblyFileVersion("0.0.1")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DunGenReferenceFixer { [BepInPlugin("Zaggy1024.DunGenReferenceFixer", "DunGenReferenceFixer", "0.0.1")] public static class DunGenReferenceFixerPreloader { public const string PluginName = "DunGenReferenceFixer"; public const string PluginGUID = "Zaggy1024.DunGenReferenceFixer"; public const string PluginVersion = "0.0.1"; public static ManualLogSource Log = Logger.CreateLogSource("DunGenReferenceFixer"); private static Harmony harmony = new Harmony("DunGenReferenceFixer"); public static IEnumerable<string> TargetDLLs { get; } = Array.Empty<string>(); public static void Patch(AssemblyDefinition assembly) { } public static void Initialize() { } public static void Finish() { harmony.PatchAll(typeof(DunGenReferenceFixerPreloader)); } [HarmonyPostfix] [HarmonyPatch(typeof(Chainloader), "Initialize")] private static void ApplyAllNativePatches() { PatchGetMonoClassWithAssemblyName.Apply(); } } } namespace DunGenReferenceFixer.Utilities.Native { internal static class NativeHelpers { internal static readonly IntPtr BaseAddress = GetUnityPlayerModule().BaseAddress; internal static readonly bool IsDebugBuild = Debug.isDebugBuild; private static ProcessModule GetUnityPlayerModule() { ProcessModuleCollection modules = Process.GetCurrentProcess().Modules; for (int i = 0; i < modules.Count; i++) { ProcessModule processModule = modules[i]; if (processModule.ModuleName.Contains("UnityPlayer")) { return processModule; } } return null; } } internal sealed class NativeHook { internal const int JumpSize = 14; private const uint MEM_COMMIT = 4096u; private const uint MEM_RESERVE = 8192u; private const uint PAGE_READWRITE = 4u; private const uint PAGE_EXECUTE_READ = 32u; private const uint PAGE_EXECUTE_READWRITE = 64u; internal IntPtr Trampoline { get; } internal NativeHook(IntPtr target, IntPtr detour, int prologueSize) { if (prologueSize < 14) { throw new ArgumentOutOfRangeException("prologueSize", $"At least {14} bytes of prologue are needed to fit the jump to the detour."); } Trampoline = BuildTrampoline(target, prologueSize); InstallJump(target, detour, prologueSize); } private unsafe static IntPtr BuildTrampoline(IntPtr target, int prologueSize) { int num = prologueSize + 14; IntPtr intPtr = VirtualAlloc(IntPtr.Zero, (UIntPtr)(ulong)num, 12288u, 4u); if (intPtr == IntPtr.Zero) { throw new InvalidOperationException($"Failed to allocate the trampoline (VirtualAlloc error {Marshal.GetLastWin32Error()})."); } Buffer.MemoryCopy((void*)target, (void*)intPtr, num, prologueSize); WriteJump(IntPtr.Add(intPtr, prologueSize), IntPtr.Add(target, prologueSize)); if (!VirtualProtect(intPtr, (UIntPtr)(ulong)num, 32u, out var _)) { throw new InvalidOperationException($"Failed to make the trampoline executable (VirtualProtect error {Marshal.GetLastWin32Error()})."); } FlushInstructionCache(GetCurrentProcess(), intPtr, (UIntPtr)(ulong)num); return intPtr; } private unsafe static void InstallJump(IntPtr target, IntPtr detour, int prologueSize) { if (!VirtualProtect(target, (UIntPtr)(ulong)prologueSize, 64u, out var oldProtect)) { throw new InvalidOperationException($"Failed to make the patch site writable (VirtualProtect error {Marshal.GetLastWin32Error()})."); } WriteJump(target, detour); byte* ptr = (byte*)(void*)target; for (int i = 14; i < prologueSize; i++) { ptr[i] = 204; } VirtualProtect(target, (UIntPtr)(ulong)prologueSize, oldProtect, out var _); FlushInstructionCache(GetCurrentProcess(), target, (UIntPtr)(ulong)prologueSize); } private unsafe static void WriteJump(IntPtr at, IntPtr destination) { byte* ptr = (byte*)(void*)at; *ptr = byte.MaxValue; ptr[1] = 37; ptr[2] = 0; ptr[3] = 0; ptr[4] = 0; ptr[5] = 0; *(long*)(ptr + 6) = destination.ToInt64(); } [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc(IntPtr address, UIntPtr size, uint allocationType, uint protect); [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool VirtualProtect(IntPtr address, UIntPtr size, uint newProtect, out uint oldProtect); [DllImport("kernel32.dll")] private static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool FlushInstructionCache(IntPtr process, IntPtr baseAddress, UIntPtr size); } } namespace DunGenReferenceFixer.Patches.Native { internal static class PatchGetMonoClassWithAssemblyName { [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate IntPtr GetMonoClassWithAssemblyNameDelegate(IntPtr thisMonoManager, IntPtr result, IntPtr param_2, IntPtr param_3, ref BasicStringRef param_4); private struct BasicStringRef { public unsafe char* Characters; public ulong Size; public unsafe BasicStringRef(ReadOnlySpan<byte> data) { Characters = (char*)UnsafeUtility.AddressOf<byte>(ref MemoryMarshal.GetReference(data)); Size = (ulong)data.Length; } public unsafe static implicit operator ReadOnlySpan<byte>(BasicStringRef str) { if (str.Size > int.MaxValue) { throw new InvalidCastException("String is too large"); } return new ReadOnlySpan<byte>(str.Characters, (int)str.Size); } public unsafe override readonly string ToString() { return Encoding.UTF8.GetString((byte*)Characters, (int)Math.Min(Size, 2147483647uL)); } } private static NativeHook getMonoClassWithAssemblyNameHook; private static GetMonoClassWithAssemblyNameDelegate getMonoClassWithAssemblyNameDetour; private static GetMonoClassWithAssemblyNameDelegate getMonoClassWithAssemblyNameOriginal; private static ReadOnlySpan<byte> ExpectedPrologue => new byte[15] { 76, 137, 76, 36, 32, 76, 137, 68, 36, 24, 72, 137, 76, 36, 8 }; [MethodImpl(MethodImplOptions.NoInlining)] internal unsafe static void Apply() { int num = 7881440; if (NativeHelpers.IsDebugBuild) { num = 16836128; } IntPtr intPtr = NativeHelpers.BaseAddress + num; if (!ExpectedPrologue.SequenceEqual(new ReadOnlySpan<byte>((void*)intPtr, ExpectedPrologue.Length))) { DunGenReferenceFixerPreloader.Log.LogError((object)$"MonoManager::GetMonoClassWithAssemblyName() does not begin with the expected instructions at UnityPlayer+0x{num:X}. This build of the game is not supported, DunGen references will be left alone."); return; } getMonoClassWithAssemblyNameDetour = GetMonoClassWithAssemblyNameDetour; IntPtr functionPointerForDelegate = Marshal.GetFunctionPointerForDelegate(getMonoClassWithAssemblyNameDetour); getMonoClassWithAssemblyNameHook = new NativeHook(intPtr, functionPointerForDelegate, ExpectedPrologue.Length); getMonoClassWithAssemblyNameOriginal = Marshal.GetDelegateForFunctionPointer<GetMonoClassWithAssemblyNameDelegate>(getMonoClassWithAssemblyNameHook.Trampoline); DunGenReferenceFixerPreloader.Log.LogInfo((object)"Patched MonoManager::GetMonoClassWithAssemblyName() to fix DunGen references."); } private unsafe static bool CheckNamespace(IntPtr namespaceName, ReadOnlySpan<byte> bytes) { if (namespaceName == IntPtr.Zero) { return false; } byte* ptr = (byte*)(void*)namespaceName; for (int i = 0; i < bytes.Length; i++) { if (ptr[i] != bytes[i]) { return false; } } byte b = ptr[bytes.Length]; if (b != 0) { return b == 46; } return true; } private static IntPtr GetMonoClassWithAssemblyNameDetour(IntPtr thisMonoManager, IntPtr result, IntPtr className, IntPtr namespaceName, ref BasicStringRef assemblyName) { ReadOnlySpan<byte> bytes = "DunGen"u8; ReadOnlySpan<byte> span = "Assembly-CSharp"u8; if (CheckNamespace(namespaceName, bytes) && span.SequenceEqual(assemblyName)) { ReadOnlySpan<byte> data = "DunGen"u8; BasicStringRef param_ = new BasicStringRef(data); getMonoClassWithAssemblyNameOriginal(thisMonoManager, result, className, namespaceName, ref param_); } else { getMonoClassWithAssemblyNameOriginal(thisMonoManager, result, className, namespaceName, ref assemblyName); } return result; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }