Esempio n. 1
0
        // See: IL2CPP/Il2CppBinary.cs
        // See: IL2CPP/Il2CppBinaryClasses.cs
        public void PostProcessBinary(Il2CppBinary binary, PluginPostProcessBinaryEventInfo data)
        {
            // This is called once per IL2CPP binary after it has been fully loaded

            // This is a direct parsing of the structs in the binary file
            // with some items re-arranged into eg. Dictionary collections for simplified access

            // CodeRegistration, MetadataRegistration, CodeGenModulePointers,
            // FieldOffsetPointers, MethodSpecs, GenericInstances, TypeReferences etc. etc.

            // See Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs for a complete list

            // Note that some items are only valid for certain IL2CPP versions
            // Use the Image.Version property to check the IL2CPP version

            // Example: list all CodeGenModules

            /*  Module mscorlib.dll has 11863 methods
            *   Module Mono.Security.dll has 421 methods
            *   Module System.Xml.dll has 1 methods
            *   Module System.dll has 3623 methods ... */
            if (binary.Image.Version >= 24.2)
            {
                foreach (var module in binary.Modules)
                {
                    Console.WriteLine($"Module {module.Key} has {module.Value.methodPointerCount} methods");
                }
            }

            // Set data.IsDataModified if you modify the Il2CppBinary
            // Set data.IsStreamModified if you modify the stream contents
        }
Esempio n. 2
0
        // See: IL2CPP/Il2CppBinary.cs
        public void PreProcessBinary(Il2CppBinary binary, PluginPreProcessBinaryEventInfo data)
        {
            // This is called once per found IL2CPP binary
            // after Il2CppCodeRegistration and Il2CppMetadataRegistration have been located and read
            // into binary.CodeRegistration and binary.MetadataRegistration,
            // but they have not been validated and no other loading or analysis has been performed

            // Example: check that the found structs make sense
            if (binary.CodeRegistration.interopDataCount > 0x1000)
            {
                // This is very unlikely
            }

            // You can acquire the underlying IFileFormatStream (BinaryObjectStream) with binary.Image
            var underlyingStream = binary.Image;

            // Set data.IsDataModified if you modify the Il2CppBinary
            // Set data.IsStreamModified if you modify the stream contents
        }
Esempio n. 3
0
        // Handle ROT name encryption found in some binaries
        public void PreProcessBinary(Il2CppBinary binary, PluginPreProcessBinaryEventInfo data)
        {
            // Get all exports
            var exports = binary.Image.GetExports()?.ToList();

            if (exports == null)
            {
                return;
            }

            // Try every ROT possibility (except 0 - these will already be added to APIExports)
            var exportRgx = new Regex(@"^_+");

            for (var rotKey = 1; rotKey <= 25; rotKey++)
            {
                var possibleExports = exports.Select(e => new {
                    Name           = string.Join("", e.Name.Select(x => (char)(x >= 'a' && x <= 'z'? (x - 'a' + rotKey) % 26 + 'a' : x))),
                    VirtualAddress = e.VirtualAddress
                }).ToList();

                var foundExports = possibleExports
                                   .Where(e => (e.Name.StartsWith("il2cpp_") || e.Name.StartsWith("_il2cpp_") || e.Name.StartsWith("__il2cpp_")) &&
                                          !e.Name.Contains("il2cpp_z_"))
                                   .Select(e => e);

                if (foundExports.Any() && !data.IsDataModified)
                {
                    PluginServices.For(this).StatusUpdate("Decrypting API export names");
                    data.IsDataModified = true;
                }

                foreach (var export in foundExports)
                {
                    if (binary.Image.TryMapVATR(export.VirtualAddress, out _))
                    {
                        binary.APIExports.Add(exportRgx.Replace(export.Name, ""), export.VirtualAddress);
                    }
                }
            }
        }
        private void LoadIlAppModel()
        {
            // Lazily create an Il2CppInspector model. Copying the ELF data array is required since the library
            // seems to mess with the data, which crashes the game.
            // TODO: Consider caching the result in a file. This is horribly slow.
            var binaryStream   = new MemoryStream(Data.ToArray());
            var metadataStream = new MemoryStream(Il2CppMetadata);

            var oldOut = Console.Out;

            // Disable Console.WriteLine() since Il2CppInspector logs text
            Console.SetOut(TextWriter.Null);

            try
            {
                // The internal class Il2CppInspector.ElfReader64 can be used to retrieve the offsets of sections
                // in the ELF file. These are relevant since Il2CppInspector saves symbol offsets relative to the
                // code section of the ELF so this offset needs to be considered when editing the ELF.
                var elfReader = CreateAndInitElfReader(binaryStream);
                ReadSectionOffsets(elfReader);

                // Create the inspector manually instead of using Il2CppInspector.LoadFromStream to avoid
                // reading the ELF a second time.
                var metadata  = new Metadata(metadataStream);
                var binary    = Il2CppBinary.Load(elfReader, metadata);
                var inspector = new Il2CppInspector.Il2CppInspector(binary, metadata);

                if (inspector == null)
                {
                    throw new Exception("Couldn't extract Il2Cpp metadata.");
                }

                ilAppModel = new AppModel(new TypeModel(inspector));
            }
            finally
            {
                Console.SetOut(oldOut);
            }
        }
