using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using AssetRipper.VersionUtilities; using LibCpp2IL.BinaryStructures; using LibCpp2IL.Elf; using LibCpp2IL.Logging; using LibCpp2IL.MachO; using LibCpp2IL.Metadata; using LibCpp2IL.NintendoSwitch; using LibCpp2IL.PE; using LibCpp2IL.Reflection; using LibCpp2IL.Wasm; using Microsoft.CodeAnalysis; using WasmDisassembler; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyMetadata("IsTrimmable", "True")] [assembly: AssemblyCompany("Samboy063")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © Samboy063 2019-2023")] [assembly: AssemblyDescription("Library for interacting with IL2CPP metadata and binaries")] [assembly: AssemblyFileVersion("2022.1.0.0")] [assembly: AssemblyInformationalVersion("2022.1.0-development.866+f87ccd1")] [assembly: AssemblyProduct("LibCpp2IL")] [assembly: AssemblyTitle("LibCpp2IL")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/SamboyCoding/Cpp2IL.git")] [assembly: AssemblyVersion("2022.1.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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; } } } namespace LibCpp2IL { public static class ArmUtils { public const uint PC_REG = 15u; public static (uint register, ushort immediateValue) GetOperandsForLiteralLdr(uint inst) { if (inst.Bits(16, 16) != 58783) { return (0u, 0); } return (inst.Bits(12, 4), (ushort)inst.Bits(0, 12)); } public static (uint firstReg, uint secondReg, uint thirdReg) GetOperandsForRegisterLdr(uint inst) { if (inst.Bits(20, 12) != 3705) { return (0u, 0u, 0u); } uint item = inst.Bits(16, 4); uint item2 = inst.Bits(12, 4); uint item3 = inst.Bits(0, 4); return (item2, item, item3); } public static (uint firstReg, uint secondReg, uint thirdReg) GetOperandsForRegisterAdd(uint inst) { if (inst.Bits(21, 11) != 1796) { return (0u, 0u, 0u); } uint item = inst.Bits(12, 4); uint item2 = inst.Bits(16, 4); uint item3 = inst.Bits(0, 4); return (item, item2, item3); } } public static class BinaryReaderHelpers { public static byte[] Reverse(this byte[] b) { Array.Reverse(b); return b; } public static ushort ReadUInt16WithReversedBits(this BinaryReader binRdr) { return BitConverter.ToUInt16(binRdr.ReadBytesRequired(2).Reverse(), 0); } public static short ReadInt16WithReversedBits(this BinaryReader binRdr) { return BitConverter.ToInt16(binRdr.ReadBytesRequired(2).Reverse(), 0); } public static uint ReadUInt32WithReversedBits(this BinaryReader binRdr) { return BitConverter.ToUInt32(binRdr.ReadBytesRequired(4).Reverse(), 0); } public static int ReadInt32WithReversedBits(this BinaryReader binRdr) { return BitConverter.ToInt32(binRdr.ReadBytesRequired(4).Reverse(), 0); } public static ulong ReadUInt64WithReversedBits(this BinaryReader binRdr) { return BitConverter.ToUInt64(binRdr.ReadBytesRequired(8).Reverse(), 0); } public static long ReadInt64WithReversedBits(this BinaryReader binRdr) { return BitConverter.ToInt64(binRdr.ReadBytesRequired(8).Reverse(), 0); } public static float ReadSingleWithReversedBits(this BinaryReader binRdr) { return BitConverter.ToSingle(binRdr.ReadBytesRequired(4).Reverse(), 0); } public static double ReadDoubleWithReversedBits(this BinaryReader binRdr) { return BitConverter.ToDouble(binRdr.ReadBytesRequired(8).Reverse(), 0); } private static byte[] ReadBytesRequired(this BinaryReader binRdr, int byteCount) { byte[] array = binRdr.ReadBytes(byteCount); if (array.Length != byteCount) { throw new EndOfStreamException($"{byteCount} bytes required from stream, but only {array.Length} returned."); } return array; } } public class BinarySearcher { private readonly Il2CppBinary _binary; private readonly byte[] _binaryBytes; private readonly int _methodCount; private readonly int _typeDefinitionsCount; public BinarySearcher(Il2CppBinary binary, int methodCount, int typeDefinitionsCount) { _binary = binary; _binaryBytes = binary.GetRawBinaryContent(); _methodCount = methodCount; _typeDefinitionsCount = typeDefinitionsCount; } private static int FindSequence(byte[] haystack, byte[] needle, int requiredAlignment = 1, int startOffset = 0) { ReadOnlySpan other = new ReadOnlySpan(needle); Span span = haystack.AsSpan(); byte value = other[0]; int num = Array.IndexOf(haystack, value, startOffset); int length = other.Length; int num2 = haystack.Length - length; bool flag = requiredAlignment > 1; while (0 <= num && num <= num2) { if ((!flag || num % requiredAlignment == 0) && span.Slice(num, length).SequenceEqual(other)) { return num; } num = Array.IndexOf(haystack, value, num + 1); } return -1; } private IEnumerable FindAllBytes(byte[] signature, int alignment = 0) { LibLogger.VerboseNewline("\t\t\tLooking for bytes: " + string.Join(" ", signature.Select((byte b) => b.ToString("x2")))); int offset = 0; int ptrSize = (_binary.is32Bit ? 4 : 8); while (offset != -1) { offset = FindSequence(_binaryBytes, signature, (alignment != 0) ? alignment : ptrSize, offset); if (offset != -1) { yield return (uint)offset; offset += ptrSize; } } } private IEnumerable FindAllStrings(string str) { return FindAllBytes(Encoding.ASCII.GetBytes(str), 1); } private IEnumerable FindAllDWords(uint word) { byte[] bytes = BitConverter.GetBytes(word); Il2CppBinary binary = _binary; return FindAllBytes(bytes, (binary is WasmFile || binary is NsoFile) ? 1 : 4); } private IEnumerable FindAllQWords(ulong word) { byte[] bytes = BitConverter.GetBytes(word); Il2CppBinary binary = _binary; return FindAllBytes(bytes, (binary is WasmFile || binary is NsoFile) ? 1 : 8); } private IEnumerable FindAllWords(ulong word) { if (!_binary.is32Bit) { return FindAllQWords(word); } return FindAllDWords((uint)word); } private IEnumerable MapOffsetsToVirt(IEnumerable offsets) { foreach (uint offset2 in offsets) { uint offset = offset2; if (_binary.TryMapRawAddressToVirtual(in offset, out var va)) { yield return va; } } } private IEnumerable FindAllMappedWords(ulong word) { List offsets = FindAllWords(word).ToList(); return MapOffsetsToVirt(offsets); } private IEnumerable FindAllMappedWords(IEnumerable va) { return va.SelectMany(FindAllMappedWords); } private IEnumerable FindAllMappedWords(IEnumerable va) { return va.SelectMany((uint a) => FindAllMappedWords(a)); } public ulong FindCodeRegistrationPre2019() { List list = MapOffsetsToVirt(FindAllBytes(BitConverter.GetBytes(_methodCount), 1)).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list.Count} instances of the method count {_methodCount}, as bytes {string.Join(", ", from x in BitConverter.GetBytes((ulong)_methodCount) select $"0x{x:X}")}"); if (list.Count == 0) { return 0uL; } foreach (ulong item in list) { LibLogger.VerboseNewline($"\t\t\tChecking for CodeRegistration at virtual address 0x{item:x}..."); Il2CppCodeRegistration il2CppCodeRegistration = _binary.ReadReadableAtVirtualAddress(item); if (il2CppCodeRegistration.customAttributeCount == (ulong)LibCpp2IlMain.TheMetadata.attributeTypeRanges.Count) { return item; } LibLogger.VerboseNewline($"\t\t\t\tNot a valid CodeRegistration - custom attribute count is {il2CppCodeRegistration.customAttributeCount}, expecting {LibCpp2IlMain.TheMetadata.attributeTypeRanges.Count}"); } return 0uL; } internal ulong FindCodeRegistrationPost2019() { List list = (from idx in FindAllStrings("mscorlib.dll\0") select _binary.MapRawAddressToVirtual(idx)).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list.Count} occurrences of mscorlib.dll: [{string.Join(", ", list.Select((ulong p) => p.ToString("X")))}]"); List list2 = FindAllMappedWords(list).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list2.Count} potential codegen modules for mscorlib: [{string.Join(", ", list2.Select((ulong p) => p.ToString("X")))}]"); List list3 = FindAllMappedWords(list2).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list3.Count} address for potential codegen modules in potential codegen module lists: [{string.Join(", ", list3.Select((ulong p) => p.ToString("X")))}]"); uint ptrSize = (_binary.is32Bit ? 4u : 8u); List list4 = null; if (LibCpp2IlMain.MetadataVersion < 27f) { list4 = ((list3.Count != 1) ? FindAllMappedWords(list3).ToList() : FindAllMappedWords(list3[0]).ToList()); } else { int num = 400; IEnumerable source = list3.AsEnumerable(); int num2 = LibCpp2IlMain.TheMetadata.imageDefinitions.Length; int initialBacktrack = num2 - 10; source = source.Select((ulong va) => va - (ulong)(ptrSize * initialBacktrack)); for (int i = initialBacktrack; i < num; i++) { if ((list4?.Count() ?? 0) == 1) { break; } list4 = FindAllMappedWords(source).ToList(); if (list4.Count == 1) { _binary.Reader.Position = _binary.MapVirtualAddressToRaw(list4.First() - ptrSize); int num3 = _binary.Reader.ReadInt32(); if (num3 < 0 || num3 > num) { list4 = new List(); } else { LibLogger.VerboseNewline($"\t\t\tFound valid address for pCodegenModules after a backtrack of {i}, module count is {LibCpp2IlMain.TheMetadata.imageDefinitions.Length}"); } } source = source.Select((ulong va) => va - ptrSize); } if (list4 == null || !list4.Any()) { throw new Exception("Failed to find pCodegenModules"); } if (list4.Count() > 1) { throw new Exception("Found more than 1 pointer as pCodegenModules"); } } LibLogger.VerboseNewline($"\t\t\tFound {list4.Count} potential pCodegenModules addresses: [{string.Join(", ", list4.Select((ulong p) => p.ToString("X")))}]"); ulong num4 = (ulong)(LibCpp2ILUtils.VersionAwareSizeOf(typeof(Il2CppCodeRegistration)) - ptrSize); LibLogger.VerboseNewline($"\t\t\tpCodegenModules is the second-to-last field of the codereg struct. Therefore on this version and architecture, we need to subtract {num4} bytes from its address to get pCodeReg"); Dictionary fieldsByName = typeof(Il2CppCodeRegistration).GetFields().ToDictionary((FieldInfo f) => f.Name); foreach (ulong item in list4) { ulong num5 = item - num4; if (list4.Count == 1) { LibLogger.VerboseNewline($"\t\t\tOnly found one codegen module pointer, so assuming it's correct and returning pCodeReg = 0x{num5:X}"); return num5; } LibLogger.Verbose($"\t\t\tConsidering potential code registration at 0x{num5:X}..."); if (ValidateCodeRegistration(LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(num5), fieldsByName)) { LibLogger.VerboseNewline("Looks good!"); return num5; } } return 0uL; } public static bool ValidateCodeRegistration(Il2CppCodeRegistration codeReg, Dictionary fieldsByName) { bool result = true; foreach (KeyValuePair item in fieldsByName) { ulong num = (ulong)item.Value.GetValue(codeReg); if (num == 0L) { continue; } long result2; if (item.Key.EndsWith("count", StringComparison.OrdinalIgnoreCase)) { if (num > 458752) { LibLogger.VerboseNewline($"Rejected due to unreasonable count field 0x{num:X} for field {item.Key}"); result = false; break; } } else if (!LibCpp2IlMain.Binary.TryMapVirtualAddressToRaw(num, out result2)) { LibLogger.VerboseNewline($"Rejected due to invalid pointer 0x{num:X} for field {item.Key}"); result = false; break; } } return result; } public ulong FindMetadataRegistrationPre24_5() { ulong num = (ulong)LibCpp2ILUtils.VersionAwareSizeOf(typeof(Il2CppMetadataRegistration)); ulong num2 = (ulong)(_binary.is32Bit ? 4 : 8); ulong bytesToSubtract = num - num2 * 4; List list = MapOffsetsToVirt(FindAllBytes(BitConverter.GetBytes(LibCpp2IlMain.TheMetadata.typeDefs.Length), 1)).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list.Count} instances of the number of type defs, {LibCpp2IlMain.TheMetadata.typeDefs.Length}"); list = list.Select((ulong p) => p - bytesToSubtract).ToList(); foreach (ulong item in list) { Il2CppMetadataRegistration il2CppMetadataRegistration = _binary.ReadReadableAtVirtualAddress(item); if (il2CppMetadataRegistration.metadataUsagesCount == (ulong)LibCpp2IlMain.TheMetadata.metadataUsageLists.Length) { LibLogger.VerboseNewline($"\t\t\tFound and selected probably valid metadata registration at 0x{item:X}."); return item; } LibLogger.VerboseNewline($"\t\t\tSkipping 0x{item:X} as the metadata reg, metadata usage count was 0x{il2CppMetadataRegistration.metadataUsagesCount:X}, expecting 0x{LibCpp2IlMain.TheMetadata.metadataUsageLists.Length:X}"); } return 0uL; } public ulong FindMetadataRegistrationPost24_5() { ulong ptrSize = (ulong)(_binary.is32Bit ? 4 : 8); uint sizeOfMr = (uint)LibCpp2ILUtils.VersionAwareSizeOf(typeof(Il2CppMetadataRegistration)); LibLogger.VerboseNewline($"\t\t\tLooking for the number of type definitions, 0x{_typeDefinitionsCount:X}"); List list = FindAllMappedWords((ulong)_typeDefinitionsCount).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list.Count} instances of the number of type definitions: [{string.Join(", ", list.Select((ulong p) => p.ToString("X")))}]"); List list2 = list.Select((ulong a) => a - sizeOfMr + ptrSize * 4).ToList(); LibLogger.VerboseNewline($"\t\t\tFound {list2.Count} potential metadata registrations: [{string.Join(", ", list2.Select((ulong p) => p.ToString("X")))}]"); ulong num = sizeOfMr / ptrSize; foreach (ulong item in list2) { ulong[] array = _binary.ReadNUintArrayAtVirtualAddress(item, (int)num); bool flag = true; for (int i = 0; i < array.Length && flag; i++) { if (i % 2 == 0) { flag = array[i] < 655360; if (!flag && array[i] < 1048575) { LibLogger.VerboseNewline($"\t\t\tRejected Metadata registration at 0x{item:X}, because it has a count field 0x{array[i]:X} which is above sanity limit of 0xA0000. If metadata registration detection fails, need to bump up the limit."); } } else if (array[i] == 0L) { flag = i >= 14; if (!flag) { LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{item:X} because the pointer at index {i} is 0."); } } else { flag = _binary.TryMapVirtualAddressToRaw(array[i], out var _); if (!flag) { LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{item:X} because the pointer at index {i}, which is 0x{array[i]:X}, can't be mapped to the binary."); } } if (!flag) { break; } } if (!flag) { continue; } Il2CppMetadataRegistration il2CppMetadataRegistration = _binary.ReadReadableAtVirtualAddress(item); if (LibCpp2IlMain.MetadataVersion >= 27f && (il2CppMetadataRegistration.metadataUsagesCount != 0L || il2CppMetadataRegistration.metadataUsages != 0L)) { LibLogger.VerboseNewline($"\t\t\tWarning: metadata registration 0x{item:X} because it has {il2CppMetadataRegistration.metadataUsagesCount} metadata usages at a pointer of 0x{il2CppMetadataRegistration.metadataUsages:X}. We're on v27, these should be 0."); } if (il2CppMetadataRegistration.typeDefinitionsSizesCount != LibCpp2IlMain.TheMetadata.typeDefs.Length) { LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{item:X} because it has {il2CppMetadataRegistration.typeDefinitionsSizesCount} type def sizes, while metadata file defines {LibCpp2IlMain.TheMetadata.typeDefs.Length} type defs"); continue; } if (il2CppMetadataRegistration.numTypes < LibCpp2IlMain.TheMetadata.typeDefs.Length) { LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{item:X} because it has {il2CppMetadataRegistration.numTypes} types, which is less than metadata-file-defined type def count of {LibCpp2IlMain.TheMetadata.typeDefs.Length}"); continue; } if (il2CppMetadataRegistration.fieldOffsetsCount != LibCpp2IlMain.TheMetadata.typeDefs.Length) { LibLogger.VerboseNewline($"\t\t\tRejecting metadata registration 0x{item:X} because it has {il2CppMetadataRegistration.fieldOffsetsCount} field offsets, while metadata file defines {LibCpp2IlMain.TheMetadata.typeDefs.Length} type defs"); continue; } LibLogger.VerboseNewline($"\t\t\tAccepting metadata reg as VA 0x{item:X}"); return item; } return 0uL; } } public class ClassReadingBinaryReader : EndianAwareBinaryReader { public static bool EnableReadableSizeInformation; private SpinLock PositionShiftLock; public bool is32Bit; private MemoryStream? _memoryStream; protected bool _hasFinishedInitialRead; private bool _inReadableRead; public ConcurrentDictionary BytesReadPerClass = new ConcurrentDictionary(); private Dictionary _cachedNoSerialize = new Dictionary(); public ulong PointerSize { get { if (!is32Bit) { return 8uL; } return 4uL; } } public long Position { get { return BaseStream.Position; } set { BaseStream.Position = value; } } public long Length => BaseStream.Length; public ClassReadingBinaryReader(MemoryStream input) : base(input) { _memoryStream = input; } public ClassReadingBinaryReader(Stream input) : base(input) { _memoryStream = null; } internal virtual object? ReadPrimitive(Type type, bool overrideArchCheck = false) { if (type == typeof(bool)) { return ReadBoolean(); } if (type == typeof(char)) { return ReadChar(); } if (type == typeof(int)) { return ReadInt32(); } if (type == typeof(uint)) { return ReadUInt32(); } if (type == typeof(short)) { return ReadInt16(); } if (type == typeof(ushort)) { return ReadUInt16(); } if (type == typeof(sbyte)) { return ReadSByte(); } if (type == typeof(byte)) { return ReadByte(); } if (type == typeof(long)) { return (is32Bit && !overrideArchCheck) ? ReadInt32() : ReadInt64(); } if (type == typeof(ulong)) { return (is32Bit && !overrideArchCheck) ? ReadUInt32() : ReadUInt64(); } if (type == typeof(float)) { return ReadSingle(); } if (type == typeof(double)) { return ReadDouble(); } return null; } public uint ReadUnityCompressedUIntAtRawAddr(long offset, out int bytesRead) { GetLockOrThrow(); try { return ReadUnityCompressedUIntAtRawAddrNoLock(offset, out bytesRead); } finally { PositionShiftLock.Exit(); } } protected internal uint ReadUnityCompressedUIntAtRawAddrNoLock(long offset, out int bytesRead) { if (offset >= 0) { Position = offset; } byte b = ReadByte(); bytesRead = 1; if (b < 128) { return b; } switch (b) { case 240: bytesRead = 5; return ReadUInt32(); case byte.MaxValue: return uint.MaxValue; case 254: return 4294967294u; default: if ((b & 0xC0) == 192) { bytesRead = 4; return (uint)(((b & -193) << 24) | (ReadByte() << 16) | (ReadByte() << 8) | ReadByte()); } if ((b & 0x80) == 128) { bytesRead = 2; return (uint)(((b & -129) << 8) | ReadByte()); } throw new Exception($"How did we even get here? Invalid compressed int first byte {b}"); } } public int ReadUnityCompressedIntAtRawAddr(long position, out int bytesRead) { return ReadUnityCompressedIntAtRawAddr(position, doLock: true, out bytesRead); } protected internal int ReadUnityCompressedIntAtRawAddr(long position, bool doLock, out int bytesRead) { uint num = ((!doLock) ? ReadUnityCompressedUIntAtRawAddrNoLock(position, out bytesRead) : ReadUnityCompressedUIntAtRawAddr(position, out bytesRead)); if (num == uint.MaxValue) { return int.MinValue; } bool num2 = (num & 1) == 1; num >>= 1; if (num2) { return (int)(0 - (num + 1)); } return (int)num; } private T InternalReadClass(bool overrideArchCheck = false) where T : new() { return (T)InternalReadClass(typeof(T), overrideArchCheck); } private T InternalReadReadableClass() where T : ReadableClass, new() { T val = new T(); if (!_inReadableRead) { _inReadableRead = true; val.Read(this); _inReadableRead = false; } else { val.Read(this); } return val; } private object InternalReadClass(Type type, bool overrideArchCheck = false) { if (type.IsPrimitive) { return ReadAndConvertPrimitive(overrideArchCheck, type); } if (type.IsEnum) { return ReadPrimitive(type.GetEnumUnderlyingType()); } throw new Exception("Support for reading classes has been removed. Please inherit from ReadableClass and call ReadReadable on your local binary reader."); } private object ReadAndConvertPrimitive(bool overrideArchCheck, Type type) { object obj = ReadPrimitive(type, overrideArchCheck); if (obj is uint && type == typeof(ulong)) { obj = Convert.ToUInt64(obj); } if (obj is int && type == typeof(long)) { obj = Convert.ToInt64(obj); } return obj; } public T[] ReadClassArrayAtRawAddr(long offset, long count) where T : new() { T[] array = new T[count]; GetLockOrThrow(); try { if (offset != -1) { Position = offset; } for (int i = 0; i < count; i++) { array[i] = InternalReadClass(); } return array; } finally { PositionShiftLock.Exit(); } } public string ReadStringToNull(ulong offset) { return ReadStringToNull((long)offset); } public virtual string ReadStringToNull(long offset) { GetLockOrThrow(); try { return ReadStringToNullNoLock(offset); } finally { PositionShiftLock.Exit(); } } internal string ReadStringToNullNoLock(long offset) { List list = new List(); if (offset != -1) { Position = offset; } try { byte item; while ((item = ReadByte()) != 0) { list.Add(item); } return Encoding.UTF8.GetString(list.ToArray()); } finally { int bytesRead = (int)(Position - offset); TrackRead(bytesRead); } } public string ReadStringToNullAtCurrentPos() { return ReadStringToNullNoLock(-1L); } public byte[] ReadByteArrayAtRawAddress(long offset, int count) { GetLockOrThrow(); try { return ReadByteArrayAtRawAddressNoLock(offset, count); } finally { PositionShiftLock.Exit(); } } protected internal byte[] ReadByteArrayAtRawAddressNoLock(long offset, int count) { if (offset != -1) { Position = offset; } _ = Position; try { byte[] array = new byte[count]; Read(array, 0, count); return array; } finally { TrackRead(count, trackIfInReadableRead: false); } } protected internal void GetLockOrThrow() { bool lockTaken = false; PositionShiftLock.Enter(ref lockTaken); if (!lockTaken) { throw new Exception("Failed to obtain lock"); } } protected internal void ReleaseLock() { PositionShiftLock.Exit(); } public ulong[] ReadNUintArrayAtRawAddress(long offset, int count) { GetLockOrThrow(); try { Position = offset; ulong[] array = new ulong[count]; for (int i = 0; i < count; i++) { array[i] = ReadNUint(); } return array; } finally { PositionShiftLock.Exit(); int bytesRead = count * (int)PointerSize; TrackRead(bytesRead, trackIfInReadableRead: false); } } public long ReadNInt() { if (!is32Bit) { return ReadInt64(); } return ReadInt32(); } public ulong ReadNUint() { if (!is32Bit) { return ReadUInt64(); } return ReadUInt32(); } protected void WriteWord(int position, ulong word) { WriteWord(position, (long)word); } protected void WriteWord(int position, long word) { if (_memoryStream == null) { throw new Exception("WriteWord is not supported in non-memory-backed readers"); } GetLockOrThrow(); try { byte[] array = ((!is32Bit) ? BitConverter.GetBytes(word) : BitConverter.GetBytes((int)word)); if (ShouldReverseArrays) { array = array.Reverse(); } if (position > _memoryStream.Length) { throw new Exception($"WriteWord: Position {position} beyond length {_memoryStream.Length}"); } int num = (is32Bit ? 4 : 8); if (position + num > _memoryStream.Length) { throw new Exception($"WriteWord: Writing {num} bytes at {position} would go beyond length {_memoryStream.Length}"); } if (array.Length != num) { throw new Exception($"WriteWord: Expected {num} bytes from BitConverter, got {position}"); } try { _memoryStream.Seek(position, SeekOrigin.Begin); _memoryStream.Write(array, 0, num); } catch { LibLogger.ErrorNewline("WriteWord: Unexpected exception!"); throw; } } finally { PositionShiftLock.Exit(); } } public T ReadReadableHereNoLock() where T : ReadableClass, new() { return InternalReadReadableClass(); } public T ReadReadable(long offset = -1L) where T : ReadableClass, new() { GetLockOrThrow(); if (offset >= 0) { Position = offset; } long position = Position; try { return InternalReadReadableClass(); } finally { int bytesRead = (int)(Position - position); TrackRead(bytesRead, trackIfInReadableRead: true, trackIfFinishedReading: true); PositionShiftLock.Exit(); } } public T[] ReadReadableArrayAtRawAddr(long offset, long count) where T : ReadableClass, new() { T[] array = new T[count]; GetLockOrThrow(); if (offset != -1) { Position = offset; } long position = Position; try { for (int i = 0; i < count; i++) { array[i] = InternalReadReadableClass(); } return array; } finally { int bytesRead = (int)(Position - position); TrackRead(bytesRead, trackIfInReadableRead: true, trackIfFinishedReading: true); PositionShiftLock.Exit(); } } public void TrackRead(int bytesRead, bool trackIfInReadableRead = true, bool trackIfFinishedReading = false) { if (EnableReadableSizeInformation && (trackIfInReadableRead || !_inReadableRead) && (!_hasFinishedInitialRead || trackIfFinishedReading)) { BytesReadPerClass[typeof(T)] = BytesReadPerClass.GetOrDefault(typeof(T)) + bytesRead; } } } public class Cpp2IlMethodRef { private readonly Il2CppMethodSpec _methodSpec; public ulong GenericVariantPtr; public Il2CppTypeDefinition DeclaringType => BaseMethod.DeclaringType; public Il2CppTypeReflectionData[] TypeGenericParams => _methodSpec.GenericClassParams; public Il2CppMethodDefinition BaseMethod => _methodSpec.MethodDefinition; public Il2CppTypeReflectionData[] MethodGenericParams => _methodSpec.GenericMethodParams; public Cpp2IlMethodRef(Il2CppMethodSpec methodSpec) { _methodSpec = methodSpec; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(BaseMethod.ReturnType).Append(" "); stringBuilder.Append(DeclaringType.FullName); if (TypeGenericParams.Length != 0) { stringBuilder.Append("<").Append(string.Join(", ", TypeGenericParams.AsEnumerable())).Append(">"); } stringBuilder.Append(".").Append(BaseMethod.Name); if (MethodGenericParams.Length != 0) { stringBuilder.Append("<").Append(string.Join(", ", MethodGenericParams.AsEnumerable())).Append(">"); } return stringBuilder.ToString(); } } public static class DefaultInstructionSets { public static readonly InstructionSetId X86_64 = new InstructionSetId("x86_64"); public static readonly InstructionSetId X86_32 = new InstructionSetId("x86_32"); public static readonly InstructionSetId ARM_V7 = new InstructionSetId("ArmV7"); public static readonly InstructionSetId ARM_V8 = new InstructionSetId("ArmV8"); public static readonly InstructionSetId WASM = new InstructionSetId("WASM"); } public class EndianAwareBinaryReader : BinaryReader { protected bool ShouldReverseArrays = !BitConverter.IsLittleEndian; private int _numBytesReadSinceLastCall; public bool IsBigEndian { get; private set; } public EndianAwareBinaryReader(Stream input) : base(input) { } public EndianAwareBinaryReader(Stream input, Encoding encoding) : base(input, encoding) { } public EndianAwareBinaryReader(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen) { } public void SetBigEndian() { ShouldReverseArrays = BitConverter.IsLittleEndian; IsBigEndian = true; } public override bool ReadBoolean() { _numBytesReadSinceLastCall++; return base.ReadBoolean(); } public sealed override byte ReadByte() { _numBytesReadSinceLastCall++; return base.ReadByte(); } public sealed override byte[] ReadBytes(int count) { _numBytesReadSinceLastCall += count; return base.ReadBytes(count); } public sealed override char ReadChar() { _numBytesReadSinceLastCall += 2; return base.ReadChar(); } public sealed override char[] ReadChars(int count) { _numBytesReadSinceLastCall += 2 * count; return base.ReadChars(count); } public sealed override short ReadInt16() { _numBytesReadSinceLastCall += 2; if (!ShouldReverseArrays) { return base.ReadInt16(); } return this.ReadInt16WithReversedBits(); } public sealed override int ReadInt32() { _numBytesReadSinceLastCall += 4; if (!ShouldReverseArrays) { return base.ReadInt32(); } return this.ReadInt32WithReversedBits(); } public sealed override long ReadInt64() { _numBytesReadSinceLastCall += 8; if (!ShouldReverseArrays) { return base.ReadInt64(); } return this.ReadInt64WithReversedBits(); } public sealed override ushort ReadUInt16() { _numBytesReadSinceLastCall += 2; if (!ShouldReverseArrays) { return base.ReadUInt16(); } return this.ReadUInt16WithReversedBits(); } public sealed override uint ReadUInt32() { _numBytesReadSinceLastCall += 4; if (!ShouldReverseArrays) { return base.ReadUInt32(); } return this.ReadUInt32WithReversedBits(); } public sealed override ulong ReadUInt64() { _numBytesReadSinceLastCall += 8; if (!ShouldReverseArrays) { return base.ReadUInt64(); } return this.ReadUInt64WithReversedBits(); } public sealed override float ReadSingle() { _numBytesReadSinceLastCall += 4; if (!ShouldReverseArrays) { return base.ReadSingle(); } return this.ReadSingleWithReversedBits(); } public sealed override double ReadDouble() { _numBytesReadSinceLastCall += 8; if (!ShouldReverseArrays) { return base.ReadDouble(); } return this.ReadDoubleWithReversedBits(); } protected int GetNumBytesReadSinceLastCallAndClear() { int numBytesReadSinceLastCall = _numBytesReadSinceLastCall; _numBytesReadSinceLastCall = 0; return numBytesReadSinceLastCall; } } public static class Extensions { public static T[] SubArray(this T[] data, int index, int length) { T[] array = new T[length]; Array.Copy(data, index, array, 0, length); return array; } public static T RemoveAndReturn(this List data, int index) { T result = data[index]; data.RemoveAt(index); return result; } public static string Repeat(this string source, int count) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < count; i++) { stringBuilder.Append(source); } return stringBuilder.ToString(); } public static string ToStringEnumerable(this IEnumerable enumerable) { StringBuilder stringBuilder = new StringBuilder("["); stringBuilder.Append(string.Join(", ", enumerable)); stringBuilder.Append("]"); return stringBuilder.ToString(); } public static TValue? GetOrDefault(this IDictionary dictionary, TKey key, TValue? defaultValue) { if (dictionary == null) { throw new ArgumentNullException("dictionary"); } if (!dictionary.TryGetValue(key, out TValue value)) { return defaultValue; } return value; } public static TValue? GetOrDefault(this IDictionary dictionary, TKey key) { return dictionary.GetOrDefault(key, default(TValue)); } public static void Deconstruct(this KeyValuePair pair, out TKey one, out TValue two) { one = pair.Key; two = pair.Value; } public static uint Bits(this uint x, int low, int count) { return (x >> low) & (uint)((1 << count) - 1); } public static bool TryAdd(this Dictionary dictionary, TKey key, TValue value) where TKey : notnull { if (dictionary.ContainsKey(key)) { return false; } dictionary.Add(key, value); return true; } public static void SortByExtractedKey(this List list, Func keyObtainer) where K : IComparable { Func keyObtainer2 = keyObtainer; list.Sort(delegate(T a, T b) { K val = keyObtainer2(a); K other = keyObtainer2(b); return val.CompareTo(other); }); } public static int ParseDigit(this char c) { int num = c - 48; if (num > 9) { throw new Exception($"Invalid digit {c}"); } return num; } } public interface IIl2CppTokenProvider { uint Token { get; } } public abstract class Il2CppBinary : ClassReadingBinaryReader { protected const long VirtToRawInvalidNoMatch = -9223372036854774808L; protected const long VirtToRawInvalidOutOfBounds = -9223372036854774807L; public InstructionSetId InstructionSetId; public readonly Dictionary> ConcreteGenericMethods = new Dictionary>(); public readonly Dictionary> ConcreteGenericImplementationsByAddress = new Dictionary>(); public ulong[] TypeDefinitionSizePointers = Array.Empty(); private readonly long _maxMetadataUsages; private Il2CppMetadataRegistration _metadataRegistration; private Il2CppCodeRegistration _codeRegistration; private ulong[] _methodPointers = Array.Empty(); private ulong[] _genericMethodPointers = Array.Empty(); private ulong[] _invokerPointers = Array.Empty(); private ulong[]? _customAttributeGenerators = Array.Empty(); private long[] _fieldOffsets = Array.Empty(); private ulong[] _metadataUsages = Array.Empty(); private ulong[][] _codeGenModuleMethodPointers = Array.Empty(); private Il2CppType[] _types = Array.Empty(); private Il2CppGenericMethodFunctionsDefinitions[] _genericMethodTables = Array.Empty(); private Il2CppGenericInst[] _genericInsts = Array.Empty(); private Il2CppMethodSpec[] _methodSpecs = Array.Empty(); private Il2CppCodeGenModule[] _codeGenModules = Array.Empty(); private Il2CppTokenRangePair[][] _codegenModuleRgctxRanges = Array.Empty(); private Il2CppRGCTXDefinition[][] _codegenModuleRgctxs = Array.Empty(); private Dictionary _codeGenModulesByName = new Dictionary(); private Dictionary _genericMethodDictionary = new Dictionary(); private readonly Dictionary _typesByAddress = new Dictionary(); public abstract long RawLength { get; } public int NumTypes => _types.Length; public Il2CppType[] AllTypes => _types; public virtual ClassReadingBinaryReader Reader => this; public int InBinaryMetadataSize { get; private set; } public Il2CppMethodSpec[] AllGenericMethodSpecs => _methodSpecs; public ulong[] AllCustomAttributeGenerators { get { if (!(LibCpp2IlMain.MetadataVersion >= 29f)) { if (!(LibCpp2IlMain.MetadataVersion >= 27f)) { return _customAttributeGenerators; } return AllCustomAttributeGeneratorsV27; } return Array.Empty(); } } private ulong[] AllCustomAttributeGeneratorsV27 => LibCpp2IlMain.TheMetadata.imageDefinitions.Select((Il2CppImageDefinition i) => (i, GetCodegenModuleByName(i.Name))).SelectMany<(Il2CppImageDefinition, Il2CppCodeGenModule), ulong>(((Il2CppImageDefinition image, Il2CppCodeGenModule cgm) tuple) => from o in LibCpp2ILUtils.Range(0, (int)tuple.image.customAttributeCount) select tuple.cgm.customAttributeCacheGenerator + (ulong)((long)o * (long)base.PointerSize)).Select(ReadPointerAtVirtualAddress) .ToArray(); protected Il2CppBinary(MemoryStream input) : base(input) { _maxMetadataUsages = LibCpp2IlMain.TheMetadata.GetMaxMetadataUsages(); } public void Init(ulong pCodeRegistration, ulong pMetadataRegistration) { _codeRegistration = ReadReadableAtVirtualAddress(pCodeRegistration); _metadataRegistration = ReadReadableAtVirtualAddress(pMetadataRegistration); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading generic instances..."); DateTime now = DateTime.Now; _genericInsts = Array.ConvertAll(ReadNUintArrayAtVirtualAddress(_metadataRegistration.genericInsts, _metadataRegistration.genericInstsCount), ReadReadableAtVirtualAddress); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading generic method pointers..."); now = DateTime.Now; _genericMethodPointers = ReadNUintArrayAtVirtualAddress(_codeRegistration.genericMethodPointers, (long)_codeRegistration.genericMethodPointersCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading invoker pointers..."); now = DateTime.Now; _invokerPointers = ReadNUintArrayAtVirtualAddress(_codeRegistration.invokerPointers, (long)_codeRegistration.invokerPointersCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); if (LibCpp2IlMain.MetadataVersion < 27f) { LibLogger.Verbose("\tReading custom attribute generators..."); now = DateTime.Now; _customAttributeGenerators = ReadNUintArrayAtVirtualAddress(_codeRegistration.customAttributeGeneratorListAddress, (long)_codeRegistration.customAttributeCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading field offsets..."); now = DateTime.Now; _fieldOffsets = ReadClassArrayAtVirtualAddress(_metadataRegistration.fieldOffsetListAddress, _metadataRegistration.fieldOffsetsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading types..."); now = DateTime.Now; ulong[] array = ReadNUintArrayAtVirtualAddress(_metadataRegistration.typeAddressListAddress, _metadataRegistration.numTypes); _types = new Il2CppType[_metadataRegistration.numTypes]; for (int i = 0; i < _metadataRegistration.numTypes; i++) { _types[i] = ReadReadableAtVirtualAddress(array[i]); _typesByAddress[array[i]] = _types[i]; } InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading type definition sizes..."); now = DateTime.Now; TypeDefinitionSizePointers = ReadNUintArrayAtVirtualAddress(_metadataRegistration.typeDefinitionsSizes, _metadataRegistration.typeDefinitionsSizesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); if (_metadataRegistration.metadataUsages != 0L) { LibLogger.Verbose("\tReading metadata usages..."); now = DateTime.Now; _metadataUsages = ReadNUintArrayAtVirtualAddress(_metadataRegistration.metadataUsages, (long)Math.Max((decimal)_metadataRegistration.metadataUsagesCount, (decimal)_maxMetadataUsages)); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); if (LibCpp2IlMain.MetadataVersion >= 24.2f) { LibLogger.VerboseNewline("\tReading code gen modules..."); now = DateTime.Now; ulong[] array2 = ReadNUintArrayAtVirtualAddress(_codeRegistration.addrCodeGenModulePtrs, (long)_codeRegistration.codeGenModulesCount); _codeGenModules = new Il2CppCodeGenModule[array2.Length]; _codeGenModuleMethodPointers = new ulong[array2.Length][]; _codegenModuleRgctxRanges = new Il2CppTokenRangePair[array2.Length][]; _codegenModuleRgctxs = new Il2CppRGCTXDefinition[array2.Length][]; for (int j = 0; j < array2.Length; j++) { Il2CppCodeGenModule il2CppCodeGenModule = ReadReadableAtVirtualAddress(array2[j]); _codeGenModules[j] = il2CppCodeGenModule; _codeGenModulesByName[il2CppCodeGenModule.Name] = il2CppCodeGenModule; string name = il2CppCodeGenModule.Name; LibLogger.VerboseNewline($"\t\t-Read module data for {name}, contains {il2CppCodeGenModule.methodPointerCount} method pointers starting at 0x{il2CppCodeGenModule.methodPointers:X}"); if (il2CppCodeGenModule.methodPointerCount > 0) { try { ulong[] array3 = ReadNUintArrayAtVirtualAddress(il2CppCodeGenModule.methodPointers, il2CppCodeGenModule.methodPointerCount); _codeGenModuleMethodPointers[j] = array3; LibLogger.VerboseNewline($"\t\t\t-Read {il2CppCodeGenModule.methodPointerCount} method pointers."); } catch (Exception ex) { LibLogger.VerboseNewline("\t\t\tWARNING: Unable to get function pointers for " + name + ": " + ex.Message); _codeGenModuleMethodPointers[j] = new ulong[il2CppCodeGenModule.methodPointerCount]; } } if (il2CppCodeGenModule.rgctxRangesCount > 0) { try { Il2CppTokenRangePair[] array4 = ReadReadableArrayAtVirtualAddress(il2CppCodeGenModule.pRgctxRanges, il2CppCodeGenModule.rgctxRangesCount); _codegenModuleRgctxRanges[j] = array4; LibLogger.VerboseNewline($"\t\t\t-Read {il2CppCodeGenModule.rgctxRangesCount} RGCTX ranges."); } catch (Exception ex2) { LibLogger.VerboseNewline("\t\t\tWARNING: Unable to get RGCTX ranges for " + name + ": " + ex2.Message); _codegenModuleRgctxRanges[j] = new Il2CppTokenRangePair[il2CppCodeGenModule.rgctxRangesCount]; } } if (il2CppCodeGenModule.rgctxsCount > 0) { try { Il2CppRGCTXDefinition[] array5 = ReadReadableArrayAtVirtualAddress(il2CppCodeGenModule.rgctxs, il2CppCodeGenModule.rgctxsCount); _codegenModuleRgctxs[j] = array5; LibLogger.VerboseNewline($"\t\t\t-Read {il2CppCodeGenModule.rgctxsCount} RGCTXs."); } catch (Exception ex3) { LibLogger.VerboseNewline("\t\t\tWARNING: Unable to get RGCTXs for " + name + ": " + ex3.Message); _codegenModuleRgctxs[j] = new Il2CppRGCTXDefinition[il2CppCodeGenModule.rgctxsCount]; } } } LibLogger.VerboseNewline($"\tOK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } else { LibLogger.Verbose("\tReading method pointers..."); now = DateTime.Now; _methodPointers = ReadNUintArrayAtVirtualAddress(_codeRegistration.methodPointers, (long)_codeRegistration.methodPointersCount); LibLogger.VerboseNewline($"Read {_methodPointers.Length} OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading generic method tables..."); now = DateTime.Now; _genericMethodTables = ReadReadableArrayAtVirtualAddress(_metadataRegistration.genericMethodTable, _metadataRegistration.genericMethodTableCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); LibLogger.Verbose("\tReading method specifications..."); now = DateTime.Now; _methodSpecs = ReadReadableArrayAtVirtualAddress(_metadataRegistration.methodSpecs, _metadataRegistration.methodSpecsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading generic methods..."); now = DateTime.Now; _genericMethodDictionary = new Dictionary(); Il2CppGenericMethodFunctionsDefinitions[] genericMethodTables = _genericMethodTables; foreach (Il2CppGenericMethodFunctionsDefinitions obj in genericMethodTables) { int genericMethodIndex = obj.GenericMethodIndex; int methodIndex = obj.Indices.methodIndex; int genericMethodFromIndex = GetGenericMethodFromIndex(genericMethodIndex, methodIndex); if (!_genericMethodDictionary.ContainsKey(genericMethodFromIndex) && methodIndex < _genericMethodPointers.Length) { _genericMethodDictionary.TryAdd(genericMethodFromIndex, _genericMethodPointers[methodIndex]); } } LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); InBinaryMetadataSize += GetNumBytesReadSinceLastCallAndClear(); _hasFinishedInitialRead = true; } private int GetGenericMethodFromIndex(int genericMethodIndex, int genericMethodPointerIndex) { Il2CppMethodSpec methodSpec = GetMethodSpec(genericMethodIndex); int methodDefinitionIndex = methodSpec.methodDefinitionIndex; Cpp2IlMethodRef cpp2IlMethodRef = new Cpp2IlMethodRef(methodSpec); if (genericMethodPointerIndex >= 0 && genericMethodPointerIndex < _genericMethodPointers.Length) { cpp2IlMethodRef.GenericVariantPtr = _genericMethodPointers[genericMethodPointerIndex]; } if (!ConcreteGenericMethods.ContainsKey(cpp2IlMethodRef.BaseMethod)) { ConcreteGenericMethods[cpp2IlMethodRef.BaseMethod] = new List(); } ConcreteGenericMethods[cpp2IlMethodRef.BaseMethod].Add(cpp2IlMethodRef); if (cpp2IlMethodRef.GenericVariantPtr != 0) { if (!ConcreteGenericImplementationsByAddress.ContainsKey(cpp2IlMethodRef.GenericVariantPtr)) { ConcreteGenericImplementationsByAddress[cpp2IlMethodRef.GenericVariantPtr] = new List(); } ConcreteGenericImplementationsByAddress[cpp2IlMethodRef.GenericVariantPtr].Add(cpp2IlMethodRef); } return methodDefinitionIndex; } public abstract byte GetByteAtRawAddress(ulong addr); public abstract long MapVirtualAddressToRaw(ulong uiAddr, bool throwOnError = true); public abstract ulong MapRawAddressToVirtual(uint offset); public abstract ulong GetRva(ulong pointer); public bool TryMapRawAddressToVirtual(in uint offset, out ulong va) { try { va = MapRawAddressToVirtual(offset); return true; } catch (Exception) { va = 0uL; return false; } } public bool TryMapVirtualAddressToRaw(ulong virtAddr, out long result) { result = MapVirtualAddressToRaw(virtAddr, throwOnError: false); if (result != -9223372036854774808L) { return true; } result = 0L; return false; } public T[] ReadClassArrayAtVirtualAddress(ulong addr, long count) where T : new() { return Reader.ReadClassArrayAtRawAddr(MapVirtualAddressToRaw(addr), count); } public T[] ReadReadableArrayAtVirtualAddress(ulong va, long count) where T : ReadableClass, new() { return Reader.ReadReadableArrayAtRawAddr(MapVirtualAddressToRaw(va), count); } public T ReadReadableAtVirtualAddress(ulong va) where T : ReadableClass, new() { return Reader.ReadReadable(MapVirtualAddressToRaw(va)); } public ulong[] ReadNUintArrayAtVirtualAddress(ulong addr, long count) { return Reader.ReadNUintArrayAtRawAddress(MapVirtualAddressToRaw(addr), (int)count); } public ulong ReadPointerAtVirtualAddress(ulong addr) { base.Position = MapVirtualAddressToRaw(addr); return Reader.ReadNUint(); } public Il2CppGenericInst GetGenericInst(int index) { return _genericInsts[index]; } public Il2CppMethodSpec GetMethodSpec(int index) { if (index < _methodSpecs.Length) { if (index >= 0) { return _methodSpecs[index]; } throw new ArgumentException($"GetMethodSpec: index {index} < 0"); } throw new ArgumentException($"GetMethodSpec: index {index} >= length {_methodSpecs.Length}"); } public Il2CppType GetType(int index) { return _types[index]; } public ulong GetRawMetadataUsage(uint index) { return _metadataUsages[index]; } public ulong[] GetCodegenModuleMethodPointers(int codegenModuleIndex) { return _codeGenModuleMethodPointers[codegenModuleIndex]; } public Il2CppCodeGenModule? GetCodegenModuleByName(string name) { return _codeGenModulesByName[name]; } public int GetCodegenModuleIndex(Il2CppCodeGenModule module) { return Array.IndexOf(_codeGenModules, module); } public int GetCodegenModuleIndexByName(string name) { Il2CppCodeGenModule codegenModuleByName = GetCodegenModuleByName(name); if (codegenModuleByName == null) { return -1; } return GetCodegenModuleIndex(codegenModuleByName); } public Il2CppTokenRangePair[] GetRgctxRangePairsForModule(Il2CppCodeGenModule module) { return _codegenModuleRgctxRanges[GetCodegenModuleIndex(module)]; } public Il2CppRGCTXDefinition[] GetRgctxDataForPair(Il2CppCodeGenModule module, Il2CppTokenRangePair rangePair) { return _codegenModuleRgctxs[GetCodegenModuleIndex(module)].Skip(rangePair.start).Take(rangePair.length).ToArray(); } public Il2CppType GetIl2CppTypeFromPointer(ulong pointer) { return _typesByAddress[pointer]; } public int GetFieldOffsetFromIndex(int typeIndex, int fieldIndexInType, int fieldIndex, bool isValueType, bool isStatic) { try { int num = -1; if (LibCpp2IlMain.MetadataVersion > 21f) { ulong num2 = (ulong)_fieldOffsets[typeIndex]; if (num2 != 0) { ulong position = (ulong)(MapVirtualAddressToRaw(num2) + 4L * (long)fieldIndexInType); base.Position = (long)position; num = ReadInt32(); } } else { num = (int)_fieldOffsets[fieldIndex]; } if (num > 0 && isValueType && !isStatic) { num = ((!is32Bit) ? (num - 16) : (num - 8)); } return num; } catch { return -1; } } public ulong GetMethodPointer(int methodIndex, int methodDefinitionIndex, int imageIndex, uint methodToken) { if (LibCpp2IlMain.MetadataVersion >= 24.2f) { if (_genericMethodDictionary.TryGetValue(methodDefinitionIndex, out var value)) { return value; } ulong[] obj = _codeGenModuleMethodPointers[imageIndex]; uint num = methodToken & 0xFFFFFFu; return obj[num - 1]; } if (methodIndex >= 0) { return _methodPointers[methodIndex]; } _genericMethodDictionary.TryGetValue(methodDefinitionIndex, out var value2); return value2; } public ulong GetCustomAttributeGenerator(int index) { return _customAttributeGenerators[index]; } public abstract byte[] GetRawBinaryContent(); public abstract ulong GetVirtualAddressOfExportedFunctionByName(string toFind); public virtual bool IsExportedFunction(ulong addr) { return false; } public abstract byte[] GetEntirePrimaryExecutableSection(); public abstract ulong GetVirtualAddressOfPrimaryExecutableSection(); public virtual (ulong pCodeRegistration, ulong pMetadataRegistration) FindCodeAndMetadataReg(int methodCount, int typeDefinitionsCount) { LibLogger.VerboseNewline("\tAttempting to locate code and metadata registration functions..."); BinarySearcher binarySearcher = new BinarySearcher(this, methodCount, typeDefinitionsCount); LibLogger.VerboseNewline("\t\t-Searching for MetadataReg..."); ulong result = ((LibCpp2IlMain.MetadataVersion < 24.5f) ? binarySearcher.FindMetadataRegistrationPre24_5() : binarySearcher.FindMetadataRegistrationPost24_5()); LibLogger.VerboseNewline("\t\t-Searching for CodeReg..."); ulong result2; if (LibCpp2IlMain.MetadataVersion >= 24.2f) { LibLogger.VerboseNewline("\t\t\tUsing mscorlib full-disassembly approach to get codereg, this may take a while..."); result2 = binarySearcher.FindCodeRegistrationPost2019(); } else { result2 = binarySearcher.FindCodeRegistrationPre2019(); } if (result2 == 0L && LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput) { LibLogger.Info("Couldn't identify a CodeRegistration address. If you know it, enter it now, otherwise enter nothing or zero to fail: "); ulong.TryParse(Console.ReadLine(), NumberStyles.HexNumber, null, out result2); } if (result == 0L && LibCpp2IlMain.Settings.AllowManualMetadataAndCodeRegInput) { LibLogger.Info("Couldn't identify a MetadataRegistration address. If you know it, enter it now, otherwise enter nothing or zero to fail: "); ulong.TryParse(Console.ReadLine(), NumberStyles.HexNumber, null, out result); } return (result2, result); } } public class InstructionSetId { public string Name; public InstructionSetId(string name) { Name = name; } public override string ToString() { return Name; } } public static class LEB128 { private const long SIGN_EXTEND_MASK = -1L; private const int INT64_BITSIZE = 64; public static void WriteLEB128Signed(this Stream stream, long value) { stream.WriteLEB128Signed(value, out var _); } public static void WriteLEB128Signed(this Stream stream, long value, out int bytes) { bytes = 0; bool flag = true; while (flag) { byte b = (byte)(value & 0x7F); value >>= 7; bool flag2 = (b & 0x40) != 0; flag = (value != 0L || flag2) && !(value == -1 && flag2); if (flag) { b = (byte)(b | 0x80u); } stream.WriteByte(b); bytes++; } } public static void WriteLEB128Unsigned(this Stream stream, ulong value) { stream.WriteLEB128Unsigned(value, out var _); } public static void WriteLEB128Unsigned(this Stream stream, ulong value, out int bytes) { bytes = 0; bool flag = true; while (flag) { byte b = (byte)(value & 0x7F); value >>= 7; flag = value != 0; if (flag) { b = (byte)(b | 0x80u); } stream.WriteByte(b); bytes++; } } public static long ReadLEB128Signed(this Stream stream) { int bytes; return stream.ReadLEB128Signed(out bytes); } public static long ReadLEB128Signed(this Stream stream, out int bytes) { bytes = 0; long num = 0L; int num2 = 0; bool flag = true; bool flag2 = false; while (flag) { int num3 = stream.ReadByte(); if (num3 < 0) { throw new InvalidOperationException("Unexpected end of stream"); } byte num4 = (byte)num3; bytes++; flag = (num4 & 0x80) != 0; flag2 = (num4 & 0x40) != 0; long num5 = (long)num4 & 0x7FL; num |= num5 << num2; num2 += 7; } if (num2 < 64 && flag2) { num |= -1L << num2; } return num; } public static ulong ReadLEB128Unsigned(this Stream stream) { int bytes; return stream.ReadLEB128Unsigned(out bytes); } public static ulong ReadLEB128Unsigned(this Stream stream, out int bytes) { bytes = 0; ulong num = 0uL; int num2 = 0; bool flag = true; while (flag) { int num3 = stream.ReadByte(); if (num3 < 0) { throw new InvalidOperationException("Unexpected end of stream"); } byte num4 = (byte)num3; bytes++; flag = (num4 & 0x80) != 0; ulong num5 = (ulong)num4 & 0x7FuL; num |= num5 << num2; num2 += 7; } return num; } } public static class LibCpp2IlBinaryRegistry { private class RegisteredBinary { public string Name; public string Source; public Func IsValid; public Func FactoryFunc; public RegisteredBinary(string name, string source, Func verificationFunc, Func factoryFunc) { Source = source; Name = name; IsValid = verificationFunc; FactoryFunc = factoryFunc; } } private static List _binaries = new List(); public static void RegisterBuiltInBinarySupport() { Register("Portable Executable", "LibCpp2IL", (byte[] bytes) => BitConverter.ToInt16(bytes, 0) == 23117, (MemoryStream memStream) => new LibCpp2IL.PE.PE(memStream)); Register("ELF", "LibCpp2IL", (byte[] bytes) => BitConverter.ToInt32(bytes, 0) == 1179403647, (MemoryStream memStream) => new ElfFile(memStream)); Register("Nintendo Switch Object", "LibCpp2IL", (byte[] bytes) => BitConverter.ToInt32(bytes, 0) == 810505038, (MemoryStream memStream) => new NsoFile(memStream).Decompress()); Register("WebAssembly File", "LibCpp2IL", (byte[] bytes) => BitConverter.ToInt32(bytes, 0) == 1836278016, (MemoryStream memStream) => new WasmFile(memStream)); Register("Mach-O File", "LibCppIL", delegate(byte[] bytes) { uint num = BitConverter.ToUInt32(bytes, 0); return num == 4277009102u || num == 4277009103u; }, (MemoryStream memStream) => new MachOFile(memStream)); } public static void Register(string name, string source, Func isValid, Func factory) where T : Il2CppBinary { _binaries.Add(new RegisteredBinary(name, source, isValid, factory)); } internal static Il2CppBinary CreateAndInit(byte[] buffer, Il2CppMetadata metadata) { byte[] buffer2 = buffer; if (_binaries.Count == 0) { RegisterBuiltInBinarySupport(); } RegisteredBinary registeredBinary = _binaries.Find((RegisteredBinary b) => b.IsValid(buffer2)); if (registeredBinary == null) { throw new Exception("Unknown binary type, no binary handling header bytes " + string.Join(" ", from b in buffer2.SubArray(0, 4) select $"{b:X2}") + " has been registered"); } LibLogger.InfoNewline($"Using binary type {registeredBinary.Name} (from {registeredBinary.Source})"); MemoryStream arg = new MemoryStream(buffer2, 0, buffer2.Length, writable: true, publiclyVisible: true); LibLogger.InfoNewline("Searching Binary for Required Data..."); DateTime now = DateTime.Now; Il2CppBinary? il2CppBinary = (LibCpp2IlMain.Binary = registeredBinary.FactoryFunc(arg)); var (num, num2) = il2CppBinary.FindCodeAndMetadataReg(metadata.methodDefs.Count((Il2CppMethodDefinition x) => x.methodIndex >= 0), metadata.typeDefs.Length); if (num == 0L || num2 == 0L) { throw new Exception("Failed to find Binary code or metadata registration"); } LibLogger.InfoNewline($"Got Binary codereg: 0x{num:X}, metareg: 0x{num2:X} in {(DateTime.Now - now).TotalMilliseconds:F0}ms."); LibLogger.InfoNewline("Initializing Binary..."); now = DateTime.Now; il2CppBinary.Init(num, num2); LibLogger.InfoNewline($"Initialized Binary in {(DateTime.Now - now).TotalMilliseconds:F0}ms"); return il2CppBinary; } } public static class LibCpp2IlGlobalMapper { internal static List TypeRefs = new List(); internal static List MethodRefs = new List(); internal static List FieldRefs = new List(); internal static List Literals = new List(); internal static Dictionary TypeRefsByAddress = new Dictionary(); internal static Dictionary MethodRefsByAddress = new Dictionary(); internal static Dictionary FieldRefsByAddress = new Dictionary(); internal static Dictionary LiteralsByAddress = new Dictionary(); internal static void Reset() { TypeRefs.Clear(); MethodRefs.Clear(); FieldRefs.Clear(); Literals.Clear(); TypeRefsByAddress.Clear(); MethodRefsByAddress.Clear(); FieldRefsByAddress.Clear(); LiteralsByAddress.Clear(); } internal static void MapGlobalIdentifiers(Il2CppMetadata metadata, Il2CppBinary cppAssembly) { if (LibCpp2IlMain.MetadataVersion < 27f) { MapGlobalIdentifiersPre27(metadata, cppAssembly); } else { MapGlobalIdentifiersPost27(metadata, cppAssembly); } } private static void MapGlobalIdentifiersPost27(Il2CppMetadata metadata, Il2CppBinary cppAssembly) { } private static void MapGlobalIdentifiersPre27(Il2CppMetadata metadata, Il2CppBinary cppAssembly) { Il2CppBinary cppAssembly2 = cppAssembly; TypeRefs = metadata.metadataUsageDic[1u].Select((KeyValuePair kvp) => new MetadataUsage(MetadataUsageType.Type, cppAssembly2.GetRawMetadataUsage(kvp.Key), kvp.Value)).ToList(); TypeRefs.AddRange(metadata.metadataUsageDic[2u].Select((KeyValuePair kvp) => new MetadataUsage(MetadataUsageType.Type, cppAssembly2.GetRawMetadataUsage(kvp.Key), kvp.Value))); MethodRefs = metadata.metadataUsageDic[3u].Select((KeyValuePair kvp) => new MetadataUsage(MetadataUsageType.MethodDef, cppAssembly2.GetRawMetadataUsage(kvp.Key), kvp.Value)).ToList(); FieldRefs = metadata.metadataUsageDic[4u].Select((KeyValuePair kvp) => new MetadataUsage(MetadataUsageType.FieldInfo, cppAssembly2.GetRawMetadataUsage(kvp.Key), kvp.Value)).ToList(); Literals = metadata.metadataUsageDic[5u].Select((KeyValuePair kvp) => new MetadataUsage(MetadataUsageType.StringLiteral, cppAssembly2.GetRawMetadataUsage(kvp.Key), kvp.Value)).ToList(); foreach (var (index, value) in metadata.metadataUsageDic[6u]) { MethodRefs.Add(new MetadataUsage(MetadataUsageType.MethodRef, cppAssembly2.GetRawMetadataUsage(index), value)); } foreach (MetadataUsage typeRef in TypeRefs) { TypeRefsByAddress[typeRef.Offset] = typeRef; } foreach (MetadataUsage methodRef in MethodRefs) { MethodRefsByAddress[methodRef.Offset] = methodRef; } foreach (MetadataUsage fieldRef in FieldRefs) { FieldRefsByAddress[fieldRef.Offset] = fieldRef; } foreach (MetadataUsage literal in Literals) { LiteralsByAddress[literal.Offset] = literal; } } public static MetadataUsage? CheckForPost27GlobalAt(ulong address) { if (!LibCpp2IlMain.Binary.TryMapVirtualAddressToRaw(address, out var result) || result >= LibCpp2IlMain.Binary.RawLength) { return null; } MetadataUsage metadataUsage = MetadataUsage.DecodeMetadataUsage(LibCpp2IlMain.Binary.ReadPointerAtVirtualAddress(address), address); if (metadataUsage == null || !metadataUsage.IsValid) { return null; } return metadataUsage; } } public static class LibCpp2IlMain { public class LibCpp2IlSettings { public bool AllowManualMetadataAndCodeRegInput; public bool DisableMethodPointerMapping; public bool DisableGlobalResolving; } private static readonly Regex UnityVersionRegex = new Regex("^[0-9]+\\.[0-9]+\\.[0-9]+[abcfxp][0-9]+$", RegexOptions.Compiled); public static readonly LibCpp2IlSettings Settings = new LibCpp2IlSettings(); public static bool Il2CppTypeHasNumMods5Bits; public static float MetadataVersion = 24f; public static Il2CppBinary? Binary; public static Il2CppMetadata? TheMetadata; public static readonly Dictionary> MethodsByPtr = new Dictionary>(); public static void Reset() { LibCpp2IlGlobalMapper.Reset(); LibCpp2ILUtils.Reset(); MethodsByPtr.Clear(); } public static List? GetManagedMethodImplementationsAtAddress(ulong addr) { MethodsByPtr.TryGetValue(addr, out List value); return value; } public static MetadataUsage? GetAnyGlobalByAddress(ulong address) { if (MetadataVersion >= 27f) { return LibCpp2IlGlobalMapper.CheckForPost27GlobalAt(address); } MetadataUsage metadataUsage = GetLiteralGlobalByAddress(address); if (metadataUsage == null) { metadataUsage = GetMethodGlobalByAddress(address); } if (metadataUsage == null) { metadataUsage = GetRawFieldGlobalByAddress(address); } if (metadataUsage == null) { metadataUsage = GetRawTypeGlobalByAddress(address); } return metadataUsage; } public static MetadataUsage? GetLiteralGlobalByAddress(ulong address) { if (MetadataVersion < 27f) { return LibCpp2IlGlobalMapper.LiteralsByAddress.GetOrDefault(address); } return GetAnyGlobalByAddress(address); } public static string? GetLiteralByAddress(ulong address) { return GetLiteralGlobalByAddress(address)?.AsLiteral(); } public static MetadataUsage? GetRawTypeGlobalByAddress(ulong address) { if (MetadataVersion < 27f) { return LibCpp2IlGlobalMapper.TypeRefsByAddress.GetOrDefault(address); } return GetAnyGlobalByAddress(address); } public static Il2CppTypeReflectionData? GetTypeGlobalByAddress(ulong address) { if (TheMetadata == null) { return null; } return GetRawTypeGlobalByAddress(address)?.AsType(); } public static MetadataUsage? GetRawFieldGlobalByAddress(ulong address) { if (MetadataVersion < 27f) { return LibCpp2IlGlobalMapper.FieldRefsByAddress.GetOrDefault(address); } return GetAnyGlobalByAddress(address); } public static Il2CppFieldDefinition? GetFieldGlobalByAddress(ulong address) { if (TheMetadata == null) { return null; } return GetRawFieldGlobalByAddress(address)?.AsField(); } public static MetadataUsage? GetMethodGlobalByAddress(ulong address) { if (TheMetadata == null) { return null; } if (MetadataVersion < 27f) { return LibCpp2IlGlobalMapper.MethodRefsByAddress.GetOrDefault(address); } return GetAnyGlobalByAddress(address); } public static Il2CppMethodDefinition? GetMethodDefinitionByGlobalAddress(ulong address) { MetadataUsage methodGlobalByAddress = GetMethodGlobalByAddress(address); if (methodGlobalByAddress != null && methodGlobalByAddress.Type == MetadataUsageType.MethodRef) { return methodGlobalByAddress.AsGenericMethodRef().BaseMethod; } return methodGlobalByAddress?.AsMethod(); } public static bool Initialize(byte[] binaryBytes, byte[] metadataBytes, UnityVersion unityVersion) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) LibCpp2IlReflection.ResetCaches(); DateTime now = DateTime.Now; LibLogger.InfoNewline("Initializing Metadata..."); TheMetadata = Il2CppMetadata.ReadFrom(metadataBytes, unityVersion); Il2CppTypeHasNumMods5Bits = MetadataVersion >= 27.2f; if (TheMetadata == null) { return false; } LibLogger.InfoNewline($"Initialized Metadata in {(DateTime.Now - now).TotalMilliseconds:F0}ms"); Binary = LibCpp2IlBinaryRegistry.CreateAndInit(binaryBytes, TheMetadata); if (!Settings.DisableGlobalResolving && MetadataVersion < 27f) { now = DateTime.Now; LibLogger.Info("Mapping Globals..."); LibCpp2IlGlobalMapper.MapGlobalIdentifiers(TheMetadata, Binary); LibLogger.InfoNewline($"OK ({(DateTime.Now - now).TotalMilliseconds:F0}ms)"); } if (!Settings.DisableMethodPointerMapping) { now = DateTime.Now; LibLogger.Info("Mapping pointers to Il2CppMethodDefinitions..."); int num = 0; foreach (var (item, key) in TheMetadata.methodDefs.Select((Il2CppMethodDefinition method) => (method, method.MethodPointer))) { if (!MethodsByPtr.ContainsKey(key)) { MethodsByPtr[key] = new List(); } MethodsByPtr[key].Add(item); num++; } LibLogger.InfoNewline($"Processed {num} OK ({(DateTime.Now - now).TotalMilliseconds:F0}ms)"); } LibCpp2IlReflection.InitCaches(); return true; } public static bool LoadFromFile(string pePath, string metadataPath, UnityVersion unityVersion) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) byte[] metadataBytes = File.ReadAllBytes(metadataPath); return Initialize(File.ReadAllBytes(pePath), metadataBytes, unityVersion); } public static UnityVersion DetermineUnityVersion(string? unityPlayerPath, string? gameDataPath) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (Environment.OSVersion.Platform == PlatformID.Win32NT && !string.IsNullOrEmpty(unityPlayerPath)) { FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(unityPlayerPath); return new UnityVersion((ushort)versionInfo.FileMajorPart, (ushort)versionInfo.FileMinorPart, (ushort)versionInfo.FileBuildPart); } if (!string.IsNullOrEmpty(gameDataPath)) { string path = Path.Combine(gameDataPath, "globalgamemanagers"); if (File.Exists(path)) { return GetVersionFromGlobalGameManagers(File.ReadAllBytes(path)); } string path2 = Path.Combine(gameDataPath, "data.unity3d"); if (File.Exists(path2)) { using (FileStream fileStream = File.OpenRead(path2)) { return GetVersionFromDataUnity3D(fileStream); } } } return default(UnityVersion); } public static UnityVersion GetVersionFromGlobalGameManagers(byte[] ggmBytes) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); for (int i = 20; ggmBytes[i] != 0; i++) { stringBuilder.Append(Convert.ToChar(ggmBytes[i])); } string text = stringBuilder.ToString(); if (!UnityVersionRegex.IsMatch(text)) { int i = 48; stringBuilder = new StringBuilder(); for (; ggmBytes[i] != 0; i++) { stringBuilder.Append(Convert.ToChar(ggmBytes[i])); } text = stringBuilder.ToString().Trim(); } return UnityVersion.Parse(text); } public static UnityVersion GetVersionFromDataUnity3D(Stream fileStream) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); if (fileStream.CanSeek) { fileStream.Seek(18L, SeekOrigin.Begin); } else if (fileStream.Read(new byte[18], 0, 18) != 18) { throw new Exception("Failed to seek to 0x12 in data.unity3d"); } while (true) { int num = fileStream.ReadByte(); if (num == 0) { break; } stringBuilder.Append(Convert.ToChar(num)); } return UnityVersion.Parse(stringBuilder.ToString().Trim()); } } public static class LibCpp2ILUtils { private static readonly Dictionary TypeString = new Dictionary { { 1, "void" }, { 2, "bool" }, { 3, "char" }, { 4, "sbyte" }, { 5, "byte" }, { 6, "short" }, { 7, "ushort" }, { 8, "int" }, { 9, "uint" }, { 10, "long" }, { 11, "ulong" }, { 12, "float" }, { 13, "double" }, { 14, "string" }, { 22, "TypedReference" }, { 24, "IntPtr" }, { 25, "UIntPtr" }, { 28, "object" } }; private static readonly Dictionary PrimitiveSizes = new Dictionary { { "Byte", 1uL }, { "SByte", 1uL }, { "Boolean", 1uL }, { "Int16", 2uL }, { "UInt16", 2uL }, { "Char", 2uL }, { "Int32", 4uL }, { "UInt32", 4uL }, { "Single", 4uL }, { "Int64", 8uL }, { "UInt64", 8uL }, { "Double", 8uL }, { "IntPtr", 8uL }, { "UIntPtr", 8uL } }; private static Dictionary _cachedVersionAttributes = new Dictionary(); internal static void Reset() { _cachedVersionAttributes.Clear(); } internal static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssembly, Il2CppTypeDefinition typeDef, bool fullName = false) { string text = string.Empty; if (fullName) { text = typeDef.Namespace; if (text != string.Empty) { text += "."; } } if (typeDef.DeclaringTypeIndex != -1) { text = text + GetTypeName(metadata, cppAssembly, cppAssembly.GetType(typeDef.DeclaringTypeIndex)) + "."; } text += metadata.GetStringFromIndex(typeDef.NameIndex); List list = new List(); if (typeDef.GenericContainerIndex < 0) { return text; } Il2CppGenericContainer il2CppGenericContainer = metadata.genericContainers[typeDef.GenericContainerIndex]; for (int i = 0; i < il2CppGenericContainer.genericParameterCount; i++) { int num = il2CppGenericContainer.genericParameterStart + i; Il2CppGenericParameter il2CppGenericParameter = metadata.genericParameters[num]; list.Add(metadata.GetStringFromIndex(il2CppGenericParameter.nameIndex)); } text = text.Replace($"`{il2CppGenericContainer.genericParameterCount}", ""); return text + "<" + string.Join(", ", list) + ">"; } internal static Il2CppTypeReflectionData[]? GetGenericTypeParams(Il2CppGenericInst genericInst) { if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null) { return null; } Il2CppTypeReflectionData[] array = new Il2CppTypeReflectionData[genericInst.pointerCount]; ulong[] array2 = LibCpp2IlMain.Binary.ReadNUintArrayAtVirtualAddress(genericInst.pointerStart, (long)genericInst.pointerCount); for (uint num = 0u; num < genericInst.pointerCount; num++) { Il2CppType il2CppTypeFromPointer = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(array2[num]); array[num] = GetTypeReflectionData(il2CppTypeFromPointer); } return array; } internal static string GetGenericTypeParamNames(Il2CppMetadata metadata, Il2CppBinary cppAssembly, Il2CppGenericInst genericInst) { Il2CppMetadata metadata2 = metadata; Il2CppBinary cppAssembly2 = cppAssembly; string[] value = genericInst.Types.Select((Il2CppType t) => GetTypeName(metadata2, cppAssembly2, t)).ToArray(); return "<" + string.Join(", ", value) + ">"; } public static string GetTypeName(Il2CppMetadata metadata, Il2CppBinary cppAssembly, Il2CppType type, bool fullName = false) { switch (type.Type) { case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: { Il2CppTypeDefinition typeDef = metadata.typeDefs[type.Data.ClassIndex]; string name = string.Empty; return name + GetTypeName(metadata, cppAssembly, typeDef, fullName); } case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: { Il2CppGenericClass il2CppGenericClass = cppAssembly.ReadReadableAtVirtualAddress(type.Data.GenericClass); string name = il2CppGenericClass.TypeDefinition.Name; Il2CppGenericInst classInst = il2CppGenericClass.Context.ClassInst; name = name.Replace($"`{classInst.pointerCount}", ""); return name + GetGenericTypeParamNames(metadata, cppAssembly, classInst); } case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: { Il2CppGenericParameter il2CppGenericParameter = metadata.genericParameters[type.Data.GenericParameterIndex]; return metadata.GetStringFromIndex(il2CppGenericParameter.nameIndex); } case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: { Il2CppArrayType il2CppArrayType = cppAssembly.ReadReadableAtVirtualAddress(type.Data.Array); Il2CppType il2CppTypeFromPointer3 = cppAssembly.GetIl2CppTypeFromPointer(il2CppArrayType.etype); return GetTypeName(metadata, cppAssembly, il2CppTypeFromPointer3) + "[" + new string(',', il2CppArrayType.rank - 1) + "]"; } case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: { Il2CppType il2CppTypeFromPointer2 = cppAssembly.GetIl2CppTypeFromPointer(type.Data.Type); return GetTypeName(metadata, cppAssembly, il2CppTypeFromPointer2) + "[]"; } case Il2CppTypeEnum.IL2CPP_TYPE_PTR: { Il2CppType il2CppTypeFromPointer = cppAssembly.GetIl2CppTypeFromPointer(type.Data.Type); return GetTypeName(metadata, cppAssembly, il2CppTypeFromPointer) + "*"; } default: return TypeString[(int)type.Type]; } } internal static object? GetDefaultValue(int dataIndex, int typeIndex) { Il2CppMetadata theMetadata = LibCpp2IlMain.TheMetadata; Il2CppBinary binary = LibCpp2IlMain.Binary; if (dataIndex == -1) { return null; } int defaultValueFromIndex = theMetadata.GetDefaultValueFromIndex(dataIndex); if (defaultValueFromIndex <= 0) { return null; } Il2CppType type = binary.GetType(typeIndex); theMetadata.GetLockOrThrow(); theMetadata.Position = defaultValueFromIndex; try { int bytesRead2; switch (type.Type) { case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: return theMetadata.ReadBoolean(); case Il2CppTypeEnum.IL2CPP_TYPE_U1: return theMetadata.ReadByte(); case Il2CppTypeEnum.IL2CPP_TYPE_I1: return theMetadata.ReadSByte(); case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: return BitConverter.ToChar(theMetadata.ReadByteArrayAtRawAddressNoLock(defaultValueFromIndex, 2), 0); case Il2CppTypeEnum.IL2CPP_TYPE_U2: return theMetadata.ReadUInt16(); case Il2CppTypeEnum.IL2CPP_TYPE_I2: return theMetadata.ReadInt16(); case Il2CppTypeEnum.IL2CPP_TYPE_U4: if (LibCpp2IlMain.MetadataVersion < 29f) { return theMetadata.ReadUInt32(); } return theMetadata.ReadUnityCompressedUIntAtRawAddrNoLock(defaultValueFromIndex, out bytesRead2); case Il2CppTypeEnum.IL2CPP_TYPE_I4: if (LibCpp2IlMain.MetadataVersion < 29f) { return theMetadata.ReadInt32(); } return theMetadata.ReadUnityCompressedIntAtRawAddr(defaultValueFromIndex, doLock: false, out bytesRead2); case Il2CppTypeEnum.IL2CPP_TYPE_U8: return theMetadata.ReadUInt64(); case Il2CppTypeEnum.IL2CPP_TYPE_I8: return theMetadata.ReadInt64(); case Il2CppTypeEnum.IL2CPP_TYPE_R4: return theMetadata.ReadSingle(); case Il2CppTypeEnum.IL2CPP_TYPE_R8: return theMetadata.ReadDouble(); case Il2CppTypeEnum.IL2CPP_TYPE_STRING: { int bytesRead = 4; int num = ((!(LibCpp2IlMain.MetadataVersion < 29f)) ? theMetadata.ReadUnityCompressedIntAtRawAddr(defaultValueFromIndex, doLock: false, out bytesRead) : theMetadata.ReadInt32()); if (num > 65536) { LibLogger.WarnNewline("[GetDefaultValue] String length is really large: " + num); } return Encoding.UTF8.GetString(theMetadata.ReadByteArrayAtRawAddressNoLock(defaultValueFromIndex + bytesRead, num)); } default: return null; } } finally { theMetadata.ReleaseLock(); } } public static Il2CppTypeReflectionData WrapType(Il2CppTypeDefinition what) { return new Il2CppTypeReflectionData { baseType = what, genericParams = Array.Empty(), isGenericType = false, isType = true }; } public static Il2CppTypeReflectionData GetTypeReflectionData(Il2CppType forWhat) { if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null) { throw new Exception("Can't get type reflection data when not initialized. How did you even get the type?"); } switch (forWhat.Type) { case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT: return WrapType(LibCpp2IlReflection.GetType("Object", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_VOID: return WrapType(LibCpp2IlReflection.GetType("Void", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: return WrapType(LibCpp2IlReflection.GetType("Boolean", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: return WrapType(LibCpp2IlReflection.GetType("Char", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_I1: return WrapType(LibCpp2IlReflection.GetType("SByte", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_U1: return WrapType(LibCpp2IlReflection.GetType("Byte", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_I2: return WrapType(LibCpp2IlReflection.GetType("Int16", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_U2: return WrapType(LibCpp2IlReflection.GetType("UInt16", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_I4: return WrapType(LibCpp2IlReflection.GetType("Int32", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_U4: return WrapType(LibCpp2IlReflection.GetType("UInt32", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_I: return WrapType(LibCpp2IlReflection.GetType("IntPtr", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_U: return WrapType(LibCpp2IlReflection.GetType("UIntPtr", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_I8: return WrapType(LibCpp2IlReflection.GetType("Int64", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_U8: return WrapType(LibCpp2IlReflection.GetType("UInt64", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_R4: return WrapType(LibCpp2IlReflection.GetType("Single", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_R8: return WrapType(LibCpp2IlReflection.GetType("Double", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_STRING: return WrapType(LibCpp2IlReflection.GetType("String", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF: return WrapType(LibCpp2IlReflection.GetType("TypedReference", "System")); case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE: case Il2CppTypeEnum.IL2CPP_TYPE_CLASS: return new Il2CppTypeReflectionData { baseType = LibCpp2IlMain.TheMetadata.typeDefs[forWhat.Data.ClassIndex], genericParams = new Il2CppTypeReflectionData[0], isType = true, isGenericType = false }; case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: { Il2CppGenericClass il2CppGenericClass = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(forWhat.Data.GenericClass); Il2CppTypeDefinition baseType; if (LibCpp2IlMain.MetadataVersion < 27f) { baseType = LibCpp2IlMain.TheMetadata.typeDefs[il2CppGenericClass.TypeDefinitionIndex]; } else { Il2CppType il2CppType = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress((ulong)il2CppGenericClass.TypeDefinitionIndex); baseType = LibCpp2IlMain.TheMetadata.typeDefs[il2CppType.Data.ClassIndex]; } List list = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(il2CppGenericClass.Context.class_inst).Pointers.Select((ulong pointer) => LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(pointer)).Select(GetTypeReflectionData).ToList(); return new Il2CppTypeReflectionData { baseType = baseType, genericParams = list.ToArray(), isType = true, isGenericType = true }; } case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: { Il2CppGenericParameter il2CppGenericParameter = LibCpp2IlMain.TheMetadata.genericParameters[forWhat.Data.GenericParameterIndex]; string stringFromIndex = LibCpp2IlMain.TheMetadata.GetStringFromIndex(il2CppGenericParameter.nameIndex); return new Il2CppTypeReflectionData { baseType = null, genericParams = Array.Empty(), isType = false, isGenericType = false, variableGenericParamName = stringFromIndex, variableGenericParamIndex = forWhat.Data.GenericParameterIndex }; } case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: { Il2CppType il2CppTypeFromPointer2 = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(forWhat.Data.Type); return new Il2CppTypeReflectionData { baseType = null, arrayType = GetTypeReflectionData(il2CppTypeFromPointer2), arrayRank = 1, isArray = true, isType = false, isGenericType = false, genericParams = Array.Empty() }; } case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: { Il2CppArrayType il2CppArrayType = LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(forWhat.Data.Array); Il2CppType il2CppTypeFromPointer = LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(il2CppArrayType.etype); return new Il2CppTypeReflectionData { baseType = null, arrayType = GetTypeReflectionData(il2CppTypeFromPointer), isArray = true, isType = false, arrayRank = il2CppArrayType.rank, isGenericType = false, genericParams = Array.Empty() }; } case Il2CppTypeEnum.IL2CPP_TYPE_PTR: { Il2CppTypeReflectionData typeReflectionData = GetTypeReflectionData(LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(forWhat.Data.Type)); typeReflectionData.isPointer = true; return typeReflectionData; } default: throw new ArgumentException($"Unknown type {forWhat.Type}"); } } public static int VersionAwareSizeOf(Type type, bool dontCheckVersionAttributes = false, bool downsize = true) { if (type.IsEnum) { type = type.GetEnumUnderlyingType(); } if (type.IsPrimitive) { return (int)PrimitiveSizes[type.Name]; } bool flag = downsize && LibCpp2IlMain.Binary.is32Bit; int num = 0; FieldInfo[] fields = type.GetFields(); foreach (FieldInfo fieldInfo in fields) { if (!dontCheckVersionAttributes && !ShouldReadFieldOnThisVersion(fieldInfo)) { continue; } switch (fieldInfo.FieldType.Name) { case "Int64": case "UInt64": num += (flag ? 4 : 8); continue; case "Int32": case "UInt32": num += 4; continue; case "Int16": case "UInt16": num += 2; continue; case "Byte": case "SByte": num++; continue; } if (fieldInfo.FieldType == type) { throw new Exception($"Infinite recursion is not allowed. Field {fieldInfo} of type {type} has the same type as its parent."); } num += VersionAwareSizeOf(fieldInfo.FieldType, dontCheckVersionAttributes, downsize); } return num; } internal static IEnumerable Range(int start, int count) { for (int i = start; i < start + count; i++) { yield return i; } } internal static bool ShouldReadFieldOnThisVersion(FieldInfo i) { if (!_cachedVersionAttributes.TryGetValue(i, out VersionAttribute[] value)) { value = Attribute.GetCustomAttributes(i, typeof(VersionAttribute)).Cast().ToArray(); _cachedVersionAttributes[i] = value; } if (value.Length != 0) { return value.Any((VersionAttribute attr) => LibCpp2IlMain.MetadataVersion >= attr.Min && LibCpp2IlMain.MetadataVersion <= attr.Max); } return true; } internal static void PopulateDeclaringAssemblyCache() { Il2CppImageDefinition[] imageDefinitions = LibCpp2IlMain.TheMetadata.imageDefinitions; foreach (Il2CppImageDefinition il2CppImageDefinition in imageDefinitions) { Il2CppTypeDefinition[] types = il2CppImageDefinition.Types; for (int j = 0; j < types.Length; j++) { types[j].DeclaringAssembly = il2CppImageDefinition; } } } } public class Lz4DecodeStream : Stream { private enum DecodePhase { ReadToken, ReadExLiteralLength, CopyLiteral, ReadMatch, ReadExMatchLength, CopyMatch, Finish } private const int InputBufferCapacity = 4096; private const int DecodeBufferCapacity = 65536; private const int DecodeBufferMask = 65535; private readonly byte[] m_inputBuffer = new byte[4096]; private readonly byte[] m_decodeBuffer = new byte[65536]; private readonly Stream m_baseStream; private readonly bool m_leaveOpen; private long m_position; private long m_inputLeft; private int m_inputBufferPosition = 4096; private int m_decodeBufferPosition; private DecodePhase m_phase; private int m_literalLength; private int m_matchLength; private int m_matchDestination; private int m_decodeBufferStart; public bool IsDataLeft { get { if (m_phase != DecodePhase.CopyLiteral) { return m_matchLength != 0; } return m_literalLength != 0; } } public override bool CanSeek => false; public override bool CanRead => true; public override bool CanWrite => false; public override long Length { get; } public override long Position { get { return m_position; } set { throw new NotSupportedException(); } } public Lz4DecodeStream(byte[] buffer, int offset, int length) : this(new MemoryStream(buffer, offset, length), length, leaveOpen: false) { } public Lz4DecodeStream(Stream baseStream, bool leaveOpen = true) : this(baseStream, baseStream.Length, leaveOpen) { } public Lz4DecodeStream(Stream baseStream, long compressedSize, bool leaveOpen = true) { if (compressedSize <= 0) { throw new ArgumentException($"Compressed size {compressedSize} must be greater then 0"); } m_baseStream = baseStream ?? throw new ArgumentNullException("baseStream"); Length = compressedSize; m_inputLeft = compressedSize; m_phase = DecodePhase.ReadToken; m_leaveOpen = leaveOpen; } ~Lz4DecodeStream() { Dispose(disposing: false); } public override void Flush() { } public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { using MemoryStream stream = new MemoryStream(buffer, offset, count); return (int)Read(stream, count); } public long Read(Stream stream, long count) { if (stream == null) { throw new ArgumentNullException("stream"); } if (count <= 0) { throw new ArgumentException("count"); } long num = count; switch (m_phase) { case DecodePhase.ReadToken: { int num2 = ReadInputByte(); m_literalLength = num2 >> 4; m_matchLength = (num2 & 0xF) + 4; if (m_literalLength != 0) { if (m_literalLength == 15) { goto case DecodePhase.ReadExLiteralLength; } goto case DecodePhase.CopyLiteral; } goto case DecodePhase.ReadMatch; } case DecodePhase.ReadExLiteralLength: { int num4; do { num4 = ReadInputByte(); m_literalLength += num4; } while (num4 == 255); goto case DecodePhase.CopyLiteral; } case DecodePhase.CopyLiteral: if (m_literalLength >= num) { Write(stream, (int)num); m_literalLength -= (int)num; num = 0L; m_phase = DecodePhase.CopyLiteral; goto case DecodePhase.Finish; } Write(stream, m_literalLength); num -= m_literalLength; goto case DecodePhase.ReadMatch; case DecodePhase.ReadMatch: m_matchDestination = ReadInputInt16(); if (m_matchLength == 19) { goto case DecodePhase.ReadExMatchLength; } goto case DecodePhase.CopyMatch; case DecodePhase.ReadExMatchLength: { int num3; do { num3 = ReadInputByte(); m_matchLength += num3; } while (num3 == 255); goto case DecodePhase.CopyMatch; } case DecodePhase.CopyMatch: { int num5 = (int)((m_matchLength < num) ? m_matchLength : num); while (num5 > 0) { int num6 = (m_decodeBufferPosition - m_matchDestination) & 0xFFFF; int num7 = 65536 - num6; int num8 = 65536 - m_decodeBufferPosition; int num9 = ((num7 < num8) ? num7 : num8); int num10 = ((num5 < num9) ? num5 : num9); int num11 = m_decodeBufferPosition - num6; if (num11 > 0 && num11 < num10) { for (int i = 0; i < num10; i++) { m_decodeBuffer[m_decodeBufferPosition++] = m_decodeBuffer[num6++]; } } else { Buffer.BlockCopy(m_decodeBuffer, num6, m_decodeBuffer, m_decodeBufferPosition, num10); m_decodeBufferPosition += num10; } num5 -= num10; m_matchLength -= num10; num -= num10; if (m_decodeBufferPosition == 65536) { FillOutputStream(stream); } } if (num != 0L) { goto case DecodePhase.ReadToken; } m_phase = DecodePhase.CopyMatch; goto case DecodePhase.Finish; } case DecodePhase.Finish: FillOutputStream(stream); return count - num; default: throw new Exception($"Unknonw decode phase {m_phase}"); } } public void ReadBuffer(byte[] buffer, int offset, int count) { using MemoryStream stream = new MemoryStream(buffer, offset, count); ReadBuffer(stream, count); } public void ReadBuffer(Stream stream, long count) { int num = (int)Read(stream, count); if (num != count) { throw new Exception($"Unexpected end of input stream. Read {num} but expected {count}"); } if (IsDataLeft) { throw new Exception("Some data left"); } } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } public override void SetLength(long value) { throw new NotSupportedException(); } protected override void Dispose(bool disposing) { if (!m_leaveOpen) { m_baseStream.Dispose(); } base.Dispose(disposing); } private int ReadInputByte() { if (m_inputBufferPosition == 4096) { FillInputBuffer(); } return m_inputBuffer[m_inputBufferPosition++]; } private int ReadInputInt16() { switch (4096 - m_inputBufferPosition) { case 0: FillInputBuffer(); break; case 1: m_inputBuffer[0] = m_inputBuffer[m_inputBufferPosition]; FillInputBuffer(1); break; } return m_inputBuffer[m_inputBufferPosition++] | (m_inputBuffer[m_inputBufferPosition++] << 8); } private void Write(Stream stream, int count) { while (count > 0) { if (m_inputBufferPosition == 4096) { FillInputBuffer(); } int num = 4096 - m_inputBufferPosition; int num2 = 65536 - m_decodeBufferPosition; int num3 = ((num < num2) ? num : num2); int num4 = ((count < num3) ? count : num3); Buffer.BlockCopy(m_inputBuffer, m_inputBufferPosition, m_decodeBuffer, m_decodeBufferPosition, num4); count -= num4; m_inputBufferPosition += num4; m_decodeBufferPosition += num4; if (m_decodeBufferPosition == 65536) { FillOutputStream(stream); } } } private void FillInputBuffer(int offset = 0) { int num = 4096 - offset; int num2 = (int)((num < m_inputLeft) ? num : m_inputLeft); m_inputBufferPosition = 0; while (num2 > 0) { int num3 = m_baseStream.Read(m_inputBuffer, offset, num2); if (num3 == 0) { throw new Exception("No data left"); } m_position += num3; offset += num3; num2 -= num3; m_inputLeft -= num3; } } private void FillOutputStream(Stream stream) { int num = m_decodeBufferPosition - m_decodeBufferStart; int num2 = 65536 - m_decodeBufferStart; int num3 = ((num2 < num) ? num2 : num); stream.Write(m_decodeBuffer, m_decodeBufferStart, num3); stream.Write(m_decodeBuffer, 0, num - num3); m_decodeBufferPosition &= 65535; m_decodeBufferStart = m_decodeBufferPosition; } } public class MetadataUsage { public readonly MetadataUsageType Type; public readonly ulong Offset; private readonly uint _value; private string? _cachedName; private Il2CppType? _cachedType; private Il2CppTypeReflectionData? _cachedTypeReflectionData; private Il2CppMethodDefinition? _cachedMethod; private Il2CppFieldDefinition? _cachedField; private string? _cachedLiteral; private Cpp2IlMethodRef? _cachedGenericMethod; public uint RawValue => _value; public object Value { get { switch (Type) { case MetadataUsageType.TypeInfo: case MetadataUsageType.Type: return AsType(); case MetadataUsageType.MethodDef: return AsMethod(); case MetadataUsageType.FieldInfo: return AsField(); case MetadataUsageType.StringLiteral: return AsLiteral(); case MetadataUsageType.MethodRef: return AsGenericMethodRef(); default: throw new ArgumentOutOfRangeException(); } } } public bool IsValid { get { try { _ = Value; return true; } catch (Exception) { return false; } } } public MetadataUsage(MetadataUsageType type, ulong offset, uint value) { Type = type; _value = value; Offset = offset; } public Il2CppTypeReflectionData AsType() { if (_cachedTypeReflectionData == null) { MetadataUsageType type = Type; if (type - 1 > MetadataUsageType.TypeInfo) { throw new Exception($"Cannot cast metadata usage of kind {Type} to a Type"); } try { _cachedType = LibCpp2IlMain.Binary.GetType((int)_value); _cachedTypeReflectionData = LibCpp2ILUtils.GetTypeReflectionData(_cachedType); _cachedName = LibCpp2ILUtils.GetTypeReflectionData(_cachedType)?.ToString(); } catch (Exception innerException) { throw new Exception($"Failed to convert this metadata usage to a type, but it is of type {Type}, with a value of {_value} (0x{_value:X}). There are {LibCpp2IlMain.Binary.NumTypes} types", innerException); } } return _cachedTypeReflectionData; } public Il2CppMethodDefinition AsMethod() { if (_cachedMethod == null) { if (Type != MetadataUsageType.MethodDef) { throw new Exception($"Cannot cast metadata usage of kind {Type} to a Method Def"); } _cachedMethod = LibCpp2IlMain.TheMetadata.methodDefs[_value]; _cachedName = _cachedMethod.GlobalKey; } return _cachedMethod; } public Il2CppFieldDefinition AsField() { if (_cachedField == null) { if (Type != MetadataUsageType.FieldInfo) { throw new Exception($"Cannot cast metadata usage of kind {Type} to a Field"); } Il2CppFieldRef il2CppFieldRef = LibCpp2IlMain.TheMetadata.fieldRefs[_value]; _cachedField = il2CppFieldRef.FieldDefinition; _cachedName = il2CppFieldRef.DeclaringTypeDefinition.FullName + "." + _cachedField.Name; } return _cachedField; } public string AsLiteral() { if (_cachedLiteral == null) { if (Type != MetadataUsageType.StringLiteral) { throw new Exception($"Cannot cast metadata usage of kind {Type} to a String Literal"); } _cachedName = (_cachedLiteral = LibCpp2IlMain.TheMetadata.GetStringLiteralFromIndex(_value)); } return _cachedLiteral; } public Cpp2IlMethodRef AsGenericMethodRef() { if (_cachedGenericMethod == null) { if (Type != MetadataUsageType.MethodRef) { throw new Exception($"Cannot cast metadata usage of kind {Type} to a Generic Method Ref"); } Il2CppMethodSpec methodSpec = LibCpp2IlMain.Binary.GetMethodSpec((int)_value); _cachedGenericMethod = new Cpp2IlMethodRef(methodSpec); _cachedName = _cachedGenericMethod.ToString(); } return _cachedGenericMethod; } public override string ToString() { return $"Metadata Usage {{type={Type}, Value={Value}}}"; } public static MetadataUsage? DecodeMetadataUsage(ulong encoded, ulong address) { MetadataUsageType metadataUsageType = (MetadataUsageType)((encoded & 0xE0000000u) >> 29); if (metadataUsageType <= MetadataUsageType.MethodRef && metadataUsageType >= MetadataUsageType.TypeInfo) { uint num = (uint)(encoded & 0x1FFFFFFF); if (LibCpp2IlMain.MetadataVersion >= 27f) { num >>= 1; } if ((metadataUsageType == MetadataUsageType.Type || metadataUsageType == MetadataUsageType.TypeInfo) && num > LibCpp2IlMain.Binary.NumTypes) { return null; } if (metadataUsageType == MetadataUsageType.MethodDef && num > LibCpp2IlMain.TheMetadata.methodDefs.Length) { return null; } return new MetadataUsage(metadataUsageType, address, num); } return null; } } public enum MetadataUsageType : uint { TypeInfo = 1u, Type, MethodDef, FieldInfo, StringLiteral, MethodRef } internal static class MiniArm64Decompiler { private static (uint reg, ulong page)? GetAdrp(uint inst, ulong pc) { if ((inst.Bits(24, 8) & 0x8F) != 128) { return null; } uint num = inst.Bits(29, 2); uint num2 = (inst.Bits(5, 19) << 14) + (num << 12); ulong num3 = pc & 0xFFFFFFFFFFFFF000uL; return (inst.Bits(0, 5), num3 + num2); } private static (uint reg, ulong addr)? GetAdr(uint inst, ulong pc) { if (inst.Bits(24, 5) != 16 || inst.Bits(31, 1) != 0) { return null; } ulong num = (inst.Bits(5, 19) << 2) + inst.Bits(29, 2); num = (((num & 0x100000) == 0L) ? num : (num | 0xFFFFFFFFFFE00000uL)); return (inst.Bits(0, 5), pc + num); } private static (uint reg_n, uint reg_d, uint imm)? GetAdd64(uint inst) { if (inst.Bits(22, 10) != 580) { return null; } uint item = inst.Bits(10, 12); uint item2 = inst.Bits(5, 5); uint item3 = inst.Bits(0, 5); return (item2, item3, item); } private static (uint reg_t, uint reg_n, uint simm)? GetLdr64ImmOffset(uint inst) { if (inst.Bits(22, 10) != 997) { return null; } uint item = inst.Bits(10, 12); uint item2 = inst.Bits(0, 5); uint item3 = inst.Bits(5, 5); return (item2, item3, item); } public static bool IsB(uint inst) { return inst.Bits(26, 6) == 5; } public static Dictionary GetAddressesLoadedIntoRegisters(List funcBody, ulong baseAddress, ElfFile image) { Dictionary dictionary = new Dictionary(); ulong num = baseAddress; foreach (uint item5 in funcBody) { (uint, ulong)? adrp = GetAdrp(item5, num); if (adrp.HasValue) { (uint, ulong) valueOrDefault = adrp.GetValueOrDefault(); uint item = valueOrDefault.Item1; ulong item2 = valueOrDefault.Item2; dictionary[item] = item2; } (uint, ulong)? adr = GetAdr(item5, num); if (adr.HasValue) { (uint, ulong) valueOrDefault2 = adr.GetValueOrDefault(); uint item3 = valueOrDefault2.Item1; ulong item4 = valueOrDefault2.Item2; dictionary[item3] = item4; } (uint, uint, uint)? add = GetAdd64(item5); if (add.HasValue) { var (num2, num3, num4) = add.GetValueOrDefault(); if (num2 == num3 && dictionary.ContainsKey(num3)) { dictionary[num3] += num4; } } (uint, uint, uint)? ldr64ImmOffset = GetLdr64ImmOffset(item5); if (ldr64ImmOffset.HasValue) { var (num5, num6, num7) = ldr64ImmOffset.GetValueOrDefault(); if (num5 == num6 && dictionary.ContainsKey(num6)) { dictionary[num6] += num7 * 8; dictionary[num6] = image.ReadPointerAtVirtualAddress(dictionary[num6]); } } num += 4; } return dictionary; } public static List ReadFunctionAtRawAddress(ElfFile file, uint loc, uint maxLength) { List list = new List(); file.Position = file.MapVirtualAddressToRaw(loc); uint num; do { num = file.ReadUInt32(); list.Add(num); } while (!IsB(num) && list.Count < maxLength); return list; } } public abstract class ReadableClass { protected bool IsAtLeast(float vers) { return LibCpp2IlMain.MetadataVersion >= vers; } protected bool IsLessThan(float vers) { return LibCpp2IlMain.MetadataVersion < vers; } protected bool IsAtMost(float vers) { return LibCpp2IlMain.MetadataVersion <= vers; } protected bool IsNot(float vers) { return Math.Abs(LibCpp2IlMain.MetadataVersion - vers) > 0.001f; } public abstract void Read(ClassReadingBinaryReader reader); } public class TokenComparer : IComparer { public int Compare(IIl2CppTokenProvider? x, IIl2CppTokenProvider? y) { if (x != y) { if (x != null) { if (y != null) { return x.Token.CompareTo(y.Token); } return 0; } return 0; } return 0; } } [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] internal class VersionAttribute : Attribute { public float Min { get; set; } public float Max { get; set; } = 99f; } } namespace LibCpp2IL.Wasm { public class ConstantExpression { public enum ConstantInstruction : byte { I32_CONST = 65, I64_CONST = 66, F32_CONST = 67, F64_CONST = 68, REF_NULL_FUNCREF = 208, REF_NULL_EXTERNREF = 209, REF_FUNC = 210, GLOBAL_GET = 35 } public ConstantInstruction Type; public IConvertible? Value; public ConstantExpression(WasmFile file) { Type = (ConstantInstruction)file.ReadByte(); switch (Type) { case ConstantInstruction.GLOBAL_GET: case ConstantInstruction.I32_CONST: case ConstantInstruction.I64_CONST: case ConstantInstruction.REF_FUNC: Value = file.BaseStream.ReadLEB128Unsigned(); break; case ConstantInstruction.F32_CONST: Value = file.ReadSingle(); break; case ConstantInstruction.F64_CONST: Value = file.ReadDouble(); break; case ConstantInstruction.REF_NULL_FUNCREF: { byte b = file.ReadByte(); switch (b) { case 111: Type = ConstantInstruction.REF_NULL_EXTERNREF; break; case 112: Type = ConstantInstruction.REF_NULL_FUNCREF; break; default: throw new Exception($"Invalid subtype {b}"); } Value = null; break; } default: throw new ArgumentOutOfRangeException(); } byte b2 = file.ReadByte(); if (b2 != 11) { throw new Exception($"Invalid end byte, got 0x{b2:X2}, expecting 0x0B"); } } } public class WasmCodeSection : WasmSection { private readonly WasmFile _file; public ulong FunctionCount; public readonly List Functions = new List(); public byte[] RawSectionContent => _file.GetRawBinaryContent().SubArray((int)Pointer, (int)Size); internal WasmCodeSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { _file = file; FunctionCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < FunctionCount; num++) { Functions.Add(new WasmFunctionBody(file)); } LibLogger.VerboseNewline($"\t\tRead {Functions.Count} function bodies"); } } public class WasmDataSection : WasmSection { public ulong DataCount; public List DataEntries = new List(); internal WasmDataSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { DataCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < DataCount; num++) { DataEntries.Add(new WasmDataSegment(file)); } LibLogger.VerboseNewline($"\t\tRead {DataEntries.Count} data segments"); } } public class WasmDataSegment { public ulong Index; public ConstantExpression? OffsetExpr; public long FileOffset; public ulong Size; public byte[] Data; public ulong VirtualOffset { get { ConstantExpression? offsetExpr = OffsetExpr; if (offsetExpr != null && offsetExpr.Type == ConstantExpression.ConstantInstruction.I32_CONST) { return (ulong)(object)OffsetExpr.Value; } return ulong.MaxValue; } } public WasmDataSegment(WasmFile readFrom) { byte b = readFrom.ReadByte(); if (b == 2) { Index = readFrom.BaseStream.ReadLEB128Unsigned(); } else { Index = 0uL; } if (b == 0 || b == 2) { OffsetExpr = new ConstantExpression(readFrom); } else { OffsetExpr = null; } Size = readFrom.BaseStream.ReadLEB128Unsigned(); FileOffset = readFrom.Position; Data = readFrom.ReadByteArrayAtRawAddress(FileOffset, (int)Size); } } public struct WasmDynCallCoefficients { public ulong andWith; public ulong addConstant; } public class WasmElementSection : WasmSection { public ulong ElementCount; public readonly List Elements = new List(); internal WasmElementSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { ElementCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < ElementCount; num++) { Elements.Add(new WasmElementSegment(file)); } } } public class WasmElementSegment { public enum ElementSegmentMode { Active, Passive, Declarative } public byte Flags; public ElementSegmentMode Mode; public ulong TableIdx; public ConstantExpression? Offset; public ulong Count; public byte ElemKind; public List? FunctionIndices; public WasmTypeEnum ElemType; public List? ConstantExpressions; public WasmElementSegment(WasmFile file) { Flags = file.ReadByte(); if ((Flags & 3) == 2) { TableIdx = file.BaseStream.ReadLEB128Unsigned(); } else { TableIdx = 0uL; } if ((Flags & 1) == 0) { Mode = ElementSegmentMode.Active; Offset = new ConstantExpression(file); } else if ((Flags & 2) == 0) { Mode = ElementSegmentMode.Passive; } else { Mode = ElementSegmentMode.Declarative; } if ((Flags & 3) == 0) { ElemKind = 0; ElemType = WasmTypeEnum.funcRef; } else { byte b = file.ReadByte(); if ((Flags & 4) == 0) { ElemKind = b; } else { ElemType = (WasmTypeEnum)b; } } Count = file.BaseStream.ReadLEB128Unsigned(); if ((Flags & 4) == 0) { FunctionIndices = new List(); for (ulong num = 0uL; num < Count; num++) { FunctionIndices.Add(file.BaseStream.ReadLEB128Unsigned()); } } else { ConstantExpressions = new List(); for (ulong num2 = 0uL; num2 < Count; num2++) { ConstantExpressions.Add(new ConstantExpression(file)); } } } } public class WasmExportEntry { public WasmString Name; public WasmExternalKind Kind; public ulong Index; public WasmExportEntry(WasmFile file) { Name = new WasmString(file); Kind = (WasmExternalKind)file.ReadByte(); Index = file.BaseStream.ReadLEB128Unsigned(); } } public class WasmExportSection : WasmSection { public ulong ExportCount; public readonly List Exports = new List(); internal WasmExportSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { ExportCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < ExportCount; num++) { WasmExportEntry item = new WasmExportEntry(file); Exports.Add(item); } LibLogger.VerboseNewline($"\t\tRead {Exports.Count} exported functions"); } } public enum WasmExternalKind : byte { EXT_FUNCTION, EXT_TABLE, EXT_MEMORY, EXT_GLOBAL } public sealed class WasmFile : Il2CppBinary { public static Dictionary? RemappedDynCallFunctions; public readonly List FunctionTable = new List(); internal readonly List Sections = new List(); private byte[] _raw; private WasmMemoryBlock _memoryBlock; private readonly Dictionary DynCallCoefficients = new Dictionary(); public override ClassReadingBinaryReader Reader => _memoryBlock; public override long RawLength => _raw.Length; internal WasmGlobalType[] GlobalTypes => (from e in ImportSection.Entries where e.Kind == WasmExternalKind.EXT_GLOBAL select e.GlobalEntry).Concat(GlobalSection.Globals.Select((WasmGlobalEntry g) => g.Type)).ToArray(); internal WasmGlobalSection GlobalSection => (WasmGlobalSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_GLOBAL); internal WasmTypeSection TypeSection => (WasmTypeSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_TYPE); internal WasmFunctionSection FunctionSection => (WasmFunctionSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_FUNCTION); internal WasmDataSection DataSection => (WasmDataSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_DATA); internal WasmCodeSection CodeSection => (WasmCodeSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_CODE); internal WasmImportSection ImportSection => (WasmImportSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_IMPORT); internal WasmElementSection ElementSection => (WasmElementSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_ELEMENT); internal WasmExportSection ExportSection => (WasmExportSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_EXPORT); public override Stream BaseStream => _memoryBlock?.BaseStream ?? base.BaseStream; public WasmFile(MemoryStream input) : base(input) { is32Bit = true; InstructionSetId = DefaultInstructionSets.WASM; _raw = input.GetBuffer(); uint num = ReadUInt32(); int num2 = ReadInt32(); if (num != 1836278016) { throw new Exception($"WASM magic mismatch; got 0x{num:X}"); } if (num2 != 1) { throw new Exception($"Unknown version, expecting 1, got {num2}"); } LibLogger.VerboseNewline("\tWASM Magic and version match expectations. Reading sections..."); int num3 = 0; while (base.Position < RawLength && num3 < 1000) { WasmSection wasmSection = WasmSection.MakeSection(this); base.Position = wasmSection.Pointer + (long)wasmSection.Size; Sections.Add(wasmSection); num3++; } LibLogger.VerboseNewline($"\tRead {Sections.Count} WASM sections. Allocating memory block..."); _memoryBlock = new WasmMemoryBlock(this); LibLogger.VerboseNewline($"\tAllocated memory block of {_memoryBlock.Bytes.Length} (0x{_memoryBlock.Bytes.Length:X}) bytes ({(float)_memoryBlock.Bytes.Length / 1024f / 1024f:F2}MB). Constructing function table..."); foreach (WasmImportEntry entry in ImportSection.Entries) { if (entry.Kind == WasmExternalKind.EXT_FUNCTION) { FunctionTable.Add(new WasmFunctionDefinition(entry)); } } for (int i = 0; i < CodeSection.Functions.Count; i++) { WasmFunctionBody body = CodeSection.Functions[i]; int count = FunctionTable.Count; FunctionTable.Add(new WasmFunctionDefinition(this, body, i, count)); } LibLogger.VerboseNewline($"\tBuilt function table of {FunctionTable.Count} entries. Calculating dynCall coefficients..."); CalculateDynCallOffsets(); LibLogger.VerboseNewline($"\tGot dynCall coefficients for {DynCallCoefficients.Count} signatures"); } public override byte GetByteAtRawAddress(ulong addr) { return _raw[addr]; } public override byte[] GetRawBinaryContent() { return _raw; } public WasmFunctionDefinition GetFunctionFromIndexAndSignature(ulong index, string signature) { if (!DynCallCoefficients.TryGetValue(signature, out var value)) { throw new Exception("Can't get function with signature " + signature + ", as it's not defined in the binary"); } index = (index & value.andWith) + value.addConstant; ulong num = ElementSection.Elements[0].FunctionIndices[(int)index]; return FunctionTable[(int)num]; } private void CalculateDynCallOffsets() { //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Invalid comparison between Unknown and I4 //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Invalid comparison between Unknown and I4 //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Invalid comparison between Unknown and I4 //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Invalid comparison between Unknown and I4 //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_03d5: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Invalid comparison between Unknown and I4 //IL_03e8: Unknown result type (might be due to invalid IL or missing references) if (RemappedDynCallFunctions != null) { foreach (WasmExportEntry item in ExportSection.Exports.Where((WasmExportEntry e) => e.Kind == WasmExternalKind.EXT_FUNCTION)) { if (RemappedDynCallFunctions.TryGetValue(item.Name, out string value)) { LibLogger.VerboseNewline($"\t\tRemapped exported function {item.Name} to {value}"); item.Name.Value = value; } } } foreach (WasmExportEntry item2 in ExportSection.Exports.Where((WasmExportEntry e) => e.Kind == WasmExternalKind.EXT_FUNCTION && e.Name.Value.StartsWith("dynCall_"))) { string value2 = item2.Name.Value; int length = "dynCall_".Length; string text = value2.Substring(length, value2.Length - length); WasmFunctionBody associatedFunctionBody = FunctionTable[(int)item2.Index].AssociatedFunctionBody; List list = Disassembler.Disassemble(associatedFunctionBody.Instructions, (uint)associatedFunctionBody.InstructionsOffset); WasmInstruction[] array = list.Where(delegate(WasmInstruction i) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_000a: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 WasmMnemonic mnemonic2 = i.Mnemonic; return (int)mnemonic2 == 65 || (int)mnemonic2 == 113 || (int)mnemonic2 == 106; }).ToArray(); ulong andWith; ulong addConstant; if (array.Length == 2) { if ((int)array[^1].Mnemonic == 113) { andWith = (ulong)array[0].Operands[0]; addConstant = 0uL; } else { if ((int)array[^1].Mnemonic != 106) { LibLogger.WarnNewline($"\t\tCouldn't calculate coefficients for {text}, got only 2 instructions but the last was {array[^1].Mnemonic}, not I32And or I32Add"); continue; } addConstant = (ulong)array[0].Operands[0]; andWith = 2147483647uL; } } else if (array.Length == 4) { IEnumerable first = array.Select((WasmInstruction i) => i.Mnemonic); WasmMnemonic[] array2 = new WasmMnemonic[4]; RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); if (!first.SequenceEqual((IEnumerable)(object)array2)) { LibLogger.WarnNewline($"\t\tCouldn't calculate coefficients for {text}, got mnemonics {string.Join(", ", array.Select((WasmInstruction i) => i.Mnemonic))}, expecting I32Const, I32And, I32Const, I32Add"); continue; } andWith = (ulong)array[0].Operands[0]; addConstant = (ulong)array[2].Operands[0]; } else { if (!list.All(delegate(WasmInstruction d) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_000a: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 WasmMnemonic mnemonic = d.Mnemonic; return (int)mnemonic == 32 || (int)mnemonic == 17 || (int)mnemonic == 11; })) { if ((int)list[list.Count - 1].Mnemonic == 11) { if ((int)list[list.Count - 2].Mnemonic == 17) { if ((int)list[list.Count - 3].Mnemonic == 32) { if ((byte)list[list.Count - 3].Operands[0] == 0) { LibLogger.VerboseNewline("\t\tAssuming index is not manipulated for dynCall_" + text + " (method ends with LocalGet 0, CallIndirect, End)"); andWith = 2147483647uL; addConstant = 0uL; goto IL_046e; } } } } LibLogger.WarnNewline($"\t\tCouldn't calculate coefficients for {text}, got {array.Length} instructions; expecting 4"); continue; } andWith = 2147483647uL; addConstant = 0uL; } goto IL_046e; IL_046e: DynCallCoefficients[text] = new WasmDynCallCoefficients { andWith = andWith, addConstant = addConstant }; } } public override long MapVirtualAddressToRaw(ulong uiAddr, bool throwOnError = true) { if (uiAddr > (ulong)(_memoryBlock.Bytes.Length + _raw.Length)) { if (throwOnError) { throw new Exception($"Way out of bounds! Requested 0x{uiAddr:X}, memory block + raw length = 0x{_memoryBlock.Bytes.Length + _raw.Length:X}"); } return -9223372036854774808L; } return (long)uiAddr; } public override ulong MapRawAddressToVirtual(uint offset) { WasmDataSection dataSection = DataSection; if (offset > dataSection.Pointer && offset < dataSection.Pointer + (long)dataSection.Size) { WasmDataSegment wasmDataSegment = dataSection.DataEntries.FirstOrDefault((WasmDataSegment entry) => offset >= entry.FileOffset && offset < entry.FileOffset + entry.Data.Length); if (wasmDataSegment != null && wasmDataSegment.VirtualOffset < ulong.MaxValue) { return wasmDataSegment.VirtualOffset + (ulong)(offset - wasmDataSegment.FileOffset); } } return offset; } internal override object? ReadPrimitive(Type type, bool overrideArchCheck = false) { return _memoryBlock.ReadPrimitive(type, overrideArchCheck); } public override string ReadStringToNull(long offset) { return _memoryBlock.ReadStringToNull(offset); } public override ulong GetRva(ulong pointer) { return pointer; } public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { return 0uL; } public override byte[] GetEntirePrimaryExecutableSection() { return ((WasmCodeSection)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_CODE)).RawSectionContent; } public override ulong GetVirtualAddressOfPrimaryExecutableSection() { return (ulong)Sections.First((WasmSection s) => s.Type == WasmSectionId.SEC_CODE).Pointer; } } public class WasmFunctionBody { public ulong BodySize; public ulong LocalCount; public readonly List Locals = new List(); public long InstructionsOffset; public byte[] Instructions; public WasmFunctionBody(WasmFile file) { BodySize = file.BaseStream.ReadLEB128Unsigned(); long position = file.Position; LocalCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < LocalCount; num++) { Locals.Add(new WasmLocalEntry(file)); } InstructionsOffset = file.Position; Instructions = file.ReadByteArrayAtRawAddress(InstructionsOffset, (int)(position + (long)BodySize - InstructionsOffset)); } } public class WasmFunctionDefinition { public bool IsImport; public string? ImportName; public ulong Pointer; public WasmFunctionBody? AssociatedFunctionBody; private ulong TypeIndex; public int FunctionTableIndex; public WasmFunctionDefinition(WasmImportEntry entry) { IsImport = true; TypeIndex = entry.FunctionEntry; ImportName = string.Concat(entry.Module, ".", entry.Field); } public WasmFunctionDefinition(WasmFile file, WasmFunctionBody body, int index, int functionTableIndex) { FunctionTableIndex = functionTableIndex; IsImport = false; Pointer = (ulong)body.InstructionsOffset; TypeIndex = file.FunctionSection.Types[index]; AssociatedFunctionBody = body; } public WasmTypeEntry GetType(WasmFile file) { return file.TypeSection.Types[(int)TypeIndex]; } public override string ToString() { if (!IsImport) { return $"WASM Function at pointer 0x{Pointer:X}, TypeIndex {TypeIndex}, with {AssociatedFunctionBody.Instructions.Length} bytes of code"; } return $"WASM Imported Function: {ImportName}, Pointer = {Pointer}"; } } public class WasmFunctionSection : WasmSection { public ulong EntryCount; public readonly List Types = new List(); internal WasmFunctionSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { EntryCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < EntryCount; num++) { Types.Add(file.BaseStream.ReadLEB128Unsigned()); } } } public class WasmGlobalEntry { public WasmGlobalType Type; public ConstantExpression Expression; public WasmGlobalEntry(WasmFile file) { Type = new WasmGlobalType(file); Expression = new ConstantExpression(file); } } public class WasmGlobalSection : WasmSection { public ulong GlobalCount; public readonly List Globals = new List(); internal WasmGlobalSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { GlobalCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < GlobalCount; num++) { Globals.Add(new WasmGlobalEntry(file)); } } } public class WasmGlobalType { public WasmTypeEnum Type; public byte Mutability; public WasmGlobalType(WasmFile readFrom) { Type = (WasmTypeEnum)readFrom.ReadByte(); Mutability = readFrom.ReadByte(); } } public class WasmImportEntry { public WasmString Module; public WasmString Field; public WasmExternalKind Kind; public ulong FunctionEntry; public WasmTableType? TableEntry; public WasmResizableLimits? MemoryEntry; public WasmGlobalType? GlobalEntry; public long StartOffset; public long EndOffset; public WasmImportEntry(WasmFile readFrom) { Module = new WasmString(readFrom); Field = new WasmString(readFrom); Kind = (WasmExternalKind)readFrom.ReadByte(); switch (Kind) { case WasmExternalKind.EXT_FUNCTION: FunctionEntry = readFrom.BaseStream.ReadLEB128Unsigned(); break; case WasmExternalKind.EXT_TABLE: TableEntry = new WasmTableType(readFrom); break; case WasmExternalKind.EXT_MEMORY: MemoryEntry = new WasmResizableLimits(readFrom); break; case WasmExternalKind.EXT_GLOBAL: GlobalEntry = new WasmGlobalType(readFrom); break; default: throw new ArgumentOutOfRangeException(); } } public override string ToString() { return $"{Module}.{Field} (Type {Kind})"; } } public class WasmImportSection : WasmSection { public ulong ImportCount; public readonly List Entries = new List(); internal WasmImportSection(WasmSectionId type, long pointer, ulong size, WasmFile readFrom) : base(type, pointer, size) { ImportCount = readFrom.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < ImportCount; num++) { Entries.Add(new WasmImportEntry(readFrom)); } LibLogger.VerboseNewline($"\t\tRead {Entries.Count} imports"); } } public class WasmLocalEntry { public ulong Count; public byte Type; public WasmLocalEntry(WasmFile file) { Count = file.BaseStream.ReadLEB128Unsigned(); Type = file.ReadByte(); } } public class WasmMemoryBlock : ClassReadingBinaryReader { internal byte[] Bytes; private static MemoryStream BuildStream(WasmFile file) { ulong num = ((from s in file.DataSection.DataEntries where s.VirtualOffset != ulong.MaxValue select s.VirtualOffset + s.Size).Max() + 4096) * 2; MemoryStream memoryStream = new MemoryStream(new byte[num], 0, (int)num, writable: true, publiclyVisible: true); foreach (WasmDataSegment dataEntry in file.DataSection.DataEntries) { memoryStream.Seek((long)dataEntry.VirtualOffset, SeekOrigin.Begin); memoryStream.Write(dataEntry.Data, 0, (int)dataEntry.Size); } return memoryStream; } public WasmMemoryBlock(WasmFile file) : base(BuildStream(file)) { is32Bit = true; Bytes = ((MemoryStream)BaseStream).ToArray(); } } public class WasmResizableLimits { public byte Flags; public ulong Initial; public ulong Max; public WasmResizableLimits(WasmFile readFrom) { Flags = readFrom.ReadByte(); Initial = readFrom.BaseStream.ReadLEB128Unsigned(); if (Flags == 1) { Max = readFrom.BaseStream.ReadLEB128Unsigned(); } } } public class WasmSection { public WasmSectionId Type; public long Pointer; public ulong Size; protected WasmSection(WasmSectionId type, long pointer, ulong size) { Type = type; Pointer = pointer; Size = size; } public static WasmSection MakeSection(WasmFile file) { long position = file.Position; WasmSectionId wasmSectionId = (WasmSectionId)file.ReadByte(); ulong num = file.BaseStream.ReadLEB128Unsigned(); LibLogger.VerboseNewline($"\t\tFound section of type {wasmSectionId} at 0x{position:X} with length 0x{num:X}"); num += (ulong)(file.Position - position); return wasmSectionId switch { WasmSectionId.SEC_TYPE => new WasmTypeSection(wasmSectionId, position, num, file), WasmSectionId.SEC_IMPORT => new WasmImportSection(wasmSectionId, position, num, file), WasmSectionId.SEC_DATA => new WasmDataSection(wasmSectionId, position, num, file), WasmSectionId.SEC_CODE => new WasmCodeSection(wasmSectionId, position, num, file), WasmSectionId.SEC_FUNCTION => new WasmFunctionSection(wasmSectionId, position, num, file), WasmSectionId.SEC_TABLE => new WasmTableSection(wasmSectionId, position, num, file), WasmSectionId.SEC_GLOBAL => new WasmGlobalSection(wasmSectionId, position, num, file), WasmSectionId.SEC_ELEMENT => new WasmElementSection(wasmSectionId, position, num, file), WasmSectionId.SEC_EXPORT => new WasmExportSection(wasmSectionId, position, num, file), _ => new WasmSection(wasmSectionId, position, num), }; } } public enum WasmSectionId : byte { SEC_CUSTOM, SEC_TYPE, SEC_IMPORT, SEC_FUNCTION, SEC_TABLE, SEC_LINEARMEMORY, SEC_GLOBAL, SEC_EXPORT, SEC_START, SEC_ELEMENT, SEC_CODE, SEC_DATA } public class WasmString { public ulong Size; public string Value; public WasmString(WasmFile readFrom) { Size = readFrom.BaseStream.ReadLEB128Unsigned(); Value = Encoding.UTF8.GetString(readFrom.ReadByteArrayAtRawAddress(readFrom.Position, (int)Size)); } public static implicit operator string(WasmString @this) { return @this.Value; } public override string ToString() { return this; } } public class WasmTableSection : WasmSection { public ulong TableCount; public readonly List Tables = new List(); internal WasmTableSection(WasmSectionId type, long pointer, ulong size, WasmFile file) : base(type, pointer, size) { TableCount = file.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < TableCount; num++) { Tables.Add(new WasmTableType(file)); } } } public class WasmTableType { public WasmTypeEnum ElemType; public WasmResizableLimits Limits; public WasmTableType(WasmFile readFrom) { ElemType = (WasmTypeEnum)readFrom.ReadByte(); Limits = new WasmResizableLimits(readFrom); } } public class WasmTypeEntry { public int Form; public ulong ParamCount; public WasmTypeEnum[] ParamTypes; public ulong ReturnCount; public WasmTypeEnum[] ReturnTypes; public WasmTypeEntry(WasmFile file) { Form = file.ReadByte(); ParamCount = file.BaseStream.ReadLEB128Unsigned(); ParamTypes = (from b in file.ReadByteArrayAtRawAddress(file.Position, (int)ParamCount) select (WasmTypeEnum)b).ToArray(); ReturnCount = file.BaseStream.ReadLEB128Unsigned(); ReturnTypes = (from b in file.ReadByteArrayAtRawAddress(file.Position, (int)ReturnCount) select (WasmTypeEnum)b).ToArray(); } } public enum WasmTypeEnum : byte { i32 = 127, i64 = 126, f32 = 125, f64 = 124, funcRef = 112, externRef = 111 } public class WasmTypeSection : WasmSection { public ulong TypeCount; public readonly List Types = new List(); internal WasmTypeSection(WasmSectionId type, long pointer, ulong size, WasmFile readFrom) : base(type, pointer, size) { TypeCount = readFrom.BaseStream.ReadLEB128Unsigned(); for (ulong num = 0uL; num < TypeCount; num++) { Types.Add(new WasmTypeEntry(readFrom)); } LibLogger.VerboseNewline($"\t\tRead {Types.Count} function types"); } } } namespace LibCpp2IL.Reflection { public class Il2CppFieldReflectionData { public Il2CppFieldDefinition Field; public FieldAttributes Attributes; public object? DefaultValue; public int IndexInParent; public int FieldOffset; public Il2CppFieldReflectionData(Il2CppFieldDefinition field, FieldAttributes attributes, object? defaultValue, int indexInParent, int fieldOffset) { Field = field; Attributes = attributes; DefaultValue = defaultValue; IndexInParent = indexInParent; FieldOffset = fieldOffset; } } public class Il2CppParameterReflectionData { public string ParameterName; public Il2CppType RawType; public Il2CppTypeReflectionData Type; public ParameterAttributes Attributes; public object? DefaultValue; public int ParameterIndex; public bool IsRefOrOut { get { if (!Attributes.HasFlag(ParameterAttributes.Out)) { return RawType.Byref == 1; } return true; } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); if (Attributes.HasFlag(ParameterAttributes.Out)) { stringBuilder.Append("out "); } else if (Attributes.HasFlag(ParameterAttributes.In)) { stringBuilder.Append("in "); } else if (RawType.Byref == 1) { stringBuilder.Append("ref "); } stringBuilder.Append(Type).Append(" "); if (string.IsNullOrEmpty(ParameterName)) { stringBuilder.Append("param_").Append(ParameterIndex); } else { stringBuilder.Append(ParameterName); } if (Attributes.HasFlag(ParameterAttributes.HasDefault)) { stringBuilder.Append(" = ").Append(DefaultValue ?? "null"); } return stringBuilder.ToString(); } } public class Il2CppTypeReflectionData { public Il2CppTypeDefinition? baseType; public Il2CppTypeReflectionData[] genericParams; public bool isType; public bool isGenericType; public bool isArray; public Il2CppTypeReflectionData? arrayType; public byte arrayRank; public string variableGenericParamName; public long variableGenericParamIndex; public bool isPointer; private string GetPtrSuffix() { if (!isPointer) { return ""; } return "*"; } public override string ToString() { if (isArray) { return arrayType?.ToString() + "[]".Repeat(arrayRank) + GetPtrSuffix(); } if (!isType) { return variableGenericParamName + GetPtrSuffix(); } if (!isGenericType) { return baseType.FullName + GetPtrSuffix(); } StringBuilder stringBuilder = new StringBuilder(baseType.FullName + "<"); Il2CppTypeReflectionData[] array = genericParams; foreach (Il2CppTypeReflectionData value in array) { stringBuilder.Append(value).Append(", "); } stringBuilder.Remove(stringBuilder.Length - 2, 2); stringBuilder.Append(">"); return stringBuilder?.ToString() + GetPtrSuffix(); } } public static class LibCpp2IlReflection { private static readonly ConcurrentDictionary<(string, string?), Il2CppTypeDefinition?> CachedTypes = new ConcurrentDictionary<(string, string), Il2CppTypeDefinition>(); private static readonly ConcurrentDictionary CachedTypesByFullName = new ConcurrentDictionary(); private static readonly Dictionary TypeIndices = new Dictionary(); private static readonly Dictionary MethodIndices = new Dictionary(); private static readonly Dictionary FieldIndices = new Dictionary(); private static readonly Dictionary PropertyIndices = new Dictionary(); private static readonly Dictionary PrimitiveTypeCache = new Dictionary(); public static readonly Dictionary PrimitiveTypeDefinitions = new Dictionary(); private static readonly Dictionary Il2CppTypeCache = new Dictionary(); internal static void ResetCaches() { CachedTypes.Clear(); CachedTypesByFullName.Clear(); lock (TypeIndices) { TypeIndices.Clear(); } MethodIndices.Clear(); FieldIndices.Clear(); PropertyIndices.Clear(); PrimitiveTypeCache.Clear(); PrimitiveTypeDefinitions.Clear(); Il2CppTypeCache.Clear(); } internal static void InitCaches() { Il2CppTypeEnum e2; for (e2 = Il2CppTypeEnum.IL2CPP_TYPE_VOID; e2 <= Il2CppTypeEnum.IL2CPP_TYPE_STRING; e2++) { PrimitiveTypeCache[e2] = LibCpp2IlMain.Binary.AllTypes.First((Il2CppType t) => t.Type == e2 && t.Byref == 0); } PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF] = LibCpp2IlMain.Binary.AllTypes.First((Il2CppType t) => t.Type == Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF && t.Byref == 0); Il2CppTypeEnum e; for (e = Il2CppTypeEnum.IL2CPP_TYPE_I; e <= Il2CppTypeEnum.IL2CPP_TYPE_U; e++) { PrimitiveTypeCache[e] = LibCpp2IlMain.Binary.AllTypes.First((Il2CppType t) => t.Type == e && t.Byref == 0); } PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_OBJECT] = LibCpp2IlMain.Binary.AllTypes.First((Il2CppType t) => t.Type == Il2CppTypeEnum.IL2CPP_TYPE_OBJECT && t.Byref == 0); for (int i = 0; i < LibCpp2IlMain.TheMetadata.typeDefs.Length; i++) { Il2CppTypeDefinition il2CppTypeDefinition = LibCpp2IlMain.TheMetadata.typeDefs[i]; TypeIndices[il2CppTypeDefinition] = i; Il2CppType il2CppType = LibCpp2IlMain.Binary.AllTypes[il2CppTypeDefinition.ByvalTypeIndex]; if (il2CppType.Type.IsIl2CppPrimitive()) { PrimitiveTypeDefinitions[il2CppType.Type] = il2CppTypeDefinition; } } Il2CppType[] allTypes = LibCpp2IlMain.Binary.AllTypes; foreach (Il2CppType il2CppType2 in allTypes) { Il2CppTypeEnum type = il2CppType2.Type; if ((type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS || type == Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) && il2CppType2.Byref == 0) { Il2CppTypeCache[il2CppType2.Data.ClassIndex] = il2CppType2; } } } public static Il2CppTypeDefinition? GetType(string name, string? @namespace = null) { string name2 = name; string namespace2 = @namespace; if (LibCpp2IlMain.TheMetadata == null) { return null; } (string, string) key = (name2, namespace2); if (!CachedTypes.ContainsKey(key)) { Il2CppTypeDefinition value = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault((Il2CppTypeDefinition td) => td.Name == name2 && (namespace2 == null || namespace2 == td.Namespace)); CachedTypes[key] = value; } return CachedTypes[key]; } public static Il2CppTypeDefinition? GetTypeByFullName(string fullName) { string fullName2 = fullName; if (LibCpp2IlMain.TheMetadata == null) { return null; } if (!CachedTypesByFullName.ContainsKey(fullName2)) { Il2CppTypeDefinition value = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault((Il2CppTypeDefinition td) => td.FullName == fullName2); CachedTypesByFullName[fullName2] = value; } return CachedTypesByFullName[fullName2]; } public static Il2CppTypeDefinition? GetTypeDefinitionByTypeIndex(int index) { if (LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null) { return null; } if (index >= LibCpp2IlMain.Binary.NumTypes || index < 0) { return null; } Il2CppType type = LibCpp2IlMain.Binary.GetType(index); return LibCpp2IlMain.TheMetadata.typeDefs[type.Data.ClassIndex]; } public static int GetTypeIndexFromType(Il2CppTypeDefinition typeDefinition) { if (LibCpp2IlMain.TheMetadata == null) { return -1; } return TypeIndices.GetOrDefault(typeDefinition, -1); } public static int GetMethodIndexFromMethod(Il2CppMethodDefinition methodDefinition) { if (LibCpp2IlMain.TheMetadata == null) { return -1; } if (MethodIndices.Count == 0) { lock (MethodIndices) { if (MethodIndices.Count == 0) { for (int i = 0; i < LibCpp2IlMain.TheMetadata.methodDefs.Length; i++) { Il2CppMethodDefinition key = LibCpp2IlMain.TheMetadata.methodDefs[i]; MethodIndices[key] = i; } } } } return MethodIndices.GetOrDefault(methodDefinition, -1); } public static int GetFieldIndexFromField(Il2CppFieldDefinition fieldDefinition) { if (LibCpp2IlMain.TheMetadata == null) { return -1; } if (FieldIndices.Count == 0) { lock (FieldIndices) { if (FieldIndices.Count == 0) { for (int i = 0; i < LibCpp2IlMain.TheMetadata.fieldDefs.Length; i++) { Il2CppFieldDefinition key = LibCpp2IlMain.TheMetadata.fieldDefs[i]; FieldIndices[key] = i; } } } } return FieldIndices[fieldDefinition]; } public static int GetPropertyIndexFromProperty(Il2CppPropertyDefinition propertyDefinition) { if (LibCpp2IlMain.TheMetadata == null) { return -1; } if (PropertyIndices.Count == 0) { lock (PropertyIndices) { if (PropertyIndices.Count == 0) { for (int i = 0; i < LibCpp2IlMain.TheMetadata.propertyDefs.Length; i++) { Il2CppPropertyDefinition key = LibCpp2IlMain.TheMetadata.propertyDefs[i]; PropertyIndices[key] = i; } } } } return PropertyIndices[propertyDefinition]; } public static Il2CppType? GetTypeFromDefinition(Il2CppTypeDefinition definition) { if (LibCpp2IlMain.Binary == null) { return null; } switch (definition.FullName) { case "System.SByte": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_I1]; case "System.Int16": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_I2]; case "System.Int32": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_I4]; case "System.Int64": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_I8]; case "System.Byte": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_U1]; case "System.UInt16": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_U2]; case "System.UInt32": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_U4]; case "System.UInt64": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_U8]; case "System.IntPtr": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_I]; case "System.UIntPtr": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_U]; case "System.Single": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_R4]; case "System.Double": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_R8]; case "System.Boolean": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN]; case "System.Char": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_CHAR]; case "System.String": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_STRING]; case "System.Void": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_VOID]; case "System.TypedReference": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF]; case "System.Object": return PrimitiveTypeCache[Il2CppTypeEnum.IL2CPP_TYPE_OBJECT]; default: { int typeIndex = definition.TypeIndex; if (Il2CppTypeCache.TryGetValue(typeIndex, out Il2CppType value)) { return value; } Il2CppType[] allTypes = LibCpp2IlMain.Binary.AllTypes; foreach (Il2CppType il2CppType in allTypes) { Il2CppTypeEnum type = il2CppType.Type; if ((type == Il2CppTypeEnum.IL2CPP_TYPE_CLASS || type == Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) && il2CppType.Data.ClassIndex == typeIndex && il2CppType.Byref == 0) { lock (Il2CppTypeCache) { Il2CppTypeCache[typeIndex] = il2CppType; return il2CppType; } } } return null; } } } } } namespace LibCpp2IL.PE { public class DataDirectory : ReadableClass { public uint VirtualAddress; public uint Size; public override void Read(ClassReadingBinaryReader reader) { VirtualAddress = reader.ReadUInt32(); Size = reader.ReadUInt32(); } } [Flags] public enum ElfProgramHeaderFlags : uint { PF_X = 1u, PF_W = 2u, PF_R = 4u } public class FileHeader : ReadableClass { public ushort Machine; public ushort NumberOfSections; public uint TimeDateStamp; public uint PointerToSymbolTable; public uint NumberOfSymbols; public ushort SizeOfOptionalHeader; public ushort Characteristics; public override void Read(ClassReadingBinaryReader reader) { Machine = reader.ReadUInt16(); NumberOfSections = reader.ReadUInt16(); TimeDateStamp = reader.ReadUInt32(); PointerToSymbolTable = reader.ReadUInt32(); NumberOfSymbols = reader.ReadUInt32(); SizeOfOptionalHeader = reader.ReadUInt16(); Characteristics = reader.ReadUInt16(); } } public class OptionalHeader : ReadableClass { public ushort Magic; public byte MajorLinkerVersion; public byte MinorLinkerVersion; public uint SizeOfCode; public uint SizeOfInitializedData; public uint SizeOfUninitializedData; public uint AddressOfEntryPoint; public uint BaseOfCode; public uint BaseOfData; public uint ImageBase; public uint SectionAlignment; public uint FileAlignment; public ushort MajorOperatingSystemVersion; public ushort MinorOperatingSystemVersion; public ushort MajorImageVersion; public ushort MinorImageVersion; public ushort MajorSubsystemVersion; public ushort MinorSubsystemVersion; public uint Win32VersionValue; public uint SizeOfImage; public uint SizeOfHeaders; public uint CheckSum; public ushort Subsystem; public ushort DllCharacteristics; public uint SizeOfStackReserve; public uint SizeOfStackCommit; public uint SizeOfHeapReserve; public uint SizeOfHeapCommit; public uint LoaderFlags; public uint NumberOfRvaAndSizes; public DataDirectory[] DataDirectory { get; set; } public override void Read(ClassReadingBinaryReader reader) { Magic = reader.ReadUInt16(); MajorLinkerVersion = reader.ReadByte(); MinorLinkerVersion = reader.ReadByte(); SizeOfCode = reader.ReadUInt32(); SizeOfInitializedData = reader.ReadUInt32(); SizeOfUninitializedData = reader.ReadUInt32(); AddressOfEntryPoint = reader.ReadUInt32(); BaseOfCode = reader.ReadUInt32(); BaseOfData = reader.ReadUInt32(); ImageBase = reader.ReadUInt32(); SectionAlignment = reader.ReadUInt32(); FileAlignment = reader.ReadUInt32(); MajorOperatingSystemVersion = reader.ReadUInt16(); MinorOperatingSystemVersion = reader.ReadUInt16(); MajorImageVersion = reader.ReadUInt16(); MinorImageVersion = reader.ReadUInt16(); MajorSubsystemVersion = reader.ReadUInt16(); MinorSubsystemVersion = reader.ReadUInt16(); Win32VersionValue = reader.ReadUInt32(); SizeOfImage = reader.ReadUInt32(); SizeOfHeaders = reader.ReadUInt32(); CheckSum = reader.ReadUInt32(); Subsystem = reader.ReadUInt16(); DllCharacteristics = reader.ReadUInt16(); SizeOfStackReserve = reader.ReadUInt32(); SizeOfStackCommit = reader.ReadUInt32(); SizeOfHeapReserve = reader.ReadUInt32(); SizeOfHeapCommit = reader.ReadUInt32(); LoaderFlags = reader.ReadUInt32(); NumberOfRvaAndSizes = reader.ReadUInt32(); DataDirectory = new DataDirectory[NumberOfRvaAndSizes]; for (int i = 0; i < NumberOfRvaAndSizes; i++) { DataDirectory[i] = reader.ReadReadableHereNoLock(); } } } public class OptionalHeader64 : ReadableClass { public ushort Magic; public byte MajorLinkerVersion; public byte MinorLinkerVersion; public uint SizeOfCode; public uint SizeOfInitializedData; public uint SizeOfUninitializedData; public uint AddressOfEntryPoint; public uint BaseOfCode; public ulong ImageBase; public uint SectionAlignment; public uint FileAlignment; public ushort MajorOperatingSystemVersion; public ushort MinorOperatingSystemVersion; public ushort MajorImageVersion; public ushort MinorImageVersion; public ushort MajorSubsystemVersion; public ushort MinorSubsystemVersion; public uint Win32VersionValue; public uint SizeOfImage; public uint SizeOfHeaders; public uint CheckSum; public ushort Subsystem; public ushort DllCharacteristics; public ulong SizeOfStackReserve; public ulong SizeOfStackCommit; public ulong SizeOfHeapReserve; public ulong SizeOfHeapCommit; public uint LoaderFlags; public uint NumberOfRvaAndSizes; public DataDirectory[] DataDirectory { get; set; } public override void Read(ClassReadingBinaryReader reader) { Magic = reader.ReadUInt16(); MajorLinkerVersion = reader.ReadByte(); MinorLinkerVersion = reader.ReadByte(); SizeOfCode = reader.ReadUInt32(); SizeOfInitializedData = reader.ReadUInt32(); SizeOfUninitializedData = reader.ReadUInt32(); AddressOfEntryPoint = reader.ReadUInt32(); BaseOfCode = reader.ReadUInt32(); ImageBase = reader.ReadUInt64(); SectionAlignment = reader.ReadUInt32(); FileAlignment = reader.ReadUInt32(); MajorOperatingSystemVersion = reader.ReadUInt16(); MinorOperatingSystemVersion = reader.ReadUInt16(); MajorImageVersion = reader.ReadUInt16(); MinorImageVersion = reader.ReadUInt16(); MajorSubsystemVersion = reader.ReadUInt16(); MinorSubsystemVersion = reader.ReadUInt16(); Win32VersionValue = reader.ReadUInt32(); SizeOfImage = reader.ReadUInt32(); SizeOfHeaders = reader.ReadUInt32(); CheckSum = reader.ReadUInt32(); Subsystem = reader.ReadUInt16(); DllCharacteristics = reader.ReadUInt16(); SizeOfStackReserve = reader.ReadUInt64(); SizeOfStackCommit = reader.ReadUInt64(); SizeOfHeapReserve = reader.ReadUInt64(); SizeOfHeapCommit = reader.ReadUInt64(); LoaderFlags = reader.ReadUInt32(); NumberOfRvaAndSizes = reader.ReadUInt32(); DataDirectory = new DataDirectory[NumberOfRvaAndSizes]; for (int i = 0; i < NumberOfRvaAndSizes; i++) { DataDirectory[i] = reader.ReadReadableHereNoLock(); } } } public sealed class PE : Il2CppBinary { internal readonly byte[] raw; internal readonly SectionHeader[] peSectionHeaders; internal readonly ulong peImageBase; private readonly OptionalHeader64? peOptionalHeader64; private readonly OptionalHeader? peOptionalHeader32; private uint[]? peExportedFunctionPointers; private uint[] peExportedFunctionNamePtrs; private ushort[] peExportedFunctionOrdinals; public override long RawLength => raw.Length; public PE(MemoryStream input) : base(input) { raw = input.GetBuffer(); LibLogger.Verbose("\tReading PE File Header..."); DateTime now = DateTime.Now; if (ReadUInt16() != 23117) { throw new FormatException("ERROR: Magic number mismatch."); } base.Position = 60L; base.Position = ReadUInt32(); if (ReadUInt32() != 17744) { throw new FormatException("ERROR: Invalid PE file signature"); } FileHeader fileHeader = ReadReadable(-1L); if (fileHeader.Machine == 332) { is32Bit = true; InstructionSetId = DefaultInstructionSets.X86_32; peOptionalHeader32 = ReadReadable(-1L); peImageBase = peOptionalHeader32.ImageBase; } else { if (fileHeader.Machine != 34404) { throw new NotSupportedException("ERROR: Unsupported machine."); } InstructionSetId = DefaultInstructionSets.X86_64; peOptionalHeader64 = ReadReadable(-1L); peImageBase = peOptionalHeader64.ImageBase; } peSectionHeaders = ReadReadableArrayAtRawAddr(-1L, fileHeader.NumberOfSections); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.VerboseNewline($"\t\tImage Base at 0x{peImageBase:X}"); LibLogger.VerboseNewline("\t\tDLL is " + (is32Bit ? "32" : "64") + "-bit"); } public override long MapVirtualAddressToRaw(ulong uiAddr, bool throwOnError = true) { if (uiAddr < peImageBase) { if (throwOnError) { throw new OverflowException($"Provided address, 0x{uiAddr:X}, was less than image base, 0x{peImageBase:X}"); } return -9223372036854774808L; } uint addr = (uint)(uiAddr - peImageBase); if (addr == 2147483648u) { if (throwOnError) { throw new OverflowException($"Provided address, 0x{uiAddr:X}, was less than image base, 0x{peImageBase:X}"); } return -9223372036854774808L; } SectionHeader sectionHeader = peSectionHeaders[peSectionHeaders.Length - 1]; if (addr > sectionHeader.VirtualAddress + sectionHeader.VirtualSize) { if (throwOnError) { throw new ArgumentOutOfRangeException("uiAddr", $"Provided address maps to image offset 0x{addr:X} which is outside the range of the file (last section ends at 0x{sectionHeader.VirtualAddress + sectionHeader.VirtualSize:X})"); } return -9223372036854774807L; } SectionHeader sectionHeader2 = peSectionHeaders.FirstOrDefault((SectionHeader x) => addr >= x.VirtualAddress && addr <= x.VirtualAddress + x.VirtualSize); if (sectionHeader2 == null) { return 0L; } return addr - (sectionHeader2.VirtualAddress - sectionHeader2.PointerToRawData); } public override ulong MapRawAddressToVirtual(uint offset) { SectionHeader sectionHeader = peSectionHeaders.First((SectionHeader x) => offset >= x.PointerToRawData && offset < x.PointerToRawData + x.SizeOfRawData); return peImageBase + sectionHeader.VirtualAddress + offset - sectionHeader.PointerToRawData; } [MemberNotNull("peExportedFunctionPointers")] private void LoadPeExportTable() { uint virtualAddress; if (is32Bit) { if (peOptionalHeader32?.DataDirectory == null || peOptionalHeader32.DataDirectory.Length == 0) { throw new InvalidDataException("Could not load 32-bit optional header or data directory, or data directory was empty!"); } virtualAddress = peOptionalHeader32.DataDirectory.First().VirtualAddress; } else { if (peOptionalHeader64?.DataDirectory == null || peOptionalHeader64.DataDirectory.Length == 0) { throw new InvalidDataException("Could not load 64-bit optional header or data directory, or data directory was empty!"); } virtualAddress = peOptionalHeader64.DataDirectory.First().VirtualAddress; } try { PeDirectoryEntryExport peDirectoryEntryExport = ReadReadableAtVirtualAddress(virtualAddress + peImageBase); peExportedFunctionPointers = ReadClassArrayAtVirtualAddress(peDirectoryEntryExport.RawAddressOfExportTable + peImageBase, peDirectoryEntryExport.NumberOfExports); peExportedFunctionNamePtrs = ReadClassArrayAtVirtualAddress(peDirectoryEntryExport.RawAddressOfExportNameTable + peImageBase, peDirectoryEntryExport.NumberOfExportNames); peExportedFunctionOrdinals = ReadClassArrayAtVirtualAddress(peDirectoryEntryExport.RawAddressOfExportOrdinalTable + peImageBase, peDirectoryEntryExport.NumberOfExportNames); } catch (EndOfStreamException) { LibLogger.WarnNewline($"PE does not appear to contain a valid export table! It would be apparently located at virt address 0x{virtualAddress + peImageBase:X}, raw 0x{MapVirtualAddressToRaw(virtualAddress + peImageBase):X}, but that's beyond the end of the binary. No exported functions will be accessible."); peExportedFunctionPointers = Array.Empty(); peExportedFunctionNamePtrs = Array.Empty(); peExportedFunctionOrdinals = Array.Empty(); } } public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { string toFind2 = toFind; if (peExportedFunctionPointers == null) { LoadPeExportTable(); } int num = Array.FindIndex(peExportedFunctionNamePtrs, delegate(uint stringAddress) { long offset = MapVirtualAddressToRaw(stringAddress + peImageBase); return ReadStringToNull(offset) == toFind2; }); if (num < 0) { return 0uL; } ushort num2 = peExportedFunctionOrdinals[num]; return peExportedFunctionPointers[num2] + peImageBase; } public override bool IsExportedFunction(ulong addr) { if (addr <= peImageBase) { return false; } ulong num = addr - peImageBase; if (num > uint.MaxValue) { return false; } if (peExportedFunctionPointers == null) { LoadPeExportTable(); } return Array.IndexOf(peExportedFunctionPointers, (uint)num) >= 0; } public override ulong GetRva(ulong pointer) { return pointer - peImageBase; } public override byte[] GetEntirePrimaryExecutableSection() { SectionHeader sectionHeader = peSectionHeaders.FirstOrDefault((SectionHeader s) => s.Name == ".text"); if (sectionHeader == null) { return Array.Empty(); } return GetRawBinaryContent().SubArray((int)sectionHeader.PointerToRawData, (int)sectionHeader.SizeOfRawData); } public override ulong GetVirtualAddressOfPrimaryExecutableSection() { return (peSectionHeaders.FirstOrDefault((SectionHeader s) => s.Name == ".text")?.VirtualAddress + peImageBase).GetValueOrDefault(); } public override byte GetByteAtRawAddress(ulong addr) { return raw[addr]; } public override byte[] GetRawBinaryContent() { return raw; } } public class PeDirectoryEntryExport : ReadableClass { public uint Characteristics; public uint TimeDataStamp; public ushort MajorVersion; public ushort MinorVersion; public uint RawAddressOfAssemblyName; public uint RawAddressOfAssemblyBase; public uint NumberOfExports; public uint NumberOfExportNames; public uint RawAddressOfExportTable; public uint RawAddressOfExportNameTable; public uint RawAddressOfExportOrdinalTable; public override void Read(ClassReadingBinaryReader reader) { Characteristics = reader.ReadUInt32(); TimeDataStamp = reader.ReadUInt32(); MajorVersion = reader.ReadUInt16(); MinorVersion = reader.ReadUInt16(); RawAddressOfAssemblyName = reader.ReadUInt32(); RawAddressOfAssemblyBase = reader.ReadUInt32(); NumberOfExports = reader.ReadUInt32(); NumberOfExportNames = reader.ReadUInt32(); RawAddressOfExportTable = reader.ReadUInt32(); RawAddressOfExportNameTable = reader.ReadUInt32(); RawAddressOfExportOrdinalTable = reader.ReadUInt32(); } } public class SectionHeader : ReadableClass { public string Name; public uint VirtualSize; public uint VirtualAddress; public uint SizeOfRawData; public uint PointerToRawData; public uint PointerToRelocations; public uint PointerToLinenumbers; public ushort NumberOfRelocations; public ushort NumberOfLinenumbers; public uint Characteristics; public override void Read(ClassReadingBinaryReader reader) { Name = Encoding.UTF8.GetString(reader.ReadBytes(8)).TrimEnd('\0'); VirtualSize = reader.ReadUInt32(); VirtualAddress = reader.ReadUInt32(); SizeOfRawData = reader.ReadUInt32(); PointerToRawData = reader.ReadUInt32(); PointerToRelocations = reader.ReadUInt32(); PointerToLinenumbers = reader.ReadUInt32(); NumberOfRelocations = reader.ReadUInt16(); NumberOfLinenumbers = reader.ReadUInt16(); Characteristics = reader.ReadUInt32(); } } } namespace LibCpp2IL.NintendoSwitch { public sealed class NsoFile : Il2CppBinary { private const ulong NsoGlobalOffset = 0uL; private readonly byte[] _raw; private readonly NsoHeader _header; private readonly bool _isTextCompressed; private readonly bool _isRoDataCompressed; private readonly bool _isDataCompressed; private NsoModHeader _modHeader; private ElfDynamicSymbol64[] _symbolTable; private List segments = new List(); private List dynamicEntries = new List(); private bool IsCompressed { get { if (!_isTextCompressed && !_isRoDataCompressed) { return _isDataCompressed; } return true; } } public override long RawLength => _raw.Length; public NsoFile(MemoryStream input) : base(input) { _raw = input.GetBuffer(); is32Bit = false; InstructionSetId = DefaultInstructionSets.ARM_V8; LibLogger.VerboseNewline("\tReading NSO Early Header..."); _header = new NsoHeader { Magic = ReadUInt32(), Version = ReadUInt32(), Reserved = ReadUInt32(), Flags = ReadUInt32() }; if (_header.Magic != 810505038) { throw new Exception($"NSO file should have a magic number of 0x304F534E, got 0x{_header.Magic:X}"); } LibLogger.VerboseNewline($"\tOK. Magic number is 0x{_header.Magic:X}, version is {_header.Version}."); _isTextCompressed = (_header.Flags & 1) != 0; _isRoDataCompressed = (_header.Flags & 2) != 0; _isDataCompressed = (_header.Flags & 4) != 0; LibLogger.VerboseNewline($"\tCompression flags: text: {_isTextCompressed}, rodata: {_isRoDataCompressed}, data: {_isDataCompressed}."); _header.TextSegment = new NsoSegmentHeader { FileOffset = ReadUInt32(), MemoryOffset = ReadUInt32(), DecompressedSize = ReadUInt32() }; segments.Add(_header.TextSegment); LibLogger.VerboseNewline("\tRead text segment header ok. Reading rodata segment header..."); _header.ModuleOffset = ReadUInt32(); _header.RoDataSegment = new NsoSegmentHeader { FileOffset = ReadUInt32(), MemoryOffset = ReadUInt32(), DecompressedSize = ReadUInt32() }; segments.Add(_header.RoDataSegment); LibLogger.VerboseNewline("\tRead rodata segment header OK. Reading data segment header..."); _header.ModuleFileSize = ReadUInt32(); _header.DataSegment = new NsoSegmentHeader { FileOffset = ReadUInt32(), MemoryOffset = ReadUInt32(), DecompressedSize = ReadUInt32() }; segments.Add(_header.DataSegment); LibLogger.VerboseNewline("\tRead data segment OK. Reading post-segment fields..."); _header.BssSize = ReadUInt32(); _header.DigestBuildId = ReadBytes(32); _header.TextCompressedSize = ReadUInt32(); _header.RoDataCompressedSize = ReadUInt32(); _header.DataCompressedSize = ReadUInt32(); _header.NsoHeaderReserved = ReadBytes(28); LibLogger.VerboseNewline("\tRead post-segment fields OK. Reading Dynamic section and Api Info offsets..."); _header.ApiInfo = new NsoRelativeExtent { RegionRoDataOffset = ReadUInt32(), RegionSize = ReadUInt32() }; _header.DynStr = new NsoRelativeExtent { RegionRoDataOffset = ReadUInt32(), RegionSize = ReadUInt32() }; _header.DynSym = new NsoRelativeExtent { RegionRoDataOffset = ReadUInt32(), RegionSize = ReadUInt32() }; LibLogger.VerboseNewline("\tRead offsets OK. Reading hashes..."); _header.TextHash = ReadBytes(32); _header.RoDataHash = ReadBytes(32); _header.DataHash = ReadBytes(32); LibLogger.VerboseNewline("\tRead hashes ok."); if (!IsCompressed) { ReadModHeader(); ReadDynamicSection(); ReadSymbolTable(); ApplyRelocations(); } LibLogger.VerboseNewline("\tNSO Read completed OK."); } private void ReadModHeader() { LibLogger.VerboseNewline("\tNSO is decompressed. Reading MOD segment header..."); _modHeader = new NsoModHeader(); base.Position = _header.TextSegment.FileOffset + 4; _modHeader.ModOffset = ReadUInt32(); base.Position = _header.TextSegment.FileOffset + _modHeader.ModOffset + 4; _modHeader.DynamicOffset = ReadUInt32() + _modHeader.ModOffset; _modHeader.BssStart = ReadUInt32(); _modHeader.BssEnd = ReadUInt32(); _modHeader.BssSegment = new NsoSegmentHeader { FileOffset = _modHeader.BssStart, MemoryOffset = _modHeader.BssStart, DecompressedSize = _modHeader.BssEnd - _modHeader.BssStart }; _modHeader.EhFrameHdrStart = ReadUInt32(); _modHeader.EhFrameHdrEnd = ReadUInt32(); } private void ReadDynamicSection() { LibLogger.VerboseNewline("\tReading NSO Dynamic section..."); base.Position = MapVirtualAddressToRaw(_modHeader.DynamicOffset); uint num = (_header.DataSegment.MemoryOffset + _header.DataSegment.DecompressedSize - _modHeader.DynamicOffset) / 16; for (int i = 0; i < num; i++) { ElfDynamicEntry elfDynamicEntry = ReadReadable(-1L); if (elfDynamicEntry.Tag != ElfDynamicType.DT_NULL) { dynamicEntries.Add(elfDynamicEntry); continue; } break; } } private void ReadSymbolTable() { LibLogger.Verbose("\tReading NSO symbol table..."); ElfDynamicEntry dynamicEntry = GetDynamicEntry(ElfDynamicType.DT_HASH); if (dynamicEntry == null) { LibLogger.WarnNewline("\tNo DT_HASH found in NSO, symbols will not be resolved"); return; } base.Position = MapVirtualAddressToRaw(dynamicEntry.Value); ReadUInt32(); uint num = ReadUInt32(); ElfDynamicEntry dynamicEntry2 = GetDynamicEntry(ElfDynamicType.DT_SYMTAB); if (dynamicEntry2 == null) { LibLogger.WarnNewline("\tNo DT_SYMTAB found in NSO, symbols will not be resolved"); return; } _symbolTable = ReadReadableArrayAtVirtualAddress((ulong)MapVirtualAddressToRaw(dynamicEntry2.Value), num); LibLogger.VerboseNewline($"Got {_symbolTable.Length} symbols"); } private void ApplyRelocations() { ElfRelaEntry[] array; try { ElfDynamicEntry elfDynamicEntry = GetDynamicEntry(ElfDynamicType.DT_RELA) ?? throw new Exception("No relocations found in NSO"); ElfDynamicEntry elfDynamicEntry2 = GetDynamicEntry(ElfDynamicType.DT_RELASZ) ?? throw new Exception("No relocation size entry found in NSO"); array = ReadReadableArrayAtVirtualAddress(elfDynamicEntry.Value, (long)(elfDynamicEntry2.Value / 24)); } catch { return; } LibLogger.VerboseNewline($"\tApplying {array.Length} relocations from DT_RELA..."); ElfRelaEntry[] array2 = array; foreach (ElfRelaEntry elfRelaEntry in array2) { switch (elfRelaEntry.Type) { case ElfRelocationType.R_AARCH64_ABS64: case ElfRelocationType.R_AARCH64_GLOB_DAT: { ElfDynamicSymbol64 elfDynamicSymbol = _symbolTable[elfRelaEntry.Symbol]; WriteWord((int)MapVirtualAddressToRaw(elfRelaEntry.Offset), elfDynamicSymbol.Value + elfRelaEntry.Addend); break; } case ElfRelocationType.R_AARCH64_RELATIVE: WriteWord((int)MapVirtualAddressToRaw(elfRelaEntry.Offset), elfRelaEntry.Addend); break; default: LibLogger.WarnNewline($"Unknown relocation type {elfRelaEntry.Type}"); break; } } } public ElfDynamicEntry? GetDynamicEntry(ElfDynamicType tag) { return dynamicEntries.Find((ElfDynamicEntry x) => x.Tag == tag); } public NsoFile Decompress() { if (!IsCompressed) { return this; } LibLogger.InfoNewline("\tDecompressing NSO file..."); MemoryStream memoryStream = new MemoryStream(); BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(_header.Magic); binaryWriter.Write(_header.Version); binaryWriter.Write(_header.Reserved); binaryWriter.Write(0); binaryWriter.Write(_header.TextSegment.FileOffset); binaryWriter.Write(_header.TextSegment.MemoryOffset); binaryWriter.Write(_header.TextSegment.DecompressedSize); binaryWriter.Write(_header.ModuleOffset); uint num = _header.TextSegment.FileOffset + _header.TextSegment.DecompressedSize; binaryWriter.Write(num); binaryWriter.Write(_header.RoDataSegment.MemoryOffset); binaryWriter.Write(_header.RoDataSegment.DecompressedSize); binaryWriter.Write(_header.ModuleFileSize); binaryWriter.Write(num + _header.RoDataSegment.DecompressedSize); binaryWriter.Write(_header.DataSegment.MemoryOffset); binaryWriter.Write(_header.DataSegment.DecompressedSize); binaryWriter.Write(_header.BssSize); binaryWriter.Write(_header.DigestBuildId); binaryWriter.Write(_header.TextCompressedSize); binaryWriter.Write(_header.RoDataCompressedSize); binaryWriter.Write(_header.DataCompressedSize); binaryWriter.Write(_header.NsoHeaderReserved); binaryWriter.Write(_header.ApiInfo.RegionRoDataOffset); binaryWriter.Write(_header.ApiInfo.RegionSize); binaryWriter.Write(_header.DynStr.RegionRoDataOffset); binaryWriter.Write(_header.DynStr.RegionSize); binaryWriter.Write(_header.DynSym.RegionRoDataOffset); binaryWriter.Write(_header.DynSym.RegionSize); binaryWriter.Write(_header.TextHash); binaryWriter.Write(_header.RoDataHash); binaryWriter.Write(_header.DataHash); binaryWriter.BaseStream.Position = _header.TextSegment.FileOffset; base.Position = _header.TextSegment.FileOffset; byte[] buffer = ReadBytes((int)_header.TextCompressedSize); if (_isTextCompressed) { byte[] array = new byte[_header.TextSegment.DecompressedSize]; using (Lz4DecodeStream lz4DecodeStream = new Lz4DecodeStream(new MemoryStream(buffer))) { lz4DecodeStream.Read(array, 0, array.Length); } binaryWriter.Write(array); } else { binaryWriter.Write(buffer); } byte[] buffer2 = ReadBytes((int)_header.RoDataCompressedSize); if (_isRoDataCompressed) { byte[] array2 = new byte[_header.RoDataSegment.DecompressedSize]; using (Lz4DecodeStream lz4DecodeStream2 = new Lz4DecodeStream(new MemoryStream(buffer2))) { lz4DecodeStream2.Read(array2, 0, array2.Length); } binaryWriter.Write(array2); } else { binaryWriter.Write(buffer2); } byte[] buffer3 = ReadBytes((int)_header.DataCompressedSize); if (_isDataCompressed) { byte[] array3 = new byte[_header.DataSegment.DecompressedSize]; using (Lz4DecodeStream lz4DecodeStream3 = new Lz4DecodeStream(new MemoryStream(buffer3))) { lz4DecodeStream3.Read(array3, 0, array3.Length); } binaryWriter.Write(array3); } else { binaryWriter.Write(buffer3); } binaryWriter.Flush(); memoryStream.Position = 0L; return new NsoFile(memoryStream); } public override byte GetByteAtRawAddress(ulong addr) { return _raw[addr]; } public override long MapVirtualAddressToRaw(ulong addr, bool throwOnError = true) { NsoSegmentHeader nsoSegmentHeader = segments.FirstOrDefault((NsoSegmentHeader x) => addr >= x.MemoryOffset && addr <= x.MemoryOffset + x.DecompressedSize); if (nsoSegmentHeader == null) { if (throwOnError) { throw new InvalidOperationException($"NSO: Address 0x{addr:X} is not present in any of the segments. Known segment ends are (hex) {string.Join(", ", segments.Select((NsoSegmentHeader s) => (s.MemoryOffset + s.DecompressedSize).ToString("X")))}"); } return -9223372036854774808L; } return (long)(addr - nsoSegmentHeader.MemoryOffset + nsoSegmentHeader.FileOffset); } public override ulong MapRawAddressToVirtual(uint offset) { NsoSegmentHeader nsoSegmentHeader = segments.FirstOrDefault((NsoSegmentHeader x) => offset >= x.FileOffset && offset <= x.FileOffset + x.DecompressedSize); if (nsoSegmentHeader == null) { return 0uL; } return (ulong)(offset - nsoSegmentHeader.FileOffset) + (ulong)nsoSegmentHeader.MemoryOffset; } public override ulong GetRva(ulong pointer) { return pointer; } public override byte[] GetRawBinaryContent() { return _raw; } public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { return 0uL; } public override byte[] GetEntirePrimaryExecutableSection() { return _raw.Skip((int)_header.TextSegment.FileOffset).Take((int)_header.TextSegment.DecompressedSize).ToArray(); } public override ulong GetVirtualAddressOfPrimaryExecutableSection() { return _header.TextSegment.MemoryOffset; } } public class NsoHeader { public uint Magic; public uint Version; public uint Reserved; public uint Flags; public NsoSegmentHeader TextSegment; public uint ModuleOffset; public NsoSegmentHeader RoDataSegment; public uint ModuleFileSize; public NsoSegmentHeader DataSegment; public uint BssSize; public byte[] DigestBuildId = Array.Empty(); public uint TextCompressedSize; public uint RoDataCompressedSize; public uint DataCompressedSize; public byte[] NsoHeaderReserved = Array.Empty(); public NsoRelativeExtent ApiInfo; public NsoRelativeExtent DynStr; public NsoRelativeExtent DynSym; public byte[] TextHash = Array.Empty(); public byte[] RoDataHash = Array.Empty(); public byte[] DataHash = Array.Empty(); } public class NsoModHeader { public uint ModOffset; public uint DynamicOffset; public uint BssStart; public uint BssEnd; public uint EhFrameHdrStart; public uint EhFrameHdrEnd; public NsoSegmentHeader BssSegment; } public class NsoRelativeExtent { public uint RegionRoDataOffset; public uint RegionSize; } public class NsoSegmentHeader { public uint FileOffset; public uint MemoryOffset; public uint DecompressedSize; } } namespace LibCpp2IL.Metadata { public class Il2CppAssemblyDefinition : ReadableClass { public int ImageIndex; [Version(Min = 24.1f)] public uint Token; [Version(Max = 24f)] public int CustomAttributeIndex; public int ReferencedAssemblyStart; public int ReferencedAssemblyCount; public Il2CppAssemblyNameDefinition AssemblyName; public Il2CppImageDefinition Image => LibCpp2IlMain.TheMetadata.imageDefinitions[ImageIndex]; public Il2CppAssemblyDefinition[] ReferencedAssemblies { get { if (ReferencedAssemblyStart >= 0) { return (from idx in LibCpp2IlMain.TheMetadata.referencedAssemblies.SubArray(ReferencedAssemblyStart, ReferencedAssemblyCount) select LibCpp2IlMain.TheMetadata.AssemblyDefinitions[idx]).ToArray(); } return Array.Empty(); } } public override string ToString() { return AssemblyName.ToString(); } public override void Read(ClassReadingBinaryReader reader) { ImageIndex = reader.ReadInt32(); if (IsAtLeast(24.1f)) { Token = reader.ReadUInt32(); } if (IsAtMost(24f)) { CustomAttributeIndex = reader.ReadInt32(); } ReferencedAssemblyStart = reader.ReadInt32(); ReferencedAssemblyCount = reader.ReadInt32(); AssemblyName = reader.ReadReadableHereNoLock(); } } public class Il2CppAssemblyNameDefinition : ReadableClass { public int nameIndex; public int cultureIndex; [Version(Max = 24.1f)] [Version(Min = 24.2f, Max = 24.3f)] public int hashValueIndex; public int publicKeyIndex; public uint hash_alg; public int hash_len; public uint flags; public int major; public int minor; public int build; public int revision; public ulong publicKeyToken; public string Name => LibCpp2IlMain.TheMetadata.GetStringFromIndex(nameIndex); public string Culture => LibCpp2IlMain.TheMetadata.GetStringFromIndex(cultureIndex); public string PublicKey => LibCpp2IlMain.TheMetadata.GetStringFromIndex(publicKeyIndex); public string HashValue { get { if (!(LibCpp2IlMain.MetadataVersion > 24.3f)) { return LibCpp2IlMain.TheMetadata.GetStringFromIndex(hashValueIndex); } return "NULL"; } } public override string ToString() { string value = string.Join("-", from b in BitConverter.GetBytes(publicKeyToken) select b.ToString("X2")); return $"{Name}, Version={major}.{minor}.{build}.{revision}, PublicKeyToken={value}"; } public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); cultureIndex = reader.ReadInt32(); if (IsAtMost(24.3f) && IsNot(24.15f)) { hashValueIndex = reader.ReadInt32(); } publicKeyIndex = reader.ReadInt32(); hash_alg = reader.ReadUInt32(); hash_len = reader.ReadInt32(); flags = reader.ReadUInt32(); major = reader.ReadInt32(); minor = reader.ReadInt32(); build = reader.ReadInt32(); revision = reader.ReadInt32(); publicKeyToken = reader.ReadUInt64(); } } public class Il2CppCustomAttributeDataRange : ReadableClass, IIl2CppTokenProvider { public uint token; public uint startOffset; public uint Token => token; public override void Read(ClassReadingBinaryReader reader) { token = reader.ReadUInt32(); startOffset = reader.ReadUInt32(); } } public class Il2CppCustomAttributeTypeRange : ReadableClass, IIl2CppTokenProvider { [Version(Min = 24.1f)] public uint token; public int start; [Version(Max = 27.9f)] public int count; public uint Token => token; public override void Read(ClassReadingBinaryReader reader) { if (IsAtLeast(24.1f)) { token = reader.ReadUInt32(); } start = reader.ReadInt32(); if (IsLessThan(29f)) { count = reader.ReadInt32(); } } } public class Il2CppEventDefinition : ReadableClass { public int nameIndex; public int typeIndex; public int add; public int remove; public int raise; [Version(Max = 24f)] public int customAttributeIndex; public uint token; [NonSerialized] private Il2CppTypeDefinition? _type; public Il2CppTypeDefinition? DeclaringType { get { if (_type != null) { return _type; } if (LibCpp2IlMain.TheMetadata == null) { return null; } _type = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault((Il2CppTypeDefinition t) => t.Events.Contains(this)); return _type; } internal set { _type = value; } } public string? Name { get; private set; } public Il2CppType? RawType => LibCpp2IlMain.Binary?.GetType(typeIndex); public Il2CppTypeReflectionData? EventType { get { if (LibCpp2IlMain.Binary != null) { return LibCpp2ILUtils.GetTypeReflectionData(RawType); } return null; } } public EventAttributes EventAttributes => (EventAttributes)RawType.Attrs; public Il2CppMethodDefinition? Adder { get { if (LibCpp2IlMain.TheMetadata != null && add >= 0 && DeclaringType != null) { return LibCpp2IlMain.TheMetadata.methodDefs[DeclaringType.FirstMethodIdx + add]; } return null; } } public Il2CppMethodDefinition? Remover { get { if (LibCpp2IlMain.TheMetadata != null && remove >= 0 && DeclaringType != null) { return LibCpp2IlMain.TheMetadata.methodDefs[DeclaringType.FirstMethodIdx + remove]; } return null; } } public Il2CppMethodDefinition? Invoker { get { if (LibCpp2IlMain.TheMetadata != null && raise >= 0 && DeclaringType != null) { return LibCpp2IlMain.TheMetadata.methodDefs[DeclaringType.FirstMethodIdx + raise]; } return null; } } public bool IsStatic { get { if (Adder != null) { return Adder.IsStatic; } if (Remover != null) { return Remover.IsStatic; } return Invoker.IsStatic; } } public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); long position = reader.Position; Name = ((Il2CppMetadata)reader).ReadStringFromIndexNoReadLock(nameIndex); reader.Position = position; typeIndex = reader.ReadInt32(); add = reader.ReadInt32(); remove = reader.ReadInt32(); raise = reader.ReadInt32(); if (IsAtMost(24f)) { customAttributeIndex = reader.ReadInt32(); } token = reader.ReadUInt32(); } } public class Il2CppFieldDefaultValue : ReadableClass { public int fieldIndex; public int typeIndex; public int dataIndex; public object? Value { get { if (dataIndex > 0) { return LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex); } return null; } } public override void Read(ClassReadingBinaryReader reader) { fieldIndex = reader.ReadInt32(); typeIndex = reader.ReadInt32(); dataIndex = reader.ReadInt32(); } } public class Il2CppFieldDefinition : ReadableClass { public int nameIndex; public int typeIndex; [Version(Max = 24f)] public int customAttributeIndex; public uint token; public string? Name { get; private set; } public Il2CppType? RawFieldType => LibCpp2IlMain.Binary?.GetType(typeIndex); public Il2CppTypeReflectionData? FieldType { get { if (RawFieldType != null) { return LibCpp2ILUtils.GetTypeReflectionData(RawFieldType); } return null; } } public int FieldIndex => LibCpp2IlReflection.GetFieldIndexFromField(this); public Il2CppFieldDefaultValue? DefaultValue => LibCpp2IlMain.TheMetadata?.GetFieldDefaultValue(this); public byte[] StaticArrayInitialValue { get { Il2CppTypeReflectionData fieldType = FieldType; if (fieldType == null || fieldType.isArray || fieldType.isPointer || !fieldType.isType || fieldType.isGenericType) { return Array.Empty(); } string? name = FieldType.baseType.Name; if (name == null || !name.StartsWith("__StaticArrayInitTypeSize=")) { return Array.Empty(); } int count = int.Parse(FieldType.baseType.Name.Replace("__StaticArrayInitTypeSize=", "")); int item = LibCpp2IlMain.TheMetadata.GetFieldDefaultValue(FieldIndex).ptr; int defaultValueFromIndex = LibCpp2IlMain.TheMetadata.GetDefaultValueFromIndex(item); if (defaultValueFromIndex <= 0) { return Array.Empty(); } return LibCpp2IlMain.TheMetadata.ReadByteArrayAtRawAddress(defaultValueFromIndex, count); } } public override string? ToString() { if (LibCpp2IlMain.TheMetadata == null) { return base.ToString(); } return $"Il2CppFieldDefinition[Name={Name}, FieldType={FieldType}]"; } public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); long position = reader.Position; Name = ((Il2CppMetadata)reader).ReadStringFromIndexNoReadLock(nameIndex); reader.Position = position; typeIndex = reader.ReadInt32(); if (IsAtMost(24f)) { customAttributeIndex = reader.ReadInt32(); } token = reader.ReadUInt32(); } } public class Il2CppFieldRef : ReadableClass { public int typeIndex; public int fieldIndex; public Il2CppType? DeclaringType => LibCpp2IlMain.Binary?.GetType(typeIndex); public Il2CppTypeDefinition? DeclaringTypeDefinition { get { Il2CppMetadata? theMetadata = LibCpp2IlMain.TheMetadata; if (theMetadata == null) { return null; } return theMetadata.typeDefs[DeclaringType.Data.ClassIndex]; } } public Il2CppFieldDefinition? FieldDefinition { get { Il2CppMetadata? theMetadata = LibCpp2IlMain.TheMetadata; if (theMetadata == null) { return null; } return theMetadata.fieldDefs[DeclaringTypeDefinition.FirstFieldIdx + fieldIndex]; } } public override void Read(ClassReadingBinaryReader reader) { typeIndex = reader.ReadInt32(); fieldIndex = reader.ReadInt32(); } } public class Il2CppGenericContainer : ReadableClass { public int ownerIndex; public int genericParameterCount; public int isGenericMethod; public int genericParameterStart; public IEnumerable GenericParameters { get { if (genericParameterCount != 0) { int end = genericParameterStart + genericParameterCount; for (int i = genericParameterStart; i < end; i++) { Il2CppGenericParameter il2CppGenericParameter = LibCpp2IlMain.TheMetadata.genericParameters[i]; il2CppGenericParameter.Index = i; yield return il2CppGenericParameter; } } } } public override void Read(ClassReadingBinaryReader reader) { ownerIndex = reader.ReadInt32(); genericParameterCount = reader.ReadInt32(); isGenericMethod = reader.ReadInt32(); genericParameterStart = reader.ReadInt32(); } } public class Il2CppGenericParameter : ReadableClass { public int ownerIndex; public int nameIndex; public short constraintsStart; public short constraintsCount; public ushort genericParameterIndexInOwner; public ushort flags; public string? Name => LibCpp2IlMain.TheMetadata?.GetStringFromIndex(nameIndex); public Il2CppType[]? ConstraintTypes { get { if (constraintsCount != 0) { return LibCpp2IlMain.TheMetadata?.constraintIndices.Skip(constraintsStart).Take(constraintsCount).Select(LibCpp2IlMain.Binary.GetType) .ToArray(); } return Array.Empty(); } } public int Index { get; internal set; } public override void Read(ClassReadingBinaryReader reader) { ownerIndex = reader.ReadInt32(); nameIndex = reader.ReadInt32(); constraintsStart = reader.ReadInt16(); constraintsCount = reader.ReadInt16(); genericParameterIndexInOwner = reader.ReadUInt16(); flags = reader.ReadUInt16(); } } public class Il2CppGlobalMetadataHeader : ReadableClass { public uint magicNumber; public int version; public int stringLiteralOffset; public int stringLiteralCount; public int stringLiteralDataOffset; public int stringLiteralDataCount; public int stringOffset; public int stringCount; public int eventsOffset; public int eventsCount; public int propertiesOffset; public int propertiesCount; public int methodsOffset; public int methodsCount; public int parameterDefaultValuesOffset; public int parameterDefaultValuesCount; public int fieldDefaultValuesOffset; public int fieldDefaultValuesCount; public int fieldAndParameterDefaultValueDataOffset; public int fieldAndParameterDefaultValueDataCount; public int fieldMarshaledSizesOffset; public int fieldMarshaledSizesCount; public int parametersOffset; public int parametersCount; public int fieldsOffset; public int fieldsCount; public int genericParametersOffset; public int genericParametersCount; public int genericParameterConstraintsOffset; public int genericParameterConstraintsCount; public int genericContainersOffset; public int genericContainersCount; public int nestedTypesOffset; public int nestedTypesCount; public int interfacesOffset; public int interfacesCount; public int vtableMethodsOffset; public int vtableMethodsCount; public int interfaceOffsetsOffset; public int interfaceOffsetsCount; public int typeDefinitionsOffset; public int typeDefinitionsCount; [Version(Max = 24.15f)] public int rgctxEntriesOffset; [Version(Max = 24.15f)] public int rgctxEntriesCount; public int imagesOffset; public int imagesCount; public int assembliesOffset; public int assembliesCount; [Version(Max = 24.5f)] public int metadataUsageListsOffset; [Version(Max = 24.5f)] public int metadataUsageListsCount; [Version(Max = 24.5f)] public int metadataUsagePairsOffset; [Version(Max = 24.5f)] public int metadataUsagePairsCount; public int fieldRefsOffset; public int fieldRefsCount; public int referencedAssembliesOffset; public int referencedAssembliesCount; [Version(Max = 27.9f)] public int attributesInfoOffset; [Version(Max = 27.9f)] public int attributesInfoCount; [Version(Max = 27.9f)] public int attributeTypesOffset; [Version(Max = 27.9f)] public int attributeTypesCount; [Version(Min = 27.9f)] public int attributeDataOffset; [Version(Min = 27.9f)] public int attributeDataCount; [Version(Min = 27.9f)] public int attributeDataRangeOffset; [Version(Min = 27.9f)] public int attributeDataRangeCount; public int unresolvedVirtualCallParameterTypesOffset; public int unresolvedVirtualCallParameterTypesCount; public int unresolvedVirtualCallParameterRangesOffset; public int unresolvedVirtualCallParameterRangesCount; [Version(Min = 23f)] public int windowsRuntimeTypeNamesOffset; [Version(Min = 23f)] public int windowsRuntimeTypeNamesSize; [Version(Min = 27f)] public int windowsRuntimeStringsOffset; [Version(Min = 27f)] public int windowsRuntimeStringsSize; [Version(Min = 24f)] public int exportedTypeDefinitionsOffset; [Version(Min = 24f)] public int exportedTypeDefinitionsCount; public override void Read(ClassReadingBinaryReader reader) { magicNumber = reader.ReadUInt32(); version = reader.ReadInt32(); stringLiteralOffset = reader.ReadInt32(); stringLiteralCount = reader.ReadInt32(); stringLiteralDataOffset = reader.ReadInt32(); stringLiteralDataCount = reader.ReadInt32(); stringOffset = reader.ReadInt32(); stringCount = reader.ReadInt32(); eventsOffset = reader.ReadInt32(); eventsCount = reader.ReadInt32(); propertiesOffset = reader.ReadInt32(); propertiesCount = reader.ReadInt32(); methodsOffset = reader.ReadInt32(); methodsCount = reader.ReadInt32(); parameterDefaultValuesOffset = reader.ReadInt32(); parameterDefaultValuesCount = reader.ReadInt32(); fieldDefaultValuesOffset = reader.ReadInt32(); fieldDefaultValuesCount = reader.ReadInt32(); fieldAndParameterDefaultValueDataOffset = reader.ReadInt32(); fieldAndParameterDefaultValueDataCount = reader.ReadInt32(); fieldMarshaledSizesOffset = reader.ReadInt32(); fieldMarshaledSizesCount = reader.ReadInt32(); parametersOffset = reader.ReadInt32(); parametersCount = reader.ReadInt32(); fieldsOffset = reader.ReadInt32(); fieldsCount = reader.ReadInt32(); genericParametersOffset = reader.ReadInt32(); genericParametersCount = reader.ReadInt32(); genericParameterConstraintsOffset = reader.ReadInt32(); genericParameterConstraintsCount = reader.ReadInt32(); genericContainersOffset = reader.ReadInt32(); genericContainersCount = reader.ReadInt32(); nestedTypesOffset = reader.ReadInt32(); nestedTypesCount = reader.ReadInt32(); interfacesOffset = reader.ReadInt32(); interfacesCount = reader.ReadInt32(); vtableMethodsOffset = reader.ReadInt32(); vtableMethodsCount = reader.ReadInt32(); interfaceOffsetsOffset = reader.ReadInt32(); interfaceOffsetsCount = reader.ReadInt32(); typeDefinitionsOffset = reader.ReadInt32(); typeDefinitionsCount = reader.ReadInt32(); if (IsAtMost(24.15f)) { rgctxEntriesOffset = reader.ReadInt32(); rgctxEntriesCount = reader.ReadInt32(); } imagesOffset = reader.ReadInt32(); imagesCount = reader.ReadInt32(); assembliesOffset = reader.ReadInt32(); assembliesCount = reader.ReadInt32(); if (IsLessThan(27f)) { metadataUsageListsOffset = reader.ReadInt32(); metadataUsageListsCount = reader.ReadInt32(); metadataUsagePairsOffset = reader.ReadInt32(); metadataUsagePairsCount = reader.ReadInt32(); } fieldRefsOffset = reader.ReadInt32(); fieldRefsCount = reader.ReadInt32(); referencedAssembliesOffset = reader.ReadInt32(); referencedAssembliesCount = reader.ReadInt32(); if (IsLessThan(29f)) { attributesInfoOffset = reader.ReadInt32(); attributesInfoCount = reader.ReadInt32(); attributeTypesOffset = reader.ReadInt32(); attributeTypesCount = reader.ReadInt32(); } else { attributeDataOffset = reader.ReadInt32(); attributeDataCount = reader.ReadInt32(); attributeDataRangeOffset = reader.ReadInt32(); attributeDataRangeCount = reader.ReadInt32(); } unresolvedVirtualCallParameterTypesOffset = reader.ReadInt32(); unresolvedVirtualCallParameterTypesCount = reader.ReadInt32(); unresolvedVirtualCallParameterRangesOffset = reader.ReadInt32(); unresolvedVirtualCallParameterRangesCount = reader.ReadInt32(); windowsRuntimeTypeNamesOffset = reader.ReadInt32(); windowsRuntimeTypeNamesSize = reader.ReadInt32(); if (IsAtLeast(27f)) { windowsRuntimeStringsOffset = reader.ReadInt32(); windowsRuntimeStringsSize = reader.ReadInt32(); } if (IsAtLeast(24f)) { exportedTypeDefinitionsOffset = reader.ReadInt32(); exportedTypeDefinitionsCount = reader.ReadInt32(); } } } public class Il2CppImageDefinition : ReadableClass { public int nameIndex; public int assemblyIndex; public int firstTypeIndex; public uint typeCount; [Version(Min = 24f)] public int exportedTypeStart; [Version(Min = 24f)] public uint exportedTypeCount; public int entryPointIndex; public uint token; [Version(Min = 24.1f)] public int customAttributeStart; [Version(Min = 24.1f)] public uint customAttributeCount; public string? Name { get { if (LibCpp2IlMain.TheMetadata != null) { return LibCpp2IlMain.TheMetadata.GetStringFromIndex(nameIndex); } return null; } } public Il2CppTypeDefinition[]? Types { get { if (LibCpp2IlMain.TheMetadata != null) { return LibCpp2IlMain.TheMetadata.typeDefs.Skip(firstTypeIndex).Take((int)typeCount).ToArray(); } return null; } } public override string ToString() { return "Il2CppImageDefinition[Name=" + Name + "]"; } public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); assemblyIndex = reader.ReadInt32(); firstTypeIndex = reader.ReadInt32(); typeCount = reader.ReadUInt32(); if (IsAtLeast(24f)) { exportedTypeStart = reader.ReadInt32(); exportedTypeCount = reader.ReadUInt32(); } entryPointIndex = reader.ReadInt32(); token = reader.ReadUInt32(); if (IsAtLeast(24.1f)) { customAttributeStart = reader.ReadInt32(); customAttributeCount = reader.ReadUInt32(); } } } public class Il2CppInterfaceOffset : ReadableClass { public int typeIndex; public int offset; public Il2CppTypeReflectionData? type => LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary.GetType(typeIndex)); public override string ToString() { return $"InterfaceOffsetPair({typeIndex}/{type?.ToString() ?? "unknown type"} => {offset})"; } public override void Read(ClassReadingBinaryReader reader) { typeIndex = reader.ReadInt32(); offset = reader.ReadInt32(); } } public class Il2CppMetadata : ClassReadingBinaryReader { public Il2CppGlobalMetadataHeader metadataHeader; public Il2CppAssemblyDefinition[] AssemblyDefinitions; public Il2CppImageDefinition[] imageDefinitions; public Il2CppTypeDefinition[] typeDefs; internal Il2CppInterfaceOffset[] interfaceOffsets; public uint[] VTableMethodIndices; public Il2CppMethodDefinition[] methodDefs; public Il2CppParameterDefinition[] parameterDefs; public Il2CppFieldDefinition[] fieldDefs; private Il2CppFieldDefaultValue[] fieldDefaultValues; private Il2CppParameterDefaultValue[] parameterDefaultValues; public Il2CppPropertyDefinition[] propertyDefs; public List attributeTypeRanges; private Il2CppStringLiteral[] stringLiterals; public Il2CppMetadataUsageList[] metadataUsageLists; private Il2CppMetadataUsagePair[] metadataUsagePairs; public Il2CppRGCTXDefinition[] RgctxDefinitions; public int[] attributeTypes; public int[] interfaceIndices; public List AttributeDataRanges; public Dictionary>? metadataUsageDic; public int[] nestedTypeIndices; public Il2CppEventDefinition[] eventDefs; public Il2CppGenericContainer[] genericContainers; public Il2CppFieldRef[] fieldRefs; public Il2CppGenericParameter[] genericParameters; public int[] constraintIndices; public int[] referencedAssemblies; private readonly Dictionary _fieldDefaultValueLookup = new Dictionary(); private readonly Dictionary _fieldDefaultLookupNew = new Dictionary(); private ConcurrentDictionary _cachedStrings = new ConcurrentDictionary(); public static bool HasMetadataHeader(byte[] bytes) { if (bytes.Length >= 4) { return BitConverter.ToUInt32(bytes, 0) == 4205910959u; } return false; } public static Il2CppMetadata? ReadFrom(byte[] bytes, UnityVersion unityVersion) { if (!HasMetadataHeader(bytes)) { throw new FormatException("Invalid or corrupt metadata (magic number check failed)"); } int num = BitConverter.ToInt32(bytes, 4); if (num < 23 || num > 29) { throw new FormatException("Unsupported metadata version found! We support 23-29, got " + num); } LibLogger.VerboseNewline($"\tIL2CPP Metadata Declares its version as {num}"); float num2 = num switch { 27 => (!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2021, (ushort)1)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2020, (ushort)2, (ushort)4)) ? ((float)num) : 27.1f) : 27.2f, 24 => (!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2020, (ushort)1, (ushort)11)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2020)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2019, (ushort)4, (ushort)21)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2019, (ushort)4, (ushort)15)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2019, (ushort)3, (ushort)7)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2019)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2018, (ushort)4, (ushort)34)) ? ((!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2018, (ushort)3)) ? ((float)num) : 24.1f) : 24.15f) : 24.2f) : 24.3f) : 24.4f) : 24.5f) : 24.3f) : 24.4f, 29 => (!((UnityVersion)(ref unityVersion)).IsGreaterEqual((ushort)2022, (ushort)1, (ushort)0, (UnityVersionType)1, (byte)7)) ? 29f : 29.1f, _ => num, }; LibLogger.InfoNewline($"\tUsing actual IL2CPP Metadata version {num2}"); LibCpp2IlMain.MetadataVersion = num2; return new Il2CppMetadata(new MemoryStream(bytes)); } private Il2CppMetadata(MemoryStream stream) : base(stream) { metadataHeader = ReadReadable(-1L); if (metadataHeader.magicNumber != 4205910959u) { throw new Exception("ERROR: Magic number mismatch. Expecting " + 4205910959u + " but got " + metadataHeader.magicNumber); } LibLogger.Verbose("\tReading image definitions..."); DateTime now = DateTime.Now; imageDefinitions = ReadMetadataClassArray(metadataHeader.imagesOffset, metadataHeader.imagesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading assembly definitions..."); now = DateTime.Now; AssemblyDefinitions = ReadMetadataClassArray(metadataHeader.assembliesOffset, metadataHeader.assembliesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading type definitions..."); now = DateTime.Now; typeDefs = ReadMetadataClassArray(metadataHeader.typeDefinitionsOffset, metadataHeader.typeDefinitionsCount); LibLogger.VerboseNewline($"{typeDefs.Length} OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading interface offsets..."); now = DateTime.Now; interfaceOffsets = ReadMetadataClassArray(metadataHeader.interfaceOffsetsOffset, metadataHeader.interfaceOffsetsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading vtable indices..."); now = DateTime.Now; VTableMethodIndices = ReadClassArrayAtRawAddr(metadataHeader.vtableMethodsOffset, metadataHeader.vtableMethodsCount / 4); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading method definitions..."); now = DateTime.Now; methodDefs = ReadMetadataClassArray(metadataHeader.methodsOffset, metadataHeader.methodsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading method parameter definitions..."); now = DateTime.Now; parameterDefs = ReadMetadataClassArray(metadataHeader.parametersOffset, metadataHeader.parametersCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading field definitions..."); now = DateTime.Now; fieldDefs = ReadMetadataClassArray(metadataHeader.fieldsOffset, metadataHeader.fieldsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading default field values..."); now = DateTime.Now; fieldDefaultValues = ReadMetadataClassArray(metadataHeader.fieldDefaultValuesOffset, metadataHeader.fieldDefaultValuesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading default parameter values..."); now = DateTime.Now; parameterDefaultValues = ReadMetadataClassArray(metadataHeader.parameterDefaultValuesOffset, metadataHeader.parameterDefaultValuesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading property definitions..."); now = DateTime.Now; propertyDefs = ReadMetadataClassArray(metadataHeader.propertiesOffset, metadataHeader.propertiesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading interface definitions..."); now = DateTime.Now; interfaceIndices = ReadClassArrayAtRawAddr(metadataHeader.interfacesOffset, metadataHeader.interfacesCount / 4); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading nested type definitions..."); now = DateTime.Now; nestedTypeIndices = ReadClassArrayAtRawAddr(metadataHeader.nestedTypesOffset, metadataHeader.nestedTypesCount / 4); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading event definitions..."); now = DateTime.Now; eventDefs = ReadMetadataClassArray(metadataHeader.eventsOffset, metadataHeader.eventsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading generic container definitions..."); now = DateTime.Now; genericContainers = ReadMetadataClassArray(metadataHeader.genericContainersOffset, metadataHeader.genericContainersCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading generic parameter definitions..."); now = DateTime.Now; genericParameters = ReadMetadataClassArray(metadataHeader.genericParametersOffset, metadataHeader.genericParametersCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading generic parameter constraint indices..."); now = DateTime.Now; constraintIndices = ReadClassArrayAtRawAddr(metadataHeader.genericParameterConstraintsOffset, metadataHeader.genericParameterConstraintsCount / 4); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading referenced assemblies..."); now = DateTime.Now; referencedAssemblies = ReadClassArrayAtRawAddr(metadataHeader.referencedAssembliesOffset, metadataHeader.referencedAssembliesCount / 4); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tReading string definitions..."); now = DateTime.Now; stringLiterals = ReadMetadataClassArray(metadataHeader.stringLiteralOffset, metadataHeader.stringLiteralCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); if (LibCpp2IlMain.MetadataVersion < 24.2f) { LibLogger.Verbose("\tReading RGCTX data..."); now = DateTime.Now; RgctxDefinitions = ReadMetadataClassArray(metadataHeader.rgctxEntriesOffset, metadataHeader.rgctxEntriesCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } if (LibCpp2IlMain.MetadataVersion < 27f) { LibLogger.Verbose("\tReading usage data..."); now = DateTime.Now; metadataUsageLists = ReadMetadataClassArray(metadataHeader.metadataUsageListsOffset, metadataHeader.metadataUsageListsCount); metadataUsagePairs = ReadMetadataClassArray(metadataHeader.metadataUsagePairsOffset, metadataHeader.metadataUsagePairsCount); DecipherMetadataUsage(); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } LibLogger.Verbose("\tReading field references..."); now = DateTime.Now; fieldRefs = ReadMetadataClassArray(metadataHeader.fieldRefsOffset, metadataHeader.fieldRefsCount); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); if (LibCpp2IlMain.MetadataVersion < 29f) { LibLogger.Verbose("\tReading attribute types..."); now = DateTime.Now; attributeTypeRanges = ReadMetadataClassArray(metadataHeader.attributesInfoOffset, metadataHeader.attributesInfoCount).ToList(); attributeTypes = ReadClassArrayAtRawAddr(metadataHeader.attributeTypesOffset, metadataHeader.attributeTypesCount / 4); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } else { LibLogger.Verbose("\tReading Attribute data..."); now = DateTime.Now; AttributeDataRanges = ReadReadableArrayAtRawAddr(metadataHeader.attributeDataRangeOffset, metadataHeader.attributeDataRangeCount / 8).ToList(); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } LibLogger.Verbose("\tBuilding Lookup Table for field defaults..."); now = DateTime.Now; Il2CppFieldDefaultValue[] array = fieldDefaultValues; foreach (Il2CppFieldDefaultValue il2CppFieldDefaultValue in array) { _fieldDefaultValueLookup[il2CppFieldDefaultValue.fieldIndex] = il2CppFieldDefaultValue; _fieldDefaultLookupNew[fieldDefs[il2CppFieldDefaultValue.fieldIndex]] = il2CppFieldDefaultValue; } LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); _hasFinishedInitialRead = true; } private T[] ReadMetadataClassArray(int offset, int length) where T : ReadableClass, new() { return ReadReadableArrayAtRawAddr(offset, length / LibCpp2ILUtils.VersionAwareSizeOf(typeof(T), dontCheckVersionAttributes: false, downsize: false)); } private void DecipherMetadataUsage() { metadataUsageDic = new Dictionary>(); for (uint num = 1u; num <= 6; num++) { metadataUsageDic[num] = new SortedDictionary(); } Il2CppMetadataUsageList[] array = metadataUsageLists; foreach (Il2CppMetadataUsageList il2CppMetadataUsageList in array) { for (int j = 0; j < il2CppMetadataUsageList.count; j++) { long num2 = il2CppMetadataUsageList.start + j; Il2CppMetadataUsagePair il2CppMetadataUsagePair = metadataUsagePairs[num2]; uint encodedIndexType = GetEncodedIndexType(il2CppMetadataUsagePair.encodedSourceIndex); uint decodedMethodIndex = GetDecodedMethodIndex(il2CppMetadataUsagePair.encodedSourceIndex); metadataUsageDic[encodedIndexType][il2CppMetadataUsagePair.destinationIndex] = decodedMethodIndex; } } } public uint GetMaxMetadataUsages() { if (metadataUsageDic == null) { return 0u; } return metadataUsageDic.Max>, uint>((KeyValuePair> x) => x.Value.Max((KeyValuePair y) => y.Key)) + 1; } private uint GetEncodedIndexType(uint index) { return (index & 0xE0000000u) >> 29; } private uint GetDecodedMethodIndex(uint index) { return index & 0x1FFFFFFFu; } public Il2CppFieldDefaultValue? GetFieldDefaultValueFromIndex(int index) { return _fieldDefaultValueLookup.GetOrDefault(index); } public Il2CppFieldDefaultValue? GetFieldDefaultValue(Il2CppFieldDefinition field) { return _fieldDefaultLookupNew.GetOrDefault(field); } public (int ptr, int type) GetFieldDefaultValue(int fieldIdx) { Il2CppFieldDefinition il2CppFieldDefinition = fieldDefs[fieldIdx]; if ((LibCpp2IlMain.Binary.GetType(il2CppFieldDefinition.typeIndex).Attrs & 0x100u) != 0) { Il2CppFieldDefaultValue fieldDefaultValueFromIndex = GetFieldDefaultValueFromIndex(fieldIdx); if (fieldDefaultValueFromIndex == null) { return (-1, -1); } return (fieldDefaultValueFromIndex.dataIndex, fieldDefaultValueFromIndex.typeIndex); } return (-1, -1); } public Il2CppParameterDefaultValue? GetParameterDefaultValueFromIndex(int index) { return parameterDefaultValues.FirstOrDefault((Il2CppParameterDefaultValue x) => x.parameterIndex == index); } public int GetDefaultValueFromIndex(int index) { return metadataHeader.fieldAndParameterDefaultValueDataOffset + index; } public string GetStringFromIndex(int index) { GetLockOrThrow(); try { return ReadStringFromIndexNoReadLock(index); } finally { ReleaseLock(); } } internal string ReadStringFromIndexNoReadLock(int index) { if (!_cachedStrings.ContainsKey(index)) { _cachedStrings[index] = ReadStringToNullNoLock(metadataHeader.stringOffset + index); } return _cachedStrings[index]; } public Il2CppCustomAttributeTypeRange? GetCustomAttributeData(Il2CppImageDefinition imageDef, int customAttributeIndex, uint token, out int idx) { idx = -1; if (LibCpp2IlMain.MetadataVersion <= 24f) { idx = customAttributeIndex; return attributeTypeRanges[customAttributeIndex]; } Il2CppCustomAttributeTypeRange item = new Il2CppCustomAttributeTypeRange { token = token }; if (imageDef.customAttributeStart < 0) { throw new Exception("Image has customAttributeStart < 0"); } if (imageDef.customAttributeStart + imageDef.customAttributeCount > attributeTypeRanges.Count) { throw new Exception($"Image has customAttributeStart + customAttributeCount > attributeTypeRanges.Count ({imageDef.customAttributeStart + imageDef.customAttributeCount} > {attributeTypeRanges.Count})"); } idx = attributeTypeRanges.BinarySearch(imageDef.customAttributeStart, (int)imageDef.customAttributeCount, item, new TokenComparer()); if (idx >= 0) { return attributeTypeRanges[idx]; } return null; } public string GetStringLiteralFromIndex(uint index) { Il2CppStringLiteral il2CppStringLiteral = stringLiterals[index]; return Encoding.UTF8.GetString(ReadByteArrayAtRawAddress(metadataHeader.stringLiteralDataOffset + il2CppStringLiteral.dataIndex, (int)il2CppStringLiteral.length)); } } public class Il2CppMetadataUsageList : ReadableClass { public uint start; public uint count; public override void Read(ClassReadingBinaryReader reader) { start = reader.ReadUInt32(); count = reader.ReadUInt32(); } } public class Il2CppMetadataUsagePair : ReadableClass { public uint destinationIndex; public uint encodedSourceIndex; public override void Read(ClassReadingBinaryReader reader) { destinationIndex = reader.ReadUInt32(); encodedSourceIndex = reader.ReadUInt32(); } } public class Il2CppMethodDefinition : ReadableClass { public int nameIndex; public int declaringTypeIdx; public int returnTypeIdx; public int parameterStart; [Version(Max = 24f)] public int customAttributeIndex; public int genericContainerIndex; [Version(Max = 24.15f)] public int methodIndex; [Version(Max = 24.15f)] public int invokerIndex; [Version(Max = 24.15f)] public int delegateWrapperIndex; [Version(Max = 24.15f)] public int rgctxStartIndex; [Version(Max = 24.15f)] public int rgctxCount; public uint token; public ushort flags; public ushort iflags; public ushort slot; public ushort parameterCount; private ulong? _methodPointer; private Il2CppParameterReflectionData[]? _cachedParameters; public MethodAttributes Attributes => (MethodAttributes)flags; public bool IsStatic => (Attributes & MethodAttributes.Static) != 0; public int MethodIndex => LibCpp2IlReflection.GetMethodIndexFromMethod(this); public string? Name { get; private set; } public string? GlobalKey { get { if (DeclaringType != null) { return DeclaringType.Name + "." + Name + "()"; } return null; } } public Il2CppType? RawReturnType => LibCpp2IlMain.Binary?.GetType(returnTypeIdx); public Il2CppTypeReflectionData? ReturnType { get { if (LibCpp2IlMain.Binary != null) { return LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary.GetType(returnTypeIdx)); } return null; } } public Il2CppTypeDefinition? DeclaringType { get { if (LibCpp2IlMain.TheMetadata != null) { return LibCpp2IlMain.TheMetadata.typeDefs[declaringTypeIdx]; } return null; } } public ulong MethodPointer { get { if (!_methodPointer.HasValue) { if (LibCpp2IlMain.Binary == null || LibCpp2IlMain.TheMetadata == null || DeclaringType == null) { LibLogger.WarnNewline($"Couldn't get method pointer for {Name}. Binary is {LibCpp2IlMain.Binary}, Meta is {LibCpp2IlMain.TheMetadata}, DeclaringType is {DeclaringType}"); return 0uL; } int imageIndex = 0; if (LibCpp2IlMain.MetadataVersion >= 27f) { imageIndex = LibCpp2IlMain.Binary.GetCodegenModuleIndexByName(DeclaringType.DeclaringAssembly.Name); } else if (LibCpp2IlMain.MetadataVersion >= 24.2f) { imageIndex = DeclaringType.DeclaringAssembly.assemblyIndex; } _methodPointer = LibCpp2IlMain.Binary.GetMethodPointer(methodIndex, MethodIndex, imageIndex, token); } return _methodPointer.Value; } } public long MethodOffsetInFile { get { if (MethodPointer != 0L && LibCpp2IlMain.Binary != null) { if (!LibCpp2IlMain.Binary.TryMapVirtualAddressToRaw(MethodPointer, out var result)) { return 0L; } return result; } return 0L; } } public ulong Rva { get { if (MethodPointer != 0L && LibCpp2IlMain.Binary != null) { return LibCpp2IlMain.Binary.GetRva(MethodPointer); } return 0uL; } } public string? HumanReadableSignature { get { if (ReturnType != null && Parameters != null && Name != null) { return $"{ReturnType} {Name}({string.Join(", ", Parameters.AsEnumerable())})"; } return null; } } public Il2CppParameterDefinition[]? InternalParameterData { get { if (LibCpp2IlMain.TheMetadata == null || LibCpp2IlMain.Binary == null) { return null; } if (parameterStart < 0 || parameterCount == 0) { return Array.Empty(); } Il2CppParameterDefinition[] array = new Il2CppParameterDefinition[parameterCount]; Array.Copy(LibCpp2IlMain.TheMetadata.parameterDefs, parameterStart, array, 0, parameterCount); return array; } } public Il2CppType[]? InternalParameterTypes { get { if (InternalParameterData != null) { return InternalParameterData.Select((Il2CppParameterDefinition paramDef) => LibCpp2IlMain.Binary.GetType(paramDef.typeIndex)).ToArray(); } return null; } } public Il2CppParameterReflectionData[]? Parameters { get { if (_cachedParameters == null && InternalParameterData != null) { _cachedParameters = InternalParameterData.Select(delegate(Il2CppParameterDefinition paramDef, int idx) { Il2CppType type = LibCpp2IlMain.Binary.GetType(paramDef.typeIndex); ParameterAttributes attrs = (ParameterAttributes)type.Attrs; Il2CppParameterDefaultValue il2CppParameterDefaultValue = (((attrs & ParameterAttributes.HasDefault) != 0) ? LibCpp2IlMain.TheMetadata.GetParameterDefaultValueFromIndex(parameterStart + idx) : null); return new Il2CppParameterReflectionData { Type = LibCpp2ILUtils.GetTypeReflectionData(type), ParameterName = LibCpp2IlMain.TheMetadata.GetStringFromIndex(paramDef.nameIndex), Attributes = attrs, RawType = type, DefaultValue = ((il2CppParameterDefaultValue == null) ? null : LibCpp2ILUtils.GetDefaultValue(il2CppParameterDefaultValue.dataIndex, il2CppParameterDefaultValue.typeIndex)), ParameterIndex = idx }; }).ToArray(); } return _cachedParameters; } } public Il2CppGenericContainer? GenericContainer { get { if (genericContainerIndex >= 0) { Il2CppMetadata? theMetadata = LibCpp2IlMain.TheMetadata; if (theMetadata == null) { return null; } return theMetadata.genericContainers[genericContainerIndex]; } return null; } } public override string? ToString() { if (LibCpp2IlMain.TheMetadata == null) { return base.ToString(); } return $"Il2CppMethodDefinition[Name='{Name}', ReturnType={ReturnType}, DeclaringType={DeclaringType}]"; } public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); long position = reader.Position; Name = ((Il2CppMetadata)reader).ReadStringFromIndexNoReadLock(nameIndex); reader.Position = position; declaringTypeIdx = reader.ReadInt32(); returnTypeIdx = reader.ReadInt32(); parameterStart = reader.ReadInt32(); if (IsAtMost(24f)) { customAttributeIndex = reader.ReadInt32(); } genericContainerIndex = reader.ReadInt32(); if (IsAtMost(24.15f)) { methodIndex = reader.ReadInt32(); invokerIndex = reader.ReadInt32(); delegateWrapperIndex = reader.ReadInt32(); rgctxStartIndex = reader.ReadInt32(); rgctxCount = reader.ReadInt32(); } token = reader.ReadUInt32(); flags = reader.ReadUInt16(); iflags = reader.ReadUInt16(); slot = reader.ReadUInt16(); parameterCount = reader.ReadUInt16(); } } public enum Il2CppPackingSizeEnum : uint { Zero, One, Two, Four, Eight, Sixteen, ThirtyTwo, SixtyFour, OneHundredTwentyEight } public static class Il2CppPackingSizeEnumExtensions { public static uint NumericalValue(this Il2CppPackingSizeEnum size) { return size switch { Il2CppPackingSizeEnum.Zero => 0u, Il2CppPackingSizeEnum.One => 1u, Il2CppPackingSizeEnum.Two => 2u, Il2CppPackingSizeEnum.Four => 4u, Il2CppPackingSizeEnum.Eight => 8u, Il2CppPackingSizeEnum.Sixteen => 16u, Il2CppPackingSizeEnum.ThirtyTwo => 32u, Il2CppPackingSizeEnum.SixtyFour => 64u, Il2CppPackingSizeEnum.OneHundredTwentyEight => 128u, _ => throw new ArgumentOutOfRangeException("size", size, null), }; } } public class Il2CppParameterDefaultValue : ReadableClass { public int parameterIndex; public int typeIndex; public int dataIndex; public object? ContainedDefaultValue => LibCpp2ILUtils.GetDefaultValue(dataIndex, typeIndex); public override void Read(ClassReadingBinaryReader reader) { parameterIndex = reader.ReadInt32(); typeIndex = reader.ReadInt32(); dataIndex = reader.ReadInt32(); } } public class Il2CppParameterDefinition : ReadableClass, IIl2CppTokenProvider { public int nameIndex; public uint token; [Version(Max = 24f)] public int customAttributeIndex; public int typeIndex; public uint Token => token; public Il2CppType? RawType => LibCpp2IlMain.Binary?.GetType(typeIndex); public string? Name { get; private set; } public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); long position = reader.Position; Name = ((Il2CppMetadata)reader).ReadStringFromIndexNoReadLock(nameIndex); reader.Position = position; token = reader.ReadUInt32(); if (IsAtMost(24f)) { customAttributeIndex = reader.ReadInt32(); } typeIndex = reader.ReadInt32(); } } public class Il2CppPropertyDefinition : ReadableClass, IIl2CppTokenProvider { public int nameIndex; public int get; public int set; public uint attrs; [Version(Max = 24f)] public int customAttributeIndex; public uint token; [NonSerialized] private Il2CppTypeDefinition? _type; public int PropertyIndex => LibCpp2IlReflection.GetPropertyIndexFromProperty(this); public Il2CppTypeDefinition? DeclaringType { get { if (_type != null) { return _type; } if (LibCpp2IlMain.TheMetadata == null) { return null; } _type = LibCpp2IlMain.TheMetadata.typeDefs.FirstOrDefault((Il2CppTypeDefinition t) => t.Properties.Contains(this)); return _type; } internal set { _type = value; } } public string? Name { get; private set; } public Il2CppMethodDefinition? Getter { get { if (LibCpp2IlMain.TheMetadata != null && get >= 0 && DeclaringType != null) { return LibCpp2IlMain.TheMetadata.methodDefs[DeclaringType.FirstMethodIdx + get]; } return null; } } public Il2CppMethodDefinition? Setter { get { if (LibCpp2IlMain.TheMetadata != null && set >= 0 && DeclaringType != null) { return LibCpp2IlMain.TheMetadata.methodDefs[DeclaringType.FirstMethodIdx + set]; } return null; } } public Il2CppTypeReflectionData? PropertyType { get { if (LibCpp2IlMain.TheMetadata != null) { if (Getter != null) { return Getter.ReturnType; } return Setter.Parameters[0].Type; } return null; } } public Il2CppType? RawPropertyType { get { if (LibCpp2IlMain.TheMetadata != null) { if (Getter != null) { return Getter.RawReturnType; } return Setter.Parameters[0].RawType; } return null; } } public bool IsStatic { get { if (Getter != null) { return Getter.IsStatic; } return Setter.IsStatic; } } public uint Token => token; public override void Read(ClassReadingBinaryReader reader) { nameIndex = reader.ReadInt32(); long position = reader.Position; Name = ((Il2CppMetadata)reader).ReadStringFromIndexNoReadLock(nameIndex); reader.Position = position; get = reader.ReadInt32(); set = reader.ReadInt32(); attrs = reader.ReadUInt32(); if (IsAtMost(24f)) { customAttributeIndex = reader.ReadInt32(); } token = reader.ReadUInt32(); } } public class Il2CppStringLiteral : ReadableClass { public uint length; public int dataIndex; public override void Read(ClassReadingBinaryReader reader) { length = reader.ReadUInt32(); dataIndex = reader.ReadInt32(); } } public class Il2CppTypeDefinition : ReadableClass { public int NameIndex; public int NamespaceIndex; [Version(Max = 24f)] public int CustomAttributeIndex; public int ByvalTypeIndex; [Version(Max = 24.5f)] public int ByrefTypeIndex; public int DeclaringTypeIndex; public int ParentIndex; public int ElementTypeIndex; [Version(Max = 24.15f)] public int RgctxStartIndex; [Version(Max = 24.15f)] public int RgctxCount; public int GenericContainerIndex; public uint Flags; public int FirstFieldIdx; public int FirstMethodIdx; public int FirstEventId; public int FirstPropertyId; public int NestedTypesStart; public int InterfacesStart; public int VtableStart; public int InterfaceOffsetsStart; public ushort MethodCount; public ushort PropertyCount; public ushort FieldCount; public ushort EventCount; public ushort NestedTypeCount; public ushort VtableCount; public ushort InterfacesCount; public ushort InterfaceOffsetsCount; public uint Bitfield; public uint Token; private Il2CppImageDefinition? _cachedDeclaringAssembly; private string? _cachedNamespace; private string? _cachedName; public bool IsValueType => (Bitfield & 1) == 1; public bool IsEnumType => ((Bitfield >> 1) & 1) == 1; public bool HasFinalizer => ((Bitfield >> 2) & 1) == 1; public bool HasCctor => ((Bitfield >> 3) & 1) == 1; public bool IsBlittable => ((Bitfield >> 4) & 1) == 1; public bool IsImportOrWindowsRuntime => ((Bitfield >> 5) & 1) == 1; public uint PackingSize => ((Il2CppPackingSizeEnum)((Bitfield >> 6) & 0xFu)).NumericalValue(); public bool PackingSizeIsDefault => ((Bitfield >> 10) & 1) == 1; public bool ClassSizeIsDefault => ((Bitfield >> 11) & 1) == 1; public uint SpecifiedPackingSize => ((Il2CppPackingSizeEnum)((Bitfield >> 12) & 0xFu)).NumericalValue(); public bool IsByRefLike => ((Bitfield >> 16) & 1) == 1; public TypeAttributes Attributes => (TypeAttributes)Flags; public Il2CppTypeDefinitionSizes RawSizes { get { ulong va = LibCpp2IlMain.Binary.TypeDefinitionSizePointers[TypeIndex]; return LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(va); } } public int Size => RawSizes.native_size; public Il2CppInterfaceOffset[] InterfaceOffsets { get { if (InterfaceOffsetsStart < 0) { return new Il2CppInterfaceOffset[0]; } return LibCpp2IlMain.TheMetadata.interfaceOffsets.SubArray(InterfaceOffsetsStart, InterfaceOffsetsCount); } } public MetadataUsage?[] VTable { get { if (VtableStart < 0) { return new MetadataUsage[0]; } return (from v in LibCpp2IlMain.TheMetadata.VTableMethodIndices.SubArray(VtableStart, VtableCount) select MetadataUsage.DecodeMetadataUsage(v, 0uL)).ToArray(); } } public int TypeIndex => LibCpp2IlReflection.GetTypeIndexFromType(this); public bool IsAbstract => (Flags & 0x80) != 0; public bool IsInterface => (Flags & 0x20) != 0; public Il2CppImageDefinition? DeclaringAssembly { get { if (_cachedDeclaringAssembly == null) { if (LibCpp2IlMain.TheMetadata == null) { return null; } LibCpp2ILUtils.PopulateDeclaringAssemblyCache(); } return _cachedDeclaringAssembly; } internal set { _cachedDeclaringAssembly = value; } } public Il2CppCodeGenModule? CodeGenModule { get { if (LibCpp2IlMain.Binary != null) { return LibCpp2IlMain.Binary.GetCodegenModuleByName(DeclaringAssembly.Name); } return null; } } public Il2CppRGCTXDefinition[] RgctXs { get { if (LibCpp2IlMain.MetadataVersion < 24.2f) { return LibCpp2IlMain.TheMetadata.RgctxDefinitions.Skip(RgctxStartIndex).Take(RgctxCount).ToArray(); } Il2CppCodeGenModule codeGenModule = CodeGenModule; if (codeGenModule == null) { return new Il2CppRGCTXDefinition[0]; } Il2CppTokenRangePair il2CppTokenRangePair = codeGenModule.RGCTXRanges.FirstOrDefault((Il2CppTokenRangePair r) => r.token == Token); if (il2CppTokenRangePair == null) { return new Il2CppRGCTXDefinition[0]; } return LibCpp2IlMain.Binary.GetRgctxDataForPair(codeGenModule, il2CppTokenRangePair); } } public ulong[] RgctxMethodPointers { get { int codegenModuleIndexByName = LibCpp2IlMain.Binary.GetCodegenModuleIndexByName(DeclaringAssembly.Name); if (codegenModuleIndexByName < 0) { return new ulong[0]; } ulong[] pointers = LibCpp2IlMain.Binary.GetCodegenModuleMethodPointers(codegenModuleIndexByName); return (from r in RgctXs where r.type == Il2CppRGCTXDataType.IL2CPP_RGCTX_DATA_METHOD select pointers[r.MethodIndex]).ToArray(); } } public string? Namespace { get { if (_cachedNamespace == null) { _cachedNamespace = ((LibCpp2IlMain.TheMetadata == null) ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(NamespaceIndex)); } return _cachedNamespace; } } public string? Name { get { if (_cachedName == null) { _cachedName = ((LibCpp2IlMain.TheMetadata == null) ? null : LibCpp2IlMain.TheMetadata.GetStringFromIndex(NameIndex)); } return _cachedName; } } public string? FullName { get { if (LibCpp2IlMain.TheMetadata == null) { return null; } if (DeclaringType != null) { return DeclaringType.FullName + "/" + Name; } return (string.IsNullOrEmpty(Namespace) ? "" : (Namespace + ".")) + Name; } } public Il2CppType? RawBaseType { get { if (ParentIndex != -1) { return LibCpp2IlMain.Binary.GetType(ParentIndex); } return null; } } public Il2CppTypeReflectionData? BaseType { get { if (ParentIndex != -1 && LibCpp2IlMain.Binary != null) { return LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary.GetType(ParentIndex)); } return null; } } public Il2CppFieldDefinition[]? Fields { get { if (LibCpp2IlMain.TheMetadata == null) { return null; } if (FirstFieldIdx < 0 || FieldCount == 0) { return Array.Empty(); } Il2CppFieldDefinition[] array = new Il2CppFieldDefinition[FieldCount]; Array.Copy(LibCpp2IlMain.TheMetadata.fieldDefs, FirstFieldIdx, array, 0, FieldCount); return array; } } public FieldAttributes[]? FieldAttributes => (from idx in Fields?.Select((Il2CppFieldDefinition f) => f.typeIndex) select LibCpp2IlMain.Binary.GetType(idx) into t select (FieldAttributes)t.Attrs).ToArray(); public object?[]? FieldDefaults => (from tuple in Fields?.Select((Il2CppFieldDefinition f, int idx) => (f.FieldIndex, FieldAttributes[idx])) select ((tuple.Item2 & System.Reflection.FieldAttributes.HasDefault) == 0) ? null : LibCpp2IlMain.TheMetadata.GetFieldDefaultValueFromIndex(tuple.FieldIndex) into def select (def != null) ? LibCpp2ILUtils.GetDefaultValue(def.dataIndex, def.typeIndex) : null).ToArray(); public Il2CppFieldReflectionData[]? FieldInfos { get { Il2CppFieldDefinition[] fields = Fields; FieldAttributes[] fieldAttributes = FieldAttributes; object[] fieldDefaults = FieldDefaults; if (fields == null || fieldAttributes == null || fieldDefaults == null) { return null; } Il2CppFieldReflectionData[] array = new Il2CppFieldReflectionData[FieldCount]; for (int i = 0; i < FieldCount; i++) { array[i] = new Il2CppFieldReflectionData(fields[i], fieldAttributes[i], fieldDefaults[i], i, LibCpp2IlMain.Binary.GetFieldOffsetFromIndex(TypeIndex, i, fields[i].FieldIndex, IsValueType, fieldAttributes[i].HasFlag(System.Reflection.FieldAttributes.Static))); } return array; } } public Il2CppMethodDefinition[]? Methods { get { if (LibCpp2IlMain.TheMetadata == null) { return null; } if (FirstMethodIdx < 0 || MethodCount == 0) { return Array.Empty(); } Il2CppMethodDefinition[] array = new Il2CppMethodDefinition[MethodCount]; Array.Copy(LibCpp2IlMain.TheMetadata.methodDefs, FirstMethodIdx, array, 0, MethodCount); return array; } } public Il2CppPropertyDefinition[]? Properties { get { if (LibCpp2IlMain.TheMetadata == null) { return null; } if (FirstPropertyId < 0 || PropertyCount == 0) { return Array.Empty(); } Il2CppPropertyDefinition[] array = new Il2CppPropertyDefinition[PropertyCount]; Array.Copy(LibCpp2IlMain.TheMetadata.propertyDefs, FirstPropertyId, array, 0, PropertyCount); return array.Select(delegate(Il2CppPropertyDefinition p) { p.DeclaringType = this; return p; }).ToArray(); } } public Il2CppEventDefinition[]? Events { get { if (LibCpp2IlMain.TheMetadata != null) { return LibCpp2IlMain.TheMetadata.eventDefs.Skip(FirstEventId).Take(EventCount).Select(delegate(Il2CppEventDefinition e) { e.DeclaringType = this; return e; }) .ToArray(); } return null; } } public Il2CppTypeDefinition[]? NestedTypes { get { if (LibCpp2IlMain.TheMetadata != null) { return (from idx in LibCpp2IlMain.TheMetadata.nestedTypeIndices.Skip(NestedTypesStart).Take(NestedTypeCount) select LibCpp2IlMain.TheMetadata.typeDefs[idx]).ToArray(); } return null; } } public Il2CppType[] RawInterfaces { get { if (LibCpp2IlMain.TheMetadata != null && LibCpp2IlMain.Binary != null) { return (from idx in LibCpp2IlMain.TheMetadata.interfaceIndices.Skip(InterfacesStart).Take(InterfacesCount) select LibCpp2IlMain.Binary.GetType(idx)).ToArray(); } return Array.Empty(); } } public Il2CppTypeReflectionData[]? Interfaces { get { if (LibCpp2IlMain.TheMetadata != null && LibCpp2IlMain.Binary != null) { return RawInterfaces.Select(LibCpp2ILUtils.GetTypeReflectionData).ToArray(); } return null; } } public Il2CppTypeDefinition? DeclaringType { get { if (LibCpp2IlMain.TheMetadata != null && LibCpp2IlMain.Binary != null && DeclaringTypeIndex >= 0) { return LibCpp2IlMain.TheMetadata.typeDefs[LibCpp2IlMain.Binary.GetType(DeclaringTypeIndex).Data.ClassIndex]; } return null; } } public Il2CppGenericContainer? GenericContainer { get { if (GenericContainerIndex >= 0) { Il2CppMetadata? theMetadata = LibCpp2IlMain.TheMetadata; if (theMetadata == null) { return null; } return theMetadata.genericContainers[GenericContainerIndex]; } return null; } } public Il2CppType EnumUnderlyingType { get { if (!IsEnumType) { throw new InvalidOperationException("Cannot get the underlying type of a non-enum type."); } return LibCpp2IlMain.Binary.GetType(ElementTypeIndex); } } public override string? ToString() { if (LibCpp2IlMain.TheMetadata == null) { return base.ToString(); } return $"Il2CppTypeDefinition[namespace='{Namespace}', name='{Name}', parentType={BaseType?.ToString() ?? "null"}, assembly={DeclaringAssembly}]"; } public override void Read(ClassReadingBinaryReader reader) { NameIndex = reader.ReadInt32(); NamespaceIndex = reader.ReadInt32(); if (IsAtMost(24f)) { CustomAttributeIndex = reader.ReadInt32(); } ByvalTypeIndex = reader.ReadInt32(); if (IsLessThan(27f)) { ByrefTypeIndex = reader.ReadInt32(); } DeclaringTypeIndex = reader.ReadInt32(); ParentIndex = reader.ReadInt32(); ElementTypeIndex = reader.ReadInt32(); if (IsAtMost(24.15f)) { RgctxStartIndex = reader.ReadInt32(); RgctxCount = reader.ReadInt32(); } GenericContainerIndex = reader.ReadInt32(); Flags = reader.ReadUInt32(); FirstFieldIdx = reader.ReadInt32(); FirstMethodIdx = reader.ReadInt32(); FirstEventId = reader.ReadInt32(); FirstPropertyId = reader.ReadInt32(); NestedTypesStart = reader.ReadInt32(); InterfacesStart = reader.ReadInt32(); VtableStart = reader.ReadInt32(); InterfaceOffsetsStart = reader.ReadInt32(); MethodCount = reader.ReadUInt16(); PropertyCount = reader.ReadUInt16(); FieldCount = reader.ReadUInt16(); EventCount = reader.ReadUInt16(); NestedTypeCount = reader.ReadUInt16(); VtableCount = reader.ReadUInt16(); InterfacesCount = reader.ReadUInt16(); InterfaceOffsetsCount = reader.ReadUInt16(); Bitfield = reader.ReadUInt32(); Token = reader.ReadUInt32(); } } } namespace LibCpp2IL.MachO { public enum LoadCommandId : uint { LC_SEGMENT = 1u, LC_SYMTAB = 2u, LC_SYMSEG = 3u, LC_THREAD = 4u, LC_UNIXTHREAD = 5u, LC_LOADFVMLIB = 6u, LC_IDFVMLIB = 7u, LC_IDENT = 8u, LC_FVMFILE = 9u, LC_PREPAGE = 10u, LC_DYSYMTAB = 11u, LC_LOAD_DYLIB = 12u, LC_ID_DYLIB = 13u, LC_LOAD_DYLINKER = 14u, LC_ID_DYLINKER = 15u, LC_PREBOUND_DYLIB = 16u, LC_ROUTINES = 17u, LC_SUB_FRAMEWORK = 18u, LC_SUB_UMBRELLA = 19u, LC_SUB_CLIENT = 20u, LC_SUB_LIBRARY = 21u, LC_TWOLEVEL_HINTS = 22u, LC_PREBIND_CKSUM = 23u, LC_LOAD_WEAK_DYLIB = 2147483672u, LC_SEGMENT_64 = 25u, LC_ROUTINES_64 = 26u, LC_UUID = 27u, LC_RPATH = 2147483676u, LC_CODE_SIGNATURE = 29u, LC_SEGMENT_SPLIT_INFO = 30u, LC_REEXPORT_DYLIB = 2147483679u, LC_LAZY_LOAD_DYLIB = 32u, LC_ENCRYPTION_INFO = 33u, LC_DYLD_INFO = 34u, LC_DYLD_INFO_ONLY = 2147483682u, LC_LOAD_UPWARD_DYLIB = 2147483683u, LC_VERSION_MIN_MACOSX = 36u, LC_VERSION_MIN_IPHONEOS = 37u, LC_FUNCTION_STARTS = 38u, LC_DYLD_ENVIRONMENT = 39u, LC_MAIN = 2147483688u, LC_DATA_IN_CODE = 41u, LC_SOURCE_VERSION = 42u, LC_DYLIB_CODE_SIGN_DRS = 43u, LC_ENCRYPTION_INFO_64 = 44u, LC_LINKER_OPTION = 45u, LC_LINKER_OPTIMIZATION_HINT = 46u, LC_VERSION_MIN_TVOS = 47u, LC_VERSION_MIN_WATCHOS = 48u, LC_NOTE = 49u, LC_BUILD_VERSION = 50u, LC_DYLD_EXPORTS_TRIE = 2147483699u, LC_DYLD_CHAINED_FIXUPS = 2147483700u, LC_LOAD_WEAK_DYLIB_UUID_REEXPORT_DYLIB = 53u, LC_UUID_DYLIB_CODE_SIGN_DRS = 54u, LC_DYLD_CHAINED_FIXUPS_COUNT = 55u, LC_FUNCTION_STARTS_64 = 56u, LC_DYLD_DEBUG = 57u, LC_DYLD_CHAINED_FRAMEWORK = 58u, LC_DYLD_CHAINED_DYLIB = 59u, LC_LOAD_DYLINKER_WITH_LIBRARY_PATH = 60u } public enum MachOCpuSubtype { CPU_SUBTYPE_ANY = 100, CPU_SUBTYPE_ARM64_ALL = 0, CPU_SUBTYPE_ARM64_V8 = 1, CPU_SUBTYPE_ARM64E = 2 } public enum MachOCpuType { CPU_TYPE_ANY = -1, CPU_TYPE_VAX = 1, CPU_TYPE_MC680x0 = 6, CPU_TYPE_I386 = 7, CPU_TYPE_X86_64 = 16777223, CPU_TYPE_MIPS = 8, CPU_TYPE_MC98000 = 10, CPU_TYPE_HPPA = 11, CPU_TYPE_ARM = 12, CPU_TYPE_ARM64 = 16777228, CPU_TYPE_ARM64_32 = 33554444, CPU_TYPE_MC88000 = 13, CPU_TYPE_SPARC = 14, CPU_TYPE_I860 = 15, CPU_TYPE_ALPHA = 16, CPU_TYPE_POWERPC = 18, CPU_TYPE_POWERPC64 = 16777234, CPU_ARCH_ABI64 = 16777216, CPU_ARCH_ABI64_32 = 33554432 } public class MachODynamicLinkerCommand : ReadableClass { public int RebaseOffset; public int RebaseSize; public int BindOffset; public int BindSize; public int WeakBindOffset; public int WeakBindSize; public int LazyBindOffset; public int LazyBindSize; public int ExportOffset; public int ExportSize; public MachOExportEntry[] Exports = Array.Empty(); public override void Read(ClassReadingBinaryReader reader) { RebaseOffset = reader.ReadInt32(); RebaseSize = reader.ReadInt32(); BindOffset = reader.ReadInt32(); BindSize = reader.ReadInt32(); WeakBindOffset = reader.ReadInt32(); WeakBindSize = reader.ReadInt32(); LazyBindOffset = reader.ReadInt32(); LazyBindSize = reader.ReadInt32(); ExportOffset = reader.ReadInt32(); ExportSize = reader.ReadInt32(); long position = reader.BaseStream.Position; reader.BaseStream.Position = ExportOffset; MachOExportTrie machOExportTrie = new MachOExportTrie(reader); Exports = machOExportTrie.Entries.ToArray(); reader.BaseStream.Position = position; } } public class MachOExportEntry { public string Name; public long Address; public long Flags; public long Other; public string? ImportName; public MachOExportEntry(string name, long address, long flags, long other, string? importName) { Name = name; Address = address; Flags = flags; Other = other; ImportName = importName; } } public class MachOExportTrie { [Flags] private enum ExportFlags { KindRegular = 0, KindThreadLocal = 1, KindAbsolute = 2, WeakDefinition = 4, ReExport = 8, StubAndResolver = 0x10 } private struct Node { public string Name; public int Offset; } public List Entries = new List(); private ClassReadingBinaryReader _reader; private long _basePtr; public MachOExportTrie(ClassReadingBinaryReader reader) { _reader = reader; _basePtr = reader.BaseStream.Position; List list = ParseNode("", 0); while (list.Count > 0) { Node node = list[0]; list.RemoveAt(0); list.AddRange(ParseNode(node.Name, node.Offset)); } } private List ParseNode(string name, int offset) { List list = new List(); _reader.BaseStream.Position = _basePtr + offset; ulong num = _reader.BaseStream.ReadLEB128Unsigned(); long position = _reader.BaseStream.Position + (long)num; if (num != 0L) { ExportFlags exportFlags = (ExportFlags)_reader.BaseStream.ReadLEB128Unsigned(); long address = 0L; long other = 0L; string importName = null; if ((exportFlags & ExportFlags.ReExport) != 0) { other = _reader.BaseStream.ReadLEB128Signed(); importName = _reader.ReadStringToNullAtCurrentPos(); } else { address = _reader.BaseStream.ReadLEB128Signed(); if ((exportFlags & ExportFlags.StubAndResolver) != 0) { other = _reader.BaseStream.ReadLEB128Signed(); } } Entries.Add(new MachOExportEntry(name, address, (long)exportFlags, other, importName)); } _reader.BaseStream.Position = position; ulong num2 = _reader.BaseStream.ReadLEB128Unsigned(); for (ulong num3 = 0uL; num3 < num2; num3++) { string text = _reader.ReadStringToNullAtCurrentPos(); ulong num4 = _reader.BaseStream.ReadLEB128Unsigned(); list.Add(new Node { Name = name + text, Offset = (int)num4 }); } return list; } } public class MachOFile : Il2CppBinary { private byte[] _raw; private readonly MachOHeader _header; private readonly MachOLoadCommand[] _loadCommands; private readonly MachOSegmentCommand[] Segments64; private readonly MachOSection[] Sections64; private Dictionary _exportsDict; public override long RawLength => _raw.Length; public MachOFile(MemoryStream input) : base(input) { _raw = input.GetBuffer(); LibLogger.Verbose("\tReading Mach-O header..."); _header = ReadReadable(-1L); switch (_header.Magic) { case 4277009102u: LibLogger.Verbose("Mach-O is 32-bit..."); is32Bit = true; break; case 4277009103u: LibLogger.Verbose("Mach-O is 64-bit..."); is32Bit = false; break; default: throw new Exception($"Unknown Mach-O Magic: {_header.Magic}"); } switch (_header.CpuType) { case MachOCpuType.CPU_TYPE_I386: LibLogger.VerboseNewline("Mach-O contains x86_32 instructions."); InstructionSetId = DefaultInstructionSets.X86_32; break; case MachOCpuType.CPU_TYPE_X86_64: LibLogger.VerboseNewline("Mach-O contains x86_64 instructions."); InstructionSetId = DefaultInstructionSets.X86_64; break; case MachOCpuType.CPU_TYPE_ARM: LibLogger.VerboseNewline("Mach-O contains ARM (32-bit) instructions."); InstructionSetId = DefaultInstructionSets.ARM_V7; break; case MachOCpuType.CPU_TYPE_ARM64: LibLogger.VerboseNewline("Mach-O contains ARM64 instructions."); InstructionSetId = DefaultInstructionSets.ARM_V8; break; default: throw new Exception($"Don't know how to handle a Mach-O CPU Type of {_header.CpuType}"); } if (_header.Magic == 4277009102u) { LibLogger.ErrorNewline("32-bit MACH-O files have not been tested! Please report any issues."); } else { LibLogger.WarnNewline("Mach-O Support is experimental. Please open an issue if anything seems incorrect."); } LibLogger.Verbose("\tReading Mach-O load commands..."); _loadCommands = ReadReadableArrayAtRawAddr(-1L, _header.NumLoadCommands); LibLogger.VerboseNewline($"Read {_loadCommands.Length} load commands."); Segments64 = (from c in _loadCommands where c.Command == LoadCommandId.LC_SEGMENT_64 select c.CommandData).Cast().ToArray(); Sections64 = Segments64.SelectMany((MachOSegmentCommand s) => s.Sections).ToArray(); _exportsDict = ((_loadCommands.FirstOrDefault(delegate(MachOLoadCommand c) { LoadCommandId command = c.Command; return command == LoadCommandId.LC_DYLD_INFO || command == LoadCommandId.LC_DYLD_INFO_ONLY; })?.CommandData as MachODynamicLinkerCommand)?.Exports ?? Array.Empty()).ToDictionary(delegate(MachOExportEntry e) { string name = e.Name; return name.Substring(1, name.Length - 1); }, (MachOExportEntry e) => e.Address); LibLogger.VerboseNewline($"Found {_exportsDict.Count} exports in the DYLD info load command."); LibLogger.VerboseNewline($"\tMach-O contains {Segments64.Length} segments, split into {Sections64.Length} sections."); } public override byte GetByteAtRawAddress(ulong addr) { return _raw[addr]; } public override long MapVirtualAddressToRaw(ulong uiAddr, bool throwOnError = true) { MachOSection machOSection = Sections64.FirstOrDefault((MachOSection s) => s.Address <= uiAddr && uiAddr < s.Address + s.Size); if (machOSection == null) { if (throwOnError) { throw new Exception($"Could not find section for virtual address 0x{uiAddr:X}. Lowest section address is 0x{Sections64.Min((MachOSection s) => s.Address):X}, highest section address is 0x{Sections64.Max((MachOSection s) => s.Address + s.Size):X}"); } return -9223372036854774808L; } return (long)(machOSection.Offset + (uiAddr - machOSection.Address)); } public override ulong MapRawAddressToVirtual(uint offset) { MachOSection machOSection = Sections64.FirstOrDefault((MachOSection s) => s.Offset <= offset && offset < s.Offset + s.Size); if (machOSection == null) { throw new Exception($"Could not find section for raw address 0x{offset:X}"); } return machOSection.Address + (offset - machOSection.Offset); } public override ulong GetRva(ulong pointer) { return pointer; } public override byte[] GetRawBinaryContent() { return _raw; } public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { if (!_exportsDict.TryGetValue(toFind, out var value)) { return 0uL; } return (ulong)value; } private MachOSection GetTextSection64() { return Sections64.FirstOrDefault((MachOSection s) => s.SectionName == "__text") ?? throw new Exception("Could not find __text section"); } public override byte[] GetEntirePrimaryExecutableSection() { MachOSection textSection = GetTextSection64(); return _raw.SubArray((int)textSection.Offset, (int)textSection.Size); } public override ulong GetVirtualAddressOfPrimaryExecutableSection() { return GetTextSection64().Address; } } public enum MachOFileType : uint { MH_OBJECT = 1u, MH_EXECUTE = 2u, MH_DYLIB = 6u, MH_BUNDLE = 8u, MH_DSYM = 10u, MH_KEXT_BUNDLE = 11u, MH_APP_EXTENSION_SAFE = 33554432u, MY_SIM_SUPPORT = 134217728u, MH_DYLIB_IN_CACHE = 2147483648u } public class MachOHeader : ReadableClass { public const uint MAGIC_32_BIT = 4277009102u; public const uint MAGIC_64_BIT = 4277009103u; public uint Magic; public MachOCpuType CpuType; public MachOCpuSubtype CpuSubtype; public MachOFileType FileType; public uint NumLoadCommands; public uint SizeOfLoadCommands; public MachOHeaderFlags Flags; public uint Reserved; public override void Read(ClassReadingBinaryReader reader) { Magic = reader.ReadUInt32(); CpuType = (MachOCpuType)reader.ReadUInt32(); CpuSubtype = (MachOCpuSubtype)reader.ReadUInt32(); FileType = (MachOFileType)reader.ReadUInt32(); NumLoadCommands = reader.ReadUInt32(); SizeOfLoadCommands = reader.ReadUInt32(); Flags = (MachOHeaderFlags)reader.ReadUInt32(); if (Magic == 4277009103u) { Reserved = reader.ReadUInt32(); } } } [Flags] public enum MachOHeaderFlags { MH_NO_UNDEFINED_SYMBOLS = 1, MH_INCREMENTAL_LINK = 2, MH_DYLDLINK = 4, MH_BINDATLOAD = 8, MH_PREBOUND = 0x10, MH_SPLIT_SEGS = 0x20, MH_LAZY_INIT = 0x40, MH_TWOLEVEL = 0x80, MH_FORCE_FLAT = 0x100, MH_NOMULTIDEFS = 0x200, MH_NOFIXPREBINDING = 0x400, MH_PREBINDABLE = 0x800, MH_ALLMODSBOUND = 0x1000, MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000, MH_CANONICAL = 0x4000, MH_WEAK_DEFINES = 0x8000, MH_BINDS_TO_WEAK = 0x10000, MH_ALLOW_STACK_EXECUTION = 0x20000, MH_ROOT_SAFE = 0x40000, MH_SETUID_SAFE = 0x80000, MH_NO_REEXPORTED_DYLIBS = 0x100000, MH_PIE = 0x200000, MH_DEAD_STRIPPABLE_DYLIB = 0x400000, MH_HAS_TLV_DESCRIPTORS = 0x800000, MH_NO_HEAP_EXECUTION = 0x1000000, MH_APP_EXTENSION_SAFE = 0x2000000, MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x4000000, MH_SIM_SUPPORT = 0x8000000 } public class MachOLoadCommand : ReadableClass { public LoadCommandId Command; public uint CommandSize; public ReadableClass? CommandData; public byte[]? UnknownCommandData; public string? UnknownDataAsString { get { if (UnknownCommandData != null) { return Encoding.UTF8.GetString(UnknownCommandData); } return null; } } public override void Read(ClassReadingBinaryReader reader) { Command = (LoadCommandId)reader.ReadUInt32(); CommandSize = reader.ReadUInt32(); switch (Command) { case LoadCommandId.LC_SEGMENT: case LoadCommandId.LC_SEGMENT_64: CommandData = reader.ReadReadableHereNoLock(); break; case LoadCommandId.LC_SYMTAB: CommandData = reader.ReadReadableHereNoLock(); break; case LoadCommandId.LC_DYLD_INFO_ONLY: case LoadCommandId.LC_DYLD_INFO: CommandData = reader.ReadReadableHereNoLock(); break; default: UnknownCommandData = reader.ReadByteArrayAtRawAddressNoLock(-1L, (int)(CommandSize - 8)); break; } } } public class MachOSection : ReadableClass { public string SectionName = "INVALID"; public string ContainingSegmentName = "INVALID"; public ulong Address; public ulong Size; public uint Offset; public uint Alignment; public uint RelocationOffset; public uint NumberOfRelocations; public MachOSectionFlags Flags; public uint Reserved1; public uint Reserved2; public uint Reserved3; public override void Read(ClassReadingBinaryReader reader) { SectionName = Encoding.UTF8.GetString(reader.ReadByteArrayAtRawAddressNoLock(-1L, 16)).TrimEnd('\0'); ContainingSegmentName = Encoding.UTF8.GetString(reader.ReadByteArrayAtRawAddressNoLock(-1L, 16)).TrimEnd('\0'); Address = reader.ReadNUint(); Size = reader.ReadNUint(); Offset = reader.ReadUInt32(); Alignment = reader.ReadUInt32(); RelocationOffset = reader.ReadUInt32(); NumberOfRelocations = reader.ReadUInt32(); Flags = (MachOSectionFlags)reader.ReadUInt32(); Reserved1 = reader.ReadUInt32(); Reserved2 = reader.ReadUInt32(); if (!reader.is32Bit) { Reserved3 = reader.ReadUInt32(); } } } [Flags] public enum MachOSectionFlags : uint { TYPE_BITMASK = 0xFFu, ATTRIBUTES_BITMASK = 0xFFFFF00u, USER_ATTRIBUTES_BITMASK = 0xFF000000u, SYSTEM_ATTRIBUTES_BITMASK = 0xFFFF00u, TYPE_REGULAR = 0u, TYPE_ZEROFILL = 1u, TYPE_CSTRING_LITERALS = 2u, TYPE_4BYTE_LITERALS = 3u, TYPE_8BYTE_LITERALS = 4u, TYPE_LITERAL_POINTERS = 5u, TYPE_NON_LAZY_SYMBOL_POINTERS = 6u, TYPE_LAZY_SYMBOL_POINTERS = 7u, TYPE_SYMBOL_STUBS = 8u, TYPE_MOD_INIT_FUNC_POINTERS = 9u, TYPE_MOD_TERM_FUNC_POINTERS = 0xAu, TYPE_COALESCED = 0xBu, TYPE_GB_ZEROFILL = 0xCu, TYPE_INTERPOSING = 0xDu, TYPE_16BYTE_LITERALS = 0xEu, TYPE_DTRACE_DOF = 0xFu, TYPE_LAZY_DYLIB_SYMBOL_POINTERS = 0x10u, TYPE_THREAD_LOCAL_REGULAR = 0x11u, TYPE_THREAD_LOCAL_ZEROFILL = 0x12u, TYPE_THREAD_LOCAL_VARIABLES = 0x13u, TYPE_THREAD_LOCAL_VARIABLE_POINTERS = 0x14u, TYPE_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15u, TYPE_INIT_FUNC_OFFSETS = 0x16u, ATTR_PURE_INSTRUCTIONS = 0x80000000u, ATTR_NO_TOC = 0x40000000u, ATTR_STRIP_STATIC_SYMS = 0x20000000u, ATTR_NO_DEAD_STRIP = 0x10000000u, ATTR_LIVE_SUPPORT = 0x8000000u, ATTR_SELF_MODIFYING_CODE = 0x4000000u, ATTR_DEBUG = 0x2000000u, ATTR_SOME_INSTRUCTIONS = 0x400u, ATTR_EXT_RELOC = 0x200u, ATTR_LOC_RELOC = 0x100u } public class MachOSegmentCommand : ReadableClass { public string SegmentName = "INVALID"; public ulong VirtualAddress; public ulong VirtualSize; public ulong FileOffset; public ulong FileSize; public MachOVmProtection MaxProtection; public MachOVmProtection InitialProtection; public uint NumSections; public MachOSegmentFlags Flags; public MachOSection[] Sections = Array.Empty(); public override void Read(ClassReadingBinaryReader reader) { SegmentName = Encoding.UTF8.GetString(reader.ReadByteArrayAtRawAddressNoLock(-1L, 16)).TrimEnd('\0'); VirtualAddress = reader.ReadNUint(); VirtualSize = reader.ReadNUint(); FileOffset = reader.ReadNUint(); FileSize = reader.ReadNUint(); MaxProtection = (MachOVmProtection)reader.ReadInt32(); InitialProtection = (MachOVmProtection)reader.ReadInt32(); NumSections = reader.ReadUInt32(); Flags = (MachOSegmentFlags)reader.ReadUInt32(); Sections = new MachOSection[NumSections]; for (int i = 0; i < NumSections; i++) { Sections[i] = reader.ReadReadableHereNoLock(); } } } [Flags] public enum MachOSegmentFlags { SG_NONE = 0, SG_HIGHVM = 1, SG_FVMLIB = 2, SG_NORELOC = 4, SG_PROTECTED_VERSION_1 = 8 } public class MachOSymtabCommand : ReadableClass { public uint SymbolTableOffset; public uint NumSymbols; public uint StringTableOffset; public uint StringTableSize; public MachOSymtabEntry[] Symbols = Array.Empty(); public override void Read(ClassReadingBinaryReader reader) { SymbolTableOffset = reader.ReadUInt32(); NumSymbols = reader.ReadUInt32(); StringTableOffset = reader.ReadUInt32(); StringTableSize = reader.ReadUInt32(); long position = reader.BaseStream.Position; reader.BaseStream.Position = SymbolTableOffset; Symbols = new MachOSymtabEntry[NumSymbols]; for (int i = 0; i < NumSymbols; i++) { Symbols[i] = new MachOSymtabEntry(); Symbols[i].Read(reader, this); } reader.BaseStream.Position = position; } } public class MachOSymtabEntry { public uint NameOffset; public byte Type; public byte Section; public ushort Description; public ulong Value; public string Name; public bool IsExternal => (Type & 1) == 1; public bool IsSymbolicDebugging => (Type & 0xE0) != 0; public bool IsPrivateExternal => (Type & 0x10) == 16; private byte TypeBits => (byte)(Type & 0xEu); public bool IsTypeUndefined { get { if (Section == 0) { return TypeBits == 0; } return false; } } public bool IsTypeAbsolute { get { if (Section == 0) { return TypeBits == 2; } return false; } } public bool IsTypePreboundUndefined { get { if (Section == 0) { return TypeBits == 12; } return false; } } public bool IsTypeIndirect { get { if (Section == 0) { return TypeBits == 10; } return false; } } public bool IsTypeSection => TypeBits == 14; public void Read(ClassReadingBinaryReader reader, MachOSymtabCommand machOSymtabCommand) { NameOffset = reader.ReadUInt32(); Type = reader.ReadByte(); Section = reader.ReadByte(); Description = reader.ReadUInt16(); Value = reader.ReadNUint(); long position = reader.BaseStream.Position; Name = reader.ReadStringToNullNoLock(machOSymtabCommand.StringTableOffset + NameOffset); reader.BaseStream.Position = position; } } [Flags] public enum MachOVmProtection { PROT_NONE = 0, PROT_READ = 1, PROT_WRITE = 2, PROT_EXEC = 4, PROT_NO_CHANGE = 8, PROT_COPY = 0x10, PROT_TRUSTED = 0x20, PROT_IS_MASK = 0x40, PROT_STRIP_READ = 0x80, PROT_COPY_FAIL_IF_EXECUTABLE = 0x100 } } namespace LibCpp2IL.Logging { public class DefaultWriter : LogWriter { public override void Info(string message) { Console.Write(message); } public override void Warn(string message) { Console.Write(message); } public override void Error(string message) { Console.Write(message); } public override void Verbose(string message) { Console.Write(message); } } public static class LibLogger { public static LogWriter Writer = new DefaultWriter(); public static bool ShowVerbose { private get; set; } = true; internal static void InfoNewline(string message) { Info(message + Environment.NewLine); } internal static void Info(string message) { Writer.Info(message); } internal static void WarnNewline(string message) { Warn(message + Environment.NewLine); } internal static void Warn(string message) { Writer.Warn(message); } internal static void ErrorNewline(string message) { Error(message + Environment.NewLine); } internal static void Error(string message) { Writer.Error(message); } internal static void VerboseNewline(string message) { Verbose(message + Environment.NewLine); } internal static void Verbose(string message) { if (ShowVerbose) { Writer.Verbose(message); } } } public abstract class LogWriter { public abstract void Info(string message); public abstract void Warn(string message); public abstract void Error(string message); public abstract void Verbose(string message); } } namespace LibCpp2IL.Elf { public class ElfDynamicEntry : ReadableClass { public ElfDynamicType Tag; public ulong Value; public override void Read(ClassReadingBinaryReader reader) { Tag = (ElfDynamicType)reader.ReadNInt(); Value = reader.ReadNUint(); } } public class ElfDynamicSymbol32 : ReadableClass, IElfDynamicSymbol { private uint _internalNameIndex; private uint _internalValue; private uint _internalSize; private byte _internalInfo; private byte _internalOther; private ushort _internalShndx; public uint NameOffset => _internalNameIndex; public ulong Value => _internalValue; public ulong Size => _internalSize; public byte Info => _internalInfo; public byte Other => _internalOther; public ushort Shndx => _internalShndx; public ElfDynamicSymbolType Type => (ElfDynamicSymbolType)(Info & 0xFu); public override void Read(ClassReadingBinaryReader reader) { _internalNameIndex = reader.ReadUInt32(); _internalValue = reader.ReadUInt32(); _internalSize = reader.ReadUInt32(); _internalInfo = reader.ReadByte(); _internalOther = reader.ReadByte(); _internalShndx = reader.ReadUInt16(); } } public class ElfDynamicSymbol64 : ReadableClass, IElfDynamicSymbol { private uint _internalNameIndex; private byte _internalInfo; private byte _internalOther; private ushort _internalShndx; private ulong _internalValue; private ulong _internalSize; public uint NameOffset => _internalNameIndex; public ulong Value => _internalValue; public ulong Size => _internalSize; public byte Info => _internalInfo; public byte Other => _internalOther; public ushort Shndx => _internalShndx; public ElfDynamicSymbolType Type => (ElfDynamicSymbolType)(Info & 0xFu); public override void Read(ClassReadingBinaryReader reader) { _internalNameIndex = reader.ReadUInt32(); _internalInfo = reader.ReadByte(); _internalOther = reader.ReadByte(); _internalShndx = reader.ReadUInt16(); _internalValue = reader.ReadUInt64(); _internalSize = reader.ReadUInt64(); } } public enum ElfDynamicSymbolType : byte { STT_NOTYPE = 0, STT_OBJECT = 1, STT_FUNC = 2, STT_SECTION = 3, STT_FILE = 4, STT_COMMON = 5, STT_LOOS = 10, STT_HIOS = 12, STT_LOPROC = 13, STT_SPARC_REGISTER = 13, STT_HIPROC = 15 } public enum ElfDynamicType : long { DT_NULL = 0L, DT_NEEDED = 1L, DT_PLTRELSZ = 2L, DT_PLTGOT = 3L, DT_HASH = 4L, DT_STRTAB = 5L, DT_SYMTAB = 6L, DT_RELA = 7L, DT_RELASZ = 8L, DT_RELAENT = 9L, DT_STRSZ = 10L, DT_SYMENT = 11L, DT_INIT = 12L, DT_FINI = 13L, DT_REL = 17L, DT_RELSZ = 18L, DT_RELENT = 19L, DT_PLTREL = 20L, DT_DEBUG = 21L, DT_TEXTREL = 22L, DT_JMPREL = 23L, DT_BIND_NOW = 24L, DT_INIT_ARRAY = 25L, DT_FINI_ARRAY = 26L, DT_INIT_ARRAYSZ = 27L, DT_FINI_ARRAYSZ = 28L, DT_RUNPATH = 29L, DT_FLAGS = 30L, DT_PREINIT_ARRAY = 32L, DT_PREINIT_ARRAYSZ = 33L } public sealed class ElfFile : Il2CppBinary { private byte[] _raw; private List _elfProgramHeaderEntries; private readonly List _elfSectionHeaderEntries; private ElfFileIdent? _elfFileIdent; private ElfFileHeader? _elfHeader; private readonly List _dynamicSection = new List(); private readonly List _symbolTable = new List(); private readonly Dictionary _exportTable = new Dictionary(); private List? _initializerPointers; private readonly List<(ulong start, ulong end)> relocationBlocks = new List<(ulong, ulong)>(); private long _globalOffset; public override long RawLength => _raw.Length; public ElfFile(MemoryStream input) : base(input) { _raw = input.GetBuffer(); LibLogger.Verbose("\tReading Elf File Ident..."); DateTime now = DateTime.Now; ReadAndValidateIdent(); bool flag = _elfFileIdent.Endianness == 2; LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.VerboseNewline($"\tBinary is {(is32Bit ? "32-bit" : "64-bit")} and {(flag ? "big-endian" : "little-endian")}."); if (flag) { SetBigEndian(); } LibLogger.Verbose("\tReading and validating full ELF header..."); now = DateTime.Now; ReadHeader(); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.VerboseNewline($"\tElf File contains instructions of type {InstructionSetId}"); LibLogger.Verbose("\tReading ELF program header table..."); now = DateTime.Now; ReadProgramHeaderTable(); LibLogger.VerboseNewline($"Read {_elfProgramHeaderEntries.Count} OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.VerboseNewline("\tReading ELF section header table and names..."); now = DateTime.Now; try { _elfSectionHeaderEntries = ReadReadableArrayAtRawAddr(_elfHeader.pSectionHeader, _elfHeader.SectionHeaderEntryCount).ToList(); } catch (Exception) { _elfSectionHeaderEntries = new List(); } if (_elfHeader.SectionNameSectionOffset >= 0 && _elfHeader.SectionNameSectionOffset < _elfSectionHeaderEntries.Count) { ulong rawAddress = _elfSectionHeaderEntries[_elfHeader.SectionNameSectionOffset].RawAddress; foreach (ElfSectionHeaderEntry elfSectionHeaderEntry2 in _elfSectionHeaderEntries) { elfSectionHeaderEntry2.Name = ReadStringToNull(rawAddress + elfSectionHeaderEntry2.NameOffset); LibLogger.VerboseNewline($"\t\t-Name for section at 0x{elfSectionHeaderEntry2.RawAddress:X} is {elfSectionHeaderEntry2.Name}"); } } LibLogger.VerboseNewline($"\tRead {_elfSectionHeaderEntries.Count} OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); ElfSectionHeaderEntry elfSectionHeaderEntry = _elfSectionHeaderEntries.FirstOrDefault((ElfSectionHeaderEntry s) => s.Name == ".text"); if (elfSectionHeaderEntry != null) { _globalOffset = (long)(elfSectionHeaderEntry.VirtualAddress - elfSectionHeaderEntry.RawAddress); } else { IElfProgramHeaderEntry elfProgramHeaderEntry = _elfProgramHeaderEntries.First((IElfProgramHeaderEntry p) => (p.Flags & ElfProgramHeaderFlags.PF_X) != 0); _globalOffset = (long)(elfProgramHeaderEntry.VirtualAddress - elfProgramHeaderEntry.RawAddress); } LibLogger.VerboseNewline($"\tELF global offset is 0x{_globalOffset:X}"); IElfProgramHeaderEntry programHeaderOfType = GetProgramHeaderOfType(ElfProgramEntryType.PT_DYNAMIC); if (programHeaderOfType != null) { _dynamicSection = ReadReadableArrayAtRawAddr((long)programHeaderOfType.RawAddress, (int)programHeaderOfType.RawSize / (is32Bit ? 8 : 16)).ToList(); } LibLogger.VerboseNewline("\tFinding Relocations..."); now = DateTime.Now; ProcessRelocations(); LibLogger.VerboseNewline($"OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.VerboseNewline("\tProcessing Symbols..."); now = DateTime.Now; ProcessSymbols(); LibLogger.VerboseNewline($"\tOK ({(DateTime.Now - now).TotalMilliseconds} ms)"); LibLogger.Verbose("\tProcessing Initializers..."); now = DateTime.Now; ProcessInitializers(); LibLogger.VerboseNewline($"Got {_initializerPointers.Count} OK ({(DateTime.Now - now).TotalMilliseconds} ms)"); } private void ReadAndValidateIdent() { _elfFileIdent = ReadReadable(0L); if (_elfFileIdent.Magic != 1179403647) { throw new FormatException("ERROR: Magic number mismatch."); } if (_elfFileIdent.Architecture == 1) { is32Bit = true; } else if (_elfFileIdent.Architecture != 2) { throw new FormatException($"Invalid arch number (expecting 1 or 2): {_elfFileIdent.Architecture}"); } if (_elfFileIdent.Version != 1) { throw new FormatException($"ELF Version is not 1? File header has version {_elfFileIdent.Version}"); } } private void ReadHeader() { _elfHeader = ReadReadable(16L); InstructionSetId = _elfHeader.Machine switch { 3 => DefaultInstructionSets.X86_32, 62 => DefaultInstructionSets.X86_64, 40 => DefaultInstructionSets.ARM_V7, 183 => DefaultInstructionSets.ARM_V8, _ => throw new NotImplementedException($"ELF Machine {_elfHeader.Machine} not implemented"), }; if (_elfHeader.Version != 1) { throw new FormatException($"Full ELF header specifies version {_elfHeader.Version}, only supported version is 1."); } } private void ReadProgramHeaderTable() { _elfProgramHeaderEntries = (is32Bit ? ReadReadableArrayAtRawAddr(_elfHeader.pProgramHeader, _elfHeader.ProgramHeaderEntryCount).Cast().ToList() : ReadReadableArrayAtRawAddr(_elfHeader.pProgramHeader, _elfHeader.ProgramHeaderEntryCount).Cast().ToList()); } private IElfProgramHeaderEntry? GetProgramHeaderOfType(ElfProgramEntryType type) { return _elfProgramHeaderEntries.FirstOrDefault((IElfProgramHeaderEntry p) => p.Type == type); } private IEnumerable GetSections(ElfSectionEntryType type) { return _elfSectionHeaderEntries.Where((ElfSectionHeaderEntry s) => s.Type == type); } private ElfSectionHeaderEntry? GetSingleSection(ElfSectionEntryType type) { return GetSections(type).FirstOrDefault(); } private ElfDynamicEntry? GetDynamicEntryOfType(ElfDynamicType type) { return _dynamicSection.FirstOrDefault((ElfDynamicEntry d) => d.Tag == type); } private void ProcessRelocations() { try { HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (ElfSectionHeaderEntry section in GetSections(ElfSectionEntryType.SHT_REL)) { ulong relatedTablePointer2 = _elfSectionHeaderEntries[section.LinkedSectionIndex].RawAddress; ElfRelEntry[] array = ReadReadableArrayAtRawAddr((long)section.RawAddress, (long)(section.Size / (ulong)section.EntrySize)); LibLogger.VerboseNewline($"\t\t-Got {array.Length} from REL section {section.Name}"); relocationBlocks.Add((section.RawAddress, section.RawAddress + section.Size)); hashSet2.Add(section.RawAddress); hashSet.UnionWith(array.Select((ElfRelEntry r) => new ElfRelocation(this, r, relatedTablePointer2))); } foreach (ElfSectionHeaderEntry section2 in GetSections(ElfSectionEntryType.SHT_RELA)) { if (hashSet2.Contains(section2.RawAddress)) { LibLogger.VerboseNewline($"\t\t-Ignoring RELA section starting at 0x{section2.RawAddress} because it's already been processed."); continue; } ulong relatedTablePointer = _elfSectionHeaderEntries[section2.LinkedSectionIndex].RawAddress; ElfRelaEntry[] array2 = ReadReadableArrayAtRawAddr((long)section2.RawAddress, (long)(section2.Size / (ulong)section2.EntrySize)); LibLogger.VerboseNewline($"\t\t-Got {array2.Length} from RELA section {section2.Name} at 0x{section2.RawAddress}"); relocationBlocks.Add((section2.RawAddress, section2.RawAddress + section2.Size)); hashSet2.Add(section2.RawAddress); hashSet.UnionWith(array2.Select((ElfRelaEntry r) => new ElfRelocation(this, r, relatedTablePointer))); } ElfDynamicEntry dynamicEntryOfType = GetDynamicEntryOfType(ElfDynamicType.DT_REL); if (dynamicEntryOfType != null) { uint num = (uint)MapVirtualAddressToRaw(dynamicEntryOfType.Value); if (!hashSet2.Contains(num)) { ulong value = GetDynamicEntryOfType(ElfDynamicType.DT_RELSZ).Value; int num2 = (int)(value / GetDynamicEntryOfType(ElfDynamicType.DT_RELENT).Value); ElfRelEntry[] array3 = ReadReadableArrayAtRawAddr(num, num2); LibLogger.VerboseNewline($"\t\t-Got {array3.Length} from dynamic REL section at 0x{num}"); ulong pSymTab2 = GetDynamicEntryOfType(ElfDynamicType.DT_SYMTAB).Value; relocationBlocks.Add((num, num + value)); hashSet.UnionWith(array3.Select((ElfRelEntry r) => new ElfRelocation(this, r, pSymTab2))); } else { LibLogger.VerboseNewline($"\t\t-Ignoring dynamic REL section starting at 0x{num} because it's already been processed."); } } ElfDynamicEntry dynamicEntryOfType2 = GetDynamicEntryOfType(ElfDynamicType.DT_RELA); if (dynamicEntryOfType2 != null) { ulong value2 = GetDynamicEntryOfType(ElfDynamicType.DT_RELASZ).Value; int num3 = (int)(value2 / GetDynamicEntryOfType(ElfDynamicType.DT_RELAENT).Value); uint num4 = (uint)MapVirtualAddressToRaw(dynamicEntryOfType2.Value); if (!hashSet2.Contains(num4)) { ElfRelaEntry[] array4 = ReadReadableArrayAtRawAddr(num4, num3); LibLogger.VerboseNewline($"\t\t-Got {array4.Length} from dynamic RELA section at 0x{num4}"); ulong pSymTab = GetDynamicEntryOfType(ElfDynamicType.DT_SYMTAB).Value; relocationBlocks.Add((num4, num4 + value2)); hashSet.UnionWith(array4.Select((ElfRelaEntry r) => new ElfRelocation(this, r, pSymTab))); } else { LibLogger.VerboseNewline($"\t\t-Ignoring dynamic RELA section starting at 0x{num4} because it's already been processed."); } } ulong num5 = (ulong)(is32Bit ? LibCpp2ILUtils.VersionAwareSizeOf(typeof(ElfDynamicSymbol32), dontCheckVersionAttributes: true, downsize: false) : LibCpp2ILUtils.VersionAwareSizeOf(typeof(ElfDynamicSymbol64), dontCheckVersionAttributes: true, downsize: false)); LibLogger.Verbose($"\t-Now Processing {hashSet.Count} relocations..."); foreach (ElfRelocation item in hashSet) { ulong num6 = item.pRelatedSymbolTable + item.IndexInSymbolTable * num5; ulong value3; try { IElfDynamicSymbol elfDynamicSymbol2; if (!is32Bit) { IElfDynamicSymbol elfDynamicSymbol = ReadReadable((long)num6); elfDynamicSymbol2 = elfDynamicSymbol; } else { IElfDynamicSymbol elfDynamicSymbol = ReadReadable((long)num6); elfDynamicSymbol2 = elfDynamicSymbol; } value3 = elfDynamicSymbol2.Value; } catch { LibLogger.ErrorNewline($"Exception reading dynamic symbol for rel of type {item.Type} at pointer 0x{num6:X} (length of file is 0x{RawLength:X}, pointer - length is 0x{(long)num6 - RawLength:X})"); throw; } long num7; try { num7 = MapVirtualAddressToRaw(item.Offset); } catch (InvalidOperationException) { continue; } ulong num8; if (item.Addend.HasValue) { num8 = item.Addend.Value; } else { base.Position = num7; num8 = ReadUInt64(); } ulong word; bool flag; if (InstructionSetId == DefaultInstructionSets.ARM_V7) { (word, flag) = item.Type switch { ElfRelocationType.R_ARM_ABS32 => (value3 + num8, true), ElfRelocationType.R_ARM_REL32 => (value3 + item.Offset - num8, true), ElfRelocationType.R_ARM_COPY => (value3, true), _ => (0uL, false), }; } else if (InstructionSetId == DefaultInstructionSets.ARM_V8) { (word, flag) = item.Type switch { ElfRelocationType.R_AARCH64_ABS64 => (value3 + num8, true), ElfRelocationType.R_AARCH64_PREL64 => (value3 + num8 - item.Offset, true), ElfRelocationType.R_AARCH64_GLOB_DAT => (value3 + num8, true), ElfRelocationType.R_AARCH64_JUMP_SLOT => (value3 + num8, true), ElfRelocationType.R_AARCH64_RELATIVE => (value3 + num8, true), _ => (0uL, false), }; } else if (InstructionSetId == DefaultInstructionSets.X86_32) { (word, flag) = item.Type switch { ElfRelocationType.R_386_32 => (value3 + num8, true), ElfRelocationType.R_ARM_ABS32 => (value3 + num8 - item.Offset, true), ElfRelocationType.R_386_GLOB_DAT => (value3, true), ElfRelocationType.R_386_JMP_SLOT => (value3, true), _ => (0uL, false), }; } else if (InstructionSetId == DefaultInstructionSets.X86_64) { (word, flag) = item.Type switch { ElfRelocationType.R_386_32 => (value3 + num8, true), ElfRelocationType.R_AMD64_RELATIVE => (num8, true), _ => (0uL, false), }; } else { word = 0uL; flag = false; } if (flag) { WriteWord((int)num7, word); } } } catch { LibLogger.Info("Exception during relocation mapping!"); throw; } } private void ProcessSymbols() { List<(ulong, ulong, ulong)> list = new List<(ulong, ulong, ulong)>(); ElfSectionHeaderEntry singleSection = GetSingleSection(ElfSectionEntryType.SHT_STRTAB); if (singleSection != null) { ElfSectionHeaderEntry singleSection2 = GetSingleSection(ElfSectionEntryType.SHT_SYMTAB); if (singleSection2 != null) { LibLogger.VerboseNewline($"\t\t-Found .symtab at 0x{singleSection2.RawAddress:X}"); list.Add((singleSection2.RawAddress, singleSection2.Size / (ulong)singleSection2.EntrySize, singleSection.RawAddress)); } ElfSectionHeaderEntry singleSection3 = GetSingleSection(ElfSectionEntryType.SHT_DYNSYM); if (singleSection3 != null) { LibLogger.VerboseNewline($"\t\t-Found .dynsym at 0x{singleSection3.RawAddress:X}"); list.Add((singleSection3.RawAddress, singleSection3.Size / (ulong)singleSection3.EntrySize, singleSection.RawAddress)); } } ElfDynamicEntry dynamicEntryOfType = GetDynamicEntryOfType(ElfDynamicType.DT_STRTAB); if (dynamicEntryOfType != null) { ElfDynamicEntry dynamicSymTab = GetDynamicEntryOfType(ElfDynamicType.DT_SYMTAB); if (dynamicSymTab != null) { ulong value = (from x in _dynamicSection where x.Value > dynamicSymTab.Value orderby x.Value select x).First().Value; ulong num = (ulong)(is32Bit ? 18 : 24); ulong num2 = (ulong)MapVirtualAddressToRaw(dynamicSymTab.Value); LibLogger.VerboseNewline($"\t\t-Found DT_SYMTAB at 0x{num2:X}"); list.Add((num2, (value - dynamicSymTab.Value) / num, dynamicEntryOfType.Value)); } } _symbolTable.Clear(); _exportTable.Clear(); foreach (var item4 in list) { ulong item = item4.Item1; ulong item2 = item4.Item2; ulong item3 = item4.Item3; List list2 = (is32Bit ? ReadReadableArrayAtRawAddr((long)item, (long)item2).Cast().ToList() : ReadReadableArrayAtRawAddr((long)item, (long)item2).Cast().ToList()); LibLogger.VerboseNewline($"\t\t-Found {list2.Count} symbols in table at 0x{item:X}"); foreach (IElfDynamicSymbol item5 in list2) { string text; try { text = ReadStringToNull(item3 + item5.NameOffset); } catch (ArgumentOutOfRangeException) { continue; } ElfSymbolTableEntry.ElfSymbolEntryType type = ((item5.Shndx == 0) ? ElfSymbolTableEntry.ElfSymbolEntryType.Import : ((item5.Type != ElfDynamicSymbolType.STT_FUNC) ? ((item5.Type == ElfDynamicSymbolType.STT_OBJECT || item5.Type == ElfDynamicSymbolType.STT_COMMON) ? ElfSymbolTableEntry.ElfSymbolEntryType.Name : ElfSymbolTableEntry.ElfSymbolEntryType.Unknown) : ElfSymbolTableEntry.ElfSymbolEntryType.Function)); ulong value2 = item5.Value; ElfSymbolTableEntry elfSymbolTableEntry = new ElfSymbolTableEntry { Name = text, Type = type, VirtualAddress = value2 }; _symbolTable.Add(elfSymbolTableEntry); if (item5.Shndx != 0) { _exportTable.TryAdd(text, elfSymbolTableEntry); } } } } private void ProcessInitializers() { ElfDynamicEntry dynamicEntryOfType = GetDynamicEntryOfType(ElfDynamicType.DT_INIT_ARRAY); if (dynamicEntryOfType != null) { ElfDynamicEntry dynamicEntryOfType2 = GetDynamicEntryOfType(ElfDynamicType.DT_INIT_ARRAYSZ); if (dynamicEntryOfType2 != null) { long offset = MapVirtualAddressToRaw(dynamicEntryOfType.Value); int count = (int)dynamicEntryOfType2.Value / (is32Bit ? 4 : 8); ulong[] source = ReadNUintArrayAtRawAddress(offset, count); ElfDynamicEntry dynamicEntryOfType3 = GetDynamicEntryOfType(ElfDynamicType.DT_INIT); if (dynamicEntryOfType3 != null) { source = source.Append(dynamicEntryOfType3.Value).ToArray(); } _initializerPointers = source.Select((ulong a) => MapVirtualAddressToRaw(a)).ToList(); return; } } _initializerPointers = new List(); } public override (ulong pCodeRegistration, ulong pMetadataRegistration) FindCodeAndMetadataReg(int methodCount, int typeDefinitionsCount) { LibLogger.Verbose("\tChecking ELF Symbol Table for code and/or meta reg..."); ulong num = 0uL; ulong num2 = 0uL; ElfSymbolTableEntry elfSymbolTableEntry = _symbolTable.FirstOrDefault((ElfSymbolTableEntry s) => s.Name.Contains("g_CodeRegistration")); if (elfSymbolTableEntry != null) { num = elfSymbolTableEntry.VirtualAddress; } ElfSymbolTableEntry elfSymbolTableEntry2 = _symbolTable.FirstOrDefault((ElfSymbolTableEntry s) => s.Name.Contains("g_MetadataRegistration")); if (elfSymbolTableEntry2 != null) { num2 = elfSymbolTableEntry2.VirtualAddress; } if (num != 0L && num2 != 0L) { LibLogger.VerboseNewline("Found them."); return (num, num2); } LibLogger.VerboseNewline("Didn't find them, scanning binary..."); if (InstructionSetId == DefaultInstructionSets.ARM_V7 && LibCpp2IlMain.MetadataVersion < 24.2f) { (ulong, ulong) tuple = FindCodeAndMetadataRegArm32(); (ulong, ulong) tuple2 = tuple; if (tuple2.Item1 != 0L || tuple2.Item2 != 0L) { return tuple; } } if (InstructionSetId == DefaultInstructionSets.ARM_V8 && LibCpp2IlMain.MetadataVersion < 24.2f) { (ulong, ulong) tuple3 = FindCodeAndMetadataRegArm64(); (ulong, ulong) tuple2 = tuple3; if (tuple2.Item1 != 0L || tuple2.Item2 != 0L) { return tuple3; } } return FindCodeAndMetadataRegDefaultBehavior(methodCount, typeDefinitionsCount); } private (ulong codeReg, ulong metaReg) FindCodeAndMetadataRegArm32() { byte[] first = new byte[8] { 0, 0, 143, 224, 1, 16, 143, 224 }; byte[] first2 = new byte[3] { 16, 159, 229 }; LibLogger.VerboseNewline($"\tARM-32 MODE: Checking {_initializerPointers.Count} initializer pointers..."); foreach (long initializerPointer in _initializerPointers) { byte[] array = ReadByteArrayAtRawAddress(initializerPointer, 24); if (!first.SequenceEqual(array.Skip(16)) || !first2.SequenceEqual(array.Skip(9).Take(3))) { continue; } long position = array[8] + initializerPointer + 16; base.Position = position; uint num = ReadUInt32(); num += (uint)((int)initializerPointer + 28); uint[] array2 = ReadClassArrayAtRawAddr(num, 10L); if (array2[6].Bits(24, 8) != 234) { continue; } uint[] array3 = new uint[3]; bool flag = false; for (uint num2 = 0u; num2 <= 2; num2++) { if (flag) { break; } var (num3, num4) = ArmUtils.GetOperandsForLiteralLdr(array2[num2]); if (num3 > 2 || num4 == 0) { flag = true; } else { array3[num3] = num4 + num2 * 4 + 8; } } if (flag) { continue; } uint[] array4 = new uint[3]; for (uint num5 = 3u; num5 <= 5; num5++) { if (flag) { break; } var (num6, num7, num8) = ArmUtils.GetOperandsForRegisterAdd(array2[num5]); var (num9, num10, num11) = ArmUtils.GetOperandsForRegisterLdr(array2[num5]); if (num7 == 15 && num6 == num8 && num6 <= 2) { array4[num6] = num + num5 * 4 + array2[array3[num6] / 4] + 8; } else if (num10 == 15 && num9 == num11 && num9 <= 2) { uint num12 = num + num5 * 4 + array2[array3[num9] / 4] + 8; array4[num9] = (uint)ReadPointerAtVirtualAddress(num12); } else { flag = true; } } if (flag) { LibLogger.VerboseNewline($"\t\tInitializer function at 0x{initializerPointer:X} is probably NOT the il2cpp initializer."); continue; } LibLogger.VerboseNewline($"\t\tFound valid sequence of bytes for il2cpp initializer function at 0x{initializerPointer:X}."); return (array4[0], array4[1]); } return (0uL, 0uL); } private (ulong codeReg, ulong metaReg) FindCodeAndMetadataRegArm64() { LibLogger.VerboseNewline($"\tARM-64 MODE: Checking {_initializerPointers.Count} initializer pointers..."); foreach (long initializerPointer in _initializerPointers) { List list = MiniArm64Decompiler.ReadFunctionAtRawAddress(this, (uint)initializerPointer, 7u); if (!MiniArm64Decompiler.IsB(list[list.Count - 1])) { continue; } Dictionary addressesLoadedIntoRegisters = MiniArm64Decompiler.GetAddressesLoadedIntoRegisters(list, (ulong)(_globalOffset + initializerPointer), this); if (addressesLoadedIntoRegisters.Count == 2 && addressesLoadedIntoRegisters.ContainsKey(0u) && addressesLoadedIntoRegisters.TryGetValue(1u, out var value)) { List list2 = MiniArm64Decompiler.ReadFunctionAtRawAddress(this, (uint)MapVirtualAddressToRaw(value), 7u); if (!MiniArm64Decompiler.IsB(list2[list2.Count - 1])) { continue; } addressesLoadedIntoRegisters = MiniArm64Decompiler.GetAddressesLoadedIntoRegisters(list2, value, this); } if (addressesLoadedIntoRegisters.Count == 3 && addressesLoadedIntoRegisters.TryGetValue(0u, out var value2) && addressesLoadedIntoRegisters.TryGetValue(1u, out value) && addressesLoadedIntoRegisters.ContainsKey(2u)) { LibLogger.VerboseNewline($"\t\tFound valid sequence of bytes for il2cpp initializer function at 0x{initializerPointer:X}."); return (value2, value); } LibLogger.VerboseNewline($"\t\tInitializer function at 0x{initializerPointer:X} is probably NOT the il2cpp initializer - got {addressesLoadedIntoRegisters.Count} register values with keys {string.Join(", ", addressesLoadedIntoRegisters.Keys)}."); } return (0uL, 0uL); } private (ulong codeReg, ulong metaReg) FindCodeAndMetadataRegDefaultBehavior(int methodCount, int typeDefinitionsCount) { LibLogger.VerboseNewline("Searching for il2cpp structures in an ELF binary using non-arch-specific method..."); BinarySearcher binarySearcher = new BinarySearcher(this, methodCount, typeDefinitionsCount); LibLogger.VerboseNewline("\tLooking for code reg (this might take a while)..."); ulong num = ((LibCpp2IlMain.MetadataVersion >= 24.2f) ? binarySearcher.FindCodeRegistrationPost2019() : binarySearcher.FindCodeRegistrationPre2019()); LibLogger.VerboseNewline($"\tGot code reg 0x{num:X}"); LibLogger.VerboseNewline("\tLooking for meta reg (" + ((LibCpp2IlMain.MetadataVersion >= 27f) ? "post-27" : "pre-27") + ")..."); ulong num2 = ((LibCpp2IlMain.MetadataVersion >= 27f) ? binarySearcher.FindMetadataRegistrationPost24_5() : binarySearcher.FindMetadataRegistrationPre24_5()); LibLogger.VerboseNewline($"\tGot meta reg 0x{num2:x}"); return (num, num2); } public override long MapVirtualAddressToRaw(ulong addr, bool throwOnError = true) { IElfProgramHeaderEntry elfProgramHeaderEntry = _elfProgramHeaderEntries.FirstOrDefault((IElfProgramHeaderEntry x) => addr >= x.VirtualAddress && addr <= x.VirtualAddress + x.VirtualSize); if (elfProgramHeaderEntry == null) { if (throwOnError) { throw new InvalidOperationException($"No entry in the Elf PHT contains virtual address 0x{addr:X}"); } return -9223372036854774808L; } return (long)(addr - (elfProgramHeaderEntry.VirtualAddress - elfProgramHeaderEntry.RawAddress)); } public override ulong MapRawAddressToVirtual(uint offset) { if (relocationBlocks.Any(((ulong start, ulong end) b) => b.start <= offset && b.end >= offset)) { throw new InvalidOperationException("Attempt to map a relocation block to a virtual address"); } IElfProgramHeaderEntry elfProgramHeaderEntry = _elfProgramHeaderEntries.First((IElfProgramHeaderEntry x) => offset >= x.RawAddress && offset < x.RawAddress + x.RawSize); return elfProgramHeaderEntry.VirtualAddress + offset - elfProgramHeaderEntry.RawAddress; } public override byte GetByteAtRawAddress(ulong addr) { return _raw[addr]; } public override ulong GetRva(ulong pointer) { return pointer - (ulong)_globalOffset; } public override byte[] GetRawBinaryContent() { return _raw; } public override ulong GetVirtualAddressOfExportedFunctionByName(string toFind) { if (!_exportTable.TryGetValue(toFind, out ElfSymbolTableEntry value)) { return 0uL; } return value.VirtualAddress; } public override ulong GetVirtualAddressOfPrimaryExecutableSection() { return _elfSectionHeaderEntries.FirstOrDefault((ElfSectionHeaderEntry s) => s.Name == ".text")?.VirtualAddress ?? 0; } public override byte[] GetEntirePrimaryExecutableSection() { ElfSectionHeaderEntry elfSectionHeaderEntry = _elfSectionHeaderEntries.FirstOrDefault((ElfSectionHeaderEntry s) => s.Name == ".text"); if (elfSectionHeaderEntry == null) { return Array.Empty(); } return GetRawBinaryContent().SubArray((int)elfSectionHeaderEntry.RawAddress, (int)elfSectionHeaderEntry.Size); } } public enum ElfFileType : ushort { ET_NONE = 0, ET_REL = 1, ET_EXEC = 2, ET_DYN = 3, ET_CORE = 4, ET_LOOS = 65024, ET_HIOS = 65279, ET_LOPROC = 65280, ET_HIPROC = ushort.MaxValue } public class ElfFileHeader : ReadableClass { public ElfFileType Type; public short Machine; public int Version; public long pEntryPoint; public long pProgramHeader; public long pSectionHeader; public int Flags; public short HeaderSize; public short ProgramHeaderEntrySize; public short ProgramHeaderEntryCount; public short SectionHeaderEntrySize; public short SectionHeaderEntryCount; public short SectionNameSectionOffset; public override void Read(ClassReadingBinaryReader reader) { Type = (ElfFileType)reader.ReadInt16(); Machine = reader.ReadInt16(); Version = reader.ReadInt32(); pEntryPoint = reader.ReadNInt(); pProgramHeader = reader.ReadNInt(); pSectionHeader = reader.ReadNInt(); Flags = reader.ReadInt32(); HeaderSize = reader.ReadInt16(); ProgramHeaderEntrySize = reader.ReadInt16(); ProgramHeaderEntryCount = reader.ReadInt16(); SectionHeaderEntrySize = reader.ReadInt16(); SectionHeaderEntryCount = reader.ReadInt16(); SectionNameSectionOffset = reader.ReadInt16(); } } public class ElfFileIdent : ReadableClass { public int Magic; public byte Architecture; public byte Endianness; public byte Version; public byte OSAbi; public byte AbiVersion; public override void Read(ClassReadingBinaryReader reader) { Magic = reader.ReadInt32(); Architecture = reader.ReadByte(); Endianness = reader.ReadByte(); Version = reader.ReadByte(); OSAbi = reader.ReadByte(); AbiVersion = reader.ReadByte(); reader.ReadBytes(7); } } public enum ElfProgramEntryType : uint { PT_NONE = 0u, PT_LOAD = 1u, PT_DYNAMIC = 2u, PT_INTERP = 3u, PT_NOTE = 4u, PT_SHLIB = 5u, PT_PHDR = 6u, PT_TLS = 7u, PT_LOOS = 1610612736u, PT_HIOS = 1879048191u, PT_LOPROC = 1879048192u, PT_HIPROC = 2147483647u } public class ElfProgramHeaderEntry32 : ReadableClass, IElfProgramHeaderEntry { private ElfProgramEntryType _internalType; private uint _internalOffsetRaw; private uint _internalVirtualAddr; private uint _internalPhysicalAddr; private uint _internalSizeRaw; private uint _internalSizeVirtual; private ElfProgramHeaderFlags _internalFlags; private int _internalAlign; public ElfProgramHeaderFlags Flags => _internalFlags; public ElfProgramEntryType Type => _internalType; public ulong RawAddress => _internalOffsetRaw; public ulong VirtualAddress => _internalVirtualAddr; public ulong PhysicalAddr => _internalPhysicalAddr; public ulong RawSize => _internalSizeRaw; public ulong VirtualSize => _internalSizeVirtual; public long Align => _internalAlign; public override void Read(ClassReadingBinaryReader reader) { _internalType = (ElfProgramEntryType)reader.ReadUInt32(); _internalOffsetRaw = reader.ReadUInt32(); _internalVirtualAddr = reader.ReadUInt32(); _internalPhysicalAddr = reader.ReadUInt32(); _internalSizeRaw = reader.ReadUInt32(); _internalSizeVirtual = reader.ReadUInt32(); _internalFlags = (ElfProgramHeaderFlags)reader.ReadUInt32(); _internalAlign = reader.ReadInt32(); } } public class ElfProgramHeaderEntry64 : ReadableClass, IElfProgramHeaderEntry { public ElfProgramEntryType _internalType; public ElfProgramHeaderFlags _internalFlags; public ulong _internalOffsetRaw; public ulong _internalVirtualAddr; public ulong _internalPhysicalAddr; public ulong _internalSizeRaw; public ulong _internalSizeVirtual; public long _internalAlign; public ElfProgramHeaderFlags Flags => _internalFlags; public ElfProgramEntryType Type => _internalType; public ulong RawAddress => _internalOffsetRaw; public ulong VirtualAddress => _internalVirtualAddr; public ulong PhysicalAddr => _internalPhysicalAddr; public ulong RawSize => _internalSizeRaw; public ulong VirtualSize => _internalSizeVirtual; public long Align => _internalAlign; public override void Read(ClassReadingBinaryReader reader) { _internalType = (ElfProgramEntryType)reader.ReadUInt32(); _internalFlags = (ElfProgramHeaderFlags)reader.ReadUInt32(); _internalOffsetRaw = reader.ReadUInt64(); _internalVirtualAddr = reader.ReadUInt64(); _internalPhysicalAddr = reader.ReadUInt64(); _internalSizeRaw = reader.ReadUInt64(); _internalSizeVirtual = reader.ReadUInt64(); _internalAlign = reader.ReadInt64(); } } public class ElfRelaEntry : ReadableClass { public ulong Offset; public ulong Info; public ulong Addend; public ElfRelocationType Type => (ElfRelocationType)(Info & 0xFFFFFFFFu); public ulong Symbol => Info >> 32; public override void Read(ClassReadingBinaryReader reader) { Offset = reader.ReadNUint(); Info = reader.ReadNUint(); Addend = reader.ReadNUint(); } } public class ElfRelEntry : ReadableClass { public ulong Offset; public ulong Info; public override void Read(ClassReadingBinaryReader reader) { Offset = reader.ReadNUint(); Info = reader.ReadNUint(); } } public class ElfRelocation { public ElfRelocationType Type; public ulong Offset; public ulong? Addend; public ulong pRelatedSymbolTable; public ulong IndexInSymbolTable; private static ulong GetTypeBitsFromInfo(ulong info, ElfFile f) { if (!f.is32Bit) { return info & 0xFFFFFFFFu; } return info & 0xFF; } private static ulong GetSymBitsFromInfo(ulong info, ElfFile f) { if (!f.is32Bit) { return info >> 32; } return info >> 8; } public ElfRelocation(ElfFile f, ElfRelEntry relocation, ulong tablePointer) { Offset = relocation.Offset; Addend = null; Type = (ElfRelocationType)GetTypeBitsFromInfo(relocation.Info, f); IndexInSymbolTable = GetSymBitsFromInfo(relocation.Info, f); pRelatedSymbolTable = tablePointer; } public ElfRelocation(ElfFile f, ElfRelaEntry relocation, ulong tablePointer) { Offset = relocation.Offset; Addend = relocation.Addend; Type = (ElfRelocationType)GetTypeBitsFromInfo(relocation.Info, f); IndexInSymbolTable = GetSymBitsFromInfo(relocation.Info, f); pRelatedSymbolTable = tablePointer; } protected bool Equals(ElfRelocation other) { return Offset == other.Offset; } public override bool Equals(object? obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj.GetType() != GetType()) { return false; } return Equals((ElfRelocation)obj); } public override int GetHashCode() { return Offset.GetHashCode(); } public static bool operator ==(ElfRelocation? left, ElfRelocation? right) { return object.Equals(left, right); } public static bool operator !=(ElfRelocation? left, ElfRelocation? right) { return !object.Equals(left, right); } } public enum ElfRelocationType : uint { R_ARM_ABS32 = 2u, R_ARM_REL32 = 3u, R_ARM_PC13 = 4u, R_ARM_COPY = 20u, R_AARCH64_ABS64 = 257u, R_AARCH64_PREL64 = 260u, R_AARCH64_GLOB_DAT = 1025u, R_AARCH64_JUMP_SLOT = 1026u, R_AARCH64_RELATIVE = 1027u, R_386_32 = 1u, R_386_PC32 = 2u, R_386_GLOB_DAT = 6u, R_386_JMP_SLOT = 7u, R_AMD64_64 = 1u, R_AMD64_RELATIVE = 8u } public enum ElfSectionEntryType : uint { SHT_NONE = 0u, SHT_PROGBITS = 1u, SHT_SYMTAB = 2u, SHT_STRTAB = 3u, SHT_RELA = 4u, SHT_HASH = 5u, SHT_DYNAMIC = 6u, SHT_NOTE = 7u, SHT_NOBITS = 8u, SHT_REL = 9u, SHT_SHLIB = 10u, SHT_DYNSYM = 11u, SHT_INIT_ARRAY = 14u, SHT_FINI_ARRAY = 15u, SHT_PREINIT_ARRAY = 16u, SHT_GROUP = 17u, SHT_SYMTAB_SHNDX = 18u, SHT_NUM = 19u, SHT_LOOS = 1610612736u, SHT_HIOS = 1879048191u, SHT_LOPROC = 1879048192u, SHT_HIPROC = 2147483647u } public class ElfSectionHeaderEntry : ReadableClass { public uint NameOffset; public ElfSectionEntryType Type; public ElfSectionHeaderFlags Flags; public ulong VirtualAddress; public ulong RawAddress; public ulong Size; public int LinkedSectionIndex; public int SectionInfo; public long Alignment; public long EntrySize; public string? Name { get; set; } public override void Read(ClassReadingBinaryReader reader) { NameOffset = reader.ReadUInt32(); Type = (ElfSectionEntryType)reader.ReadUInt32(); Flags = (ElfSectionHeaderFlags)reader.ReadNInt(); VirtualAddress = reader.ReadNUint(); RawAddress = reader.ReadNUint(); Size = reader.ReadNUint(); LinkedSectionIndex = reader.ReadInt32(); SectionInfo = reader.ReadInt32(); Alignment = reader.ReadNInt(); EntrySize = reader.ReadNInt(); } } [Flags] public enum ElfSectionHeaderFlags : long { SHF_WRITE = 1L, SHF_ALLOC = 2L, SHF_EXECINSTR = 4L, SHF_MERGE = 0x10L, SHF_STRINGS = 0x20L, SHF_INFO_LINK = 0x40L, SHF_LINK_ORDER = 0x80L, SHF_OS_NONCONFORMING = 0x100L, SHF_GROUP = 0x200L, SHF_TLS = 0x400L, SHF_MASKOS = 0xFF00000L, SHF_MASKPROC = 0xF0000000L, SHF_ORDERED = 0x40000000L, SHF_EXCLUDE = 0x80000000L } public class ElfSymbolTableEntry { public enum ElfSymbolEntryType { Function, Name, Import, Unknown } public string Name; public ElfSymbolEntryType Type; public ulong VirtualAddress; } public interface IElfDynamicSymbol { uint NameOffset { get; } ulong Value { get; } ulong Size { get; } byte Info { get; } byte Other { get; } ushort Shndx { get; } ElfDynamicSymbolType Type { get; } } public interface IElfProgramHeaderEntry { ElfProgramHeaderFlags Flags { get; } ElfProgramEntryType Type { get; } ulong RawAddress { get; } ulong VirtualAddress { get; } ulong PhysicalAddr { get; } ulong RawSize { get; } ulong VirtualSize { get; } long Align { get; } } } namespace LibCpp2IL.BinaryStructures { public class Il2CppArrayType : ReadableClass { public ulong etype; public byte rank; public byte numsizes; public byte numlobounds; public ulong sizes; public ulong lobounds; public override void Read(ClassReadingBinaryReader reader) { etype = reader.ReadNUint(); rank = reader.ReadByte(); numsizes = reader.ReadByte(); numlobounds = reader.ReadByte(); sizes = reader.ReadNUint(); lobounds = reader.ReadNUint(); } } public class Il2CppCodeGenModule : ReadableClass { public ulong moduleName; public long methodPointerCount; public ulong methodPointers; [Version(Min = 27.1f)] [Version(Min = 24.5f, Max = 24.5f)] public long adjustorThunkCount; [Version(Min = 27.1f)] [Version(Min = 24.5f, Max = 24.5f)] public ulong adjustorThunks; public ulong invokerIndices; public ulong reversePInvokeWrapperCount; public ulong reversePInvokeWrapperIndices; public long rgctxRangesCount; public ulong pRgctxRanges; public long rgctxsCount; public ulong rgctxs; public ulong debuggerMetadata; [Version(Min = 27f, Max = 27.9f)] public ulong customAttributeCacheGenerator; [Version(Min = 27f)] public ulong moduleInitializer; [Version(Min = 27f)] public ulong staticConstructorTypeIndices; [Version(Min = 27f)] public ulong metadataRegistration; [Version(Min = 27f)] public ulong codeRegistration; private string? _cachedName; public string Name { get { if (_cachedName == null) { _cachedName = LibCpp2IlMain.Binary.ReadStringToNull(LibCpp2IlMain.Binary.MapVirtualAddressToRaw(moduleName)); } return _cachedName; } } public Il2CppTokenRangePair[] RGCTXRanges => LibCpp2IlMain.Binary.GetRgctxRangePairsForModule(this); public override void Read(ClassReadingBinaryReader reader) { moduleName = reader.ReadNUint(); methodPointerCount = reader.ReadNInt(); methodPointers = reader.ReadNUint(); if (IsAtLeast(24.5f) && IsNot(27f)) { adjustorThunkCount = reader.ReadNInt(); adjustorThunks = reader.ReadNUint(); } invokerIndices = reader.ReadNUint(); reversePInvokeWrapperCount = reader.ReadNUint(); reversePInvokeWrapperIndices = reader.ReadNUint(); rgctxRangesCount = reader.ReadNInt(); pRgctxRanges = reader.ReadNUint(); rgctxsCount = reader.ReadNInt(); rgctxs = reader.ReadNUint(); debuggerMetadata = reader.ReadNUint(); if (IsAtLeast(27f)) { if (IsLessThan(29f)) { customAttributeCacheGenerator = reader.ReadNUint(); } moduleInitializer = reader.ReadNUint(); staticConstructorTypeIndices = reader.ReadNUint(); metadataRegistration = reader.ReadNUint(); codeRegistration = reader.ReadNUint(); } } } public class Il2CppCodeRegistration : ReadableClass { [Version(Max = 24.15f)] public ulong methodPointersCount; [Version(Max = 24.15f)] public ulong methodPointers; public ulong reversePInvokeWrapperCount; public ulong reversePInvokeWrappers; public ulong genericMethodPointersCount; public ulong genericMethodPointers; [Version(Min = 27.1f)] [Version(Min = 24.5f, Max = 24.5f)] public ulong genericAdjustorThunks; public ulong invokerPointersCount; public ulong invokerPointers; [Version(Max = 24.5f)] public ulong customAttributeCount; [Version(Max = 24.5f)] public ulong customAttributeGeneratorListAddress; public ulong unresolvedVirtualCallCount; public ulong unresolvedVirtualCallPointers; [Version(Min = 29.1f)] public ulong unresolvedInstanceCallPointers; [Version(Min = 29.1f)] public ulong unresolvedStaticCallPointers; [Version(Min = 23f)] public ulong interopDataCount; [Version(Min = 23f)] public ulong interopData; [Version(Min = 24.3f)] public ulong windowsRuntimeFactoryCount; [Version(Min = 24.3f)] public ulong windowsRuntimeFactoryTable; [Version(Min = 24.2f)] public ulong codeGenModulesCount; [Version(Min = 24.2f)] public ulong addrCodeGenModulePtrs; public override void Read(ClassReadingBinaryReader reader) { if (IsAtMost(24.15f)) { methodPointersCount = reader.ReadNUint(); methodPointers = reader.ReadNUint(); } reversePInvokeWrapperCount = reader.ReadNUint(); reversePInvokeWrappers = reader.ReadNUint(); genericMethodPointersCount = reader.ReadNUint(); genericMethodPointers = reader.ReadNUint(); if (IsAtLeast(24.5f) && IsNot(27f)) { genericAdjustorThunks = reader.ReadNUint(); } invokerPointersCount = reader.ReadNUint(); invokerPointers = reader.ReadNUint(); if (IsAtMost(24.5f)) { customAttributeCount = reader.ReadNUint(); customAttributeGeneratorListAddress = reader.ReadNUint(); } unresolvedVirtualCallCount = reader.ReadNUint(); unresolvedVirtualCallPointers = reader.ReadNUint(); if (IsAtLeast(29.1f)) { unresolvedInstanceCallPointers = reader.ReadNUint(); unresolvedStaticCallPointers = reader.ReadNUint(); } if (IsAtLeast(23f)) { interopDataCount = reader.ReadNUint(); interopData = reader.ReadNUint(); } if (IsAtLeast(24.2f)) { if (IsAtLeast(24.3f)) { windowsRuntimeFactoryCount = reader.ReadNUint(); windowsRuntimeFactoryTable = reader.ReadNUint(); } codeGenModulesCount = reader.ReadNUint(); addrCodeGenModulePtrs = reader.ReadNUint(); } } } public class Il2CppGenericClass : ReadableClass { public long TypeDefinitionIndex; public Il2CppGenericContext Context; public ulong CachedClass; public Il2CppTypeDefinition TypeDefinition { get { if (!(LibCpp2IlMain.MetadataVersion < 27f)) { return V27BaseType.AsClass(); } return LibCpp2IlMain.TheMetadata.typeDefs[(int)TypeDefinitionIndex]; } } public Il2CppType? V27BaseType { get { if (!(LibCpp2IlMain.MetadataVersion < 27f)) { return LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress((ulong)TypeDefinitionIndex); } return null; } } public override void Read(ClassReadingBinaryReader reader) { TypeDefinitionIndex = reader.ReadNInt(); Context = reader.ReadReadableHereNoLock(); CachedClass = reader.ReadNUint(); } } public class Il2CppGenericContext : ReadableClass { public ulong class_inst; public ulong method_inst; public Il2CppGenericInst ClassInst => LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(class_inst); public Il2CppGenericInst MethodInst => LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(method_inst); public override void Read(ClassReadingBinaryReader reader) { class_inst = reader.ReadNUint(); method_inst = reader.ReadNUint(); } } public class Il2CppGenericInst : ReadableClass { public ulong pointerCount; public ulong pointerStart; public ulong[] Pointers => LibCpp2IlMain.Binary.ReadNUintArrayAtVirtualAddress(pointerStart, (long)pointerCount); public Il2CppType[] Types => Pointers.Select(LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer).ToArray(); public override void Read(ClassReadingBinaryReader reader) { pointerCount = reader.ReadNUint(); pointerStart = reader.ReadNUint(); } } public class Il2CppGenericMethodFunctionsDefinitions : ReadableClass { public int GenericMethodIndex; public Il2CppGenericMethodIndices Indices; public override void Read(ClassReadingBinaryReader reader) { GenericMethodIndex = reader.ReadInt32(); Indices = reader.ReadReadableHereNoLock(); } } public class Il2CppGenericMethodIndices : ReadableClass { public int methodIndex; public int invokerIndex; [Version(Min = 27.1f)] [Version(Min = 24.5f, Max = 24.5f)] public int adjustorThunk; public override void Read(ClassReadingBinaryReader reader) { methodIndex = reader.ReadInt32(); invokerIndex = reader.ReadInt32(); if (IsAtLeast(24.5f) && IsNot(27f)) { adjustorThunk = reader.ReadInt32(); } } } public class Il2CppMetadataRegistration : ReadableClass { public long genericClassesCount; public ulong genericClasses; public long genericInstsCount; public ulong genericInsts; public long genericMethodTableCount; public ulong genericMethodTable; public long numTypes; public ulong typeAddressListAddress; public long methodSpecsCount; public ulong methodSpecs; public long fieldOffsetsCount; public ulong fieldOffsetListAddress; public long typeDefinitionsSizesCount; public ulong typeDefinitionsSizes; public ulong metadataUsagesCount; public ulong metadataUsages; public override void Read(ClassReadingBinaryReader reader) { genericClassesCount = reader.ReadNInt(); genericClasses = reader.ReadNUint(); genericInstsCount = reader.ReadNInt(); genericInsts = reader.ReadNUint(); genericMethodTableCount = reader.ReadNInt(); genericMethodTable = reader.ReadNUint(); numTypes = reader.ReadNInt(); typeAddressListAddress = reader.ReadNUint(); methodSpecsCount = reader.ReadNInt(); methodSpecs = reader.ReadNUint(); fieldOffsetsCount = reader.ReadNInt(); fieldOffsetListAddress = reader.ReadNUint(); typeDefinitionsSizesCount = reader.ReadNInt(); typeDefinitionsSizes = reader.ReadNUint(); metadataUsagesCount = reader.ReadNUint(); metadataUsages = reader.ReadNUint(); } } public class Il2CppMethodSpec : ReadableClass { public int methodDefinitionIndex; public int classIndexIndex; public int methodIndexIndex; public Il2CppMethodDefinition? MethodDefinition { get { Il2CppMetadata? theMetadata = LibCpp2IlMain.TheMetadata; if (theMetadata == null) { return null; } return theMetadata.methodDefs[methodDefinitionIndex]; } } public Il2CppGenericInst? GenericClassInst => LibCpp2IlMain.Binary?.GetGenericInst(classIndexIndex); public Il2CppGenericInst? GenericMethodInst => LibCpp2IlMain.Binary?.GetGenericInst(methodIndexIndex); public Il2CppTypeReflectionData[] GenericClassParams { get { if (classIndexIndex != -1) { return LibCpp2ILUtils.GetGenericTypeParams(GenericClassInst); } return Array.Empty(); } } public Il2CppTypeReflectionData[] GenericMethodParams { get { if (methodIndexIndex != -1) { return LibCpp2ILUtils.GetGenericTypeParams(GenericMethodInst); } return Array.Empty(); } } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(MethodDefinition?.ReturnType).Append(" "); stringBuilder.Append(MethodDefinition?.DeclaringType?.FullName); if (classIndexIndex != -1) { stringBuilder.Append("<").Append(string.Join(", ", GenericClassParams.AsEnumerable())).Append(">"); } stringBuilder.Append(".").Append(MethodDefinition?.Name); if (methodIndexIndex != -1) { stringBuilder.Append("<").Append(string.Join(", ", GenericMethodParams.AsEnumerable())).Append(">"); } return stringBuilder.ToString(); } public override void Read(ClassReadingBinaryReader reader) { methodDefinitionIndex = reader.ReadInt32(); classIndexIndex = reader.ReadInt32(); methodIndexIndex = reader.ReadInt32(); } } public enum Il2CppRGCTXDataType { IL2CPP_RGCTX_DATA_INVALID, IL2CPP_RGCTX_DATA_TYPE, IL2CPP_RGCTX_DATA_CLASS, IL2CPP_RGCTX_DATA_METHOD, IL2CPP_RGCTX_DATA_ARRAY } public class Il2CppRGCTXDefinition : ReadableClass { public Il2CppRGCTXDataType type; public int _rawIndex; public int MethodIndex => _rawIndex; public int TypeIndex => _rawIndex; public Il2CppMethodSpec? MethodSpec => LibCpp2IlMain.Binary?.GetMethodSpec(MethodIndex); public Il2CppTypeReflectionData? Type => LibCpp2ILUtils.GetTypeReflectionData(LibCpp2IlMain.Binary.GetType(TypeIndex)); public override void Read(ClassReadingBinaryReader reader) { type = (Il2CppRGCTXDataType)reader.ReadInt32(); _rawIndex = reader.ReadInt32(); } } public class Il2CppTokenRangePair : ReadableClass { public int token; public int start; public int length; public override void Read(ClassReadingBinaryReader reader) { token = reader.ReadInt32(); start = reader.ReadInt32(); length = reader.ReadInt32(); } } public class Il2CppType : ReadableClass { public class Union { public ulong Dummy; public long ClassIndex => (long)Dummy; public ulong Type => Dummy; public ulong Array => Dummy; public long GenericParameterIndex => (long)Dummy; public ulong GenericClass => Dummy; } public ulong Datapoint; public uint Bits; public Union Data { get; set; } public uint Attrs { get; set; } public Il2CppTypeEnum Type { get; set; } public uint NumMods { get; set; } public uint Byref { get; set; } public uint Pinned { get; set; } public uint ValueType { get; set; } private void InitUnionAndFlags() { Attrs = Bits & 0xFFFFu; Type = (Il2CppTypeEnum)((int)(Bits >> 16) & 0xFF); Data = new Union { Dummy = Datapoint }; if (LibCpp2IlMain.Il2CppTypeHasNumMods5Bits) { NumMods = (Bits >> 24) & 0x1Fu; Byref = (Bits >> 29) & 1u; Pinned = (Bits >> 30) & 1u; ValueType = Bits >> 31; } else { NumMods = (Bits >> 24) & 0x3Fu; Byref = (Bits >> 30) & 1u; Pinned = Bits >> 31; ValueType = 0u; } } public Il2CppTypeDefinition AsClass() { Il2CppTypeEnum type = Type; if (type != Il2CppTypeEnum.IL2CPP_TYPE_CLASS && type != Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE) { throw new Exception("Type is not a class, but a " + Type); } return LibCpp2IlMain.TheMetadata.typeDefs[Data.ClassIndex]; } public Il2CppType GetEncapsulatedType() { Il2CppTypeEnum type = Type; if (type != Il2CppTypeEnum.IL2CPP_TYPE_PTR && type != Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY) { throw new Exception("Type does not have a encapsulated type - it is not a pointer or an szarray"); } return LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(Data.Type); } public Il2CppArrayType GetArrayType() { if (Type != Il2CppTypeEnum.IL2CPP_TYPE_ARRAY) { throw new Exception("Type is not an array"); } return LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(Data.Array); } public Il2CppType GetArrayElementType() { return LibCpp2IlMain.Binary.GetIl2CppTypeFromPointer(GetArrayType().etype); } public int GetArrayRank() { return GetArrayType().rank; } public Il2CppGenericParameter GetGenericParameterDef() { Il2CppTypeEnum type = Type; if (type != Il2CppTypeEnum.IL2CPP_TYPE_VAR && type != Il2CppTypeEnum.IL2CPP_TYPE_MVAR) { throw new Exception("Type is not a generic parameter"); } return LibCpp2IlMain.TheMetadata.genericParameters[Data.GenericParameterIndex]; } public Il2CppGenericClass GetGenericClass() { if (Type != Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST) { throw new Exception("Type is not a generic class"); } return LibCpp2IlMain.Binary.ReadReadableAtVirtualAddress(Data.GenericClass); } public override void Read(ClassReadingBinaryReader reader) { Datapoint = reader.ReadNUint(); Bits = reader.ReadUInt32(); InitUnionAndFlags(); } public Il2CppTypeDefinition CoerceToUnderlyingTypeDefinition() { Il2CppTypeEnum type = Type; if (type == Il2CppTypeEnum.IL2CPP_TYPE_VAR || type == Il2CppTypeEnum.IL2CPP_TYPE_MVAR) { throw new Exception("Can't get the type definition of a generic parameter"); } switch (Type) { case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST: return GetGenericClass().TypeDefinition; case Il2CppTypeEnum.IL2CPP_TYPE_PTR: case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: return GetEncapsulatedType().CoerceToUnderlyingTypeDefinition(); case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: return GetArrayElementType().CoerceToUnderlyingTypeDefinition(); default: return Type.IsIl2CppPrimitive() ? LibCpp2IlReflection.PrimitiveTypeDefinitions[Type] : AsClass(); } } public bool ThisOrElementIsGenericParam() { switch (Type) { case Il2CppTypeEnum.IL2CPP_TYPE_PTR: case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: return GetEncapsulatedType().ThisOrElementIsGenericParam(); case Il2CppTypeEnum.IL2CPP_TYPE_ARRAY: return GetArrayElementType().ThisOrElementIsGenericParam(); case Il2CppTypeEnum.IL2CPP_TYPE_VAR: case Il2CppTypeEnum.IL2CPP_TYPE_MVAR: return true; default: return false; } } public string GetGenericParamName() { if (!ThisOrElementIsGenericParam()) { throw new Exception("Type is not a generic parameter"); } return Type switch { Il2CppTypeEnum.IL2CPP_TYPE_PTR => GetEncapsulatedType().GetGenericParamName() + "&", Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY => GetEncapsulatedType().GetGenericParamName() + "[]", Il2CppTypeEnum.IL2CPP_TYPE_ARRAY => GetArrayElementType().GetGenericParamName() + "[]".Repeat(GetArrayRank()), _ => GetGenericParameterDef().Name ?? "", }; } } public class Il2CppTypeDefinitionSizes : ReadableClass { public uint instance_size; public int native_size; public uint static_fields_size; public uint thread_static_fields_size; public override string ToString() { return $"Il2Cpp TypeDefinition Size Data {{InstanceSize={instance_size}, NativeSize={native_size}, StaticFieldsSize={static_fields_size}, Thread StaticFieldsSize={thread_static_fields_size}}}"; } public override void Read(ClassReadingBinaryReader reader) { instance_size = reader.ReadUInt32(); native_size = reader.ReadInt32(); static_fields_size = reader.ReadUInt32(); thread_static_fields_size = reader.ReadUInt32(); } } public enum Il2CppTypeEnum { IL2CPP_TYPE_END = 0, IL2CPP_TYPE_VOID = 1, IL2CPP_TYPE_BOOLEAN = 2, IL2CPP_TYPE_CHAR = 3, IL2CPP_TYPE_I1 = 4, IL2CPP_TYPE_U1 = 5, IL2CPP_TYPE_I2 = 6, IL2CPP_TYPE_U2 = 7, IL2CPP_TYPE_I4 = 8, IL2CPP_TYPE_U4 = 9, IL2CPP_TYPE_I8 = 10, IL2CPP_TYPE_U8 = 11, IL2CPP_TYPE_R4 = 12, IL2CPP_TYPE_R8 = 13, IL2CPP_TYPE_STRING = 14, IL2CPP_TYPE_PTR = 15, IL2CPP_TYPE_BYREF = 16, IL2CPP_TYPE_VALUETYPE = 17, IL2CPP_TYPE_CLASS = 18, IL2CPP_TYPE_VAR = 19, IL2CPP_TYPE_ARRAY = 20, IL2CPP_TYPE_GENERICINST = 21, IL2CPP_TYPE_TYPEDBYREF = 22, IL2CPP_TYPE_I = 24, IL2CPP_TYPE_U = 25, IL2CPP_TYPE_FNPTR = 27, IL2CPP_TYPE_OBJECT = 28, IL2CPP_TYPE_SZARRAY = 29, IL2CPP_TYPE_MVAR = 30, IL2CPP_TYPE_CMOD_REQD = 31, IL2CPP_TYPE_CMOD_OPT = 32, IL2CPP_TYPE_INTERNAL = 33, IL2CPP_TYPE_MODIFIER = 64, IL2CPP_TYPE_SENTINEL = 65, IL2CPP_TYPE_PINNED = 69, IL2CPP_TYPE_ENUM = 85, IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 255 } public static class Il2CppTypeEnumExtensions { public static bool IsIl2CppPrimitive(this Il2CppTypeEnum e) { switch (e) { case Il2CppTypeEnum.IL2CPP_TYPE_VOID: case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN: case Il2CppTypeEnum.IL2CPP_TYPE_CHAR: case Il2CppTypeEnum.IL2CPP_TYPE_I1: case Il2CppTypeEnum.IL2CPP_TYPE_U1: case Il2CppTypeEnum.IL2CPP_TYPE_I2: case Il2CppTypeEnum.IL2CPP_TYPE_U2: case Il2CppTypeEnum.IL2CPP_TYPE_I4: case Il2CppTypeEnum.IL2CPP_TYPE_U4: case Il2CppTypeEnum.IL2CPP_TYPE_I8: case Il2CppTypeEnum.IL2CPP_TYPE_U8: case Il2CppTypeEnum.IL2CPP_TYPE_R4: case Il2CppTypeEnum.IL2CPP_TYPE_R8: case Il2CppTypeEnum.IL2CPP_TYPE_STRING: case Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF: case Il2CppTypeEnum.IL2CPP_TYPE_I: case Il2CppTypeEnum.IL2CPP_TYPE_U: case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT: case Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX: return true; default: return false; } } } }