private void Configure(ulong codeRegistration, ulong metadataRegistration) { // Store locations CodeRegistrationPointer = codeRegistration; MetadataRegistrationPointer = metadataRegistration; Console.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration)); Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration)); // Root structures from which we find everything else CodeRegistration = Image.ReadMappedObject <Il2CppCodeRegistration>(codeRegistration); MetadataRegistration = Image.ReadMappedObject <Il2CppMetadataRegistration>(metadataRegistration); // The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule if (Image.Version <= 24.1) { GlobalMethodPointers = Image.ReadMappedArray <ulong>(CodeRegistration.pmethodPointers, (int)CodeRegistration.methodPointersCount); } // After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules if (Image.Version >= 24.2) { Modules = new Dictionary <string, Il2CppCodeGenModule>(); // In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0; // if this changes we'll have to get smarter about disambiguating these two. if (CodeRegistration.codeGenModulesCount == 0) { Image.Version = 24.3; CodeRegistration = Image.ReadMappedObject <Il2CppCodeRegistration>(codeRegistration); } // Array of pointers to Il2CppCodeGenModule var codeGenModulePointers = Image.ReadMappedArray <ulong>(CodeRegistration.pcodeGenModules, (int)CodeRegistration.codeGenModulesCount); var modules = Image.ReadMappedObjectPointerArray <Il2CppCodeGenModule>(CodeRegistration.pcodeGenModules, (int)CodeRegistration.codeGenModulesCount); foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) { var module = mp.Module; var name = Image.ReadMappedNullTerminatedString(module.moduleName); Modules.Add(name, module); CodeGenModulePointers.Add(name, mp.Pointer); // Read method pointers ModuleMethodPointers.Add(module, Image.ReadMappedArray <ulong>(module.methodPointers, (int)module.methodPointerCount)); // Read method invoker pointer indices - one per method MethodInvokerIndices.Add(module, Image.ReadMappedArray <int>(module.invokerIndices, (int)module.methodPointerCount)); } } // Field offset data. Metadata <=21.x uses a value-type array; >=21.x uses a pointer array // Versions from 22 onwards use an array of pointers in Binary.FieldOffsetData bool fieldOffsetsArePointers = (Image.Version >= 22); // Some variants of 21 also use an array of pointers if (Image.Version == 21) { var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6); // We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32 // are always the first six defined types, and that all but Int32 have no fields fieldOffsetsArePointers = (fieldTest[0] == 0 && fieldTest[1] == 0 && fieldTest[2] == 0 && fieldTest[3] == 0 && fieldTest[4] == 0 && fieldTest[5] > 0); } // All older versions use values directly in the array if (!fieldOffsetsArePointers) { FieldOffsets = Image.ReadMappedArray <uint>(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); } else { FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); } // Type references (pointer array) var typeRefPointers = Image.ReadMappedArray <ulong>(MetadataRegistration.ptypes, (int)MetadataRegistration.typesCount); TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i); TypeReferences = Image.ReadMappedObjectPointerArray <Il2CppType>(MetadataRegistration.ptypes, (int)MetadataRegistration.typesCount); // Custom attribute constructors (function pointers) // This is managed in Il2CppInspector for metadata >= 27 if (Image.Version < 27) { CustomAttributeGenerators = Image.ReadMappedArray <ulong>(CodeRegistration.customAttributeGenerators, (int)CodeRegistration.customAttributeCount); } // Method.Invoke function pointers MethodInvokePointers = Image.ReadMappedArray <ulong>(CodeRegistration.invokerPointers, (int)CodeRegistration.invokerPointersCount); // TODO: Function pointers as shown below // reversePInvokeWrappers // <=22: delegateWrappersFromManagedToNative, marshalingFunctions // >=21 <=22: ccwMarshalingFunctions // >=22: unresolvedVirtualCallPointers // >=23: interopData if (Image.Version < 19) { VTableMethodReferences = Image.ReadMappedArray <uint>(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount); } // Generic type and method specs (open and closed constructed types) MethodSpecs = Image.ReadMappedArray <Il2CppMethodSpec>(MetadataRegistration.methodSpecs, (int)MetadataRegistration.methodSpecsCount); // Concrete generic class and method signatures GenericInstances = Image.ReadMappedObjectPointerArray <Il2CppGenericInst>(MetadataRegistration.genericInsts, (int)MetadataRegistration.genericInstsCount); // Concrete generic method pointers var genericMethodPointers = Image.ReadMappedArray <ulong>(CodeRegistration.genericMethodPointers, (int)CodeRegistration.genericMethodPointersCount); var genericMethodTable = Image.ReadMappedArray <Il2CppGenericMethodFunctionsDefinitions>(MetadataRegistration.genericMethodTable, (int)MetadataRegistration.genericMethodTableCount); foreach (var tableEntry in genericMethodTable) { GenericMethodPointers.Add(MethodSpecs[tableEntry.genericMethodIndex], genericMethodPointers[tableEntry.indices.methodIndex]); GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.genericMethodIndex], tableEntry.indices.invokerIndex); } }
// Load all of the discovered metadata in the binary private void PrepareMetadata(ulong codeRegistration, ulong metadataRegistration) { // Store locations CodeRegistrationPointer = codeRegistration; MetadataRegistrationPointer = metadataRegistration; Console.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration)); Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration)); // Root structures from which we find everything else CodeRegistration = Image.ReadMappedObject <Il2CppCodeRegistration>(codeRegistration); MetadataRegistration = Image.ReadMappedObject <Il2CppMetadataRegistration>(metadataRegistration); // genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1 // pointer expected if we need to bump version if (Image.Version == 24.4 && CodeRegistration.invokerPointersCount > 0x100000) { Image.Version = 24.5; CodeRegistration = Image.ReadMappedObject <Il2CppCodeRegistration>(codeRegistration); } // Plugin hook to pre-process binary isModified |= PluginHooks.PreProcessBinary(this).IsStreamModified; StatusUpdate($"Analyzing IL2CPP data for {Image.Format}/{Image.Arch} image"); // Do basic validatation that MetadataRegistration and CodeRegistration are sane /* * GlobalMethodPointers (<= 24.1) must be a series of pointers in il2cpp or .text, and in sequential order * FieldOffsetPointers (>= 21.1) must be a series of pointers in __const or zero, and in sequential order * typeRefPointers must be a series of pointers in __const * MethodInvokePointers must be a series of pointers in __text or .text, and in sequential order */ if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.typeDefinitionsSizesCount) || CodeRegistration.reversePInvokeWrapperCount > 0x10000 || CodeRegistration.unresolvedVirtualCallCount > 0x4000 || // >= 22 CodeRegistration.interopDataCount > 0x1000 || // >= 23 (Image.Version <= 24.1 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount)) { throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again."); } // The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule if (Image.Version <= 24.1) { GlobalMethodPointers = Image.ReadMappedArray <ulong>(CodeRegistration.pmethodPointers, (int)CodeRegistration.methodPointersCount); } // After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules if (Image.Version >= 24.2) { Modules = new Dictionary <string, Il2CppCodeGenModule>(); // In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0; // if this changes we'll have to get smarter about disambiguating these two. if (CodeRegistration.codeGenModulesCount == 0) { Image.Version = 24.3; CodeRegistration = Image.ReadMappedObject <Il2CppCodeRegistration>(codeRegistration); } // Array of pointers to Il2CppCodeGenModule var codeGenModulePointers = Image.ReadMappedArray <ulong>(CodeRegistration.pcodeGenModules, (int)CodeRegistration.codeGenModulesCount); var modules = Image.ReadMappedObjectPointerArray <Il2CppCodeGenModule>(CodeRegistration.pcodeGenModules, (int)CodeRegistration.codeGenModulesCount); foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) { var module = mp.Module; var name = Image.ReadMappedNullTerminatedString(module.moduleName); Modules.Add(name, module); CodeGenModulePointers.Add(name, mp.Pointer); // Read method pointers // If a module contains only interfaces, abstract methods and/or non-concrete generic methods, // the entire method pointer array will be NULL values, causing the methodPointer to be mapped to .bss // and therefore out of scope of the binary image try { ModuleMethodPointers.Add(module, Image.ReadMappedArray <ulong>(module.methodPointers, (int)module.methodPointerCount)); } catch (InvalidOperationException) { ModuleMethodPointers.Add(module, new ulong[module.methodPointerCount]); } // Read method invoker pointer indices - one per method MethodInvokerIndices.Add(module, Image.ReadMappedArray <int>(module.invokerIndices, (int)module.methodPointerCount)); } } // Field offset data. Metadata <=21.x uses a value-type array; >=21.x uses a pointer array // Versions from 22 onwards use an array of pointers in Binary.FieldOffsetData bool fieldOffsetsArePointers = (Image.Version >= 22); // Some variants of 21 also use an array of pointers if (Image.Version == 21) { var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6); // We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32 // are always the first six defined types, and that all but Int32 have no fields fieldOffsetsArePointers = (fieldTest[0] == 0 && fieldTest[1] == 0 && fieldTest[2] == 0 && fieldTest[3] == 0 && fieldTest[4] == 0 && fieldTest[5] > 0); } // All older versions use values directly in the array if (!fieldOffsetsArePointers) { FieldOffsets = Image.ReadMappedArray <uint>(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); } else { FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount); } // Type references (pointer array) var typeRefPointers = Image.ReadMappedArray <ulong>(MetadataRegistration.ptypes, (int)MetadataRegistration.typesCount); TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i); TypeReferences = Image.ReadMappedObjectPointerArray <Il2CppType>(MetadataRegistration.ptypes, (int)MetadataRegistration.typesCount); // Custom attribute constructors (function pointers) // This is managed in Il2CppInspector for metadata >= 27 if (Image.Version < 27) { CustomAttributeGenerators = Image.ReadMappedArray <ulong>(CodeRegistration.customAttributeGenerators, (int)CodeRegistration.customAttributeCount); } // Method.Invoke function pointers MethodInvokePointers = Image.ReadMappedArray <ulong>(CodeRegistration.invokerPointers, (int)CodeRegistration.invokerPointersCount); // TODO: Function pointers as shown below // reversePInvokeWrappers // <=22: delegateWrappersFromManagedToNative, marshalingFunctions // >=21 <=22: ccwMarshalingFunctions // >=22: unresolvedVirtualCallPointers // >=23: interopData if (Image.Version < 19) { VTableMethodReferences = Image.ReadMappedArray <uint>(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount); } // Generic type and method specs (open and closed constructed types) MethodSpecs = Image.ReadMappedArray <Il2CppMethodSpec>(MetadataRegistration.methodSpecs, (int)MetadataRegistration.methodSpecsCount); // Concrete generic class and method signatures GenericInstances = Image.ReadMappedObjectPointerArray <Il2CppGenericInst>(MetadataRegistration.genericInsts, (int)MetadataRegistration.genericInstsCount); // Concrete generic method pointers var genericMethodPointers = Image.ReadMappedArray <ulong>(CodeRegistration.genericMethodPointers, (int)CodeRegistration.genericMethodPointersCount); var genericMethodTable = Image.ReadMappedArray <Il2CppGenericMethodFunctionsDefinitions>(MetadataRegistration.genericMethodTable, (int)MetadataRegistration.genericMethodTableCount); foreach (var tableEntry in genericMethodTable) { GenericMethodPointers.Add(MethodSpecs[tableEntry.genericMethodIndex], genericMethodPointers[tableEntry.indices.methodIndex]); GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.genericMethodIndex], tableEntry.indices.invokerIndex); } // Plugin hook to pre-process binary isModified |= PluginHooks.PostProcessBinary(this).IsStreamModified; }