Example #1
0
        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);
            }
        }
Example #2
0
        // 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;
        }