├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── config.yml │ ├── problem_report.md │ └── problem_report_cn.md ├── .gitignore ├── Il2CppDumper.sln ├── Il2CppDumper ├── Attributes │ ├── ArrayLengthAttribute.cs │ └── VersionAttribute.cs ├── Config.cs ├── ExecutableFormats │ ├── Elf.cs │ ├── Elf64.cs │ ├── ElfBase.cs │ ├── ElfClass.cs │ ├── Macho.cs │ ├── Macho64.cs │ ├── MachoClass.cs │ ├── MachoFat.cs │ ├── NSO.cs │ ├── NSOClass.cs │ ├── PE.cs │ ├── PEClass.cs │ ├── WebAssembly.cs │ ├── WebAssemblyClass.cs │ └── WebAssemblyMemory.cs ├── Extensions │ ├── BinaryReaderExtensions.cs │ ├── BoyerMooreHorspool.cs │ ├── HexExtensions.cs │ └── StringExtensions.cs ├── IO │ ├── BinaryStream.cs │ └── Lz4DecoderStream.cs ├── Il2Cpp │ ├── Il2Cpp.cs │ ├── Il2CppClass.cs │ ├── Metadata.cs │ └── MetadataClass.cs ├── Il2CppBinaryNinja │ ├── __init__.py │ └── plugin.json ├── Il2CppDumper.csproj ├── Libraries │ └── Il2CppDummyDll.dll ├── Outputs │ ├── DummyAssemblyExporter.cs │ ├── HeaderConstants.cs │ ├── Il2CppConstants.cs │ ├── Il2CppDecompiler.cs │ ├── ScriptJson.cs │ ├── StructGenerator.cs │ └── StructInfo.cs ├── Program.cs ├── Resource1.Designer.cs ├── Resource1.resx ├── Utils │ ├── ArmUtils.cs │ ├── AttributeArgument.cs │ ├── BlobValue.cs │ ├── CustomAttributeDataReader.cs │ ├── CustomAttributeReaderVisitor.cs │ ├── DummyAssemblyGenerator.cs │ ├── FileDialogNative.cs │ ├── Il2CppExecutor.cs │ ├── MyAssemblyResolver.cs │ ├── OpenFileDialog.cs │ ├── PELoader.cs │ ├── SearchSection.cs │ └── SectionHelper.cs ├── config.json ├── ghidra.py ├── ghidra_wasm.py ├── ghidra_with_struct.py ├── hopper-py3.py ├── ida.py ├── ida_py3.py ├── ida_with_struct.py ├── ida_with_struct_py3.py ├── il2cpp_header_to_binja.py └── il2cpp_header_to_ghidra.py ├── LICENSE ├── README.md └── README.zh-CN.md /.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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/problem_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Problem report 3 | about: Problem report 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Note: if you do not provide all of the following information I will directly ignore and close this issue 11 | 12 | - Il2CppDumper version 13 | 14 | - Target Unity version (optional) 15 | 16 | - Describe the issue 17 | 18 | - Upload executable file and global-metadata.dat 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/problem_report_cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 问题报告 3 | about: 中文问题报告 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 请注意:如果你没有提供以下所有信息我将会直接无视并关闭这个issue 11 | 12 | - Il2CppDumper版本 13 | 14 | - 目标Unity版本 (可以不填) 15 | 16 | - 问题描述 17 | 18 | - 上传可执行文件和global-metadata.dat 19 | -------------------------------------------------------------------------------- /.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.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/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/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/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/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/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/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/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/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/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/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/ExecutableFormats/NSOClass.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class NSOHeader 4 | { 5 | public uint Magic; 6 | public uint Version; 7 | public uint Reserved; 8 | public uint Flags; 9 | public NSOSegmentHeader TextSegment; 10 | public uint ModuleOffset; 11 | public NSOSegmentHeader RoDataSegment; 12 | public uint ModuleFileSize; 13 | public NSOSegmentHeader DataSegment; 14 | public uint BssSize; 15 | public byte[] DigestBuildID; 16 | public uint TextCompressedSize; 17 | public uint RoDataCompressedSize; 18 | public uint DataCompressedSize; 19 | public byte[] Padding; 20 | public NSORelativeExtent APIInfo; 21 | public NSORelativeExtent DynStr; 22 | public NSORelativeExtent DynSym; 23 | public byte[] TextHash; 24 | public byte[] RoDataHash; 25 | public byte[] DataHash; 26 | 27 | public NSOSegmentHeader BssSegment; 28 | } 29 | 30 | public class NSOSegmentHeader 31 | { 32 | public uint FileOffset; 33 | public uint MemoryOffset; 34 | public uint DecompressedSize; 35 | } 36 | 37 | public class NSORelativeExtent 38 | { 39 | public uint RegionRoDataOffset; 40 | public uint RegionSize; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/PE.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace Il2CppDumper 7 | { 8 | public sealed class PE : Il2Cpp 9 | { 10 | private readonly SectionHeader[] sections; 11 | 12 | public PE(Stream stream) : base(stream) 13 | { 14 | var dosHeader = ReadClass(); 15 | if (dosHeader.Magic != 0x5A4D) 16 | { 17 | throw new InvalidDataException("ERROR: Invalid PE file"); 18 | } 19 | Position = dosHeader.Lfanew; 20 | if (ReadUInt32() != 0x4550u) //Signature 21 | { 22 | throw new InvalidDataException("ERROR: Invalid PE file"); 23 | } 24 | var fileHeader = ReadClass(); 25 | var pos = Position; 26 | var magic = ReadUInt16(); 27 | Position -= 2; 28 | if (magic == 0x10b) 29 | { 30 | Is32Bit = true; 31 | var optionalHeader = ReadClass(); 32 | ImageBase = optionalHeader.ImageBase; 33 | } 34 | else if (magic == 0x20b) 35 | { 36 | var optionalHeader = ReadClass(); 37 | ImageBase = optionalHeader.ImageBase; 38 | } 39 | else 40 | { 41 | throw new NotSupportedException($"Invalid Optional header magic {magic}"); 42 | } 43 | Position = pos + fileHeader.SizeOfOptionalHeader; 44 | sections = ReadClassArray(fileHeader.NumberOfSections); 45 | } 46 | 47 | public void LoadFromMemory(ulong addr) 48 | { 49 | ImageBase = addr; 50 | foreach (var section in sections) 51 | { 52 | section.PointerToRawData = section.VirtualAddress; 53 | section.SizeOfRawData = section.VirtualSize; 54 | } 55 | } 56 | 57 | public override ulong MapVATR(ulong absAddr) 58 | { 59 | var addr = absAddr - ImageBase; 60 | var section = sections.FirstOrDefault(x => addr >= x.VirtualAddress && addr <= x.VirtualAddress + x.VirtualSize); 61 | if (section == null) 62 | { 63 | return 0ul; 64 | } 65 | return addr - section.VirtualAddress + section.PointerToRawData; 66 | } 67 | 68 | public override ulong MapRTVA(ulong addr) 69 | { 70 | var section = sections.FirstOrDefault(x => addr >= x.PointerToRawData && addr <= x.PointerToRawData + x.SizeOfRawData); 71 | if (section == null) 72 | { 73 | return 0ul; 74 | } 75 | return addr - section.PointerToRawData + section.VirtualAddress + ImageBase; 76 | } 77 | 78 | public override bool Search() 79 | { 80 | return false; 81 | } 82 | 83 | public override bool PlusSearch(int methodCount, int typeDefinitionsCount, int imageCount) 84 | { 85 | var sectionHelper = GetSectionHelper(methodCount, typeDefinitionsCount, imageCount); 86 | var codeRegistration = sectionHelper.FindCodeRegistration(); 87 | var metadataRegistration = sectionHelper.FindMetadataRegistration(); 88 | return AutoPlusInit(codeRegistration, metadataRegistration); 89 | } 90 | 91 | public override bool SymbolSearch() 92 | { 93 | return false; 94 | } 95 | 96 | public override ulong GetRVA(ulong pointer) 97 | { 98 | return pointer - ImageBase; 99 | } 100 | 101 | public override SectionHelper GetSectionHelper(int methodCount, int typeDefinitionsCount, int imageCount) 102 | { 103 | var execList = new List(); 104 | var dataList = new List(); 105 | foreach (var section in sections) 106 | { 107 | switch (section.Characteristics) 108 | { 109 | case 0x60000020: 110 | execList.Add(section); 111 | break; 112 | case 0x40000040: 113 | case 0xC0000040: 114 | dataList.Add(section); 115 | break; 116 | } 117 | } 118 | var sectionHelper = new SectionHelper(this, methodCount, typeDefinitionsCount, metadataUsagesCount, imageCount); 119 | var data = dataList.ToArray(); 120 | var exec = execList.ToArray(); 121 | sectionHelper.SetSection(SearchSectionType.Exec, ImageBase, exec); 122 | sectionHelper.SetSection(SearchSectionType.Data, ImageBase, data); 123 | sectionHelper.SetSection(SearchSectionType.Bss, ImageBase, data); 124 | return sectionHelper; 125 | } 126 | 127 | public override bool CheckDump() 128 | { 129 | if (Is32Bit) 130 | { 131 | return ImageBase != 0x10000000; 132 | } 133 | else 134 | { 135 | return ImageBase != 0x180000000; 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/PEClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public class DosHeader 6 | { 7 | public ushort Magic; 8 | public ushort Cblp; 9 | public ushort Cp; 10 | public ushort Crlc; 11 | public ushort Cparhdr; 12 | public ushort Minalloc; 13 | public ushort Maxalloc; 14 | public ushort Ss; 15 | public ushort Sp; 16 | public ushort Csum; 17 | public ushort Ip; 18 | public ushort Cs; 19 | public ushort Lfarlc; 20 | public ushort Ovno; 21 | [ArrayLength(Length = 4)] 22 | public ushort[] Res; 23 | public ushort Oemid; 24 | public ushort Oeminfo; 25 | [ArrayLength(Length = 10)] 26 | public ushort[] Res2; 27 | public uint Lfanew; 28 | } 29 | 30 | public class FileHeader 31 | { 32 | public ushort Machine; 33 | public ushort NumberOfSections; 34 | public uint TimeDateStamp; 35 | public uint PointerToSymbolTable; 36 | public uint NumberOfSymbols; 37 | public ushort SizeOfOptionalHeader; 38 | public ushort Characteristics; 39 | } 40 | 41 | public class OptionalHeader 42 | { 43 | public ushort Magic; 44 | public byte MajorLinkerVersion; 45 | public byte MinorLinkerVersion; 46 | public uint SizeOfCode; 47 | public uint SizeOfInitializedData; 48 | public uint SizeOfUninitializedData; 49 | public uint AddressOfEntryPoint; 50 | public uint BaseOfCode; 51 | public uint BaseOfData; 52 | public uint ImageBase; 53 | public uint SectionAlignment; 54 | public uint FileAlignment; 55 | public ushort MajorOperatingSystemVersion; 56 | public ushort MinorOperatingSystemVersion; 57 | public ushort MajorImageVersion; 58 | public ushort MinorImageVersion; 59 | public ushort MajorSubsystemVersion; 60 | public ushort MinorSubsystemVersion; 61 | public uint Win32VersionValue; 62 | public uint SizeOfImage; 63 | public uint SizeOfHeaders; 64 | public uint CheckSum; 65 | public ushort Subsystem; 66 | public ushort DllCharacteristics; 67 | public uint SizeOfStackReserve; 68 | public uint SizeOfStackCommit; 69 | public uint SizeOfHeapReserve; 70 | public uint SizeOfHeapCommit; 71 | public uint LoaderFlags; 72 | public uint NumberOfRvaAndSizes; 73 | //public DataDirectory[] DataDirectory; 74 | } 75 | 76 | public class OptionalHeader64 77 | { 78 | public ushort Magic; 79 | public byte MajorLinkerVersion; 80 | public byte MinorLinkerVersion; 81 | public uint SizeOfCode; 82 | public uint SizeOfInitializedData; 83 | public uint SizeOfUninitializedData; 84 | public uint AddressOfEntryPoint; 85 | public uint BaseOfCode; 86 | public ulong ImageBase; 87 | public uint SectionAlignment; 88 | public uint FileAlignment; 89 | public ushort MajorOperatingSystemVersion; 90 | public ushort MinorOperatingSystemVersion; 91 | public ushort MajorImageVersion; 92 | public ushort MinorImageVersion; 93 | public ushort MajorSubsystemVersion; 94 | public ushort MinorSubsystemVersion; 95 | public uint Win32VersionValue; 96 | public uint SizeOfImage; 97 | public uint SizeOfHeaders; 98 | public uint CheckSum; 99 | public ushort Subsystem; 100 | public ushort DllCharacteristics; 101 | public ulong SizeOfStackReserve; 102 | public ulong SizeOfStackCommit; 103 | public ulong SizeOfHeapReserve; 104 | public ulong SizeOfHeapCommit; 105 | public uint LoaderFlags; 106 | public uint NumberOfRvaAndSizes; 107 | //public DataDirectory[] DataDirectory; 108 | } 109 | 110 | /*public class DataDirectory 111 | { 112 | public uint VirtualAddress; 113 | public uint Size; 114 | }*/ 115 | 116 | public class SectionHeader 117 | { 118 | [ArrayLength(Length = 8)] 119 | public byte[] Name; 120 | public uint VirtualSize; 121 | public uint VirtualAddress; 122 | public uint SizeOfRawData; 123 | public uint PointerToRawData; 124 | public uint PointerToRelocations; 125 | public uint PointerToLinenumbers; 126 | public ushort NumberOfRelocations; 127 | public ushort NumberOfLinenumbers; 128 | public uint Characteristics; 129 | } 130 | 131 | [Flags] 132 | public enum SectionCharacteristics : uint 133 | { 134 | IMAGE_SCN_MEM_EXECUTE = 0x20000000, 135 | IMAGE_SCN_MEM_READ = 0x40000000, 136 | IMAGE_SCN_MEM_WRITE = 0x80000000 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/WebAssembly.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Il2CppDumper 5 | { 6 | public sealed class WebAssembly : BinaryStream 7 | { 8 | private readonly DataSection[] dataSections; 9 | 10 | public WebAssembly(Stream stream) : base(stream) 11 | { 12 | Is32Bit = true; 13 | var magic = ReadUInt32(); 14 | var version = ReadInt32(); 15 | while (Position < Length) 16 | { 17 | var id = ReadULeb128(); 18 | var len = ReadULeb128(); 19 | if (id == 11) 20 | { 21 | var count = ReadULeb128(); 22 | dataSections = new DataSection[count]; 23 | for (int i = 0; i < count; i++) 24 | { 25 | var dataSection = new DataSection(); 26 | dataSections[i] = dataSection; 27 | dataSection.Index = ReadULeb128(); 28 | var opCode = ReadByte(); 29 | if (opCode != 0x41) //i32.const 30 | { 31 | throw new InvalidOperationException(); 32 | } 33 | dataSection.Offset = ReadULeb128(); 34 | opCode = ReadByte(); 35 | if (opCode != 0xB) //end 36 | { 37 | throw new InvalidOperationException(); 38 | } 39 | dataSection.Data = ReadBytes((int)ReadULeb128()); 40 | } 41 | break; 42 | } 43 | Position += len; 44 | } 45 | } 46 | 47 | public WebAssemblyMemory CreateMemory() 48 | { 49 | var last = dataSections[^1]; 50 | var bssStart = last.Offset + (uint)last.Data.Length; 51 | var stream = new MemoryStream(new byte[Length]); 52 | foreach (var dataSection in dataSections) 53 | { 54 | stream.Position = dataSection.Offset; 55 | stream.Write(dataSection.Data, 0, dataSection.Data.Length); 56 | } 57 | return new WebAssemblyMemory(stream, bssStart); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/WebAssemblyClass.cs: -------------------------------------------------------------------------------- 1 | namespace Il2CppDumper 2 | { 3 | public class DataSection 4 | { 5 | public uint Index; 6 | public uint Offset; 7 | public byte[] Data; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Il2CppDumper/ExecutableFormats/WebAssemblyMemory.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Il2CppDumper 4 | { 5 | public sealed class WebAssemblyMemory : Il2Cpp 6 | { 7 | private readonly uint bssStart; 8 | 9 | public WebAssemblyMemory(Stream stream, uint bssStart) : base(stream) 10 | { 11 | Is32Bit = true; 12 | this.bssStart = bssStart; 13 | } 14 | 15 | public override ulong MapVATR(ulong addr) 16 | { 17 | return addr; 18 | } 19 | 20 | public override ulong MapRTVA(ulong addr) 21 | { 22 | return addr; 23 | } 24 | 25 | public override bool PlusSearch(int methodCount, int typeDefinitionsCount, int imageCount) 26 | { 27 | var sectionHelper = GetSectionHelper(methodCount, typeDefinitionsCount, imageCount); 28 | var codeRegistration = sectionHelper.FindCodeRegistration(); 29 | var metadataRegistration = sectionHelper.FindMetadataRegistration(); 30 | return AutoPlusInit(codeRegistration, metadataRegistration); 31 | } 32 | 33 | public override bool Search() 34 | { 35 | return false; 36 | } 37 | 38 | public override bool SymbolSearch() 39 | { 40 | return false; 41 | } 42 | 43 | public override SectionHelper GetSectionHelper(int methodCount, int typeDefinitionsCount, int imageCount) 44 | { 45 | var exec = new SearchSection 46 | { 47 | offset = 0, 48 | offsetEnd = (ulong)methodCount, //hack 49 | address = 0, 50 | addressEnd = (ulong)methodCount //hack 51 | }; 52 | var data = new SearchSection 53 | { 54 | offset = 1024, 55 | offsetEnd = Length, 56 | address = 1024, 57 | addressEnd = Length 58 | }; 59 | var bss = new SearchSection 60 | { 61 | offset = bssStart, 62 | offsetEnd = long.MaxValue, //hack 63 | address = bssStart, 64 | addressEnd = long.MaxValue //hack 65 | }; 66 | var sectionHelper = new SectionHelper(this, methodCount, typeDefinitionsCount, metadataUsagesCount, imageCount); 67 | sectionHelper.SetSection(SearchSectionType.Exec, exec); 68 | sectionHelper.SetSection(SearchSectionType.Data, data); 69 | sectionHelper.SetSection(SearchSectionType.Bss, bss); 70 | return sectionHelper; 71 | } 72 | 73 | public override bool CheckDump() => false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | if (version < 0 || version > 1000) 52 | { 53 | throw new InvalidDataException("ERROR: Metadata file supplied is not valid metadata file."); 54 | } 55 | if (version < 16 || version > 31) 56 | { 57 | throw new NotSupportedException($"ERROR: Metadata file supplied is not a supported version[{version}]."); 58 | } 59 | Version = version; 60 | header = ReadClass(0); 61 | if (version == 24) 62 | { 63 | if (header.stringLiteralOffset == 264) 64 | { 65 | Version = 24.2; 66 | header = ReadClass(0); 67 | } 68 | else 69 | { 70 | imageDefs = ReadMetadataClassArray(header.imagesOffset, header.imagesSize); 71 | if (imageDefs.Any(x => x.token != 1)) 72 | { 73 | Version = 24.1; 74 | } 75 | } 76 | } 77 | imageDefs = ReadMetadataClassArray(header.imagesOffset, header.imagesSize); 78 | if (Version == 24.2 && header.assembliesSize / 68 < imageDefs.Length) 79 | { 80 | Version = 24.4; 81 | } 82 | var v241Plus = false; 83 | if (Version == 24.1 && header.assembliesSize / 64 == imageDefs.Length) 84 | { 85 | v241Plus = true; 86 | } 87 | if (v241Plus) 88 | { 89 | Version = 24.4; 90 | } 91 | assemblyDefs = ReadMetadataClassArray(header.assembliesOffset, header.assembliesSize); 92 | if (v241Plus) 93 | { 94 | Version = 24.1; 95 | } 96 | typeDefs = ReadMetadataClassArray(header.typeDefinitionsOffset, header.typeDefinitionsSize); 97 | methodDefs = ReadMetadataClassArray(header.methodsOffset, header.methodsSize); 98 | parameterDefs = ReadMetadataClassArray(header.parametersOffset, header.parametersSize); 99 | fieldDefs = ReadMetadataClassArray(header.fieldsOffset, header.fieldsSize); 100 | var fieldDefaultValues = ReadMetadataClassArray(header.fieldDefaultValuesOffset, header.fieldDefaultValuesSize); 101 | var parameterDefaultValues = ReadMetadataClassArray(header.parameterDefaultValuesOffset, header.parameterDefaultValuesSize); 102 | fieldDefaultValuesDic = fieldDefaultValues.ToDictionary(x => x.fieldIndex); 103 | parameterDefaultValuesDic = parameterDefaultValues.ToDictionary(x => x.parameterIndex); 104 | propertyDefs = ReadMetadataClassArray(header.propertiesOffset, header.propertiesSize); 105 | interfaceIndices = ReadClassArray(header.interfacesOffset, header.interfacesSize / 4); 106 | nestedTypeIndices = ReadClassArray(header.nestedTypesOffset, header.nestedTypesSize / 4); 107 | eventDefs = ReadMetadataClassArray(header.eventsOffset, header.eventsSize); 108 | genericContainers = ReadMetadataClassArray(header.genericContainersOffset, header.genericContainersSize); 109 | genericParameters = ReadMetadataClassArray(header.genericParametersOffset, header.genericParametersSize); 110 | constraintIndices = ReadClassArray(header.genericParameterConstraintsOffset, header.genericParameterConstraintsSize / 4); 111 | vtableMethods = ReadClassArray(header.vtableMethodsOffset, header.vtableMethodsSize / 4); 112 | stringLiterals = ReadMetadataClassArray(header.stringLiteralOffset, header.stringLiteralSize); 113 | if (Version > 16) 114 | { 115 | fieldRefs = ReadMetadataClassArray(header.fieldRefsOffset, header.fieldRefsSize); 116 | if (Version < 27) 117 | { 118 | metadataUsageLists = ReadMetadataClassArray(header.metadataUsageListsOffset, header.metadataUsageListsCount); 119 | metadataUsagePairs = ReadMetadataClassArray(header.metadataUsagePairsOffset, header.metadataUsagePairsCount); 120 | 121 | ProcessingMetadataUsage(); 122 | } 123 | } 124 | if (Version > 20 && Version < 29) 125 | { 126 | attributeTypeRanges = ReadMetadataClassArray(header.attributesInfoOffset, header.attributesInfoCount); 127 | attributeTypes = ReadClassArray(header.attributeTypesOffset, header.attributeTypesCount / 4); 128 | } 129 | if (Version >= 29) 130 | { 131 | attributeDataRanges = ReadMetadataClassArray(header.attributeDataRangeOffset, header.attributeDataRangeSize); 132 | } 133 | if (Version > 24) 134 | { 135 | attributeTypeRangesDic = new Dictionary>(); 136 | foreach (var imageDef in imageDefs) 137 | { 138 | var dic = new Dictionary(); 139 | attributeTypeRangesDic[imageDef] = dic; 140 | var end = imageDef.customAttributeStart + imageDef.customAttributeCount; 141 | for (int i = imageDef.customAttributeStart; i < end; i++) 142 | { 143 | if (Version >= 29) 144 | { 145 | dic.Add(attributeDataRanges[i].token, i); 146 | } 147 | else 148 | { 149 | dic.Add(attributeTypeRanges[i].token, i); 150 | } 151 | } 152 | } 153 | } 154 | if (Version <= 24.1) 155 | { 156 | rgctxEntries = ReadMetadataClassArray(header.rgctxEntriesOffset, header.rgctxEntriesCount); 157 | } 158 | } 159 | 160 | private T[] ReadMetadataClassArray(uint addr, int count) where T : new() 161 | { 162 | return ReadClassArray(addr, count / SizeOf(typeof(T))); 163 | } 164 | 165 | public bool GetFieldDefaultValueFromIndex(int index, out Il2CppFieldDefaultValue value) 166 | { 167 | return fieldDefaultValuesDic.TryGetValue(index, out value); 168 | } 169 | 170 | public bool GetParameterDefaultValueFromIndex(int index, out Il2CppParameterDefaultValue value) 171 | { 172 | return parameterDefaultValuesDic.TryGetValue(index, out value); 173 | } 174 | 175 | public uint GetDefaultValueFromIndex(int index) 176 | { 177 | return (uint)(header.fieldAndParameterDefaultValueDataOffset + index); 178 | } 179 | 180 | public string GetStringFromIndex(uint index) 181 | { 182 | if (!stringCache.TryGetValue(index, out var result)) 183 | { 184 | result = ReadStringToNull(header.stringOffset + index); 185 | stringCache.Add(index, result); 186 | } 187 | return result; 188 | } 189 | 190 | public int GetCustomAttributeIndex(Il2CppImageDefinition imageDef, int customAttributeIndex, uint token) 191 | { 192 | if (Version > 24) 193 | { 194 | if (attributeTypeRangesDic[imageDef].TryGetValue(token, out var index)) 195 | { 196 | return index; 197 | } 198 | else 199 | { 200 | return -1; 201 | } 202 | } 203 | else 204 | { 205 | return customAttributeIndex; 206 | } 207 | } 208 | 209 | public string GetStringLiteralFromIndex(uint index) 210 | { 211 | var stringLiteral = stringLiterals[index]; 212 | Position = (uint)(header.stringLiteralDataOffset + stringLiteral.dataIndex); 213 | return Encoding.UTF8.GetString(ReadBytes((int)stringLiteral.length)); 214 | } 215 | 216 | private void ProcessingMetadataUsage() 217 | { 218 | metadataUsageDic = new Dictionary>(); 219 | for (uint i = 1; i <= 6; i++) 220 | { 221 | metadataUsageDic[(Il2CppMetadataUsage)i] = new SortedDictionary(); 222 | } 223 | foreach (var metadataUsageList in metadataUsageLists) 224 | { 225 | for (int i = 0; i < metadataUsageList.count; i++) 226 | { 227 | var offset = metadataUsageList.start + i; 228 | if (offset >= metadataUsagePairs.Length) 229 | { 230 | continue; 231 | } 232 | var metadataUsagePair = metadataUsagePairs[offset]; 233 | var usage = GetEncodedIndexType(metadataUsagePair.encodedSourceIndex); 234 | var decodedIndex = GetDecodedMethodIndex(metadataUsagePair.encodedSourceIndex); 235 | metadataUsageDic[(Il2CppMetadataUsage)usage][metadataUsagePair.destinationIndex] = decodedIndex; 236 | } 237 | } 238 | //metadataUsagesCount = metadataUsagePairs.Max(x => x.destinationIndex) + 1; 239 | metadataUsagesCount = metadataUsageDic.Max(x => x.Value.Select(y => y.Key).DefaultIfEmpty().Max()) + 1; 240 | } 241 | 242 | public static uint GetEncodedIndexType(uint index) 243 | { 244 | return (index & 0xE0000000) >> 29; 245 | } 246 | 247 | public uint GetDecodedMethodIndex(uint index) 248 | { 249 | if (Version >= 27) 250 | { 251 | return (index & 0x1FFFFFFEU) >> 1; 252 | } 253 | return index & 0x1FFFFFFFU; 254 | } 255 | 256 | public int SizeOf(Type type) 257 | { 258 | var size = 0; 259 | foreach (var i in type.GetFields()) 260 | { 261 | var attr = (VersionAttribute)Attribute.GetCustomAttribute(i, typeof(VersionAttribute)); 262 | if (attr != null) 263 | { 264 | if (Version < attr.Min || Version > attr.Max) 265 | continue; 266 | } 267 | var fieldType = i.FieldType; 268 | if (fieldType.IsPrimitive) 269 | { 270 | size += GetPrimitiveTypeSize(fieldType.Name); 271 | } 272 | else if (fieldType.IsEnum) 273 | { 274 | var e = fieldType.GetField("value__").FieldType; 275 | size += GetPrimitiveTypeSize(e.Name); 276 | } 277 | else if (fieldType.IsArray) 278 | { 279 | var arrayLengthAttribute = i.GetCustomAttribute(); 280 | size += arrayLengthAttribute.Length; 281 | } 282 | else 283 | { 284 | size += SizeOf(fieldType); 285 | } 286 | } 287 | return size; 288 | 289 | static int GetPrimitiveTypeSize(string name) 290 | { 291 | return name switch 292 | { 293 | "Int32" or "UInt32" => 4, 294 | "Int16" or "UInt16" => 2, 295 | _ => 0, 296 | }; 297 | } 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /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/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/Il2CppDumper.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0;net8.0 6 | 1.0.0.0 7 | 1.0.0.0 8 | 1.0.0.0 9 | Copyright © Perfare 2016-2024 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/Libraries/Il2CppDummyDll.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Perfare/Il2CppDumper/4741d46ba9cd6159c5d853eb9d6fc48b4bfa2b1a/Il2CppDumper/Libraries/Il2CppDummyDll.dll -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/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 0x6D736100: 135 | var web = new WebAssembly(il2CppMemory); 136 | il2Cpp = web.CreateMemory(); 137 | break; 138 | case 0x304F534E: 139 | var nso = new NSO(il2CppMemory); 140 | il2Cpp = nso.UnCompress(); 141 | break; 142 | case 0x905A4D: //PE 143 | il2Cpp = new PE(il2CppMemory); 144 | break; 145 | case 0x464c457f: //ELF 146 | if (il2cppBytes[4] == 2) //ELF64 147 | { 148 | il2Cpp = new Elf64(il2CppMemory); 149 | } 150 | else 151 | { 152 | il2Cpp = new Elf(il2CppMemory); 153 | } 154 | break; 155 | case 0xCAFEBABE: //FAT Mach-O 156 | case 0xBEBAFECA: 157 | var machofat = new MachoFat(new MemoryStream(il2cppBytes)); 158 | Console.Write("Select Platform: "); 159 | for (var i = 0; i < machofat.fats.Length; i++) 160 | { 161 | var fat = machofat.fats[i]; 162 | Console.Write(fat.magic == 0xFEEDFACF ? $"{i + 1}.64bit " : $"{i + 1}.32bit "); 163 | } 164 | Console.WriteLine(); 165 | var key = Console.ReadKey(true); 166 | var index = int.Parse(key.KeyChar.ToString()) - 1; 167 | var magic = machofat.fats[index % 2].magic; 168 | il2cppBytes = machofat.GetMacho(index % 2); 169 | il2CppMemory = new MemoryStream(il2cppBytes); 170 | if (magic == 0xFEEDFACF) 171 | goto case 0xFEEDFACF; 172 | else 173 | goto case 0xFEEDFACE; 174 | case 0xFEEDFACF: // 64bit Mach-O 175 | il2Cpp = new Macho64(il2CppMemory); 176 | break; 177 | case 0xFEEDFACE: // 32bit Mach-O 178 | il2Cpp = new Macho(il2CppMemory); 179 | break; 180 | } 181 | var version = config.ForceIl2CppVersion ? config.ForceVersion : metadata.Version; 182 | il2Cpp.SetProperties(version, metadata.metadataUsagesCount); 183 | Console.WriteLine($"Il2Cpp Version: {il2Cpp.Version}"); 184 | if (config.ForceDump || il2Cpp.CheckDump()) 185 | { 186 | if (il2Cpp is ElfBase elf) 187 | { 188 | Console.WriteLine("Detected this may be a dump file."); 189 | Console.WriteLine("Input il2cpp dump address or input 0 to force continue:"); 190 | var DumpAddr = Convert.ToUInt64(Console.ReadLine(), 16); 191 | if (DumpAddr != 0) 192 | { 193 | il2Cpp.ImageBase = DumpAddr; 194 | il2Cpp.IsDumped = true; 195 | if (!config.NoRedirectedPointer) 196 | { 197 | elf.Reload(); 198 | } 199 | } 200 | } 201 | else 202 | { 203 | il2Cpp.IsDumped = true; 204 | } 205 | } 206 | 207 | Console.WriteLine("Searching..."); 208 | try 209 | { 210 | var flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); 211 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 212 | { 213 | if (!flag && il2Cpp is PE) 214 | { 215 | Console.WriteLine("Use custom PE loader"); 216 | il2Cpp = PELoader.Load(il2cppPath); 217 | il2Cpp.SetProperties(version, metadata.metadataUsagesCount); 218 | flag = il2Cpp.PlusSearch(metadata.methodDefs.Count(x => x.methodIndex >= 0), metadata.typeDefs.Length, metadata.imageDefs.Length); 219 | } 220 | } 221 | if (!flag) 222 | { 223 | flag = il2Cpp.Search(); 224 | } 225 | if (!flag) 226 | { 227 | flag = il2Cpp.SymbolSearch(); 228 | } 229 | if (!flag) 230 | { 231 | Console.WriteLine("ERROR: Can't use auto mode to process file, try manual mode."); 232 | Console.Write("Input CodeRegistration: "); 233 | var codeRegistration = Convert.ToUInt64(Console.ReadLine(), 16); 234 | Console.Write("Input MetadataRegistration: "); 235 | var metadataRegistration = Convert.ToUInt64(Console.ReadLine(), 16); 236 | il2Cpp.Init(codeRegistration, metadataRegistration); 237 | } 238 | if (il2Cpp.Version >= 27 && il2Cpp.IsDumped) 239 | { 240 | var typeDef = metadata.typeDefs[0]; 241 | var il2CppType = il2Cpp.types[typeDef.byvalTypeIndex]; 242 | metadata.ImageBase = il2CppType.data.typeHandle - metadata.header.typeDefinitionsOffset; 243 | } 244 | } 245 | catch (Exception e) 246 | { 247 | Console.WriteLine(e); 248 | Console.WriteLine("ERROR: An error occurred while processing."); 249 | return false; 250 | } 251 | return true; 252 | } 253 | 254 | private static void Dump(Metadata metadata, Il2Cpp il2Cpp, string outputDir) 255 | { 256 | Console.WriteLine("Dumping..."); 257 | var executor = new Il2CppExecutor(metadata, il2Cpp); 258 | var decompiler = new Il2CppDecompiler(executor); 259 | decompiler.Decompile(config, outputDir); 260 | Console.WriteLine("Done!"); 261 | if (config.GenerateStruct) 262 | { 263 | Console.WriteLine("Generate struct..."); 264 | var scriptGenerator = new StructGenerator(executor); 265 | scriptGenerator.WriteScript(outputDir); 266 | Console.WriteLine("Done!"); 267 | } 268 | if (config.GenerateDummyDll) 269 | { 270 | Console.WriteLine("Generate dummy dll..."); 271 | DummyAssemblyExporter.Export(executor, outputDir, config.DummyDllAddToken); 272 | Console.WriteLine("Done!"); 273 | } 274 | } 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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/Utils/PELoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace Il2CppDumper 8 | { 9 | public class PELoader 10 | { 11 | [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 12 | private extern static IntPtr LoadLibrary(string path); 13 | 14 | public static PE Load(string fileName) 15 | { 16 | var buff = File.ReadAllBytes(fileName); 17 | using var reader = new BinaryStream(new MemoryStream(buff)); 18 | var dosHeader = reader.ReadClass(); 19 | if (dosHeader.Magic != 0x5A4D) 20 | { 21 | throw new InvalidDataException("ERROR: Invalid PE file"); 22 | } 23 | reader.Position = dosHeader.Lfanew; 24 | if (reader.ReadUInt32() != 0x4550u) //Signature 25 | { 26 | throw new InvalidDataException("ERROR: Invalid PE file"); 27 | } 28 | var fileHeader = reader.ReadClass(); 29 | if (fileHeader.Machine == 0x14c && Environment.Is64BitProcess) //64bit process can't load 32bit dll 30 | { 31 | throw new InvalidOperationException("The file is a 32-bit file, please try to load it with Il2CppDumper-x86.exe"); 32 | } 33 | if (fileHeader.Machine == 0x8664 && !Environment.Is64BitProcess) //32bit process can't load 64bit dll 34 | { 35 | throw new InvalidOperationException("The file is a 64-bit file, please try to load it with Il2CppDumper.exe"); 36 | } 37 | var pos = reader.Position; 38 | reader.Position = pos + fileHeader.SizeOfOptionalHeader; 39 | var sections = reader.ReadClassArray(fileHeader.NumberOfSections); 40 | var last = sections[^1]; 41 | var size = last.VirtualAddress + last.VirtualSize; 42 | var peBuff = new byte[size]; 43 | var handle = LoadLibrary(fileName); 44 | if (handle == IntPtr.Zero) 45 | { 46 | throw new Win32Exception(Marshal.GetLastWin32Error()); 47 | } 48 | foreach (var section in sections) 49 | { 50 | switch (section.Characteristics) 51 | { 52 | case 0x60000020: 53 | case 0x40000040: 54 | case 0xC0000040: 55 | Marshal.Copy(new IntPtr(handle.ToInt64() + section.VirtualAddress), peBuff, (int)section.VirtualAddress, (int)section.VirtualSize); 56 | break; 57 | } 58 | } 59 | var peMemory = new MemoryStream(peBuff); 60 | var writer = new BinaryWriter(peMemory, Encoding.UTF8, true); 61 | var headerSize = reader.Position; 62 | reader.Position = 0; 63 | var buff2 = reader.ReadBytes((int)headerSize); 64 | writer.Write(buff2); 65 | writer.Flush(); 66 | writer.Close(); 67 | peMemory.Position = 0; 68 | var pe = new PE(peMemory); 69 | pe.LoadFromMemory((ulong)handle.ToInt64()); 70 | return pe; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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_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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Il2CppDumper 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/anhqw33vcpmp8ofa?svg=true)](https://ci.appveyor.com/project/Perfare/il2cppdumper/branch/master/artifacts) 4 | 5 | 中文说明请戳[这里](README.zh-CN.md) 6 | 7 | Unity il2cpp reverse engineer 8 | 9 | ## Features 10 | 11 | * Complete DLL restore (except code), can be used to extract `MonoBehaviour` and `MonoScript` 12 | * Supports ELF, ELF64, Mach-O, PE, NSO and WASM format 13 | * Supports Unity 5.3 - 2022.2 14 | * Supports generate IDA, Ghidra and Binary Ninja scripts to help them better analyze il2cpp files 15 | * Supports generate structures header file 16 | * Supports Android memory dumped `libil2cpp.so` file to bypass protection 17 | * Support bypassing simple PE protection 18 | 19 | ## Usage 20 | 21 | Run `Il2CppDumper.exe` and choose the il2cpp executable file and `global-metadata.dat` file, then enter the information as prompted 22 | 23 | The program will then generate all the output files in current working directory 24 | 25 | ### Command-line 26 | 27 | ``` 28 | Il2CppDumper.exe 29 | ``` 30 | 31 | ### Outputs 32 | 33 | #### DummyDll 34 | 35 | Folder, containing all restored dll files 36 | 37 | Use [dnSpy](https://github.com/0xd4d/dnSpy), [ILSpy](https://github.com/icsharpcode/ILSpy) or other .Net decompiler tools to view 38 | 39 | Can be used to extract Unity `MonoBehaviour` and `MonoScript`, for [UtinyRipper](https://github.com/mafaca/UtinyRipper), [UABE](https://7daystodie.com/forums/showthread.php?22675-Unity-Assets-Bundle-Extractor) 40 | 41 | #### ida.py 42 | 43 | For IDA 44 | 45 | #### ida_with_struct.py 46 | 47 | For IDA, read il2cpp.h file and apply structure information in IDA 48 | 49 | #### il2cpp.h 50 | 51 | structure information header file 52 | 53 | #### ghidra.py 54 | 55 | For Ghidra 56 | 57 | #### Il2CppBinaryNinja 58 | 59 | For BinaryNinja 60 | 61 | #### ghidra_wasm.py 62 | 63 | For Ghidra, work with [ghidra-wasm-plugin](https://github.com/nneonneo/ghidra-wasm-plugin) 64 | 65 | #### script.json 66 | 67 | For ida.py, ghidra.py and Il2CppBinaryNinja 68 | 69 | #### stringliteral.json 70 | 71 | Contains all stringLiteral information 72 | 73 | ### Configuration 74 | 75 | All the configuration options are located in `config.json` 76 | 77 | Available options: 78 | 79 | * `DumpMethod`, `DumpField`, `DumpProperty`, `DumpAttribute`, `DumpFieldOffset`, `DumpMethodOffset`, `DumpTypeDefIndex` 80 | * Whether to output these information to dump.cs 81 | 82 | * `GenerateDummyDll`, `GenerateScript` 83 | * Whether to generate these things 84 | 85 | * `DummyDllAddToken` 86 | * Whether to add token in DummyDll 87 | 88 | * `RequireAnyKey` 89 | * Whether to press any key to exit at the end 90 | 91 | * `ForceIl2CppVersion`, `ForceVersion` 92 | * If `ForceIl2CppVersion` is `true`, the program will use the version number specified in `ForceVersion` to choose parser for il2cpp binaries (does not affect the choice of metadata parser). This may be useful on some older il2cpp version (e.g. the program may need to use v16 parser on il2cpp v20 (Android) binaries in order to work properly) 93 | 94 | * `ForceDump` 95 | * Force files to be treated as dumped 96 | 97 | * `NoRedirectedPointer` 98 | * Treat pointers in dumped files as unredirected, This option needs to be `true` for files dumped from some devices 99 | 100 | ## Common errors 101 | 102 | #### `ERROR: Metadata file supplied is not valid metadata file.` 103 | 104 | Make sure you choose the correct file. Sometimes games may obfuscate this file for content protection purposes and so on. Deobfuscating of such files is beyond the scope of this program, so please **DO NOT** file an issue regarding to deobfuscating. 105 | 106 | If your file is `libil2cpp.so` and you have a rooted Android phone, you can try my other project [Zygisk-Il2CppDumper](https://github.com/Perfare/Zygisk-Il2CppDumper), it can bypass this protection. 107 | 108 | #### `ERROR: Can't use auto mode to process file, try manual mode.` 109 | 110 | Please note that the executable file for the PC platform is `GameAssembly.dll` or `*Assembly.dll` 111 | 112 | You can open a new issue and upload the file, I will try to solve. 113 | 114 | #### `ERROR: This file may be protected.` 115 | 116 | Il2CppDumper detected that the executable file has been protected, use `GameGuardian` to dump `libil2cpp.so` from the game memory, then use Il2CppDumper to load and follow the prompts, can bypass most protections. 117 | 118 | If you have a rooted Android phone, you can try my other project [Zygisk-Il2CppDumper](https://github.com/Perfare/Zygisk-Il2CppDumper), it can bypass almost all protections. 119 | 120 | ## Credits 121 | 122 | - Jumboperson - [Il2CppDumper](https://github.com/Jumboperson/Il2CppDumper) 123 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Il2CppDumper 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/anhqw33vcpmp8ofa?svg=true)](https://ci.appveyor.com/project/Perfare/il2cppdumper/branch/master/artifacts) 4 | 5 | Unity il2cpp逆向工程 6 | 7 | ## 功能 8 | 9 | * 还原DLL文件(不包含代码),可用于提取`MonoBehaviour`和`MonoScript` 10 | * 支持ELF, ELF64, Mach-O, PE, NSO和WASM格式 11 | * 支持Unity 5.3 - 2022.2 12 | * 生成IDA和Ghidra的脚本,帮助IDA和Ghidra更好的分析il2cpp文件 13 | * 生成结构体头文件 14 | * 支持从内存dump的`libil2cpp.so`文件以绕过保护 15 | * 支持绕过简单的PE保护 16 | 17 | ## 使用说明 18 | 19 | 直接运行Il2CppDumper.exe并依次选择il2cpp的可执行文件和global-metadata.dat文件,然后根据提示输入相应信息。 20 | 21 | 程序运行完成后将在当前运行目录下生成输出文件 22 | 23 | ### 命令行 24 | 25 | ``` 26 | Il2CppDumper.exe 27 | ``` 28 | 29 | ### 输出文件 30 | 31 | #### DummyDll 32 | 33 | 文件夹,包含所有还原的DLL文件 34 | 35 | 使用[dnSpy](https://github.com/0xd4d/dnSpy),[ILSpy](https://github.com/icsharpcode/ILSpy)或者其他.Net反编译工具即可查看具体信息 36 | 37 | 可用于提取Unity的`MonoBehaviour`和`MonoScript`,适用于[UtinyRipper](https://github.com/mafaca/UtinyRipper)或者[UABE](https://7daystodie.com/forums/showthread.php?22675-Unity-Assets-Bundle-Extractor)等 38 | 39 | #### ida.py 40 | 41 | 用于IDA 42 | 43 | #### ida_with_struct.py 44 | 45 | 用于IDA, 读取il2cpp.h文件并在IDA中应用结构信息 46 | 47 | #### il2cpp.h 48 | 49 | 包含结构体的头文件 50 | 51 | #### ghidra.py 52 | 53 | 用于Ghidra 54 | 55 | #### Il2CppBinaryNinja 56 | 57 | 用于BinaryNinja 58 | 59 | #### ghidra_wasm.py 60 | 61 | 用于Ghidra, 和[ghidra-wasm-plugin](https://github.com/nneonneo/ghidra-wasm-plugin)一起工作 62 | 63 | #### script.json 64 | 65 | 用于IDA和Ghidra脚本 66 | 67 | #### stringliteral.json 68 | 69 | 包含所有stringLiteral信息 70 | 71 | ### 关于config.json 72 | 73 | * `DumpMethod`,`DumpField`,`DumpProperty`,`DumpAttribute`,`DumpFieldOffset`, `DumpMethodOffset`, `DumpTypeDefIndex` 74 | * 是否在dump.cs输出相应的内容 75 | 76 | * `GenerateDummyDll`,`GenerateScript` 77 | * 是否生成这些内容 78 | 79 | * `DummyDllAddToken` 80 | * 是否在DummyDll中添加token 81 | 82 | * `RequireAnyKey` 83 | * 在程序结束时是否需要按键退出 84 | 85 | * `ForceIl2CppVersion`,`ForceVersion` 86 | * 当ForceIl2CppVersion为`true`时,程序将根据ForceVersion指定的版本读取il2cpp的可执行文件(Metadata仍然使用header里的版本),在部分低版本的il2cpp中可能会用到(比如安卓20版本下,你可能需要设置ForceVersion为16程序才能正常工作) 87 | 88 | * `ForceDump` 89 | * 强制将文件视为dump文件 90 | 91 | * `NoRedirectedPointer` 92 | * 将dump文件中的指针视为未重定向的, 从某些设备dump出的文件需要设置该项为`true` 93 | 94 | ## 常见问题 95 | 96 | #### `ERROR: Metadata file supplied is not valid metadata file.` 97 | 98 | global-metadata.dat已被加密。关于解密的问题请去相关破解论坛寻求帮助,请不要在issues提问! 99 | 100 | 如果你的文件是`libil2cpp.so`并且你拥有一台已root的安卓手机,你可以尝试我的另一个项目[Zygisk-Il2CppDumper](https://github.com/Perfare/Zygisk-Il2CppDumper),它能够无视global-metadata.dat加密 101 | 102 | #### `ERROR: Can't use auto mode to process file, try manual mode.` 103 | 104 | 请注意PC平台的可执行文件是`GameAssembly.dll`或者`*Assembly.dll` 105 | 106 | 你可以打开一个新的issue,并上传文件,我会尝试解决 107 | 108 | #### `ERROR: This file may be protected.` 109 | 110 | Il2CppDumper检测到可执行文件已被保护,使用`GameGuardian`从游戏内存中dump `libil2cpp.so`,然后使用Il2CppDumper载入按提示操作,可绕过大部分保护 111 | 112 | 如果你拥有一台已root的安卓手机,你可以尝试我的另一个项目[Zygisk-Il2CppDumper](https://github.com/Perfare/Zygisk-Il2CppDumper),它能够绕过几乎所有保护 113 | 114 | ## 感谢 115 | 116 | - Jumboperson - [Il2CppDumper](https://github.com/Jumboperson/Il2CppDumper) 117 | --------------------------------------------------------------------------------