Beispiel #1
0
        public bool PlusSearch(int methodCount, int typeDefinitionsCount)
        {
            Console.WriteLine("Looking for registration functions...");

            var execList = new List <SectionHeader>();
            var dataList = new List <SectionHeader>();

            foreach (var section in sections)
            {
                switch (section.Characteristics)
                {
                case 0x60000020:
                    Console.WriteLine("\tIdentified execute section " + section.Name);
                    execList.Add(section);
                    break;

                case 0x40000040:
                case 0xC0000040:
                    Console.WriteLine("\tIdentified data section " + section.Name);
                    dataList.Add(section);
                    break;
                }
            }

            ulong codeRegistration;
            ulong metadataRegistration;

            Console.WriteLine("Attempting to locate code and metadata registration functions...");

            var plusSearch   = new PlusSearch(this, methodCount, typeDefinitionsCount, maxMetadataUsages);
            var dataSections = dataList.ToArray();
            var execSections = execList.ToArray();

            plusSearch.SetSearch(imageBase, dataSections);
            plusSearch.SetDataSections(imageBase, dataSections);
            plusSearch.SetExecSections(imageBase, execSections);
            if (is32Bit)
            {
                Console.WriteLine("\t(32-bit PE)");
                codeRegistration = plusSearch.FindCodeRegistration();
                plusSearch.SetExecSections(imageBase, dataSections);
                metadataRegistration = plusSearch.FindMetadataRegistration();
            }
            else
            {
                Console.WriteLine("\t(64-bit PE)");
                codeRegistration = plusSearch.FindCodeRegistration64Bit();
                plusSearch.SetExecSections(imageBase, dataSections);
                metadataRegistration = plusSearch.FindMetadataRegistration64Bit();
            }

            if (codeRegistration == 0 || metadataRegistration == 0)
            {
                Console.WriteLine("\tFailed to find code and metadata registration functions using primary location method (probably because we're post-2019), checking if we can use the fallback...");
                //Get export for il2cpp_init function
                var virtualAddrInit = GetVirtualAddressOfUnmanagedExportByName("il2cpp_init");
                if (virtualAddrInit <= 0)
                {
                    Console.WriteLine("\tCould not find exported il2cpp_init function! Fallback method failed, execution will fail!");
                    goto bailout;
                }

                Console.WriteLine($"\tFound il2cpp_init export (resolves to virtual addr 0x{virtualAddrInit:X}), using fallback method to find Code and Metadata registration...");
                List <Instruction> initMethodBody = Utils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtualAddrInit), false);

                //Look for a JMP for older il2cpp versions, on newer ones it appears to be a CALL
                var callToRuntimeInit = initMethodBody.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ijmp);

                if (callToRuntimeInit == null)
                {
                    callToRuntimeInit = initMethodBody.FindLast(i => i.Mnemonic == ud_mnemonic_code.UD_Icall);
                }

                if (callToRuntimeInit == null)
                {
                    Console.WriteLine("\tCould not find a call to Runtime::Init! Fallback method failed!");
                    goto bailout;
                }

                var virtualAddressRuntimeInit = Utils.GetJumpTarget(callToRuntimeInit, callToRuntimeInit.PC + virtualAddrInit);
                Console.WriteLine($"\tLocated probable Runtime::Init function at virtual addr 0x{virtualAddressRuntimeInit:X}");

                List <Instruction> methodBodyRuntimeInit = Utils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtualAddressRuntimeInit), false);

                //This is kind of sketchy, but look for a global read (i.e an LEA where the second base is RIP), as that's the framework version read, then there's a MOV, then 4 calls, the third of which is our target.
                //So as to ensure compat with 2018, ensure we have a call before this LEA.
                var minimumIndex = methodBodyRuntimeInit.FindIndex(i => i.Mnemonic == ud_mnemonic_code.UD_Icall);

                var idx = -1;
                var indexOfFrameworkVersionLoad = methodBodyRuntimeInit.FindIndex(i => idx++ > minimumIndex && i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands.Last().Base == ud_type.UD_R_RIP);

                var instructionsFromThatPoint = methodBodyRuntimeInit.Skip(indexOfFrameworkVersionLoad + 1).TakeWhile(i => true).ToList();
                var calls = instructionsFromThatPoint.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Icall).ToList();

                if (calls.Count < 3)
                {
                    Console.WriteLine("\tRuntime::Init does not call enough methods for us to locate ExecuteInitializations! Fallback failed!");
                    goto bailout;
                }

                var thirdCall = calls[2];

                //Now we have the address of the ExecuteInitializations function
                var virtAddrExecuteInit = Utils.GetJumpTarget(thirdCall, thirdCall.PC + virtualAddressRuntimeInit);
                Console.WriteLine($"\tLocated probable ExecuteInitializations function at virt addr 0x{virtAddrExecuteInit:X}");

                //We peek this as we only want the second instruction.
                List <Instruction> execInitMethodBody = Utils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtAddrExecuteInit), true);
                if (execInitMethodBody.Count < 2 || execInitMethodBody[1].Error || execInitMethodBody[1].Mnemonic != ud_mnemonic_code.UD_Imov)
                {
                    Console.WriteLine("\tMissing or invalid second instruction in ExecuteInitializations, fallback failed!");
                    goto bailout;
                }

                var offset = Utils.GetOffsetFromMemoryAccess(execInitMethodBody[1], execInitMethodBody[1].Operands[1]);

                //This SHOULD be the address of the global list of callbacks il2cpp executes on boot, which should only contain one item, that being the function which invokes the code + metadata registration
                var addrGlobalCallbackList = virtAddrExecuteInit + offset;

                var bytesNotToCheck = execInitMethodBody[1].Bytes;
                Console.WriteLine($"\tGot what we believe is the address of the global callback list - 0x{addrGlobalCallbackList:X}. Searching for another MOV instruction that references it within the .text segment...");

                var textSection = sections.First(s => s.Name == ".text");
                var toDisasm    = raw.SubArray((int)textSection.PointerToRawData, (int)textSection.SizeOfRawData);
                var allInstructionsInTextSection = Utils.DisassembleBytes(toDisasm);

                Console.WriteLine($"\tDisassembled entire .text section, into {allInstructionsInTextSection.Count} instructions.");

                var allMOVs = allInstructionsInTextSection.Where(i => i.Mnemonic == ud_mnemonic_code.UD_Imov && i.Operands[0].Base == ud_type.UD_R_RIP).ToList();

                Console.WriteLine($"\t\t...of which {allMOVs.Count} are MOV instructions with a global/Rip first base");

                var references = allMOVs.AsParallel().Where(mov =>
                {
                    var rawMemoryRead  = Utils.GetOffsetFromMemoryAccess(mov, mov.Operands[0]);
                    var virtMemoryRead = rawMemoryRead + textSection.VirtualAddress + imageBase;
                    return(virtMemoryRead == addrGlobalCallbackList);
                }).ToList();

                Console.WriteLine($"\t\t...of which {references.Count} have a first parameter as that callback list.");

                if (references.Count != 1)
                {
                    Console.WriteLine("\tExpected only one reference, but didn't get that, fallback failed!");
                    goto bailout;
                }

                var callbackListWrite           = references[0];
                var virtualAddressOfInstruction = callbackListWrite.PC + imageBase + textSection.VirtualAddress - (ulong)callbackListWrite.Length;
                Console.WriteLine($"\tLocated a single write reference to callback list, therefore identified callback registration function, which must contain the instruction at virt address 0x{virtualAddressOfInstruction:X}");

                var instructionIdx = allInstructionsInTextSection.IndexOf(callbackListWrite);
                var instructionsUpToCallbackListWrite = allInstructionsInTextSection.Take(instructionIdx).ToList();
                instructionsUpToCallbackListWrite.Reverse();

                var indexOfFirstInt3 = instructionsUpToCallbackListWrite.FindIndex(i => i.Mnemonic == ud_mnemonic_code.UD_Iint3);
                var firstInstructionInRegisterCallback = instructionsUpToCallbackListWrite[indexOfFirstInt3 - 1];

                var virtAddrRegisterCallback = firstInstructionInRegisterCallback.PC + imageBase + textSection.VirtualAddress - (ulong)firstInstructionInRegisterCallback.Length;

                Console.WriteLine($"\tGot address of register callback function to be 0x{virtAddrRegisterCallback:X}");

                var callToRegisterCallback = allInstructionsInTextSection.Find(i => (i.Mnemonic == ud_mnemonic_code.UD_Icall || i.Mnemonic == ud_mnemonic_code.UD_Ijmp) && Utils.GetJumpTarget(i, imageBase + textSection.VirtualAddress + i.PC) == virtAddrRegisterCallback);

                var addrCallToRegCallback = callToRegisterCallback.PC + imageBase + textSection.VirtualAddress - (ulong)callToRegisterCallback.Length;
                Console.WriteLine($"\tFound a call to that function at 0x{addrCallToRegCallback:X}");

                var         indexOfCallToRegisterCallback = allInstructionsInTextSection.IndexOf(callToRegisterCallback);
                Instruction loadOfAddressToCodegenRegistrationFunction = null;
                for (var i = indexOfCallToRegisterCallback; i > 0; i--)
                {
                    if (allInstructionsInTextSection[i].Mnemonic == ud_mnemonic_code.UD_Ilea && allInstructionsInTextSection[i].Operands[0].Base == ud_type.UD_R_RDX)
                    {
                        loadOfAddressToCodegenRegistrationFunction = allInstructionsInTextSection[i];
                        break;
                    }
                }

                if (loadOfAddressToCodegenRegistrationFunction == null)
                {
                    Console.WriteLine("Failed to find an instruction loading the address of the codegen reg function. Fallback failed.");
                    goto bailout;
                }

                Console.WriteLine("\tGot instruction containing the address of the codegen registration function: " + loadOfAddressToCodegenRegistrationFunction);
                var virtAddrS_Il2CppCodegenRegistration = Utils.GetOffsetFromMemoryAccess(loadOfAddressToCodegenRegistrationFunction, loadOfAddressToCodegenRegistrationFunction.Operands[1]) + imageBase + textSection.VirtualAddress;
                Console.WriteLine($"\tWhich means s_Il2CppCodegenRegistration is in-binary at 0x{virtAddrS_Il2CppCodegenRegistration:X}");

                //This should consist of LEA, LEA, LEA, JMP
                List <Instruction> methodBodyS_Il2CppCodegenRegistration = Utils.GetMethodBodyAtRawAddress(this, MapVirtualAddressToRaw(virtAddrS_Il2CppCodegenRegistration), false);

                var loadMetadataRegistration = methodBodyS_Il2CppCodegenRegistration.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands[0].Base == ud_type.UD_R_RDX);
                var loadCodeRegistration     = methodBodyS_Il2CppCodegenRegistration.Find(i => i.Mnemonic == ud_mnemonic_code.UD_Ilea && i.Operands[0].Base == ud_type.UD_R_RCX); //This one's RCX not RDX

                metadataRegistration = Utils.GetOffsetFromMemoryAccess(loadMetadataRegistration, loadMetadataRegistration.Operands[1]) + virtAddrS_Il2CppCodegenRegistration;
                codeRegistration     = Utils.GetOffsetFromMemoryAccess(loadCodeRegistration, loadCodeRegistration.Operands[1]) + virtAddrS_Il2CppCodegenRegistration;
            }