Esempio n. 5
0
        // Guess which header file(s) correspond to the given metadata+binary.
        // Note that this may match multiple headers due to structural changes between versions
        // that are not reflected in the metadata version.
        public static List <UnityHeaders> GuessHeadersForBinary(Il2CppBinary binary)
        {
            List <UnityResource> typeHeaders = new List <UnityResource>();

            foreach (var r in GetAllTypeHeaders())
            {
                var metadataVersion = GetMetadataVersionFromFilename(r.Name);

                if (metadataVersion != binary.Image.Version)
                {
                    continue;
                }
                if (metadataVersion == 21)
                {
                    /* Special version logic for metadata version 21 based on the Il2CppMetadataRegistration.fieldOffsets field */
                    var headerFieldOffsetsArePointers = r.VersionRange.Min.CompareTo("5.3.7") >= 0 && r.VersionRange.Min.CompareTo("5.4.0") != 0;
                    var binaryFieldOffsetsArePointers = binary.FieldOffsets == null;
                    if (headerFieldOffsetsArePointers != binaryFieldOffsetsArePointers)
                    {
                        continue;
                    }
                }
                typeHeaders.Add(r);
            }

            // Get total range of selected headers
            // Sort is needed because 5.x.x comes before 20xx.x.x in the resource list
            typeHeaders = typeHeaders.OrderBy(x => x.VersionRange).ToList();
            var totalRange = new UnityVersionRange(typeHeaders.First().VersionRange.Min, typeHeaders.Last().VersionRange.Max);

            // Get all API versions in this range
            var apis = GetAllAPIHeaders().Where(a => a.VersionRange.Intersect(totalRange) != null).ToList();

            // Get the API exports for the binary
            var exports = binary.GetAPIExports();

            // No il2cpp exports? Just return the earliest version from the header range
            // The API version may be incorrect but should be a subset of the real API and won't cause C++ compile errors
            if (!exports.Any())
            {
                Console.WriteLine("No IL2CPP API exports found in binary - IL2CPP APIs will be unavailable in C++ project");

                return(typeHeaders.Select(t => new UnityHeaders(t,
                                                                apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList());
            }

            // Go through all of the possible API versions and see how closely they match the binary
            // Note: if apis.Count == 1, we can't actually narrow down the version range further,
            // but we still need to check that the APIs actually exist in the binary
            var apiMatches = new List <UnityResource>();

            foreach (var api in apis)
            {
                var apiFunctionList = GetFunctionNamesFromAPIHeaderText(api.GetText());

                // Every single function in the API list must be an export for a match
                if (!apiFunctionList.Except(exports.Keys).Any())
                {
                    apiMatches.Add(api);
                }
            }

            if (apiMatches.Any())
            {
                // Intersect all API ranges with all header ranges to produce final list of possible ranges
                Console.WriteLine("IL2CPP API discovery was successful");

                return(typeHeaders.SelectMany(
                           t => apiMatches.Where(a => t.VersionRange.Intersect(a.VersionRange) != null)
                           .Select(a => new UnityHeaders(t, a))).ToList());
            }

            // None of the possible API versions match the binary
            // Select the oldest API version from the group - C++ project compilation will fail
            Console.WriteLine("No exact match for IL2CPP APIs found in binary - IL2CPP API availability in C++ project will be partial");

            return(typeHeaders.Select(t => new UnityHeaders(t,
                                                            apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList());
        }