25 Commits

Author SHA1 Message Date
LukeFZ
4befde8ab4 increase CodeRegistration validation heuristics thresholds due to some games reaching them 2025-11-26 19:54:05 +01:00
LukeFZ
6d674ecc8c use same metareg scanner for < v27 and > v27, implement TryMapVATR in PEReader for performance 2025-11-11 04:14:31 +01:00
LukeFZ
8b93dda191 Merge branch 'master' of https://github.com/LukeFZ/Il2CppInspectorRedux 2025-10-13 09:17:05 +02:00
LukeFZ
bba8a2913a also unload mssdk64 tils to prevent type name conflicts 2025-10-13 09:16:45 +02:00
Luke
193395db29 Change Bin2Object URL to point to forked repo instead 2025-10-12 17:28:16 +02:00
LukeFZ
481d05668d (hopefully) lower python requirement to 3.8 2025-10-12 17:13:20 +02:00
LukeFZ
ca6c958f9a add pyghidra runtime annotation to fix support 2025-10-12 17:08:19 +02:00
LukeFZ
7a621b40c6 fix support for properties without methods 2025-09-26 00:30:59 +02:00
LukeFZ
1a418280fb update bin2object (again) 2025-09-26 00:28:51 +02:00
LukeFZ
f1a69cafe3 fix support for 35+, add support for 39 2025-09-18 23:36:57 +02:00
LukeFZ
e5f2fa703d update bin2object 2025-09-18 23:35:48 +02:00
LukeFZ
38aa333764 fix workflow pnpm install and caching 2025-09-03 23:11:01 +02:00
LukeFZ
3ae9dba60d improve caching in workflow, remove duplicated .net install steps 2025-09-03 23:04:23 +02:00
LukeFZ
c94bc1c225 update tauri js deps, make pnpm install always run on release build 2025-09-03 23:01:01 +02:00
TrialCarrot
ac361bd90f build: auto run pnpm install if node_modules/ doesn't exists (#24)
Co-authored-by: TrialCarrot <TrialCarrot@users.noreply.github.com>
2025-09-03 22:57:55 +02:00
LukeFZ
a88e91451a add (untested) support for metadata v38 2025-08-22 04:53:00 +02:00
LukeFZ
079373815f update readme to inform about the new UIs 2025-08-15 21:28:58 +02:00
Luke
3439ca912b Implement new GUI and CLI, fix misc. smaller issues (#22)
* Initial commit of new UI c# component

* Initial commit of new UI frontend component

* target WinExe to hide console window in release mode, move ui exe into resources

* force single file publishing and add initial gh workflow for publishing ui

* fix workflow errors

* update dependencies and remove cxxdemangler, as it was outdated

* fix c# single file output due to invalid output path

* smaller tweaks, hack around loops in cpp type layouting

* process other queued exports even if one fails and show error message

* add basic support for processing LC_DYLD_CHAINED_FIXUPS

* ELF loading should not use the file offset for loading the dynamic section

* fix symbol table loading in some modified elfs

* add "start export" button on format selection screen, clear all toasts after selecting an export format

* embed ui executable directly into c# assembly

* only build tauri component in c# release builds

* add il2cpp file (binary, metadata) export to advanced tab

* fix and enable binary ninja fake string segment support

* add support for metadata

* unify logic for getting element type index

* fix new ui not allowing script exports other than ida

* new ui: clear out loaded binary if no IL2CPP images could be loaded

* fix toAddr calls in ghidra script target

* remove dependency on a section being named .text in loaded pe files

* tweak symbol reading a bit and remove sht relocation reading

* add initial support for required forward references in il2cpp types, also fix issues with type names clashing with il2cpp api types

* reduce clang errors for header file, fix better array size struct, emit required forward definitions in header

* expose forward definitions in AppModel, fix issue with method-only used types not being emitted

* remove debug log line

* fix spelling mistakes in gui outputs

* fix il2cpp_array_size_t not being an actual type for later method definitions

* change the default port for new ui dev to 5000

* show current version and hash in new ui footer

* seperate redux ui impl into FrontendCore project

* make inspector version a server api, split up output subtypes and tweak some option names

* add redux CLI based on redux GUI output formats

* replace all Console.WriteLine calls in core inspector with AnsiConsole calls

* add workflow for new cli and add back old gui workflow

* disable aot publish and enable single file for redux cli
2025-08-15 21:13:32 +02:00
TrialCarrot
e161e0f226 Fix #14: Error while dotnet publish -c Release (#21)
Co-authored-by: TrialCarrot <TrialCarrot@users.noreply.github.com>
2025-07-18 16:32:20 +02:00
Luke
ec76447122 Fix misc. issues in the generated disassembler scripts (#17)
* attempt to fix ghidra script issues

* add ghidra python 3 note to readme

* fix binary ninja script erroring on function type parsing

* fix ida script not skipping function creation on pe binaries

* fix writing of multibyte strings breaking fake string segment

* also adjust binja write_string impl
2025-01-25 14:21:56 +01:00
LukeFZ
4e46c29cee Merge branch 'master' of https://github.com/LukeFZ/Il2CppInspectorRedux 2024-12-13 20:58:57 +01:00
LukeFZ
507c42024e Fix loading of 32-Bit binaries by removing shadowed Bits property 2024-12-13 20:58:41 +01:00
Luke
8403f8a62d Fix incorrect if condition in GetVTable causing them to all be empty 2024-11-19 00:13:26 +01:00
LukeFZ
5254c4b85a adjust v24.4<->v24.5 condition and update sln file 2024-11-17 23:20:50 +01:00
Luke
b05c03964a Struct reading and disassembly script overhaul, various misc. loading fixes, bump to .NET 9 (#13)
* Bump projects to .net 9 and update nugets

* add VersionedSerialization + source generator

* migrate versioning to StructVersion class, add handling/detection for 29.2/31.2

* add new struct definitions

* rename serialization methods and add BinaryObjectStreamReader for interop

* Rework metadata struct loading to use new struct versioning

* move 29/31.1/.2 to use tags (-2022,-2023) instead of minor versions

* fix metadata usage validity checks

* rework code registration offsetting a bit and add second 29/31.1 condition

* tweak .1 condition (again)

* 29/31.2 was a psyop

* also remove 29.2 from the readme

* remove loading of packed dlls - this was a very unsafe feature

* support auto-recovering type indices from type handles
fixes loading of memory-dumped v29+ libraries since those replacee their class indices on load with a pointer to the corresponding type

* support loading PEs without an export table

* also read UnresolvedVirtualCallCount on regular v31

* Disable plugin loading for now

* Overhaul disassembler script + add Binary Ninja target (#12)

* Overhaul diassembler scripts:
- No longer defines top level functions
- Split into three classes: StatusHandler (like before), DisassemblerInterface (for interfacing with the used program API), ScriptContext (for definiting general functions that use the disassembler interface)
- Add type annotations to all class methods and remove 2.7 compatibility stuff (Ghidra now supports Python 3 so this is unnecessary anymore)
- Disassembler backends are now responsible for launching metadata/script processing, to better support disassembler differences
- String handling is back in the base ScriptContext class, disassembler interfaces opt into the fake string segment creation and fall back to the old method if it isn't supported

* Add Binary Ninja disassembler script backend
This uses the new backend-controlled execution to launch metadata processing on a background thread to keep the ui responsive

* make binary ninja script use own _BINARYNINJA_ define and add define helpers to header

* Update README to account for new script and binary ninja backend

* implement fake string segment functions for binary ninja but don't advertise support

* also cache API function types in binary ninja backend

* fix ida script and disable folders again

* Fix metadata usage issues caused by it being a value type now

* make TryMapVATR overrideable and implement it for ELFs

* Make field offset reading use TryMapVATR to reduce exceptions

* Fix NRE in Assembly ctor on < v24.2

* Update actions workflow to produce cross-platform CLI binaries, update readme to reflect .net 9 changes

* workflow: only restore packages for projects that are being built

* workflow: tweak caching and fix gui compilation

* workflow: remove double .zip in CLI artifact name

* 29/31.2 don't actually exist, this logic is not needed
2024-11-14 14:32:11 +01:00
297 changed files with 23683 additions and 2660 deletions

View File

@@ -1,57 +1,176 @@
name: Il2CppInspectorRedux Build
on:
push:
branches: [ master ]
workflow_dispatch:
on: [push, workflow_dispatch]
jobs:
build:
build-redux-gui: # this already includes stuff only relevant for linux/macos for when the gui is released on those platforms
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- uses: microsoft/setup-msbuild@v1.1
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
package_json_file: Il2CppInspector.Redux.GUI.UI/package.json
- name: Setup Node.JS
uses: actions/setup-node@v4
with:
node-version: lts/*
cache: "pnpm"
cache-dependency-path: Il2CppInspector.Redux.GUI.UI/pnpm-lock.yaml
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: Setup Tauri dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install frontend dependencies
run: pnpm install
working-directory: ./Il2CppInspector.Redux.GUI.UI
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-redux-gui-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-redux-gui-
- name: Restore NuGet packages
run: dotnet restore -r win-x64 ./Il2CppInspector.Redux.GUI
# note: we embed the exe directly into the c# component, and it it is built alongside it
# in another msbuild target.
- name: Build GUI
run: dotnet publish ./Il2CppInspector.Redux.GUI/Il2CppInspector.Redux.GUI.csproj -r win-x64 --no-self-contained
- name: Copy components to output directory
run: |
mkdir ./build_output
cp ./Il2CppInspector.Redux.GUI/bin/Release/net9.0/win-x64/publish/Il2CppInspector.Redux.GUI.exe ./build_output/
- name: Upload GUI Artifact
uses: actions/upload-artifact@v4
with:
name: Il2CppInspectorRedux.GUI
path: build_output
build-redux-cli:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [ '9.0.x' ]
rid: ['win-x64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64']
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup .NET SDK ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-cli-${{ matrix.rid }}-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-cli-${{ matrix.rid }}-
- name: Install dependencies
run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.Redux.CLI
- name: Build & Publish
run: dotnet publish -c Release --no-self-contained --no-restore -o ./${{ matrix.rid }} -r ${{ matrix.rid }} ./Il2CppInspector.Redux.CLI/Il2CppInspector.Redux.CLI.csproj
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Il2CppInspectorRedux.CLI-${{ matrix.rid }}
path: ./${{ matrix.rid }}
build-old-gui:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-gui-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-gui-
- name: Restore NuGet packages
run: dotnet restore -r win-x64 ./Il2CppInspector.GUI
- name: Build GUI
run: dotnet publish ./Il2CppInspector.GUI/Il2CppInspector.GUI.csproj -c Release -r win-x64 --no-self-contained
- name: Upload GUI Artifact
uses: actions/upload-artifact@v4
with:
name: Il2CppInspectorRedux.Old.GUI
path: Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish
build-old-cli:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [ '9.0.x' ]
rid: ['win-x64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64']
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Setup .NET SDK ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}
- uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-cli-${{ matrix.rid }}-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-
${{ runner.os }}-nuget-cli-${{ matrix.rid }}-
- name: Restore NuGet packages
run: nuget restore
- name: Install dependencies
run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.CLI
- name: Build GUI
run: msbuild /t:Il2CppInspector_GUI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net8.0-windows /p:SelfContained=false /verbosity:minimal
- name: Build & Publish
run: dotnet publish -c Release --no-self-contained --no-restore -o ./${{ matrix.rid }} -r ${{ matrix.rid }} ./Il2CppInspector.CLI/Il2CppInspector.CLI.csproj
- name: Build CLI
run: msbuild /t:Il2CppInspector_CLI:publish /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFramework=net8.0 /p:SelfContained=false /verbosity:minimal
- name: Add Plugins folder (GUI)
shell: pwsh
working-directory: Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish
run: ../../../../../../get-plugins.ps1
- name: Add Plugins folder (CLI)
shell: pwsh
working-directory: Il2CppInspector.CLI/bin/Release/net8.0/win-x64/publish
run: ../../../../../../get-plugins.ps1
- name: Upload GUI Artifact
uses: actions/upload-artifact@v3
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Il2CppInspectorRedux.GUI
path: Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish
- name: Upload CLI Artifact
uses: actions/upload-artifact@v2
with:
name: Il2CppInspectorRedux.CLI
path: Il2CppInspector.CLI/bin/Release/net8.0/win-x64/publish
name: Il2CppInspectorRedux.Old.CLI-${{ matrix.rid }}
path: ./${{ matrix.rid }}

3
.gitignore vendored
View File

@@ -268,3 +268,6 @@ paket-files/
/Il2CppInspector.sln.DotSettings
/Il2CppTests/samples
/Il2CppTests/TestCpp
# for dotnet-tools.json
.config

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "Bin2Object"]
path = Bin2Object
url = https://github.com/djkaty/Bin2Object
url = https://github.com/LukeFZ/Bin2Object

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<!-- Plugins may require bass class library assemblies we're not using so disable trimming -->
@@ -32,7 +32,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.6.0" />
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -5,6 +5,7 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -342,10 +343,10 @@ namespace Il2CppInspector
// In v21 and later, R0-R2 + PC will be set and they will be the only registers set
// Pre-v21, R0-R1 + PC will be the only registers set
if (image.Version >= 21 && regs.Count == 4 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1) && regs.TryGetValue(2, out uint _))
if (image.Version >= MetadataVersions.V210 && regs.Count == 4 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1) && regs.TryGetValue(2, out uint _))
return (r0 & 0xffff_fffe, r1 & 0xffff_fffe);
if (image.Version < 21 && regs.Count == 3 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1))
if (image.Version < MetadataVersions.V210 && regs.Count == 3 && regs.TryGetValue(0, out r0) && regs.TryGetValue(1, out r1))
return (r0 & 0xffff_fffe, r1 & 0xffff_fffe);
return (0, 0);

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Collections.Generic;
@@ -168,10 +169,10 @@ namespace Il2CppInspector
// Is it Il2CppCodegenRegistration(void)?
// In v21 and later, X0-X2 will be set and they will be the only registers set
// Pre-v21, X0-X1 will be the only registers set
if (image.Version >= 21 && regs.Count == 3 && regs.TryGetValue(0, out ulong x0) && regs.TryGetValue(1, out x1) && regs.TryGetValue(2, out ulong _))
if (image.Version >= MetadataVersions.V210 && regs.Count == 3 && regs.TryGetValue(0, out ulong x0) && regs.TryGetValue(1, out x1) && regs.TryGetValue(2, out ulong _))
return (x0, x1);
if (image.Version < 21 && regs.Count == 2 && regs.TryGetValue(0, out x0) && regs.TryGetValue(1, out x1))
if (image.Version < MetadataVersions.V210 && regs.Count == 2 && regs.TryGetValue(0, out x0) && regs.TryGetValue(1, out x1))
return (x0, x1);
return (0, 0);

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -216,7 +217,7 @@ namespace Il2CppInspector
offset = nextLea?.foundOffset + leaSize ?? buff2Size;
}
if ((image.Version < 21 && leas.Count == 2) || (image.Version >= 21 && leas.Count == 3)) {
if ((image.Version < MetadataVersions.V210 && leas.Count == 2) || (image.Version >= MetadataVersions.V210 && leas.Count == 3)) {
// Register-based argument passing?
var leaRSI = leas.FirstOrDefault(l => l.Value == RSI).Key.address;
var leaRDI = leas.FirstOrDefault(l => l.Value == RDI).Key.address;

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Linq;
@@ -34,7 +35,7 @@ namespace Il2CppInspector
return (0, 0);
// Jump to Il2CppCodegenRegistration
if(image.Version < 21) {
if(image.Version < MetadataVersions.V210) {
image.Position = image.MapVATR((ulong)pCgr + 1);
metadata = image.ReadUInt32();
image.Position = image.MapVATR((ulong)pCgr + 6);

View File

@@ -5,19 +5,17 @@
All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Text.RegularExpressions;
using Il2CppInspector.Cpp.UnityHeaders;
using Il2CppInspector.Model;
using Il2CppInspector.Reflection;
namespace Il2CppInspector.Cpp
namespace Il2CppInspector.Cpp;
// Class for generating C header declarations from Reflection objects (TypeInfo, etc.)
public class CppDeclarationGenerator
{
// Class for generating C header declarations from Reflection objects (TypeInfo, etc.)
public class CppDeclarationGenerator
{
private readonly AppModel appModel;
private TypeModel model => appModel.TypeModel;
@@ -33,7 +31,7 @@ namespace Il2CppInspector.Cpp
// Different C++ compilers lay out C++ class structures differently,
// meaning that the compiler must be known in order to generate class type structures
// with the correct layout.
public CppCompilerType InheritanceStyle;
public readonly CppCompilerType InheritanceStyle;
public CppDeclarationGenerator(AppModel appModel) {
this.appModel = appModel;
@@ -46,7 +44,8 @@ namespace Il2CppInspector.Cpp
}
// C type declaration used to name variables of the given C# type
private static Dictionary<string, string> primitiveTypeMap = new Dictionary<string, string> {
private static readonly Dictionary<string, string> primitiveTypeMap = new()
{
["Boolean"] = "bool",
["Byte"] = "uint8_t",
["SByte"] = "int8_t",
@@ -68,24 +67,28 @@ namespace Il2CppInspector.Cpp
if (ti.IsByRef || ti.IsPointer) {
return AsCType(ti.ElementType).AsPointer(WordSize);
}
if (ti.IsValueType) {
if (ti.IsPrimitive && primitiveTypeMap.ContainsKey(ti.Name)) {
return types.GetType(primitiveTypeMap[ti.Name]);
if (ti.IsPrimitive && primitiveTypeMap.TryGetValue(ti.Name, out var value)) {
return types.GetType(value);
}
return types.GetType(TypeNamer.GetName(ti));
}
if (ti.IsEnum) {
return types.GetType(TypeNamer.GetName(ti));
}
return types.GetType(TypeNamer.GetName(ti) + " *");
}
// Resets the cache of visited types and pending types to output, but preserve any names we have already generated
public void Reset() {
VisitedFieldStructs.Clear();
VisitedTypes.Clear();
TodoFieldStructs.Clear();
TodoTypeStructs.Clear();
_visitedFieldStructs.Clear();
_visitedTypes.Clear();
_todoFieldStructs.Clear();
_todoTypeStructs.Clear();
}
#region Field Struct Generation
@@ -96,37 +99,123 @@ namespace Il2CppInspector.Cpp
*/
// A cache of field structures that have already been generated, to eliminate duplicate definitions
private readonly HashSet<TypeInfo> VisitedFieldStructs = new HashSet<TypeInfo>();
private readonly HashSet<TypeInfo> _visitedFieldStructs = [];
// A queue of field structures that need to be generated.
private readonly List<TypeInfo> TodoFieldStructs = new List<TypeInfo>();
private readonly List<TypeInfo> _todoFieldStructs = [];
// Walk over dependencies of the given type, to figure out what field structures it depends on
private void VisitFieldStructs(TypeInfo ti) {
if (VisitedFieldStructs.Contains(ti))
return;
if (ti.IsByRef || ti.ContainsGenericParameters)
return;
VisitedFieldStructs.Add(ti);
private readonly HashSet<TypeInfo> _requiredForwardDefinitionsForFields = [];
if (ti.BaseType != null)
VisitFieldStructs(ti.BaseType);
private readonly HashSet<TypeInfo> _currentVisitedFieldStructs = [];
private readonly HashSet<TypeInfo> _currentTodoFieldStructs = [];
private readonly HashSet<TypeInfo> _currentRequiredForwardDefinitions = [];
private readonly HashSet<TypeInfo> _currentlyVisitingFieldStructs = [];
if (ti.IsArray)
VisitFieldStructs(ti.ElementType);
if (ti.IsEnum)
VisitFieldStructs(ti.GetEnumUnderlyingType());
foreach (var fi in ti.DeclaredFields.Where(fi => !fi.IsStatic && !fi.IsLiteral))
private class CircularReferenceException(TypeInfo circularType, TypeInfo parentType) : Exception("Circular reference detected")
{
if (fi.FieldType.IsEnum || fi.FieldType.IsValueType)
VisitFieldStructs(fi.FieldType);
else if (fi.FieldType.HasElementType)
VisitFieldStructs(fi.FieldType.ElementType);
public TypeInfo CircularReferencedType { get; } = circularType;
public TypeInfo ParentType { get; } = parentType;
}
TodoFieldStructs.Add(ti);
// Walk over dependencies of the given type, to figure out what field structures it depends on
private void VisitFieldStructsInner(TypeInfo ti)
{
if (_visitedFieldStructs.Contains(ti) || _currentVisitedFieldStructs.Contains(ti))
return;
if (ti.IsByRef || ti.ContainsGenericParameters)
return;
_currentVisitedFieldStructs.Add(ti);
_currentlyVisitingFieldStructs.Add(ti);
if (ti.BaseType != null)
VisitFieldStructsInner(ti.BaseType);
if (ti.IsArray)
VisitFieldStructsInner(ti.ElementType);
if (ti.IsEnum)
VisitFieldStructsInner(ti.GetEnumUnderlyingType());
foreach (var fi in ti.DeclaredFields.Where(fi => !fi.IsStatic && !fi.IsLiteral))
ProcessTypeField(fi);
_currentTodoFieldStructs.Add(ti);
_currentlyVisitingFieldStructs.Remove(ti);
return;
void ProcessTypeField(FieldInfo fi)
{
if (fi.FieldType.IsEnum || fi.FieldType.IsValueType)
{
VisitFieldStructsInner(fi.FieldType);
}
else if (fi.FieldType.HasElementType)
{
var elementType = fi.FieldType.ElementType;
if (!fi.FieldType.IsPointer || !_currentRequiredForwardDefinitions.Contains(elementType))
{
VisitFieldStructsInner(elementType);
if (elementType.IsValueType
&& elementType != ti
&& _currentlyVisitingFieldStructs.Contains(elementType)
&& !_currentRequiredForwardDefinitions.Contains(elementType))
{
// this is now an issue: there is a loop, and we need to resolve it
// if the field type is a pointer, we can make a forward declaration and be done with it
// otherwise, we cannot generate these types
if (!fi.FieldType.IsPointer)
Debugger.Break();
throw new CircularReferenceException(elementType, ti);
}
}
}
}
}
private void ClearCurrentFieldStructVisitState()
{
_currentTodoFieldStructs.Clear();
_currentVisitedFieldStructs.Clear();
_currentlyVisitingFieldStructs.Clear();
}
private void VisitFieldStructs(TypeInfo ti)
{
ClearCurrentFieldStructVisitState();
var requiredTypesToVisit = new Stack<TypeInfo>([ti]);
while (true)
{
try
{
foreach (var typeToVisit in requiredTypesToVisit)
VisitFieldStructsInner(typeToVisit);
}
catch (CircularReferenceException ex)
{
ClearCurrentFieldStructVisitState();
_currentRequiredForwardDefinitions.Add(ex.CircularReferencedType);
requiredTypesToVisit.Push(ex.ParentType);
continue;
}
break;
}
_todoFieldStructs.AddRange(_currentTodoFieldStructs);
foreach (var visitedType in _currentVisitedFieldStructs)
_visitedFieldStructs.Add(visitedType);
foreach (var requiredType in _currentRequiredForwardDefinitions)
_requiredForwardDefinitionsForFields.Add(requiredType);
}
// Generate the fields for the base class of all objects (Il2CppObject)
@@ -228,6 +317,14 @@ namespace Il2CppInspector.Cpp
ns.ReserveName("_");
fieldType = types.Struct(name + "__Fields");
var baseFieldType = types[TypeNamer.GetName(ti.BaseType) + "__Fields"];
if (baseFieldType == null)
{
// if we end up here, there is a loop in the type generation.
// this is not currently supported, so we throw an exception.
throw new InvalidOperationException($"Failed to generate type for {ti}");
}
fieldType.AddField("_", baseFieldType);
GenerateFieldList(fieldType, ns, ti);
}
@@ -253,8 +350,8 @@ namespace Il2CppInspector.Cpp
// "Flush" the list of visited types, generating C structures for each one
private List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType)> GenerateVisitedFieldStructs() {
var structs = new List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType)>(TodoTypeStructs.Count);
foreach (var ti in TodoFieldStructs) {
var structs = new List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType)>(_todoTypeStructs.Count);
foreach (var ti in _todoFieldStructs) {
if (ti.IsEnum || ti.IsValueType) {
var (valueType, boxedType) = GenerateValueFieldStruct(ti);
structs.Add((ti, valueType, boxedType, null));
@@ -264,7 +361,7 @@ namespace Il2CppInspector.Cpp
structs.Add((ti, null, objectOrArrayType, fieldsType));
}
}
TodoFieldStructs.Clear();
_todoFieldStructs.Clear();
return structs;
}
#endregion
@@ -272,7 +369,7 @@ namespace Il2CppInspector.Cpp
#region Class Struct Generation
// Concrete implementations for abstract classes, for use in looking up VTable signatures and names
private readonly Dictionary<TypeInfo, TypeInfo> ConcreteImplementations = new Dictionary<TypeInfo, TypeInfo>();
private readonly Dictionary<TypeInfo, TypeInfo> _concreteImplementations = new();
/// <summary>
/// VTables for abstract types have "null" in place of abstract functions.
/// This function searches for concrete implementations so that we can properly
@@ -284,8 +381,9 @@ namespace Il2CppInspector.Cpp
continue;
var baseType = ti.BaseType;
while (baseType != null) {
if (baseType.IsAbstract && !ConcreteImplementations.ContainsKey(baseType))
ConcreteImplementations[baseType] = ti;
if (baseType.IsAbstract)
_concreteImplementations.TryAdd(baseType, ti);
baseType = baseType.BaseType;
}
}
@@ -307,37 +405,42 @@ namespace Il2CppInspector.Cpp
* care which concrete implementation we put in this table! The name
* and signature will always match that of the abstract type.
*/
if (ti.IsAbstract && ConcreteImplementations.ContainsKey(ti)) {
if (ti.IsAbstract && _concreteImplementations.TryGetValue(ti, out var implementation)) {
var impl = implementation.GetVTable();
res = (MethodBase[])res.Clone();
MethodBase[] impl = ConcreteImplementations[ti].GetVTable();
for (int i = 0; i < res.Length; i++) {
if (res[i] == null)
res[i] = impl[i];
for (int i = 0; i < res.Length; i++)
{
res[i] ??= impl[i];
}
}
return res;
}
private readonly HashSet<TypeInfo> VisitedTypes = new HashSet<TypeInfo>();
private readonly List<TypeInfo> TodoTypeStructs = new List<TypeInfo>();
private readonly HashSet<TypeInfo> _visitedTypes = [];
private readonly List<TypeInfo> _todoTypeStructs = [];
/// <summary>
/// Include the given type into this generator. This will add the given type and all types it depends on.
/// Call GenerateRemainingTypeDeclarations to produce the actual type declarations afterwards.
/// </summary>
/// <param name="ti"></param>
public void IncludeType(TypeInfo ti) {
if (VisitedTypes.Contains(ti))
public void IncludeType(TypeInfo ti)
{
if (_visitedTypes.Contains(ti))
return;
if (ti.ContainsGenericParameters)
return;
VisitedTypes.Add(ti);
if (ti.IsArray) {
_visitedTypes.Add(ti);
if (ti.IsArray || ti.HasElementType)
{
IncludeType(ti.ElementType);
} else if (ti.HasElementType) {
IncludeType(ti.ElementType);
} else if (ti.IsEnum) {
}
else if (ti.IsEnum)
{
IncludeType(ti.GetEnumUnderlyingType());
}
@@ -354,10 +457,12 @@ namespace Il2CppInspector.Cpp
IncludeType(fi.FieldType);
foreach (var mi in GetFilledVTable(ti))
if (mi != null && !mi.ContainsGenericParameters)
{
if (mi is { ContainsGenericParameters: false })
IncludeMethod(mi);
}
TodoTypeStructs.Add(ti);
_todoTypeStructs.Add(ti);
}
// Generate the C structure for virtual function calls in a given type (the VTable)
@@ -437,17 +542,38 @@ namespace Il2CppInspector.Cpp
/// <returns>A string containing C type declarations</returns>
public List<(TypeInfo ilType, CppComplexType valueType, CppComplexType referenceType, CppComplexType fieldsType,
CppComplexType vtableType, CppComplexType staticsType)> GenerateRemainingTypeDeclarations() {
try
{
var decl = GenerateVisitedFieldStructs().Select(s =>
(s.ilType, s.valueType, s.referenceType, s.fieldsType, (CppComplexType) null, (CppComplexType) null)).ToList();
(s.ilType, s.valueType, s.referenceType, s.fieldsType, (CppComplexType)null,
(CppComplexType)null))
.ToList();
foreach (var ti in TodoTypeStructs) {
foreach (var ti in _todoTypeStructs)
{
var (cls, statics, vtable) = GenerateTypeStruct(ti);
decl.Add((ti, null, cls, null, vtable, statics));
}
TodoTypeStructs.Clear();
return decl;
}
catch (Exception)
{
return null;
}
finally
{
_todoTypeStructs.Clear();
_todoFieldStructs.Clear();
}
}
public List<CppType> GenerateRequiredForwardDefinitions()
=> _requiredForwardDefinitionsForFields
.Select(x => new CppForwardDefinitionType(TypeNamer.GetName(x)))
.Cast<CppType>()
.ToList();
#endregion
#region Method Generation
@@ -613,5 +739,4 @@ namespace Il2CppInspector.Cpp
public CppNamespace GlobalsNamespace { get; private set; }
public CppNamespace.Namer<MethodBase> GlobalNamer { get; private set; }
#endregion
}
}

View File

@@ -4,8 +4,6 @@
All rights reserved.
*/
using System;
namespace Il2CppInspector.Cpp
{
// A field in a C++ type

View File

@@ -5,9 +5,6 @@
All rights reserved.
*/
using System;
using System.Collections.Generic;
namespace Il2CppInspector.Cpp
{
/// <summary>
@@ -65,9 +62,9 @@ namespace Il2CppInspector.Cpp
// Uniquely name an object within the parent namespace
public string GetName(T t) {
// If we've named this particular object before, just return that name
string name;
if (names.TryGetValue(t, out name))
if (names.TryGetValue(t, out var name))
return name;
// Obtain the mangled name for the object
name = keyFunc(t);
// Check if the mangled name has been given to another object - if it has,

View File

@@ -456,4 +456,14 @@ namespace Il2CppInspector.Cpp
return sb.ToString();
}
}
public class CppForwardDefinitionType : CppType
{
public CppForwardDefinitionType(string name) : base(name)
{
}
public override string ToString(string format = "") => $"struct {Name};";
}
}

View File

@@ -4,15 +4,11 @@
All rights reserved.
*/
using System;
using Il2CppInspector.Cpp.UnityHeaders;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Il2CppInspector.Cpp.UnityHeaders;
namespace Il2CppInspector.Cpp
{
@@ -23,7 +19,7 @@ namespace Il2CppInspector.Cpp
public Dictionary<string, CppType> Types { get; }
// All of the literal typedef aliases
public Dictionary<string, CppType> TypedefAliases { get; } = new Dictionary<string, CppType>();
public Dictionary<string, CppType> TypedefAliases { get; } = [];
public CppType this[string s] => Types.ContainsKey(s)? Types[s] :
TypedefAliases.ContainsKey(s)? TypedefAliases[s].AsAlias(s) : null;
@@ -34,7 +30,8 @@ namespace Il2CppInspector.Cpp
// Architecture width in bits (32/64) - to determine pointer sizes
public int WordSize { get; }
private Dictionary<string, ComplexValueType> complexTypeMap = new Dictionary<string, ComplexValueType> {
private Dictionary<string, ComplexValueType> complexTypeMap = new()
{
["struct"] = ComplexValueType.Struct,
["union"] = ComplexValueType.Union,
["enum"] = ComplexValueType.Enum
@@ -44,22 +41,23 @@ namespace Il2CppInspector.Cpp
private string currentGroup = string.Empty;
public void SetGroup(string group) => currentGroup = group;
private static readonly List<CppType> primitiveTypes = new List<CppType> {
new CppType("uint8_t", 8),
new CppType("uint16_t", 16),
new CppType("uint32_t", 32),
new CppType("uint64_t", 64),
new CppType("int8_t", 8),
new CppType("int16_t", 16),
new CppType("int32_t", 32),
new CppType("int64_t", 64),
new CppType("char", 8),
new CppType("int", 32),
new CppType("float", 32),
new CppType("double", 64),
new CppType("bool", 8),
new CppType("void", 0)
};
private static readonly List<CppType> primitiveTypes =
[
new("uint8_t", 8),
new("uint16_t", 16),
new("uint32_t", 32),
new("uint64_t", 64),
new("int8_t", 8),
new("int16_t", 16),
new("int32_t", 32),
new("int64_t", 64),
new("char", 8),
new("int", 32),
new("float", 32),
new("double", 64),
new("bool", 8),
new("void", 0)
];
public CppTypeCollection(int wordSize) {
if (wordSize != 32 && wordSize != 64)
@@ -538,15 +536,18 @@ namespace Il2CppInspector.Cpp
public CppComplexType Struct(string name = "", int alignmentBytes = 0) {
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
return (CppComplexType) cppType;
var type = new CppComplexType(ComplexValueType.Struct) {Name = name, Group = currentGroup, AlignmentBytes = alignmentBytes};
if (!string.IsNullOrEmpty(name))
Add(type);
return type;
}
public CppComplexType Union(string name = "", int alignmentBytes = 0) {
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
return (CppComplexType) cppType;
var type = new CppComplexType(ComplexValueType.Union) {Name = name, Group = currentGroup, AlignmentBytes = alignmentBytes};
if (!string.IsNullOrEmpty(name))
Add(type);
@@ -554,9 +555,13 @@ namespace Il2CppInspector.Cpp
}
public CppEnumType Enum(CppType underlyingType, string name = "") {
if (!string.IsNullOrEmpty(name) && Types.TryGetValue(name, out var cppType))
return (CppEnumType)cppType;
var type = new CppEnumType(underlyingType) {Name = name, Group = currentGroup};
if (!string.IsNullOrEmpty(name))
Add(type);
return type;
}
@@ -585,11 +590,17 @@ namespace Il2CppInspector.Cpp
cppTypes.AddFromDeclarationText(apis);
// Don't allow any of the header type names or primitive type names to be re-used
foreach (var type in cppTypes.Types.Values)
declGen?.TypeNamespace.TryReserveName(type.Name);
foreach (var type in cppTypes.Types.Keys)
{
declGen?.TypeNamespace.TryReserveName(type);
declGen?.GlobalsNamespace.TryReserveName(type);
}
foreach (var typedef in cppTypes.TypedefAliases.Values)
declGen?.GlobalsNamespace.TryReserveName(typedef.Name);
foreach (var typedef in cppTypes.TypedefAliases.Keys)
{
declGen?.TypeNamespace.TryReserveName(typedef);
declGen?.GlobalsNamespace.TryReserveName(typedef);
}
cppTypes.SetGroup("");

View File

@@ -362,6 +362,7 @@ typedef struct Il2CppMethodDefinition
StringIndex nameIndex;
TypeDefinitionIndex declaringType;
TypeIndex returnType;
uint32_t returnParameterIndex;
ParameterIndex parameterStart;
GenericContainerIndex genericContainerIndex;
uint32_t token;
@@ -369,7 +370,6 @@ typedef struct Il2CppMethodDefinition
uint16_t iflags;
uint16_t slot;
uint16_t parameterCount;
uint8_t isUnmanagedCallersOnly;
} Il2CppMethodDefinition;
typedef struct Il2CppEventDefinition
{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -5,12 +5,15 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using Spectre.Console;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using VersionedSerialization;
namespace Il2CppInspector.Cpp.UnityHeaders
{
@@ -19,7 +22,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
public class UnityHeaders : IEquatable<UnityHeaders>
{
// Metadata version for which this group of headers are valid. Multiple headers may have the same metadata version
public double MetadataVersion { get; }
public StructVersion MetadataVersion { get; }
// Range of Unity versions for which this group of headers are valid
public UnityVersionRange VersionRange { get; }
@@ -114,7 +117,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
if (metadataVersion != binary.Image.Version)
continue;
if (metadataVersion == 21) {
if (metadataVersion == MetadataVersions.V210) {
/* Special version logic for metadata version 21 based on the Il2CppMetadataRegistration.fieldOffsets field */
var headerFieldOffsetsArePointers = r.VersionRange.Min.CompareTo("5.3.7") >= 0 && r.VersionRange.Min.CompareTo("5.4.0") != 0;
var binaryFieldOffsetsArePointers = binary.FieldOffsets == null;
@@ -138,7 +141,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
// No il2cpp exports? Just return the earliest version from the header range
// The API version may be incorrect but should be a subset of the real API and won't cause C++ compile errors
if (!exports.Any()) {
Console.WriteLine("No IL2CPP API exports found in binary - IL2CPP APIs will be unavailable in C++ project");
AnsiConsole.WriteLine("No IL2CPP API exports found in binary - IL2CPP APIs will be unavailable in C++ project");
return typeHeaders.Select(t => new UnityHeaders(t,
apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList();
@@ -159,7 +162,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
if (apiMatches.Any()) {
// Intersect all API ranges with all header ranges to produce final list of possible ranges
Console.WriteLine("IL2CPP API discovery was successful");
AnsiConsole.WriteLine("IL2CPP API discovery was successful");
return typeHeaders.SelectMany(
t => apiMatches.Where(a => t.VersionRange.Intersect(a.VersionRange) != null)
@@ -168,7 +171,7 @@ namespace Il2CppInspector.Cpp.UnityHeaders
// None of the possible API versions match the binary
// Select the oldest API version from the group - C++ project compilation will fail
Console.WriteLine("No exact match for IL2CPP APIs found in binary - IL2CPP API availability in C++ project will be partial");
AnsiConsole.WriteLine("No exact match for IL2CPP APIs found in binary - IL2CPP API availability in C++ project will be partial");
return typeHeaders.Select(t => new UnityHeaders(t,
apis.Last(a => a.VersionRange.Intersect(t.VersionRange) != null))).ToList();
@@ -194,8 +197,8 @@ namespace Il2CppInspector.Cpp.UnityHeaders
}
// Get the metadata version from a type header resource name
private static double GetMetadataVersionFromFilename(string resourceName)
=> double.Parse(resourceName.Substring(typeof(UnityHeaders).Namespace.Length + 1).Split('-')[0], NumberFormatInfo.InvariantInfo);
private static StructVersion GetMetadataVersionFromFilename(string resourceName)
=> resourceName[(typeof(UnityHeaders).Namespace!.Length + 1)..].Split('-')[0];
// Equality comparisons
public static bool operator ==(UnityHeaders first, UnityHeaders second) {

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.IO;
@@ -51,7 +52,7 @@ namespace Il2CppInspector
public override IFileFormatStream this[uint index] {
get {
Console.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
AnsiConsole.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
IFileFormatStream loaded = null;
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.IO;
@@ -51,7 +52,7 @@ namespace Il2CppInspector
public override IFileFormatStream this[uint index] {
get {
Console.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
AnsiConsole.WriteLine($"Extracting binary from {binaryFiles[index].FullName}");
IFileFormatStream loaded = null;
// ZipArchiveEntry does not support seeking so we have to close and re-open for each possible load format

View File

@@ -5,6 +5,7 @@
All rights reserved.
*/
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -160,7 +161,7 @@ namespace Il2CppInspector
}
catch (Exception ex)
{
Console.WriteLine($"Got exception {ex} while parsing SHT - reverting to PHT");
AnsiConsole.WriteLine($"Got exception {ex} while parsing SHT - reverting to PHT");
preferPHT = true;
SHT = [];
}
@@ -170,12 +171,12 @@ namespace Il2CppInspector
// These can happen as a result of conversions from other formats to ELF,
// or if the SHT has been deliberately stripped
if (!SHT.Any()) {
Console.WriteLine("ELF binary has no SHT - reverting to PHT");
AnsiConsole.WriteLine("ELF binary has no SHT - reverting to PHT");
preferPHT = true;
}
else if (SHT.All(s => conv.ULong(s.sh_addr) == 0ul)) {
Console.WriteLine("ELF binary SHT is all-zero - reverting to PHT");
AnsiConsole.WriteLine("ELF binary SHT is all-zero - reverting to PHT");
preferPHT = true;
}
@@ -192,7 +193,7 @@ namespace Il2CppInspector
// If the first file offset of the first PHT is zero, assume a dumped image
if (PHT.Any(t => conv.ULong(t.p_vaddr) == 0ul)) {
Console.WriteLine("ELF binary appears to be a dumped memory image");
AnsiConsole.WriteLine("ELF binary appears to be a dumped memory image");
isMemoryImage = true;
}
preferPHT = true;
@@ -202,7 +203,7 @@ namespace Il2CppInspector
else {
var shtOverlap = shtShouldBeOrdered.Aggregate((x, y) => x <= y? y : ulong.MaxValue) == ulong.MaxValue;
if (shtOverlap) {
Console.WriteLine("ELF binary SHT contains invalid ranges - reverting to PHT");
AnsiConsole.WriteLine("ELF binary SHT contains invalid ranges - reverting to PHT");
preferPHT = true;
}
}
@@ -223,7 +224,20 @@ namespace Il2CppInspector
// Get dynamic table if it exists (must be done after rebasing)
if (GetProgramHeader(Elf.PT_DYNAMIC) is TPHdr PT_DYNAMIC)
DynamicTable = ReadArray<elf_dynamic<TWord>>(conv.Long(PT_DYNAMIC.p_offset), (int) (conv.Long(PT_DYNAMIC.p_filesz) / Sizeof(typeof(elf_dynamic<TWord>))));
{
// Important: do not use p_offset here!
// Only load sections should be loaded, which should also include the memory region that contains the dynamic section.
// This just provides the virtual address of the section.
// Some binaries may use the offset here to point to a fake version of the dynamic section,
// making relocation resolution and subsequent analysis fail.
// Reference for Android:
// phdr_table_get_dynamic_section, https://cs.android.com/android/platform/superproject/main/+/main:bionic/linker/linker_phdr.cpp
var dynamicAddr = conv.ULong(PT_DYNAMIC.p_vaddr);
var dynamicSize = (int)(conv.Long(PT_DYNAMIC.p_filesz) / Sizeof(typeof(elf_dynamic<TWord>)));
DynamicTable = ReadMappedArray<elf_dynamic<TWord>>(dynamicAddr, dynamicSize);
}
// Get offset of code section
var codeSegment = PHT.First(x => ((Elf) x.p_flags & Elf.PF_X) == Elf.PF_X);
@@ -254,21 +268,6 @@ namespace Il2CppInspector
StatusUpdate("Finding relocations");
// Two types: add value from offset in image, and add value from specified addend
foreach (var relSection in GetSections(Elf.SHT_REL)) {
reverseMapExclusions.Add(((uint) conv.Int(relSection.sh_offset), (uint) (conv.Int(relSection.sh_offset) + conv.Int(relSection.sh_size) - 1)));
rels.UnionWith(
from rel in ReadArray<elf_rel<TWord>>(conv.Long(relSection.sh_offset), conv.Int(conv.Div(relSection.sh_size, relSection.sh_entsize)))
select new ElfReloc(rel, SHT[relSection.sh_link].sh_offset));
}
foreach (var relaSection in GetSections(Elf.SHT_RELA)) {
reverseMapExclusions.Add(((uint) conv.Int(relaSection.sh_offset), (uint) (conv.Int(relaSection.sh_offset) + conv.Int(relaSection.sh_size) - 1)));
rels.UnionWith(
from rela in ReadArray<elf_rela<TWord>>(conv.Long(relaSection.sh_offset), conv.Int(conv.Div(relaSection.sh_size, relaSection.sh_entsize)))
select new ElfReloc(rela, SHT[relaSection.sh_link].sh_offset));
}
// Relocations in dynamic section
if (GetDynamicEntry(Elf.DT_REL) is elf_dynamic<TWord> dt_rel) {
var dt_rel_count = conv.Int(conv.Div(GetDynamicEntry(Elf.DT_RELSZ).d_un, GetDynamicEntry(Elf.DT_RELENT).d_un));
@@ -291,7 +290,7 @@ namespace Il2CppInspector
}
// Process relocations
var relsz = Sizeof(typeof(TSym));
var relsz = (uint)Sizeof(typeof(TSym));
var currentRel = 0;
var totalRel = rels.Count();
@@ -301,7 +300,26 @@ namespace Il2CppInspector
if (currentRel % 1000 == 0)
StatusUpdate($"Processing relocations ({currentRel * 100 / totalRel:F0}%)");
var symValue = ReadObject<TSym>(conv.Long(rel.SymbolTable) + conv.Long(rel.SymbolIndex) * relsz).st_value; // S
TWord symValue;
try
{
// man this really needs a full overhaul
symValue = ReadMappedObject<TSym>(conv.ULong(rel.SymbolTable) + conv.ULong(rel.SymbolIndex) * relsz)
.st_value; // S
}
catch (InvalidOperationException)
{
try
{
symValue = ReadObject<TSym>(conv.Long(rel.SymbolTable) + conv.Long(rel.SymbolIndex) * relsz)
.st_value; // S
}
catch (InvalidOperationException)
{
continue;
}
}
// Ignore relocations into memory addresses not mapped from the image
try {
@@ -344,7 +362,7 @@ namespace Il2CppInspector
WriteWord(result.newValue);
}
}
Console.WriteLine($"Processed {rels.Count} relocations");
AnsiConsole.WriteLine($"Processed {rels.Count} relocations");
// Build symbol and export tables
processSymbols();
@@ -388,7 +406,8 @@ namespace Il2CppInspector
WriteArray(conv.Long(PT_DYNAMIC.p_offset), dt);
}
private void processSymbols() {
private void processSymbols()
{
StatusUpdate("Processing symbols");
// Three possible symbol tables in ELF files
@@ -436,7 +455,15 @@ namespace Il2CppInspector
symbolTable.Clear();
var exportTable = new Dictionary<string, Export>();
foreach (var pTab in pTables) {
var alreadyProcessed = new List<(TWord offset, TWord count)>();
foreach (var pTab in pTables)
{
if (alreadyProcessed.Any(x =>
conv.ULong(x.offset) == conv.ULong(pTab.offset)))
continue;
alreadyProcessed.Add((pTab.offset, pTab.count));
var symbol_table = ReadArray<TSym>(conv.Long(pTab.offset), conv.Int(pTab.count));
foreach (var symbol in symbol_table)
@@ -463,7 +490,7 @@ namespace Il2CppInspector
var symbolItem = new Symbol {Name = name, Type = type, VirtualAddress = conv.ULong(symbol.st_value) };
symbolTable.TryAdd(name, symbolItem);
if (symbol.st_shndx != (ushort) Elf.SHN_UNDEF)
exportTable.TryAdd(name, new Export {Name = symbolItem.DemangledName, VirtualAddress = conv.ULong(symbol.st_value)});
exportTable.TryAdd(name, new Export {Name = symbolItem.Name, VirtualAddress = conv.ULong(symbol.st_value)});
}
}
@@ -528,11 +555,28 @@ namespace Il2CppInspector
// Note if uiAddr is a valid segment but filesz < memsz and the adjusted uiAddr falls between the range of filesz and memsz,
// an exception will be thrown. This area of memory is assumed to contain all zeroes.
public override uint MapVATR(ulong uiAddr) {
// Additions in the argument to MapVATR may cause an overflow which should be discarded for 32-bit files
if (!TryMapVATR(uiAddr, out var offset))
throw new InvalidOperationException("Failed to map virtual address");
return offset;
}
public override bool TryMapVATR(ulong uiAddr, out uint fileOffset)
{
// Additions in the argument to MapVATR may cause an overflow which should be discarded for 32-bit files
if (Bits == 32)
uiAddr &= 0xffff_ffff;
var program_header_table = this.PHT.First(x => uiAddr >= conv.ULong(x.p_vaddr) && uiAddr <= conv.ULong(conv.Add(x.p_vaddr, x.p_filesz)));
return (uint) (uiAddr - conv.ULong(conv.Sub(program_header_table.p_vaddr, program_header_table.p_offset)));
var phtEntry = PHT.FirstOrDefault(x => uiAddr >= conv.ULong(x.p_vaddr) && uiAddr <= conv.ULong(conv.Add(x.p_vaddr, x.p_filesz)));
if (phtEntry == null)
{
fileOffset = 0;
return false;
}
fileOffset = (uint)(uiAddr - conv.ULong(conv.Sub(phtEntry.p_vaddr, phtEntry.p_offset)));
return true;
}
public override ulong MapFileOffsetToVA(uint offset) {

View File

@@ -6,17 +6,20 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Il2CppInspector.Next;
using NoisyCowStudios.Bin2Object;
using VersionedSerialization;
namespace Il2CppInspector
{
public interface IFileFormatStream
{
double Version { get; set; }
StructVersion Version { get; set; }
long Length { get; }
uint NumImages { get; }
string DefaultFilename { get; }
@@ -91,6 +94,9 @@ namespace Il2CppInspector
long[] ReadMappedWordArray(ulong uiAddr, int count);
List<U> ReadMappedObjectPointerArray<U>(ulong uiAddr, int count) where U : new();
ulong ReadMappedUWord(ulong uiAddr);
ulong[] ReadMappedUWordArray(ulong uiAddr, int count);
void WriteEndianBytes(byte[] bytes);
void Write(long int64);
void Write(ulong uint64);
@@ -120,6 +126,30 @@ namespace Il2CppInspector
public void AddPrimitiveMapping(Type objType, Type streamType);
public void CopyTo(Stream stream);
public TType ReadMappedPrimitive<TType>(ulong addr) where TType : unmanaged;
public TType ReadPrimitive<TType>(long addr) where TType : unmanaged;
public TType ReadPrimitive<TType>() where TType : unmanaged;
public ImmutableArray<TType> ReadMappedPrimitiveArray<TType>(ulong addr, long count) where TType : unmanaged;
public ImmutableArray<TType> ReadPrimitiveArray<TType>(long addr, long count) where TType : unmanaged;
public ImmutableArray<TType> ReadPrimitiveArray<TType>(long count) where TType : unmanaged;
public TType ReadMappedVersionedObject<TType>(ulong addr) where TType : IReadable, new();
public TType ReadVersionedObject<TType>(long addr) where TType : IReadable, new();
public TType ReadVersionedObject<TType>() where TType : IReadable, new();
public ImmutableArray<TType> ReadMappedVersionedObjectArray<TType>(ulong addr, long count)
where TType : IReadable, new();
public ImmutableArray<TType> ReadVersionedObjectArray<TType>(long addr, long count)
where TType : IReadable, new();
public ImmutableArray<TType> ReadVersionedObjectArray<TType>(long count)
where TType : IReadable, new();
public ImmutableArray<TType> ReadMappedVersionedObjectPointerArray<TType>(ulong addr, int count)
where TType : IReadable, new();
}
public class FileFormatStream
@@ -146,7 +176,7 @@ namespace Il2CppInspector
try {
if (type.GetMethod("Load", BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public,
null, new[] { typeof(BinaryObjectStream), typeof(LoadOptions), typeof(EventHandler<string>) }, null)
.Invoke(null, new object[] { binaryObjectStream, loadOptions, statusCallback }) is IFileFormatStream loaded) {
.Invoke(null, [binaryObjectStream, loadOptions, statusCallback]) is IFileFormatStream loaded) {
loaded.IsModified |= preProcessResult.IsStreamModified;
return loaded;
@@ -160,7 +190,7 @@ namespace Il2CppInspector
}
}
public abstract class FileFormatStream<T> : BinaryObjectStream, IFileFormatStream where T : FileFormatStream<T>
public abstract class FileFormatStream<T> : BinaryObjectStreamReader, IFileFormatStream where T : FileFormatStream<T>
{
public abstract string DefaultFilename { get; }
@@ -177,8 +207,6 @@ namespace Il2CppInspector
public virtual string Arch => throw new NotImplementedException();
public virtual int Bits => throw new NotImplementedException();
// Extra parameters to be passed to a loader
protected LoadOptions LoadOptions;
@@ -254,7 +282,7 @@ namespace Il2CppInspector
public virtual uint MapVATR(ulong uiAddr) => (uint) uiAddr;
// Try to map an RVA to an offset in the file image
public bool TryMapVATR(ulong uiAddr, out uint fileOffset) {
public virtual bool TryMapVATR(ulong uiAddr, out uint fileOffset) {
try {
fileOffset = MapVATR(uiAddr);
return true;
@@ -322,5 +350,42 @@ namespace Il2CppInspector
array.Add(ReadMappedObject<U>(pointers[i]));
return array;
}
public TType ReadMappedPrimitive<TType>(ulong addr) where TType : unmanaged => ReadPrimitive<TType>(MapVATR(addr));
public ImmutableArray<TType> ReadMappedPrimitiveArray<TType>(ulong addr, long count) where TType : unmanaged
=> ReadPrimitiveArray<TType>(MapVATR(addr), count);
public TType ReadMappedVersionedObject<TType>(ulong addr) where TType : IReadable, new() => ReadVersionedObject<TType>(MapVATR(addr));
public ImmutableArray<TType> ReadMappedVersionedObjectArray<TType>(ulong addr, long count) where TType : IReadable, new()
=> ReadVersionedObjectArray<TType>(MapVATR(addr), count);
public ImmutableArray<TType> ReadMappedVersionedObjectPointerArray<TType>(ulong addr, int count)
where TType : IReadable, new()
{
var pointers = ReadMappedUWordArray(addr, count);
var builder = ImmutableArray.CreateBuilder<TType>((int)count);
for (long i = 0; i < count; i++)
builder.Add(ReadMappedVersionedObject<TType>(pointers[i]));
return builder.MoveToImmutable();
}
public ulong ReadMappedUWord(ulong uiAddr)
{
Position = MapVATR(uiAddr);
return ReadNUInt();
}
public ulong[] ReadMappedUWordArray(ulong uiAddr, int count)
{
Position = MapVATR(uiAddr);
var arr = new ulong[count];
for (int i = 0; i < count; i++)
arr[i] = ReadNUInt();
return arr;
}
}
}

View File

@@ -6,6 +6,7 @@
using System;
using NoisyCowStudios.Bin2Object;
using VersionedSerialization.Attributes;
namespace Il2CppInspector
{
@@ -31,6 +32,7 @@ namespace Il2CppInspector
LC_DYLD_INFO_ONLY = 0x80000022,
LC_FUNCTION_STARTS = 0x26,
LC_ENCRYPTION_INFO_64 = 0x2C,
LC_DYLD_CHAINED_FIXUPS = 0x80000034,
CPU_TYPE_X86 = 7,
CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86,
@@ -172,4 +174,47 @@ namespace Il2CppInspector
public bool r_extern => ((r_data >> 27) & 1) == 1;
public uint r_type => r_data >> 28;
}
[VersionedStruct]
public partial struct MachODyldChainedFixupsHeader
{
public uint FixupsVersion;
public uint StartsOffset;
public uint ImportsOffset;
public uint SymbolsOffset;
public uint ImportsCount;
public uint ImportsFormat;
public uint SymbolsFormat;
}
[VersionedStruct]
public partial struct MachODyldChainedStartsInSegment
{
public const ushort DYLD_CHAINED_PTR_START_NONE = 0xffff;
public uint StructSize;
public ushort PageSize;
public ushort PointerFormat;
public ulong SegmentOffset;
public uint MaxValidPointer;
public ushort PageCount;
}
public enum MachODyldChainedPtr
{
DYLD_CHAINED_PTR_64 = 2,
DYLD_CHAINED_PTR_64_OFFSET = 6,
}
[VersionedStruct]
public partial struct MachODyldChainedPtr64Rebase
{
private ulong _value;
public ulong Target => _value & 0xfffffffff;
public ulong High8 => (_value >> 36) & 0xff;
public ulong Reserved => (_value >> (36 + 8)) & 0x7f;
public ulong Next => (_value >> (36 + 8 + 7)) & 0xfff;
public bool Bind => ((_value >> (36 + 8 + 7 + 12)) & 0x1) == 0x1;
}
}

View File

@@ -4,12 +4,13 @@
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
@@ -172,15 +173,21 @@ namespace Il2CppInspector
if (encryptionInfo.CryptID != 0)
throw new NotImplementedException("This Mach-O executable is encrypted with FairPlay DRM and cannot be processed. Please provide a decrypted version of the executable.");
break;
case MachO.LC_DYLD_CHAINED_FIXUPS:
var chainedFixupsInfo = ReadObject<MachOLinkEditDataCommand>();
ApplyChainedFixups(chainedFixupsInfo);
break;
}
// There might be other data after the load command so always use the specified total size to step forwards
Position = startPos + loadCommand.Size;
}
// Note: Some binaries do not have __mod_init_func, but instead just __init_offset with offsets to the init functions. This check is disabled.
// Must find __mod_init_func
if (funcTab == null)
return false;
//if (funcTab == null)
// return false;
// Process relocations
foreach (var section in machoSections) {
@@ -188,7 +195,7 @@ namespace Il2CppInspector
// TODO: Implement Mach-O relocations
if (rels.Any()) {
Console.WriteLine("Mach-O file contains relocations (feature not yet implemented)");
AnsiConsole.WriteLine("Mach-O file contains relocations (feature not yet implemented)");
break;
}
}
@@ -282,7 +289,7 @@ namespace Il2CppInspector
: SymbolType.Unknown;
if (type == SymbolType.Unknown) {
Console.WriteLine($"Unknown symbol type: {((int) ntype):x2} {value:x16} " + CxxDemangler.CxxDemangler.Demangle(name));
AnsiConsole.WriteLine($"Unknown symbol type: {((int) ntype):x2} {value:x16} {name}");
}
// Ignore duplicates
@@ -290,7 +297,82 @@ namespace Il2CppInspector
}
}
public override uint[] GetFunctionTable() => ReadArray<TWord>(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray();
private void ApplyChainedFixups(in MachOLinkEditDataCommand info)
{
var chainedFixupsHeader = ReadVersionedObject<MachODyldChainedFixupsHeader>(info.Offset);
if (chainedFixupsHeader.FixupsVersion != 0)
{
AnsiConsole.WriteLine($"Unsupported chained fixups version: {chainedFixupsHeader.FixupsVersion}");
return;
}
if (chainedFixupsHeader.ImportsFormat != 1 /* DYLD_CHAINED_IMPORT */)
{
AnsiConsole.WriteLine($"Unsupported chained fixups import format: {chainedFixupsHeader.ImportsFormat}");
return;
}
//var importsBase = info.Offset + chainedFixupsHeader.ImportsOffset;
//var imports = ReadPrimitiveArray<uint>(importsBase,
// chainedFixupsHeader.ImportsCount);
//var symbolsBase = info.Offset + chainedFixupsHeader.SymbolsOffset; // todo: apparently this supports zlib
var startsBase = info.Offset + chainedFixupsHeader.StartsOffset;
var segmentCount = ReadPrimitive<uint>(startsBase);
var segmentStartOffsets = ReadPrimitiveArray<uint>(startsBase + 4, segmentCount);
foreach (var startOffset in segmentStartOffsets)
{
if (startOffset == 0)
continue;
var startsInfo = ReadVersionedObject<MachODyldChainedStartsInSegment>(startsBase + startOffset);
if (startsInfo.SegmentOffset == 0)
continue;
var pointerFormat = (MachODyldChainedPtr)startsInfo.PointerFormat;
var pages = ReadPrimitiveArray<ushort>(
startsBase + startOffset + MachODyldChainedStartsInSegment.Size(), startsInfo.PageCount);
for (var i = 0; i < pages.Length; i++)
{
var page = pages[i];
if (page == MachODyldChainedStartsInSegment.DYLD_CHAINED_PTR_START_NONE)
continue;
var chainOffset = startsInfo.SegmentOffset + (ulong)(i * startsInfo.PageSize) + page;
while (true)
{
var currentEntry = ReadVersionedObject<MachODyldChainedPtr64Rebase>((long)chainOffset);
var fixedValue = 0ul;
if (!currentEntry.Bind) // todo: bind
{
fixedValue = pointerFormat switch
{
MachODyldChainedPtr.DYLD_CHAINED_PTR_64
or MachODyldChainedPtr.DYLD_CHAINED_PTR_64_OFFSET
=> currentEntry.High8 << 56 | currentEntry.Target,
_ => fixedValue
};
Write((long)chainOffset, fixedValue);
}
if (currentEntry.Next == 0)
break;
chainOffset += currentEntry.Next * 4;
}
}
}
}
public override uint[] GetFunctionTable() => funcTab == null ? [] : ReadArray<TWord>(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray();
public override Dictionary<string, Symbol> GetSymbolTable() => symbolTable;

View File

@@ -4,13 +4,14 @@
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
@@ -102,7 +103,7 @@ namespace Il2CppInspector
// Unpacking must be done starting here, one byte after the end of the headers
// Packed or previously packed with Themida? This is purely for information
if (sections.FirstOrDefault(x => x.Name == ".themida") is PESection _)
Console.WriteLine("Themida protection detected");
AnsiConsole.WriteLine("Themida protection detected");
// Packed with anything (including Themida)?
mightBePacked = sections.FirstOrDefault(x => x.Name == ".rdata") is null;
@@ -114,102 +115,41 @@ namespace Il2CppInspector
section.Name = wantedSectionTypes[section.Characteristics];
// Get base of code
GlobalOffset = pe.ImageBase + pe.BaseOfCode - sections.First(x => x.Name == ".text").PointerToRawData;
GlobalOffset = pe.ImageBase + pe.BaseOfCode - sections
.FirstOrDefault(x => x.Characteristics.HasFlag(PE.IMAGE_SCN_MEM_EXECUTE))?.PointerToRawData ?? 0;
// Confirm that .rdata section begins at same place as IAT
var rData = sections.First(x => x.Name == ".rdata");
mightBePacked |= rData.VirtualAddress != IATStart;
var rData = sections.FirstOrDefault(x => x.Name == ".rdata");
mightBePacked |= rData == null || rData.VirtualAddress != IATStart;
if (rData != null)
{
// Calculate start of function pointer table
pFuncTable = rData.PointerToRawData + IATSize;
// Skip over __guard_check_icall_fptr and __guard_dispatch_icall_fptr if present, then the following zero offset
Position = pFuncTable;
if (pe is PEOptHeader32) {
if (pe is PEOptHeader32)
{
while (ReadUInt32() != 0)
pFuncTable += 4;
pFuncTable += 4;
}
else {
else
{
while (ReadUInt64() != 0)
pFuncTable += 8;
pFuncTable += 8;
}
}
// In the fist go round, we signal that this is at least a valid PE file; we don't try to unpack yet
return true;
}
// Load DLL into memory and save it as a new PE stream
private void load() {
// Check that the process is running in the same word size as the DLL
// One way round this in future would be to spawn a new process of the correct word size
if ((Environment.Is64BitProcess && Bits == 32) || (!Environment.Is64BitProcess && Bits == 64))
throw new InvalidOperationException($"Cannot unpack a {Bits}-bit DLL from within a {(Environment.Is64BitProcess ? 64 : 32)}-bit process. Use the {Bits}-version of Il2CppInspector to unpack this DLL.");
// Get file path
// This error should never occur with the bundled CLI and GUI; only when used as a library by a 3rd party tool
if (LoadOptions == null || !(LoadOptions.BinaryFilePath is string dllPath))
throw new InvalidOperationException("To load a packed PE file, you must specify the DLL file path in LoadOptions");
// Attempt to load DLL and run startup functions
// NOTE: This can cause a CSE (AccessViolation) for certain types of protection
// so only try to unpack as the final load strategy
IntPtr hModule = LoadLibrary(dllPath);
if (hModule == IntPtr.Zero) {
var lastErrorCode = Marshal.GetLastWin32Error();
throw new InvalidOperationException($"Unable to load the DLL for unpacking: error code {lastErrorCode}");
}
// Maximum image size
var size = sections.Last().VirtualAddress + sections.Last().VirtualSize;
// Allocate memory for unpacked image
var peBytes = new byte[size];
// Copy relevant sections from unmanaged memory
foreach (var section in sections.Where(s => wantedSectionTypes.Keys.Contains(s.Characteristics)))
Marshal.Copy(IntPtr.Add(hModule, (int) section.VirtualAddress), peBytes, (int) section.VirtualAddress, (int) section.VirtualSize);
// Decrease reference count for unload
FreeLibrary(hModule);
// Rebase
pe.ImageBase = (ulong) hModule.ToInt64();
// Rewrite sections to match memory layout
foreach (var section in sections) {
section.PointerToRawData = section.VirtualAddress;
section.SizeOfRawData = section.VirtualSize;
}
// Truncate memory stream at start of COFF header
var endOfSignature = ReadUInt32(0x3C) + 4; // DOS header + 4-byte PE signature
SetLength(endOfSignature);
// Re-write the stream (the headers are only necessary in case the user wants to save)
Position = endOfSignature;
WriteObject(coff);
if (Bits == 32) WriteObject((PEOptHeader32) pe);
else WriteObject((PEOptHeader64) pe);
WriteArray(sections);
Write(peBytes, (int) Position, peBytes.Length - (int) Position);
IsModified = true;
}
// Raw file / unpacked file load strategies
public override IEnumerable<IFileFormatStream> TryNextLoadStrategy() {
// First load strategy: the regular file
yield return this;
// Second load strategy: load the DLL into memory to unpack it
if (mightBePacked) {
Console.WriteLine("IL2CPP binary appears to be packed - attempting to unpack and retrying");
StatusUpdate("Unpacking binary");
load();
yield return this;
}
}
public override uint[] GetFunctionTable() {
@@ -227,12 +167,17 @@ namespace Il2CppInspector
return addrs.ToArray();
}
public override IEnumerable<Export> GetExports() {
public override IEnumerable<Export> GetExports()
{
var exportDirectory = pe.DataDirectory[0];
if (exportDirectory.Size == 0)
return [];
// Get exports table
var ETStart = pe.DataDirectory[0].VirtualAddress + pe.ImageBase;
var exportTableStart = exportDirectory.VirtualAddress + pe.ImageBase;
// Get export RVAs
var exportDirectoryTable = ReadObject<PEExportDirectory>(MapVATR(ETStart));
var exportDirectoryTable = ReadObject<PEExportDirectory>(MapVATR(exportTableStart));
var exportCount = (int) exportDirectoryTable.NumberOfFunctions;
var exportAddresses = ReadArray<uint>(MapVATR(exportDirectoryTable.AddressOfFunctions + pe.ImageBase), exportCount);
var exports = exportAddresses.Select((a, i) => new Export {
@@ -253,6 +198,26 @@ namespace Il2CppInspector
return exports.Values;
}
public override bool TryMapVATR(ulong uiAddr, out uint fileOffset)
{
if (uiAddr == 0)
{
fileOffset = 0;
return true;
}
var section = sections.FirstOrDefault(x => uiAddr - pe.ImageBase >= x.VirtualAddress &&
uiAddr - pe.ImageBase < x.VirtualAddress + x.SizeOfRawData);
if (section == null)
{
fileOffset = 0;
return false;
}
fileOffset = (uint)(uiAddr - section.VirtualAddress - pe.ImageBase + section.PointerToRawData);
return true;
}
public override uint MapVATR(ulong uiAddr) {
if (uiAddr == 0)
return 0;

View File

@@ -4,11 +4,12 @@
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
using Spectre.Console;
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
@@ -100,7 +101,7 @@ namespace Il2CppInspector
// Get the entire remaining chunk, or to the end of the file if it doesn't contain the end of the chunk
var length = (uint) Math.Min(chunk.Memory.End - memoryNext, source.Length);
Console.WriteLine($"Writing {length:x8} bytes from {Path.GetFileName(file.Name)} +{fileStart:x8} ({memoryNext:x8}) to target {il2cpp.Position:x8}");
AnsiConsole.WriteLine($"Writing {length:x8} bytes from {Path.GetFileName(file.Name)} +{fileStart:x8} ({memoryNext:x8}) to target {il2cpp.Position:x8}");
// Can't use Stream.CopyTo as it doesn't support length parameter
var buffer = new byte[length];

View File

@@ -20,7 +20,5 @@ namespace Il2CppInspector
public ulong VirtualAddress { get; set; }
public string Name { get; set; }
public SymbolType Type { get; set; }
public string DemangledName => CxxDemangler.CxxDemangler.Demangle(Name);
}
}

View File

@@ -38,7 +38,7 @@ namespace Il2CppInspector
Position = arch.Offset;
Endianness = Endianness.Little;
using var s = new BinaryObjectStream(ReadBytes((int) arch.Size));
using var s = new BinaryObjectStream(ReadBytes((int) arch.Size).ToArray());
return (IFileFormatStream) MachOReader32.Load(s, LoadOptions, OnStatusUpdate) ?? MachOReader64.Load(s, LoadOptions, OnStatusUpdate);
}
}

View File

@@ -4,9 +4,11 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using dnlib.DotNet;
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using Il2CppInspector.Reflection;
using Il2CppInspector.Utils;
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
@@ -14,7 +16,7 @@ namespace Il2CppInspector
{
private readonly Il2CppInspector _inspector;
private readonly Assembly _assembly;
private readonly BinaryObjectStream _data;
private readonly BinaryObjectStreamReader _data;
private readonly uint _start;
private readonly uint _end;
@@ -24,7 +26,7 @@ namespace Il2CppInspector
public uint Count { get; }
public CustomAttributeDataReader(Il2CppInspector inspector, Assembly assembly, BinaryObjectStream data, uint startOffset, uint endOffset)
public CustomAttributeDataReader(Il2CppInspector inspector, Assembly assembly, BinaryObjectStreamReader data, uint startOffset, uint endOffset)
{
_inspector = inspector;
_assembly = assembly;
@@ -143,9 +145,9 @@ namespace Il2CppInspector
}
private TypeInfo ConvertTypeDef(Il2CppTypeDefinition typeDef, Il2CppTypeEnum type)
=> typeDef == null
? _assembly.Model.GetTypeDefinitionFromTypeEnum(type)
: _assembly.Model.TypesByDefinitionIndex[Array.IndexOf(_inspector.TypeDefinitions, typeDef)];
=> typeDef.IsValid
? _assembly.Model.TypesByDefinitionIndex[_inspector.TypeDefinitions.IndexOf(typeDef)]
: _assembly.Model.GetTypeDefinitionFromTypeEnum(type);
private (TypeInfo, int) ReadCustomAttributeNamedArgumentClassAndIndex(TypeInfo attrInfo)
{

View File

@@ -5,13 +5,15 @@
All rights reserved.
*/
using System;
using System.Collections.Generic;
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using Spectre.Console;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using VersionedSerialization;
namespace Il2CppInspector
{
@@ -34,16 +36,16 @@ namespace Il2CppInspector
public ulong CodeRegistrationPointer { get; private set; }
public ulong MetadataRegistrationPointer { get; private set; }
public ulong RegistrationFunctionPointer { get; private set; }
public Dictionary<string, ulong> CodeGenModulePointers { get; } = new Dictionary<string, ulong>();
public Dictionary<string, ulong> CodeGenModulePointers { get; } = new();
// Only for <=v24.1
public ulong[] GlobalMethodPointers { get; set; }
// Only for >=v24.2
public Dictionary<Il2CppCodeGenModule, ulong[]> ModuleMethodPointers { get; set; } = new Dictionary<Il2CppCodeGenModule, ulong[]>();
public Dictionary<Il2CppCodeGenModule, ulong[]> ModuleMethodPointers { get; set; } = new();
// Only for >=v24.2. In earlier versions, invoker indices are stored in Il2CppMethodDefinition in the metadata file
public Dictionary<Il2CppCodeGenModule, int[]> MethodInvokerIndices { get; set; } = new Dictionary<Il2CppCodeGenModule, int[]>();
public Dictionary<Il2CppCodeGenModule, ImmutableArray<int>> MethodInvokerIndices { get; set; } = new();
// NOTE: In versions <21 and earlier releases of v21, use FieldOffsets:
// global field index => field offset
@@ -51,7 +53,7 @@ namespace Il2CppInspector
// type index => RVA in image where the list of field offsets for the type start (4 bytes per field)
// Negative field offsets from start of each function
public uint[] FieldOffsets { get; private set; }
public ImmutableArray<uint> FieldOffsets { get; private set; }
// Pointers to field offsets
public long[] FieldOffsetPointers { get; private set; }
@@ -65,13 +67,13 @@ namespace Il2CppInspector
public ulong[] MethodInvokePointers { get; private set; }
// Version 16 and below: method references for vtable
public uint[] VTableMethodReferences { get; private set; }
public ImmutableArray<uint> VTableMethodReferences { get; private set; }
// Generic method specs for vtables
public Il2CppMethodSpec[] MethodSpecs { get; private set; }
public ImmutableArray<Il2CppMethodSpec> MethodSpecs { get; private set; }
// List of run-time concrete generic class and method signatures
public List<Il2CppGenericInst> GenericInstances { get; private set; }
public ImmutableArray<Il2CppGenericInst> GenericInstances { get; private set; }
// List of constructed generic method function pointers corresponding to each possible method instantiation
public Dictionary<Il2CppMethodSpec, ulong> GenericMethodPointers { get; } = new Dictionary<Il2CppMethodSpec, ulong>();
@@ -80,7 +82,7 @@ namespace Il2CppInspector
public Dictionary<Il2CppMethodSpec, int> GenericMethodInvokerIndices { get; } = new Dictionary<Il2CppMethodSpec, int>();
// Every type reference (TypeRef) sorted by index
public List<Il2CppType> TypeReferences { get; private set; }
public ImmutableArray<Il2CppType> TypeReferences { get; private set; }
// Every type reference index sorted by virtual address
public Dictionary<ulong, int> TypeReferenceIndicesByAddress { get; private set; }
@@ -89,7 +91,7 @@ namespace Il2CppInspector
// One assembly may contain multiple modules
public Dictionary<string, Il2CppCodeGenModule> Modules { get; private set; }
public List<Il2CppTypeDefinitionSizes> TypeDefinitionSizes { get; private set; }
public ImmutableArray<Il2CppTypeDefinitionSizes> TypeDefinitionSizes { get; private set; }
// Status update callback
private EventHandler<string> OnStatusUpdate { get; set; }
@@ -135,7 +137,7 @@ namespace Il2CppInspector
}
// Load binary without a global-metadata.dat available
public static Il2CppBinary Load(IFileFormatStream stream, double metadataVersion, EventHandler<string> statusCallback = null) {
public static Il2CppBinary Load(IFileFormatStream stream, StructVersion metadataVersion, EventHandler<string> statusCallback = null) {
foreach (var loadedImage in stream.TryNextLoadStrategy()) {
var inst = LoadImpl(stream, statusCallback);
if (inst.FindRegistrationStructs(metadataVersion))
@@ -167,7 +169,7 @@ namespace Il2CppInspector
}
// Initialize binary without a global-metadata.dat available
public bool FindRegistrationStructs(double metadataVersion) {
public bool FindRegistrationStructs(StructVersion metadataVersion) {
Image.Version = metadataVersion;
StatusUpdate("Searching for binary metadata");
@@ -190,7 +192,7 @@ namespace Il2CppInspector
var symbols = Image.GetSymbolTable();
if (symbols.Any()) {
Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
AnsiConsole.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
symbols.TryGetValue("g_CodeRegistration", out var code);
symbols.TryGetValue("g_MetadataRegistration", out var metadata);
@@ -201,13 +203,13 @@ namespace Il2CppInspector
symbols.TryGetValue("_g_MetadataRegistration", out metadata);
if (code != null && metadata != null) {
Console.WriteLine("Required structures acquired from symbol lookup");
AnsiConsole.WriteLine("Required structures acquired from symbol lookup");
return (code.VirtualAddress, metadata.VirtualAddress);
} else {
Console.WriteLine("No matches in symbol table");
AnsiConsole.WriteLine("No matches in symbol table");
}
} else if (symbols != null) {
Console.WriteLine("No symbol table present in binary file");
AnsiConsole.WriteLine("No symbol table present in binary file");
} else {
Console.WriteLine("Symbol table search not implemented for this binary format");
}
@@ -226,12 +228,12 @@ namespace Il2CppInspector
var (code, metadata) = ConsiderCode(Image, loc);
if (code != 0) {
RegistrationFunctionPointer = loc + Image.GlobalOffset;
Console.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
AnsiConsole.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
return (code, metadata);
}
}
Console.WriteLine("No matches via code heuristics");
AnsiConsole.WriteLine("No matches via code heuristics");
return null;
}
@@ -243,11 +245,11 @@ namespace Il2CppInspector
var (codePtr, metadataPtr) = ImageScan(Metadata);
if (codePtr == 0) {
Console.WriteLine("No matches via data heuristics");
AnsiConsole.WriteLine("No matches via data heuristics");
return null;
}
Console.WriteLine("Required structures acquired from data heuristics");
AnsiConsole.WriteLine("Required structures acquired from data heuristics");
return (codePtr, metadataPtr);
}
@@ -273,33 +275,12 @@ namespace Il2CppInspector
var pointerSize = Image.Bits == 32 ? 4u : 8u;
Console.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration));
Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
AnsiConsole.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration));
AnsiConsole.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
// Root structures from which we find everything else
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
MetadataRegistration = Image.ReadMappedObject<Il2CppMetadataRegistration>(metadataRegistration);
// genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1
// pointer expected if we need to bump version
if (Image.Version == 24.4 && CodeRegistration.invokerPointersCount > 0x50000)
{
Image.Version = 24.5;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
}
if (Image.Version == 24.4 && CodeRegistration.reversePInvokeWrapperCount > 0x50000) {
Image.Version = 24.5;
codeRegistration -= 1 * pointerSize;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
}
if (Image.Version is 29 or 31 && (long)CodeRegistration.genericMethodPointersCount - MetadataRegistration.genericMethodTableCount > 0x10000)
{
Image.Version += 0.1;
codeRegistration -= 2 * pointerSize;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
}
CodeRegistration = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
MetadataRegistration = Image.ReadMappedVersionedObject<Il2CppMetadataRegistration>(metadataRegistration);
// Plugin hook to pre-process binary
isModified |= PluginHooks.PreProcessBinary(this).IsStreamModified;
@@ -313,36 +294,39 @@ namespace Il2CppInspector
* typeRefPointers must be a series of pointers in __const
* MethodInvokePointers must be a series of pointers in __text or .text, and in sequential order
*/
if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.typeDefinitionsSizesCount)
|| CodeRegistration.reversePInvokeWrapperCount > 0x10000
|| CodeRegistration.unresolvedVirtualCallCount > 0x4000 // >= 22
|| CodeRegistration.interopDataCount > 0x1000 // >= 23
|| (Image.Version <= 24.1 && CodeRegistration.invokerPointersCount > CodeRegistration.methodPointersCount))
if ((Metadata != null && Metadata.Types.Length != MetadataRegistration.TypeDefinitionsSizesCount)
|| CodeRegistration.ReversePInvokeWrapperCount > 0x10000
// L-NOTE: These below boundaries have been updated already as some games
// have reached these limits during normal use. Maybe we should just remove them
// at this point?
|| CodeRegistration.UnresolvedVirtualCallCount > 0x8000 // >= 22
|| CodeRegistration.InteropDataCount > 0x2000 // >= 23
|| (Image.Version <= MetadataVersions.V241 && CodeRegistration.InvokerPointersCount > CodeRegistration.MethodPointersCount))
throw new NotSupportedException("The detected Il2CppCodeRegistration / Il2CppMetadataRegistration structs do not pass validation. This may mean that their fields have been re-ordered as a form of obfuscation and Il2CppInspector has not been able to restore the original order automatically. Consider re-ordering the fields in Il2CppBinaryClasses.cs and try again.");
// The global method pointer list was deprecated in v24.2 in favour of Il2CppCodeGenModule
if (Image.Version <= 24.1)
GlobalMethodPointers = Image.ReadMappedArray<ulong>(CodeRegistration.pmethodPointers, (int) CodeRegistration.methodPointersCount);
if (Image.Version <= MetadataVersions.V241)
GlobalMethodPointers = Image.ReadMappedUWordArray(CodeRegistration.MethodPointers, (int) CodeRegistration.MethodPointersCount);
// After v24 method pointers and RGCTX data were stored in Il2CppCodeGenModules
if (Image.Version >= 24.2) {
if (Image.Version >= MetadataVersions.V242) {
Modules = new Dictionary<string, Il2CppCodeGenModule>();
// In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0;
// if this changes we'll have to get smarter about disambiguating these two.
if (CodeRegistration.codeGenModulesCount == 0) {
Image.Version = 24.3;
CodeRegistration = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
if (CodeRegistration.CodeGenModulesCount == 0) {
Image.Version = MetadataVersions.V243;
CodeRegistration = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
}
// Array of pointers to Il2CppCodeGenModule
var codeGenModulePointers = Image.ReadMappedArray<ulong>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount);
var modules = Image.ReadMappedObjectPointerArray<Il2CppCodeGenModule>(CodeRegistration.pcodeGenModules, (int) CodeRegistration.codeGenModulesCount);
var codeGenModulePointers = Image.ReadMappedUWordArray(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount);
var modules = Image.ReadMappedVersionedObjectPointerArray<Il2CppCodeGenModule>(CodeRegistration.CodeGenModules, (int) CodeRegistration.CodeGenModulesCount);
foreach (var mp in modules.Zip(codeGenModulePointers, (m, p) => new { Module = m, Pointer = p })) {
var module = mp.Module;
var name = Image.ReadMappedNullTerminatedString(module.moduleName);
var name = Image.ReadMappedNullTerminatedString(module.ModuleName);
Modules.Add(name, module);
CodeGenModulePointers.Add(name, mp.Pointer);
@@ -351,24 +335,32 @@ namespace Il2CppInspector
// the entire method pointer array will be NULL values, causing the methodPointer to be mapped to .bss
// and therefore out of scope of the binary image
try {
ModuleMethodPointers.Add(module, Image.ReadMappedArray<ulong>(module.methodPointers, (int) module.methodPointerCount));
ModuleMethodPointers.Add(module, Image.ReadMappedUWordArray(module.MethodPointers, (int) module.MethodPointerCount));
} catch (InvalidOperationException) {
ModuleMethodPointers.Add(module, new ulong[module.methodPointerCount]);
ModuleMethodPointers.Add(module, new ulong[module.MethodPointerCount]);
}
// Read method invoker pointer indices - one per method
MethodInvokerIndices.Add(module, Image.ReadMappedArray<int>(module.invokerIndices, (int) module.methodPointerCount));
try
{
MethodInvokerIndices.Add(module,
Image.ReadMappedPrimitiveArray<int>(module.InvokerIndices, (int)module.MethodPointerCount));
}
catch (InvalidOperationException)
{
MethodInvokerIndices.Add(module, [..new int[(int)module.MethodPointerCount]]);
}
}
}
// Field offset data. Metadata <=21.x uses a value-type array; >=21.x uses a pointer array
// Versions from 22 onwards use an array of pointers in Binary.FieldOffsetData
bool fieldOffsetsArePointers = (Image.Version >= 22);
bool fieldOffsetsArePointers = (Image.Version >= MetadataVersions.V220);
// Some variants of 21 also use an array of pointers
if (Image.Version == 21) {
var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, 6);
if (Image.Version == MetadataVersions.V210) {
var fieldTest = Image.ReadMappedWordArray(MetadataRegistration.FieldOffsets, 6);
// We detect this by relying on the fact Module, Object, ValueType, Attribute, _Attribute and Int32
// are always the first six defined types, and that all but Int32 have no fields
@@ -377,29 +369,75 @@ namespace Il2CppInspector
// All older versions use values directly in the array
if (!fieldOffsetsArePointers)
FieldOffsets = Image.ReadMappedArray<uint>(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount);
FieldOffsets = Image.ReadMappedPrimitiveArray<uint>(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount);
else
FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.pfieldOffsets, (int)MetadataRegistration.fieldOffsetsCount);
FieldOffsetPointers = Image.ReadMappedWordArray(MetadataRegistration.FieldOffsets, (int)MetadataRegistration.FieldOffsetsCount);
// Type references (pointer array)
var typeRefPointers = Image.ReadMappedArray<ulong>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount);
var typeRefPointers = Image.ReadMappedUWordArray(MetadataRegistration.Types, (int) MetadataRegistration.TypesCount);
TypeReferenceIndicesByAddress = typeRefPointers.Zip(Enumerable.Range(0, typeRefPointers.Length), (a, i) => new { a, i }).ToDictionary(x => x.a, x => x.i);
TypeReferences =
Image.Version >= 27.2
? Image.ReadMappedObjectPointerArray<Il2CppTypeV272>(MetadataRegistration.ptypes, (int) MetadataRegistration.typesCount)
.Cast<Il2CppType>()
.ToList()
: Image.ReadMappedObjectPointerArray<Il2CppType>(MetadataRegistration.ptypes, (int)MetadataRegistration.typesCount);
TypeReferences = Image.ReadMappedVersionedObjectPointerArray<Il2CppType>(MetadataRegistration.Types, (int)MetadataRegistration.TypesCount);
if (TypeReferences.Any(x =>
x.Type.IsTypeDefinitionEnum()
&& (uint)x.Data.KlassIndex >= (uint)Metadata.Types.Length))
{
// This is a memory-dumped binary.
// We need to fix the remapped type indices from their pointer form back to the indices.
var baseDefinitionPtr = ulong.MaxValue;
var baseGenericPtr = ulong.MaxValue;
foreach (var entry in TypeReferences)
{
if (entry.Type.IsTypeDefinitionEnum())
{
baseDefinitionPtr = Math.Min(baseDefinitionPtr, entry.Data.Type.PointerValue);
}
else if (entry.Type.IsGenericParameterEnum())
{
baseGenericPtr = Math.Min(baseGenericPtr, entry.Data.GenericParameterHandle.PointerValue);
}
}
var definitionSize = (ulong)Il2CppTypeDefinition.Size(Image.Version);
var genericParameterSize = (ulong)Il2CppGenericParameter.Size(Image.Version);
var builder = ImmutableArray.CreateBuilder<Il2CppType>(TypeReferences.Length);
for (var i = 0; i < TypeReferences.Length; i++)
{
var type = TypeReferences[i];
if (type.Type.IsTypeDefinitionEnum())
{
if (type.Data.Type.PointerValue >= baseDefinitionPtr)
type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize;
Debug.Assert(Metadata!.Types.Length > type.Data.KlassIndex);
}
else if (type.Type.IsGenericParameterEnum())
{
if (type.Data.Type.PointerValue >= baseGenericPtr)
type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize;
Debug.Assert(Metadata!.GenericParameters.Length > type.Data.KlassIndex);
}
Debug.Assert((long)type.Data.Value >= 0);
builder.Add(type);
}
TypeReferences = builder.MoveToImmutable();
}
// Custom attribute constructors (function pointers)
// This is managed in Il2CppInspector for metadata >= 27
if (Image.Version < 27) {
CustomAttributeGenerators = Image.ReadMappedArray<ulong>(CodeRegistration.customAttributeGenerators, (int) CodeRegistration.customAttributeCount);
if (Image.Version < MetadataVersions.V270) {
CustomAttributeGenerators = Image.ReadMappedUWordArray(CodeRegistration.CustomAttributeGenerators, (int) CodeRegistration.CustomAttributeCount);
}
// Method.Invoke function pointers
MethodInvokePointers = Image.ReadMappedArray<ulong>(CodeRegistration.invokerPointers, (int) CodeRegistration.invokerPointersCount);
MethodInvokePointers = Image.ReadMappedUWordArray(CodeRegistration.InvokerPointers, (int) CodeRegistration.InvokerPointersCount);
// TODO: Function pointers as shown below
// reversePInvokeWrappers
@@ -408,26 +446,26 @@ namespace Il2CppInspector
// >=22: unresolvedVirtualCallPointers
// >=23: interopData
if (Image.Version < 19) {
VTableMethodReferences = Image.ReadMappedArray<uint>(MetadataRegistration.methodReferences, (int)MetadataRegistration.methodReferencesCount);
if (Image.Version < MetadataVersions.V190) {
VTableMethodReferences = Image.ReadMappedPrimitiveArray<uint>(MetadataRegistration.MethodReferences, (int)MetadataRegistration.MethodReferencesCount);
}
// Generic type and method specs (open and closed constructed types)
MethodSpecs = Image.ReadMappedArray<Il2CppMethodSpec>(MetadataRegistration.methodSpecs, (int) MetadataRegistration.methodSpecsCount);
MethodSpecs = Image.ReadMappedVersionedObjectArray<Il2CppMethodSpec>(MetadataRegistration.MethodSpecs, (int) MetadataRegistration.MethodSpecsCount);
// Concrete generic class and method signatures
GenericInstances = Image.ReadMappedObjectPointerArray<Il2CppGenericInst>(MetadataRegistration.genericInsts, (int) MetadataRegistration.genericInstsCount);
GenericInstances = Image.ReadMappedVersionedObjectPointerArray<Il2CppGenericInst>(MetadataRegistration.GenericInsts, (int) MetadataRegistration.GenericInstsCount);
// Concrete generic method pointers
var genericMethodPointers = Image.ReadMappedArray<ulong>(CodeRegistration.genericMethodPointers, (int) CodeRegistration.genericMethodPointersCount);
var genericMethodTable = Image.ReadMappedArray<Il2CppGenericMethodFunctionsDefinitions>(MetadataRegistration.genericMethodTable, (int) MetadataRegistration.genericMethodTableCount);
var genericMethodPointers = Image.ReadMappedUWordArray(CodeRegistration.GenericMethodPointers, (int) CodeRegistration.GenericMethodPointersCount);
var genericMethodTable = Image.ReadMappedVersionedObjectArray<Il2CppGenericMethodFunctionsDefinitions>(MetadataRegistration.GenericMethodTable, (int) MetadataRegistration.GenericMethodTableCount);
foreach (var tableEntry in genericMethodTable) {
GenericMethodPointers.Add(MethodSpecs[tableEntry.genericMethodIndex], genericMethodPointers[tableEntry.indices.methodIndex]);
GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.genericMethodIndex], tableEntry.indices.invokerIndex);
GenericMethodPointers.Add(MethodSpecs[tableEntry.GenericMethodIndex], genericMethodPointers[tableEntry.Indices.MethodIndex]);
GenericMethodInvokerIndices.Add(MethodSpecs[tableEntry.GenericMethodIndex], tableEntry.Indices.InvokerIndex);
}
TypeDefinitionSizes = Image.ReadMappedObjectPointerArray<Il2CppTypeDefinitionSizes>(
MetadataRegistration.typeDefinitionsSizes, (int) MetadataRegistration.typeDefinitionsSizesCount);
TypeDefinitionSizes = Image.ReadMappedVersionedObjectPointerArray<Il2CppTypeDefinitionSizes>(
MetadataRegistration.TypeDefinitionsSizes, (int) MetadataRegistration.TypeDefinitionsSizesCount);
// Plugin hook to pre-process binary
isModified |= PluginHooks.PostProcessBinary(this).IsStreamModified;

View File

@@ -1,306 +0,0 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
// From class-internals.h / il2cpp-class-internals.h
public class Il2CppCodeRegistration
{
// Moved to Il2CppCodeGenModule in v24.2
[Version(Max = 24.1)]
public ulong methodPointersCount;
[Version(Max = 24.1)]
public ulong pmethodPointers;
public ulong reversePInvokeWrapperCount; // (was renamed from delegateWrappersFromNativeToManagedCount in v22)
public ulong reversePInvokeWrappers; // (was renamed from delegateWrappersFromNativeToManaged in v22)
// Removed in metadata v23
[Version(Max = 22)]
public ulong delegateWrappersFromManagedToNativeCount;
[Version(Max = 22)]
public ulong delegateWrappersFromManagedToNative;
[Version(Max = 22)]
public ulong marshalingFunctionsCount;
[Version(Max = 22)]
public ulong marshalingFunctions;
[Version(Min = 21, Max = 22)]
public ulong ccwMarshalingFunctionsCount;
[Version(Min = 21, Max = 22)]
public ulong ccwMarshalingFunctions;
public ulong genericMethodPointersCount;
public ulong genericMethodPointers;
[Version(Min = 24.5, Max = 24.5)]
[Version(Min = 27.1)]
public ulong genericAdjustorThunks;
public ulong invokerPointersCount;
public ulong invokerPointers;
// Removed in metadata v27
[Version(Max = 24.5)]
public long customAttributeCount;
[Version(Max = 24.5)]
public ulong customAttributeGenerators;
// Removed in metadata v23
[Version(Min = 21, Max = 22)]
public long guidCount;
[Version(Min = 21, Max = 22)]
public ulong guids; // Il2CppGuid
// Added in metadata v22
[Version(Min = 22, Max = 29)]
public ulong unresolvedVirtualCallCount;
[Version(Min = 29.1, Max = 29.1)]
[Version(Min = 31.1, Max = 31.1)]
public ulong unresolvedIndirectCallCount;
[Version(Min = 22)]
public ulong unresolvedVirtualCallPointers;
[Version(Min = 29.1, Max = 29.1)]
[Version(Min = 31.1, Max = 31.1)]
public ulong unresolvedInstanceCallPointers;
[Version(Min = 29.1, Max = 29.1)]
[Version(Min = 31.1, Max = 31.1)]
public ulong unresolvedStaticCallPointers;
// Added in metadata v23
[Version(Min = 23)]
public ulong interopDataCount;
[Version(Min = 23)]
public ulong interopData;
[Version(Min = 24.3)]
public ulong windowsRuntimeFactoryCount;
[Version(Min = 24.3)]
public ulong windowsRuntimeFactoryTable;
// Added in metadata v24.2 to replace methodPointers and methodPointersCount
[Version(Min = 24.2)]
public ulong codeGenModulesCount;
[Version(Min = 24.2)]
public ulong pcodeGenModules;
}
// Introduced in metadata v24.2 (replaces method pointers in Il2CppCodeRegistration)
public class Il2CppCodeGenModule
{
public ulong moduleName;
public ulong methodPointerCount;
public ulong methodPointers;
[Version(Min = 24.5, Max = 24.5)]
[Version(Min = 27.1)]
public long adjustorThunkCount;
[Version(Min = 24.5, Max = 24.5)]
[Version(Min = 27.1)]
public ulong adjustorThunks; //Pointer
public ulong invokerIndices;
public ulong reversePInvokeWrapperCount;
public ulong reversePInvokeWrapperIndices;
public ulong rgctxRangesCount;
public ulong rgctxRanges;
public ulong rgctxsCount;
public ulong rgctxs;
public ulong debuggerMetadata;
// Added in metadata v27
[Version(Min = 27, Max = 27.2)]
public ulong customAttributeCacheGenerator; // CustomAttributesCacheGenerator*
[Version(Min = 27)]
public ulong moduleInitializer; // Il2CppMethodPointer
[Version(Min = 27)]
public ulong staticConstructorTypeIndices; // TypeDefinitionIndex*
[Version(Min = 27)]
public ulong metadataRegistration; // Il2CppMetadataRegistration* // Per-assembly mode only
[Version(Min = 27)]
public ulong codeRegistration; // Il2CppCodeRegistration* // Per-assembly mode only
}
#pragma warning disable CS0649
public class Il2CppMetadataRegistration
{
public long genericClassesCount;
public ulong genericClasses;
public long genericInstsCount;
public ulong genericInsts;
public long genericMethodTableCount;
public ulong genericMethodTable; // Il2CppGenericMethodFunctionsDefinitions
public long typesCount;
public ulong ptypes;
public long methodSpecsCount;
public ulong methodSpecs;
[Version(Max = 16)]
public long methodReferencesCount;
[Version(Max = 16)]
public ulong methodReferences;
public long fieldOffsetsCount;
public ulong pfieldOffsets; // Changed from int32_t* to int32_t** after 5.4.0f3, before 5.5.0f3
public long typeDefinitionsSizesCount;
public ulong typeDefinitionsSizes;
[Version(Min = 19)]
public ulong metadataUsagesCount;
[Version(Min = 19)]
public ulong metadataUsages;
}
#pragma warning restore CS0649
// From blob.h / il2cpp-blob.h
public enum Il2CppTypeEnum
{
IL2CPP_TYPE_END = 0x00, /* End of List */
IL2CPP_TYPE_VOID = 0x01,
IL2CPP_TYPE_BOOLEAN = 0x02,
IL2CPP_TYPE_CHAR = 0x03,
IL2CPP_TYPE_I1 = 0x04,
IL2CPP_TYPE_U1 = 0x05,
IL2CPP_TYPE_I2 = 0x06,
IL2CPP_TYPE_U2 = 0x07,
IL2CPP_TYPE_I4 = 0x08,
IL2CPP_TYPE_U4 = 0x09,
IL2CPP_TYPE_I8 = 0x0a,
IL2CPP_TYPE_U8 = 0x0b,
IL2CPP_TYPE_R4 = 0x0c,
IL2CPP_TYPE_R8 = 0x0d,
IL2CPP_TYPE_STRING = 0x0e,
IL2CPP_TYPE_PTR = 0x0f, /* arg: <type> token */
IL2CPP_TYPE_BYREF = 0x10, /* arg: <type> token */
IL2CPP_TYPE_VALUETYPE = 0x11, /* arg: <type> token */
IL2CPP_TYPE_CLASS = 0x12, /* arg: <type> token */
IL2CPP_TYPE_VAR = 0x13, /* Generic parameter in a generic type definition, represented as number (compressed unsigned integer) number */
IL2CPP_TYPE_ARRAY = 0x14, /* type, rank, boundsCount, bound1, loCount, lo1 */
IL2CPP_TYPE_GENERICINST = 0x15, /* <type> <type-arg-count> <type-1> \x{2026} <type-n> */
IL2CPP_TYPE_TYPEDBYREF = 0x16,
IL2CPP_TYPE_I = 0x18,
IL2CPP_TYPE_U = 0x19,
IL2CPP_TYPE_FNPTR = 0x1b, /* arg: full method signature */
IL2CPP_TYPE_OBJECT = 0x1c,
IL2CPP_TYPE_SZARRAY = 0x1d, /* 0-based one-dim-array */
IL2CPP_TYPE_MVAR = 0x1e, /* Generic parameter in a generic method definition, represented as number (compressed unsigned integer) */
IL2CPP_TYPE_CMOD_REQD = 0x1f, /* arg: typedef or typeref token */
IL2CPP_TYPE_CMOD_OPT = 0x20, /* optional arg: typedef or typref token */
IL2CPP_TYPE_INTERNAL = 0x21, /* CLR internal type */
IL2CPP_TYPE_MODIFIER = 0x40, /* Or with the following types */
IL2CPP_TYPE_SENTINEL = 0x41, /* Sentinel for varargs method signature */
IL2CPP_TYPE_PINNED = 0x45, /* Local var that points to pinned object */
IL2CPP_TYPE_ENUM = 0x55, /* an enumeration */
IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff /* Type index metadata table */
}
// From metadata.h / il2cpp-runtime-metadata.h
public class Il2CppType
{
public ulong datapoint;
public ulong bits; // this should be private but we need it to be public for BinaryObjectReader to work
//public Union data { get; set; }
public virtual uint attrs => (uint) bits & 0xffff;
public virtual Il2CppTypeEnum type => (Il2CppTypeEnum)((bits >> 16) & 0xff);
public virtual uint num_mods => (uint) (bits >> 24) & 0x3f;
public virtual bool byref => ((bits >> 30) & 1) == 1;
public virtual bool pinned => ((bits >> 31) & 1) == 1;
public virtual bool valuetype => false;
/*
union
{
TypeDefinitionIndex klassIndex; // for VALUETYPE and CLASS (<v27; v27: at startup)
Il2CppMetadataTypeHandle typeHandle; // for VALUETYPE and CLASS (added in v27: at runtime)
const Il2CppType* type; // for PTR and SZARRAY
Il2CppArrayType* array; // for ARRAY
GenericParameterIndex genericParameterIndex; // for VAR and MVAR (<v27; v27: at startup)
Il2CppMetadataGenericParameterHandle genericParameterHandle; // for VAR and MVAR (added in v27: at runtime)
Il2CppGenericClass* generic_class; // for GENERICINST
}
*/
}
// Unity 2021.1 (v27.2): num_mods becomes 1 bit shorter, shifting byref and pinned left 1 bit, valuetype bit added
public class Il2CppTypeV272 : Il2CppType
{
public override uint num_mods => (uint) (bits >> 24) & 0x1f;
public override bool byref => ((bits >> 29) & 1) == 1;
public override bool pinned => ((bits >> 30) & 1) == 1;
public override bool valuetype => ((bits >> 31) & 1) == 1;
}
public class Il2CppGenericClass
{
[Version(Max = 24.5)]
public long typeDefinitionIndex; /* the generic type definition */
[Version(Min = 27)]
public ulong type; // Il2CppType* /* the generic type definition */
public Il2CppGenericContext context; /* a context that contains the type instantiation doesn't contain any method instantiation */
public ulong cached_class; /* if present, the Il2CppClass corresponding to the instantiation. */
}
public class Il2CppGenericContext
{
/* The instantiation corresponding to the class generic parameters */
public ulong class_inst;
/* The instantiation corresponding to the method generic parameters */
public ulong method_inst;
}
public class Il2CppGenericInst
{
public ulong type_argc;
public ulong type_argv;
}
public class Il2CppArrayType
{
public ulong etype;
public byte rank;
public byte numsizes;
public byte numlobounds;
public ulong sizes;
public ulong lobounds;
}
public class Il2CppMethodSpec
{
public int methodDefinitionIndex;
public int classIndexIndex;
public int methodIndexIndex;
}
public class Il2CppGenericMethodFunctionsDefinitions
{
public int genericMethodIndex;
public Il2CppGenericMethodIndices indices;
}
public class Il2CppGenericMethodIndices
{
public int methodIndex;
public int invokerIndex;
[Version(Min = 24.5, Max = 24.5)]
[Version(Min = 27.1)]
public int adjustorThunk;
}
public class Il2CppTypeDefinitionSizes
{
public uint instanceSize;
public int nativeSize;
public uint staticFieldsSize;
public uint threadStaticFieldsSize;
}
}

View File

@@ -4,14 +4,20 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using Il2CppInspector.Utils;
using NoisyCowStudios.Bin2Object;
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using VersionedSerialization;
namespace Il2CppInspector
{
@@ -31,42 +37,44 @@ namespace Il2CppInspector
public List<MetadataUsage> MetadataUsages { get; }
// Shortcuts
public double Version => Math.Max(Metadata.Version, Binary.Image.Version);
public StructVersion Version => Metadata.Version > Binary.Image.Version
? Metadata.Version
: Binary.Image.Version;
public Dictionary<int, string> Strings => Metadata.Strings;
public string[] StringLiterals => Metadata.StringLiterals;
public Il2CppTypeDefinition[] TypeDefinitions => Metadata.Types;
public Il2CppAssemblyDefinition[] Assemblies => Metadata.Assemblies;
public Il2CppImageDefinition[] Images => Metadata.Images;
public Il2CppMethodDefinition[] Methods => Metadata.Methods;
public Il2CppParameterDefinition[] Params => Metadata.Params;
public Il2CppFieldDefinition[] Fields => Metadata.Fields;
public Il2CppPropertyDefinition[] Properties => Metadata.Properties;
public Il2CppEventDefinition[] Events => Metadata.Events;
public Il2CppGenericContainer[] GenericContainers => Metadata.GenericContainers;
public Il2CppGenericParameter[] GenericParameters => Metadata.GenericParameters;
public int[] GenericConstraintIndices => Metadata.GenericConstraintIndices;
public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges => Metadata.AttributeTypeRanges;
public Il2CppCustomAttributeDataRange[] AttributeDataRanges => Metadata.AttributeDataRanges;
public Il2CppInterfaceOffsetPair[] InterfaceOffsets => Metadata.InterfaceOffsets;
public int[] InterfaceUsageIndices => Metadata.InterfaceUsageIndices;
public int[] NestedTypeIndices => Metadata.NestedTypeIndices;
public int[] AttributeTypeIndices => Metadata.AttributeTypeIndices;
public uint[] VTableMethodIndices => Metadata.VTableMethodIndices;
public Il2CppFieldRef[] FieldRefs => Metadata.FieldRefs;
public ImmutableArray<Il2CppTypeDefinition> TypeDefinitions => Metadata.Types;
public ImmutableArray<Il2CppAssemblyDefinition> Assemblies => Metadata.Assemblies;
public ImmutableArray<Il2CppImageDefinition> Images => Metadata.Images;
public ImmutableArray<Il2CppMethodDefinition> Methods => Metadata.Methods;
public ImmutableArray<Il2CppParameterDefinition> Params => Metadata.Params;
public ImmutableArray<Il2CppFieldDefinition> Fields => Metadata.Fields;
public ImmutableArray<Il2CppPropertyDefinition> Properties => Metadata.Properties;
public ImmutableArray<Il2CppEventDefinition> Events => Metadata.Events;
public ImmutableArray<Il2CppGenericContainer> GenericContainers => Metadata.GenericContainers;
public ImmutableArray<Il2CppGenericParameter> GenericParameters => Metadata.GenericParameters;
public ImmutableArray<int> GenericConstraintIndices => Metadata.GenericConstraintIndices;
public ImmutableArray<Il2CppCustomAttributeTypeRange> AttributeTypeRanges => Metadata.AttributeTypeRanges;
public ImmutableArray<Il2CppCustomAttributeDataRange> AttributeDataRanges => Metadata.AttributeDataRanges;
public ImmutableArray<Il2CppInterfaceOffsetPair> InterfaceOffsets => Metadata.InterfaceOffsets;
public ImmutableArray<int> InterfaceUsageIndices => Metadata.InterfaceUsageIndices;
public ImmutableArray<int> NestedTypeIndices => Metadata.NestedTypeIndices;
public ImmutableArray<int> AttributeTypeIndices => Metadata.AttributeTypeIndices;
public ImmutableArray<uint> VTableMethodIndices => Metadata.VTableMethodIndices;
public ImmutableArray<Il2CppFieldRef> FieldRefs => Metadata.FieldRefs;
public Dictionary<int, (ulong, object)> FieldDefaultValue { get; } = new Dictionary<int, (ulong, object)>();
public Dictionary<int, (ulong, object)> ParameterDefaultValue { get; } = new Dictionary<int, (ulong, object)>();
public List<long> FieldOffsets { get; }
public List<Il2CppType> TypeReferences => Binary.TypeReferences;
public ImmutableArray<Il2CppType> TypeReferences => Binary.TypeReferences;
public Dictionary<ulong, int> TypeReferenceIndicesByAddress => Binary.TypeReferenceIndicesByAddress;
public List<Il2CppGenericInst> GenericInstances => Binary.GenericInstances;
public ImmutableArray<Il2CppGenericInst> GenericInstances => Binary.GenericInstances;
public Dictionary<string, Il2CppCodeGenModule> Modules => Binary.Modules;
public ulong[] CustomAttributeGenerators { get; }
public ulong[] MethodInvokePointers { get; }
public Il2CppMethodSpec[] MethodSpecs => Binary.MethodSpecs;
public ImmutableArray<Il2CppMethodSpec> MethodSpecs => Binary.MethodSpecs;
public Dictionary<Il2CppMethodSpec, ulong> GenericMethodPointers { get; }
public Dictionary<Il2CppMethodSpec, int> GenericMethodInvokerIndices => Binary.GenericMethodInvokerIndices;
public List<Il2CppTypeDefinitionSizes> TypeDefinitionSizes => Binary.TypeDefinitionSizes;
public ImmutableArray<Il2CppTypeDefinitionSizes> TypeDefinitionSizes => Binary.TypeDefinitionSizes;
// TODO: Finish all file access in the constructor and eliminate the need for this
public IFileFormatStream BinaryImage => Binary.Image;
@@ -77,7 +85,7 @@ namespace Il2CppInspector
return (0ul, null);
// Get pointer in binary to default value
var pValue = Metadata.Header.fieldAndParameterDefaultValueDataOffset + dataIndex;
var pValue = Metadata.FieldAndParameterDefaultValueDataOffset + dataIndex;
var typeRef = TypeReferences[typeIndex];
// Default value is null
@@ -85,7 +93,7 @@ namespace Il2CppInspector
return (0ul, null);
Metadata.Position = pValue;
var value = BlobReader.GetConstantValueFromBlob(this, typeRef.type, Metadata);
var value = BlobReader.GetConstantValueFromBlob(this, typeRef.Type, Metadata);
return ((ulong) pValue, value);
}
@@ -93,21 +101,21 @@ namespace Il2CppInspector
private List<MetadataUsage> buildMetadataUsages()
{
// No metadata usages for versions < 19
if (Version < 19)
if (Version < MetadataVersions.V190)
return null;
// Metadata usages are lazily initialized during runtime for versions >= 27
if (Version >= 27)
if (Version >= MetadataVersions.V270)
return buildLateBindingMetadataUsages();
// Version >= 19 && < 27
var usages = new Dictionary<uint, MetadataUsage>();
var usages = new Dictionary<uint, uint>();
foreach (var metadataUsageList in Metadata.MetadataUsageLists)
{
for (var i = 0; i < metadataUsageList.count; i++)
for (var i = 0; i < metadataUsageList.Count; i++)
{
var metadataUsagePair = Metadata.MetadataUsagePairs[metadataUsageList.start + i];
usages.TryAdd(metadataUsagePair.destinationindex, MetadataUsage.FromEncodedIndex(this, metadataUsagePair.encodedSourceIndex));
var metadataUsagePair = Metadata.MetadataUsagePairs[metadataUsageList.Start + i];
usages.TryAdd(metadataUsagePair.DestinationIndex, metadataUsagePair.EncodedSourceIndex);
}
}
@@ -115,11 +123,13 @@ namespace Il2CppInspector
// Unfortunately the value supplied in MetadataRegistration.matadataUsagesCount seems to be incorrect,
// so we have to calculate the correct number of usages above before reading the usage address list from the binary
var count = usages.Keys.Max() + 1;
var addresses = Binary.Image.ReadMappedArray<ulong>(Binary.MetadataRegistration.metadataUsages, (int) count);
foreach (var usage in usages)
usage.Value.SetAddress(addresses[usage.Key]);
var addresses = Binary.Image.ReadMappedUWordArray(Binary.MetadataRegistration.MetadataUsages, (int) count);
return usages.Values.ToList();
var metadataUsages = new List<MetadataUsage>();
foreach (var (index, encodedUsage) in usages)
metadataUsages.Add(MetadataUsage.FromEncodedIndex(this, encodedUsage, addresses[index]));
return metadataUsages;
}
private List<MetadataUsage> buildLateBindingMetadataUsages()
@@ -142,10 +152,7 @@ namespace Il2CppInspector
if (CheckMetadataUsageSanity(usage)
&& BinaryImage.TryMapFileOffsetToVA(i * ((uint)BinaryImage.Bits / 8), out var va))
{
usage.SetAddress(va);
usages.Add(usage);
}
usages.Add(MetadataUsage.FromEncodedIndex(this, encodedToken, va));
}
}
@@ -155,7 +162,7 @@ namespace Il2CppInspector
{
return usage.Type switch
{
MetadataUsageType.TypeInfo or MetadataUsageType.Type => TypeReferences.Count > usage.SourceIndex,
MetadataUsageType.TypeInfo or MetadataUsageType.Type => TypeReferences.Length > usage.SourceIndex,
MetadataUsageType.MethodDef => Methods.Length > usage.SourceIndex,
MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva => FieldRefs.Length > usage.SourceIndex,
MetadataUsageType.StringLiteral => StringLiterals.Length > usage.SourceIndex,
@@ -180,11 +187,11 @@ namespace Il2CppInspector
// Get all field default values
foreach (var fdv in Metadata.FieldDefaultValues)
FieldDefaultValue.Add(fdv.fieldIndex, ((ulong,object)) getDefaultValue(fdv.typeIndex, fdv.dataIndex));
FieldDefaultValue.Add(fdv.FieldIndex, ((ulong,object)) getDefaultValue(fdv.TypeIndex, fdv.DataIndex));
// Get all parameter default values
foreach (var pdv in Metadata.ParameterDefaultValues)
ParameterDefaultValue.Add(pdv.parameterIndex, ((ulong,object)) getDefaultValue(pdv.typeIndex, pdv.dataIndex));
ParameterDefaultValue.Add(pdv.ParameterIndex, ((ulong,object)) getDefaultValue(pdv.TypeIndex, pdv.DataIndex));
// Get all field offsets
if (Binary.FieldOffsets != null) {
@@ -197,19 +204,21 @@ namespace Il2CppInspector
for (var i = 0; i < TypeDefinitions.Length; i++) {
var def = TypeDefinitions[i];
var pFieldOffsets = Binary.FieldOffsetPointers[i];
if (pFieldOffsets != 0) {
bool available = true;
if (pFieldOffsets != 0)
{
// If the target address range is not mapped in the file, assume zeroes
try {
BinaryImage.Position = BinaryImage.MapVATR((ulong) pFieldOffsets);
if (BinaryImage.TryMapVATR((ulong)pFieldOffsets, out var fieldOffsetPosition))
{
BinaryImage.Position = fieldOffsetPosition;
var fieldOffsets = BinaryImage.ReadArray<uint>(def.FieldCount);
for (var fieldIndex = 0; fieldIndex < def.FieldCount; fieldIndex++)
offsets.Add(def.FieldIndex + fieldIndex, fieldOffsets[fieldIndex]);
}
catch (InvalidOperationException) {
available = false;
else
{
for (var fieldIndex = 0; fieldIndex < def.FieldCount; fieldIndex++)
offsets.Add(def.FieldIndex + fieldIndex, 0);
}
for (var f = 0; f < def.field_count; f++)
offsets.Add(def.fieldStart + f, available? BinaryImage.ReadUInt32() : 0);
}
}
@@ -217,20 +226,20 @@ namespace Il2CppInspector
}
// Build list of custom attribute generators
if (Version < 27)
if (Version < MetadataVersions.V270)
CustomAttributeGenerators = Binary.CustomAttributeGenerators;
else if (Version < 29)
else if (Version < MetadataVersions.V290)
{
var cagCount = Images.Sum(i => i.customAttributeCount);
var cagCount = Images.Sum(i => i.CustomAttributeCount);
CustomAttributeGenerators = new ulong[cagCount];
foreach (var image in Images)
{
// Get CodeGenModule for this image
var codeGenModule = Binary.Modules[Strings[image.nameIndex]];
var cags = BinaryImage.ReadMappedWordArray(codeGenModule.customAttributeCacheGenerator,
(int) image.customAttributeCount);
cags.CopyTo(CustomAttributeGenerators, image.customAttributeStart);
var codeGenModule = Binary.Modules[Strings[image.NameIndex]];
var cags = BinaryImage.ReadMappedWordArray(codeGenModule.CustomAttributeCacheGenerator,
(int) image.CustomAttributeCount);
cags.CopyTo(CustomAttributeGenerators, image.CustomAttributeStart);
}
}
else
@@ -243,7 +252,7 @@ namespace Il2CppInspector
// Get sorted list of function pointers from all sources
// TODO: This does not include IL2CPP API functions
var sortedFunctionPointers = (Version <= 24.1)?
var sortedFunctionPointers = (Version <= MetadataVersions.V241) ?
Binary.GlobalMethodPointers.Select(getDecodedAddress).ToList() :
Binary.ModuleMethodPointers.SelectMany(module => module.Value).Select(getDecodedAddress).ToList();
@@ -261,20 +270,20 @@ namespace Il2CppInspector
FunctionAddresses.Add(sortedFunctionPointers[^1], sortedFunctionPointers[^1]);
// Organize custom attribute indices
if (Version >= 24.1) {
if (Version >= MetadataVersions.V241) {
AttributeIndicesByToken = [];
foreach (var image in Images)
{
var attsByToken = new Dictionary<uint, int>();
for (int i = 0; i < image.customAttributeCount; i++)
for (int i = 0; i < image.CustomAttributeCount; i++)
{
var index = image.customAttributeStart + i;
var token = Version >= 29 ? AttributeDataRanges[index].token : AttributeTypeRanges[index].token;
var index = image.CustomAttributeStart + i;
var token = Version >= MetadataVersions.V290 ? AttributeDataRanges[index].Token : AttributeTypeRanges[index].Token;
attsByToken.Add(token, index);
}
if (attsByToken.Count > 0)
AttributeIndicesByToken.Add(image.customAttributeStart, attsByToken);
AttributeIndicesByToken.Add(image.CustomAttributeStart, attsByToken);
}
}
@@ -288,20 +297,20 @@ namespace Il2CppInspector
// Get a method pointer if available
public (ulong Start, ulong End)? GetMethodPointer(Il2CppCodeGenModule module, Il2CppMethodDefinition methodDef) {
// Find method pointer
if (methodDef.methodIndex < 0)
if (methodDef.MethodIndex < 0)
return null;
ulong start = 0;
// Global method pointer array
if (Version <= 24.1) {
start = Binary.GlobalMethodPointers[methodDef.methodIndex];
if (Version <= MetadataVersions.V241) {
start = Binary.GlobalMethodPointers[methodDef.MethodIndex];
}
// Per-module method pointer array uses the bottom 24 bits of the method's metadata token
// Derived from il2cpp::vm::MetadataCache::GetMethodPointer
if (Version >= 24.2) {
var method = (methodDef.token & 0xffffff);
if (Version >= MetadataVersions.V242) {
var method = (methodDef.Token & 0xffffff);
if (method == 0)
return null;
@@ -335,19 +344,19 @@ namespace Il2CppInspector
// Get a method invoker index from a method definition
public int GetInvokerIndex(Il2CppCodeGenModule module, Il2CppMethodDefinition methodDef) {
if (Version <= 24.1) {
return methodDef.invokerIndex;
if (Version <= MetadataVersions.V241) {
return methodDef.InvokerIndex;
}
// Version >= 24.2
var methodInModule = (methodDef.token & 0xffffff);
return Binary.MethodInvokerIndices[module][methodInModule - 1];
var methodInModule = (methodDef.Token & 0xffffff);
return Binary.MethodInvokerIndices[module][(int)methodInModule - 1];
}
public MetadataUsage[] GetVTable(Il2CppTypeDefinition definition) {
MetadataUsage[] res = new MetadataUsage[definition.vtable_count];
for (int i = 0; i < definition.vtable_count; i++) {
var encodedIndex = VTableMethodIndices[definition.vtableStart + i];
MetadataUsage[] res = new MetadataUsage[definition.VTableCount];
for (int i = 0; i < definition.VTableCount; i++) {
var encodedIndex = VTableMethodIndices[definition.VTableIndex + i];
MetadataUsage usage = MetadataUsage.FromEncodedIndex(this, encodedIndex);
if (usage.SourceIndex != 0)
res[i] = usage;
@@ -402,7 +411,7 @@ namespace Il2CppInspector
if (metadataFile != null) {
// Extract the metadata file to memory
if (!silent)
Console.WriteLine($"Extracting metadata from (archive){Path.DirectorySeparatorChar}{metadataFile.FullName}");
AnsiConsole.WriteLine($"Extracting metadata from (archive){Path.DirectorySeparatorChar}{metadataFile.FullName}");
metadataMemoryStream = new MemoryStream();
using var metadataStream = metadataFile.Open();
@@ -420,7 +429,7 @@ namespace Il2CppInspector
// IPAs will only have one binary (which may or may not be a UB covering multiple architectures)
if (ipaBinaryFolder != null) {
if (!silent)
Console.WriteLine($"Extracting binary from {zipStreams.First()}{Path.DirectorySeparatorChar}{binaryFiles.First().FullName}");
AnsiConsole.WriteLine($"Extracting binary from {zipStreams.First()}{Path.DirectorySeparatorChar}{binaryFiles.First().FullName}");
// Extract the binary file or package to memory
binaryMemoryStream = new MemoryStream();
@@ -523,7 +532,7 @@ namespace Il2CppInspector
return null;
}
Console.WriteLine("Detected metadata version " + metadata.Version);
AnsiConsole.WriteLine("Detected metadata version " + metadata.Version);
// Load the il2cpp code file (try all available file formats)
IFileFormatStream stream;
@@ -551,16 +560,16 @@ namespace Il2CppInspector
var processors = new List<Il2CppInspector>();
foreach (var image in stream.Images) {
Console.WriteLine("Container format: " + image.Format);
Console.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
Console.WriteLine("Architecture word size: {0}-bit", image.Bits);
Console.WriteLine("Instruction set: " + image.Arch);
Console.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
AnsiConsole.WriteLine("Container format: " + image.Format);
AnsiConsole.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
AnsiConsole.WriteLine("Architecture word size: {0}-bit", image.Bits);
AnsiConsole.WriteLine("Instruction set: " + image.Arch);
AnsiConsole.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
// Architecture-agnostic load attempt
try {
if (Il2CppBinary.Load(image, metadata, statusCallback) is Il2CppBinary binary) {
Console.WriteLine("IL2CPP binary version " + image.Version);
AnsiConsole.WriteLine("IL2CPP binary version " + image.Version);
processors.Add(new Il2CppInspector(binary, metadata));
}

View File

@@ -9,6 +9,9 @@ using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using VersionedSerialization;
namespace Il2CppInspector
{
@@ -120,7 +123,7 @@ namespace Il2CppInspector
// Find CodeRegistration
// >= 24.2
if (metadata.Version >= 24.2) {
if (metadata.Version >= MetadataVersions.V242) {
// < 27: mscorlib.dll is always the first CodeGenModule
// >= 27: mscorlib.dll is always the last CodeGenModule (Assembly-CSharp.dll is always the first but non-Unity builds don't have this DLL)
@@ -137,7 +140,7 @@ namespace Il2CppInspector
// Unwind from string pointer -> CodeGenModule -> CodeGenModules + x
foreach (var potentialCodeGenModules in FindAllPointerChains(imageBytes, va, 2))
{
if (metadata.Version >= 27)
if (metadata.Version >= MetadataVersions.V270)
{
for (int i = imagesCount - 1; i >= 0; i--)
{
@@ -145,7 +148,7 @@ namespace Il2CppInspector
potentialCodeGenModules - (ulong) i * ptrSize, 1))
{
var expectedImageCountPtr = potentialCodeRegistrationPtr - ptrSize;
var expectedImageCount = ptrSize == 4 ? Image.ReadMappedInt32(expectedImageCountPtr) : Image.ReadMappedInt64(expectedImageCountPtr);
var expectedImageCount = Image.ReadMappedWord(expectedImageCountPtr);
if (expectedImageCount == imagesCount)
return potentialCodeRegistrationPtr;
}
@@ -203,24 +206,43 @@ namespace Il2CppInspector
return (0, 0);
var codeGenEndPtr = codeRegVa + ptrSize;
// pCodeGenModules is the last field in CodeRegistration so we subtract the size of one pointer from the struct size
codeRegistration = codeRegVa - ((ulong) metadata.Sizeof(typeof(Il2CppCodeRegistration), Image.Version, Image.Bits / 8) - ptrSize);
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
// In v24.3, windowsRuntimeFactoryTable collides with codeGenModules. So far no samples have had windowsRuntimeFactoryCount > 0;
// if this changes we'll have to get smarter about disambiguating these two.
var cr = Image.ReadMappedObject<Il2CppCodeRegistration>(codeRegistration);
var cr = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
if (Image.Version == 24.2 && cr.interopDataCount == 0) {
Image.Version = 24.3;
codeRegistration -= ptrSize * 2; // two extra words for WindowsRuntimeFactory
if (Image.Version == MetadataVersions.V242 && cr.InteropDataCount == 0) {
Image.Version = MetadataVersions.V243;
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
}
if (Image.Version == 27 && cr.reversePInvokeWrapperCount > 0x30000)
if (Image.Version == MetadataVersions.V270 && cr.ReversePInvokeWrapperCount > 0x30000)
{
// If reversePInvokeWrapperCount is a pointer, then it's because we're actually on 27.1 and there's a genericAdjustorThunks pointer interfering.
// We need to bump version to 27.1 and back up one more pointer.
Image.Version = 27.1;
codeRegistration -= ptrSize;
Image.Version = MetadataVersions.V271;
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
cr = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
}
// genericAdjustorThunks was inserted before invokerPointersCount in 24.5 and 27.1
// pointer expected if we need to bump version
if (Image.Version == MetadataVersions.V244 &&
(cr.InvokerPointersCount > 0x50000 || cr.ReversePInvokeWrapperCount > cr.ReversePInvokeWrappers))
{
Image.Version = MetadataVersions.V245;
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
cr = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
}
if ((Image.Version == MetadataVersions.V290 || Image.Version == MetadataVersions.V310) &&
cr.GenericMethodPointersCount >= cr.GenericMethodPointers)
{
Image.Version = new StructVersion(Image.Version.Major, 0, MetadataVersions.Tag2022);
codeRegistration = codeGenEndPtr - (ulong)Il2CppCodeRegistration.Size(Image.Version, Image.Bits == 32);
}
}
@@ -228,18 +250,15 @@ namespace Il2CppInspector
// <= 24.1
else {
// The first item in CodeRegistration is the total number of method pointers
vas = FindAllMappedWords(imageBytes, (ulong) metadata.Methods.Count(m => (uint) m.methodIndex != 0xffff_ffff));
if (!vas.Any())
return (0, 0);
vas = FindAllMappedWords(imageBytes, (ulong) metadata.Methods.Count(m => (uint) m.MethodIndex != 0xffff_ffff));
// The count of method pointers will be followed some bytes later by
// the count of custom attribute generators; the distance between them
// depends on the il2cpp version so we just use ReadMappedObject to simplify the math
foreach (var va in vas) {
var cr = Image.ReadMappedObject<Il2CppCodeRegistration>(va);
var cr = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(va);
if (cr.customAttributeCount == metadata.AttributeTypeRanges.Length)
if (cr.CustomAttributeCount == metadata.AttributeTypeRanges.Length)
codeRegistration = va;
}
@@ -253,42 +272,30 @@ namespace Il2CppInspector
// Find TypeDefinitionsSizesCount (4th last field) then work back to the start of the struct
// This saves us from guessing where metadataUsagesCount is later
var mrSize = (ulong) metadata.Sizeof(typeof(Il2CppMetadataRegistration), Image.Version, Image.Bits / 8);
var mrSize = (ulong)Il2CppMetadataRegistration.Size(Image.Version, Image.Bits == 32);
var typesLength = (ulong) metadata.Types.Length;
vas = FindAllMappedWords(imageBytes, typesLength).Select(a => a - mrSize + ptrSize * 4);
// >= 19 && < 27
if (Image.Version < 27)
foreach (var va in vas) {
var mr = Image.ReadMappedObject<Il2CppMetadataRegistration>(va);
if (mr.metadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length)
metadataRegistration = va;
}
// plagiarism. noun - https://www.lexico.com/en/definition/plagiarism
// the practice of taking someone else's work or ideas and passing them off as one's own.
// Synonyms: copying, piracy, theft, strealing, infringement of copyright
// >= 27
else {
// We're going to just sanity check all of the fields
// All counts should be under a certain threshold
// All pointers should be mappable to the binary
var mrFieldCount = mrSize / (ulong) (Image.Bits / 8);
foreach (var va in vas) {
var mrWords = Image.ReadMappedWordArray(va, (int) mrFieldCount);
// Even field indices are counts, odd field indices are pointers
bool ok = true;
for (var i = 0; i < mrWords.Length && ok; i++) {
ok = i % 2 == 0 || Image.TryMapVATR((ulong) mrWords[i], out _);
}
if (ok)
// >= 19
// Luke: Previously, a check comparing MetadataUsagesCount was used here,
// but I know of at least one binary where this will break detection.
// Testing showed that we can just use the same heuristic used for v27+
// on older versions as well, so we'll just use it for all cases.
if (Image.Version >= MetadataVersions.V190)
{
foreach (var va in vas)
{
var mr = Image.ReadMappedVersionedObject<Il2CppMetadataRegistration>(va);
if (mr.TypeDefinitionsSizesCount == metadata.Types.Length
&& mr.FieldOffsetsCount == metadata.Types.Length)
{
metadataRegistration = va;
break;
}
}
}
if (metadataRegistration == 0)
return (0, 0);

View File

@@ -5,46 +5,53 @@
All rights reserved.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using NoisyCowStudios.Bin2Object;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using Il2CppInspector.Next;
using Il2CppInspector.Next.Metadata;
using VersionedSerialization;
namespace Il2CppInspector
{
public class Metadata : BinaryObjectStream
public class Metadata : BinaryObjectStreamReader
{
public Il2CppGlobalMetadataHeader Header { get; set; }
public Il2CppAssemblyDefinition[] Assemblies { get; set; }
public Il2CppImageDefinition[] Images { get; set; }
public Il2CppTypeDefinition[] Types { get; set; }
public Il2CppMethodDefinition[] Methods { get; set; }
public Il2CppParameterDefinition[] Params { get; set; }
public Il2CppFieldDefinition[] Fields { get; set; }
public Il2CppFieldDefaultValue[] FieldDefaultValues { get; set; }
public Il2CppParameterDefaultValue[] ParameterDefaultValues { get; set; }
public Il2CppPropertyDefinition[] Properties { get; set; }
public Il2CppEventDefinition[] Events { get; set; }
public Il2CppGenericContainer[] GenericContainers { get; set; }
public Il2CppGenericParameter[] GenericParameters { get; set; }
public Il2CppCustomAttributeTypeRange[] AttributeTypeRanges { get; set; }
public Il2CppCustomAttributeDataRange[] AttributeDataRanges { get; set; }
public Il2CppInterfaceOffsetPair[] InterfaceOffsets { get; set; }
public Il2CppMetadataUsageList[] MetadataUsageLists { get; set; }
public Il2CppMetadataUsagePair[] MetadataUsagePairs { get; set; }
public Il2CppFieldRef[] FieldRefs { get; set; }
public ImmutableArray<Il2CppAssemblyDefinition> Assemblies { get; set; }
public ImmutableArray<Il2CppImageDefinition> Images { get; set; }
public ImmutableArray<Il2CppTypeDefinition> Types { get; set; }
public ImmutableArray<Il2CppMethodDefinition> Methods { get; set; }
public ImmutableArray<Il2CppParameterDefinition> Params { get; set; }
public ImmutableArray<Il2CppFieldDefinition> Fields { get; set; }
public ImmutableArray<Il2CppFieldDefaultValue> FieldDefaultValues { get; set; }
public ImmutableArray<Il2CppParameterDefaultValue> ParameterDefaultValues { get; set; }
public ImmutableArray<Il2CppPropertyDefinition> Properties { get; set; }
public ImmutableArray<Il2CppEventDefinition> Events { get; set; }
public ImmutableArray<Il2CppGenericContainer> GenericContainers { get; set; }
public ImmutableArray<Il2CppGenericParameter> GenericParameters { get; set; }
public ImmutableArray<Il2CppCustomAttributeTypeRange> AttributeTypeRanges { get; set; }
public ImmutableArray<Il2CppCustomAttributeDataRange> AttributeDataRanges { get; set; }
public ImmutableArray<Il2CppInterfaceOffsetPair> InterfaceOffsets { get; set; }
public ImmutableArray<Il2CppMetadataUsageList> MetadataUsageLists { get; set; }
public ImmutableArray<Il2CppMetadataUsagePair> MetadataUsagePairs { get; set; }
public ImmutableArray<Il2CppFieldRef> FieldRefs { get; set; }
public int[] InterfaceUsageIndices { get; set; }
public int[] NestedTypeIndices { get; set; }
public int[] AttributeTypeIndices { get; set; }
public int[] GenericConstraintIndices { get; set; }
public uint[] VTableMethodIndices { get; set; }
public ImmutableArray<int> InterfaceUsageIndices { get; set; }
public ImmutableArray<int> NestedTypeIndices { get; set; }
public ImmutableArray<int> AttributeTypeIndices { get; set; }
public ImmutableArray<int> GenericConstraintIndices { get; set; }
public ImmutableArray<uint> VTableMethodIndices { get; set; }
public string[] StringLiterals { get; set; }
public Dictionary<int, string> Strings { get; private set; } = new Dictionary<int, string>();
public int FieldAndParameterDefaultValueDataOffset => Version >= MetadataVersions.V380
? Header.FieldAndParameterDefaultValueData.Offset
: Header.FieldAndParameterDefaultValueDataOffset;
public int AttributeDataOffset => Version >= MetadataVersions.V380
? Header.AttributeData.Offset
: Header.AttributeDataOffset;
public Dictionary<int, string> Strings { get; private set; } = [];
// Set if something in the metadata has been modified / decrypted
public bool IsModified { get; private set; } = false;
@@ -78,22 +85,72 @@ namespace Il2CppInspector
StatusUpdate("Processing metadata");
// Read metadata header
Header = ReadObject<Il2CppGlobalMetadataHeader>(0);
Header = ReadVersionedObject<Il2CppGlobalMetadataHeader>(0);
// Check for correct magic bytes
if (Header.signature != Il2CppConstants.MetadataSignature) {
if (!Header.SanityValid) {
throw new InvalidOperationException("The supplied metadata file is not valid.");
}
// Set object versioning for Bin2Object from metadata version
Version = Header.version;
Version = new StructVersion(Header.Version);
if (Version < 16 || Version > 31) {
throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.version}).");
if (Version < MetadataVersions.V160 || Version > MetadataVersions.V390) {
throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.Version}).");
}
// Rewind and read metadata header with the correct version settings
Header = ReadObject<Il2CppGlobalMetadataHeader>(0);
Header = ReadVersionedObject<Il2CppGlobalMetadataHeader>(0);
// Setup the proper index sizes for metadata v38+
if (Version >= MetadataVersions.V380)
{
static int GetIndexSize(int elementCount)
{
return elementCount switch
{
<= byte.MaxValue => sizeof(byte),
<= ushort.MaxValue => sizeof(ushort),
_ => sizeof(int)
};
}
var typeDefinitionIndexSize = GetIndexSize(Header.TypeDefinitions.Count);
var genericContainerIndexSize = GetIndexSize(Header.GenericContainers.Count);
var tag = $"{TypeDefinitionIndex.TagPrefix}{typeDefinitionIndexSize}"
+ $"_{GenericContainerIndex.TagPrefix}{genericContainerIndexSize}";
var tempVersion = new StructVersion(Version.Major, Version.Minor, tag);
// now we need to derive the size for TypeIndex.
// this is normally done through s_Il2CppMetadataRegistration->typesCount, but we don't want to use the binary for this
// as we do not have it available at this point.
// thankfully, we can just guess the size based off the three available options and the known total size of
// a type entry that uses TypeIndex.
var expectedEventDefinitionSize = Header.Events.SectionSize / Header.Events.Count;
var maxEventDefinitionSize = Il2CppEventDefinition.Size(tempVersion);
int typeIndexSize;
if (expectedEventDefinitionSize == maxEventDefinitionSize)
typeIndexSize = sizeof(int);
else if (expectedEventDefinitionSize == maxEventDefinitionSize - 2)
typeIndexSize = sizeof(ushort);
else if (expectedEventDefinitionSize == maxEventDefinitionSize - 3)
typeIndexSize = sizeof(byte);
else
throw new InvalidOperationException("Could not determine TypeIndex size based on the metadata header");
var fullTag = $"{tag}_{TypeIndex.TagPrefix}{typeIndexSize}";
if (Version >= MetadataVersions.V390)
{
var parameterIndexSize = GetIndexSize(Header.Parameters.Count);
fullTag += $"_{ParameterIndex.TagPrefix}{parameterIndexSize}";
}
Version = new StructVersion(Version.Major, Version.Minor, fullTag);
}
// Sanity checking
// Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata always writes the metadata information in the same order it appears in the header,
@@ -104,110 +161,102 @@ namespace Il2CppInspector
// in the header after the sanity and version fields, and since it will always point directly to the first byte after the end of the header,
// we can use this value to determine the actual header length and therefore narrow down the metadata version to 24.0/24.1 or 24.2.
if (!pluginResult.SkipValidation) {
var realHeaderLength = Header.stringLiteralOffset;
if (!pluginResult.SkipValidation)
{
var realHeaderLength = Version >= MetadataVersions.V380
? Header.StringLiterals.Offset
: Header.StringLiteralOffset;
if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) {
if (Version == 24.0) {
Version = 24.2;
Header = ReadObject<Il2CppGlobalMetadataHeader>(0);
if (realHeaderLength != Sizeof<Il2CppGlobalMetadataHeader>()) {
if (Version == MetadataVersions.V240) {
Version = MetadataVersions.V242;
Header = ReadVersionedObject<Il2CppGlobalMetadataHeader>(0);
}
}
if (realHeaderLength != Sizeof(typeof(Il2CppGlobalMetadataHeader))) {
if (realHeaderLength != Sizeof<Il2CppGlobalMetadataHeader>()) {
throw new InvalidOperationException("Could not verify the integrity of the metadata file or accurately identify the metadata sub-version");
}
}
// Load all the relevant metadata using offsets provided in the header
if (Version >= 16)
Images = ReadArray<Il2CppImageDefinition>(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition)));
if (Version >= MetadataVersions.V160)
Images = ReadMetadataArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize, Header.Images);
// As an additional sanity check, all images in the metadata should have Mono.Cecil.MetadataToken == 1
// In metadata v24.1, two extra fields were added which will cause the below test to fail.
// In that case, we can then adjust the version number and reload
// Tokens were introduced in v19 - we don't bother testing earlier versions
if (Version >= 19 && Images.Any(x => x.token != 1))
if (Version == 24.0) {
Version = 24.1;
if (Version >= MetadataVersions.V190 && Images.Any(x => x.Token != 1))
if (Version == MetadataVersions.V240) {
Version = MetadataVersions.V241;
// No need to re-read the header, it's the same for both sub-versions
Images = ReadArray<Il2CppImageDefinition>(Header.imagesOffset, Header.imagesCount / Sizeof(typeof(Il2CppImageDefinition)));
Images = ReadMetadataArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize, Header.Images);
if (Images.Any(x => x.token != 1))
if (Images.Any(x => x.Token != 1))
throw new InvalidOperationException("Could not verify the integrity of the metadata file image list");
}
Types = ReadArray<Il2CppTypeDefinition>(Header.typeDefinitionsOffset, Header.typeDefinitionsCount / Sizeof(typeof(Il2CppTypeDefinition)));
Methods = ReadArray<Il2CppMethodDefinition>(Header.methodsOffset, Header.methodsCount / Sizeof(typeof(Il2CppMethodDefinition)));
Params = ReadArray<Il2CppParameterDefinition>(Header.parametersOffset, Header.parametersCount / Sizeof(typeof(Il2CppParameterDefinition)));
Fields = ReadArray<Il2CppFieldDefinition>(Header.fieldsOffset, Header.fieldsCount / Sizeof(typeof(Il2CppFieldDefinition)));
FieldDefaultValues = ReadArray<Il2CppFieldDefaultValue>(Header.fieldDefaultValuesOffset, Header.fieldDefaultValuesCount / Sizeof(typeof(Il2CppFieldDefaultValue)));
Properties = ReadArray<Il2CppPropertyDefinition>(Header.propertiesOffset, Header.propertiesCount / Sizeof(typeof(Il2CppPropertyDefinition)));
Events = ReadArray<Il2CppEventDefinition>(Header.eventsOffset, Header.eventsCount / Sizeof(typeof(Il2CppEventDefinition)));
InterfaceUsageIndices = ReadArray<int>(Header.interfacesOffset, Header.interfacesCount / sizeof(int));
NestedTypeIndices = ReadArray<int>(Header.nestedTypesOffset, Header.nestedTypesCount / sizeof(int));
GenericContainers = ReadArray<Il2CppGenericContainer>(Header.genericContainersOffset, Header.genericContainersCount / Sizeof(typeof(Il2CppGenericContainer)));
GenericParameters = ReadArray<Il2CppGenericParameter>(Header.genericParametersOffset, Header.genericParametersCount / Sizeof(typeof(Il2CppGenericParameter)));
GenericConstraintIndices = ReadArray<int>(Header.genericParameterConstraintsOffset, Header.genericParameterConstraintsCount / sizeof(int));
InterfaceOffsets = ReadArray<Il2CppInterfaceOffsetPair>(Header.interfaceOffsetsOffset, Header.interfaceOffsetsCount / Sizeof(typeof(Il2CppInterfaceOffsetPair)));
VTableMethodIndices = ReadArray<uint>(Header.vtableMethodsOffset, Header.vtableMethodsCount / sizeof(uint));
Types = ReadMetadataArray<Il2CppTypeDefinition>(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize, Header.TypeDefinitions);
Methods = ReadMetadataArray<Il2CppMethodDefinition>(Header.MethodsOffset, Header.MethodsSize, Header.Methods);
Params = ReadMetadataArray<Il2CppParameterDefinition>(Header.ParametersOffset, Header.ParametersSize, Header.Parameters);
Fields = ReadMetadataArray<Il2CppFieldDefinition>(Header.FieldsOffset, Header.FieldsSize, Header.Fields);
FieldDefaultValues = ReadMetadataArray<Il2CppFieldDefaultValue>(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize, Header.FieldDefaultValues);
Properties = ReadMetadataArray<Il2CppPropertyDefinition>(Header.PropertiesOffset, Header.PropertiesSize, Header.Properties);
Events = ReadMetadataArray<Il2CppEventDefinition>(Header.EventsOffset, Header.EventsSize, Header.Events);
InterfaceUsageIndices = ReadMetadataPrimitiveArray<int>(Header.InterfacesOffset, Header.InterfacesSize, Header.Interfaces);
NestedTypeIndices = ReadMetadataPrimitiveArray<int>(Header.NestedTypesOffset, Header.NestedTypesSize, Header.NestedTypes);
GenericContainers = ReadMetadataArray<Il2CppGenericContainer>(Header.GenericContainersOffset, Header.GenericContainersSize, Header.GenericContainers);
GenericParameters = ReadMetadataArray<Il2CppGenericParameter>(Header.GenericParametersOffset, Header.GenericParametersSize, Header.GenericParameters);
GenericConstraintIndices = ReadMetadataPrimitiveArray<int>(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize, Header.GenericParameterConstraints);
InterfaceOffsets = ReadMetadataArray<Il2CppInterfaceOffsetPair>(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize, Header.InterfaceOffsets);
VTableMethodIndices = ReadMetadataPrimitiveArray<uint>(Header.VTableMethodsOffset, Header.VTableMethodsSize, Header.VtableMethods);
if (Version >= 16) {
if (Version >= MetadataVersions.V160)
{
// In v24.4 hashValueIndex was removed from Il2CppAssemblyNameDefinition, which is a field in Il2CppAssemblyDefinition
// The number of images and assemblies should be the same. If they are not, we deduce that we are using v24.4
// Note the version comparison matches both 24.2 and 24.3 here since 24.3 is tested for during binary loading
var assemblyCount = Header.assembliesCount / Sizeof(typeof(Il2CppAssemblyDefinition));
var assemblyCount = Header.AssembliesSize / Sizeof<Il2CppAssemblyDefinition>();
var changedAssemblyDefStruct = false;
if ((Version == 24.1 || Version == 24.2 || Version == 24.3) && assemblyCount < Images.Length)
if ((Version == MetadataVersions.V241 || Version == MetadataVersions.V242 || Version == MetadataVersions.V243) && assemblyCount < Images.Length)
{
if (Version == 24.1)
if (Version == MetadataVersions.V241)
changedAssemblyDefStruct = true;
Version = 24.4;
Version = MetadataVersions.V244;
}
Assemblies = ReadArray<Il2CppAssemblyDefinition>(Header.assembliesOffset, Images.Length);
Assemblies = ReadMetadataArray<Il2CppAssemblyDefinition>(Header.AssembliesOffset, Header.AssembliesSize, Header.Assemblies);
if (changedAssemblyDefStruct)
Version = 24.1;
Version = MetadataVersions.V241;
ParameterDefaultValues = ReadArray<Il2CppParameterDefaultValue>(Header.parameterDefaultValuesOffset, Header.parameterDefaultValuesCount / Sizeof(typeof(Il2CppParameterDefaultValue)));
}
if (Version >= 19 && Version < 27) {
MetadataUsageLists = ReadArray<Il2CppMetadataUsageList>(Header.metadataUsageListsOffset, Header.metadataUsageListsCount / Sizeof(typeof(Il2CppMetadataUsageList)));
MetadataUsagePairs = ReadArray<Il2CppMetadataUsagePair>(Header.metadataUsagePairsOffset, Header.metadataUsagePairsCount / Sizeof(typeof(Il2CppMetadataUsagePair)));
}
if (Version >= 19) {
FieldRefs = ReadArray<Il2CppFieldRef>(Header.fieldRefsOffset, Header.fieldRefsCount / Sizeof(typeof(Il2CppFieldRef)));
}
if (Version >= 21 && Version < 29) {
AttributeTypeIndices = ReadArray<int>(Header.attributeTypesOffset, Header.attributeTypesCount / sizeof(int));
AttributeTypeRanges = ReadArray<Il2CppCustomAttributeTypeRange>(Header.attributesInfoOffset, Header.attributesInfoCount / Sizeof(typeof(Il2CppCustomAttributeTypeRange)));
ParameterDefaultValues = ReadMetadataArray<Il2CppParameterDefaultValue>(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize, Header.ParameterDefaultValues);
}
if (Version >= 29)
if (Version >= MetadataVersions.V190 && Version < MetadataVersions.V270)
{
AttributeDataRanges = ReadArray<Il2CppCustomAttributeDataRange>(Header.attributeDataRangeOffset,
Header.attributeDataRangeSize / Sizeof(typeof(Il2CppCustomAttributeDataRange)));
MetadataUsageLists = ReadMetadataArray<Il2CppMetadataUsageList>(Header.MetadataUsageListsOffset, Header.MetadataUsageListsCount, default);
MetadataUsagePairs = ReadMetadataArray<Il2CppMetadataUsagePair>(Header.MetadataUsagePairsOffset, Header.MetadataUsagePairsCount, default);
}
if (Version is 29 or 31)
if (Version >= MetadataVersions.V190)
{
// 29.2/31.2 added a new isUnmanagedCallersOnly flag to Il2CppMethodDefinition.
// This offsets all subsequent entries by one - we can detect this by checking the
// top token byte (which should always be 0x06).
if (Methods.Length >= 2)
{
var secondToken = Methods[1].token;
if (secondToken >> 24 != 0x6)
{
Version += 0.2;
Methods = ReadArray<Il2CppMethodDefinition>(Header.methodsOffset,
Header.methodsCount / Sizeof(typeof(Il2CppMethodDefinition)));
FieldRefs = ReadMetadataArray<Il2CppFieldRef>(Header.FieldRefsOffset, Header.FieldRefsSize, Header.FieldRefs);
}
if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290)
{
AttributeTypeIndices = ReadMetadataPrimitiveArray<int>(Header.AttributesTypesOffset, Header.AttributesTypesCount, default);
AttributeTypeRanges = ReadMetadataArray<Il2CppCustomAttributeTypeRange>(Header.AttributesInfoOffset, Header.AttributesInfoCount, default);
}
if (Version >= MetadataVersions.V290)
{
AttributeDataRanges = ReadMetadataArray<Il2CppCustomAttributeDataRange>(Header.AttributeDataRangeOffset,
Header.AttributeDataRangeSize, Header.AttributeDataRanges);
}
// Get all metadata strings
@@ -216,10 +265,17 @@ namespace Il2CppInspector
Strings = pluginGetStringsResult.Strings;
else {
Position = Header.stringOffset;
var stringOffset = Version >= MetadataVersions.V380
? Header.Strings.Offset
: Header.StringOffset;
var stringLength = Version >= MetadataVersions.V380
? Header.Strings.SectionSize
: Header.StringSize;
while (Position < Header.stringOffset + Header.stringCount)
Strings.Add((int) Position - Header.stringOffset, ReadNullTerminatedString());
Position = stringOffset;
while (Position < stringOffset + stringLength)
Strings.Add((int)Position - stringOffset, ReadNullTerminatedString());
}
// Get all string literals
@@ -227,59 +283,67 @@ namespace Il2CppInspector
if (pluginGetStringLiteralsResult.IsDataModified)
StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray();
else {
var stringLiteralList = ReadArray<Il2CppStringLiteral>(Header.stringLiteralOffset, Header.stringLiteralCount / Sizeof(typeof(Il2CppStringLiteral)));
else
{
var stringLiteralList = ReadMetadataArray<Il2CppStringLiteral>(Header.StringLiteralOffset,
Header.StringLiteralSize, Header.StringLiterals);
var dataOffset = Version >= MetadataVersions.V380
? Header.StringLiteralData.Offset
: Header.StringLiteralDataOffset;
if (Version >= MetadataVersions.V350)
{
var literals = new string[stringLiteralList.Length - 1];
for (var i = 0; i < literals.Length; i++)
{
var currentStringDataIndex = stringLiteralList[i].DataIndex;
var nextStringDataIndex = stringLiteralList[i + 1].DataIndex;
var stringLength = nextStringDataIndex - currentStringDataIndex;
literals[i] = ReadFixedLengthString(dataOffset + currentStringDataIndex, stringLength);
}
StringLiterals = literals;
}
else
{
StringLiterals = new string[stringLiteralList.Length];
for (var i = 0; i < stringLiteralList.Length; i++)
StringLiterals[i] = ReadFixedLengthString(Header.stringLiteralDataOffset + stringLiteralList[i].dataIndex, stringLiteralList[i].length);
StringLiterals[i] = ReadFixedLengthString(dataOffset + stringLiteralList[i].DataIndex,
(int)stringLiteralList[i].Length);
}
}
// Post-processing hook
IsModified |= PluginHooks.PostProcessMetadata(this).IsStreamModified;
return;
}
public ImmutableArray<T> ReadMetadataPrimitiveArray<T>(int oldOffset, int oldSize, Il2CppSectionMetadata newMetadata)
where T : unmanaged
{
return Version >= MetadataVersions.V380
? ReadPrimitiveArray<T>(newMetadata.Offset, newMetadata.Count)
: ReadPrimitiveArray<T>(oldOffset, oldSize / Unsafe.SizeOf<T>());
}
public ImmutableArray<T> ReadMetadataArray<T>(int oldOffset, int oldSize, Il2CppSectionMetadata newMetadata)
where T : IReadable, new()
{
return Version >= MetadataVersions.V380
? ReadVersionedObjectArray<T>(newMetadata.Offset, newMetadata.Count)
: ReadVersionedObjectArray<T>(oldOffset, oldSize / Sizeof<T>());
}
// Save metadata to file, overwriting if necessary
public void SaveToFile(string pathname) {
Position = 0;
using (var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write))
using var outFile = new FileStream(pathname, FileMode.Create, FileAccess.Write);
CopyTo(outFile);
}
public int Sizeof(Type type) => Sizeof(type, Version);
public int Sizeof(Type type, double metadataVersion, int longSizeBytes = 8) {
if (Reader.ObjectMappings.TryGetValue(type, out var streamType))
type = streamType;
int size = 0;
foreach (var i in type.GetTypeInfo().GetFields())
{
// Only process fields for our selected object versioning (always process if none supplied)
var versions = i.GetCustomAttributes<VersionAttribute>(false).Select(v => (v.Min, v.Max)).ToList();
if (versions.Any() && !versions.Any(v => (v.Min <= metadataVersion || v.Min == -1) && (v.Max >= metadataVersion || v.Max == -1)))
continue;
if (i.FieldType == typeof(long) || i.FieldType == typeof(ulong))
size += longSizeBytes;
else if (i.FieldType == typeof(int) || i.FieldType == typeof(uint))
size += 4;
else if (i.FieldType == typeof(short) || i.FieldType == typeof(ushort))
size += 2;
// Fixed-length array
else if (i.FieldType.IsArray) {
var attr = i.GetCustomAttribute<ArrayLengthAttribute>(false) ??
throw new InvalidOperationException("Array field " + i.Name + " must have ArrayLength attribute");
size += attr.FixedSize;
}
// Embedded object
else
size += Sizeof(i.FieldType, metadataVersion);
}
return size;
}
public int Sizeof<T>() where T : IReadable => T.Size(Version, Is32Bit);
}
}

View File

@@ -1,483 +0,0 @@
/*
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
Copyright 2017-2021 Katy Coe - http://www.djkaty.com - https://github.com/djkaty
All rights reserved.
*/
using NoisyCowStudios.Bin2Object;
namespace Il2CppInspector
{
// Unity 4.6.1p5 - first release, no global-metadata.dat
// Unity 5.2.0f3 -> v15
// Unity 5.3.0f4 -> v16
// Unity 5.3.2f1 -> v19
// Unity 5.3.3f1 -> v20
// Unity 5.3.5f1 -> v21
// Unity 5.5.0f3 -> v22
// Unity 5.6.0f3 -> v23
// Unity 2017.1.0f3 -> v24
// Unity 2018.3.0f2 -> v24.1
// Unity 2019.1.0f2 -> v24.2
// Unity 2019.3.7f1 -> v24.3
// Unity 2019.4.15f1 -> v24.4
// Unity 2019.4.21f1 -> v24.5
// Unity 2020.1.0f1 -> v24.3
// Unity 2020.1.11f1 -> v24.4
// Unity 2020.2.0f1 -> v27
// Unity 2020.2.4f1 -> v27.1
// Unity 2021.1.0f1 -> v27.2
// https://unity3d.com/get-unity/download/archive
// Metadata version is written at the end of Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata or WriteMetadata (Unity.IL2CPP.dll)
// From il2cpp-metadata.h
#pragma warning disable CS0649
public class Il2CppGlobalMetadataHeader
{
public uint signature;
public int version;
public int stringLiteralOffset; // string data for managed code
public int stringLiteralCount;
public int stringLiteralDataOffset;
public int stringLiteralDataCount;
public int stringOffset; // string data for metadata
public int stringCount;
public int eventsOffset; // Il2CppEventDefinition
public int eventsCount;
public int propertiesOffset; // Il2CppPropertyDefinition
public int propertiesCount;
public int methodsOffset; // Il2CppMethodDefinition
public int methodsCount;
[Version(Min = 16)]
public int parameterDefaultValuesOffset; // Il2CppParameterDefaultValue
[Version(Min = 16)]
public int parameterDefaultValuesCount;
public int fieldDefaultValuesOffset; // Il2CppFieldDefaultValue
public int fieldDefaultValuesCount;
public int fieldAndParameterDefaultValueDataOffset; // uint8_t
public int fieldAndParameterDefaultValueDataCount;
[Version(Min = 16)]
public int fieldMarshaledSizesOffset; // Il2CppFieldMarshaledSize
[Version(Min = 16)]
public int fieldMarshaledSizesCount;
public int parametersOffset; // Il2CppParameterDefinition
public int parametersCount;
public int fieldsOffset; // Il2CppFieldDefinition
public int fieldsCount;
public int genericParametersOffset; // Il2CppGenericParameter
public int genericParametersCount;
public int genericParameterConstraintsOffset; // TypeIndex
public int genericParameterConstraintsCount;
public int genericContainersOffset; // Il2CppGenericContainer
public int genericContainersCount;
public int nestedTypesOffset; // TypeDefinitionIndex
public int nestedTypesCount;
public int interfacesOffset; // TypeIndex
public int interfacesCount;
public int vtableMethodsOffset; // EncodedMethodIndex
public int vtableMethodsCount;
public int interfaceOffsetsOffset; // Il2CppInterfaceOffsetPair
public int interfaceOffsetsCount;
public int typeDefinitionsOffset; // Il2CppTypeDefinition
public int typeDefinitionsCount;
[Version(Max = 24.1)]
public int rgctxEntriesOffset; // Il2CppRGCTXDefinition
[Version(Max = 24.1)]
public int rgctxEntriesCount;
[Version(Min = 16)]
public int imagesOffset; // Il2CppImageDefinition
[Version(Min = 16)]
public int imagesCount;
[Version(Min = 16)]
public int assembliesOffset; // Il2CppAssemblyDefinition
[Version(Min = 16)]
public int assembliesCount;
[Version(Min = 19, Max = 24.5)]
public int metadataUsageListsOffset; // Il2CppMetadataUsageList
[Version(Min = 19, Max = 24.5)]
public int metadataUsageListsCount;
[Version(Min = 19, Max = 24.5)]
public int metadataUsagePairsOffset; // Il2CppMetadataUsagePair
[Version(Min = 19, Max = 24.5)]
public int metadataUsagePairsCount;
[Version(Min = 19)]
public int fieldRefsOffset; // Il2CppFieldRef
[Version(Min = 19)]
public int fieldRefsCount;
[Version(Min = 20)]
public int referencedAssembliesOffset; // int32_t
[Version(Min = 20)]
public int referencedAssembliesCount;
[Version(Min = 21, Max = 27.2)]
public int attributesInfoOffset; // Il2CppCustomAttributeTypeRange
[Version(Min = 21, Max = 27.2)]
public int attributesInfoCount;
[Version(Min = 21, Max = 27.2)]
public int attributeTypesOffset; // TypeIndex
[Version(Min = 21, Max = 27.2)]
public int attributeTypesCount;
[Version(Min = 29)]
public uint attributeDataOffset;
[Version(Min = 29)]
public int attributeDataSize;
[Version(Min = 29)]
public uint attributeDataRangeOffset;
[Version(Min = 29)]
public int attributeDataRangeSize;
// Added in metadata v22
[Version(Min = 22)]
public int unresolvedVirtualCallParameterTypesOffset; // TypeIndex
[Version(Min = 22)]
public int unresolvedVirtualCallParameterTypesCount;
[Version(Min = 22)]
public int unresolvedVirtualCallParameterRangesOffset; // Il2CppRange
[Version(Min = 22)]
public int unresolvedVirtualCallParameterRangesCount;
// Added in metadata v23
[Version(Min = 23)]
public int windowsRuntimeTypeNamesOffset; // Il2CppWindowsRuntimeTypeNamePair
[Version(Min = 23)]
public int windowsRuntimeTypeNamesSize;
// Added in metadata v27
[Version(Min = 27)]
public int windowsRuntimeStringsOffset; // const char*
[Version(Min = 27)]
public int windowsRuntimeStringsSize;
// Added in metadata v24
[Version(Min = 24)]
public int exportedTypeDefinitionsOffset; // TypeDefinitionIndex
[Version(Min = 24)]
public int exportedTypeDefinitionsCount;
}
public class Il2CppImageDefinition
{
public int nameIndex;
public int assemblyIndex;
public int typeStart;
public uint typeCount;
[Version(Min = 24)]
public int exportedTypeStart;
[Version(Min = 24)]
public uint exportedTypeCount;
public int entryPointIndex;
[Version(Min = 19)]
public uint token;
[Version(Min = 24.1)]
public int customAttributeStart;
[Version(Min = 24.1)]
public uint customAttributeCount;
}
#pragma warning restore CS0649
// Renamed from Il2CppAssembly somewhere after Unity 2017.2f3 up to Unity 2018.2.0f2
public class Il2CppAssemblyDefinition
{
// They moved the position of aname in v16 from the top to the bottom of the struct
public Il2CppAssemblyNameDefinition aname => aname_pre16 ?? aname_post16;
[Version(Max = 15)]
public Il2CppAssemblyNameDefinition aname_pre16;
public int imageIndex;
[Version(Min = 24.1)]
public uint token;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 20)]
public int referencedAssemblyStart;
[Version(Min = 20)]
public int referencedAssemblyCount;
[Version(Min = 16)]
public Il2CppAssemblyNameDefinition aname_post16;
}
// Renamed from Il2CppAssemblyName somewhere after Unity 2017.2f3 up to Unity 2018.2.0f2
public class Il2CppAssemblyNameDefinition
{
// They moved the position of publicKeyToken in v16 from the middle to the bottom of the struct
public byte[] publicKeyToken => publicKeyToken_post16;
public int nameIndex;
public int cultureIndex;
[Version(Max = 24.3)]
public int hashValueIndex;
public int publicKeyIndex;
[Version(Max = 15), ArrayLength(FixedSize = 8)]
public byte[] publicKeyToken_pre16;
public uint hash_alg;
public int hash_len;
public uint flags;
public int major;
public int minor;
public int build;
public int revision;
[Version(Min = 16), ArrayLength(FixedSize = 8)]
public byte[] publicKeyToken_post16;
}
public class Il2CppTypeDefinition
{
public int nameIndex;
public int namespaceIndex;
// Removed in metadata v24.1
[Version(Max = 24.0)]
public int customAttributeIndex;
public int byvalTypeIndex;
[Version(Max = 24.5)]
public int byrefTypeIndex;
public int declaringTypeIndex;
public int parentIndex;
public int elementTypeIndex; // we can probably remove this one. Only used for enums
[Version(Max = 24.1)]
public int rgctxStartIndex;
[Version(Max = 24.1)]
public int rgctxCount;
public int genericContainerIndex;
// Removed in metadata v23
[Version(Max = 22)]
public int delegateWrapperFromManagedToNativeIndex; // (was renamed to reversePInvokeWrapperIndex in v22)
[Version(Max = 22)]
public int marshalingFunctionsIndex;
[Version(Min = 21, Max = 22)]
public int ccwFunctionIndex;
[Version(Min = 21, Max = 22)]
public int guidIndex;
public uint flags;
public int fieldStart;
public int methodStart;
public int eventStart;
public int propertyStart;
public int nestedTypesStart;
public int interfacesStart;
public int vtableStart;
public int interfaceOffsetsStart;
public ushort method_count;
public ushort property_count;
public ushort field_count;
public ushort event_count;
public ushort nested_type_count;
public ushort vtable_count;
public ushort interfaces_count;
public ushort interface_offsets_count;
// bitfield to portably encode boolean values as single bits
// 01 - valuetype;
// 02 - enumtype;
// 03 - has_finalize;
// 04 - has_cctor;
// 05 - is_blittable;
// 06 - is_import; (from v22: is_import_or_windows_runtime)
// 07-10 - One of nine possible PackingSize values (0, 1, 2, 4, 8, 16, 32, 64, or 128)
public uint bitfield;
[Version(Min = 19)]
public uint token;
}
public class Il2CppMethodDefinition
{
public int nameIndex;
[Version(Min = 16)]
public int declaringType;
public int returnType;
[Version(Min = 31)]
public int returnParameterToken;
public int parameterStart;
[Version(Max = 24.0)]
public int customAttributeIndex;
public int genericContainerIndex;
[Version(Max = 24.1)]
public int methodIndex;
[Version(Max = 24.1)]
public int invokerIndex;
[Version(Max = 24.1)]
public int reversePInvokeWrapperIndex; // (was renamed from delegateWrapperIndex in v22)
[Version(Max = 24.1)]
public int rgctxStartIndex;
[Version(Max = 24.1)]
public int rgctxCount;
public uint token;
public ushort flags;
public ushort iflags;
public ushort slot;
public ushort parameterCount;
[Version(Min = 29.2, Max = 29.2)]
[Version(Min = 31.2, Max = 31.2)]
public byte isUnmanagedCallersOnly;
}
public class Il2CppParameterDefinition
{
public int nameIndex;
public uint token;
[Version(Max = 24.0)]
public int customAttributeIndex;
public int typeIndex;
}
public class Il2CppParameterDefaultValue
{
public int parameterIndex;
public int typeIndex;
public int dataIndex;
}
public class Il2CppFieldDefinition
{
public int nameIndex;
public int typeIndex;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 19)]
public uint token;
}
public class Il2CppFieldDefaultValue
{
public int fieldIndex;
public int typeIndex;
public int dataIndex;
}
public class Il2CppPropertyDefinition
{
public int nameIndex;
public int get;
public int set;
public uint attrs;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 19)]
public uint token;
}
public class Il2CppEventDefinition
{
public int nameIndex;
public int typeIndex;
public int add;
public int remove;
public int raise;
[Version(Max = 24.0)]
public int customAttributeIndex;
[Version(Min = 19)]
public uint token;
}
public class Il2CppGenericContainer
{
/* index of the generic type definition or the generic method definition corresponding to this container */
public int ownerIndex; // either index into Il2CppClass metadata array or Il2CppMethodDefinition array
public int type_argc;
/* If true, we're a generic method, otherwise a generic type definition. */
public int is_method;
/* Our type parameters. */
public uint genericParameterStart; // GenericParameterIndex
}
public class Il2CppGenericParameter
{
public int ownerIndex; /* Type or method this parameter was defined in. */ // GenericContainerIndex
public int nameIndex; // StringIndex
public short constraintsStart; // GenericParameterConstraintIndex
public short constraintsCount;
public ushort num; // Generic parameter position
public ushort flags; // GenericParameterAttributes
}
public class Il2CppCustomAttributeTypeRange
{
[Version(Min = 24.1)]
public uint token;
public int start;
public int count;
}
public class Il2CppInterfaceOffsetPair
{
public int interfaceTypeIndex;
public int offset;
}
// Removed in metadata v27
public class Il2CppMetadataUsageList
{
public uint start;
public uint count;
}
// Removed in metadata v27
public class Il2CppMetadataUsagePair
{
public uint destinationindex;
public uint encodedSourceIndex;
}
public class Il2CppStringLiteral
{
public int length;
public int dataIndex;
}
public class Il2CppFieldRef
{
public int typeIndex;
public int fieldIndex; // local offset into type fields
}
public class Il2CppCustomAttributeDataRange
{
public uint token;
public uint startOffset;
}
}

View File

@@ -6,6 +6,8 @@
All rights reserved.
*/
using Il2CppInspector.Next;
namespace Il2CppInspector
{
public enum MetadataUsageType
@@ -19,11 +21,13 @@ namespace Il2CppInspector
FieldRva = 7
}
public class MetadataUsage
public record struct MetadataUsage
{
public MetadataUsageType Type { get; }
public int SourceIndex { get; }
public ulong VirtualAddress { get; private set; }
public ulong VirtualAddress { get; }
public readonly bool IsValid => Type != 0;
public MetadataUsage(MetadataUsageType type, int sourceIndex, ulong virtualAddress = 0) {
Type = type;
@@ -34,10 +38,10 @@ namespace Il2CppInspector
public static MetadataUsage FromEncodedIndex(Il2CppInspector package, uint encodedIndex, ulong virtualAddress = 0) {
uint index;
MetadataUsageType usageType;
if (package.Version < 19) {
if (package.Version < MetadataVersions.V190) {
/* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */
var isGeneric = encodedIndex & 0x80000000;
index = package.Binary.VTableMethodReferences[encodedIndex & 0x7FFFFFFF];
index = package.Binary.VTableMethodReferences[(int)(encodedIndex & 0x7FFFFFFF)];
usageType = (isGeneric != 0) ? MetadataUsageType.MethodRef : MetadataUsageType.MethodDef;
} else {
/* These encoded indices appear in metadata usages, and are decoded by GetEncodedIndexType/GetDecodedMethodIndex */
@@ -46,12 +50,10 @@ namespace Il2CppInspector
index = encodedIndex & 0x1FFFFFFF;
// From v27 the bottom bit is set to indicate the usage token hasn't been replaced with a pointer at runtime yet
if (package.Version >= 27)
if (package.Version >= MetadataVersions.V270)
index >>= 1;
}
return new MetadataUsage(usageType, (int)index, virtualAddress);
}
public void SetAddress(ulong virtualAddress) => VirtualAddress = virtualAddress;
}
}

View File

@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<AssemblyName>Il2CppInspector.Common</AssemblyName>
<Authors>Katy Coe</Authors>
<Version>2023.1</Version>
@@ -38,15 +40,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="dnlib" Version="4.3.0" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.1" />
<PackageReference Include="CxxDemangler" Version="0.2.4.11">
<NoWarn>NU1605</NoWarn>
</PackageReference>
<PackageReference Include="dnlib" Version="4.4.0" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="2.0.0" />
<PackageReference Include="Spectre.Console" Version="0.50.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Bin2Object\Bin2Object\Bin2Object.csproj" PrivateAssets="all" />
<ProjectReference Include="..\VersionedSerialization.Generator\VersionedSerialization.Generator.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
<ProjectReference Include="..\VersionedSerialization\VersionedSerialization.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using Il2CppInspector.Cpp;
using Il2CppInspector.Next;
using Il2CppInspector.Reflection;
namespace Il2CppInspector.Model
@@ -123,9 +124,9 @@ namespace Il2CppInspector.Model
Add(binary.CodeRegistrationPointer, binary.CodeRegistration);
Add(binary.MetadataRegistrationPointer, binary.MetadataRegistration);
if (Model.Package.Version >= 24.2) {
if (Model.Package.Version >= MetadataVersions.V242) {
// TODO: Add some kind of AppArray composite type for arrays as we'll be adding more later
Add(binary.CodeRegistration.pcodeGenModules, binary.CodeGenModulePointers);
Add(binary.CodeRegistration.CodeGenModules, binary.CodeGenModulePointers);
foreach (var ptr in binary.CodeGenModulePointers)
Add(ptr.Value, binary.Modules[ptr.Key]);

View File

@@ -12,7 +12,9 @@ using System.Linq;
using Aron.Weiler;
using Il2CppInspector.Cpp;
using Il2CppInspector.Cpp.UnityHeaders;
using Il2CppInspector.Next;
using Il2CppInspector.Reflection;
using Spectre.Console;
namespace Il2CppInspector.Model
{
@@ -39,6 +41,9 @@ namespace Il2CppInspector.Model
// The types are ordered to enable the production of code output without forward dependencies
public List<CppType> DependencyOrderedCppTypes { get; private set; }
// Required forward definition types for the C++ type definitions
public List<CppType> RequiredForwardDefinitions { get; private set; } = [];
// Composite mapping of all the .NET methods in the IL2CPP binary
public MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod> Methods { get; } = new MultiKeyDictionary<MethodBase, CppFnPtrType, AppMethod>();
@@ -54,7 +59,7 @@ namespace Il2CppInspector.Model
public Dictionary<ulong, (FieldInfo Field, string Value)> Fields { get; } = [];
public Dictionary<ulong, (FieldInfo Field, string Value)> FieldRvas { get; } = [];
public bool StringIndexesAreOrdinals => Package.Version < 19;
public bool StringIndexesAreOrdinals => Package.Version < MetadataVersions.V190;
// The .NET type model for the application
public TypeModel TypeModel { get; }
@@ -104,7 +109,8 @@ namespace Il2CppInspector.Model
}
// Initialize
public AppModel(TypeModel model, bool makeDefaultBuild = true) {
public AppModel(TypeModel model, bool makeDefaultBuild = true)
{
// Save .NET type model
TypeModel = model;
@@ -147,11 +153,11 @@ namespace Il2CppInspector.Model
UnityHeaders = unityVersion != null ? UnityHeaders.GetHeadersForVersion(unityVersion) : UnityHeaders.GuessHeadersForBinary(TypeModel.Package.Binary).Last();
UnityVersion = unityVersion ?? UnityHeaders.VersionRange.Min;
Console.WriteLine($"Selected Unity version(s) {UnityHeaders.VersionRange} (types: {UnityHeaders.TypeHeaderResource.VersionRange}, APIs: {UnityHeaders.APIHeaderResource.VersionRange})");
AnsiConsole.WriteLine($"Selected Unity version(s) {UnityHeaders.VersionRange} (types: {UnityHeaders.TypeHeaderResource.VersionRange}, APIs: {UnityHeaders.APIHeaderResource.VersionRange})");
// Check for matching metadata and binary versions
if (UnityHeaders.MetadataVersion != Image.Version) {
Console.WriteLine($"Warning: selected version {UnityVersion} (metadata version {UnityHeaders.MetadataVersion})" +
AnsiConsole.WriteLine($"Warning: selected version {UnityVersion} (metadata version {UnityHeaders.MetadataVersion})" +
$" does not match metadata version {Image.Version}.");
}
@@ -234,8 +240,18 @@ namespace Il2CppInspector.Model
break;
case MetadataUsageType.MethodDef or MetadataUsageType.MethodRef:
var method = TypeModel.GetMetadataUsageMethod(usage);
declarationGenerator.IncludeMethod(method);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
var definitions = declarationGenerator.GenerateRemainingTypeDeclarations();
if (definitions == null)
{
// if we end up here, type generation has failed
// todo: this try/catch is a massive hack to sidestep the original issue of generation failing,
// todo: this needs to be improved.
break;
}
AddTypes(definitions);
// Any method here SHOULD already be in the Methods list
// but we have seen one example where this is not the case for a MethodDef
@@ -245,6 +261,7 @@ namespace Il2CppInspector.Model
Methods.Add(method, fnPtr, new AppMethod(method, fnPtr) { Group = Group });
}
Methods[method].MethodInfoPtrAddress = address;
break;
// FieldInfo is used for array initializers.
@@ -252,11 +269,11 @@ namespace Il2CppInspector.Model
case MetadataUsageType.FieldInfo or MetadataUsageType.FieldRva:
var fieldRef = TypeModel.Package.FieldRefs[usage.SourceIndex];
var fieldType = TypeModel.GetMetadataUsageType(usage);
var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.fieldStart + fieldRef.fieldIndex);
var field = fieldType.DeclaredFields.First(f => f.Index == fieldType.Definition.FieldIndex + fieldRef.FieldIndex);
var value = field.HasFieldRVA
? Convert.ToHexString(Package.Metadata.ReadBytes(
(long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.nativeSize))
(long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.NativeSize))
: "";
@@ -270,7 +287,7 @@ namespace Il2CppInspector.Model
}
// Add string literals for metadata <19 to the model
if (Package.Version < 19) {
if (Package.Version < MetadataVersions.V190) {
/* Version < 19 calls `il2cpp_codegen_string_literal_from_index` to get string literals.
* Unfortunately, metadata references are just loose globals in Il2CppMetadataUsage.cpp
* so we can't automatically name those. Next best thing is to define an enum for the strings. */
@@ -292,6 +309,8 @@ namespace Il2CppInspector.Model
declarationGenerator.IncludeType(type);
AddTypes(declarationGenerator.GenerateRemainingTypeDeclarations());
RequiredForwardDefinitions = declarationGenerator.GenerateRequiredForwardDefinitions();
// Restore stdout
Console.SetOut(stdout);

View File

@@ -0,0 +1,15 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppArrayType
{
public Pointer<Il2CppType> ElementType;
public byte Rank;
public byte NumSizes;
public byte NumLowerBound;
public PrimitivePointer<int> Sizes;
public PrimitivePointer<int> LoBounds;
}

View File

@@ -0,0 +1,55 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppCodeGenModule
{
public PrimitivePointer<byte> ModuleName; // const char*
[NativeInteger]
public uint MethodPointerCount;
public Pointer<Il2CppMethodPointer> MethodPointers;
[NativeInteger]
[VersionCondition(EqualTo = "24.5")]
[VersionCondition(GreaterThan = "27.1")]
public uint AdjustorThunksCount;
[VersionCondition(EqualTo = "24.5")]
[VersionCondition(GreaterThan = "27.1")]
public Pointer<Il2CppTokenAdjustorThunkPair> AdjustorThunks;
public PrimitivePointer<int> InvokerIndices; // int*
[NativeInteger]
public uint ReversePInvokeWrapperCount;
public Pointer<Il2CppTokenIndexMethodTuple> ReversePInvokeWrapperIndices;
[NativeInteger]
public uint RgctxRangesCount;
public Pointer<Il2CppTokenRangePair> RgctxRanges;
[NativeInteger]
public uint RgctxsCount;
public Pointer<Il2CppRgctxDefinition> Rgctxs;
public PrimitivePointer<byte> DebuggerMetadata; // Pointer<Il2CppDebuggerMetadataRegistration> DebuggerMetadata;
[VersionCondition(GreaterThan = "27.0", LessThan = "27.2")]
public Pointer<Il2CppMethodPointer> CustomAttributeCacheGenerator;
[VersionCondition(GreaterThan = "27.0")]
public Il2CppMethodPointer ModuleInitializer;
[VersionCondition(GreaterThan = "27.0")]
public PrimitivePointer<int> StaticConstructorTypeIndices; // TypeDefinitionIndex*
[VersionCondition(GreaterThan = "27.0")]
public PrimitivePointer<byte> MetadataRegistration; // Pointer<Il2CppMetadataRegistration>
[VersionCondition(GreaterThan = "27.0")]
public PrimitivePointer<byte> CodeRegistration; // Pointer<Il2CppCodeRegistration>
}

View File

@@ -0,0 +1,116 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using InvokerMethod = Il2CppMethodPointer;
[VersionedStruct]
public partial record struct Il2CppCodeRegistration
{
[NativeInteger]
[VersionCondition(LessThan = "24.1")]
public uint MethodPointersCount;
[VersionCondition(LessThan = "24.1")]
public Pointer<Il2CppMethodPointer> MethodPointers;
[NativeInteger]
public uint ReversePInvokeWrapperCount;
public Pointer<Il2CppMethodPointer> ReversePInvokeWrappers;
[NativeInteger]
[VersionCondition(LessThan = "22.0")]
public uint DelegateWrappersFromManagedToNativeCount;
[VersionCondition(LessThan = "22.0")]
public Pointer<Il2CppMethodPointer> DelegateWrappersFromManagedToNative;
[NativeInteger]
[VersionCondition(LessThan = "22.0")]
public uint MarshalingFunctionsCount;
[VersionCondition(LessThan = "22.0")]
public Pointer<Il2CppMethodPointer> MarshalingFunctions;
[NativeInteger]
[VersionCondition(GreaterThan = "21.0", LessThan = "22.0")]
public uint CcwMarshalingFunctionsCount;
[VersionCondition(GreaterThan = "21.0", LessThan = "22.0")]
public Pointer<Il2CppMethodPointer> CcwMarshalingFunctions;
[NativeInteger]
public uint GenericMethodPointersCount;
public Pointer<Il2CppMethodPointer> GenericMethodPointers;
[VersionCondition(EqualTo = "24.5")]
[VersionCondition(GreaterThan = "27.1")]
public Pointer<Il2CppMethodPointer> GenericAdjustorThunks;
[NativeInteger]
public uint InvokerPointersCount;
public Pointer<InvokerMethod> InvokerPointers;
[NativeInteger]
[VersionCondition(LessThan = "24.5")]
public int CustomAttributeCount;
[VersionCondition(LessThan = "24.5")]
public Pointer<Il2CppMethodPointer> CustomAttributeGenerators;
[NativeInteger]
[VersionCondition(GreaterThan = "21.0", LessThan = "22.0")]
public int GuidCount;
[VersionCondition(GreaterThan = "21.0", LessThan = "22.0")]
public Pointer<Il2CppGuid> Guids;
[NativeInteger]
[VersionCondition(GreaterThan = "22.0", LessThan = "27.2")]
[VersionCondition(EqualTo = "29.0", IncludingTag = "")]
[VersionCondition(EqualTo = "31.0", IncludingTag = "")]
public int UnresolvedVirtualCallCount;
[NativeInteger]
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
[VersionCondition(GreaterThan = "35.0")]
public uint UnresolvedIndirectCallCount; // UnresolvedVirtualCallCount pre 29.1
[VersionCondition(GreaterThan = "22.0")]
public Pointer<Il2CppMethodPointer> UnresolvedVirtualCallPointers;
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
[VersionCondition(GreaterThan = "35.0")]
public Pointer<Il2CppMethodPointer> UnresolvedInstanceCallWrappers;
[VersionCondition(EqualTo = "29.0", IncludingTag = "2022"), VersionCondition(EqualTo = "31.0", IncludingTag = "2022")]
[VersionCondition(EqualTo = "29.0", IncludingTag = "2023"), VersionCondition(EqualTo = "31.0", IncludingTag = "2023")]
[VersionCondition(GreaterThan = "35.0")]
public Pointer<Il2CppMethodPointer> UnresolvedStaticCallPointers;
[NativeInteger]
[VersionCondition(GreaterThan = "23.0")]
public uint InteropDataCount;
[VersionCondition(GreaterThan = "23.0")]
public Pointer<Il2CppInteropData> InteropData;
[NativeInteger]
[VersionCondition(GreaterThan = "24.3")]
public uint WindowsRuntimeFactoryCount;
[VersionCondition(GreaterThan = "24.3")]
public Pointer<Il2CppWindowsRuntimeFactoryTableEntry> WindowsRuntimeFactoryTable;
[NativeInteger]
[VersionCondition(GreaterThan = "24.2")]
public uint CodeGenModulesCount;
[VersionCondition(GreaterThan = "24.2")]
public Pointer<Pointer<Il2CppCodeGenModule>> CodeGenModules;
}

View File

@@ -0,0 +1,18 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppGenericClass
{
[NativeInteger]
[VersionCondition(LessThan = "24.5")]
public int TypeDefinitionIndex;
[VersionCondition(GreaterThan = "27.0")]
public Pointer<Il2CppType> Type;
public Il2CppGenericContext Context;
public PrimitivePointer<byte> CachedClass; // Il2CppClass*, optional
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppGenericContext
{
public Pointer<Il2CppGenericInst> ClassInst;
public Pointer<Il2CppGenericInst> MethodInst;
}

View File

@@ -0,0 +1,14 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppGenericInst
{
public readonly bool Valid => TypeArgc > 0;
[NativeInteger]
public uint TypeArgc;
public Pointer<Pointer<Il2CppType>> TypeArgv;
}

View File

@@ -0,0 +1,12 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using GenericMethodIndex = int;
[VersionedStruct]
public partial record struct Il2CppGenericMethodFunctionsDefinitions
{
public GenericMethodIndex GenericMethodIndex;
public Il2CppGenericMethodIndices Indices;
}

View File

@@ -0,0 +1,16 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using MethodIndex = int;
[VersionedStruct]
public partial record struct Il2CppGenericMethodIndices
{
public MethodIndex MethodIndex;
public MethodIndex InvokerIndex;
[VersionCondition(EqualTo = "24.5")]
[VersionCondition(GreaterThan = "27.1")]
public MethodIndex AdjustorThunkIndex;
}

View File

@@ -0,0 +1,21 @@
using VersionedSerialization;
namespace Il2CppInspector.Next.BinaryMetadata;
public record struct Il2CppGuid : IReadable
{
public Guid Value;
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
var guid = reader.ReadBytes(16);
Value = new Guid(guid, false);
}
public static int Size(in StructVersion version = default, bool is32Bit = false)
{
return 16;
}
public static implicit operator Guid(Il2CppGuid value) => value.Value;
}

View File

@@ -0,0 +1,20 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using PInvokeMarshalToNativeFunc = Il2CppMethodPointer;
using PInvokeMarshalFromNativeFunc = Il2CppMethodPointer;
using PInvokeMarshalCleanupFunc = Il2CppMethodPointer;
using CreateCCWFunc = Il2CppMethodPointer;
[VersionedStruct]
public partial record struct Il2CppInteropData
{
public Il2CppMethodPointer DelegatePInvokeWrapperFunction;
public PInvokeMarshalToNativeFunc PInvokeMarshalToNativeFunction;
public PInvokeMarshalFromNativeFunc PInvokeMarshalFromNativeFunction;
public PInvokeMarshalCleanupFunc PInvokeMarshalCleanupFunction;
public CreateCCWFunc CreateCCWFunction;
public Pointer<Il2CppGuid> Guid;
public Pointer<Il2CppType> Type;
}

View File

@@ -0,0 +1,59 @@
using Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using FieldIndex = int;
using TypeDefinitionIndex = int;
[VersionedStruct]
public partial record struct Il2CppMetadataRegistration
{
[NativeInteger]
public int GenericClassesCount;
public Pointer<Pointer<Il2CppGenericClass>> GenericClasses;
[NativeInteger]
public int GenericInstsCount;
public Pointer<Pointer<Il2CppGenericInst>> GenericInsts;
[NativeInteger]
public int GenericMethodTableCount;
public Pointer<Il2CppGenericMethodFunctionsDefinitions> GenericMethodTable;
[NativeInteger]
public int TypesCount;
public Pointer<Pointer<Il2CppType>> Types;
[NativeInteger]
public int MethodSpecsCount;
public Pointer<Il2CppMethodSpec> MethodSpecs;
[NativeInteger]
[VersionCondition(LessThan = "16.0")]
public int MethodReferencesCount;
[VersionCondition(LessThan = "16.0")]
public PrimitivePointer<PrimitivePointer<uint>> MethodReferences; // uint**
[NativeInteger]
public FieldIndex FieldOffsetsCount;
public PrimitivePointer<PrimitivePointer<int>> FieldOffsets; // int**
[NativeInteger]
public TypeDefinitionIndex TypeDefinitionsSizesCount;
public Pointer<Pointer<Il2CppTypeDefinitionSizes>> TypeDefinitionsSizes;
[NativeInteger]
[VersionCondition(GreaterThan = "19.0")]
public ulong MetadataUsagesCount;
[VersionCondition(GreaterThan = "19.0")]
public Pointer<Pointer<Il2CppMetadataUsage>> MetadataUsages;
}

View File

@@ -0,0 +1,19 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppMethodPointer(ulong addr = 0)
{
public static readonly Il2CppMethodPointer Null = new();
[NativeInteger]
public ulong Value { get; set; } = addr;
public readonly bool IsNull => Value == 0;
public readonly override string ToString() => $"0x{Value:X}";
public static implicit operator ulong(Il2CppMethodPointer ptr) => ptr.Value;
public static implicit operator Il2CppMethodPointer(ulong ptr) => new(ptr);
}

View File

@@ -0,0 +1,14 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using MethodIndex = int;
using GenericInstIndex = int;
[VersionedStruct]
public partial record struct Il2CppMethodSpec
{
public MethodIndex MethodDefinitionIndex;
public GenericInstIndex ClassIndexIndex;
public GenericInstIndex MethodIndexIndex;
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppRange
{
public int Start;
public int Length;
}

View File

@@ -0,0 +1,13 @@
using Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using TypeIndex = int;
[VersionedStruct]
public partial record struct Il2CppRgctxConstrainedData
{
public TypeIndex TypeIndex;
public Il2CppMetadataUsage EncodedMethodIndex;
}

View File

@@ -0,0 +1,12 @@
// ReSharper disable InconsistentNaming
namespace Il2CppInspector.Next.BinaryMetadata;
public enum Il2CppRgctxDataType
{
IL2CPP_RGCTX_DATA_INVALID,
IL2CPP_RGCTX_DATA_TYPE,
IL2CPP_RGCTX_DATA_CLASS,
IL2CPP_RGCTX_DATA_METHOD,
IL2CPP_RGCTX_DATA_ARRAY,
IL2CPP_RGCTX_DATA_CONSTRAINED,
}

View File

@@ -0,0 +1,15 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppRgctxDefinition
{
[NativeInteger]
public Il2CppRgctxDataType Type;
public PrimitivePointer<byte> Data; // void*
public readonly Pointer<Il2CppRgctxDefinitionData> Definition => Data.PointerValue;
public readonly Pointer<Il2CppRgctxConstrainedData> Constrained => Data.PointerValue;
}

View File

@@ -0,0 +1,15 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
using MethodIndex = int;
using TypeIndex = int;
[VersionedStruct]
public partial record struct Il2CppRgctxDefinitionData
{
public int Value;
public readonly MethodIndex MethodIndex => Value;
public readonly TypeIndex TypeIndex => Value;
}

View File

@@ -0,0 +1,12 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppTokenAdjustorThunkPair
{
[NativeInteger]
public uint Token;
public Il2CppMethodPointer AdjustorThunk;
}

View File

@@ -0,0 +1,14 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppTokenIndexMethodTuple
{
public uint Token;
public int Index;
public PrimitivePointer<Il2CppMethodPointer> Method; // void**
public uint GenericMethodIndex;
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppTokenRangePair
{
public uint Token;
public Il2CppRange Range;
}

View File

@@ -0,0 +1,111 @@
using Il2CppInspector.Next.Metadata;
using System.Reflection;
using VersionedSerialization;
namespace Il2CppInspector.Next.BinaryMetadata;
using TypeDefinitionIndex = int;
using GenericParameterIndex = int;
using Il2CppMetadataTypeHandle = Pointer<Il2CppTypeDefinition>;
using Il2CppMetadataGenericParameterHandle = Pointer<Il2CppGenericParameter>;
public record struct Il2CppType : IReadable
{
public record struct DataUnion : IReadable
{
public ulong Value;
public readonly TypeDefinitionIndex KlassIndex => (int)Value;
public readonly Il2CppMetadataTypeHandle TypeHandle => Value;
public readonly Pointer<Il2CppType> Type => Value;
public readonly Pointer<Il2CppArrayType> ArrayType => Value;
public readonly GenericParameterIndex GenericParameterIndex => (int)Value;
public readonly Il2CppMetadataGenericParameterHandle GenericParameterHandle => Value;
public readonly Pointer<Il2CppGenericClass> GenericClass => Value;
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
Value = reader.ReadNUInt();
}
public static int Size(in StructVersion version = default, bool is32Bit = false)
{
return is32Bit ? 4 : 8;
}
}
public DataUnion Data;
public uint Value;
public TypeAttributes Attrs
{
readonly get => (TypeAttributes)(Value & 0xFFFF);
set => Value = (Value & 0xFFFF0000) | (uint)value;
}
public Il2CppTypeEnum Type
{
readonly get => (Il2CppTypeEnum)((Value >> 16) & 0b11111111);
set => Value = (Value & 0xFF00FFFF) | ((uint)value) << 16;
}
public uint NumModifiers
{
readonly get => (Value >> 24) & 0b11111;
set => Value = (Value & 0xE0FFFFFF) | value << 24;
}
public bool ByRef
{
readonly get => ((Value >> 29) & 1) == 1;
set => Value = (Value & 0xDFFFFFFF) | (value ? 1u : 0u) << 29;
}
public bool Pinned
{
readonly get => ((Value >> 30) & 1) == 1;
set => Value = (Value & 0xBFFFFFFF) | (value ? 1u : 0u) << 30;
}
public bool ValueType
{
readonly get => ((Value >> 31) & 1) == 1;
set => Value = (Value & 0x7FFFFFFF) | (value ? 1u : 0u) << 31;
}
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
Data.Read(ref reader, version);
Value = reader.ReadPrimitive<uint>();
if (MetadataVersions.V272 > version)
{
// Versions pre-27.2 had NumModifiers at 6 bits and no ValueType bit
var numModifiers = (Value >> 24) & 0b111111;
// If NumModifiers > 31, we throw here (as the old behavior isn't implemented
if (numModifiers > 31)
throw new InvalidOperationException(
"Versions pre-27.2 with a type having more than 31 modifiers are not supported yet");
// Else, we do some bit-juggling to convert the old value into the new format:
Value =
(Value & 0xFFFFFF) | // Attributes + Type
(((Value >> 24) & 0b111111) << 24) | // 5 Bits for the modifiers
(((Value >> 30) & 1) << 29) | // Shifted ByRef
(((Value >> 31) & 1) << 30) | // Shifted Pinned
0; // 0 ValueType
}
}
public static int Size(in StructVersion version = default, bool is32Bit = false)
{
return DataUnion.Size(version, is32Bit) + sizeof(uint);
}
public static Il2CppType FromTypeEnum(Il2CppTypeEnum type)
=> new()
{
Value = (uint)type << 16
};
}

View File

@@ -0,0 +1,12 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppTypeDefinitionSizes
{
public uint InstanceSize;
public int NativeSize;
public uint StaticFieldsSize;
public uint ThreadStaticFieldsSize;
}

View File

@@ -0,0 +1,76 @@
// ReSharper disable InconsistentNaming
namespace Il2CppInspector.Next.BinaryMetadata;
public enum Il2CppTypeEnum : byte
{
Il2CPP_TYPE_END,
IL2CPP_TYPE_VOID,
IL2CPP_TYPE_BOOLEAN,
IL2CPP_TYPE_CHAR,
IL2CPP_TYPE_I1,
IL2CPP_TYPE_U1,
IL2CPP_TYPE_I2,
IL2CPP_TYPE_U2,
IL2CPP_TYPE_I4,
IL2CPP_TYPE_U4,
IL2CPP_TYPE_I8,
IL2CPP_TYPE_U8,
IL2CPP_TYPE_R4,
IL2CPP_TYPE_R8,
IL2CPP_TYPE_STRING,
IL2CPP_TYPE_PTR,
IL2CPP_TYPE_BYREF,
IL2CPP_TYPE_VALUETYPE,
IL2CPP_TYPE_CLASS,
IL2CPP_TYPE_VAR,
IL2CPP_TYPE_ARRAY,
IL2CPP_TYPE_GENERICINST,
IL2CPP_TYPE_TYPEDBYREF,
IL2CPP_TYPE_I = 0x18,
IL2CPP_TYPE_U,
IL2CPP_TYPE_FNPTR = 0x1b,
IL2CPP_TYPE_OBJECT,
IL2CPP_TYPE_SZARRAY,
IL2CPP_TYPE_MVAR,
IL2CPP_TYPE_CMOD_REQD,
IL2CPP_TYPE_CMOD_OPT,
IL2CPP_TYPE_INTERNAL,
IL2CPP_TYPE_MODIFIER = 0x40,
IL2CPP_TYPE_SENTINEL = 0x41,
IL2CPP_TYPE_PINNED = 0x45,
IL2CPP_TYPE_ENUM = 0x55,
IL2CPP_TYPE_IL2CPP_TYPE_INDEX = 0xff
}
public static class Il2CppTypeEnumExtensions
{
public static bool IsTypeDefinitionEnum(this Il2CppTypeEnum value)
=> value
is Il2CppTypeEnum.IL2CPP_TYPE_VOID
or Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN
or Il2CppTypeEnum.IL2CPP_TYPE_CHAR
or Il2CppTypeEnum.IL2CPP_TYPE_I1
or Il2CppTypeEnum.IL2CPP_TYPE_U1
or Il2CppTypeEnum.IL2CPP_TYPE_I2
or Il2CppTypeEnum.IL2CPP_TYPE_U2
or Il2CppTypeEnum.IL2CPP_TYPE_I4
or Il2CppTypeEnum.IL2CPP_TYPE_U4
or Il2CppTypeEnum.IL2CPP_TYPE_I8
or Il2CppTypeEnum.IL2CPP_TYPE_U8
or Il2CppTypeEnum.IL2CPP_TYPE_R4
or Il2CppTypeEnum.IL2CPP_TYPE_R8
or Il2CppTypeEnum.IL2CPP_TYPE_STRING
or Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE
or Il2CppTypeEnum.IL2CPP_TYPE_CLASS
or Il2CppTypeEnum.IL2CPP_TYPE_I
or Il2CppTypeEnum.IL2CPP_TYPE_U
or Il2CppTypeEnum.IL2CPP_TYPE_OBJECT
or Il2CppTypeEnum.IL2CPP_TYPE_TYPEDBYREF;
public static bool IsGenericParameterEnum(this Il2CppTypeEnum value)
=> value
is Il2CppTypeEnum.IL2CPP_TYPE_VAR
or Il2CppTypeEnum.IL2CPP_TYPE_MVAR;
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.BinaryMetadata;
[VersionedStruct]
public partial record struct Il2CppWindowsRuntimeFactoryTableEntry
{
public Pointer<Il2CppType> Type;
public Il2CppMethodPointer CreateFactoryFunction;
}

View File

@@ -0,0 +1,147 @@
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using NoisyCowStudios.Bin2Object;
using VersionedSerialization;
namespace Il2CppInspector.Next;
public class BinaryObjectStreamReader : BinaryObjectStream, IReader
{
public new StructVersion Version
{
get => _version;
set
{
_version = value;
base.Version = _version.AsDouble;
}
}
private StructVersion _version;
public virtual int Bits { get; set; }
public bool Is32Bit => Bits == 32;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static TTo Cast<TFrom, TTo>(in TFrom from) => Unsafe.As<TFrom, TTo>(ref Unsafe.AsRef(in from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private T ReadInternal<T>() where T : unmanaged
{
var size = Unsafe.SizeOf<T>();
var value = MemoryMarshal.Read<T>(ReadBytes(size));
return value;
}
public T ReadPrimitive<T>() where T : unmanaged
{
if (typeof(T) == typeof(sbyte))
return Cast<byte, T>(ReadByte());
if (typeof(T) == typeof(short))
return Cast<short, T>(ReadInt16());
if (typeof(T) == typeof(int))
return Cast<int, T>(ReadInt32());
if (typeof(T) == typeof(long))
return Cast<long, T>(ReadInt64());
if (typeof(T) == typeof(byte))
return Cast<byte, T>(ReadByte());
if (typeof(T) == typeof(ushort))
return Cast<ushort, T>(ReadUInt16());
if (typeof(T) == typeof(uint))
return Cast<uint, T>(ReadUInt32());
if (typeof(T) == typeof(ulong))
return Cast<ulong, T>(ReadUInt64());
return ReadInternal<T>();
}
public ImmutableArray<T> ReadPrimitiveArray<T>(long count) where T : unmanaged
{
var array = ImmutableArray.CreateBuilder<T>(checked((int)count));
for (long i = 0; i < count; i++)
array.Add(ReadPrimitive<T>());
return array.MoveToImmutable();
}
public T ReadVersionedObject<T>() where T : IReadable, new() => ReadVersionedObject<T>(Version);
public T ReadVersionedObject<T>(in StructVersion version = default) where T : IReadable, new()
{
var obj = new T();
var a = this;
obj.Read(ref a, in version);
return obj;
}
public ImmutableArray<T> ReadVersionedObjectArray<T>(long count) where T : IReadable, new() => ReadVersionedObjectArray<T>(count, Version);
public ImmutableArray<T> ReadVersionedObjectArray<T>(long count, in StructVersion version = default) where T : IReadable, new()
{
var array = ImmutableArray.CreateBuilder<T>(checked((int)count));
for (long i = 0; i < count; i++)
array.Add(ReadVersionedObject<T>(in version));
return array.MoveToImmutable();
}
public long ReadNInt()
=> Is32Bit ? ReadPrimitive<int>() : ReadPrimitive<long>();
public ulong ReadNUInt()
=> Is32Bit ? ReadPrimitive<uint>() : ReadPrimitive<ulong>();
public string ReadString() => ReadNullTerminatedString();
public new ReadOnlySpan<byte> ReadBytes(int length)
{
return base.ReadBytes(length);
}
public void Align(int alignment = 0)
{
if (alignment == 0)
alignment = Is32Bit ? 4 : 8;
var rem = Position % alignment;
if (rem != 0)
Position += alignment - rem;
}
public TType ReadPrimitive<TType>(long addr) where TType : unmanaged
{
Position = addr;
return ReadPrimitive<TType>();
}
public ImmutableArray<TType> ReadPrimitiveArray<TType>(long addr, long count) where TType : unmanaged
{
Position = addr;
return ReadPrimitiveArray<TType>(count);
}
public TType ReadVersionedObject<TType>(long addr) where TType : IReadable, new()
{
Position = addr;
return ReadVersionedObject<TType>(Version);
}
public ImmutableArray<TType> ReadVersionedObjectArray<TType>(long addr, long count) where TType : IReadable, new()
{
Position = addr;
return ReadVersionedObjectArray<TType>(count, Version);
}
public void Skip(int count)
{
Position += count;
}
}

View File

@@ -0,0 +1,45 @@
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public struct GenericContainerIndex(int value) : IIndexType<GenericContainerIndex>, IReadable, IEquatable<GenericContainerIndex>
{
public const string TagPrefix = nameof(GenericContainerIndex);
static string IIndexType<GenericContainerIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<GenericContainerIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value;
public static int Size(in StructVersion version = default, bool is32Bit = false)
=> IIndexType<GenericContainerIndex>.IndexSize(version, is32Bit);
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
_value = IIndexType<GenericContainerIndex>.ReadIndex(ref reader, in version);
}
#region Operators + ToString
public static implicit operator int(GenericContainerIndex idx) => idx._value;
public static implicit operator GenericContainerIndex(int idx) => new(idx);
public static bool operator ==(GenericContainerIndex left, GenericContainerIndex right)
=> left._value == right._value;
public static bool operator !=(GenericContainerIndex left, GenericContainerIndex right)
=> !(left == right);
public readonly override bool Equals(object obj)
=> obj is GenericContainerIndex other && Equals(other);
public readonly bool Equals(GenericContainerIndex other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => _value.ToString();
#endregion
}

View File

@@ -0,0 +1,55 @@
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public interface IIndexType<T> where T
: IIndexType<T>, allows ref struct
{
public static abstract string TagPrefix { get; }
public static abstract StructVersion AddedVersion { get; }
private static string TagSize4 => $"{T.TagPrefix}4";
private static string TagSize2 => $"{T.TagPrefix}2";
private static string TagSize1 => $"{T.TagPrefix}1";
private static bool HasCustomSize(in StructVersion version)
=> version >= T.AddedVersion
&& version.Tag != null
&& version.Tag.Contains(T.TagPrefix)
&& !version.Tag.Contains(TagSize4);
public static int IndexSize(in StructVersion version = default, bool is32Bit = false)
{
if (version.Tag != null && HasCustomSize(version))
{
if (version.Tag.Contains(TagSize2))
return sizeof(ushort);
if (version.Tag.Contains(TagSize1))
return sizeof(byte);
}
return sizeof(int);
}
public static int ReadIndex<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
if (version.Tag != null && HasCustomSize(version))
{
if (version.Tag.Contains(TagSize2))
{
var value = reader.ReadPrimitive<ushort>();
return value == ushort.MaxValue ? -1 : value;
}
if (version.Tag.Contains(TagSize1))
{
var value = reader.ReadPrimitive<byte>();
return value == byte.MaxValue ? -1 : value;
}
}
return reader.ReadPrimitive<int>();
}
}

View File

@@ -0,0 +1,41 @@
using System.Runtime.InteropServices;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using ImageIndex = int;
[VersionedStruct]
[StructLayout(LayoutKind.Explicit)]
public partial record struct Il2CppAssemblyDefinition
{
[FieldOffset(20)]
[VersionCondition(LessThan = "15.0")]
public Il2CppAssemblyNameDefinition LegacyAname;
[FieldOffset(0)]
public ImageIndex ImageIndex;
[FieldOffset(4)]
[VersionCondition(GreaterThan = "24.1")]
public uint Token;
[FieldOffset(20 + 52)]
[VersionCondition(GreaterThan = "38.0")]
public uint ModuleToken;
[FieldOffset(8)]
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex;
[FieldOffset(12)]
[VersionCondition(GreaterThan = "20.0")]
public int ReferencedAssemblyStart;
[FieldOffset(16)]
[VersionCondition(GreaterThan = "20.0")]
public int ReferencedAssemblyCount;
[FieldOffset(20)]
public Il2CppAssemblyNameDefinition Aname;
}

View File

@@ -0,0 +1,64 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
[InlineArray(PublicKeyLength)]
public struct PublicKeyToken
{
private const int PublicKeyLength = 8;
private byte _value;
}
[VersionedStruct]
[StructLayout(LayoutKind.Explicit)]
public partial record struct Il2CppAssemblyNameDefinition
{
[FieldOffset(0)]
public StringIndex NameIndex;
[FieldOffset(4)]
public StringIndex CultureIndex;
[FieldOffset(8)]
[VersionCondition(LessThan = "24.3")]
public int HashValueIndex;
[FieldOffset(12)]
public StringIndex PublicKeyIndex;
[FieldOffset(44)]
[VersionCondition(LessThan = "15.0")]
[CustomSerialization("reader.ReadPrimitive<PublicKeyToken>();", "8")]
private PublicKeyToken _legacyPublicKeyToken;
[FieldOffset(16)]
public AssemblyHashAlgorithm HashAlg;
[FieldOffset(20)]
public int HashLen;
[FieldOffset(24)]
public AssemblyNameFlags Flags;
[FieldOffset(28)]
public int Major;
[FieldOffset(32)]
public int Minor;
[FieldOffset(36)]
public int Build;
[FieldOffset(40)]
public int Revision;
[FieldOffset(44)]
[CustomSerialization("reader.ReadPrimitive<PublicKeyToken>();", "8")]
public PublicKeyToken PublicKeyToken;
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppCustomAttributeDataRange
{
public uint Token { get; private set; }
public uint StartOffset { get; private set; }
}

View File

@@ -0,0 +1,13 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppCustomAttributeTypeRange
{
[VersionCondition(GreaterThan = "24.1")]
public uint Token { get; private set; }
public int Start { get; private set; }
public int Count { get; private set; }
}

View File

@@ -0,0 +1,23 @@
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using MethodIndex = int;
using VersionedSerialization.Attributes;
[VersionedStruct]
public partial record struct Il2CppEventDefinition
{
public StringIndex NameIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
public MethodIndex Add { get; private set; }
public MethodIndex Remove { get; private set; }
public MethodIndex Raise { get; private set; }
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex { get; private set; }
[VersionCondition(GreaterThan = "19.0")]
public uint Token { get; private set; }
public readonly bool IsValid => NameIndex != 0;
}

View File

@@ -0,0 +1,14 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using FieldIndex = int;
using DefaultValueDataIndex = int;
[VersionedStruct]
public partial record struct Il2CppFieldDefaultValue
{
public FieldIndex FieldIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
public DefaultValueDataIndex DataIndex { get; private set; }
}

View File

@@ -0,0 +1,19 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringIndex = int;
[VersionedStruct]
public partial record struct Il2CppFieldDefinition
{
public StringIndex NameIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex { get; private set; }
[VersionCondition(GreaterThan = "19.0")]
public uint Token { get; private set; }
public readonly bool IsValid => NameIndex != 0;
}

View File

@@ -0,0 +1,12 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using FieldIndex = int;
[VersionedStruct]
public partial record struct Il2CppFieldMarshaledSize
{
public FieldIndex FieldIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
public int MarshaledSize { get; private set; }
}

View File

@@ -0,0 +1,12 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using FieldIndex = int;
[VersionedStruct]
public partial record struct Il2CppFieldRef
{
public TypeIndex TypeIndex { get; private set; }
public FieldIndex FieldIndex { get; private set; }
}

View File

@@ -0,0 +1,13 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using GenericParameterIndex = int;
[VersionedStruct]
public partial record struct Il2CppGenericContainer
{
public int OwnerIndex { get; private set; }
public int TypeArgc { get; private set; }
public int IsMethod { get; private set; }
public GenericParameterIndex GenericParameterStart { get; private set; }
}

View File

@@ -0,0 +1,20 @@
using System.Reflection;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using GenericParameterConstraintIndex = short;
[VersionedStruct]
public partial record struct Il2CppGenericParameter
{
public GenericContainerIndex OwnerIndex { get; private set; }
public StringIndex NameIndex { get; private set; }
public GenericParameterConstraintIndex ConstraintsStart { get; private set; }
public short ConstraintsCount { get; private set; }
public ushort Num { get; private set; }
public ushort Flags { get; private set; }
public readonly GenericParameterAttributes Attributes => (GenericParameterAttributes)Flags;
}

View File

@@ -0,0 +1,349 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
// Unity 4.6.1p5 - first release, no global-metadata.dat
// Unity 5.2.0f3 -> v15
// Unity 5.3.0f4 -> v16
// Unity 5.3.2f1 -> v19
// Unity 5.3.3f1 -> v20
// Unity 5.3.5f1 -> v21
// Unity 5.5.0f3 -> v22
// Unity 5.6.0f3 -> v23
// Unity 2017.1.0f3 -> v24
// Unity 2018.3.0f2 -> v24.1
// Unity 2019.1.0f2 -> v24.2
// Unity 2019.3.7f1 -> v24.3
// Unity 2019.4.15f1 -> v24.4
// Unity 2019.4.21f1 -> v24.5
// Unity 2020.1.0f1 -> v24.3
// Unity 2020.1.11f1 -> v24.4
// Unity 2020.2.0f1 -> v27
// Unity 2020.2.4f1 -> v27.1
// Unity 2021.1.0f1 -> v27.2
// https://unity3d.com/get-unity/download/archive
// Metadata version is written at the end of Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata or WriteMetadata (Unity.IL2CPP.dll)
[VersionedStruct]
public partial record struct Il2CppGlobalMetadataHeader
{
public int Sanity { get; private set; }
public int Version { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int StringLiteralOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int StringLiteralSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int StringLiteralDataOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int StringLiteralDataSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int StringOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int StringSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int EventsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int EventsSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int PropertiesOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int PropertiesSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int MethodsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int MethodsSize { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
[VersionCondition(EqualTo = "16.0")]
public int ParameterDefaultValuesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
[VersionCondition(EqualTo = "16.0")]
public int ParameterDefaultValuesSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int FieldDefaultValuesOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int FieldDefaultValuesSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int FieldAndParameterDefaultValueDataOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int FieldAndParameterDefaultValueDataSize { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
public int FieldMarshaledSizesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
public int FieldMarshaledSizesSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int ParametersOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int ParametersSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int FieldsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int FieldsSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int GenericParametersOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int GenericParametersSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int GenericParameterConstraintsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int GenericParameterConstraintsSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int GenericContainersOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int GenericContainersSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int NestedTypesOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int NestedTypesSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int InterfacesOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int InterfacesSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int VTableMethodsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int VTableMethodsSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int InterfaceOffsetsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int InterfaceOffsetsSize { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int TypeDefinitionsOffset { get; private set; }
[VersionCondition(LessThan = "35.0")]
public int TypeDefinitionsSize { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int RgctxEntriesOffset { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int RgctxEntriesCount { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
public int ImagesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
public int ImagesSize { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
public int AssembliesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0", LessThan = "35.0")]
public int AssembliesSize { get; private set; }
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
public int MetadataUsageListsOffset { get; private set; }
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
public int MetadataUsageListsCount { get; private set; }
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
public int MetadataUsagePairsOffset { get; private set; }
[VersionCondition(GreaterThan = "19.0", LessThan = "24.5")]
public int MetadataUsagePairsCount { get; private set; }
[VersionCondition(GreaterThan = "19.0", LessThan = "35.0")]
public int FieldRefsOffset { get; private set; }
[VersionCondition(GreaterThan = "19.0", LessThan = "35.0")]
public int FieldRefsSize { get; private set; }
[VersionCondition(GreaterThan = "20.0", LessThan = "35.0")]
public int ReferencedAssembliesOffset { get; private set; }
[VersionCondition(GreaterThan = "20.0", LessThan = "35.0")]
public int ReferencedAssembliesSize { get; private set; }
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
public int AttributesInfoOffset { get; private set; }
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
public int AttributesInfoCount { get; private set; }
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
public int AttributesTypesOffset { get; private set; }
[VersionCondition(GreaterThan = "21.0", LessThan = "27.2")]
public int AttributesTypesCount { get; private set; }
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
public int AttributeDataOffset { get; private set; }
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
public int AttributeDataSize { get; private set; }
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
public int AttributeDataRangeOffset { get; private set; }
[VersionCondition(GreaterThan = "29.0", LessThan = "35.0")]
public int AttributeDataRangeSize { get; private set; }
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
public int UnresolvedIndirectCallParameterTypesOffset { get; private set; }
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
public int UnresolvedIndirectCallParameterTypesSize { get; private set; }
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
public int UnresolvedIndirectCallParameterRangesOffset { get; private set; }
[VersionCondition(GreaterThan = "22.0", LessThan = "35.0")]
public int UnresolvedIndirectCallParameterRangesSize { get; private set; }
[VersionCondition(GreaterThan = "23.0", LessThan = "35.0")]
public int WindowsRuntimeTypeNamesOffset { get; private set; }
[VersionCondition(GreaterThan = "23.0", LessThan = "35.0")]
public int WindowsRuntimeTypeNamesSize { get; private set; }
[VersionCondition(GreaterThan = "27.0", LessThan = "35.0")]
public int WindowsRuntimeStringsOffset { get; private set; }
[VersionCondition(GreaterThan = "27.0", LessThan = "35.0")]
public int WindowsRuntimeStringsSize { get; private set; }
[VersionCondition(GreaterThan = "24.0", LessThan = "35.0")]
public int ExportedTypeDefinitionsOffset { get; private set; }
[VersionCondition(GreaterThan = "24.0", LessThan = "35.0")]
public int ExportedTypeDefinitionsSize { get; private set; }
// new, v38 metadata sections
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata StringLiterals { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata StringLiteralData { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Strings { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Events { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Properties { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Methods { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata ParameterDefaultValues { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata FieldDefaultValues { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata FieldAndParameterDefaultValueData { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata FieldMarshaledSizes { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Parameters { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Fields { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata GenericParameters { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata GenericParameterConstraints { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata GenericContainers { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata NestedTypes { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Interfaces { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata VtableMethods { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata InterfaceOffsets { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata TypeDefinitions { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Images { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata Assemblies { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata FieldRefs { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata ReferencedAssemblies { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata AttributeData { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata AttributeDataRanges { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata UnresolvedIndirectCallParameterTypes { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata UnresolvedIndirectCallParameterRanges { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata WindowsRuntimeTypeNames { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata WindowsRuntimeStrings { get; private set; }
[VersionCondition(GreaterThan = "38.0")]
public Il2CppSectionMetadata ExportedTypeDefinitions { get; private set; }
public const int ExpectedSanity = unchecked((int)0xFAB11BAF);
public readonly bool SanityValid => Sanity == ExpectedSanity;
}

View File

@@ -0,0 +1,36 @@
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using AssemblyIndex = int;
using MethodIndex = int;
using CustomAttributeIndex = int;
using VersionedSerialization.Attributes;
[VersionedStruct]
public partial record struct Il2CppImageDefinition
{
public StringIndex NameIndex { get; private set; }
public AssemblyIndex AssemblyIndex { get; private set; }
public TypeDefinitionIndex TypeStart { get; private set; }
public uint TypeCount { get; private set; }
[VersionCondition(GreaterThan = "24.0")]
public TypeDefinitionIndex ExportedTypeStart { get; private set; }
[VersionCondition(GreaterThan = "24.0")]
public uint ExportedTypeCount { get; private set; }
public MethodIndex EntryPointIndex { get; private set; }
[VersionCondition(GreaterThan = "19.0")]
public uint Token { get; private set; }
[VersionCondition(GreaterThan = "24.1")]
public CustomAttributeIndex CustomAttributeStart { get; private set; }
[VersionCondition(GreaterThan = "24.1")]
public uint CustomAttributeCount { get; private set; }
public readonly bool IsValid => NameIndex != 0;
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppInterfaceOffsetPair
{
public TypeIndex InterfaceTypeIndex { get; private set; }
public int Offset { get; private set; }
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppMetadataRange
{
public int Start { get; private set; }
public int Length { get; private set; }
}

View File

@@ -0,0 +1,64 @@
using System.Diagnostics;
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using EncodedMethodIndex = uint;
[VersionedStruct]
public partial record struct Il2CppMetadataUsage
{
private const uint TypeMask = 0b111u << 29;
private const uint InflatedMask = 0b1;
private const uint IndexMask = ~(TypeMask | InflatedMask);
public readonly Il2CppMetadataUsageType Type => (Il2CppMetadataUsageType)((EncodedValue & TypeMask) >> 29);
public readonly uint Index => (EncodedValue & IndexMask) >> 1;
public readonly bool Inflated => (EncodedValue & InflatedMask) == 1;
public EncodedMethodIndex EncodedValue;
public static Il2CppMetadataUsage FromValue(in StructVersion version, uint encodedValue)
{
if (version >= MetadataVersions.V270)
{
return new Il2CppMetadataUsage
{
EncodedValue = encodedValue
};
}
if (version >= MetadataVersions.V190)
{
// Below v27 we need to fake the 'inflated' flag, so shift the value by one
var type = (encodedValue & TypeMask) >> 29;
var value = encodedValue & (IndexMask | 1);
Debug.Assert((value & 0x10000000) == 0);
return new Il2CppMetadataUsage
{
EncodedValue = (type << 29) | (value << 1)
};
}
/* These encoded indices appear only in vtables, and are decoded by IsGenericMethodIndex/GetDecodedMethodIndex */
var methodType = (encodedValue >> 31) != 0
? Il2CppMetadataUsageType.MethodRef
: Il2CppMetadataUsageType.MethodDef;
var index = encodedValue & 0x7FFFFFFF;
Debug.Assert((index & 0x60000000) == 0);
return new Il2CppMetadataUsage
{
EncodedValue = ((uint)methodType << 29) | (index << 1)
};
}
public readonly override string ToString()
{
return $"{Type} @ 0x{Index:X}";
}
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppMetadataUsageList
{
public int Start { get; private set; }
public int Count { get; private set; }
}

View File

@@ -0,0 +1,10 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppMetadataUsagePair
{
public uint DestinationIndex { get; private set; }
public uint EncodedSourceIndex { get; private set; }
}

View File

@@ -0,0 +1,13 @@
namespace Il2CppInspector.Next.Metadata;
public enum Il2CppMetadataUsageType
{
Invalid = 0b000,
TypeInfo = 0b001,
Il2CppType = 0b010,
MethodDef = 0b011,
FieldInfo = 0b100,
StringLiteral = 0b101,
MethodRef = 0b110,
FieldRva = 0b111,
}

View File

@@ -0,0 +1,52 @@
using System.Reflection;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
[VersionedStruct]
public partial record struct Il2CppMethodDefinition
{
public StringIndex NameIndex { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
public TypeDefinitionIndex DeclaringType { get; private set; }
public TypeIndex ReturnType { get; private set; }
[VersionCondition(GreaterThan = "31.0")]
public uint ReturnParameterToken { get; private set; }
public ParameterIndex ParameterStart { get; private set; }
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex { get; private set; }
public GenericContainerIndex GenericContainerIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int MethodIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int InvokerIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int ReversePInvokeWrapperIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int RgctxStartIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int RgctxCount { get; private set; }
public uint Token { get; private set; }
public ushort Flags { get; private set; }
public ushort ImplFlags { get; private set; }
public ushort Slot { get; private set; }
public ushort ParameterCount { get; private set; }
public readonly MethodAttributes Attributes => (MethodAttributes)Flags;
public readonly MethodImplAttributes ImplAttributes => (MethodImplAttributes)ImplFlags;
public readonly bool IsValid => NameIndex != 0;
}

View File

@@ -0,0 +1,12 @@
namespace Il2CppInspector.Next.Metadata;
using DefaultValueDataIndex = int;
using VersionedSerialization.Attributes;
[VersionedStruct]
public partial record struct Il2CppParameterDefaultValue
{
public ParameterIndex ParameterIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
public DefaultValueDataIndex DataIndex { get; private set; }
}

View File

@@ -0,0 +1,18 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringIndex = int;
[VersionedStruct]
public partial record struct Il2CppParameterDefinition
{
public StringIndex NameIndex { get; private set; }
public uint Token { get; private set; }
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
public readonly bool IsValid => NameIndex != 0;
}

View File

@@ -0,0 +1,24 @@
using System.Reflection;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using MethodIndex = int;
[VersionedStruct]
public partial record struct Il2CppPropertyDefinition
{
public StringIndex NameIndex { get; private set; }
public MethodIndex Get { get; private set; }
public MethodIndex Set { get; private set; }
public PropertyAttributes Attrs { get; private set; }
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex { get; private set; }
[VersionCondition(GreaterThan = "19.0")]
public uint Token { get; private set; }
public readonly bool IsValid => NameIndex != 0;
}

View File

@@ -0,0 +1,11 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppSectionMetadata
{
public int Offset { get; private set; }
public int SectionSize { get; private set; }
public int Count { get; private set; }
}

View File

@@ -0,0 +1,12 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringLiteralIndex = int;
[VersionedStruct]
public partial record struct Il2CppStringLiteral
{
[VersionCondition(LessThan = "31.0")]
public uint Length { get; private set; }
public StringLiteralIndex DataIndex { get; private set; }
}

View File

@@ -0,0 +1,89 @@
using System.Reflection;
using VersionedSerialization;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using FieldIndex = int;
using MethodIndex = int;
using EventIndex = int;
using PropertyIndex = int;
using NestedTypeIndex = int;
using InterfacesIndex = int;
using VTableIndex = int;
[VersionedStruct]
public partial record struct Il2CppTypeDefinition
{
public static readonly TypeIndex InvalidTypeIndex = -1;
public StringIndex NameIndex { get; private set; }
public StringIndex NamespaceIndex { get; private set; }
[VersionCondition(LessThan = "24.0")]
public int CustomAttributeIndex { get; private set; }
public TypeIndex ByValTypeIndex { get; private set; }
[VersionCondition(LessThan = "24.5")]
public TypeIndex ByRefTypeIndex { get; private set; }
public TypeIndex DeclaringTypeIndex { get; private set; }
public TypeIndex ParentIndex { get; private set; }
[VersionCondition(LessThan = "31.0")]
public TypeIndex ElementTypeIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int RgctxStartIndex { get; private set; }
[VersionCondition(LessThan = "24.1")]
public int RgctxCount { get; private set; }
public GenericContainerIndex GenericContainerIndex { get; private set; }
[VersionCondition(LessThan = "22.0")]
public int ReversePInvokeWrapperIndex { get; private set; }
[VersionCondition(LessThan = "22.0")]
public int MarshalingFunctionsIndex { get; private set; }
[VersionCondition(GreaterThan = "21.0", LessThan = "22.0")]
public int CcwFunctionIndex { get; private set; }
[VersionCondition(GreaterThan = "21.0", LessThan = "22.0")]
public int GuidIndex { get; private set; }
public TypeAttributes Flags { get; private set; }
public FieldIndex FieldIndex { get; private set; }
public MethodIndex MethodIndex { get; private set; }
public EventIndex EventIndex { get; private set; }
public PropertyIndex PropertyIndex { get; private set; }
public NestedTypeIndex NestedTypeIndex { get; private set; }
public InterfacesIndex InterfacesIndex { get; private set; }
public VTableIndex VTableIndex { get; private set; }
public InterfacesIndex InterfaceOffsetsStart { get; private set; }
public ushort MethodCount { get; private set; }
public ushort PropertyCount { get; private set; }
public ushort FieldCount { get; private set; }
public ushort EventCount { get; private set; }
public ushort NestedTypeCount { get; private set; }
public ushort VTableCount { get; private set; }
public ushort InterfacesCount { get; private set; }
public ushort InterfaceOffsetsCount { get; private set; }
public Il2CppTypeDefinitionBitfield Bitfield { get; private set; }
[VersionCondition(GreaterThan = "19.0")]
public uint Token { get; private set; }
public readonly bool IsValid => NameIndex != 0;
public int GetEnumElementTypeIndex(StructVersion version)
=> version >= MetadataVersions.V350
? ParentIndex
: ElementTypeIndex;
}

View File

@@ -0,0 +1,21 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
[VersionedStruct]
public partial record struct Il2CppTypeDefinitionBitfield
{
private uint _value;
public bool ValueType => ((_value >> 0) & 1) == 1;
public bool EnumType => ((_value >> 1) & 1) == 1;
public bool HasFinalize => ((_value >> 2) & 1) == 1;
public bool HasCctor => ((_value >> 3) & 1) == 1;
public bool IsBlittable => ((_value >> 4) & 1) == 1;
public bool IsImportOrWindowsRuntime => ((_value >> 5) & 1) == 1;
public PackingSize PackingSize => (PackingSize)((_value >> 6) & 0b1111);
public bool DefaultPackingSize => ((_value >> 10) & 1) == 1;
public bool DefaultClassSize => ((_value >> 11) & 1) == 1;
public PackingSize ClassSize => (PackingSize)((_value >> 12) & 0b1111);
public bool IsByRefLike => ((_value >> 13) & 1) == 1;
}

View File

@@ -0,0 +1,11 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringIndex = int;
[VersionedStruct]
public partial record struct Il2CppWindowsRuntimeTypeNamePair
{
public StringIndex NameIndex { get; private set; }
public TypeIndex TypeIndex { get; private set; }
}

View File

@@ -0,0 +1,14 @@
namespace Il2CppInspector.Next.Metadata;
public enum PackingSize
{
Zero,
One,
Two,
Four,
Eight,
Sixteen,
ThirtyTwo,
SixtyFour,
OneHundredTwentyEight
}

View File

@@ -0,0 +1,46 @@
using NoisyCowStudios.Bin2Object;
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public struct ParameterIndex(int value) : IIndexType<ParameterIndex>, IReadable, IEquatable<ParameterIndex>
{
public const string TagPrefix = nameof(ParameterIndex);
static string IIndexType<ParameterIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<ParameterIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value;
public static int Size(in StructVersion version = default, bool is32Bit = false)
=> IIndexType<ParameterIndex>.IndexSize(version, is32Bit);
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
_value = IIndexType<ParameterIndex>.ReadIndex(ref reader, in version);
}
#region Operators + ToString
public static implicit operator int(ParameterIndex idx) => idx._value;
public static implicit operator ParameterIndex(int idx) => new(idx);
public static bool operator ==(ParameterIndex left, ParameterIndex right)
=> left._value == right._value;
public static bool operator !=(ParameterIndex left, ParameterIndex right)
=> !(left == right);
public readonly override bool Equals(object obj)
=> obj is ParameterIndex other && Equals(other);
public readonly bool Equals(ParameterIndex other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => _value.ToString();
#endregion
}

View File

@@ -0,0 +1,45 @@
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public struct TypeDefinitionIndex(int value) : IIndexType<TypeDefinitionIndex>, IReadable, IEquatable<TypeDefinitionIndex>
{
public const string TagPrefix = nameof(TypeDefinitionIndex);
static string IIndexType<TypeDefinitionIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<TypeDefinitionIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value;
public static int Size(in StructVersion version = default, bool is32Bit = false)
=> IIndexType<TypeDefinitionIndex>.IndexSize(version, is32Bit);
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
_value = IIndexType<TypeDefinitionIndex>.ReadIndex(ref reader, in version);
}
#region Operators + ToString
public static implicit operator int(TypeDefinitionIndex idx) => idx._value;
public static implicit operator TypeDefinitionIndex(int idx) => new(idx);
public static bool operator ==(TypeDefinitionIndex left, TypeDefinitionIndex right)
=> left._value == right._value;
public static bool operator !=(TypeDefinitionIndex left, TypeDefinitionIndex right)
=> !(left == right);
public readonly override bool Equals(object obj)
=> obj is TypeDefinitionIndex other && Equals(other);
public readonly bool Equals(TypeDefinitionIndex other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => _value.ToString();
#endregion
}

View File

@@ -0,0 +1,45 @@
using VersionedSerialization;
namespace Il2CppInspector.Next.Metadata;
public struct TypeIndex(int value) : IIndexType<TypeIndex>, IReadable, IEquatable<TypeIndex>
{
public const string TagPrefix = nameof(TypeIndex);
static string IIndexType<TypeIndex>.TagPrefix => TagPrefix;
static StructVersion IIndexType<TypeIndex>.AddedVersion => MetadataVersions.V390;
private int _value = value;
public static int Size(in StructVersion version = default, bool is32Bit = false)
=> IIndexType<TypeIndex>.IndexSize(version, is32Bit);
public void Read<TReader>(ref TReader reader, in StructVersion version = default) where TReader : IReader, allows ref struct
{
_value = IIndexType<TypeIndex>.ReadIndex(ref reader, in version);
}
#region Operators + ToString
public static implicit operator int(TypeIndex idx) => idx._value;
public static implicit operator TypeIndex(int idx) => new(idx);
public static bool operator ==(TypeIndex left, TypeIndex right)
=> left._value == right._value;
public static bool operator !=(TypeIndex left, TypeIndex right)
=> !(left == right);
public readonly override bool Equals(object obj)
=> obj is TypeIndex other && Equals(other);
public readonly bool Equals(TypeIndex other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => _value.ToString();
#endregion
}

View File

@@ -0,0 +1,42 @@
using VersionedSerialization;
namespace Il2CppInspector.Next;
public static class MetadataVersions
{
public static readonly StructVersion V160 = new(16);
public static readonly StructVersion V190 = new(19);
public static readonly StructVersion V210 = new(21);
public static readonly StructVersion V220 = new(22);
public static readonly StructVersion V240 = new(24);
public static readonly StructVersion V241 = new(24, 1);
public static readonly StructVersion V242 = new(24, 2);
public static readonly StructVersion V243 = new(24, 3);
public static readonly StructVersion V244 = new(24, 4);
public static readonly StructVersion V245 = new(24, 5);
public static readonly StructVersion V270 = new(27);
public static readonly StructVersion V271 = new(27, 1);
public static readonly StructVersion V272 = new(27, 2);
// These two versions have two variations:
public static readonly StructVersion V290 = new(29);
public static readonly StructVersion V310 = new(31);
// No tag - 29.0/31.0
public static readonly string Tag2022 = "2022"; // 29.1/31.1
// Unity 6000.3.0a2
public static readonly StructVersion V350 = new(35);
// Unity 6000.3.0a5
public static readonly StructVersion V380 = new(38);
// NOTE: This version uses tags to specify the size of TypeIndex, TypeDefinitionIndex, and GenericContainerIndex.
// Unity 6000.3.0b1
public static readonly StructVersion V390 = new(39);
// NOTE This version additionally uses a tag to specify the size of ParameterIndex.
}

Some files were not shown because too many files have changed in this diff Show More