bailout:
            Console.WriteLine("Initializing with located addresses:");
            return(AutoInit(codeRegistration, metadataRegistration));
        }
Beispiel #2
0
        public void Init(ulong codeRegistration, ulong metadataRegistration)
        {
            Console.WriteLine("Initializing PE data...");
            this.codeRegistration     = ReadClassAtVirtualAddress <Il2CppCodeRegistration>(codeRegistration);
            this.metadataRegistration = ReadClassAtVirtualAddress <Il2CppMetadataRegistration>(metadataRegistration);

            Console.Write("\tReading generic instances...");
            var start = DateTime.Now;

            genericInsts = Array.ConvertAll(ReadClassArrayAtVirtualAddress <ulong>(this.metadataRegistration.genericInsts, this.metadataRegistration.genericInstsCount), x => ReadClassAtVirtualAddress <Il2CppGenericInst>(x));
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading generic method pointers...");
            start = DateTime.Now;
            genericMethodPointers = ReadClassArrayAtVirtualAddress <ulong>(this.codeRegistration.genericMethodPointers, (long)this.codeRegistration.genericMethodPointersCount);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading invoker pointers...");
            start           = DateTime.Now;
            invokerPointers = ReadClassArrayAtVirtualAddress <ulong>(this.codeRegistration.invokerPointers, (long)this.codeRegistration.invokerPointersCount);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading custom attribute generators...");
            start = DateTime.Now;
            customAttributeGenerators = ReadClassArrayAtVirtualAddress <ulong>(this.codeRegistration.customAttributeGeneratorListAddress, this.codeRegistration.customAttributeCount);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading field offsets...");
            start        = DateTime.Now;
            fieldOffsets = ReadClassArrayAtVirtualAddress <long>(this.metadataRegistration.fieldOffsetListAddress, this.metadataRegistration.fieldOffsetsCount);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading types...");
            start = DateTime.Now;
            var typesAddress = ReadClassArrayAtVirtualAddress <ulong>(this.metadataRegistration.typeAddressListAddress, this.metadataRegistration.numTypes);

            types = new Il2CppType[this.metadataRegistration.numTypes];
            for (var i = 0; i < this.metadataRegistration.numTypes; ++i)
            {
                types[i] = ReadClassAtVirtualAddress <Il2CppType>(typesAddress[i]);
                types[i].Init();
                typesDict.Add(typesAddress[i], types[i]);
            }

            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading metadata usages...");
            start          = DateTime.Now;
            metadataUsages = ReadClassArrayAtVirtualAddress <ulong>(this.metadataRegistration.metadataUsages, maxMetadataUsages);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            if (Program.MetadataVersion >= 24.2f)
            {
                Console.WriteLine("\tReading code gen modules...");
                start = DateTime.Now;

                var codeGenModulePtrs = ReadClassArrayAtVirtualAddress <ulong>(this.codeRegistration.addrCodeGenModulePtrs, (long)this.codeRegistration.codeGenModulesCount);
                codeGenModules = new Il2CppCodeGenModule[codeGenModulePtrs.Length];
                codeGenModuleMethodPointers = new ulong[codeGenModulePtrs.Length][];
                for (int i = 0; i < codeGenModulePtrs.Length; i++)
                {
                    var codeGenModule = ReadClassAtVirtualAddress <Il2CppCodeGenModule>(codeGenModulePtrs[i]);
                    codeGenModules[i] = codeGenModule;
                    string name = ReadStringToNull(MapVirtualAddressToRaw(codeGenModule.moduleName));
                    Console.WriteLine($"\t\t-Read module data for {name}, contains {codeGenModule.methodPointerCount} method pointers starting at 0x{codeGenModule.methodPointers:X}");
                    if (codeGenModule.methodPointerCount > 0)
                    {
                        try
                        {
                            var ptrs = ReadClassArrayAtVirtualAddress <ulong>(codeGenModule.methodPointers, codeGenModule.methodPointerCount);
                            codeGenModuleMethodPointers[i] = ptrs;
                            Console.WriteLine($"\t\t\t-Read {codeGenModule.methodPointerCount} method pointers.");
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine($"\t\t\tWARNING: Unable to get function pointers for {name}: {e.Message}");
                            codeGenModuleMethodPointers[i] = new ulong[codeGenModule.methodPointerCount];
                        }
                    }
                }

                Console.WriteLine($"\tOK ({(DateTime.Now - start).TotalMilliseconds} ms)");
            }
            else
            {
                Console.Write("\tReading method pointers...");
                start          = DateTime.Now;
                methodPointers = ReadClassArrayAtVirtualAddress <ulong>(this.codeRegistration.methodPointers, (long)this.codeRegistration.methodPointersCount);
                Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
            }


            Console.Write("\tReading generic method tables...");
            start = DateTime.Now;
            genericMethodTables = ReadClassArrayAtVirtualAddress <Il2CppGenericMethodFunctionsDefinitions>(this.metadataRegistration.genericMethodTable, this.metadataRegistration.genericMethodTableCount);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading method specifications...");
            start       = DateTime.Now;
            methodSpecs = ReadClassArrayAtVirtualAddress <Il2CppMethodSpec>(this.metadataRegistration.methodSpecs, this.metadataRegistration.methodSpecsCount);
            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");

            Console.Write("\tReading generic methods...");
            start = DateTime.Now;
            genericMethodDictionary = new Dictionary <int, ulong>(genericMethodTables.Length);
            foreach (var table in genericMethodTables)
            {
                var index = methodSpecs[table.genericMethodIndex].methodDefinitionIndex;
                if (!genericMethodDictionary.ContainsKey(index))
                {
                    genericMethodDictionary.Add(index, genericMethodPointers[table.indices.methodIndex]);
                }
            }

            Console.WriteLine($"OK ({(DateTime.Now - start).TotalMilliseconds} ms)");
        }