using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.MemoryMappedFiles; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using Iced.Intel; using Il2CppInterop.Common.Attributes; using Il2CppInterop.Common.Host; using Il2CppInterop.Common.Maps; using Il2CppInterop.Common.XrefScans; using Microsoft.CodeAnalysis; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("Il2CppInterop.Runtime")] [assembly: InternalsVisibleTo("Il2CppInterop.Generator")] [assembly: InternalsVisibleTo("Il2CppInterop.HarmonySupport")] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("BepInEx, knah et al.")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Common API for Il2CppInterop packages")] [assembly: AssemblyFileVersion("1.4.6.0")] [assembly: AssemblyInformationalVersion("1.4.6-ci.389")] [assembly: AssemblyProduct("Il2CppInterop.Common")] [assembly: AssemblyTitle("Il2CppInterop.Common")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.6.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class IsReadOnlyAttribute : Attribute { } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NativeIntegerAttribute : Attribute { public readonly bool[] TransformFlags; public NativeIntegerAttribute() { TransformFlags = new bool[1] { true }; } public NativeIntegerAttribute(bool[] P_0) { TransformFlags = P_0; } } } namespace Il2CppInterop.Common { public static class Il2CppInteropUtils { private static FieldInfo? GetFieldInfoFromMethod(MethodBase method, string prefix) { MethodBody? obj = method.GetMethodBody() ?? throw new ArgumentException("Target method may not be abstract"); Module module = method.DeclaringType.Assembly.Modules.Single(); foreach (var (opCode, num) in MiniIlParser.Decode(obj.GetILAsByteArray())) { if (opCode != OpCodes.Ldsfld) { continue; } FieldInfo fieldInfo = module.ResolveField((int)num, method.DeclaringType.GenericTypeArguments, method.GetGenericArguments()); if (!(fieldInfo?.FieldType != typeof(IntPtr))) { if (fieldInfo.Name.StartsWith(prefix)) { return fieldInfo; } if (method.IsGenericMethod && fieldInfo.DeclaringType.Name.StartsWith("MethodInfoStoreGeneric_") && fieldInfo.Name == "Pointer") { return fieldInfo; } } } return null; } public static FieldInfo GetIl2CppMethodInfoPointerFieldForGeneratedMethod(MethodBase method) { return GetFieldInfoFromMethod(method, "NativeMethodInfoPtr_"); } public static FieldInfo GetIl2CppFieldInfoPointerFieldForGeneratedFieldAccessor(MethodBase method) { return GetFieldInfoFromMethod(method, "NativeFieldInfoPtr_"); } } public static class LoggerExtensions { public static T AddLogger(this T host, ILogger logger) where T : BaseHost { host.AddComponent(new Logger(logger)); return host; } } internal class Logger : IHostComponent, IDisposable { public static ILogger Instance { get; private set; } = NullLogger.Instance; public Logger(ILogger logger) { Instance = logger; } public void Start() { } public void Dispose() { Instance = NullLogger.Instance; } } internal static class MiniIlParser { private static readonly Dictionary OpCodesMap; private static readonly HashSet PrefixCodes; static MiniIlParser() { OpCodesMap = new Dictionary(); PrefixCodes = new HashSet(); FieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (!(fieldInfo.FieldType != typeof(OpCode))) { OpCode value = (OpCode)fieldInfo.GetValue(null); OpCodesMap[value.Value] = value; if ((ushort)value.Value > 255) { PrefixCodes.Add((byte)((ushort)value.Value >> 8)); } } } } public static IEnumerable<(OpCode, long)> Decode(byte[] ilBytes) { int argLength; for (int index = 0; index < ilBytes.Length; index += argLength) { short num = ilBytes[index++]; if (PrefixCodes.Contains(num)) { num = (short)((ushort)(num << 8) | ilBytes[index++]); } if (!OpCodesMap.TryGetValue(num, out var value)) { throw new NotSupportedException($"Unknown opcode {num} encountered"); } argLength = GetOperandSize(value); switch (argLength) { case 0: yield return (value, 0L); break; case 1: yield return (value, ilBytes[index]); break; case 2: yield return (value, BitConverter.ToInt16(ilBytes, index)); break; case 4: yield return (value, BitConverter.ToInt32(ilBytes, index)); break; case 8: yield return (value, BitConverter.ToInt64(ilBytes, index)); break; default: throw new NotSupportedException($"Unsupported opcode argument length {argLength}"); } } } private static int GetOperandSize(OpCode opCode) { switch (opCode.OperandType) { case OperandType.InlineBrTarget: case OperandType.InlineField: case OperandType.InlineI: case OperandType.InlineMethod: case OperandType.InlineSig: case OperandType.InlineString: case OperandType.InlineSwitch: case OperandType.InlineTok: case OperandType.InlineType: case OperandType.ShortInlineR: return 4; case OperandType.InlineI8: case OperandType.InlineR: return 8; case OperandType.InlineNone: return 0; case OperandType.InlineVar: return 2; case OperandType.ShortInlineBrTarget: case OperandType.ShortInlineI: case OperandType.ShortInlineVar: return 1; default: throw new ArgumentOutOfRangeException(); } } } } namespace Il2CppInterop.Common.XrefScans { internal static class GeneratedDatabasesUtil { private static string? DatabasesLocationOverride => Environment.GetEnvironmentVariable("IL2CPP_INTEROP_DATABASES_LOCATION"); public static string GetDatabasePath(string databaseName) { return Path.Combine(DatabasesLocationOverride ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), databaseName); } } internal interface IXrefScannerImpl { (XrefScanUtil.InitMetadataForMethod, IntPtr)? GetMetadataResolver(); bool XrefGlobalClassFilter(IntPtr movTarget); } public readonly struct XrefInstance { public readonly XrefType Type; public readonly nint Pointer; public readonly nint FoundAt; public XrefInstance(XrefType type, nint pointer, nint foundAt) { Type = type; Pointer = pointer; FoundAt = foundAt; } internal XrefInstance RelativeToBase(nint baseAddress) { return new XrefInstance(Type, Pointer - baseAddress, FoundAt - baseAddress); } } public static class XrefScanMethodDb { private static readonly MethodAddressToTokenMap MethodMap; private static readonly MethodXrefScanCache XrefScanCache; private static readonly long GameAssemblyBase; private static XrefScanUtil.InitMetadataForMethod ourMetadataInitForMethodDelegate; static XrefScanMethodDb() { MethodMap = new MethodAddressToTokenMap(GeneratedDatabasesUtil.GetDatabasePath("MethodAddressToToken.db")); XrefScanCache = new MethodXrefScanCache(GeneratedDatabasesUtil.GetDatabasePath("MethodXrefScanCache.db")); foreach (ProcessModule module in Process.GetCurrentProcess().Modules) { if (module.ModuleName == "GameAssembly.dll") { GameAssemblyBase = (long)module.BaseAddress; break; } } } public static MethodBase TryResolvePointer(IntPtr methodStart) { return MethodMap.Lookup((long)methodStart - GameAssemblyBase); } internal static IEnumerable ListUsers(CachedScanResultsAttribute attribute) { for (int i = attribute.RefRangeStart; i < attribute.RefRangeEnd; i++) { yield return XrefScanCache.GetAt(i).AsXrefInstance(GameAssemblyBase); } } internal static IEnumerable CachedXrefScan(CachedScanResultsAttribute attribute) { for (int i = attribute.XrefRangeStart; i < attribute.XrefRangeEnd; i++) { yield return XrefScanCache.GetAt(i).AsXrefInstance(GameAssemblyBase); } } internal static void CallMetadataInitForMethod(CachedScanResultsAttribute attribute) { if (attribute.MetadataInitFlagRva != 0L && attribute.MetadataInitTokenRva != 0L && Marshal.ReadByte((IntPtr)(GameAssemblyBase + attribute.MetadataInitFlagRva)) == 0) { if (ourMetadataInitForMethodDelegate == null) { ourMetadataInitForMethodDelegate = Marshal.GetDelegateForFunctionPointer((IntPtr)(GameAssemblyBase + XrefScanCache.Header.InitMethodMetadataRva)); } int metadataUsageToken = Marshal.ReadInt32((IntPtr)(GameAssemblyBase + attribute.MetadataInitTokenRva)); ourMetadataInitForMethodDelegate(metadataUsageToken); Marshal.WriteByte((IntPtr)(GameAssemblyBase + attribute.MetadataInitFlagRva), 1); } } } public static class XrefScanner { public unsafe static IEnumerable XrefScan(MethodBase methodBase) { object obj = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(methodBase)?.GetValue(null); if (obj == null) { return Enumerable.Empty(); } CachedScanResultsAttribute cachedScanResultsAttribute = null; try { cachedScanResultsAttribute = methodBase.GetCustomAttribute(inherit: false); } catch (Exception ex) { Logger.Instance.LogWarning("Failed to get custom attribute for {TypeName}.{MethodName}: {Error}. Falling back to scanning", methodBase.DeclaringType.FullName, methodBase.Name, ex.Message); } if (cachedScanResultsAttribute == null) { XrefScanUtil.CallMetadataInitForMethod(methodBase); return XrefScanImpl(DecoderForAddress(*(IntPtr*)(void*)(IntPtr)obj)); } if (cachedScanResultsAttribute.XrefRangeStart == cachedScanResultsAttribute.XrefRangeEnd) { return Enumerable.Empty(); } XrefScanMethodDb.CallMetadataInitForMethod(cachedScanResultsAttribute); return from it in XrefScanMethodDb.CachedXrefScan(cachedScanResultsAttribute) where it.Type == XrefType.Method || XrefScannerManager.Impl.XrefGlobalClassFilter(it.Pointer) select it; } public static IEnumerable UsedBy(MethodBase methodBase) { CachedScanResultsAttribute customAttribute = methodBase.GetCustomAttribute(inherit: false); if (customAttribute == null || customAttribute.RefRangeStart == customAttribute.RefRangeEnd) { return Enumerable.Empty(); } return XrefScanMethodDb.ListUsers(customAttribute); } internal unsafe static Decoder DecoderForAddress(IntPtr codeStart, int lengthLimit = 1000) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown if (codeStart == IntPtr.Zero) { throw new NullReferenceException("codeStart"); } StreamCodeReader val = new StreamCodeReader((Stream)new UnmanagedMemoryStream((byte*)(void*)codeStart, lengthLimit, lengthLimit, FileAccess.Read)); Decoder obj = Decoder.Create(IntPtr.Size * 8, (CodeReader)(object)val, (DecoderOptions)0); obj.IP = (ulong)(long)codeStart; return obj; } internal static IEnumerable XrefScanImpl(Decoder decoder, bool skipClassCheck = false) { Instruction instruction = default(Instruction); while (true) { decoder.Decode(ref instruction); if ((int)decoder.LastError == 2 || (int)((Instruction)(ref instruction)).FlowControl == 4 || (int)((Instruction)(ref instruction)).Mnemonic == 287 || (int)((Instruction)(ref instruction)).Mnemonic == 288) { break; } if ((int)((Instruction)(ref instruction)).Mnemonic == 59 || (int)((Instruction)(ref instruction)).Mnemonic == 308) { ulong num = ExtractTargetAddress(in instruction); if (num != 0L) { yield return new XrefInstance(XrefType.Method, (nint)num, (nint)((Instruction)(ref instruction)).IP); } } else { if ((int)((Instruction)(ref instruction)).FlowControl == 1 || !IsMoveMnemonic(((Instruction)(ref instruction)).Mnemonic)) { continue; } XrefInstance? xrefInstance = null; try { if ((int)((Instruction)(ref instruction)).Op1Kind == 24 && ((Instruction)(ref instruction)).IsIPRelativeMemoryOperand) { nint num2 = (IntPtr)(long)((Instruction)(ref instruction)).IPRelativeMemoryAddress; if ((int)((Instruction)(ref instruction)).MemorySize != 5) { continue; } if (skipClassCheck || XrefScannerManager.Impl.XrefGlobalClassFilter(num2)) { xrefInstance = new XrefInstance(XrefType.Global, num2, (IntPtr)(long)((Instruction)(ref instruction)).IP); } } } catch (Exception ex) { Logger.Instance.LogError("{Error}", ex.ToString()); } if (xrefInstance.HasValue) { yield return xrefInstance.Value; } } } } internal static bool IsMoveMnemonic(Mnemonic mnemonic) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 if ((int)mnemonic >= 77 && ((int)mnemonic <= 92 || (int)mnemonic == 414)) { return true; } return false; } internal static ulong ExtractTargetAddress(in Instruction instruction) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown OpKind op0Kind = ((Instruction)(ref instruction)).Op0Kind; return (op0Kind - 1) switch { 0 => ((Instruction)(ref instruction)).NearBranch16, 1 => ((Instruction)(ref instruction)).NearBranch32, 2 => ((Instruction)(ref instruction)).NearBranch64, 3 => ((Instruction)(ref instruction)).FarBranch16, 4 => ((Instruction)(ref instruction)).FarBranch32, _ => 0uL, }; } } public static class XrefScannerLowLevel { public static IEnumerable JumpTargets(IntPtr codeStart, bool ignoreRetn = false) { return JumpTargetsImpl(XrefScanner.DecoderForAddress(codeStart), ignoreRetn); } private static IEnumerable JumpTargetsImpl(Decoder myDecoder, bool ignoreRetn) { bool firstFlowControl = true; Instruction instruction = default(Instruction); while (true) { myDecoder.Decode(ref instruction); if ((int)myDecoder.LastError == 2 || (int)((Instruction)(ref instruction)).Mnemonic == 1620 || ((int)((Instruction)(ref instruction)).FlowControl == 4 && !ignoreRetn)) { break; } if (((int)((Instruction)(ref instruction)).FlowControl == 1 || (int)((Instruction)(ref instruction)).FlowControl == 5) && !((Instruction)(ref instruction)).IsJmpShort) { yield return (IntPtr)(long)ExtractTargetAddress(in instruction); if (firstFlowControl && (int)((Instruction)(ref instruction)).FlowControl == 1) { break; } } if ((int)((Instruction)(ref instruction)).FlowControl != 0) { firstFlowControl = false; } instruction = default(Instruction); } } public static IEnumerable CallAndIndirectTargets(IntPtr pointer) { return CallAndIndirectTargetsImpl(XrefScanner.DecoderForAddress(pointer, 1048576)); } private static IEnumerable CallAndIndirectTargetsImpl(Decoder decoder) { Instruction instruction = default(Instruction); while (true) { decoder.Decode(ref instruction); if ((int)decoder.LastError == 2 || (int)((Instruction)(ref instruction)).FlowControl == 4 || (int)((Instruction)(ref instruction)).Mnemonic == 287 || (int)((Instruction)(ref instruction)).Mnemonic == 288) { break; } if ((int)((Instruction)(ref instruction)).Mnemonic == 59 || (int)((Instruction)(ref instruction)).Mnemonic == 308) { ulong num = XrefScanner.ExtractTargetAddress(in instruction); if (num != 0L) { yield return (IntPtr)(long)num; } } else if ((int)((Instruction)(ref instruction)).Mnemonic == 374 && (int)((Instruction)(ref instruction)).MemoryBase == 70) { ulong iPRelativeMemoryAddress = ((Instruction)(ref instruction)).IPRelativeMemoryAddress; if (iPRelativeMemoryAddress != 0L) { yield return (IntPtr)(long)iPRelativeMemoryAddress; } } } } private static ulong ExtractTargetAddress(in Instruction instruction) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown OpKind op0Kind = ((Instruction)(ref instruction)).Op0Kind; return (op0Kind - 1) switch { 0 => ((Instruction)(ref instruction)).NearBranch16, 1 => ((Instruction)(ref instruction)).NearBranch32, 2 => ((Instruction)(ref instruction)).NearBranch64, 3 => ((Instruction)(ref instruction)).FarBranch16, 4 => ((Instruction)(ref instruction)).FarBranch32, _ => throw new ArgumentOutOfRangeException(), }; } } internal static class XrefScannerManagerExtensions { public static T AddXrefScanner(this T host) where T : BaseHost where TScanner : IXrefScannerImpl, new() { host.AddComponent(new XrefScannerManager(new TScanner())); return host; } } internal class XrefScannerManager : IHostComponent, IDisposable { private static IXrefScannerImpl s_xrefScanner; public static IXrefScannerImpl Impl { get { if (s_xrefScanner == null) { throw new InvalidOperationException("XrefScanner is not initialized! Initialize the host before using XrefScanner!"); } return s_xrefScanner; } } public XrefScannerManager(IXrefScannerImpl impl) { s_xrefScanner = impl; } public void Dispose() { s_xrefScanner = null; } public void Start() { } } internal static class XrefScanUtil { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void InitMetadataForMethod(int metadataUsageToken); private static InitMetadataForMethod ourMetadataInitForMethodDelegate; private static IntPtr ourMetadataInitForMethodPointer; internal static event Func<(InitMetadataForMethod, IntPtr)> InitRuntimeUtils; internal unsafe static bool CallMetadataInitForMethod(MethodBase method) { if (ourMetadataInitForMethodPointer == IntPtr.Zero) { (InitMetadataForMethod, IntPtr)? metadataResolver = XrefScannerManager.Impl.GetMetadataResolver(); if (metadataResolver.HasValue) { (InitMetadataForMethod, IntPtr) valueOrDefault = metadataResolver.GetValueOrDefault(); var (initMetadataForMethod, _) = valueOrDefault; if (initMetadataForMethod != null) { IntPtr item = valueOrDefault.Item2; ourMetadataInitForMethodDelegate = initMetadataForMethod; ourMetadataInitForMethodPointer = item; goto IL_0057; } } return false; } goto IL_0057; IL_0057: object obj = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(method)?.GetValue(null); if (obj == null) { return false; } IntPtr codeStart = *(IntPtr*)(void*)(IntPtr)obj; IntPtr intPtr = XrefScannerLowLevel.JumpTargets(codeStart).FirstOrDefault(); if (intPtr != ourMetadataInitForMethodPointer || intPtr == IntPtr.Zero) { return false; } IntPtr intPtr2 = XrefScanUtilFinder.FindLastRcxReadAddressBeforeCallTo(codeStart, ourMetadataInitForMethodPointer); IntPtr intPtr3 = XrefScanUtilFinder.FindByteWriteTargetRightAfterCallTo(codeStart, ourMetadataInitForMethodPointer); if (intPtr2 == IntPtr.Zero || intPtr3 == IntPtr.Zero) { return false; } if (Marshal.ReadByte(intPtr3) == 0) { ourMetadataInitForMethodDelegate(Marshal.ReadInt32(intPtr2)); Marshal.WriteByte(intPtr3, 1); } return true; } } internal static class XrefScanUtilFinder { public static IntPtr FindLastRcxReadAddressBeforeCallTo(IntPtr codeStart, IntPtr callTarget) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Invalid comparison between Unknown and I4 //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Invalid comparison between Unknown and I4 //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Invalid comparison between Unknown and I4 Decoder val = XrefScanner.DecoderForAddress(codeStart); IntPtr result = IntPtr.Zero; Instruction instruction = default(Instruction); while (true) { val.Decode(ref instruction); if ((int)val.LastError == 2) { return IntPtr.Zero; } if ((int)((Instruction)(ref instruction)).FlowControl == 4) { return IntPtr.Zero; } if ((int)((Instruction)(ref instruction)).FlowControl == 1) { continue; } if ((int)((Instruction)(ref instruction)).Mnemonic == 287 || (int)((Instruction)(ref instruction)).Mnemonic == 288) { return IntPtr.Zero; } if ((int)((Instruction)(ref instruction)).Mnemonic == 59 && (IntPtr)(long)ExtractTargetAddress(in instruction) == callTarget) { break; } if ((int)((Instruction)(ref instruction)).Mnemonic == 414 && (int)((Instruction)(ref instruction)).Op0Kind == 0 && (int)((Instruction)(ref instruction)).Op0Register == 38 && (int)((Instruction)(ref instruction)).Op1Kind == 24 && ((Instruction)(ref instruction)).IsIPRelativeMemoryOperand) { IntPtr intPtr = (IntPtr)(long)((Instruction)(ref instruction)).IPRelativeMemoryAddress; if ((int)((Instruction)(ref instruction)).MemorySize == 3 || (int)((Instruction)(ref instruction)).MemorySize == 11) { result = intPtr; } } } return result; } public static IntPtr FindByteWriteTargetRightAfterCallTo(IntPtr codeStart, IntPtr callTarget) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Invalid comparison between Unknown and I4 //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Invalid comparison between Unknown and I4 //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Invalid comparison between Unknown and I4 //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Invalid comparison between Unknown and I4 Decoder val = XrefScanner.DecoderForAddress(codeStart); bool flag = false; Instruction instruction = default(Instruction); while (true) { val.Decode(ref instruction); if ((int)val.LastError == 2) { return IntPtr.Zero; } if ((int)((Instruction)(ref instruction)).FlowControl == 4) { return IntPtr.Zero; } if ((int)((Instruction)(ref instruction)).FlowControl != 1) { if ((int)((Instruction)(ref instruction)).Mnemonic == 287 || (int)((Instruction)(ref instruction)).Mnemonic == 288) { return IntPtr.Zero; } if ((int)((Instruction)(ref instruction)).Mnemonic == 59 && (IntPtr)(long)ExtractTargetAddress(in instruction) == callTarget) { flag = true; } if ((int)((Instruction)(ref instruction)).Mnemonic == 414 && flag && (int)((Instruction)(ref instruction)).Op0Kind == 24 && ((int)((Instruction)(ref instruction)).MemorySize == 9 || (int)((Instruction)(ref instruction)).MemorySize == 1)) { break; } } } return (IntPtr)(long)((Instruction)(ref instruction)).IPRelativeMemoryAddress; } private static ulong ExtractTargetAddress(in Instruction instruction) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown OpKind op0Kind = ((Instruction)(ref instruction)).Op0Kind; return (op0Kind - 1) switch { 0 => ((Instruction)(ref instruction)).NearBranch16, 1 => ((Instruction)(ref instruction)).NearBranch32, 2 => ((Instruction)(ref instruction)).NearBranch64, 3 => ((Instruction)(ref instruction)).FarBranch16, 4 => ((Instruction)(ref instruction)).FarBranch32, _ => 0uL, }; } } public enum XrefType { Global, Method } } namespace Il2CppInterop.Common.Maps { public class MethodAddressToTokenMap : MethodAddressToTokenMapBase { public MethodAddressToTokenMap(string filePath) : base(filePath) { } protected override Assembly LoadAssembly(string assemblyName) { return Assembly.Load(assemblyName); } protected override MethodBase? ResolveMethod(Assembly? assembly, int token) { return assembly?.ManifestModule.ResolveMethod(token); } } public abstract class MethodAddressToTokenMapBase : IDisposable, IEnumerable<(long, TMethod?)>, IEnumerable { public class Enumerator : IEnumerator<(long, TMethod?)>, IEnumerator, IDisposable { private readonly MethodAddressToTokenMapBase myMap; private int myOffset = -1; object IEnumerator.Current => Current; public (long, TMethod?) Current { get; private set; } public Enumerator(MethodAddressToTokenMapBase map) { myMap = map; } public unsafe bool MoveNext() { myOffset++; if (myOffset < myMap.myHeader.NumMethods) { int token = myMap.myValues[myOffset * 2]; int index = myMap.myValues[myOffset * 2 + 1]; Current = (myMap.myPointers[myOffset], myMap.ResolveMethod(myMap.myAssemblyList[index], token)); return true; } return false; } public void Reset() { myOffset = -1; } public void Dispose() { } } public const int Magic = 1297370453; public const int Version = 1; public const string FileName = "MethodAddressToToken.db"; private readonly MemoryMappedViewAccessor? myAccessor; private readonly List myAssemblyList = new List(); protected readonly string myFilePath; private readonly MethodAddressToTokenMapFileHeader myHeader; private readonly MemoryMappedFile? myMapFile; private unsafe long* myPointers; private unsafe int* myValues; public unsafe MethodAddressToTokenMapBase(string filePath) { myFilePath = filePath; myMapFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0L, MemoryMappedFileAccess.Read); (myAccessor = myMapFile.CreateViewAccessor(0L, 0L, MemoryMappedFileAccess.Read)).Read(0L, out myHeader); if (myHeader.Magic != 1297370453) { myMapFile.Dispose(); throw new FileLoadException($"File magic mismatched for {filePath}; Expected {1297370453:X}, got {myHeader.Magic:X}"); } if (myHeader.Version != 1) { myMapFile.Dispose(); throw new FileLoadException($"File version mismatched for {filePath}; Expected {1}, got {myHeader.Version}"); } int num = Marshal.SizeOf(); using BinaryReader binaryReader = new BinaryReader(myMapFile.CreateViewStream(num, 0L, MemoryMappedFileAccess.Read), Encoding.UTF8, leaveOpen: false); for (int i = 0; i < myHeader.NumAssemblies; i++) { string assemblyName = binaryReader.ReadString(); myAssemblyList.Add(LoadAssembly(assemblyName)); } byte* pointer = null; myAccessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); myPointers = (long*)(pointer + myHeader.DataOffset); myValues = (int*)(pointer + myHeader.DataOffset + myHeader.NumMethods * 8); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator<(long, TMethod?)> IEnumerable<(long, TMethod)>.GetEnumerator() { return GetEnumerator(); } protected abstract TAssembly? LoadAssembly(string assemblyName); private unsafe void ReleaseUnmanagedResources() { myAccessor?.SafeMemoryMappedViewHandle.ReleasePointer(); myPointers = null; myValues = null; } private void Dispose(bool disposing) { ReleaseUnmanagedResources(); if (disposing) { myAccessor?.Dispose(); myMapFile?.Dispose(); } } ~MethodAddressToTokenMapBase() { Dispose(disposing: false); } public unsafe TMethod? Lookup(long parsedRva) { int num = 0; int num2 = myHeader.NumMethods; while (num2 - num > 1) { int num3 = (num + num2) / 2; if (myPointers[num3] > parsedRva) { num2 = num3; } else { num = num3; } } if (myPointers[num] != parsedRva) { return default(TMethod); } int token = myValues[num * 2]; int index = myValues[num * 2 + 1]; return ResolveMethod(myAssemblyList[index], token); } protected abstract TMethod? ResolveMethod(TAssembly? assembly, int token); public Enumerator GetEnumerator() { return new Enumerator(this); } } public struct MethodAddressToTokenMapFileHeader { public int Magic; public int Version; public int NumAssemblies; public int NumMethods; public int DataOffset; } public class MethodXrefScanCache : IDisposable { public struct FileHeader { public int Magic; public int Version; public long InitMethodMetadataRva; } public struct MethodData { public long Address; public long FoundAt; public XrefType Type; public XrefInstance AsXrefInstance(long baseAddress) { return new XrefInstance(Type, (IntPtr)(baseAddress + Address), (IntPtr)(baseAddress + FoundAt)); } public static MethodData FromXrefInstance(XrefInstance instance) { MethodData result = default(MethodData); result.Address = instance.Pointer; result.FoundAt = instance.FoundAt; result.Type = instance.Type; return result; } } public const int Magic = 1129860437; public const int Version = 1; public const string FileName = "MethodXrefScanCache.db"; public readonly FileHeader Header; private readonly MemoryMappedViewAccessor myAccessor; private readonly MemoryMappedFile myMapFile; private unsafe MethodData* myData; public unsafe MethodXrefScanCache(string filePath) { myMapFile = MemoryMappedFile.CreateFromFile(filePath, FileMode.Open, null, 0L, MemoryMappedFileAccess.Read); (myAccessor = myMapFile.CreateViewAccessor(0L, 0L, MemoryMappedFileAccess.Read)).Read(0L, out Header); if (Header.Magic != 1129860437) { myMapFile.Dispose(); throw new FileLoadException($"File magic mismatched for {filePath}; Expected {1129860437:X}, got {Header.Magic:X}"); } if (Header.Version != 1) { myMapFile.Dispose(); throw new FileLoadException($"File version mismatched for {filePath}; Expected {1}, got {Header.Version}"); } int num = Marshal.SizeOf(); byte* pointer = null; myAccessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); myData = (MethodData*)(pointer + num); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } private unsafe void ReleaseUnmanagedResources() { myAccessor.SafeMemoryMappedViewHandle.ReleasePointer(); myData = null; } private void Dispose(bool disposing) { ReleaseUnmanagedResources(); if (disposing) { myAccessor?.Dispose(); myMapFile?.Dispose(); } } ~MethodXrefScanCache() { Dispose(disposing: false); } internal unsafe ref MethodData GetAt(int index) { return ref myData[index]; } } } namespace Il2CppInterop.Common.Host { public abstract class BaseHost : IDisposable { private static BaseHost s_instance; private readonly Dictionary _components = new Dictionary(); protected static T GetInstance() where T : BaseHost { if (!(s_instance is T result)) { throw new InvalidOperationException(typeof(T).Name + " is not yet initialized. Call " + typeof(T).Name + ".Create and " + typeof(T).Name + ".Start first."); } return result; } protected static void SetInstance(T instance) where T : BaseHost { BaseHost baseHost = s_instance; if (baseHost != null && !(baseHost is T)) { s_instance.Dispose(); s_instance = null; } s_instance = instance; } public void AddComponent(TComponent component) where TComponent : IHostComponent { _components.Add(typeof(TComponent), component); } public TComponent GetComponent() where TComponent : IHostComponent { if (!_components.TryGetValue(typeof(TComponent), out var value)) { throw new InvalidOperationException("The host does not have component " + typeof(TComponent).FullName + " registered."); } return (TComponent)value; } public virtual void Start() { foreach (IHostComponent value in _components.Values) { value.Start(); } } public virtual void Dispose() { foreach (IHostComponent value in _components.Values) { value.Dispose(); } _components.Clear(); } } public interface IHostComponent : IDisposable { void Start(); } } namespace Il2CppInterop.Common.Extensions { internal static class AssemblyExtensions { public static Type[] GetTypesSafe(this Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null).ToArray(); } } } } namespace Il2CppInterop.Common.Attributes { [AttributeUsage(AttributeTargets.Method, Inherited = false)] public class CachedScanResultsAttribute : Attribute { public long MetadataInitFlagRva; public long MetadataInitTokenRva; public int RefRangeEnd; public int RefRangeStart; public int XrefRangeEnd; public int XrefRangeStart; } [AttributeUsage(AttributeTargets.Method, Inherited = false)] public class CallerCountAttribute : Attribute { public readonly int Count; public CallerCountAttribute(int count) { Count = count; } } [AttributeUsage(AttributeTargets.All, Inherited = false)] public class ObfuscatedNameAttribute : Attribute { public readonly string ObfuscatedName; public ObfuscatedNameAttribute(string obfuscatedName) { ObfuscatedName = obfuscatedName; } } [AttributeUsage(AttributeTargets.Enum, Inherited = false)] public class OriginalNameAttribute : Attribute { public readonly string AssemblyName; public readonly string Namespace; public readonly string Name; public OriginalNameAttribute(string assemblyName, string @namespace, string name) { AssemblyName = assemblyName; Namespace = @namespace; Name = name; } } }