├── Il2CppDumper ├── Libraries │ └── Il2CppDummyDll.dll ├── Utils │ ├── AttributeArgument.cs │ ├── BlobValue.cs │ ├── MyAssemblyResolver.cs │ ├── CustomAttributeReaderVisitor.cs │ ├── SearchSection.cs │ ├── ArmUtils.cs │ ├── OpenFileDialog.cs │ ├── CustomAttributeDataReader.cs │ ├── FileDialogNative.cs │ └── SectionHelper.cs ├── Attributes │ ├── ArrayLengthAttribute.cs │ └── VersionAttribute.cs ├── ExecutableFormats │ ├── ElfBase.cs │ ├── MachoClass.cs │ ├── MachoFat.cs │ ├── ElfClass.cs │ ├── Macho.cs │ ├── Macho64.cs │ └── Elf64.cs ├── config.json ├── Extensions │ ├── HexExtensions.cs │ ├── StringExtensions.cs │ ├── BinaryReaderExtensions.cs │ └── BoyerMooreHorspool.cs ├── hopper-py3.py ├── Outputs │ ├── DummyAssemblyExporter.cs │ ├── StructInfo.cs │ ├── ScriptJson.cs │ └── Il2CppConstants.cs ├── Config.cs ├── il2cpp_header_to_ghidra.py ├── il2cpp_header_to_binja.py ├── Il2CppBinaryNinja │ ├── plugin.json │ └── __init__.py ├── Il2CppDumper.csproj ├── ida_py3.py ├── ida.py ├── Resource1.Designer.cs ├── ghidra.py ├── ida_with_struct_py3.py ├── ida_with_struct.py ├── ghidra_wasm.py ├── ghidra_with_struct.py ├── Resource1.resx ├── IO │ └── BinaryStream.cs ├── Program.cs └── Il2Cpp │ ├── Metadata.cs │ ├── Il2CppClass.cs │ └── MetadataClass.cs ├── README.md ├── LICENSE ├── Il2CppDumper.sln ├── .gitattributes └── .gitignore /Il2CppDumper/Libraries/Il2CppDummyDll.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Poko-Apps/Il2CppDumperCODM/HEAD/Il2CppDumper/Libraries/Il2CppDummyDll.dll -------------------------------------------------------------------------------- /Il2CppDumper/Utils/AttributeArgument.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class AttributeArgument 4 | { 5 | public BlobValue Value; 6 | public int Index; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/BlobValue.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class BlobValue 4 | { 5 | public object Value; 6 | public Il2CppTypeEnum il2CppTypeEnum; 7 | public Il2CppType EnumType; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Il2CppDumper/Attributes/ArrayLengthAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Il2CppDumper 4 | { 5 | [AttributeUsage(AttributeTargets.Field)] 6 | class ArrayLengthAttribute : Attribute 7 | { 8 | public int Length { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/MyAssemblyResolver.cs: -------------------------------------------------------------------------------- 1 | using Mono.Cecil; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public class MyAssemblyResolver : DefaultAssemblyResolver 6 | { 7 | public void Register(AssemblyDefinition assembly) 8 | { 9 | RegisterAssembly(assembly); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Il2CppDumperCODM 2 | A modified version of il2cppdumper that supports dumping call of duty mobile only. **iOS** and **Android** Both supports . 3 | ## Usage 4 | Use it just like original il2cppdumper by perfare. everything written in official repository so read it there. 5 | [Il2CppDumper](https://github.com/Perfare/Il2CppDumper) -------------------------------------------------------------------------------- /Il2CppDumper/Attributes/VersionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Il2CppDumper 4 | { 5 | [AttributeUsage(AttributeTargets.Field, AllowMultiple = true)] 6 | class VersionAttribute : Attribute 7 | { 8 | public double Min { get; set; } = 0; 9 | public double Max { get; set; } = 99; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/CustomAttributeReaderVisitor.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class CustomAttributeReaderVisitor 4 | { 5 | public int CtorIndex; 6 | public AttributeArgument[] Arguments; 7 | public AttributeArgument[] Fields; 8 | public AttributeArgument[] Properties; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/SearchSection.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public enum SearchSectionType 4 | { 5 | Exec, 6 | Data, 7 | Bss 8 | } 9 | 10 | public class SearchSection 11 | { 12 | public ulong offset; 13 | public ulong offsetEnd; 14 | public ulong address; 15 | public ulong addressEnd; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/ElfBase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public abstract class ElfBase : Il2Cpp 6 | { 7 | protected ElfBase(Stream stream) : base(stream) { } 8 | protected abstract void Load(); 9 | protected abstract bool CheckSection(); 10 | 11 | public override bool CheckDump() => !CheckSection(); 12 | 13 | public void Reload() => Load(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Il2CppDumper/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "DumpMethod": true, 3 | "DumpField": true, 4 | "DumpProperty": true, 5 | "DumpAttribute": true, 6 | "DumpFieldOffset": true, 7 | "DumpMethodOffset": true, 8 | "DumpTypeDefIndex": true, 9 | "GenerateDummyDll": true, 10 | "GenerateStruct": true, 11 | "DummyDllAddToken": true, 12 | "RequireAnyKey": true, 13 | "ForceIl2CppVersion": false, 14 | "ForceVersion": 16, 15 | "ForceDump": false, 16 | "NoRedirectedPointer": false 17 | } -------------------------------------------------------------------------------- /Il2CppDumper/Extensions/HexExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace Il2CppDumper 5 | { 6 | static class HexExtensions 7 | { 8 | public static string HexToBin(this byte b) 9 | { 10 | return Convert.ToString(b, 2).PadLeft(8, '0'); 11 | } 12 | 13 | public static string HexToBin(this byte[] bytes) 14 | { 15 | var result = new StringBuilder(bytes.Length * 8); 16 | foreach (var b in bytes) 17 | { 18 | result.Insert(0, b.HexToBin()); 19 | } 20 | return result.ToString(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/MachoClass.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class MachoSection 4 | { 5 | public string sectname; 6 | public uint addr; 7 | public uint size; 8 | public uint offset; 9 | public uint flags; 10 | } 11 | 12 | public class MachoSection64Bit 13 | { 14 | public string sectname; 15 | public ulong addr; 16 | public ulong size; 17 | public ulong offset; 18 | public uint flags; 19 | } 20 | 21 | public class Fat 22 | { 23 | public uint offset; 24 | public uint size; 25 | public uint magic; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Il2CppDumper/hopper-py3.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import json 3 | 4 | def deserializeJSON(script_file): 5 | if script_file is not None: 6 | f = codecs.open(script_file, "r","utf-8") 7 | 8 | # Reading from file 9 | data = json.loads(f.read()) 10 | f.close() 11 | 12 | return data 13 | 14 | def changeAddressNames(script): 15 | for i in script['ScriptMethod']: 16 | addr = i['Address'] 17 | name = i['Name'] 18 | #sig = i['Signature'] 19 | #typesig = i['TypeSignature'] 20 | 21 | #print(addr, name) 22 | doc.setNameAtAddress(addr, name) 23 | 24 | return 25 | 26 | def main(): 27 | script_file = doc.askFile('Select script.py', None, None) 28 | script = deserializeJSON(script_file) 29 | changeAddressNames(script) 30 | 31 | doc = Document.getCurrentDocument() 32 | main() 33 | -------------------------------------------------------------------------------- /Il2CppDumper/Outputs/DummyAssemblyExporter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public static class DummyAssemblyExporter 6 | { 7 | public static void Export(Il2CppExecutor il2CppExecutor, string outputDir, bool addToken) 8 | { 9 | Directory.SetCurrentDirectory(outputDir); 10 | if (Directory.Exists("DummyDll")) 11 | Directory.Delete("DummyDll", true); 12 | Directory.CreateDirectory("DummyDll"); 13 | Directory.SetCurrentDirectory("DummyDll"); 14 | var dummy = new DummyAssemblyGenerator(il2CppExecutor, addToken); 15 | foreach (var assembly in dummy.Assemblies) 16 | { 17 | using var stream = new MemoryStream(); 18 | assembly.Write(stream); 19 | File.WriteAllBytes(assembly.MainModule.Name, stream.ToArray()); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Il2CppDumper/Config.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class Config 4 | { 5 | public bool DumpMethod { get; set; } = true; 6 | public bool DumpField { get; set; } = true; 7 | public bool DumpProperty { get; set; } = false; 8 | public bool DumpAttribute { get; set; } = false; 9 | public bool DumpFieldOffset { get; set; } = true; 10 | public bool DumpMethodOffset { get; set; } = true; 11 | public bool DumpTypeDefIndex { get; set; } = true; 12 | public bool GenerateDummyDll { get; set; } = true; 13 | public bool GenerateStruct { get; set; } = true; 14 | public bool DummyDllAddToken { get; set; } = true; 15 | public bool RequireAnyKey { get; set; } = true; 16 | public bool ForceIl2CppVersion { get; set; } = false; 17 | public double ForceVersion { get; set; } = 24.3; 18 | public bool ForceDump { get; set; } = false; 19 | public bool NoRedirectedPointer { get; set; } = false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Il2CppDumper/Outputs/StructInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Il2CppDumper 5 | { 6 | public class StructInfo 7 | { 8 | public string TypeName; 9 | public bool IsValueType; 10 | public string Parent; 11 | public List Fields = new(); 12 | public List StaticFields = new(); 13 | public StructVTableMethodInfo[] VTableMethod = Array.Empty(); 14 | public List RGCTXs = new(); 15 | } 16 | 17 | public class StructFieldInfo 18 | { 19 | public string FieldTypeName; 20 | public string FieldName; 21 | public bool IsValueType; 22 | public bool IsCustomType; 23 | } 24 | 25 | public class StructVTableMethodInfo 26 | { 27 | public string MethodName; 28 | } 29 | 30 | public class StructRGCTXInfo 31 | { 32 | public Il2CppRGCTXDataType Type; 33 | public string TypeName; 34 | public string ClassName; 35 | public string MethodName; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Il2CppDumper/Outputs/ScriptJson.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public class ScriptJson 6 | { 7 | public List ScriptMethod = new(); 8 | public List ScriptString = new(); 9 | public List ScriptMetadata = new(); 10 | public List ScriptMetadataMethod = new(); 11 | public ulong[] Addresses; 12 | } 13 | 14 | public class ScriptMethod 15 | { 16 | public ulong Address; 17 | public string Name; 18 | public string Signature; 19 | public string TypeSignature; 20 | } 21 | 22 | public class ScriptString 23 | { 24 | public ulong Address; 25 | public string Value; 26 | } 27 | 28 | public class ScriptMetadata 29 | { 30 | public ulong Address; 31 | public string Name; 32 | public string Signature; 33 | } 34 | 35 | public class ScriptMetadataMethod 36 | { 37 | public ulong Address; 38 | public string Name; 39 | public ulong MethodAddress; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Perfare 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Il2CppDumper.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32228.430 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Il2CppDumper", "Il2CppDumper\Il2CppDumper.csproj", "{2087F99A-A655-41C1-84BB-54798AEA4080}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2087F99A-A655-41C1-84BB-54798AEA4080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {2087F99A-A655-41C1-84BB-54798AEA4080}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {2087F99A-A655-41C1-84BB-54798AEA4080}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {2087F99A-A655-41C1-84BB-54798AEA4080}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {E570C2EE-9A67-4FA2-A564-FB23AD4800C9} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/MachoFat.cs: -------------------------------------------------------------------------------- 1 | using System.Buffers.Binary; 2 | using System.IO; 3 | 4 | namespace Il2CppDumper 5 | { 6 | public sealed class MachoFat : BinaryStream 7 | { 8 | public Fat[] fats; 9 | 10 | public MachoFat(Stream stream) : base(stream) 11 | { 12 | Position += 4; 13 | var size = BinaryPrimitives.ReadInt32BigEndian(ReadBytes(4)); 14 | fats = new Fat[size]; 15 | for (var i = 0; i < size; i++) 16 | { 17 | Position += 8; 18 | fats[i] = new Fat 19 | { 20 | offset = BinaryPrimitives.ReadUInt32BigEndian(ReadBytes(4)), 21 | size = BinaryPrimitives.ReadUInt32BigEndian(ReadBytes(4)) 22 | }; 23 | Position += 4; 24 | } 25 | for (var i = 0; i < size; i++) 26 | { 27 | Position = fats[i].offset; 28 | fats[i].magic = ReadUInt32(); 29 | } 30 | } 31 | 32 | public byte[] GetMacho(int index) 33 | { 34 | Position = fats[index].offset; 35 | return ReadBytes((int)fats[index].size); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Il2CppDumper/il2cpp_header_to_ghidra.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | header = "typedef unsigned __int8 uint8_t;\n" \ 4 | "typedef unsigned __int16 uint16_t;\n" \ 5 | "typedef unsigned __int32 uint32_t;\n" \ 6 | "typedef unsigned __int64 uint64_t;\n" \ 7 | "typedef __int8 int8_t;\n" \ 8 | "typedef __int16 int16_t;\n" \ 9 | "typedef __int32 int32_t;\n" \ 10 | "typedef __int64 int64_t;\n" \ 11 | "typedef __int64 intptr_t;\n" \ 12 | "typedef __int64 uintptr_t;\n" \ 13 | "typedef unsigned __int64 size_t;\n" \ 14 | "typedef _Bool bool;\n" 15 | 16 | 17 | def main(): 18 | fixed_header_data = "" 19 | with open("il2cpp.h", 'r') as f: 20 | print("il2cpp.h opened...") 21 | original_header_data = f.read() 22 | print("il2cpp.h read...") 23 | fixed_header_data = re.sub(r": (\w+) {", r"{\n \1 super;", original_header_data) 24 | print("il2cpp.h data fixed...") 25 | print("il2cpp.h closed.") 26 | with open("il2cpp_ghidra.h", 'w') as f: 27 | print("il2cpp_ghidra.h opened...") 28 | f.write(header) 29 | print("header written...") 30 | f.write(fixed_header_data) 31 | print("fixed data written...") 32 | print("il2cpp_ghidra.h closed.") 33 | 34 | 35 | if __name__ == '__main__': 36 | print("Script started...") 37 | main() 38 | -------------------------------------------------------------------------------- /Il2CppDumper/il2cpp_header_to_binja.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | data = open("./il2cpp.h").read() 4 | 5 | builtin = ["void", "intptr_t", "uint32_t", "uint16_t", "int32_t", "uint8_t", "bool", 6 | "int64_t", "uint64_t", "double", "int16_t", "int8_t", "float", "uintptr_t", 7 | "const", "union", "{", "};", "il2cpp_array_size_t", "il2cpp_array_lower_bound_t", 8 | "struct", "Il2CppMethodPointer"] 9 | structs = [] 10 | notfound = [] 11 | header = "" 12 | 13 | for line in data.splitlines(): 14 | if line.startswith("struct") or line.startswith("union"): 15 | struct = line.split()[1] 16 | if struct.endswith(";"): 17 | struct = struct[:-1] 18 | structs.append(struct) 19 | if line.startswith("\t"): 20 | struct = line[1:].split()[0] 21 | if struct == "struct": 22 | struct = line[1:].split()[1] 23 | if struct.endswith("*"): 24 | struct = struct[:-1] 25 | if struct.endswith("*"): 26 | struct = struct[:-1] 27 | if struct in builtin: 28 | continue 29 | if struct not in structs and struct not in notfound: 30 | notfound.append(struct) 31 | for struct in notfound: 32 | header += f"struct {struct};" + "\n" 33 | to_replace = re.findall("struct (.*) {\n};", data) 34 | for item in to_replace: 35 | data = data.replace("struct "+item+" {\n};", "") 36 | data = data.replace("\t"+item.split()[0]+" ", "\tvoid *") 37 | data = data.replace("\t struct "+item.split()[0]+" ", "\t void *") 38 | data = re.sub(r": (\w+) {", r"{\n\t\1 super;", data) 39 | with open("./il2cpp_binja.h", "w") as f: 40 | f.write(header) 41 | f.write(data) 42 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/ArmUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Il2CppDumper 4 | { 5 | static class ArmUtils 6 | { 7 | public static uint DecodeMov(byte[] asm) 8 | { 9 | var low = (ushort)(asm[2] + ((asm[3] & 0x70) << 4) + ((asm[1] & 0x04) << 9) + ((asm[0] & 0x0f) << 12)); 10 | var high = (ushort)(asm[6] + ((asm[7] & 0x70) << 4) + ((asm[5] & 0x04) << 9) + ((asm[4] & 0x0f) << 12)); 11 | return (uint)((high << 16) + low); 12 | } 13 | 14 | public static ulong DecodeAdr(ulong pc, byte[] inst) 15 | { 16 | var bin = inst.HexToBin(); 17 | var uint64 = string.Concat(bin.AsSpan(8, 19), bin.AsSpan(1, 2)); 18 | uint64 = uint64.PadLeft(64, uint64[0]); 19 | return pc + Convert.ToUInt64(uint64, 2); 20 | } 21 | 22 | public static ulong DecodeAdrp(ulong pc, byte[] inst) 23 | { 24 | pc &= 0xFFFFFFFFFFFFF000; 25 | var bin = inst.HexToBin(); 26 | var uint64 = string.Concat(bin.AsSpan(8, 19), bin.AsSpan(1, 2), new string('0', 12)); 27 | uint64 = uint64.PadLeft(64, uint64[0]); 28 | return pc + Convert.ToUInt64(uint64, 2); 29 | } 30 | 31 | public static ulong DecodeAdd(byte[] inst) 32 | { 33 | var bin = inst.HexToBin(); 34 | var uint64 = Convert.ToUInt64(bin.Substring(10, 12), 2); 35 | if (bin[9] == '1') 36 | uint64 <<= 12; 37 | return uint64; 38 | } 39 | 40 | public static bool IsAdr(byte[] inst) 41 | { 42 | var bin = inst.HexToBin(); 43 | return bin[0] == '0' && bin.Substring(3, 5) == "10000"; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/OpenFileDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static Il2CppDumper.FileDialogNative; 3 | 4 | namespace Il2CppDumper 5 | { 6 | public class OpenFileDialog 7 | { 8 | public string Title { get; set; } 9 | public string Filter { get; set; } 10 | public string FileName { get; set; } 11 | 12 | public bool ShowDialog() 13 | { 14 | var dialog = (IFileDialog)(new FileOpenDialogRCW()); 15 | dialog.GetOptions(out var options); 16 | options |= FOS.FOS_FORCEFILESYSTEM | FOS.FOS_NOVALIDATE | FOS.FOS_DONTADDTORECENT; 17 | dialog.SetOptions(options); 18 | if (!string.IsNullOrEmpty(Title)) 19 | { 20 | dialog.SetTitle(Title); 21 | } 22 | if (!string.IsNullOrEmpty(Filter)) 23 | { 24 | string[] filterElements = Filter.Split(new char[] { '|' }); 25 | COMDLG_FILTERSPEC[] filter = new COMDLG_FILTERSPEC[filterElements.Length / 2]; 26 | for (int x = 0; x < filterElements.Length; x += 2) 27 | { 28 | filter[x / 2].pszName = filterElements[x]; 29 | filter[x / 2].pszSpec = filterElements[x + 1]; 30 | } 31 | dialog.SetFileTypes((uint)filter.Length, filter); 32 | } 33 | if (dialog.Show(IntPtr.Zero) == 0) 34 | { 35 | dialog.GetResult(out var shellItem); 36 | shellItem.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var ppszName); 37 | FileName = ppszName; 38 | return true; 39 | } 40 | else 41 | { 42 | return false; 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Il2CppDumper/Il2CppBinaryNinja/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginmetadataversion": 2, 3 | "name": "Il2CppDumper", 4 | "type": [ 5 | "core", 6 | "ui", 7 | "binaryview" 8 | ], 9 | "api": [ 10 | "python3" 11 | ], 12 | "description": "Add Il2Cpp structs and method signatures", 13 | "longdescription": "", 14 | "license": { 15 | "name": "MIT", 16 | "text": "Copyright (c) 2022 Il2CppDumper contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." 17 | }, 18 | "platforms": [ 19 | "Darwin", 20 | "Linux", 21 | "Windows" 22 | ], 23 | "installinstructions": { 24 | "Darwin": "Install Il2CppDumper", 25 | "Linux": "Install Il2CppDumper", 26 | "Windows": "Install Il2CppDumper" 27 | }, 28 | "dependencies": { 29 | }, 30 | "version": "1.0.0", 31 | "author": "Il2CppDumper contributors", 32 | "minimumbinaryninjaversion": 3164 33 | } 34 | -------------------------------------------------------------------------------- /Il2CppDumper/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public static class StringExtensions 6 | { 7 | public static string ToEscapedString(this string s) 8 | { 9 | var re = new StringBuilder(s.Length); 10 | foreach (var c in s) 11 | { 12 | switch (c) 13 | { 14 | case '\'': 15 | re.Append(@"\'"); 16 | break; 17 | case '"': 18 | re.Append(@"\"""); 19 | break; 20 | case '\\': 21 | re.Append(@"\\"); 22 | break; 23 | case '\0': 24 | re.Append(@"\0"); 25 | break; 26 | case '\a': 27 | re.Append(@"\a"); 28 | break; 29 | case '\b': 30 | re.Append(@"\b"); 31 | break; 32 | case '\f': 33 | re.Append(@"\f"); 34 | break; 35 | case '\n': 36 | re.Append(@"\n"); 37 | break; 38 | case '\r': 39 | re.Append(@"\r"); 40 | break; 41 | case '\t': 42 | re.Append(@"\t"); 43 | break; 44 | case '\v': 45 | re.Append(@"\v"); 46 | break; 47 | case '\u0085': 48 | re.Append(@"\u0085"); 49 | break; 50 | case '\u2028': 51 | re.Append(@"\u2028"); 52 | break; 53 | case '\u2029': 54 | re.Append(@"\u2029"); 55 | break; 56 | default: 57 | re.Append(c); 58 | break; 59 | } 60 | } 61 | return re.ToString(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Il2CppDumper/Il2CppDumper.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0;net8.0 6 | 1.0.47 7 | 1.0.47 8 | 1.0.47 9 | Copyright © Perfare 2016-2024 (CodM by Poko) 10 | embedded 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | True 21 | True 22 | Resource1.resx 23 | 24 | 25 | 26 | 27 | 28 | ResXFileCodeGenerator 29 | Resource1.Designer.cs 30 | 31 | 32 | 33 | 34 | 35 | PreserveNewest 36 | 37 | 38 | PreserveNewest 39 | 40 | 41 | PreserveNewest 42 | 43 | 44 | PreserveNewest 45 | 46 | 47 | PreserveNewest 48 | 49 | 50 | PreserveNewest 51 | 52 | 53 | PreserveNewest 54 | 55 | 56 | PreserveNewest 57 | 58 | 59 | PreserveNewest 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Il2CppDumper/ida_py3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | processFields = [ 5 | "ScriptMethod", 6 | "ScriptString", 7 | "ScriptMetadata", 8 | "ScriptMetadataMethod", 9 | "Addresses", 10 | ] 11 | 12 | imageBase = idaapi.get_imagebase() 13 | 14 | def get_addr(addr): 15 | return imageBase + addr 16 | 17 | def set_name(addr, name): 18 | ret = idc.set_name(addr, name, SN_NOWARN | SN_NOCHECK) 19 | if ret == 0: 20 | new_name = name + '_' + str(addr) 21 | ret = idc.set_name(addr, new_name, SN_NOWARN | SN_NOCHECK) 22 | 23 | def make_function(start, end): 24 | next_func = idc.get_next_func(start) 25 | if next_func < end: 26 | end = next_func 27 | if idc.get_func_attr(start, FUNCATTR_START) == start: 28 | ida_funcs.del_func(start) 29 | ida_funcs.add_func(start, end) 30 | 31 | path = idaapi.ask_file(False, '*.json', 'script.json from Il2cppdumper') 32 | data = json.loads(open(path, 'rb').read().decode('utf-8')) 33 | 34 | if "Addresses" in data and "Addresses" in processFields: 35 | addresses = data["Addresses"] 36 | for index in range(len(addresses) - 1): 37 | start = get_addr(addresses[index]) 38 | end = get_addr(addresses[index + 1]) 39 | make_function(start, end) 40 | 41 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 42 | scriptMethods = data["ScriptMethod"] 43 | for scriptMethod in scriptMethods: 44 | addr = get_addr(scriptMethod["Address"]) 45 | name = scriptMethod["Name"] 46 | set_name(addr, name) 47 | 48 | if "ScriptString" in data and "ScriptString" in processFields: 49 | index = 1 50 | scriptStrings = data["ScriptString"] 51 | for scriptString in scriptStrings: 52 | addr = get_addr(scriptString["Address"]) 53 | value = scriptString["Value"] 54 | name = "StringLiteral_" + str(index) 55 | idc.set_name(addr, name, SN_NOWARN) 56 | idc.set_cmt(addr, value, 1) 57 | index += 1 58 | 59 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 60 | scriptMetadatas = data["ScriptMetadata"] 61 | for scriptMetadata in scriptMetadatas: 62 | addr = get_addr(scriptMetadata["Address"]) 63 | name = scriptMetadata["Name"] 64 | set_name(addr, name) 65 | idc.set_cmt(addr, name, 1) 66 | 67 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 68 | scriptMetadataMethods = data["ScriptMetadataMethod"] 69 | for scriptMetadataMethod in scriptMetadataMethods: 70 | addr = get_addr(scriptMetadataMethod["Address"]) 71 | name = scriptMetadataMethod["Name"] 72 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 73 | set_name(addr, name) 74 | idc.set_cmt(addr, name, 1) 75 | idc.set_cmt(addr, '{0:X}'.format(methodAddr), 0) 76 | 77 | print('Script finished!') 78 | 79 | -------------------------------------------------------------------------------- /Il2CppDumper/ida.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | processFields = [ 5 | "ScriptMethod", 6 | "ScriptString", 7 | "ScriptMetadata", 8 | "ScriptMetadataMethod", 9 | "Addresses", 10 | ] 11 | 12 | imageBase = idaapi.get_imagebase() 13 | 14 | def get_addr(addr): 15 | return imageBase + addr 16 | 17 | def set_name(addr, name): 18 | ret = idc.set_name(addr, name, SN_NOWARN | SN_NOCHECK) 19 | if ret == 0: 20 | new_name = name + '_' + str(addr) 21 | ret = idc.set_name(addr, new_name, SN_NOWARN | SN_NOCHECK) 22 | 23 | def make_function(start, end): 24 | next_func = idc.get_next_func(start) 25 | if next_func < end: 26 | end = next_func 27 | if idc.get_func_attr(start, FUNCATTR_START) == start: 28 | ida_funcs.del_func(start) 29 | ida_funcs.add_func(start, end) 30 | 31 | path = idaapi.ask_file(False, '*.json', 'script.json from Il2cppdumper') 32 | data = json.loads(open(path, 'rb').read().decode('utf-8')) 33 | 34 | if "Addresses" in data and "Addresses" in processFields: 35 | addresses = data["Addresses"] 36 | for index in range(len(addresses) - 1): 37 | start = get_addr(addresses[index]) 38 | end = get_addr(addresses[index + 1]) 39 | make_function(start, end) 40 | 41 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 42 | scriptMethods = data["ScriptMethod"] 43 | for scriptMethod in scriptMethods: 44 | addr = get_addr(scriptMethod["Address"]) 45 | name = scriptMethod["Name"].encode("utf-8") 46 | set_name(addr, name) 47 | 48 | if "ScriptString" in data and "ScriptString" in processFields: 49 | index = 1 50 | scriptStrings = data["ScriptString"] 51 | for scriptString in scriptStrings: 52 | addr = get_addr(scriptString["Address"]) 53 | value = scriptString["Value"].encode("utf-8") 54 | name = "StringLiteral_" + str(index) 55 | idc.set_name(addr, name, SN_NOWARN) 56 | idc.set_cmt(addr, value, 1) 57 | index += 1 58 | 59 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 60 | scriptMetadatas = data["ScriptMetadata"] 61 | for scriptMetadata in scriptMetadatas: 62 | addr = get_addr(scriptMetadata["Address"]) 63 | name = scriptMetadata["Name"].encode("utf-8") 64 | set_name(addr, name) 65 | idc.set_cmt(addr, name, 1) 66 | 67 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 68 | scriptMetadataMethods = data["ScriptMetadataMethod"] 69 | for scriptMetadataMethod in scriptMetadataMethods: 70 | addr = get_addr(scriptMetadataMethod["Address"]) 71 | name = scriptMetadataMethod["Name"].encode("utf-8") 72 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 73 | set_name(addr, name) 74 | idc.set_cmt(addr, name, 1) 75 | idc.set_cmt(addr, '{0:X}'.format(methodAddr), 0) 76 | 77 | print 'Script finished!' 78 | 79 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Il2CppDumper/Resource1.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Il2CppDumper { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resource1 { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resource1() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Il2CppDumper.Resource1", typeof(Resource1).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 查找 System.Byte[] 类型的本地化资源。 65 | /// 66 | internal static byte[] Il2CppDummyDll { 67 | get { 68 | object obj = ResourceManager.GetObject("Il2CppDummyDll", resourceCulture); 69 | return ((byte[])(obj)); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Il2CppDumper/ghidra.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | processFields = [ 5 | "ScriptMethod", 6 | "ScriptString", 7 | "ScriptMetadata", 8 | "ScriptMetadataMethod", 9 | "Addresses", 10 | ] 11 | 12 | functionManager = currentProgram.getFunctionManager() 13 | baseAddress = currentProgram.getImageBase() 14 | USER_DEFINED = ghidra.program.model.symbol.SourceType.USER_DEFINED 15 | 16 | def get_addr(addr): 17 | return baseAddress.add(addr) 18 | 19 | def set_name(addr, name): 20 | name = name.replace(' ', '-') 21 | createLabel(addr, name, True, USER_DEFINED) 22 | 23 | def make_function(start): 24 | func = getFunctionAt(start) 25 | if func is None: 26 | createFunction(start, None) 27 | 28 | f = askFile("script.json from Il2cppdumper", "Open") 29 | data = json.loads(open(f.absolutePath, 'rb').read().decode('utf-8')) 30 | 31 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 32 | scriptMethods = data["ScriptMethod"] 33 | monitor.initialize(len(scriptMethods)) 34 | monitor.setMessage("Methods") 35 | for scriptMethod in scriptMethods: 36 | addr = get_addr(scriptMethod["Address"]) 37 | name = scriptMethod["Name"].encode("utf-8") 38 | set_name(addr, name) 39 | monitor.incrementProgress(1) 40 | 41 | if "ScriptString" in data and "ScriptString" in processFields: 42 | index = 1 43 | scriptStrings = data["ScriptString"] 44 | monitor.initialize(len(scriptStrings)) 45 | monitor.setMessage("Strings") 46 | for scriptString in scriptStrings: 47 | addr = get_addr(scriptString["Address"]) 48 | value = scriptString["Value"].encode("utf-8") 49 | name = "StringLiteral_" + str(index) 50 | createLabel(addr, name, True, USER_DEFINED) 51 | setEOLComment(addr, value) 52 | index += 1 53 | monitor.incrementProgress(1) 54 | 55 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 56 | scriptMetadatas = data["ScriptMetadata"] 57 | monitor.initialize(len(scriptMetadatas)) 58 | monitor.setMessage("Metadata") 59 | for scriptMetadata in scriptMetadatas: 60 | addr = get_addr(scriptMetadata["Address"]) 61 | name = scriptMetadata["Name"].encode("utf-8") 62 | set_name(addr, name) 63 | setEOLComment(addr, name) 64 | monitor.incrementProgress(1) 65 | 66 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 67 | scriptMetadataMethods = data["ScriptMetadataMethod"] 68 | monitor.initialize(len(scriptMetadataMethods)) 69 | monitor.setMessage("Metadata Methods") 70 | for scriptMetadataMethod in scriptMetadataMethods: 71 | addr = get_addr(scriptMetadataMethod["Address"]) 72 | name = scriptMetadataMethod["Name"].encode("utf-8") 73 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 74 | set_name(addr, name) 75 | setEOLComment(addr, name) 76 | monitor.incrementProgress(1) 77 | 78 | if "Addresses" in data and "Addresses" in processFields: 79 | addresses = data["Addresses"] 80 | monitor.initialize(len(addresses)) 81 | monitor.setMessage("Addresses") 82 | for index in range(len(addresses) - 1): 83 | start = get_addr(addresses[index]) 84 | make_function(start) 85 | monitor.incrementProgress(1) 86 | 87 | print 'Script finished!' 88 | -------------------------------------------------------------------------------- /Il2CppDumper/ida_with_struct_py3.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | processFields = [ 5 | "ScriptMethod", 6 | "ScriptString", 7 | "ScriptMetadata", 8 | "ScriptMetadataMethod", 9 | "Addresses", 10 | ] 11 | 12 | imageBase = idaapi.get_imagebase() 13 | 14 | def get_addr(addr): 15 | return imageBase + addr 16 | 17 | def set_name(addr, name): 18 | ret = idc.set_name(addr, name, SN_NOWARN | SN_NOCHECK) 19 | if ret == 0: 20 | new_name = name + '_' + str(addr) 21 | ret = idc.set_name(addr, new_name, SN_NOWARN | SN_NOCHECK) 22 | 23 | def make_function(start, end): 24 | next_func = idc.get_next_func(start) 25 | if next_func < end: 26 | end = next_func 27 | if idc.get_func_attr(start, FUNCATTR_START) == start: 28 | ida_funcs.del_func(start) 29 | ida_funcs.add_func(start, end) 30 | 31 | path = idaapi.ask_file(False, '*.json', 'script.json from Il2cppdumper') 32 | hpath = idaapi.ask_file(False, '*.h', 'il2cpp.h from Il2cppdumper') 33 | parse_decls(open(hpath, 'r').read(), 0) 34 | data = json.loads(open(path, 'rb').read().decode('utf-8')) 35 | 36 | if "Addresses" in data and "Addresses" in processFields: 37 | addresses = data["Addresses"] 38 | for index in range(len(addresses) - 1): 39 | start = get_addr(addresses[index]) 40 | end = get_addr(addresses[index + 1]) 41 | make_function(start, end) 42 | 43 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 44 | scriptMethods = data["ScriptMethod"] 45 | for scriptMethod in scriptMethods: 46 | addr = get_addr(scriptMethod["Address"]) 47 | name = scriptMethod["Name"] 48 | set_name(addr, name) 49 | signature = scriptMethod["Signature"] 50 | if apply_type(addr, parse_decl(signature, 0), 1) == False: 51 | print("apply_type failed:", hex(addr), signature) 52 | 53 | if "ScriptString" in data and "ScriptString" in processFields: 54 | index = 1 55 | scriptStrings = data["ScriptString"] 56 | for scriptString in scriptStrings: 57 | addr = get_addr(scriptString["Address"]) 58 | value = scriptString["Value"] 59 | name = "StringLiteral_" + str(index) 60 | idc.set_name(addr, name, SN_NOWARN) 61 | idc.set_cmt(addr, value, 1) 62 | index += 1 63 | 64 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 65 | scriptMetadatas = data["ScriptMetadata"] 66 | for scriptMetadata in scriptMetadatas: 67 | addr = get_addr(scriptMetadata["Address"]) 68 | name = scriptMetadata["Name"] 69 | set_name(addr, name) 70 | idc.set_cmt(addr, name, 1) 71 | if scriptMetadata["Signature"] is not None: 72 | signature = scriptMetadata["Signature"] 73 | if apply_type(addr, parse_decl(signature, 0), 1) == False: 74 | print("apply_type failed:", hex(addr), signature) 75 | 76 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 77 | scriptMetadataMethods = data["ScriptMetadataMethod"] 78 | for scriptMetadataMethod in scriptMetadataMethods: 79 | addr = get_addr(scriptMetadataMethod["Address"]) 80 | name = scriptMetadataMethod["Name"] 81 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 82 | set_name(addr, name) 83 | idc.set_cmt(addr, name, 1) 84 | idc.set_cmt(addr, '{0:X}'.format(methodAddr), 0) 85 | 86 | print('Script finished!') 87 | 88 | -------------------------------------------------------------------------------- /Il2CppDumper/ida_with_struct.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | processFields = [ 5 | "ScriptMethod", 6 | "ScriptString", 7 | "ScriptMetadata", 8 | "ScriptMetadataMethod", 9 | "Addresses", 10 | ] 11 | 12 | imageBase = idaapi.get_imagebase() 13 | 14 | def get_addr(addr): 15 | return imageBase + addr 16 | 17 | def set_name(addr, name): 18 | ret = idc.set_name(addr, name, SN_NOWARN | SN_NOCHECK) 19 | if ret == 0: 20 | new_name = name + '_' + str(addr) 21 | ret = idc.set_name(addr, new_name, SN_NOWARN | SN_NOCHECK) 22 | 23 | def make_function(start, end): 24 | next_func = idc.get_next_func(start) 25 | if next_func < end: 26 | end = next_func 27 | if idc.get_func_attr(start, FUNCATTR_START) == start: 28 | ida_funcs.del_func(start) 29 | ida_funcs.add_func(start, end) 30 | 31 | path = idaapi.ask_file(False, '*.json', 'script.json from Il2cppdumper') 32 | hpath = idaapi.ask_file(False, '*.h', 'il2cpp.h from Il2cppdumper') 33 | parse_decls(open(hpath, 'rb').read(), 0) 34 | data = json.loads(open(path, 'rb').read().decode('utf-8')) 35 | 36 | if "Addresses" in data and "Addresses" in processFields: 37 | addresses = data["Addresses"] 38 | for index in range(len(addresses) - 1): 39 | start = get_addr(addresses[index]) 40 | end = get_addr(addresses[index + 1]) 41 | make_function(start, end) 42 | 43 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 44 | scriptMethods = data["ScriptMethod"] 45 | for scriptMethod in scriptMethods: 46 | addr = get_addr(scriptMethod["Address"]) 47 | name = scriptMethod["Name"].encode("utf-8") 48 | set_name(addr, name) 49 | signature = scriptMethod["Signature"].encode("utf-8") 50 | if apply_type(addr, parse_decl(signature, 0), 1) == False: 51 | print "apply_type failed:", hex(addr), signature 52 | 53 | if "ScriptString" in data and "ScriptString" in processFields: 54 | index = 1 55 | scriptStrings = data["ScriptString"] 56 | for scriptString in scriptStrings: 57 | addr = get_addr(scriptString["Address"]) 58 | value = scriptString["Value"].encode("utf-8") 59 | name = "StringLiteral_" + str(index) 60 | idc.set_name(addr, name, SN_NOWARN) 61 | idc.set_cmt(addr, value, 1) 62 | index += 1 63 | 64 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 65 | scriptMetadatas = data["ScriptMetadata"] 66 | for scriptMetadata in scriptMetadatas: 67 | addr = get_addr(scriptMetadata["Address"]) 68 | name = scriptMetadata["Name"].encode("utf-8") 69 | set_name(addr, name) 70 | idc.set_cmt(addr, name, 1) 71 | if scriptMetadata["Signature"] is not None: 72 | signature = scriptMetadata["Signature"].encode("utf-8") 73 | if apply_type(addr, parse_decl(signature, 0), 1) == False: 74 | print "apply_type failed:", hex(addr), signature 75 | 76 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 77 | scriptMetadataMethods = data["ScriptMetadataMethod"] 78 | for scriptMetadataMethod in scriptMetadataMethods: 79 | addr = get_addr(scriptMetadataMethod["Address"]) 80 | name = scriptMetadataMethod["Name"].encode("utf-8") 81 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 82 | set_name(addr, name) 83 | idc.set_cmt(addr, name, 1) 84 | idc.set_cmt(addr, '{0:X}'.format(methodAddr), 0) 85 | 86 | print 'Script finished!' 87 | 88 | -------------------------------------------------------------------------------- /Il2CppDumper/Outputs/Il2CppConstants.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | class Il2CppConstants 4 | { 5 | /* 6 | * Field Attributes (21.1.5). 7 | */ 8 | public const int FIELD_ATTRIBUTE_FIELD_ACCESS_MASK = 0x0007; 9 | public const int FIELD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000; 10 | public const int FIELD_ATTRIBUTE_PRIVATE = 0x0001; 11 | public const int FIELD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002; 12 | public const int FIELD_ATTRIBUTE_ASSEMBLY = 0x0003; 13 | public const int FIELD_ATTRIBUTE_FAMILY = 0x0004; 14 | public const int FIELD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005; 15 | public const int FIELD_ATTRIBUTE_PUBLIC = 0x0006; 16 | 17 | public const int FIELD_ATTRIBUTE_STATIC = 0x0010; 18 | public const int FIELD_ATTRIBUTE_INIT_ONLY = 0x0020; 19 | public const int FIELD_ATTRIBUTE_LITERAL = 0x0040; 20 | 21 | /* 22 | * Method Attributes (22.1.9) 23 | */ 24 | public const int METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK = 0x0007; 25 | public const int METHOD_ATTRIBUTE_COMPILER_CONTROLLED = 0x0000; 26 | public const int METHOD_ATTRIBUTE_PRIVATE = 0x0001; 27 | public const int METHOD_ATTRIBUTE_FAM_AND_ASSEM = 0x0002; 28 | public const int METHOD_ATTRIBUTE_ASSEM = 0x0003; 29 | public const int METHOD_ATTRIBUTE_FAMILY = 0x0004; 30 | public const int METHOD_ATTRIBUTE_FAM_OR_ASSEM = 0x0005; 31 | public const int METHOD_ATTRIBUTE_PUBLIC = 0x0006; 32 | 33 | public const int METHOD_ATTRIBUTE_STATIC = 0x0010; 34 | public const int METHOD_ATTRIBUTE_FINAL = 0x0020; 35 | public const int METHOD_ATTRIBUTE_VIRTUAL = 0x0040; 36 | 37 | public const int METHOD_ATTRIBUTE_VTABLE_LAYOUT_MASK = 0x0100; 38 | public const int METHOD_ATTRIBUTE_REUSE_SLOT = 0x0000; 39 | public const int METHOD_ATTRIBUTE_NEW_SLOT = 0x0100; 40 | 41 | public const int METHOD_ATTRIBUTE_ABSTRACT = 0x0400; 42 | 43 | public const int METHOD_ATTRIBUTE_PINVOKE_IMPL = 0x2000; 44 | 45 | /* 46 | * Type Attributes (21.1.13). 47 | */ 48 | public const int TYPE_ATTRIBUTE_VISIBILITY_MASK = 0x00000007; 49 | public const int TYPE_ATTRIBUTE_NOT_PUBLIC = 0x00000000; 50 | public const int TYPE_ATTRIBUTE_PUBLIC = 0x00000001; 51 | public const int TYPE_ATTRIBUTE_NESTED_PUBLIC = 0x00000002; 52 | public const int TYPE_ATTRIBUTE_NESTED_PRIVATE = 0x00000003; 53 | public const int TYPE_ATTRIBUTE_NESTED_FAMILY = 0x00000004; 54 | public const int TYPE_ATTRIBUTE_NESTED_ASSEMBLY = 0x00000005; 55 | public const int TYPE_ATTRIBUTE_NESTED_FAM_AND_ASSEM = 0x00000006; 56 | public const int TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM = 0x00000007; 57 | 58 | 59 | public const int TYPE_ATTRIBUTE_INTERFACE = 0x00000020; 60 | 61 | public const int TYPE_ATTRIBUTE_ABSTRACT = 0x00000080; 62 | public const int TYPE_ATTRIBUTE_SEALED = 0x00000100; 63 | 64 | public const int TYPE_ATTRIBUTE_SERIALIZABLE = 0x00002000; 65 | 66 | /* 67 | * Flags for Params (22.1.12) 68 | */ 69 | public const int PARAM_ATTRIBUTE_IN = 0x0001; 70 | public const int PARAM_ATTRIBUTE_OUT = 0x0002; 71 | public const int PARAM_ATTRIBUTE_OPTIONAL = 0x0010; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Il2CppDumper/Extensions/BinaryReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Il2CppDumper 6 | { 7 | public static class BinaryReaderExtensions 8 | { 9 | public static string ReadString(this BinaryReader reader, int numChars) 10 | { 11 | var start = reader.BaseStream.Position; 12 | // UTF8 takes up to 4 bytes per character 13 | var str = Encoding.UTF8.GetString(reader.ReadBytes(numChars * 4))[..numChars]; 14 | // make our position what it would have been if we'd known the exact number of bytes needed. 15 | reader.BaseStream.Position = start; 16 | reader.ReadBytes(Encoding.UTF8.GetByteCount(str)); 17 | return str; 18 | } 19 | 20 | public static uint ReadULeb128(this BinaryReader reader) 21 | { 22 | uint value = reader.ReadByte(); 23 | if (value >= 0x80) 24 | { 25 | var bitshift = 0; 26 | value &= 0x7f; 27 | while (true) 28 | { 29 | var b = reader.ReadByte(); 30 | bitshift += 7; 31 | value |= (uint)((b & 0x7f) << bitshift); 32 | if (b < 0x80) 33 | break; 34 | } 35 | } 36 | return value; 37 | } 38 | 39 | public static uint ReadCompressedUInt32(this BinaryReader reader) 40 | { 41 | uint val; 42 | var read = reader.ReadByte(); 43 | 44 | if ((read & 0x80) == 0) 45 | { 46 | // 1 byte written 47 | val = read; 48 | } 49 | else if ((read & 0xC0) == 0x80) 50 | { 51 | // 2 bytes written 52 | val = (read & ~0x80u) << 8; 53 | val |= reader.ReadByte(); 54 | } 55 | else if ((read & 0xE0) == 0xC0) 56 | { 57 | // 4 bytes written 58 | val = (read & ~0xC0u) << 24; 59 | val |= ((uint)reader.ReadByte() << 16); 60 | val |= ((uint)reader.ReadByte() << 8); 61 | val |= reader.ReadByte(); 62 | } 63 | else if (read == 0xF0) 64 | { 65 | // 5 bytes written, we had a really large int32! 66 | val = reader.ReadUInt32(); 67 | } 68 | else if (read == 0xFE) 69 | { 70 | // Special encoding for Int32.MaxValue 71 | val = uint.MaxValue - 1; 72 | } 73 | else if (read == 0xFF) 74 | { 75 | // Yes we treat UInt32.MaxValue (and Int32.MinValue, see ReadCompressedInt32) specially 76 | val = uint.MaxValue; 77 | } 78 | else 79 | { 80 | throw new Exception("Invalid compressed integer format"); 81 | } 82 | 83 | return val; 84 | } 85 | 86 | public static int ReadCompressedInt32(this BinaryReader reader) 87 | { 88 | var encoded = reader.ReadCompressedUInt32(); 89 | 90 | // -UINT32_MAX can't be represted safely in an int32_t, so we treat it specially 91 | if (encoded == uint.MaxValue) 92 | return int.MinValue; 93 | 94 | bool isNegative = (encoded & 1) != 0; 95 | encoded >>= 1; 96 | if (isNegative) 97 | return -(int)(encoded + 1); 98 | return (int)encoded; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Il2CppDumper/ghidra_wasm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | from wasm import WasmLoader 5 | from wasm.analysis import WasmAnalysis 6 | from ghidra.util.task import ConsoleTaskMonitor 7 | 8 | monitor = ConsoleTaskMonitor() 9 | WasmLoader.loadElementsToTable(currentProgram, WasmAnalysis.getState(currentProgram).module, 0, 0, 0, monitor) 10 | 11 | runScript("analyze_dyncalls.py") 12 | 13 | processFields = [ 14 | "ScriptMethod", 15 | "ScriptString", 16 | "ScriptMetadata", 17 | "ScriptMetadataMethod", 18 | "Addresses", 19 | ] 20 | 21 | functionManager = currentProgram.getFunctionManager() 22 | progspace = currentProgram.addressFactory.getAddressSpace("ram") 23 | USER_DEFINED = ghidra.program.model.symbol.SourceType.USER_DEFINED 24 | 25 | def get_addr(addr): 26 | return progspace.getAddress(addr) 27 | 28 | def set_name(addr, name): 29 | name = name.replace(' ', '-') 30 | createLabel(addr, name, True, USER_DEFINED) 31 | 32 | def make_function(start): 33 | func = getFunctionAt(start) 34 | if func is None: 35 | createFunction(start, None) 36 | 37 | f = askFile("script.json from Il2cppdumper", "Open") 38 | data = json.loads(open(f.absolutePath, 'rb').read().decode('utf-8')) 39 | 40 | 41 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 42 | scriptMethods = data["ScriptMethod"] 43 | dynCallNamespace = currentProgram.symbolTable.getNamespace("dynCall", None) 44 | monitor.initialize(len(scriptMethods)) 45 | monitor.setMessage("Methods") 46 | for scriptMethod in scriptMethods: 47 | offset = scriptMethod["Address"] 48 | sig = scriptMethod["TypeSignature"] 49 | symbolName = "func_%s_%d" % (sig, offset) 50 | symbol = currentProgram.symbolTable.getSymbols(symbolName, dynCallNamespace) 51 | if len(symbol) > 0: 52 | addr = symbol[0].address 53 | name = scriptMethod["Name"].encode("utf-8") 54 | set_name(addr, name) 55 | else: 56 | print "Warning at %s:" % scriptMethod["Name"] 57 | print "Symbol %s not found!" % symbolName 58 | monitor.incrementProgress(1) 59 | 60 | if "ScriptString" in data and "ScriptString" in processFields: 61 | index = 1 62 | scriptStrings = data["ScriptString"] 63 | monitor.initialize(len(scriptStrings)) 64 | monitor.setMessage("Strings") 65 | for scriptString in scriptStrings: 66 | addr = get_addr(scriptString["Address"]) 67 | value = scriptString["Value"].encode("utf-8") 68 | name = "StringLiteral_" + str(index) 69 | createLabel(addr, name, True, USER_DEFINED) 70 | setEOLComment(addr, value) 71 | index += 1 72 | monitor.incrementProgress(1) 73 | 74 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 75 | scriptMetadatas = data["ScriptMetadata"] 76 | monitor.initialize(len(scriptMetadatas)) 77 | monitor.setMessage("Metadata") 78 | for scriptMetadata in scriptMetadatas: 79 | addr = get_addr(scriptMetadata["Address"]) 80 | name = scriptMetadata["Name"].encode("utf-8") 81 | set_name(addr, name) 82 | setEOLComment(addr, name) 83 | monitor.incrementProgress(1) 84 | 85 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 86 | scriptMetadataMethods = data["ScriptMetadataMethod"] 87 | monitor.initialize(len(scriptMetadataMethods)) 88 | monitor.setMessage("Metadata Methods") 89 | for scriptMetadataMethod in scriptMetadataMethods: 90 | addr = get_addr(scriptMetadataMethod["Address"]) 91 | name = scriptMetadataMethod["Name"].encode("utf-8") 92 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 93 | set_name(addr, name) 94 | setEOLComment(addr, name) 95 | monitor.incrementProgress(1) 96 | 97 | if "Addresses" in data and "Addresses" in processFields: 98 | pass 99 | 100 | print 'Script finished!' 101 | -------------------------------------------------------------------------------- /Il2CppDumper/Il2CppBinaryNinja/__init__.py: -------------------------------------------------------------------------------- 1 | from binaryninja import * 2 | from os.path import exists 3 | 4 | def get_addr(bv: BinaryView, addr: int): 5 | imageBase = bv.start 6 | return imageBase + addr 7 | 8 | class Il2CppProcessTask(BackgroundTaskThread): 9 | def __init__(self, bv: BinaryView, script_path: str, 10 | header_path: str): 11 | BackgroundTaskThread.__init__(self, "Il2Cpp start", True) 12 | self.bv = bv 13 | self.script_path = script_path 14 | self.header_path = header_path 15 | self.has_types = False 16 | 17 | def process_header(self): 18 | self.progress = "Il2Cpp types (1/3)" 19 | with open(self.header_path) as f: 20 | result = self.bv.parse_types_from_string(f.read()) 21 | length = len(result.types) 22 | i = 0 23 | for name in result.types: 24 | i += 1 25 | if i % 100 == 0: 26 | percent = i / length * 100 27 | self.progress = f"Il2Cpp types: {percent:.2f}%" 28 | if self.bv.get_type_by_name(name): 29 | continue 30 | self.bv.define_user_type(name, result.types[name]) 31 | 32 | def process_methods(self, data: dict): 33 | self.progress = f"Il2Cpp methods (2/3)" 34 | scriptMethods = data["ScriptMethod"] 35 | length = len(scriptMethods) 36 | i = 0 37 | for scriptMethod in scriptMethods: 38 | if self.cancelled: 39 | self.progress = "Il2Cpp cancelled, aborting" 40 | return 41 | i += 1 42 | if i % 100 == 0: 43 | percent = i / length * 100 44 | self.progress = f"Il2Cpp methods: {percent:.2f}%" 45 | addr = get_addr(self.bv, scriptMethod["Address"]) 46 | name = scriptMethod["Name"].replace("$", "_").replace(".", "_") 47 | signature = scriptMethod["Signature"] 48 | func = self.bv.get_function_at(addr) 49 | if func != None: 50 | if func.name == name: 51 | continue 52 | if self.has_types: 53 | func.function_type = signature 54 | else: 55 | func.name = scriptMethod["Name"] 56 | 57 | def process_strings(self, data: dict): 58 | self.progress = "Il2Cpp strings (3/3)" 59 | scriptStrings = data["ScriptString"] 60 | i = 0 61 | for scriptString in scriptStrings: 62 | i += 1 63 | if self.cancelled: 64 | self.progress = "Il2Cpp cancelled, aborting" 65 | return 66 | addr = get_addr(self.bv, scriptString["Address"]) 67 | value = scriptString["Value"] 68 | var = self.bv.get_data_var_at(addr) 69 | if var != None: 70 | var.name = f"StringLiteral_{i}" 71 | self.bv.set_comment_at(addr, value) 72 | 73 | def run(self): 74 | if exists(self.header_path): 75 | self.process_header() 76 | else: 77 | log_warn("Header file not found") 78 | if self.bv.get_type_by_name("Il2CppClass"): 79 | self.has_types = True 80 | data = json.loads(open(self.script_path, 'rb').read().decode('utf-8')) 81 | if "ScriptMethod" in data: 82 | self.process_methods(data) 83 | if "ScriptString" in data: 84 | self.process_strings(data) 85 | 86 | def process(bv: BinaryView): 87 | scriptDialog = OpenFileNameField("Select script.json", "script.json", "script.json") 88 | headerDialog = OpenFileNameField("Select il2cpp_binja.h", "il2cpp_binja.h", "il2cpp_binja.h") 89 | if not get_form_input([scriptDialog, headerDialog], "script.json from Il2CppDumper"): 90 | return log_error("File not selected, try again!") 91 | if not exists(scriptDialog.result): 92 | return log_error("File not found, try again!") 93 | task = Il2CppProcessTask(bv, scriptDialog.result, headerDialog.result) 94 | task.start() 95 | 96 | PluginCommand.register("Il2CppDumper", "Process file", process) 97 | -------------------------------------------------------------------------------- /Il2CppDumper/Extensions/BoyerMooreHorspool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Il2CppDumper 5 | { 6 | static class BoyerMooreHorspool 7 | { 8 | public static IEnumerable Search(this byte[] source, byte[] pattern) 9 | { 10 | if (source == null) 11 | { 12 | throw new ArgumentNullException(nameof(source)); 13 | } 14 | 15 | if (pattern == null) 16 | { 17 | throw new ArgumentNullException(nameof(pattern)); 18 | } 19 | 20 | int valueLength = source.Length; 21 | int patternLength = pattern.Length; 22 | 23 | if (valueLength == 0 || patternLength == 0 || patternLength > valueLength) 24 | { 25 | yield break; 26 | } 27 | 28 | var badCharacters = new int[256]; 29 | 30 | for (var i = 0; i < 256; i++) 31 | { 32 | badCharacters[i] = patternLength; 33 | } 34 | 35 | var lastPatternByte = patternLength - 1; 36 | 37 | for (int i = 0; i < lastPatternByte; i++) 38 | { 39 | badCharacters[pattern[i]] = lastPatternByte - i; 40 | } 41 | 42 | int index = 0; 43 | 44 | while (index <= valueLength - patternLength) 45 | { 46 | for (var i = lastPatternByte; source[index + i] == pattern[i]; i--) 47 | { 48 | if (i == 0) 49 | { 50 | yield return index; 51 | break; 52 | } 53 | } 54 | 55 | index += badCharacters[source[index + lastPatternByte]]; 56 | } 57 | } 58 | 59 | public static IEnumerable Search(this byte[] source, string stringPattern) 60 | { 61 | if (source == null) 62 | { 63 | throw new ArgumentNullException(nameof(source)); 64 | } 65 | 66 | if (stringPattern == null) 67 | { 68 | throw new ArgumentNullException(nameof(stringPattern)); 69 | } 70 | 71 | var pattern = stringPattern.Split(' '); 72 | 73 | int valueLength = source.Length; 74 | int patternLength = pattern.Length; 75 | 76 | if (valueLength == 0 || patternLength == 0 || patternLength > valueLength) 77 | { 78 | yield break; 79 | } 80 | 81 | var badCharacters = new int[256]; 82 | 83 | for (var i = 0; i < 256; i++) 84 | { 85 | badCharacters[i] = patternLength; 86 | } 87 | 88 | var lastPatternByte = patternLength - 1; 89 | 90 | for (int i = 0; i < lastPatternByte; i++) 91 | { 92 | if (pattern[i] != "?") 93 | { 94 | var result = Convert.ToInt32(pattern[i], 16); 95 | badCharacters[result] = lastPatternByte - i; 96 | } 97 | } 98 | 99 | int index = 0; 100 | 101 | while (index <= valueLength - patternLength) 102 | { 103 | for (var i = lastPatternByte; CheckEqual(source, pattern, index, i); i--) 104 | { 105 | if (i == 0) 106 | { 107 | yield return index; 108 | break; 109 | } 110 | } 111 | 112 | index += badCharacters[source[index + lastPatternByte]]; 113 | } 114 | } 115 | 116 | private static bool CheckEqual(byte[] source, string[] pattern, int index, int i) 117 | { 118 | if (pattern[i] != "?") 119 | { 120 | var result = Convert.ToInt32(pattern[i], 16); 121 | return source[index + i] == result; 122 | } 123 | return true; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Il2CppDumper/ghidra_with_struct.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | 4 | from ghidra.app.util.cparser.C import CParserUtils 5 | from ghidra.app.cmd.function import ApplyFunctionSignatureCmd 6 | 7 | processFields = [ 8 | "ScriptMethod", 9 | "ScriptString", 10 | "ScriptMetadata", 11 | "ScriptMetadataMethod", 12 | "Addresses", 13 | ] 14 | 15 | functionManager = currentProgram.getFunctionManager() 16 | baseAddress = currentProgram.getImageBase() 17 | USER_DEFINED = ghidra.program.model.symbol.SourceType.USER_DEFINED 18 | 19 | def get_addr(addr): 20 | return baseAddress.add(addr) 21 | 22 | def set_name(addr, name): 23 | try: 24 | name = name.replace(' ', '-') 25 | createLabel(addr, name, True, USER_DEFINED) 26 | except: 27 | print("set_name() Failed.") 28 | 29 | def set_type(addr, type): 30 | # Requires types (il2cpp.h) to be imported first 31 | newType = type.replace("*"," *").replace(" "," ").strip() 32 | dataTypes = getDataTypes(newType) 33 | addrType = None 34 | if len(dataTypes) == 0: 35 | if newType == newType[:-2] + " *": 36 | baseType = newType[:-2] 37 | dataTypes = getDataTypes(baseType) 38 | if len(dataTypes) == 1: 39 | dtm = currentProgram.getDataTypeManager() 40 | pointerType = dtm.getPointer(dataTypes[0]) 41 | addrType = dtm.addDataType(pointerType, None) 42 | elif len(dataTypes) > 1: 43 | print("Conflicting data types found for type " + type + "(parsed as '" + newType + "')") 44 | return 45 | else: 46 | addrType = dataTypes[0] 47 | if addrType is None: 48 | print("Could not identify type " + type + "(parsed as '" + newType + "')") 49 | else: 50 | try: 51 | createData(addr, addrType) 52 | except ghidra.program.model.util.CodeUnitInsertionException: 53 | print("Warning: unable to set type (CodeUnitInsertionException)") 54 | 55 | 56 | def make_function(start): 57 | func = getFunctionAt(start) 58 | if func is None: 59 | try: 60 | createFunction(start, None) 61 | except: 62 | print("Warning: Unable to create function") 63 | 64 | def set_sig(addr, name, sig): 65 | try: 66 | typeSig = CParserUtils.parseSignature(None, currentProgram, sig, False) 67 | except ghidra.app.util.cparser.C.ParseException: 68 | print("Warning: Unable to parse") 69 | print(sig) 70 | print("Attempting to modify...") 71 | # try to fix by renaming the parameters 72 | try: 73 | newSig = sig.replace(", ","ext, ").replace("\)","ext\)") 74 | typeSig = CParserUtils.parseSignature(None, currentProgram, newSig, False) 75 | except: 76 | print("Warning: also unable to parse") 77 | print(newSig) 78 | print("Skipping.") 79 | return 80 | if typeSig is not None: 81 | try: 82 | typeSig.setName(name) 83 | ApplyFunctionSignatureCmd(addr, typeSig, USER_DEFINED, False, True).applyTo(currentProgram) 84 | except: 85 | print("Warning: unable to set Signature. ApplyFunctionSignatureCmd() Failed.") 86 | 87 | f = askFile("script.json from Il2cppdumper", "Open") 88 | data = json.loads(open(f.absolutePath, 'rb').read().decode('utf-8')) 89 | 90 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 91 | scriptMethods = data["ScriptMethod"] 92 | monitor.initialize(len(scriptMethods)) 93 | monitor.setMessage("Methods") 94 | for scriptMethod in scriptMethods: 95 | addr = get_addr(scriptMethod["Address"]) 96 | name = scriptMethod["Name"].encode("utf-8") 97 | set_name(addr, name) 98 | monitor.incrementProgress(1) 99 | 100 | if "ScriptString" in data and "ScriptString" in processFields: 101 | index = 1 102 | scriptStrings = data["ScriptString"] 103 | monitor.initialize(len(scriptStrings)) 104 | monitor.setMessage("Strings") 105 | for scriptString in scriptStrings: 106 | addr = get_addr(scriptString["Address"]) 107 | value = scriptString["Value"].encode("utf-8") 108 | name = "StringLiteral_" + str(index) 109 | createLabel(addr, name, True, USER_DEFINED) 110 | setEOLComment(addr, value) 111 | index += 1 112 | monitor.incrementProgress(1) 113 | 114 | if "ScriptMetadata" in data and "ScriptMetadata" in processFields: 115 | scriptMetadatas = data["ScriptMetadata"] 116 | monitor.initialize(len(scriptMetadatas)) 117 | monitor.setMessage("Metadata") 118 | for scriptMetadata in scriptMetadatas: 119 | addr = get_addr(scriptMetadata["Address"]) 120 | name = scriptMetadata["Name"].encode("utf-8") 121 | set_name(addr, name) 122 | setEOLComment(addr, name) 123 | monitor.incrementProgress(1) 124 | if scriptMetadata["Signature"]: 125 | set_type(addr, scriptMetadata["Signature"].encode("utf-8")) 126 | 127 | if "ScriptMetadataMethod" in data and "ScriptMetadataMethod" in processFields: 128 | scriptMetadataMethods = data["ScriptMetadataMethod"] 129 | monitor.initialize(len(scriptMetadataMethods)) 130 | monitor.setMessage("Metadata Methods") 131 | for scriptMetadataMethod in scriptMetadataMethods: 132 | addr = get_addr(scriptMetadataMethod["Address"]) 133 | name = scriptMetadataMethod["Name"].encode("utf-8") 134 | methodAddr = get_addr(scriptMetadataMethod["MethodAddress"]) 135 | set_name(addr, name) 136 | setEOLComment(addr, name) 137 | monitor.incrementProgress(1) 138 | 139 | if "Addresses" in data and "Addresses" in processFields: 140 | addresses = data["Addresses"] 141 | monitor.initialize(len(addresses)) 142 | monitor.setMessage("Addresses") 143 | for index in range(len(addresses) - 1): 144 | start = get_addr(addresses[index]) 145 | make_function(start) 146 | monitor.incrementProgress(1) 147 | 148 | if "ScriptMethod" in data and "ScriptMethod" in processFields: 149 | scriptMethods = data["ScriptMethod"] 150 | for scriptMethod in scriptMethods: 151 | addr = get_addr(scriptMethod["Address"]) 152 | sig = scriptMethod["Signature"][:-1].encode("utf-8") 153 | name = scriptMethod["Name"].encode("utf-8") 154 | set_sig(addr, name, sig) 155 | 156 | print 'Script finished!' 157 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/ElfClass.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class Elf32_Ehdr 4 | { 5 | public uint ei_mag; 6 | public byte ei_class; 7 | public byte ei_data; 8 | public byte ei_version; 9 | public byte ei_osabi; 10 | public byte ei_abiversion; 11 | [ArrayLength(Length = 7)] 12 | public byte[] ei_pad; 13 | public ushort e_type; 14 | public ushort e_machine; 15 | public uint e_version; 16 | public uint e_entry; 17 | public uint e_phoff; 18 | public uint e_shoff; 19 | public uint e_flags; 20 | public ushort e_ehsize; 21 | public ushort e_phentsize; 22 | public ushort e_phnum; 23 | public ushort e_shentsize; 24 | public ushort e_shnum; 25 | public ushort e_shstrndx; 26 | } 27 | 28 | public class Elf32_Phdr 29 | { 30 | public uint p_type; 31 | public uint p_offset; 32 | public uint p_vaddr; 33 | public uint p_paddr; 34 | public uint p_filesz; 35 | public uint p_memsz; 36 | public uint p_flags; 37 | public uint p_align; 38 | } 39 | 40 | public class Elf32_Shdr 41 | { 42 | public uint sh_name; 43 | public uint sh_type; 44 | public uint sh_flags; 45 | public uint sh_addr; 46 | public uint sh_offset; 47 | public uint sh_size; 48 | public uint sh_link; 49 | public uint sh_info; 50 | public uint sh_addralign; 51 | public uint sh_entsize; 52 | } 53 | 54 | public class Elf32_Sym 55 | { 56 | public uint st_name; 57 | public uint st_value; 58 | public uint st_size; 59 | public byte st_info; 60 | public byte st_other; 61 | public ushort st_shndx; 62 | } 63 | 64 | public class Elf32_Dyn 65 | { 66 | public int d_tag; 67 | public uint d_un; 68 | } 69 | 70 | public class Elf32_Rel 71 | { 72 | public uint r_offset; 73 | public uint r_info; 74 | } 75 | 76 | public class Elf64_Ehdr 77 | { 78 | public uint ei_mag; 79 | public byte ei_class; 80 | public byte ei_data; 81 | public byte ei_version; 82 | public byte ei_osabi; 83 | public byte ei_abiversion; 84 | [ArrayLength(Length = 7)] 85 | public byte[] ei_pad; 86 | public ushort e_type; 87 | public ushort e_machine; 88 | public uint e_version; 89 | public ulong e_entry; 90 | public ulong e_phoff; 91 | public ulong e_shoff; 92 | public uint e_flags; 93 | public ushort e_ehsize; 94 | public ushort e_phentsize; 95 | public ushort e_phnum; 96 | public ushort e_shentsize; 97 | public ushort e_shnum; 98 | public ushort e_shstrndx; 99 | } 100 | 101 | public class Elf64_Phdr 102 | { 103 | public uint p_type; 104 | public uint p_flags; 105 | public ulong p_offset; 106 | public ulong p_vaddr; 107 | public ulong p_paddr; 108 | public ulong p_filesz; 109 | public ulong p_memsz; 110 | public ulong p_align; 111 | } 112 | 113 | public class Elf64_Shdr 114 | { 115 | public uint sh_name; 116 | public uint sh_type; 117 | public ulong sh_flags; 118 | public ulong sh_addr; 119 | public ulong sh_offset; 120 | public ulong sh_size; 121 | public uint sh_link; 122 | public uint sh_info; 123 | public ulong sh_addralign; 124 | public ulong sh_entsize; 125 | } 126 | 127 | public class Elf64_Sym 128 | { 129 | public uint st_name; 130 | public byte st_info; 131 | public byte st_other; 132 | public ushort st_shndx; 133 | public ulong st_value; 134 | public ulong st_size; 135 | } 136 | 137 | public class Elf64_Dyn 138 | { 139 | public long d_tag; 140 | public ulong d_un; 141 | } 142 | 143 | public class Elf64_Rela 144 | { 145 | public ulong r_offset; 146 | public ulong r_info; 147 | public ulong r_addend; 148 | } 149 | 150 | public static class ElfConstants 151 | { 152 | //e_machine 153 | public const int EM_386 = 3; 154 | public const int EM_ARM = 40; 155 | public const int EM_X86_64 = 62; 156 | public const int EM_AARCH64 = 183; 157 | 158 | //p_type 159 | public const int PT_LOAD = 1; 160 | public const int PT_DYNAMIC = 2; 161 | 162 | //p_flags 163 | public const int PF_X = 1; 164 | 165 | //d_tag 166 | public const int DT_PLTGOT = 3; 167 | public const int DT_HASH = 4; 168 | public const int DT_STRTAB = 5; 169 | public const int DT_SYMTAB = 6; 170 | public const int DT_RELA = 7; 171 | public const int DT_RELASZ = 8; 172 | public const int DT_INIT = 12; 173 | public const int DT_FINI = 13; 174 | public const int DT_REL = 17; 175 | public const int DT_RELSZ = 18; 176 | public const int DT_JMPREL = 23; 177 | public const int DT_INIT_ARRAY = 25; 178 | public const int DT_FINI_ARRAY = 26; 179 | public const int DT_GNU_HASH = 0x6ffffef5; 180 | 181 | //sh_type 182 | public const uint SHT_LOUSER = 0x80000000; 183 | 184 | //ARM relocs 185 | public const int R_ARM_ABS32 = 2; 186 | 187 | //i386 relocs 188 | public const int R_386_32 = 1; 189 | 190 | //AArch64 relocs 191 | public const int R_AARCH64_ABS64 = 257; 192 | public const int R_AARCH64_RELATIVE = 1027; 193 | 194 | //AMD x86-64 relocations 195 | public const int R_X86_64_64 = 1; 196 | public const int R_X86_64_RELATIVE = 8; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /Il2CppDumper/Resource1.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | Libraries\Il2CppDummyDll.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 123 | 124 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/CustomAttributeDataReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | 4 | namespace Il2CppDumper 5 | { 6 | public class CustomAttributeDataReader : BinaryReader 7 | { 8 | private readonly Il2CppExecutor executor; 9 | private readonly Metadata metadata; 10 | private long ctorBuffer; 11 | private long dataBuffer; 12 | 13 | public uint Count { get; set; } 14 | 15 | public CustomAttributeDataReader(Il2CppExecutor executor, byte[] buff) : base(new MemoryStream(buff)) 16 | { 17 | this.executor = executor; 18 | metadata = executor.metadata; 19 | Count = this.ReadCompressedUInt32(); 20 | ctorBuffer = BaseStream.Position; 21 | dataBuffer = BaseStream.Position + Count * 4; 22 | } 23 | 24 | public string GetStringCustomAttributeData() 25 | { 26 | BaseStream.Position = ctorBuffer; 27 | var ctorIndex = ReadInt32(); 28 | var methodDef = metadata.methodDefs[ctorIndex]; 29 | var typeDef = metadata.typeDefs[methodDef.declaringType]; 30 | ctorBuffer = BaseStream.Position; 31 | 32 | BaseStream.Position = dataBuffer; 33 | var argumentCount = this.ReadCompressedUInt32(); 34 | var fieldCount = this.ReadCompressedUInt32(); 35 | var propertyCount = this.ReadCompressedUInt32(); 36 | 37 | var argList = new List(); 38 | 39 | for (var i = 0; i < argumentCount; i++) 40 | { 41 | argList.Add($"{AttributeDataToString(ReadAttributeDataValue())}"); 42 | } 43 | for (var i = 0; i < fieldCount; i++) 44 | { 45 | var str = AttributeDataToString(ReadAttributeDataValue()); 46 | (var declaring, var fieldIndex) = ReadCustomAttributeNamedArgumentClassAndIndex(typeDef); 47 | var fieldDef = metadata.fieldDefs[declaring.fieldStart + fieldIndex]; 48 | argList.Add($"{metadata.GetStringFromIndex(fieldDef.nameIndex)} = {str}"); 49 | } 50 | for (var i = 0; i < propertyCount; i++) 51 | { 52 | var str = AttributeDataToString(ReadAttributeDataValue()); 53 | (var declaring, var propertyIndex) = ReadCustomAttributeNamedArgumentClassAndIndex(typeDef); 54 | var propertyDef = metadata.propertyDefs[declaring.propertyStart + propertyIndex]; 55 | argList.Add($"{metadata.GetStringFromIndex(propertyDef.nameIndex)} = {str}"); 56 | } 57 | dataBuffer = BaseStream.Position; 58 | 59 | 60 | var typeName = metadata.GetStringFromIndex(typeDef.nameIndex).Replace("Attribute", ""); 61 | if (argList.Count > 0) 62 | { 63 | return $"[{typeName}({string.Join(", ", argList)})]"; 64 | } 65 | else 66 | { 67 | return $"[{typeName}]"; 68 | } 69 | } 70 | 71 | private string AttributeDataToString(BlobValue blobValue) 72 | { 73 | //TODO enum 74 | if (blobValue.Value == null) 75 | { 76 | return "null"; 77 | } 78 | switch (blobValue.il2CppTypeEnum) 79 | { 80 | case Il2CppTypeEnum.IL2CPP_TYPE_STRING: 81 | return $"\"{blobValue.Value}\""; 82 | case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY: 83 | var array = (BlobValue[])blobValue.Value; 84 | var list = new List(); 85 | foreach (var item in array) 86 | { 87 | list.Add(AttributeDataToString(item)); 88 | } 89 | return $"new[] {{ {string.Join(", ", list)} }}"; 90 | case Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX: 91 | var il2CppType = (Il2CppType)blobValue.Value; 92 | return $"typeof({executor.GetTypeName(il2CppType, false, false)})"; 93 | default: 94 | return blobValue.Value.ToString(); 95 | } 96 | } 97 | 98 | public CustomAttributeReaderVisitor VisitCustomAttributeData() 99 | { 100 | var visitor = new CustomAttributeReaderVisitor(); 101 | 102 | BaseStream.Position = ctorBuffer; 103 | var ctorIndex = ReadInt32(); 104 | visitor.CtorIndex = ctorIndex; 105 | var methodDef = metadata.methodDefs[ctorIndex]; 106 | var typeDef = metadata.typeDefs[methodDef.declaringType]; 107 | ctorBuffer = BaseStream.Position; 108 | 109 | BaseStream.Position = dataBuffer; 110 | var argumentCount = this.ReadCompressedUInt32(); 111 | var fieldCount = this.ReadCompressedUInt32(); 112 | var propertyCount = this.ReadCompressedUInt32(); 113 | 114 | visitor.Arguments = new AttributeArgument[argumentCount]; 115 | for (var i = 0; i < argumentCount; i++) 116 | { 117 | var argument = visitor.Arguments[i] = new AttributeArgument(); 118 | argument.Value = ReadAttributeDataValue(); 119 | argument.Index = i; 120 | } 121 | visitor.Fields = new AttributeArgument[fieldCount]; 122 | for (var i = 0; i < fieldCount; i++) 123 | { 124 | var field = visitor.Fields[i] = new AttributeArgument(); 125 | field.Value = ReadAttributeDataValue(); 126 | (var declaring, var fieldIndex) = ReadCustomAttributeNamedArgumentClassAndIndex(typeDef); 127 | field.Index = declaring.fieldStart + fieldIndex; 128 | } 129 | visitor.Properties = new AttributeArgument[propertyCount]; 130 | for (var i = 0; i < propertyCount; i++) 131 | { 132 | var property = visitor.Properties[i] = new AttributeArgument(); 133 | property.Value = ReadAttributeDataValue(); 134 | (var declaring, var propertyIndex) = ReadCustomAttributeNamedArgumentClassAndIndex(typeDef); 135 | property.Index = declaring.propertyStart + propertyIndex; 136 | } 137 | 138 | dataBuffer = BaseStream.Position; 139 | return visitor; 140 | } 141 | 142 | private BlobValue ReadAttributeDataValue() 143 | { 144 | var type = executor.ReadEncodedTypeEnum(this, out var enumType); 145 | executor.GetConstantValueFromBlob(type, this, out var blobValue); 146 | if (enumType != null) 147 | { 148 | blobValue.EnumType = enumType; 149 | } 150 | return blobValue; 151 | } 152 | 153 | private (Il2CppTypeDefinition, int) ReadCustomAttributeNamedArgumentClassAndIndex(Il2CppTypeDefinition typeDef) 154 | { 155 | var memberIndex = this.ReadCompressedInt32(); 156 | if (memberIndex >= 0) 157 | { 158 | return (typeDef, memberIndex); 159 | } 160 | memberIndex = -(memberIndex + 1); 161 | 162 | var typeIndex = this.ReadCompressedUInt32(); 163 | var declaringClass = metadata.typeDefs[typeIndex]; 164 | 165 | return (declaringClass, memberIndex); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /Il2CppDumper/Utils/FileDialogNative.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Il2CppDumper 5 | { 6 | static class FileDialogNative 7 | { 8 | [ComImport] 9 | [ClassInterface(ClassInterfaceType.None)] 10 | [TypeLibType(TypeLibTypeFlags.FCanCreate)] 11 | [Guid(CLSIDGuid.FileOpenDialog)] 12 | internal class FileOpenDialogRCW 13 | { } 14 | 15 | internal class IIDGuid 16 | { 17 | private IIDGuid() { } // Avoid FxCop violation AvoidUninstantiatedInternalClasses 18 | // IID GUID strings for relevant COM interfaces 19 | internal const string IModalWindow = "b4db1657-70d7-485e-8e3e-6fcb5a5c1802"; 20 | internal const string IFileDialog = "42f85136-db7e-439c-85f1-e4075d135fc8"; 21 | internal const string IFileOpenDialog = "d57c7288-d4ad-4768-be02-9d969532d960"; 22 | internal const string IFileSaveDialog = "84bccd23-5fde-4cdb-aea4-af64b83d78ab"; 23 | internal const string IFileDialogEvents = "973510DB-7D7F-452B-8975-74A85828D354"; 24 | internal const string IShellItem = "43826D1E-E718-42EE-BC55-A1E261C37BFE"; 25 | internal const string IShellItemArray = "B63EA76D-1F85-456F-A19C-48159EFA858B"; 26 | } 27 | 28 | internal class CLSIDGuid 29 | { 30 | private CLSIDGuid() { } // Avoid FxCop violation AvoidUninstantiatedInternalClasses 31 | internal const string FileOpenDialog = "DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7"; 32 | internal const string FileSaveDialog = "C0B4E2F3-BA21-4773-8DBA-335EC946EB8B"; 33 | } 34 | 35 | [ComImport()] 36 | [Guid(IIDGuid.IFileDialog)] 37 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 38 | internal interface IFileDialog 39 | { 40 | [PreserveSig] 41 | int Show([In] IntPtr parent); 42 | 43 | void SetFileTypes([In] uint cFileTypes, [In][MarshalAs(UnmanagedType.LPArray)] COMDLG_FILTERSPEC[] rgFilterSpec); 44 | 45 | void SetFileTypeIndex([In] uint iFileType); 46 | 47 | void GetFileTypeIndex(out uint piFileType); 48 | 49 | void Advise([In, MarshalAs(UnmanagedType.Interface)] IFileDialogEvents pfde, out uint pdwCookie); 50 | 51 | void Unadvise([In] uint dwCookie); 52 | 53 | void SetOptions([In] FOS fos); 54 | 55 | void GetOptions(out FOS pfos); 56 | 57 | void SetDefaultFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); 58 | 59 | void SetFolder([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi); 60 | 61 | void GetFolder([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); 62 | 63 | void GetCurrentSelection([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); 64 | 65 | void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName); 66 | 67 | void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName); 68 | 69 | void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle); 70 | 71 | void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText); 72 | 73 | void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel); 74 | 75 | void GetResult([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); 76 | 77 | void AddPlace([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, int alignment); 78 | 79 | void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); 80 | 81 | void Close([MarshalAs(UnmanagedType.Error)] int hr); 82 | 83 | void SetClientGuid([In] ref Guid guid); 84 | 85 | void ClearClientData(); 86 | 87 | void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter); 88 | } 89 | 90 | [ComImport, 91 | Guid(IIDGuid.IFileDialogEvents), 92 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 93 | internal interface IFileDialogEvents 94 | { 95 | // NOTE: some of these callbacks are cancelable - returning S_FALSE means that 96 | // the dialog should not proceed (e.g. with closing, changing folder); to 97 | // support this, we need to use the PreserveSig attribute to enable us to return 98 | // the proper HRESULT 99 | [PreserveSig] 100 | int OnFileOk([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); 101 | 102 | [PreserveSig] 103 | int OnFolderChanging([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd, [In, MarshalAs(UnmanagedType.Interface)] IShellItem psiFolder); 104 | 105 | void OnFolderChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); 106 | 107 | void OnSelectionChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); 108 | 109 | void OnShareViolation([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd, [In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, out FDE_SHAREVIOLATION_RESPONSE pResponse); 110 | 111 | void OnTypeChange([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd); 112 | 113 | void OnOverwrite([In, MarshalAs(UnmanagedType.Interface)] IFileDialog pfd, [In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, out FDE_OVERWRITE_RESPONSE pResponse); 114 | } 115 | 116 | [ComImport, 117 | Guid(IIDGuid.IShellItem), 118 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 119 | internal interface IShellItem 120 | { 121 | void BindToHandler([In, MarshalAs(UnmanagedType.Interface)] IntPtr pbc, [In] ref Guid bhid, [In] ref Guid riid, out IntPtr ppv); 122 | 123 | void GetParent([MarshalAs(UnmanagedType.Interface)] out IShellItem ppsi); 124 | 125 | void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName); 126 | 127 | void GetAttributes([In] uint sfgaoMask, out uint psfgaoAttribs); 128 | 129 | void Compare([In, MarshalAs(UnmanagedType.Interface)] IShellItem psi, [In] uint hint, out int piOrder); 130 | } 131 | 132 | internal enum SIGDN : uint 133 | { 134 | SIGDN_NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL 135 | SIGDN_PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING 136 | SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING 137 | SIGDN_PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING 138 | SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR 139 | SIGDN_FILESYSPATH = 0x80058000, // SHGDN_FORPARSING 140 | SIGDN_URL = 0x80068000, // SHGDN_FORPARSING 141 | SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR 142 | SIGDN_PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER 143 | } 144 | 145 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)] 146 | internal struct COMDLG_FILTERSPEC 147 | { 148 | [MarshalAs(UnmanagedType.LPWStr)] 149 | internal string pszName; 150 | [MarshalAs(UnmanagedType.LPWStr)] 151 | internal string pszSpec; 152 | } 153 | 154 | [Flags] 155 | internal enum FOS : uint 156 | { 157 | FOS_OVERWRITEPROMPT = 0x00000002, 158 | FOS_STRICTFILETYPES = 0x00000004, 159 | FOS_NOCHANGEDIR = 0x00000008, 160 | FOS_PICKFOLDERS = 0x00000020, 161 | FOS_FORCEFILESYSTEM = 0x00000040, // Ensure that items returned are filesystem items. 162 | FOS_ALLNONSTORAGEITEMS = 0x00000080, // Allow choosing items that have no storage. 163 | FOS_NOVALIDATE = 0x00000100, 164 | FOS_ALLOWMULTISELECT = 0x00000200, 165 | FOS_PATHMUSTEXIST = 0x00000800, 166 | FOS_FILEMUSTEXIST = 0x00001000, 167 | FOS_CREATEPROMPT = 0x00002000, 168 | FOS_SHAREAWARE = 0x00004000, 169 | FOS_NOREADONLYRETURN = 0x00008000, 170 | FOS_NOTESTFILECREATE = 0x00010000, 171 | FOS_HIDEMRUPLACES = 0x00020000, 172 | FOS_HIDEPINNEDPLACES = 0x00040000, 173 | FOS_NODEREFERENCELINKS = 0x00100000, 174 | FOS_DONTADDTORECENT = 0x02000000, 175 | FOS_FORCESHOWHIDDEN = 0x10000000, 176 | FOS_DEFAULTNOMINIMODE = 0x20000000 177 | } 178 | 179 | internal enum FDE_SHAREVIOLATION_RESPONSE 180 | { 181 | FDESVR_DEFAULT = 0x00000000, 182 | FDESVR_ACCEPT = 0x00000001, 183 | FDESVR_REFUSE = 0x00000002 184 | } 185 | 186 | internal enum FDE_OVERWRITE_RESPONSE 187 | { 188 | FDEOR_DEFAULT = 0x00000000, 189 | FDEOR_ACCEPT = 0x00000001, 190 | FDEOR_REFUSE = 0x00000002 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Il2CppDumper/IO/BinaryStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace Il2CppDumper 9 | { 10 | public class BinaryStream : IDisposable 11 | { 12 | public double Version; 13 | public bool Is32Bit; 14 | public ulong ImageBase; 15 | private readonly Stream stream; 16 | private readonly BinaryReader reader; 17 | private readonly BinaryWriter writer; 18 | private readonly MethodInfo readClass; 19 | private readonly MethodInfo readClassArray; 20 | private readonly Dictionary genericMethodCache; 21 | private readonly Dictionary attributeCache; 22 | 23 | public BinaryStream(Stream input) 24 | { 25 | stream = input; 26 | reader = new BinaryReader(stream, Encoding.UTF8, true); 27 | writer = new BinaryWriter(stream, Encoding.UTF8, true); 28 | readClass = GetType().GetMethod("ReadClass", Type.EmptyTypes); 29 | readClassArray = GetType().GetMethod("ReadClassArray", new[] { typeof(long) }); 30 | genericMethodCache = new(); 31 | attributeCache = new(); 32 | } 33 | 34 | public bool ReadBoolean() => reader.ReadBoolean(); 35 | 36 | public byte ReadByte() => reader.ReadByte(); 37 | 38 | public byte[] ReadBytes(int count) => reader.ReadBytes(count); 39 | 40 | public sbyte ReadSByte() => reader.ReadSByte(); 41 | 42 | public short ReadInt16() => reader.ReadInt16(); 43 | 44 | public ushort ReadUInt16() => reader.ReadUInt16(); 45 | 46 | public int ReadInt32() => reader.ReadInt32(); 47 | 48 | public uint ReadUInt32() => reader.ReadUInt32(); 49 | 50 | public long ReadInt64() => reader.ReadInt64(); 51 | 52 | public ulong ReadUInt64() => reader.ReadUInt64(); 53 | 54 | public float ReadSingle() => reader.ReadSingle(); 55 | 56 | public double ReadDouble() => reader.ReadDouble(); 57 | 58 | public uint ReadCompressedUInt32() => reader.ReadCompressedUInt32(); 59 | 60 | public int ReadCompressedInt32() => reader.ReadCompressedInt32(); 61 | 62 | public uint ReadULeb128() => reader.ReadULeb128(); 63 | 64 | public void Write(bool value) => writer.Write(value); 65 | 66 | public void Write(byte value) => writer.Write(value); 67 | 68 | public void Write(sbyte value) => writer.Write(value); 69 | 70 | public void Write(short value) => writer.Write(value); 71 | 72 | public void Write(ushort value) => writer.Write(value); 73 | 74 | public void Write(int value) => writer.Write(value); 75 | 76 | public void Write(uint value) => writer.Write(value); 77 | 78 | public void Write(long value) => writer.Write(value); 79 | 80 | public void Write(ulong value) => writer.Write(value); 81 | 82 | public void Write(float value) => writer.Write(value); 83 | 84 | public void Write(double value) => writer.Write(value); 85 | 86 | public ulong Position 87 | { 88 | get => (ulong)stream.Position; 89 | set => stream.Position = (long)value; 90 | } 91 | 92 | public ulong Length => (ulong)stream.Length; 93 | 94 | private object ReadPrimitive(Type type) 95 | { 96 | return type.Name switch 97 | { 98 | "Int32" => ReadInt32(), 99 | "UInt32" => ReadUInt32(), 100 | "Int16" => ReadInt16(), 101 | "UInt16" => ReadUInt16(), 102 | "Byte" => ReadByte(), 103 | "Int64" => ReadIntPtr(), 104 | "UInt64" => ReadUIntPtr(), 105 | _ => throw new NotSupportedException() 106 | }; 107 | } 108 | 109 | public T ReadClass(ulong addr) where T : new() 110 | { 111 | Position = addr; 112 | return ReadClass(); 113 | } 114 | 115 | public T ReadClass() where T : new() 116 | { 117 | var type = typeof(T); 118 | if (type.IsPrimitive) 119 | { 120 | return (T)ReadPrimitive(type); 121 | } 122 | else 123 | { 124 | var t = new T(); 125 | foreach (var i in t.GetType().GetFields()) 126 | { 127 | if (!attributeCache.TryGetValue(i, out var versionAttributes)) 128 | { 129 | if (Attribute.IsDefined(i, typeof(VersionAttribute))) 130 | { 131 | versionAttributes = i.GetCustomAttributes().ToArray(); 132 | attributeCache.Add(i, versionAttributes); 133 | } 134 | } 135 | if (versionAttributes?.Length > 0) 136 | { 137 | var read = false; 138 | foreach (var versionAttribute in versionAttributes) 139 | { 140 | if (Version >= versionAttribute.Min && Version <= versionAttribute.Max) 141 | { 142 | read = true; 143 | break; 144 | } 145 | } 146 | if (!read) 147 | { 148 | continue; 149 | } 150 | } 151 | var fieldType = i.FieldType; 152 | if (fieldType.IsPrimitive) 153 | { 154 | i.SetValue(t, ReadPrimitive(fieldType)); 155 | } 156 | else if (fieldType.IsEnum) 157 | { 158 | var e = fieldType.GetField("value__").FieldType; 159 | i.SetValue(t, ReadPrimitive(e)); 160 | } 161 | else if (fieldType.IsArray) 162 | { 163 | var arrayLengthAttribute = i.GetCustomAttribute(); 164 | if (!genericMethodCache.TryGetValue(fieldType, out var methodInfo)) 165 | { 166 | methodInfo = readClassArray.MakeGenericMethod(fieldType.GetElementType()); 167 | genericMethodCache.Add(fieldType, methodInfo); 168 | } 169 | i.SetValue(t, methodInfo.Invoke(this, new object[] { arrayLengthAttribute.Length })); 170 | } 171 | else 172 | { 173 | if (!genericMethodCache.TryGetValue(fieldType, out var methodInfo)) 174 | { 175 | methodInfo = readClass.MakeGenericMethod(fieldType); 176 | genericMethodCache.Add(fieldType, methodInfo); 177 | } 178 | i.SetValue(t, methodInfo.Invoke(this, null)); 179 | } 180 | } 181 | return t; 182 | } 183 | } 184 | 185 | public T[] ReadClassArray(long count) where T : new() 186 | { 187 | var t = new T[count]; 188 | for (var i = 0; i < count; i++) 189 | { 190 | t[i] = ReadClass(); 191 | } 192 | return t; 193 | } 194 | 195 | public T[] ReadClassArray(ulong addr, ulong count) where T : new() 196 | { 197 | return ReadClassArray(addr, (long)count); 198 | } 199 | 200 | public T[] ReadClassArray(ulong addr, long count) where T : new() 201 | { 202 | Position = addr; 203 | return ReadClassArray(count); 204 | } 205 | 206 | public string ReadStringToNull(ulong addr) 207 | { 208 | Position = addr; 209 | var bytes = new List(); 210 | byte b; 211 | while ((b = ReadByte()) != 0) 212 | bytes.Add(b); 213 | return Encoding.UTF8.GetString(bytes.ToArray()); 214 | } 215 | 216 | public long ReadIntPtr() 217 | { 218 | return Is32Bit ? ReadInt32() : ReadInt64(); 219 | } 220 | 221 | public virtual ulong ReadUIntPtr() 222 | { 223 | return Is32Bit ? ReadUInt32() : ReadUInt64(); 224 | } 225 | 226 | public ulong PointerSize 227 | { 228 | get => Is32Bit ? 4ul : 8ul; 229 | } 230 | 231 | public BinaryReader Reader => reader; 232 | 233 | public BinaryWriter Writer => writer; 234 | 235 | protected virtual void Dispose(bool disposing) 236 | { 237 | if (disposing) 238 | { 239 | reader.Dispose(); 240 | writer.Dispose(); 241 | stream.Close(); 242 | } 243 | } 244 | 245 | public void Dispose() 246 | { 247 | Dispose(true); 248 | GC.SuppressFinalize(this); 249 | } 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/Macho.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using static Il2CppDumper.ArmUtils; 7 | 8 | namespace Il2CppDumper 9 | { 10 | public sealed class Macho : Il2Cpp 11 | { 12 | private static readonly byte[] FeatureBytes1 = { 0x0, 0x22 };//MOVS R2, #0 13 | private static readonly byte[] FeatureBytes2 = { 0x78, 0x44, 0x79, 0x44 };//ADD R0, PC and ADD R1, PC 14 | private readonly List sections = new(); 15 | private readonly ulong vmaddr; 16 | 17 | public Macho(Stream stream) : base(stream) 18 | { 19 | Is32Bit = true; 20 | Position += 16; //skip magic, cputype, cpusubtype, filetype 21 | var ncmds = ReadUInt32(); 22 | Position += 8; //skip sizeofcmds, flags 23 | for (var i = 0; i < ncmds; i++) 24 | { 25 | var pos = Position; 26 | var cmd = ReadUInt32(); 27 | var cmdsize = ReadUInt32(); 28 | switch (cmd) 29 | { 30 | case 1: //LC_SEGMENT 31 | var segname = Encoding.UTF8.GetString(ReadBytes(16)).TrimEnd('\0'); 32 | if (segname == "__TEXT") //__PAGEZERO 33 | { 34 | vmaddr = ReadUInt32(); 35 | } 36 | else 37 | { 38 | Position += 4; 39 | } 40 | Position += 20; //skip vmsize, fileoff, filesize, maxprot, initprot 41 | var nsects = ReadUInt32(); 42 | Position += 4; //skip flags 43 | for (var j = 0; j < nsects; j++) 44 | { 45 | var section = new MachoSection(); 46 | sections.Add(section); 47 | section.sectname = Encoding.UTF8.GetString(ReadBytes(16)).TrimEnd('\0'); 48 | Position += 16; //skip segname 49 | section.addr = ReadUInt32(); 50 | section.size = ReadUInt32(); 51 | section.offset = ReadUInt32(); 52 | Position += 12; //skip align, reloff, nreloc 53 | section.flags = ReadUInt32(); 54 | Position += 8; //skip reserved1, reserved2 55 | } 56 | break; 57 | case 0x21: //LC_ENCRYPTION_INFO 58 | Position += 8; 59 | var cryptID = ReadUInt32(); 60 | if (cryptID != 0) 61 | { 62 | Console.WriteLine("ERROR: This Mach-O executable is encrypted and cannot be processed."); 63 | } 64 | break; 65 | } 66 | Position = pos + cmdsize;//next 67 | } 68 | } 69 | 70 | public override void Init(ulong codeRegistration, ulong metadataRegistration) 71 | { 72 | base.Init(codeRegistration, metadataRegistration); 73 | methodPointers = methodPointers.Select(x => x - 1).ToArray(); 74 | customAttributeGenerators = customAttributeGenerators.Select(x => x - 1).ToArray(); 75 | } 76 | 77 | public override ulong MapVATR(ulong addr) 78 | { 79 | var section = sections.First(x => addr >= x.addr && addr <= x.addr + x.size); 80 | return addr - section.addr + section.offset; 81 | } 82 | 83 | public override ulong MapRTVA(ulong addr) 84 | { 85 | var section = sections.FirstOrDefault(x => addr >= x.offset && addr <= x.offset + x.size); 86 | if (section == null) 87 | { 88 | return 0; 89 | } 90 | return addr - section.offset + section.addr; 91 | } 92 | 93 | public override bool Search() 94 | { 95 | if (Version < 21) 96 | { 97 | var __mod_init_func = sections.First(x => x.sectname == "__mod_init_func"); 98 | var addrs = ReadClassArray(__mod_init_func.offset, __mod_init_func.size / 4u); 99 | foreach (var a in addrs) 100 | { 101 | if (a > 0) 102 | { 103 | var i = a - 1; 104 | Position = MapVATR(i); 105 | Position += 4; 106 | var buff = ReadBytes(2); 107 | if (FeatureBytes1.SequenceEqual(buff)) 108 | { 109 | Position += 12; 110 | buff = ReadBytes(4); 111 | if (FeatureBytes2.SequenceEqual(buff)) 112 | { 113 | Position = MapVATR(i) + 10; 114 | var subaddr = DecodeMov(ReadBytes(8)) + i + 24u - 1u; 115 | var rsubaddr = MapVATR(subaddr); 116 | Position = rsubaddr; 117 | var ptr = DecodeMov(ReadBytes(8)) + subaddr + 16u; 118 | Position = MapVATR(ptr); 119 | var metadataRegistration = ReadUInt32(); 120 | Position = rsubaddr + 8; 121 | buff = ReadBytes(4); 122 | Position = rsubaddr + 14; 123 | buff = buff.Concat(ReadBytes(4)).ToArray(); 124 | var codeRegistration = DecodeMov(buff) + subaddr + 22u; 125 | Console.WriteLine("CodeRegistration : {0:x}", codeRegistration); 126 | Console.WriteLine("MetadataRegistration : {0:x}", metadataRegistration); 127 | Init(codeRegistration, metadataRegistration); 128 | return true; 129 | } 130 | } 131 | } 132 | } 133 | return false; 134 | } 135 | else 136 | { 137 | var __mod_init_func = sections.First(x => x.sectname == "__mod_init_func"); 138 | var addrs = ReadClassArray(__mod_init_func.offset, __mod_init_func.size / 4u); 139 | foreach (var a in addrs) 140 | { 141 | if (a > 0) 142 | { 143 | var i = a - 1; 144 | Position = MapVATR(i); 145 | Position += 4; 146 | var buff = ReadBytes(2); 147 | if (FeatureBytes1.SequenceEqual(buff)) 148 | { 149 | Position += 12; 150 | buff = ReadBytes(4); 151 | if (FeatureBytes2.SequenceEqual(buff)) 152 | { 153 | Position = MapVATR(i) + 10; 154 | var subaddr = DecodeMov(ReadBytes(8)) + i + 24u - 1u; 155 | var rsubaddr = MapVATR(subaddr); 156 | Position = rsubaddr; 157 | var ptr = DecodeMov(ReadBytes(8)) + subaddr + 16u; 158 | Position = MapVATR(ptr); 159 | var metadataRegistration = ReadUInt32(); 160 | Position = rsubaddr + 8; 161 | buff = ReadBytes(4); 162 | Position = rsubaddr + 14; 163 | buff = buff.Concat(ReadBytes(4)).ToArray(); 164 | var codeRegistration = DecodeMov(buff) + subaddr + 26u; 165 | Console.WriteLine("CodeRegistration : {0:x}", codeRegistration); 166 | Console.WriteLine("MetadataRegistration : {0:x}", metadataRegistration); 167 | Init(codeRegistration, metadataRegistration); 168 | return true; 169 | } 170 | } 171 | } 172 | } 173 | return false; 174 | } 175 | } 176 | 177 | public override bool PlusSearch(int methodCount, int typeDefinitionsCount, int imageCount) 178 | { 179 | var sectionHelper = GetSectionHelper(methodCount, typeDefinitionsCount, imageCount); 180 | var codeRegistration = sectionHelper.FindCodeRegistration(); 181 | var metadataRegistration = sectionHelper.FindMetadataRegistration(); 182 | return AutoPlusInit(codeRegistration, metadataRegistration); 183 | } 184 | 185 | public override bool SymbolSearch() 186 | { 187 | return false; 188 | } 189 | 190 | public override ulong GetRVA(ulong pointer) 191 | { 192 | return pointer - vmaddr; 193 | } 194 | 195 | public override SectionHelper GetSectionHelper(int methodCount, int typeDefinitionsCount, int imageCount) 196 | { 197 | var data = sections.Where(x => x.sectname == "__const").ToArray(); 198 | var code = sections.Where(x => x.flags == 0x80000400).ToArray(); 199 | var bss = sections.Where(x => x.flags == 1u).ToArray(); 200 | var sectionHelper = new SectionHelper(this, methodCount, typeDefinitionsCount, metadataUsagesCount, imageCount); 201 | sectionHelper.SetSection(SearchSectionType.Exec, code); 202 | sectionHelper.SetSection(SearchSectionType.Data, data); 203 | sectionHelper.SetSection(SearchSectionType.Bss, bss); 204 | return sectionHelper; 205 | } 206 | 207 | public override bool CheckDump() => false; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /Il2CppDumper/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text.Json; 6 | 7 | namespace Il2CppDumper 8 | { 9 | class Program 10 | { 11 | private static Config config; 12 | 13 | [STAThread] 14 | static void Main(string[] args) 15 | { 16 | config = JsonSerializer.Deserialize(File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + @"config.json")); 17 | string il2cppPath = null; 18 | string metadataPath = null; 19 | string outputDir = null; 20 | 21 | if (args.Length == 1) 22 | { 23 | if (args[0] == "-h" || args[0] == "--help" || args[0] == "/?" || args[0] == "/h") 24 | { 25 | ShowHelp(); 26 | return; 27 | } 28 | } 29 | if (args.Length > 3) 30 | { 31 | ShowHelp(); 32 | return; 33 | } 34 | if (args.Length > 1) 35 | { 36 | foreach (var arg in args) 37 | { 38 | if (File.Exists(arg)) 39 | { 40 | var file = File.ReadAllBytes(arg); 41 | if (BitConverter.ToUInt32(file, 0) == 0xFAB11BAF) 42 | { 43 | metadataPath = arg; 44 | } 45 | else 46 | { 47 | il2cppPath = arg; 48 | } 49 | } 50 | else if (Directory.Exists(arg)) 51 | { 52 | outputDir = Path.GetFullPath(arg) + Path.DirectorySeparatorChar; 53 | } 54 | } 55 | } 56 | outputDir ??= AppDomain.CurrentDomain.BaseDirectory; 57 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 58 | { 59 | if (il2cppPath == null) 60 | { 61 | var ofd = new OpenFileDialog 62 | { 63 | Filter = "Il2Cpp binary file|*.*" 64 | }; 65 | if (ofd.ShowDialog()) 66 | { 67 | il2cppPath = ofd.FileName; 68 | ofd.Filter = "global-metadata|global-metadata.dat"; 69 | if (ofd.ShowDialog()) 70 | { 71 | metadataPath = ofd.FileName; 72 | } 73 | else 74 | { 75 | return; 76 | } 77 | } 78 | else 79 | { 80 | return; 81 | } 82 | } 83 | } 84 | if (il2cppPath == null) 85 | { 86 | ShowHelp(); 87 | return; 88 | } 89 | if (metadataPath == null) 90 | { 91 | Console.WriteLine($"ERROR: Metadata file not found or encrypted."); 92 | } 93 | else 94 | { 95 | try 96 | { 97 | if (Init(il2cppPath, metadataPath, out var metadata, out var il2Cpp)) 98 | { 99 | Dump(metadata, il2Cpp, outputDir); 100 | } 101 | } 102 | catch (Exception e) 103 | { 104 | Console.WriteLine(e); 105 | } 106 | } 107 | if (config.RequireAnyKey) 108 | { 109 | Console.WriteLine("Press any key to exit..."); 110 | Console.ReadKey(true); 111 | } 112 | } 113 | 114 | static void ShowHelp() 115 | { 116 | Console.WriteLine($"usage: {AppDomain.CurrentDomain.FriendlyName} "); 117 | } 118 | 119 | private static bool Init(string il2cppPath, string metadataPath, out Metadata metadata, out Il2Cpp il2Cpp) 120 | { 121 | Console.WriteLine("Initializing metadata..."); 122 | var metadataBytes = File.ReadAllBytes(metadataPath); 123 | metadata = new Metadata(new MemoryStream(metadataBytes)); 124 | Console.WriteLine($"Metadata Version: {metadata.Version}"); 125 | 126 | Console.WriteLine("Initializing il2cpp file..."); 127 | var il2cppBytes = File.ReadAllBytes(il2cppPath); 128 | var il2cppMagic = BitConverter.ToUInt32(il2cppBytes, 0); 129 | var il2CppMemory = new MemoryStream(il2cppBytes); 130 | switch (il2cppMagic) 131 | { 132 | default: 133 | throw new NotSupportedException("ERROR: il2cpp file not supported."); 134 | case 0x464c457f: //ELF 135 | if (il2cppBytes[4] == 2) //ELF64 136 | { 137 | il2Cpp = new Elf64(il2CppMemory); 138 | } 139 | else 140 | { 141 | il2Cpp = new Elf(il2CppMemory); 142 | } 143 | break; 144 | case 0xCAFEBABE: //FAT Mach-O 145 | case 0xBEBAFECA: 146 | var machofat = new MachoFat(new MemoryStream(il2cppBytes)); 147 | Console.Write("Select Platform: "); 148 | for (var i = 0; i < machofat.fats.Length; i++) 149 | { 150 | var fat = machofat.fats[i]; 151 | Console.Write(fat.magic == 0xFEEDFACF ? $"{i + 1}.64bit " : $"{i + 1}.32bit "); 152 | } 153 | Console.WriteLine(); 154 | var key = Console.ReadKey(true); 155 | var index = int.Parse(key.KeyChar.ToString()) - 1; 156 | var magic = machofat.fats[index % 2].magic; 157 | il2cppBytes = machofat.GetMacho(index % 2); 158 | il2CppMemory = new MemoryStream(il2cppBytes); 159 | if (magic == 0xFEEDFACF) 160 | goto case 0xFEEDFACF; 161 | else 162 | goto case 0xFEEDFACE; 163 | case 0xFEEDFACF: // 64bit Mach-O 164 | il2Cpp = new Macho64(il2CppMemory); 165 | break; 166 | case 0xFEEDFACE: // 32bit Mach-O 167 | il2Cpp = new Macho(il2CppMemory); 168 | break; 169 | } 170 | var version = config.ForceIl2CppVersion ? config.ForceVersion : metadata.Version; 171 | il2Cpp.SetProperties(version, metadata.metadataUsagesCount); 172 | Console.WriteLine($"Il2Cpp Version: {il2Cpp.Version}"); 173 | if (config.ForceDump || il2Cpp.CheckDump()) 174 | { 175 | if (il2Cpp is ElfBase elf) 176 | { 177 | Console.WriteLine("Detected this may be a dump file."); 178 | Console.WriteLine("Input il2cpp dump address or input 0 to force continue:"); 179 | var DumpAddr = Convert.ToUInt64(Console.ReadLine(), 16); 180 | if (DumpAddr != 0) 181 | { 182 | il2Cpp.ImageBase = DumpAddr; 183 | il2Cpp.IsDumped = true; 184 | if (!config.NoRedirectedPointer) 185 | { 186 | elf.Reload(); 187 | } 188 | } 189 | } 190 | else 191 | { 192 | il2Cpp.IsDumped = true; 193 | } 194 | } 195 | 196 | Console.WriteLine("Searching..."); 197 | try 198 | { 199 | var flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); 200 | 201 | if (!flag) 202 | { 203 | flag = il2Cpp.Search(); 204 | } 205 | if (!flag) 206 | { 207 | flag = il2Cpp.SymbolSearch(); 208 | } 209 | if (!flag) 210 | { 211 | Console.WriteLine("ERROR: Can't use auto mode to process file, try manual mode."); 212 | Console.Write("Input CodeRegistration: "); 213 | var codeRegistration = Convert.ToUInt64(Console.ReadLine(), 16); 214 | Console.Write("Input MetadataRegistration: "); 215 | var metadataRegistration = Convert.ToUInt64(Console.ReadLine(), 16); 216 | il2Cpp.Init(codeRegistration, metadataRegistration); 217 | } 218 | } 219 | catch (Exception e) 220 | { 221 | Console.WriteLine(e); 222 | Console.WriteLine("ERROR: An error occurred while processing."); 223 | return false; 224 | } 225 | return true; 226 | } 227 | 228 | private static void Dump(Metadata metadata, Il2Cpp il2Cpp, string outputDir) 229 | { 230 | Console.WriteLine("Dumping..."); 231 | var executor = new Il2CppExecutor(metadata, il2Cpp); 232 | var decompiler = new Il2CppDecompiler(executor); 233 | decompiler.Decompile(config, outputDir); 234 | Console.WriteLine("Done!"); 235 | if (config.GenerateStruct) 236 | { 237 | Console.WriteLine("Generate struct..."); 238 | var scriptGenerator = new StructGenerator(executor); 239 | scriptGenerator.WriteScript(outputDir); 240 | Console.WriteLine("Done!"); 241 | } 242 | if (config.GenerateDummyDll) 243 | { 244 | Console.WriteLine("Generate dummy dll..."); 245 | DummyAssemblyExporter.Export(executor, outputDir, config.DummyDllAddToken); 246 | Console.WriteLine("Done!"); 247 | } 248 | } 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Il2CppDumper/Il2Cpp/Metadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace Il2CppDumper 9 | { 10 | public sealed class Metadata : BinaryStream 11 | { 12 | public Il2CppGlobalMetadataHeader header; 13 | public Il2CppImageDefinition[] imageDefs; 14 | public Il2CppAssemblyDefinition[] assemblyDefs; 15 | public Il2CppTypeDefinition[] typeDefs; 16 | public Il2CppMethodDefinition[] methodDefs; 17 | public Il2CppParameterDefinition[] parameterDefs; 18 | public Il2CppFieldDefinition[] fieldDefs; 19 | private readonly Dictionary fieldDefaultValuesDic; 20 | private readonly Dictionary parameterDefaultValuesDic; 21 | public Il2CppPropertyDefinition[] propertyDefs; 22 | public Il2CppCustomAttributeTypeRange[] attributeTypeRanges; 23 | public Il2CppCustomAttributeDataRange[] attributeDataRanges; 24 | private readonly Dictionary> attributeTypeRangesDic; 25 | public Il2CppStringLiteral[] stringLiterals; 26 | private readonly Il2CppMetadataUsageList[] metadataUsageLists; 27 | private readonly Il2CppMetadataUsagePair[] metadataUsagePairs; 28 | public int[] attributeTypes; 29 | public int[] interfaceIndices; 30 | public Dictionary> metadataUsageDic; 31 | public long metadataUsagesCount; 32 | public int[] nestedTypeIndices; 33 | public Il2CppEventDefinition[] eventDefs; 34 | public Il2CppGenericContainer[] genericContainers; 35 | public Il2CppFieldRef[] fieldRefs; 36 | public Il2CppGenericParameter[] genericParameters; 37 | public int[] constraintIndices; 38 | public uint[] vtableMethods; 39 | public Il2CppRGCTXDefinition[] rgctxEntries; 40 | 41 | private readonly Dictionary stringCache = new(); 42 | 43 | public Metadata(Stream stream) : base(stream) 44 | { 45 | var sanity = ReadUInt32(); 46 | if (sanity != 0xFAB11BAF) 47 | { 48 | throw new InvalidDataException("ERROR: Metadata file supplied is not valid metadata file."); 49 | } 50 | var version = ReadInt32(); 51 | 52 | if (version != 23) 53 | { 54 | throw new NotSupportedException($"ERROR: Metadata file supplied is not a codm metadata[{version}]."); 55 | } 56 | Version = version; 57 | header = ReadClass(0); 58 | 59 | imageDefs = ReadMetadataClassArray(header.imagesOffset, header.imagesSize); 60 | assemblyDefs = ReadMetadataClassArray(header.assembliesOffset, header.assembliesSize); 61 | typeDefs = ReadMetadataClassArray(header.typeDefinitionsOffset, header.typeDefinitionsSize); 62 | methodDefs = ReadMetadataClassArray(header.methodsOffset, header.methodsSize); 63 | parameterDefs = ReadMetadataClassArray(header.parametersOffset, header.parametersSize); 64 | fieldDefs = ReadMetadataClassArray(header.fieldsOffset, header.fieldsSize); 65 | var fieldDefaultValues = ReadMetadataClassArray(header.fieldDefaultValuesOffset, header.fieldDefaultValuesSize); 66 | var parameterDefaultValues = ReadMetadataClassArray(header.parameterDefaultValuesOffset, header.parameterDefaultValuesSize); 67 | fieldDefaultValuesDic = fieldDefaultValues.ToDictionary(x => x.fieldIndex); 68 | parameterDefaultValuesDic = parameterDefaultValues.ToDictionary(x => x.parameterIndex); 69 | propertyDefs = ReadMetadataClassArray(header.propertiesOffset, header.propertiesSize); 70 | interfaceIndices = ReadClassArray(header.interfacesOffset, header.interfacesSize / 4); 71 | nestedTypeIndices = ReadClassArray(header.nestedTypesOffset, header.nestedTypesSize / 4); 72 | eventDefs = ReadMetadataClassArray(header.eventsOffset, header.eventsSize); 73 | genericContainers = ReadMetadataClassArray(header.genericContainersOffset, header.genericContainersSize); 74 | genericParameters = ReadMetadataClassArray(header.genericParametersOffset, header.genericParametersSize); 75 | constraintIndices = ReadClassArray(header.genericParameterConstraintsOffset, header.genericParameterConstraintsSize / 4); 76 | vtableMethods = ReadClassArray(header.vtableMethodsOffset, header.vtableMethodsSize / 4); 77 | stringLiterals = ReadMetadataClassArray(header.stringLiteralOffset, header.stringLiteralSize); 78 | fieldRefs = ReadMetadataClassArray(header.fieldRefsOffset, header.fieldRefsSize); 79 | metadataUsageLists = ReadMetadataClassArray(header.metadataUsageListsOffset, header.metadataUsageListsCount); 80 | metadataUsagePairs = ReadMetadataClassArray(header.metadataUsagePairsOffset, header.metadataUsagePairsCount); 81 | ProcessingMetadataUsage(); 82 | 83 | attributeTypeRanges = ReadMetadataClassArray(header.attributesInfoOffset, header.attributesInfoCount); 84 | attributeTypes = ReadClassArray(header.attributeTypesOffset, header.attributeTypesCount / 4); 85 | rgctxEntries = ReadMetadataClassArray(header.rgctxEntriesOffset, header.rgctxEntriesCount); 86 | } 87 | 88 | private T[] ReadMetadataClassArray(uint addr, int count) where T : new() 89 | { 90 | return ReadClassArray(addr, count / SizeOf(typeof(T))); 91 | } 92 | 93 | public bool GetFieldDefaultValueFromIndex(int index, out Il2CppFieldDefaultValue value) 94 | { 95 | return fieldDefaultValuesDic.TryGetValue(index, out value); 96 | } 97 | 98 | public bool GetParameterDefaultValueFromIndex(int index, out Il2CppParameterDefaultValue value) 99 | { 100 | return parameterDefaultValuesDic.TryGetValue(index, out value); 101 | } 102 | 103 | public uint GetDefaultValueFromIndex(int index) 104 | { 105 | return (uint)(header.fieldAndParameterDefaultValueDataOffset + index); 106 | } 107 | 108 | public string GetStringFromIndex(uint index) 109 | { 110 | if (!stringCache.TryGetValue(index, out var result)) 111 | { 112 | result = ReadStringToNull(header.stringOffset + index); 113 | stringCache.Add(index, result); 114 | } 115 | return result; 116 | } 117 | 118 | public int GetCustomAttributeIndex(Il2CppImageDefinition imageDef, int customAttributeIndex, uint token) 119 | { 120 | if (Version > 24) 121 | { 122 | if (attributeTypeRangesDic[imageDef].TryGetValue(token, out var index)) 123 | { 124 | return index; 125 | } 126 | else 127 | { 128 | return -1; 129 | } 130 | } 131 | else 132 | { 133 | return customAttributeIndex; 134 | } 135 | } 136 | 137 | public string GetStringLiteralFromIndex(uint index) 138 | { 139 | var stringLiteral = stringLiterals[index]; 140 | Position = (uint)(header.stringLiteralDataOffset + stringLiteral.dataIndex); 141 | return Encoding.UTF8.GetString(ReadBytes((int)stringLiteral.length)); 142 | } 143 | 144 | private void ProcessingMetadataUsage() 145 | { 146 | metadataUsageDic = new Dictionary>(); 147 | for (uint i = 1; i <= 6; i++) 148 | { 149 | metadataUsageDic[(Il2CppMetadataUsage)i] = new SortedDictionary(); 150 | } 151 | foreach (var metadataUsageList in metadataUsageLists) 152 | { 153 | for (int i = 0; i < metadataUsageList.count; i++) 154 | { 155 | var offset = metadataUsageList.start + i; 156 | if (offset >= metadataUsagePairs.Length) 157 | { 158 | continue; 159 | } 160 | var metadataUsagePair = metadataUsagePairs[offset]; 161 | var usage = GetEncodedIndexType(metadataUsagePair.encodedSourceIndex); 162 | var decodedIndex = GetDecodedMethodIndex(metadataUsagePair.encodedSourceIndex); 163 | metadataUsageDic[(Il2CppMetadataUsage)usage][metadataUsagePair.destinationIndex] = decodedIndex; 164 | } 165 | } 166 | //metadataUsagesCount = metadataUsagePairs.Max(x => x.destinationIndex) + 1; 167 | metadataUsagesCount = metadataUsageDic.Max(x => x.Value.Select(y => y.Key).DefaultIfEmpty().Max()) + 1; 168 | } 169 | 170 | public static uint GetEncodedIndexType(uint index) 171 | { 172 | return (index & 0xE0000000) >> 29; 173 | } 174 | 175 | public uint GetDecodedMethodIndex(uint index) 176 | { 177 | if (Version >= 27) 178 | { 179 | return (index & 0x1FFFFFFEU) >> 1; 180 | } 181 | return index & 0x1FFFFFFFU; 182 | } 183 | 184 | public int SizeOf(Type type) 185 | { 186 | var size = 0; 187 | foreach (var i in type.GetFields()) 188 | { 189 | var attr = (VersionAttribute)Attribute.GetCustomAttribute(i, typeof(VersionAttribute)); 190 | if (attr != null) 191 | { 192 | if (Version < attr.Min || Version > attr.Max) 193 | continue; 194 | } 195 | var fieldType = i.FieldType; 196 | if (fieldType.IsPrimitive) 197 | { 198 | size += GetPrimitiveTypeSize(fieldType.Name); 199 | } 200 | else if (fieldType.IsEnum) 201 | { 202 | var e = fieldType.GetField("value__").FieldType; 203 | size += GetPrimitiveTypeSize(e.Name); 204 | } 205 | else if (fieldType.IsArray) 206 | { 207 | var arrayLengthAttribute = i.GetCustomAttribute(); 208 | size += arrayLengthAttribute.Length; 209 | } 210 | else 211 | { 212 | size += SizeOf(fieldType); 213 | } 214 | } 215 | return size; 216 | 217 | static int GetPrimitiveTypeSize(string name) 218 | { 219 | return name switch 220 | { 221 | "Int32" or "UInt32" => 4, 222 | "Int16" or "UInt16" => 2, 223 | _ => 0, 224 | }; 225 | } 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /Il2CppDumper/Il2Cpp/Il2CppClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public class Il2CppCodeRegistration 6 | { 7 | [Version(Max = 24.1)] 8 | public ulong methodPointersCount; 9 | [Version(Max = 24.1)] 10 | public ulong methodPointers; 11 | [Version(Max = 21)] 12 | public ulong delegateWrappersFromNativeToManagedCount; 13 | [Version(Max = 21)] 14 | public ulong delegateWrappersFromNativeToManaged; // note the double indirection to handle different calling conventions 15 | [Version(Min = 22)] 16 | public ulong reversePInvokeWrapperCount; 17 | [Version(Min = 22)] 18 | public ulong reversePInvokeWrappers; 19 | [Version(Max = 22)] 20 | public ulong delegateWrappersFromManagedToNativeCount; 21 | [Version(Max = 22)] 22 | public ulong delegateWrappersFromManagedToNative; 23 | [Version(Max = 22)] 24 | public ulong marshalingFunctionsCount; 25 | [Version(Max = 22)] 26 | public ulong marshalingFunctions; 27 | [Version(Min = 21, Max = 22)] 28 | public ulong ccwMarshalingFunctionsCount; 29 | [Version(Min = 21, Max = 22)] 30 | public ulong ccwMarshalingFunctions; 31 | public ulong genericMethodPointersCount; 32 | public ulong genericMethodPointers; 33 | [Version(Min = 24.5, Max = 24.5)] 34 | [Version(Min = 27.1)] 35 | public ulong genericAdjustorThunks; 36 | public ulong invokerPointersCount; 37 | public ulong invokerPointers; 38 | [Version(Max = 24.5)] 39 | public ulong customAttributeCount; 40 | [Version(Max = 24.5)] 41 | public ulong customAttributeGenerators; 42 | [Version(Min = 21, Max = 22)] 43 | public ulong guidCount; 44 | [Version(Min = 21, Max = 22)] 45 | public ulong guids; // Il2CppGuid 46 | [Version(Min = 22)] 47 | public ulong unresolvedVirtualCallCount; //29.1 unresolvedIndirectCallCount; 48 | [Version(Min = 22)] 49 | public ulong unresolvedVirtualCallPointers; 50 | [Version(Min = 29.1)] 51 | public ulong unresolvedInstanceCallPointers; 52 | [Version(Min = 29.1)] 53 | public ulong unresolvedStaticCallPointers; 54 | [Version(Min = 23)] 55 | public ulong interopDataCount; 56 | [Version(Min = 23)] 57 | public ulong interopData; 58 | [Version(Min = 24.3)] 59 | public ulong windowsRuntimeFactoryCount; 60 | [Version(Min = 24.3)] 61 | public ulong windowsRuntimeFactoryTable; 62 | [Version(Min = 24.2)] 63 | public ulong codeGenModulesCount; 64 | [Version(Min = 24.2)] 65 | public ulong codeGenModules; 66 | } 67 | 68 | public class Il2CppMetadataRegistration 69 | { 70 | public long genericClassesCount; 71 | public ulong genericClasses; 72 | public long genericInstsCount; 73 | public ulong genericInsts; 74 | public long genericMethodTableCount; 75 | public ulong genericMethodTable; 76 | public long typesCount; 77 | public ulong types; 78 | public long methodSpecsCount; 79 | public ulong methodSpecs; 80 | [Version(Max = 16)] 81 | public long methodReferencesCount; 82 | [Version(Max = 16)] 83 | public ulong methodReferences; 84 | 85 | public long fieldOffsetsCount; 86 | public ulong fieldOffsets; 87 | 88 | public long typeDefinitionsSizesCount; 89 | public ulong typeDefinitionsSizes; 90 | [Version(Min = 19)] 91 | public ulong metadataUsagesCount; 92 | [Version(Min = 19)] 93 | public ulong metadataUsages; 94 | } 95 | 96 | public enum Il2CppTypeEnum 97 | { 98 | IL2CPP_TYPE_END = 0x00, /* End of List */ 99 | IL2CPP_TYPE_VOID = 0x01, 100 | IL2CPP_TYPE_BOOLEAN = 0x02, 101 | IL2CPP_TYPE_CHAR = 0x03, 102 | IL2CPP_TYPE_I1 = 0x04, 103 | IL2CPP_TYPE_U1 = 0x05, 104 | IL2CPP_TYPE_I2 = 0x06, 105 | IL2CPP_TYPE_U2 = 0x07, 106 | IL2CPP_TYPE_I4 = 0x08, 107 | IL2CPP_TYPE_U4 = 0x09, 108 | IL2CPP_TYPE_I8 = 0x0a, 109 | IL2CPP_TYPE_U8 = 0x0b, 110 | IL2CPP_TYPE_R4 = 0x0c, 111 | IL2CPP_TYPE_R8 = 0x0d, 112 | IL2CPP_TYPE_STRING = 0x0e, 113 | IL2CPP_TYPE_PTR = 0x0f, /* arg: token */ 114 | IL2CPP_TYPE_BYREF = 0x10, /* arg: token */ 115 | IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: token */ 116 | IL2CPP_TYPE_CLASS = 0x12, /* arg: token */ 117 | IL2CPP_TYPE_VAR = 0x13, /* Generic parameter in a generic type definition, represented as number (compressed unsigned integer) number */ 118 | IL2CPP_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */ 119 | IL2CPP_TYPE_GENERICINST = 0x15, /* \x{2026} */ 120 | IL2CPP_TYPE_TYPEDBYREF = 0x16, 121 | IL2CPP_TYPE_I = 0x18, 122 | IL2CPP_TYPE_U = 0x19, 123 | IL2CPP_TYPE_FNPTR = 0x1b, /* arg: full method signature */ 124 | IL2CPP_TYPE_OBJECT = 0x1c, 125 | IL2CPP_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */ 126 | IL2CPP_TYPE_MVAR = 0x1e, /* Generic parameter in a generic method definition, represented as number (compressed unsigned integer) */ 127 | IL2CPP_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */ 128 | IL2CPP_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */ 129 | IL2CPP_TYPE_INTERNAL = 0x21, /* CLR internal type */ 130 | 131 | IL2CPP_TYPE_MODIFIER = 0x40, /* Or with the following types */ 132 | IL2CPP_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */ 133 | IL2CPP_TYPE_PINNED = 0x45, /* Local var that points to pinned object */ 134 | 135 | IL2CPP_TYPE_ENUM = 0x55, /* an enumeration */ 136 | IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff /* an index into IL2CPP type metadata table */ 137 | } 138 | 139 | public class Il2CppType 140 | { 141 | public ulong datapoint; 142 | public uint bits; 143 | public Union data { get; set; } 144 | public uint attrs { get; set; } 145 | public Il2CppTypeEnum type { get; set; } 146 | public uint num_mods { get; set; } 147 | public uint byref { get; set; } 148 | public uint pinned { get; set; } 149 | public uint valuetype { get; set; } 150 | 151 | public void Init(double version) 152 | { 153 | attrs = bits & 0xffff; 154 | type = (Il2CppTypeEnum)((bits >> 16) & 0xff); 155 | if (version >= 27.2) 156 | { 157 | num_mods = (bits >> 24) & 0x1f; 158 | byref = (bits >> 29) & 1; 159 | pinned = (bits >> 30) & 1; 160 | valuetype = bits >> 31; 161 | } 162 | else 163 | { 164 | num_mods = (bits >> 24) & 0x3f; 165 | byref = (bits >> 30) & 1; 166 | pinned = bits >> 31; 167 | } 168 | data = new Union { dummy = datapoint }; 169 | } 170 | 171 | public class Union 172 | { 173 | public ulong dummy; 174 | /// 175 | /// for VALUETYPE and CLASS 176 | /// 177 | public long klassIndex => (long)dummy; 178 | /// 179 | /// for VALUETYPE and CLASS at runtime 180 | /// 181 | public ulong typeHandle => dummy; 182 | /// 183 | /// for PTR and SZARRAY 184 | /// 185 | public ulong type => dummy; 186 | /// 187 | /// for ARRAY 188 | /// 189 | public ulong array => dummy; 190 | /// 191 | /// for VAR and MVAR 192 | /// 193 | public long genericParameterIndex => (long)dummy; 194 | /// 195 | /// for VAR and MVAR at runtime 196 | /// 197 | public ulong genericParameterHandle => dummy; 198 | /// 199 | /// for GENERICINST 200 | /// 201 | public ulong generic_class => dummy; 202 | } 203 | } 204 | 205 | public class Il2CppGenericClass 206 | { 207 | [Version(Max = 24.5)] 208 | public long typeDefinitionIndex; /* the generic type definition */ 209 | [Version(Min = 27)] 210 | public ulong type; /* the generic type definition */ 211 | public Il2CppGenericContext context; /* a context that contains the type instantiation doesn't contain any method instantiation */ 212 | public ulong cached_class; /* if present, the Il2CppClass corresponding to the instantiation. */ 213 | } 214 | 215 | public class Il2CppGenericContext 216 | { 217 | /* The instantiation corresponding to the class generic parameters */ 218 | public ulong class_inst; 219 | /* The instantiation corresponding to the method generic parameters */ 220 | public ulong method_inst; 221 | } 222 | 223 | public class Il2CppGenericInst 224 | { 225 | public long type_argc; 226 | public ulong type_argv; 227 | } 228 | 229 | public class Il2CppArrayType 230 | { 231 | public ulong etype; 232 | public byte rank; 233 | public byte numsizes; 234 | public byte numlobounds; 235 | public ulong sizes; 236 | public ulong lobounds; 237 | } 238 | 239 | public class Il2CppGenericMethodFunctionsDefinitions 240 | { 241 | public int genericMethodIndex; 242 | public Il2CppGenericMethodIndices indices; 243 | } 244 | 245 | public class Il2CppGenericMethodIndices 246 | { 247 | public int methodIndex; 248 | public int invokerIndex; 249 | [Version(Min = 24.5, Max = 24.5)] 250 | [Version(Min = 27.1)] 251 | public int adjustorThunk; 252 | }; 253 | 254 | public class Il2CppMethodSpec 255 | { 256 | public int methodDefinitionIndex; 257 | public int classIndexIndex; 258 | public int methodIndexIndex; 259 | }; 260 | 261 | public class Il2CppCodeGenModule 262 | { 263 | public ulong moduleName; 264 | public long methodPointerCount; 265 | public ulong methodPointers; 266 | [Version(Min = 24.5, Max = 24.5)] 267 | [Version(Min = 27.1)] 268 | public long adjustorThunkCount; 269 | [Version(Min = 24.5, Max = 24.5)] 270 | [Version(Min = 27.1)] 271 | public ulong adjustorThunks; 272 | public ulong invokerIndices; 273 | public ulong reversePInvokeWrapperCount; 274 | public ulong reversePInvokeWrapperIndices; 275 | public long rgctxRangesCount; 276 | public ulong rgctxRanges; 277 | public long rgctxsCount; 278 | public ulong rgctxs; 279 | public ulong debuggerMetadata; 280 | [Version(Min = 27, Max = 27.2)] 281 | public ulong customAttributeCacheGenerator; 282 | [Version(Min = 27)] 283 | public ulong moduleInitializer; 284 | [Version(Min = 27)] 285 | public ulong staticConstructorTypeIndices; 286 | [Version(Min = 27)] 287 | public ulong metadataRegistration; // Per-assembly mode only 288 | [Version(Min = 27)] 289 | public ulong codeRegistaration; // Per-assembly mode only 290 | } 291 | 292 | public class Il2CppRange 293 | { 294 | public int start; 295 | public int length; 296 | } 297 | 298 | public class Il2CppTokenRangePair 299 | { 300 | public uint token; 301 | public Il2CppRange range; 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /Il2CppDumper/Il2Cpp/MetadataClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public class Il2CppGlobalMetadataHeader 6 | { 7 | public uint sanity; 8 | public int version; 9 | public uint stringLiteralOffset; // string data for managed code 10 | public int stringLiteralSize; 11 | public uint stringLiteralDataOffset; 12 | public int stringLiteralDataSize; 13 | public uint stringOffset; // string data for metadata 14 | public int stringSize; 15 | public uint eventsOffset; // Il2CppEventDefinition 16 | public int eventsSize; 17 | public uint propertiesOffset; // Il2CppPropertyDefinition 18 | public int propertiesSize; 19 | public uint methodsOffset; // Il2CppMethodDefinition 20 | public int methodsSize; 21 | public uint parameterDefaultValuesOffset; // Il2CppParameterDefaultValue 22 | public int parameterDefaultValuesSize; 23 | public uint fieldDefaultValuesOffset; // Il2CppFieldDefaultValue 24 | public int fieldDefaultValuesSize; 25 | public uint fieldAndParameterDefaultValueDataOffset; // uint8_t 26 | public int fieldAndParameterDefaultValueDataSize; 27 | public int fieldMarshaledSizesOffset; // Il2CppFieldMarshaledSize 28 | public int fieldMarshaledSizesSize; 29 | public uint parametersOffset; // Il2CppParameterDefinition 30 | public int parametersSize; 31 | public uint fieldsOffset; // Il2CppFieldDefinition 32 | public int fieldsSize; 33 | public uint genericParametersOffset; // Il2CppGenericParameter 34 | public int genericParametersSize; 35 | public uint genericParameterConstraintsOffset; // TypeIndex 36 | public int genericParameterConstraintsSize; 37 | public uint genericContainersOffset; // Il2CppGenericContainer 38 | public int genericContainersSize; 39 | public uint nestedTypesOffset; // TypeDefinitionIndex 40 | public int nestedTypesSize; 41 | public uint interfacesOffset; // TypeIndex 42 | public int interfacesSize; 43 | public uint vtableMethodsOffset; // EncodedMethodIndex 44 | public int vtableMethodsSize; 45 | public int interfaceOffsetsOffset; // Il2CppInterfaceOffsetPair 46 | public int interfaceOffsetsSize; 47 | public uint typeDefinitionsOffset; // Il2CppTypeDefinition 48 | public int typeDefinitionsSize; 49 | public uint rgctxEntriesOffset; // Il2CppRGCTXDefinition 50 | public int rgctxEntriesCount; 51 | public uint imagesOffset; // Il2CppImageDefinition 52 | public int imagesSize; 53 | public uint assembliesOffset; // Il2CppAssemblyDefinition 54 | public int assembliesSize; 55 | public uint metadataUsageListsOffset; // Il2CppMetadataUsageList 56 | public int metadataUsageListsCount; 57 | public uint metadataUsagePairsOffset; // Il2CppMetadataUsagePair 58 | public int metadataUsagePairsCount; 59 | public uint fieldRefsOffset; // Il2CppFieldRef 60 | public int fieldRefsSize; 61 | public int referencedAssembliesOffset; // int32_t 62 | public int referencedAssembliesSize; 63 | public uint attributesInfoOffset; // Il2CppCustomAttributeTypeRange 64 | public int attributesInfoCount; 65 | public uint attributeTypesOffset; // TypeIndex 66 | public int attributeTypesCount; 67 | public int unresolvedVirtualCallParameterTypesOffset; // TypeIndex 68 | public int unresolvedVirtualCallParameterTypesSize; 69 | public int unresolvedVirtualCallParameterRangesOffset; // Il2CppRange 70 | public int unresolvedVirtualCallParameterRangesSize; 71 | public int windowsRuntimeTypeNamesOffset; // Il2CppWindowsRuntimeTypeNamePair 72 | public int windowsRuntimeTypeNamesSize; 73 | } 74 | 75 | 76 | /* 2 bytes shrink */ 77 | public class Il2CppAssemblyDefinition 78 | { 79 | public int imageIndex; 80 | public short customAttributeIndex; 81 | public int referencedAssemblyStart; 82 | public int referencedAssemblyCount; 83 | public Il2CppAssemblyNameDefinition aname; 84 | } 85 | 86 | public class Il2CppAssemblyNameDefinition 87 | { 88 | public uint nameIndex; 89 | public uint cultureIndex; 90 | public int hashValueIndex; 91 | public uint publicKeyIndex; 92 | public uint hash_alg; 93 | public int hash_len; 94 | public uint flags; 95 | public int major; 96 | public int minor; 97 | public int build; 98 | public int revision; 99 | [ArrayLength(Length = 8)] 100 | public byte[] public_key_token; 101 | } 102 | 103 | 104 | /* 20 bytes*/ 105 | public class Il2CppImageDefinition 106 | { 107 | public uint nameIndex; 108 | public int assemblyIndex; 109 | public int typeStart; 110 | public uint typeCount; 111 | public int entryPointIndex; 112 | 113 | } 114 | 115 | /* 80 bytes*/ 116 | public class Il2CppTypeDefinition 117 | { 118 | public uint nameIndex; 119 | public uint namespaceIndex; 120 | public int byvalTypeIndex; 121 | public int byrefTypeIndex; 122 | public int declaringTypeIndex; 123 | public int parentIndex; 124 | public int elementTypeIndex; // we can probably remove this one. Only used for enums 125 | public uint flags; 126 | public int fieldStart; 127 | public int methodStart; 128 | public int vtableStart; 129 | public short customAttributeIndex; 130 | public short rgctxStartIndex; 131 | public short rgctxCount; 132 | public short genericContainerIndex; 133 | public ushort eventStart; 134 | public ushort propertyStart; 135 | public ushort nestedTypesStart; 136 | public ushort interfacesStart; 137 | public ushort interfaceOffsetsStart; 138 | public ushort method_count; 139 | public ushort property_count; 140 | public ushort field_count; 141 | public ushort event_count; 142 | public ushort nested_type_count; 143 | public ushort vtable_count; 144 | public ushort interfaces_count; 145 | public ushort interface_offsets_count; 146 | 147 | // bitfield to portably encode boolean values as single bits 148 | // 01 - valuetype; 149 | // 02 - enumtype; 150 | // 03 - has_finalize; 151 | // 04 - has_cctor; 152 | // 05 - is_blittable; 153 | // 06 - is_import_or_windows_runtime; 154 | // 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) 155 | // 11 - PackingSize is default 156 | // 12 - ClassSize is default 157 | // 13-16 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128) - the specified packing size (even for explicit layouts) 158 | public ushort bitfield; 159 | public bool IsValueType => (bitfield & 0x1) == 1; 160 | public bool IsEnum => ((bitfield >> 1) & 0x1) == 1; 161 | } 162 | 163 | /* 42 bytes */ 164 | public class Il2CppMethodDefinition 165 | { 166 | public uint nameIndex; 167 | public int methodIndex; 168 | public int returnType; 169 | public int parameterStart; 170 | public uint token; 171 | public ushort declaringType; 172 | public short customAttributeIndex; 173 | public short genericContainerIndex; 174 | public ushort invokerIndex; 175 | public short delegateWrapperIndex; 176 | public short rgctxStartIndex; 177 | public ushort rgctxCount; 178 | public ushort flags; 179 | public ushort iflags; 180 | public ushort slot; 181 | public ushort parameterCount; 182 | } 183 | 184 | 185 | /* 10 bytes*/ 186 | public class Il2CppParameterDefinition 187 | { 188 | public uint nameIndex; 189 | public short customAttributeIndex; 190 | public int typeIndex; 191 | } 192 | 193 | /* 10 bytes */ 194 | public class Il2CppFieldDefinition 195 | { 196 | public uint nameIndex; 197 | public int typeIndex; 198 | public short customAttributeIndex; 199 | } 200 | 201 | public class Il2CppFieldDefaultValue 202 | { 203 | public int fieldIndex; 204 | public int typeIndex; 205 | public int dataIndex; 206 | } 207 | 208 | /* 12 bytes*/ 209 | public class Il2CppPropertyDefinition 210 | { 211 | public uint nameIndex; 212 | public short get; 213 | public short set; 214 | public ushort attrs; 215 | public ushort customAttributeIndex; 216 | } 217 | 218 | /* 4 bytes*/ 219 | public class Il2CppCustomAttributeTypeRange 220 | { 221 | public ushort start; 222 | public ushort count; 223 | } 224 | 225 | public class Il2CppMetadataUsageList 226 | { 227 | public uint start; 228 | public ushort count; 229 | } 230 | 231 | 232 | public class Il2CppMetadataUsagePair 233 | { 234 | public uint destinationIndex; 235 | public uint encodedSourceIndex; 236 | } 237 | 238 | public class Il2CppStringLiteral 239 | { 240 | public uint length; 241 | public int dataIndex; 242 | } 243 | 244 | public class Il2CppParameterDefaultValue 245 | { 246 | public int parameterIndex; 247 | public int typeIndex; 248 | public int dataIndex; 249 | } 250 | 251 | /* 16 bytes*/ 252 | public class Il2CppEventDefinition 253 | { 254 | public uint nameIndex; 255 | public int typeIndex; 256 | public short add; 257 | public short remove; 258 | public short raise; 259 | public short customAttributeIndex; 260 | } 261 | 262 | /* 12 bytes*/ 263 | public class Il2CppGenericContainer 264 | { 265 | /* index of the generic type definition or the generic method definition corresponding to this container */ 266 | public int ownerIndex; // either index into Il2CppClass metadata array or Il2CppMethodDefinition array 267 | public int genericParameterStart; 268 | /* If true, we're a generic method, otherwise a generic type definition. */ 269 | public short type_argc; 270 | /* Our type parameters. */ 271 | public short is_method; 272 | } 273 | 274 | /* 6 bytes*/ 275 | public class Il2CppFieldRef 276 | { 277 | public int typeIndex; 278 | public short fieldIndex; // local offset into type fields 279 | } 280 | 281 | /* 14 bytes*/ 282 | public class Il2CppGenericParameter 283 | { 284 | public uint nameIndex; /* Type or method this parameter was defined in. */ 285 | public ushort ownerIndex; 286 | public short constraintsStart; 287 | public short constraintsCount; 288 | public ushort num; 289 | public ushort flags; 290 | } 291 | 292 | public enum Il2CppRGCTXDataType 293 | { 294 | IL2CPP_RGCTX_DATA_INVALID, 295 | IL2CPP_RGCTX_DATA_TYPE, 296 | IL2CPP_RGCTX_DATA_CLASS, 297 | IL2CPP_RGCTX_DATA_METHOD, 298 | IL2CPP_RGCTX_DATA_ARRAY, 299 | IL2CPP_RGCTX_DATA_CONSTRAINED, 300 | } 301 | 302 | public class Il2CppRGCTXDefinitionData 303 | { 304 | public int rgctxDataDummy; 305 | public int methodIndex => rgctxDataDummy; 306 | public int typeIndex => rgctxDataDummy; 307 | } 308 | 309 | public class Il2CppRGCTXDefinition 310 | { 311 | public Il2CppRGCTXDataType type => (Il2CppRGCTXDataType)type_pre29; 312 | public int type_pre29; 313 | public Il2CppRGCTXDefinitionData data; 314 | [Version(Min = 27.2)] 315 | public ulong _data; 316 | } 317 | 318 | public enum Il2CppMetadataUsage 319 | { 320 | kIl2CppMetadataUsageInvalid, 321 | kIl2CppMetadataUsageTypeInfo, 322 | kIl2CppMetadataUsageIl2CppType, 323 | kIl2CppMetadataUsageMethodDef, 324 | kIl2CppMetadataUsageFieldInfo, 325 | kIl2CppMetadataUsageStringLiteral, 326 | kIl2CppMetadataUsageMethodRef, 327 | }; 328 | 329 | public class Il2CppCustomAttributeDataRange 330 | { 331 | public uint token; 332 | public uint startOffset; 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/Macho64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using static Il2CppDumper.ArmUtils; 7 | 8 | namespace Il2CppDumper 9 | { 10 | public sealed class Macho64 : Il2Cpp 11 | { 12 | private static readonly byte[] FeatureBytes1 = { 0x2, 0x0, 0x80, 0xD2 };//MOV X2, #0 13 | private static readonly byte[] FeatureBytes2 = { 0x3, 0x0, 0x80, 0x52 };//MOV W3, #0 14 | private readonly List sections = new(); 15 | private readonly ulong vmaddr; 16 | 17 | public Macho64(Stream stream) : base(stream) 18 | { 19 | Position += 16; //skip magic, cputype, cpusubtype, filetype 20 | var ncmds = ReadUInt32(); 21 | Position += 12; //skip sizeofcmds, flags, reserved 22 | for (var i = 0; i < ncmds; i++) 23 | { 24 | var pos = Position; 25 | var cmd = ReadUInt32(); 26 | var cmdsize = ReadUInt32(); 27 | switch (cmd) 28 | { 29 | case 0x19: //LC_SEGMENT_64 30 | var segname = Encoding.UTF8.GetString(ReadBytes(16)).TrimEnd('\0'); 31 | if (segname == "__TEXT") //__PAGEZERO 32 | { 33 | vmaddr = ReadUInt64(); 34 | } 35 | else 36 | { 37 | Position += 8; 38 | } 39 | Position += 32; //skip vmsize, fileoff, filesize, maxprot, initprot 40 | var nsects = ReadUInt32(); 41 | Position += 4; //skip flags 42 | for (var j = 0; j < nsects; j++) 43 | { 44 | var section = new MachoSection64Bit(); 45 | sections.Add(section); 46 | section.sectname = Encoding.UTF8.GetString(ReadBytes(16)).TrimEnd('\0'); 47 | Position += 16; //skip segname 48 | section.addr = ReadUInt64(); 49 | section.size = ReadUInt64(); 50 | section.offset = ReadUInt32(); 51 | Position += 12; //skip align, reloff, nreloc 52 | section.flags = ReadUInt32(); 53 | Position += 12; //skip reserved1, reserved2, reserved3 54 | } 55 | break; 56 | case 0x2C: //LC_ENCRYPTION_INFO_64 57 | Position += 8; 58 | var cryptID = ReadUInt32(); 59 | if (cryptID != 0) 60 | { 61 | Console.WriteLine("ERROR: This Mach-O executable is encrypted and cannot be processed."); 62 | } 63 | break; 64 | } 65 | Position = pos + cmdsize;//skip 66 | } 67 | } 68 | 69 | public override ulong MapVATR(ulong addr) 70 | { 71 | var section = sections.First(x => addr >= x.addr && addr <= x.addr + x.size); 72 | if (section.sectname == "__bss") 73 | { 74 | throw new Exception(); 75 | } 76 | return addr - section.addr + section.offset; 77 | } 78 | 79 | public override ulong MapRTVA(ulong addr) 80 | { 81 | var section = sections.FirstOrDefault(x => addr >= x.offset && addr <= x.offset + x.size); 82 | if (section == null) 83 | { 84 | return 0; 85 | } 86 | if (section.sectname == "__bss") 87 | { 88 | throw new Exception(); 89 | } 90 | return addr - section.offset + section.addr; 91 | } 92 | 93 | public override bool Search() 94 | { 95 | var codeRegistration = 0ul; 96 | var metadataRegistration = 0ul; 97 | if (Version < 23) 98 | { 99 | var __mod_init_func = sections.First(x => x.sectname == "__mod_init_func"); 100 | var addrs = ReadClassArray(__mod_init_func.offset, __mod_init_func.size / 8); 101 | foreach (var i in addrs) 102 | { 103 | if (i > 0) 104 | { 105 | var flag = false; 106 | var subaddr = 0ul; 107 | Position = MapVATR(i); 108 | var buff = ReadBytes(4); 109 | if (FeatureBytes1.SequenceEqual(buff)) 110 | { 111 | buff = ReadBytes(4); 112 | if (FeatureBytes2.SequenceEqual(buff)) 113 | { 114 | Position += 8; 115 | var inst = ReadBytes(4); 116 | if (IsAdr(inst)) 117 | { 118 | subaddr = DecodeAdr(i + 16, inst); 119 | flag = true; 120 | } 121 | } 122 | } 123 | else 124 | { 125 | Position += 0xc; 126 | buff = ReadBytes(4); 127 | if (FeatureBytes2.SequenceEqual(buff)) 128 | { 129 | buff = ReadBytes(4); 130 | if (FeatureBytes1.SequenceEqual(buff)) 131 | { 132 | Position -= 0x10; 133 | var inst = ReadBytes(4); 134 | if (IsAdr(inst)) 135 | { 136 | subaddr = DecodeAdr(i + 8, inst); 137 | flag = true; 138 | } 139 | } 140 | } 141 | } 142 | if (flag) 143 | { 144 | var rsubaddr = MapVATR(subaddr); 145 | Position = rsubaddr; 146 | codeRegistration = DecodeAdrp(subaddr, ReadBytes(4)); 147 | codeRegistration += DecodeAdd(ReadBytes(4)); 148 | Position = rsubaddr + 8; 149 | metadataRegistration = DecodeAdrp(subaddr + 8, ReadBytes(4)); 150 | metadataRegistration += DecodeAdd(ReadBytes(4)); 151 | } 152 | } 153 | } 154 | } 155 | if (Version == 23) 156 | { 157 | /* ADRP X0, unk 158 | * ADD X0, X0, unk 159 | * ADR X1, sub 160 | * NOP 161 | * MOV X2, #0 162 | * MOV W3, #0 163 | * B sub 164 | */ 165 | var __mod_init_func = sections.First(x => x.sectname == "__mod_init_func"); 166 | var addrs = ReadClassArray(__mod_init_func.offset, __mod_init_func.size / 8); 167 | foreach (var i in addrs) 168 | { 169 | if (i > 0) 170 | { 171 | Position = MapVATR(i) + 16; 172 | var buff = ReadBytes(4); 173 | if (FeatureBytes1.SequenceEqual(buff)) 174 | { 175 | buff = ReadBytes(4); 176 | if (FeatureBytes2.SequenceEqual(buff)) 177 | { 178 | Position -= 16; 179 | var subaddr = DecodeAdr(i + 8, ReadBytes(4)); 180 | var rsubaddr = MapVATR(subaddr); 181 | Position = rsubaddr; 182 | codeRegistration = DecodeAdrp(subaddr, ReadBytes(4)); 183 | codeRegistration += DecodeAdd(ReadBytes(4)); 184 | Position = rsubaddr + 8; 185 | metadataRegistration = DecodeAdrp(subaddr + 8, ReadBytes(4)); 186 | metadataRegistration += DecodeAdd(ReadBytes(4)); 187 | } 188 | } 189 | } 190 | } 191 | } 192 | if (Version >= 24) 193 | { 194 | /* ADRP X0, unk 195 | * ADD X0, X0, unk 196 | * ADR X1, sub 197 | * NOP 198 | * MOV W3, #0 199 | * MOV X2, #0 200 | * B sub 201 | */ 202 | var __mod_init_func = sections.First(x => x.sectname == "__mod_init_func"); 203 | var addrs = ReadClassArray(__mod_init_func.offset, __mod_init_func.size / 8); 204 | foreach (var i in addrs) 205 | { 206 | if (i > 0) 207 | { 208 | Position = MapVATR(i) + 16; 209 | var buff = ReadBytes(4); 210 | if (FeatureBytes2.SequenceEqual(buff)) 211 | { 212 | buff = ReadBytes(4); 213 | if (FeatureBytes1.SequenceEqual(buff)) 214 | { 215 | Position -= 16; 216 | var subaddr = DecodeAdr(i + 8, ReadBytes(4)); 217 | var rsubaddr = MapVATR(subaddr); 218 | Position = rsubaddr; 219 | codeRegistration = DecodeAdrp(subaddr, ReadBytes(4)); 220 | codeRegistration += DecodeAdd(ReadBytes(4)); 221 | Position = rsubaddr + 8; 222 | metadataRegistration = DecodeAdrp(subaddr + 8, ReadBytes(4)); 223 | metadataRegistration += DecodeAdd(ReadBytes(4)); 224 | } 225 | } 226 | } 227 | } 228 | } 229 | if (codeRegistration != 0 && metadataRegistration != 0) 230 | { 231 | Console.WriteLine("CodeRegistration : {0:x}", codeRegistration); 232 | Console.WriteLine("MetadataRegistration : {0:x}", metadataRegistration); 233 | Init(codeRegistration, metadataRegistration); 234 | return true; 235 | } 236 | return false; 237 | } 238 | 239 | public override bool PlusSearch(int methodCount, int typeDefinitionsCount, int imageCount) 240 | { 241 | var sectionHelper = GetSectionHelper(methodCount, typeDefinitionsCount, imageCount); 242 | var codeRegistration = sectionHelper.FindCodeRegistration(); 243 | var metadataRegistration = sectionHelper.FindMetadataRegistration(); 244 | return AutoPlusInit(codeRegistration, metadataRegistration); 245 | } 246 | 247 | public override bool SymbolSearch() 248 | { 249 | return false; 250 | } 251 | 252 | public override ulong GetRVA(ulong pointer) 253 | { 254 | return pointer - vmaddr; 255 | } 256 | 257 | public override SectionHelper GetSectionHelper(int methodCount, int typeDefinitionsCount, int imageCount) 258 | { 259 | var data = sections.Where(x => x.sectname == "__const" || x.sectname == "__cstring" || x.sectname == "__data").ToArray(); 260 | var code = sections.Where(x => x.flags == 0x80000400).ToArray(); 261 | var bss = sections.Where(x => x.flags == 1u).ToArray(); 262 | var sectionHelper = new SectionHelper(this, methodCount, typeDefinitionsCount, metadataUsagesCount, imageCount); 263 | sectionHelper.SetSection(SearchSectionType.Exec, code); 264 | sectionHelper.SetSection(SearchSectionType.Data, data); 265 | sectionHelper.SetSection(SearchSectionType.Bss, bss); 266 | return sectionHelper; 267 | } 268 | 269 | public override bool CheckDump() => false; 270 | 271 | public override ulong ReadUIntPtr() 272 | { 273 | var pointer = ReadUInt64(); 274 | if (pointer > vmaddr + 0xFFFFFFFF) 275 | { 276 | var addr = Position; 277 | var section = sections.First(x => addr >= x.offset && addr <= x.offset + x.size); 278 | if (section.sectname == "__const" || section.sectname == "__data") 279 | { 280 | var rva = pointer - vmaddr; 281 | rva &= 0xFFFFFFFF; 282 | pointer = rva + vmaddr; 283 | } 284 | } 285 | return pointer; 286 | } 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/Elf64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using static Il2CppDumper.ElfConstants; 6 | 7 | namespace Il2CppDumper 8 | { 9 | public sealed class Elf64 : ElfBase 10 | { 11 | private Elf64_Ehdr elfHeader; 12 | private Elf64_Phdr[] programSegment; 13 | private Elf64_Dyn[] dynamicSection; 14 | private Elf64_Sym[] symbolTable; 15 | private Elf64_Shdr[] sectionTable; 16 | private Elf64_Phdr pt_dynamic; 17 | 18 | public Elf64(Stream stream) : base(stream) 19 | { 20 | Load(); 21 | } 22 | 23 | protected override void Load() 24 | { 25 | elfHeader = ReadClass(0); 26 | programSegment = ReadClassArray(elfHeader.e_phoff, elfHeader.e_phnum); 27 | if (IsDumped) 28 | { 29 | FixedProgramSegment(); 30 | } 31 | pt_dynamic = programSegment.First(x => x.p_type == PT_DYNAMIC); 32 | dynamicSection = ReadClassArray(pt_dynamic.p_offset, pt_dynamic.p_filesz / 16L); 33 | if (IsDumped) 34 | { 35 | FixedDynamicSection(); 36 | } 37 | ReadSymbol(); 38 | if (!IsDumped) 39 | { 40 | RelocationProcessing(); 41 | if (CheckProtection()) 42 | { 43 | Console.WriteLine("ERROR: This file may be protected."); 44 | } 45 | } 46 | } 47 | 48 | protected override bool CheckSection() 49 | { 50 | try 51 | { 52 | var names = new List(); 53 | sectionTable = ReadClassArray(elfHeader.e_shoff, elfHeader.e_shnum); 54 | var shstrndx = sectionTable[elfHeader.e_shstrndx].sh_offset; 55 | foreach (var section in sectionTable) 56 | { 57 | names.Add(ReadStringToNull(shstrndx + section.sh_name)); 58 | } 59 | if (!names.Contains(".text")) 60 | { 61 | return false; 62 | } 63 | return true; 64 | } 65 | catch 66 | { 67 | return false; 68 | } 69 | } 70 | 71 | public override ulong MapVATR(ulong addr) 72 | { 73 | var phdr = programSegment.First(x => addr >= x.p_vaddr && addr <= x.p_vaddr + x.p_memsz); 74 | return addr - phdr.p_vaddr + phdr.p_offset; 75 | } 76 | 77 | public override ulong MapRTVA(ulong addr) 78 | { 79 | var phdr = programSegment.FirstOrDefault(x => addr >= x.p_offset && addr <= x.p_offset + x.p_filesz); 80 | if (phdr == null) 81 | { 82 | return 0; 83 | } 84 | return addr - phdr.p_offset + phdr.p_vaddr; 85 | } 86 | 87 | public override bool Search() 88 | { 89 | return false; 90 | } 91 | 92 | public override bool PlusSearch(int methodCount, int typeDefinitionsCount, int imageCount) 93 | { 94 | var sectionHelper = GetSectionHelper(methodCount, typeDefinitionsCount, imageCount); 95 | var codeRegistration = sectionHelper.FindCodeRegistration(); 96 | var metadataRegistration = sectionHelper.FindMetadataRegistration(); 97 | return AutoPlusInit(codeRegistration, metadataRegistration); 98 | } 99 | 100 | public override bool SymbolSearch() 101 | { 102 | ulong codeRegistration = 0ul; 103 | ulong metadataRegistration = 0ul; 104 | ulong dynstrOffset = MapVATR(dynamicSection.First(x => x.d_tag == DT_STRTAB).d_un); 105 | foreach (var symbol in symbolTable) 106 | { 107 | var name = ReadStringToNull(dynstrOffset + symbol.st_name); 108 | switch (name) 109 | { 110 | case "g_CodeRegistration": 111 | codeRegistration = symbol.st_value; 112 | break; 113 | case "g_MetadataRegistration": 114 | metadataRegistration = symbol.st_value; 115 | break; 116 | } 117 | } 118 | if (codeRegistration > 0 && metadataRegistration > 0) 119 | { 120 | Console.WriteLine("Detected Symbol !"); 121 | Console.WriteLine("CodeRegistration : {0:x}", codeRegistration); 122 | Console.WriteLine("MetadataRegistration : {0:x}", metadataRegistration); 123 | Init(codeRegistration, metadataRegistration); 124 | return true; 125 | } 126 | Console.WriteLine("ERROR: No symbol is detected"); 127 | return false; 128 | } 129 | 130 | private void ReadSymbol() 131 | { 132 | try 133 | { 134 | var symbolCount = 0u; 135 | var hash = dynamicSection.FirstOrDefault(x => x.d_tag == DT_HASH); 136 | if (hash != null) 137 | { 138 | var addr = MapVATR(hash.d_un); 139 | Position = addr; 140 | var nbucket = ReadUInt32(); 141 | var nchain = ReadUInt32(); 142 | symbolCount = nchain; 143 | } 144 | else 145 | { 146 | hash = dynamicSection.First(x => x.d_tag == DT_GNU_HASH); 147 | var addr = MapVATR(hash.d_un); 148 | Position = addr; 149 | var nbuckets = ReadUInt32(); 150 | var symoffset = ReadUInt32(); 151 | var bloom_size = ReadUInt32(); 152 | var bloom_shift = ReadUInt32(); 153 | var buckets_address = addr + 16 + (8 * bloom_size); 154 | var buckets = ReadClassArray(buckets_address, nbuckets); 155 | var last_symbol = buckets.Max(); 156 | if (last_symbol < symoffset) 157 | { 158 | symbolCount = symoffset; 159 | } 160 | else 161 | { 162 | var chains_base_address = buckets_address + 4 * nbuckets; 163 | Position = chains_base_address + (last_symbol - symoffset) * 4; 164 | while (true) 165 | { 166 | var chain_entry = ReadUInt32(); 167 | ++last_symbol; 168 | if ((chain_entry & 1) != 0) 169 | break; 170 | } 171 | symbolCount = last_symbol; 172 | } 173 | } 174 | var dynsymOffset = MapVATR(dynamicSection.First(x => x.d_tag == DT_SYMTAB).d_un); 175 | symbolTable = ReadClassArray(dynsymOffset, symbolCount); 176 | } 177 | catch 178 | { 179 | // ignored 180 | } 181 | } 182 | 183 | private void RelocationProcessing() 184 | { 185 | Console.WriteLine("Applying relocations..."); 186 | try 187 | { 188 | var relaOffset = MapVATR(dynamicSection.First(x => x.d_tag == DT_RELA).d_un); 189 | var relaSize = dynamicSection.First(x => x.d_tag == DT_RELASZ).d_un; 190 | var relaTable = ReadClassArray(relaOffset, relaSize / 24L); 191 | foreach (var rela in relaTable) 192 | { 193 | var type = rela.r_info & 0xffffffff; 194 | var sym = rela.r_info >> 32; 195 | (ulong value, bool recognized) result = (type, elfHeader.e_machine) switch 196 | { 197 | (R_AARCH64_ABS64, EM_AARCH64) => (symbolTable[sym].st_value + rela.r_addend, true), 198 | (R_AARCH64_RELATIVE, EM_AARCH64) => (rela.r_addend, true), 199 | 200 | (R_X86_64_64, EM_X86_64) => (symbolTable[sym].st_value + rela.r_addend, true), 201 | (R_X86_64_RELATIVE, EM_X86_64) => (rela.r_addend, true), 202 | 203 | _ => (0, false) 204 | }; 205 | if (result.recognized) 206 | { 207 | Position = MapVATR(rela.r_offset); 208 | Write(result.value); 209 | } 210 | } 211 | } 212 | catch 213 | { 214 | // ignored 215 | } 216 | } 217 | 218 | private bool CheckProtection() 219 | { 220 | try 221 | { 222 | //.init_proc 223 | if (dynamicSection.Any(x => x.d_tag == DT_INIT)) 224 | { 225 | Console.WriteLine("WARNING: find .init_proc"); 226 | return true; 227 | } 228 | //JNI_OnLoad 229 | ulong dynstrOffset = MapVATR(dynamicSection.First(x => x.d_tag == DT_STRTAB).d_un); 230 | foreach (var symbol in symbolTable) 231 | { 232 | var name = ReadStringToNull(dynstrOffset + symbol.st_name); 233 | switch (name) 234 | { 235 | case "JNI_OnLoad": 236 | Console.WriteLine("WARNING: find JNI_OnLoad"); 237 | return true; 238 | } 239 | } 240 | if (sectionTable != null && sectionTable.Any(x => x.sh_type == SHT_LOUSER)) 241 | { 242 | Console.WriteLine("WARNING: find SHT_LOUSER section"); 243 | return true; 244 | } 245 | } 246 | catch 247 | { 248 | // ignored 249 | } 250 | return false; 251 | } 252 | 253 | public override ulong GetRVA(ulong pointer) 254 | { 255 | if (IsDumped) 256 | { 257 | return pointer - ImageBase; 258 | } 259 | return pointer; 260 | } 261 | 262 | private void FixedProgramSegment() 263 | { 264 | for (uint i = 0; i < programSegment.Length; i++) 265 | { 266 | Position = elfHeader.e_phoff + i * 56u + 8u; 267 | var phdr = programSegment[i]; 268 | phdr.p_offset = phdr.p_vaddr; 269 | Write(phdr.p_offset); 270 | phdr.p_vaddr += ImageBase; 271 | Write(phdr.p_vaddr); 272 | Position += 8; 273 | phdr.p_filesz = phdr.p_memsz; 274 | Write(phdr.p_filesz); 275 | } 276 | } 277 | 278 | private void FixedDynamicSection() 279 | { 280 | for (uint i = 0; i < dynamicSection.Length; i++) 281 | { 282 | Position = pt_dynamic.p_offset + i * 16 + 8; 283 | var dyn = dynamicSection[i]; 284 | switch (dyn.d_tag) 285 | { 286 | case DT_PLTGOT: 287 | case DT_HASH: 288 | case DT_STRTAB: 289 | case DT_SYMTAB: 290 | case DT_RELA: 291 | case DT_INIT: 292 | case DT_FINI: 293 | case DT_REL: 294 | case DT_JMPREL: 295 | case DT_INIT_ARRAY: 296 | case DT_FINI_ARRAY: 297 | dyn.d_un += ImageBase; 298 | Write(dyn.d_un); 299 | break; 300 | } 301 | } 302 | } 303 | 304 | public override SectionHelper GetSectionHelper(int methodCount, int typeDefinitionsCount, int imageCount) 305 | { 306 | var dataList = new List(); 307 | var execList = new List(); 308 | foreach (var phdr in programSegment) 309 | { 310 | if (phdr.p_memsz != 0ul) 311 | { 312 | switch (phdr.p_flags) 313 | { 314 | case 1u: //PF_X 315 | case 3u: 316 | case 5u: 317 | case 7u: 318 | execList.Add(phdr); 319 | break; 320 | case 2u: //PF_W && PF_R 321 | case 4u: 322 | case 6u: 323 | dataList.Add(phdr); 324 | break; 325 | } 326 | } 327 | } 328 | var data = dataList.ToArray(); 329 | var exec = execList.ToArray(); 330 | var sectionHelper = new SectionHelper(this, methodCount, typeDefinitionsCount, metadataUsagesCount, imageCount); 331 | sectionHelper.SetSection(SearchSectionType.Exec, exec); 332 | sectionHelper.SetSection(SearchSectionType.Data, data); 333 | sectionHelper.SetSection(SearchSectionType.Bss, data); 334 | return sectionHelper; 335 | } 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /Il2CppDumper/Utils/SectionHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Il2CppDumper 6 | { 7 | public class SectionHelper 8 | { 9 | private List exec; 10 | private List data; 11 | private List bss; 12 | private readonly Il2Cpp il2Cpp; 13 | private readonly int methodCount; 14 | private readonly int typeDefinitionsCount; 15 | private readonly long metadataUsagesCount; 16 | private readonly int imageCount; 17 | private bool pointerInExec; 18 | 19 | public List Exec => exec; 20 | public List Data => data; 21 | public List Bss => bss; 22 | 23 | public SectionHelper(Il2Cpp il2Cpp, int methodCount, int typeDefinitionsCount, long metadataUsagesCount, int imageCount) 24 | { 25 | this.il2Cpp = il2Cpp; 26 | this.methodCount = methodCount; 27 | this.typeDefinitionsCount = typeDefinitionsCount; 28 | this.metadataUsagesCount = metadataUsagesCount; 29 | this.imageCount = imageCount; 30 | } 31 | 32 | public void SetSection(SearchSectionType type, Elf32_Phdr[] sections) 33 | { 34 | var secs = new List(); 35 | foreach (var section in sections) 36 | { 37 | if (section != null) 38 | { 39 | secs.Add(new SearchSection 40 | { 41 | offset = section.p_offset, 42 | offsetEnd = section.p_offset + section.p_filesz, 43 | address = section.p_vaddr, 44 | addressEnd = section.p_vaddr + section.p_memsz 45 | }); 46 | } 47 | } 48 | SetSection(type, secs); 49 | } 50 | 51 | public void SetSection(SearchSectionType type, Elf64_Phdr[] sections) 52 | { 53 | var secs = new List(); 54 | foreach (var section in sections) 55 | { 56 | if (section != null) 57 | { 58 | secs.Add(new SearchSection 59 | { 60 | offset = section.p_offset, 61 | offsetEnd = section.p_offset + section.p_filesz, 62 | address = section.p_vaddr, 63 | addressEnd = section.p_vaddr + section.p_memsz 64 | }); 65 | } 66 | } 67 | SetSection(type, secs); 68 | } 69 | 70 | public void SetSection(SearchSectionType type, MachoSection[] sections) 71 | { 72 | var secs = new List(); 73 | foreach (var section in sections) 74 | { 75 | if (section != null) 76 | { 77 | secs.Add(new SearchSection 78 | { 79 | offset = section.offset, 80 | offsetEnd = section.offset + section.size, 81 | address = section.addr, 82 | addressEnd = section.addr + section.size 83 | }); 84 | } 85 | } 86 | SetSection(type, secs); 87 | } 88 | 89 | public void SetSection(SearchSectionType type, MachoSection64Bit[] sections) 90 | { 91 | var secs = new List(); 92 | foreach (var section in sections) 93 | { 94 | if (section != null) 95 | { 96 | secs.Add(new SearchSection 97 | { 98 | offset = section.offset, 99 | offsetEnd = section.offset + section.size, 100 | address = section.addr, 101 | addressEnd = section.addr + section.size 102 | }); 103 | } 104 | } 105 | SetSection(type, secs); 106 | } 107 | 108 | public void SetSection(SearchSectionType type, params SearchSection[] secs) 109 | { 110 | SetSection(type, secs.ToList()); 111 | } 112 | 113 | private void SetSection(SearchSectionType type, List secs) 114 | { 115 | switch (type) 116 | { 117 | case SearchSectionType.Exec: 118 | exec = secs; 119 | break; 120 | case SearchSectionType.Data: 121 | data = secs; 122 | break; 123 | case SearchSectionType.Bss: 124 | bss = secs; 125 | break; 126 | } 127 | } 128 | 129 | public ulong FindCodeRegistration() 130 | { 131 | if (il2Cpp.Version >= 24.2) 132 | { 133 | ulong codeRegistration; 134 | if (il2Cpp is ElfBase) 135 | { 136 | codeRegistration = FindCodeRegistrationExec(); 137 | if (codeRegistration == 0) 138 | { 139 | codeRegistration = FindCodeRegistrationData(); 140 | } 141 | else 142 | { 143 | pointerInExec = true; 144 | } 145 | } 146 | else 147 | { 148 | codeRegistration = FindCodeRegistrationData(); 149 | if (codeRegistration == 0) 150 | { 151 | codeRegistration = FindCodeRegistrationExec(); 152 | pointerInExec = true; 153 | } 154 | } 155 | return codeRegistration; 156 | } 157 | return FindCodeRegistrationOld(); 158 | } 159 | 160 | public ulong FindMetadataRegistration() 161 | { 162 | if (il2Cpp.Version < 19) 163 | { 164 | return 0; 165 | } 166 | if (il2Cpp.Version >= 27) 167 | { 168 | return FindMetadataRegistrationV21(); 169 | } 170 | return FindMetadataRegistrationOld(); 171 | } 172 | 173 | private ulong FindCodeRegistrationOld() 174 | { 175 | foreach (var section in data) 176 | { 177 | il2Cpp.Position = section.offset; 178 | while (il2Cpp.Position < section.offsetEnd) 179 | { 180 | var addr = il2Cpp.Position; 181 | if (il2Cpp.ReadIntPtr() == methodCount) 182 | { 183 | try 184 | { 185 | var pointer = il2Cpp.MapVATR(il2Cpp.ReadUIntPtr()); 186 | if (CheckPointerRangeDataRa(pointer)) 187 | { 188 | var pointers = il2Cpp.ReadClassArray(pointer, methodCount); 189 | if (CheckPointerRangeExecVa(pointers)) 190 | { 191 | return addr - section.offset + section.address; 192 | } 193 | } 194 | } 195 | catch 196 | { 197 | // ignored 198 | } 199 | } 200 | il2Cpp.Position = addr + il2Cpp.PointerSize; 201 | } 202 | } 203 | 204 | return 0ul; 205 | } 206 | 207 | private ulong FindMetadataRegistrationOld() 208 | { 209 | foreach (var section in data) 210 | { 211 | il2Cpp.Position = section.offset; 212 | var end = Math.Min(section.offsetEnd, il2Cpp.Length) - il2Cpp.PointerSize; 213 | while (il2Cpp.Position < end) 214 | { 215 | var addr = il2Cpp.Position; 216 | if (il2Cpp.ReadIntPtr() == typeDefinitionsCount) 217 | { 218 | try 219 | { 220 | il2Cpp.Position += il2Cpp.PointerSize * 2; 221 | var pointer = il2Cpp.MapVATR(il2Cpp.ReadUIntPtr()); 222 | if (CheckPointerRangeDataRa(pointer)) 223 | { 224 | var pointers = il2Cpp.ReadClassArray(pointer, metadataUsagesCount); 225 | if (CheckPointerRangeBssVa(pointers)) 226 | { 227 | return addr - il2Cpp.PointerSize * 12 - section.offset + section.address; 228 | } 229 | } 230 | } 231 | catch 232 | { 233 | // ignored 234 | } 235 | } 236 | il2Cpp.Position = addr + il2Cpp.PointerSize; 237 | } 238 | } 239 | 240 | return 0ul; 241 | } 242 | 243 | private ulong FindMetadataRegistrationV21() 244 | { 245 | foreach (var section in data) 246 | { 247 | il2Cpp.Position = section.offset; 248 | var end = Math.Min(section.offsetEnd, il2Cpp.Length) - il2Cpp.PointerSize; 249 | while (il2Cpp.Position < end) 250 | { 251 | var addr = il2Cpp.Position; 252 | if (il2Cpp.ReadIntPtr() == typeDefinitionsCount) 253 | { 254 | il2Cpp.Position += il2Cpp.PointerSize; 255 | if (il2Cpp.ReadIntPtr() == typeDefinitionsCount) 256 | { 257 | try 258 | { 259 | var pointer = il2Cpp.MapVATR(il2Cpp.ReadUIntPtr()); 260 | if (CheckPointerRangeDataRa(pointer)) 261 | { 262 | var pointers = il2Cpp.ReadClassArray(pointer, typeDefinitionsCount); 263 | bool flag; 264 | if (pointerInExec) 265 | { 266 | flag = CheckPointerRangeExecVa(pointers); 267 | } 268 | else 269 | { 270 | flag = CheckPointerRangeDataVa(pointers); 271 | } 272 | if (flag) 273 | { 274 | return addr - il2Cpp.PointerSize * 10 - section.offset + section.address; 275 | } 276 | } 277 | } 278 | catch 279 | { 280 | // ignored 281 | } 282 | } 283 | } 284 | il2Cpp.Position = addr + il2Cpp.PointerSize; 285 | } 286 | } 287 | 288 | return 0ul; 289 | } 290 | 291 | private bool CheckPointerRangeDataRa(ulong pointer) 292 | { 293 | return data.Any(x => pointer >= x.offset && pointer <= x.offsetEnd); 294 | } 295 | 296 | private bool CheckPointerRangeExecVa(ulong[] pointers) 297 | { 298 | return pointers.All(x => exec.Any(y => x >= y.address && x <= y.addressEnd)); 299 | } 300 | 301 | private bool CheckPointerRangeDataVa(ulong[] pointers) 302 | { 303 | return pointers.All(x => data.Any(y => x >= y.address && x <= y.addressEnd)); 304 | } 305 | 306 | private bool CheckPointerRangeBssVa(ulong[] pointers) 307 | { 308 | return pointers.All(x => bss.Any(y => x >= y.address && x <= y.addressEnd)); 309 | } 310 | 311 | private static readonly byte[] featureBytes = { 0x6D, 0x73, 0x63, 0x6F, 0x72, 0x6C, 0x69, 0x62, 0x2E, 0x64, 0x6C, 0x6C, 0x00 }; //mscorlib.dll 312 | 313 | private ulong FindCodeRegistrationData() 314 | { 315 | return FindCodeRegistration2019(data); 316 | } 317 | 318 | private ulong FindCodeRegistrationExec() 319 | { 320 | return FindCodeRegistration2019(exec); 321 | } 322 | 323 | private ulong FindCodeRegistration2019(List secs) 324 | { 325 | foreach (var sec in secs) 326 | { 327 | il2Cpp.Position = sec.offset; 328 | var buff = il2Cpp.ReadBytes((int)(sec.offsetEnd - sec.offset)); 329 | foreach (var index in buff.Search(featureBytes)) 330 | { 331 | var dllva = (ulong)index + sec.address; 332 | foreach (var refva in FindReference(dllva)) 333 | { 334 | foreach (var refva2 in FindReference(refva)) 335 | { 336 | if (il2Cpp.Version >= 27) 337 | { 338 | for (int i = imageCount - 1; i >= 0; i--) 339 | { 340 | foreach (var refva3 in FindReference(refva2 - (ulong)i * il2Cpp.PointerSize)) 341 | { 342 | il2Cpp.Position = il2Cpp.MapVATR(refva3 - il2Cpp.PointerSize); 343 | if (il2Cpp.ReadIntPtr() == imageCount) 344 | { 345 | if (il2Cpp.Version >= 29) 346 | { 347 | return refva3 - il2Cpp.PointerSize * 14; 348 | } 349 | return refva3 - il2Cpp.PointerSize * 13; 350 | } 351 | } 352 | } 353 | } 354 | else 355 | { 356 | for (int i = 0; i < imageCount; i++) 357 | { 358 | foreach (var refva3 in FindReference(refva2 - (ulong)i * il2Cpp.PointerSize)) 359 | { 360 | return refva3 - il2Cpp.PointerSize * 13; 361 | } 362 | } 363 | } 364 | } 365 | } 366 | } 367 | } 368 | return 0ul; 369 | } 370 | 371 | private IEnumerable FindReference(ulong addr) 372 | { 373 | foreach (var dataSec in data) 374 | { 375 | var position = dataSec.offset; 376 | var end = Math.Min(dataSec.offsetEnd, il2Cpp.Length) - il2Cpp.PointerSize; 377 | while (position < end) 378 | { 379 | il2Cpp.Position = position; 380 | if (il2Cpp.ReadUIntPtr() == addr) 381 | { 382 | yield return position - dataSec.offset + dataSec.address; 383 | } 384 | position += il2Cpp.PointerSize; 385 | } 386 | } 387 | } 388 | } 389 | } 390 | --------------------------------------------------------------------------------