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
This commit is contained in:
Luke
2024-11-14 14:32:11 +01:00
committed by GitHub
parent 5b0476fcc5
commit b05c03964a
130 changed files with 5117 additions and 4371 deletions

View File

@@ -1,57 +1,77 @@
name: Il2CppInspectorRedux Build
on:
push:
branches: [ master ]
workflow_dispatch:
on: [push, workflow_dispatch]
jobs:
build:
build-gui:
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'
- 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.GUI
path: Il2CppInspector.GUI/bin/Release/net9.0-windows/win-x64/publish
build-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
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- 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: 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 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: Setup .NET SDK ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v3
with:
name: Il2CppInspectorRedux.GUI
path: Il2CppInspector.GUI/bin/Release/net8.0-windows/win-x64/publish
dotnet-version: ${{ matrix.dotnet-version }}
- name: Upload CLI Artifact
uses: actions/upload-artifact@v2
- name: Install dependencies
run: dotnet restore -r ${{ matrix.rid }} ./Il2CppInspector.CLI
- 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: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Il2CppInspectorRedux.CLI
path: Il2CppInspector.CLI/bin/Release/net8.0/win-x64/publish
name: Il2CppInspectorRedux.CLI-${{ matrix.rid }}
path: ./${{ matrix.rid }}

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);

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;
using Il2CppInspector.Next;
using VersionedSerialization;
namespace Il2CppInspector.Cpp.UnityHeaders
{
@@ -19,7 +21,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 +116,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;
@@ -194,8 +196,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

@@ -528,11 +528,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
@@ -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; }
@@ -254,7 +284,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 +352,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

@@ -140,76 +140,10 @@ namespace Il2CppInspector
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 +161,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 {

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,14 @@
All rights reserved.
*/
using System;
using System.Collections.Generic;
using Il2CppInspector.Next;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using VersionedSerialization;
namespace Il2CppInspector
{
@@ -34,16 +35,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 +52,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 +66,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 +81,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 +90,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 +136,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 +168,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");
@@ -277,29 +278,8 @@ namespace Il2CppInspector
Console.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 +293,36 @@ 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
|| CodeRegistration.UnresolvedVirtualCallCount > 0x4000 // >= 22
|| CodeRegistration.InteropDataCount > 0x1000 // >= 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 +331,24 @@ 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));
MethodInvokerIndices.Add(module, Image.ReadMappedPrimitiveArray<int>(module.InvokerIndices, (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 +357,66 @@ 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())
{
type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize;
}
else if (type.Type.IsGenericParameterEnum())
{
type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize;
}
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 +425,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,19 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using Il2CppInspector.Utils;
using NoisyCowStudios.Bin2Object;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using VersionedSerialization;
namespace Il2CppInspector
{
@@ -31,42 +36,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 +84,7 @@ namespace Il2CppInspector
return (0ul, null);
// Get pointer in binary to default value
var pValue = Metadata.Header.fieldAndParameterDefaultValueDataOffset + dataIndex;
var pValue = Metadata.Header.FieldAndParameterDefaultValueDataOffset + dataIndex;
var typeRef = TypeReferences[typeIndex];
// Default value is null
@@ -85,7 +92,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 +100,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 +122,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 +151,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 +161,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 +186,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 +203,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 +225,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 +251,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 +269,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 +296,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 +343,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;

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,42 @@ 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)
{
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 +249,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,16 +271,17 @@ 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)
if (Image.Version < MetadataVersions.V270)
foreach (var va in vas)
{
var mr = Image.ReadMappedVersionedObject<Il2CppMetadataRegistration>(va);
if (mr.MetadataUsagesCount == (ulong) metadata.MetadataUsageLists.Length)
metadataRegistration = va;
}
@@ -271,22 +290,17 @@ namespace Il2CppInspector
// 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)
else
{
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)

View File

@@ -7,41 +7,45 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next;
using Il2CppInspector.Next.Metadata;
using NoisyCowStudios.Bin2Object;
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>();
@@ -78,22 +82,22 @@ 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.V310) {
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);
// Sanity checking
// Unity.IL2CPP.MetadataCacheWriter.WriteLibIl2CppMetadata always writes the metadata information in the same order it appears in the header,
@@ -105,109 +109,90 @@ namespace Il2CppInspector
// 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;
var realHeaderLength = 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 = ReadVersionedObjectArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize / Sizeof<Il2CppImageDefinition>());
// 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 = ReadVersionedObjectArray<Il2CppImageDefinition>(Header.ImagesOffset, Header.ImagesSize / Sizeof<Il2CppImageDefinition>());
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 = ReadVersionedObjectArray<Il2CppTypeDefinition>(Header.TypeDefinitionsOffset, Header.TypeDefinitionsSize / Sizeof<Il2CppTypeDefinition>());
Methods = ReadVersionedObjectArray<Il2CppMethodDefinition>(Header.MethodsOffset, Header.MethodsSize / Sizeof<Il2CppMethodDefinition>());
Params = ReadVersionedObjectArray<Il2CppParameterDefinition>(Header.ParametersOffset, Header.ParametersSize / Sizeof<Il2CppParameterDefinition>());
Fields = ReadVersionedObjectArray<Il2CppFieldDefinition>(Header.FieldsOffset, Header.FieldsSize / Sizeof<Il2CppFieldDefinition>());
FieldDefaultValues = ReadVersionedObjectArray<Il2CppFieldDefaultValue>(Header.FieldDefaultValuesOffset, Header.FieldDefaultValuesSize / Sizeof<Il2CppFieldDefaultValue>());
Properties = ReadVersionedObjectArray<Il2CppPropertyDefinition>(Header.PropertiesOffset, Header.PropertiesSize / Sizeof<Il2CppPropertyDefinition>());
Events = ReadVersionedObjectArray<Il2CppEventDefinition>(Header.EventsOffset, Header.EventsSize / Sizeof<Il2CppEventDefinition>());
InterfaceUsageIndices = ReadPrimitiveArray<int>(Header.InterfacesOffset, Header.InterfacesSize / sizeof(int));
NestedTypeIndices = ReadPrimitiveArray<int>(Header.NestedTypesOffset, Header.NestedTypesSize / sizeof(int));
GenericContainers = ReadVersionedObjectArray<Il2CppGenericContainer>(Header.GenericContainersOffset, Header.GenericContainersSize / Sizeof<Il2CppGenericContainer>());
GenericParameters = ReadVersionedObjectArray<Il2CppGenericParameter>(Header.GenericParametersOffset, Header.GenericParametersSize / Sizeof<Il2CppGenericParameter>());
GenericConstraintIndices = ReadPrimitiveArray<int>(Header.GenericParameterConstraintsOffset, Header.GenericParameterConstraintsSize / sizeof(int));
InterfaceOffsets = ReadVersionedObjectArray<Il2CppInterfaceOffsetPair>(Header.InterfaceOffsetsOffset, Header.InterfaceOffsetsSize / Sizeof<Il2CppInterfaceOffsetPair>());
VTableMethodIndices = ReadPrimitiveArray<uint>(Header.VTableMethodsOffset, Header.VTableMethodsSize / sizeof(uint));
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 = ReadVersionedObjectArray<Il2CppAssemblyDefinition>(Header.AssembliesOffset, Images.Length);
if (changedAssemblyDefStruct)
Version = 24.1;
Version = MetadataVersions.V241;
ParameterDefaultValues = ReadArray<Il2CppParameterDefaultValue>(Header.parameterDefaultValuesOffset, Header.parameterDefaultValuesCount / Sizeof(typeof(Il2CppParameterDefaultValue)));
ParameterDefaultValues = ReadVersionedObjectArray<Il2CppParameterDefaultValue>(Header.ParameterDefaultValuesOffset, Header.ParameterDefaultValuesSize / Sizeof<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 >= MetadataVersions.V190 && Version < MetadataVersions.V270) {
MetadataUsageLists = ReadVersionedObjectArray<Il2CppMetadataUsageList>(Header.MetadataUsageListsOffset, Header.MetadataUsageListsCount / Sizeof<Il2CppMetadataUsageList>());
MetadataUsagePairs = ReadVersionedObjectArray<Il2CppMetadataUsagePair>(Header.MetadataUsagePairsOffset, Header.MetadataUsagePairsCount / Sizeof<Il2CppMetadataUsagePair>());
}
if (Version >= 19) {
FieldRefs = ReadArray<Il2CppFieldRef>(Header.fieldRefsOffset, Header.fieldRefsCount / Sizeof(typeof(Il2CppFieldRef)));
if (Version >= MetadataVersions.V190) {
FieldRefs = ReadVersionedObjectArray<Il2CppFieldRef>(Header.FieldRefsOffset, Header.FieldRefsSize / Sizeof<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)));
if (Version >= MetadataVersions.V210 && Version < MetadataVersions.V290) {
AttributeTypeIndices = ReadPrimitiveArray<int>(Header.AttributesTypesOffset, Header.AttributesTypesCount / sizeof(int));
AttributeTypeRanges = ReadVersionedObjectArray<Il2CppCustomAttributeTypeRange>(Header.AttributesInfoOffset, Header.AttributesInfoCount / Sizeof<Il2CppCustomAttributeTypeRange>());
}
if (Version >= 29)
if (Version >= MetadataVersions.V290)
{
AttributeDataRanges = ReadArray<Il2CppCustomAttributeDataRange>(Header.attributeDataRangeOffset,
Header.attributeDataRangeSize / Sizeof(typeof(Il2CppCustomAttributeDataRange)));
}
if (Version is 29 or 31)
{
// 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)));
}
}
AttributeDataRanges = ReadVersionedObjectArray<Il2CppCustomAttributeDataRange>(Header.AttributeDataRangeOffset,
Header.AttributeDataRangeSize / Sizeof<Il2CppCustomAttributeDataRange>());
}
// Get all metadata strings
@@ -216,10 +201,10 @@ namespace Il2CppInspector
Strings = pluginGetStringsResult.Strings;
else {
Position = Header.stringOffset;
Position = Header.StringOffset;
while (Position < Header.stringOffset + Header.stringCount)
Strings.Add((int) Position - Header.stringOffset, ReadNullTerminatedString());
while (Position < Header.StringOffset + Header.StringSize)
Strings.Add((int) Position - Header.StringOffset, ReadNullTerminatedString());
}
// Get all string literals
@@ -228,11 +213,11 @@ namespace Il2CppInspector
StringLiterals = pluginGetStringLiteralsResult.StringLiterals.ToArray();
else {
var stringLiteralList = ReadArray<Il2CppStringLiteral>(Header.stringLiteralOffset, Header.stringLiteralCount / Sizeof(typeof(Il2CppStringLiteral)));
var stringLiteralList = ReadVersionedObjectArray<Il2CppStringLiteral>(Header.StringLiteralOffset, Header.StringLiteralSize / Sizeof<Il2CppStringLiteral>());
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(Header.StringLiteralDataOffset + stringLiteralList[i].DataIndex, (int)stringLiteralList[i].Length);
}
// Post-processing hook
@@ -246,40 +231,6 @@ namespace Il2CppInspector
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,8 +40,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="dnlib" Version="4.3.0" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.1" />
<PackageReference Include="dnlib" Version="4.4.0" />
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
<PackageReference Include="CxxDemangler" Version="0.2.4.11">
<NoWarn>NU1605</NoWarn>
</PackageReference>
@@ -47,6 +49,8 @@
<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,6 +12,7 @@ using System.Linq;
using Aron.Weiler;
using Il2CppInspector.Cpp;
using Il2CppInspector.Cpp.UnityHeaders;
using Il2CppInspector.Next;
using Il2CppInspector.Reflection;
namespace Il2CppInspector.Model
@@ -54,7 +55,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 +105,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;
@@ -252,11 +254,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 +272,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. */

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,113 @@
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")]
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")]
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")]
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,37 @@
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(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,24 @@
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using TypeIndex = 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,15 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using FieldIndex = int;
using TypeIndex = 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,20 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringIndex = int;
using TypeIndex = 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,13 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using FieldIndex = int;
using TypeIndex = 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,13 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using FieldIndex = int;
using TypeIndex = 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,21 @@
using System.Reflection;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using GenericContainerIndex = int;
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,182 @@
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; }
public int StringLiteralOffset { get; private set; }
public int StringLiteralSize { get; private set; }
public int StringLiteralDataOffset { get; private set; }
public int StringLiteralDataSize { get; private set; }
public int StringOffset { get; private set; }
public int StringSize { get; private set; }
public int EventsOffset { get; private set; }
public int EventsSize { get; private set; }
public int PropertiesOffset { get; private set; }
public int PropertiesSize { get; private set; }
public int MethodsOffset { get; private set; }
public int MethodsSize { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
[VersionCondition(EqualTo = "16.0")]
public int ParameterDefaultValuesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
[VersionCondition(EqualTo = "16.0")]
public int ParameterDefaultValuesSize { get; private set; }
public int FieldDefaultValuesOffset { get; private set; }
public int FieldDefaultValuesSize { get; private set; }
public int FieldAndParameterDefaultValueDataOffset { get; private set; }
public int FieldAndParameterDefaultValueDataSize { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
public int FieldMarshaledSizesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
public int FieldMarshaledSizesSize { get; private set; }
public int ParametersOffset { get; private set; }
public int ParametersSize { get; private set; }
public int FieldsOffset { get; private set; }
public int FieldsSize { get; private set; }
public int GenericParametersOffset { get; private set; }
public int GenericParametersSize { get; private set; }
public int GenericParameterConstraintsOffset { get; private set; }
public int GenericParameterConstraintsSize { get; private set; }
public int GenericContainersOffset { get; private set; }
public int GenericContainersSize { get; private set; }
public int NestedTypesOffset { get; private set; }
public int NestedTypesSize { get; private set; }
public int InterfacesOffset { get; private set; }
public int InterfacesSize { get; private set; }
public int VTableMethodsOffset { get; private set; }
public int VTableMethodsSize { get; private set; }
public int InterfaceOffsetsOffset { get; private set; }
public int InterfaceOffsetsSize { get; private set; }
public int TypeDefinitionsOffset { get; private set; }
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")]
public int ImagesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
public int ImagesSize { get; private set; }
[VersionCondition(GreaterThan = "16.0")]
public int AssembliesOffset { get; private set; }
[VersionCondition(GreaterThan = "16.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")]
public int FieldRefsOffset { get; private set; }
[VersionCondition(GreaterThan = "19.0")]
public int FieldRefsSize { get; private set; }
[VersionCondition(GreaterThan = "20.0")]
public int ReferencedAssembliesOffset { get; private set; }
[VersionCondition(GreaterThan = "20.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")]
public int AttributeDataOffset { get; private set; }
[VersionCondition(GreaterThan = "29.0")]
public int AttributeDataSize { get; private set; }
[VersionCondition(GreaterThan = "29.0")]
public int AttributeDataRangeOffset { get; private set; }
[VersionCondition(GreaterThan = "29.0")]
public int AttributeDataRangeSize { get; private set; }
[VersionCondition(GreaterThan = "22.0")]
public int UnresolvedIndirectCallParameterTypesOffset { get; private set; }
[VersionCondition(GreaterThan = "22.0")]
public int UnresolvedIndirectCallParameterTypesSize { get; private set; }
[VersionCondition(GreaterThan = "22.0")]
public int UnresolvedIndirectCallParameterRangesOffset { get; private set; }
[VersionCondition(GreaterThan = "22.0")]
public int UnresolvedIndirectCallParameterRangesSize { get; private set; }
[VersionCondition(GreaterThan = "23.0")]
public int WindowsRuntimeTypeNamesOffset { get; private set; }
[VersionCondition(GreaterThan = "23.0")]
public int WindowsRuntimeTypeNamesSize { get; private set; }
[VersionCondition(GreaterThan = "27.0")]
public int WindowsRuntimeStringsOffset { get; private set; }
[VersionCondition(GreaterThan = "27.0")]
public int WindowsRuntimeStringsSize { get; private set; }
[VersionCondition(GreaterThan = "24.0")]
public int ExportedTypeDefinitionsOffset { get; private set; }
[VersionCondition(GreaterThan = "24.0")]
public int ExportedTypeDefinitionsSize { get; private set; }
public const int ExpectedSanity = unchecked((int)0xFAB11BAF);
public readonly bool SanityValid => Sanity == ExpectedSanity;
}

View File

@@ -0,0 +1,37 @@
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using AssemblyIndex = int;
using TypeDefinitionIndex = 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,12 @@
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using TypeIndex = int;
[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,56 @@
using System.Reflection;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using TypeDefinitionIndex = int;
using TypeIndex = int;
using ParameterIndex = int;
using GenericContainerIndex = 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(EqualTo = "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,14 @@
namespace Il2CppInspector.Next.Metadata;
using ParameterIndex = int;
using TypeIndex = int;
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,19 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringIndex = int;
using TypeIndex = 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 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringLiteralIndex = int;
[VersionedStruct]
public partial record struct Il2CppStringLiteral
{
public uint Length { get; private set; }
public StringLiteralIndex DataIndex { get; private set; }
}

View File

@@ -0,0 +1,83 @@
using System.Reflection;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next.Metadata;
using StringIndex = int;
using TypeIndex = int;
using GenericContainerIndex = 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 const 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; }
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;
}

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,12 @@
namespace Il2CppInspector.Next.Metadata;
using VersionedSerialization.Attributes;
using StringIndex = int;
using TypeIndex = 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,31 @@
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
}

View File

@@ -0,0 +1,60 @@
using System.Collections.Immutable;
using VersionedSerialization;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next;
public struct Pointer<T>(ulong value = 0) : IReadable, IEquatable<Pointer<T>> where T : struct, IReadable
{
[NativeInteger]
private ulong _value = value;
public readonly ulong PointerValue => _value;
public readonly bool Null => _value == 0;
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 readonly T Read(ref SpanReader reader, in StructVersion version)
{
reader.Offset = (int)PointerValue;
return reader.ReadVersionedObject<T>(version);
}
public readonly ImmutableArray<T> ReadArray(ref SpanReader reader, long count, in StructVersion version)
{
reader.Offset = (int)PointerValue;
return reader.ReadVersionedObjectArray<T>(count, version);
}
public static implicit operator Pointer<T>(ulong value) => new(value);
public static implicit operator ulong(Pointer<T> ptr) => ptr.PointerValue;
#region Equality operators + ToString
public static bool operator ==(Pointer<T> left, Pointer<T> right)
=> left._value == right._value;
public static bool operator !=(Pointer<T> left, Pointer<T> right)
=> !(left == right);
public readonly override bool Equals(object? obj)
=> obj is Pointer<T> other && Equals(other);
public readonly bool Equals(Pointer<T> other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => $"0x{_value:X} <{typeof(T).Name}>";
#endregion
}

View File

@@ -0,0 +1,60 @@
using System.Collections.Immutable;
using VersionedSerialization;
using VersionedSerialization.Attributes;
namespace Il2CppInspector.Next;
public struct PrimitivePointer<T>(ulong value = 0) : IReadable, IEquatable<PrimitivePointer<T>> where T : unmanaged
{
[NativeInteger]
private ulong _value = value;
public readonly ulong PointerValue => _value;
public readonly bool Null => _value == 0;
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 readonly T Read(ref SpanReader reader)
{
reader.Offset = (int)PointerValue;
return reader.ReadPrimitive<T>();
}
public readonly ImmutableArray<T> ReadArray(ref SpanReader reader, long count)
{
reader.Offset = (int)PointerValue;
return reader.ReadPrimitiveArray<T>(count);
}
public static implicit operator PrimitivePointer<T>(ulong value) => new(value);
public static implicit operator ulong(PrimitivePointer<T> ptr) => ptr.PointerValue;
#region Equality operators + ToString
public static bool operator ==(PrimitivePointer<T> left, PrimitivePointer<T> right)
=> left._value == right._value;
public static bool operator !=(PrimitivePointer<T> left, PrimitivePointer<T> right)
=> !(left == right);
public readonly override bool Equals(object? obj)
=> obj is PrimitivePointer<T> other && Equals(other);
public readonly bool Equals(PrimitivePointer<T> other)
=> this == other;
public readonly override int GetHashCode()
=> HashCode.Combine(_value);
public readonly override string ToString() => $"0x{_value:X} <{typeof(T).Name}>";
#endregion
}

View File

@@ -12,6 +12,7 @@ using System.IO;
using System.Linq;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Il2CppInspector.Next;
using Il2CppInspector.Reflection;
namespace Il2CppInspector.Outputs
@@ -186,7 +187,7 @@ namespace Il2CppInspector.Outputs
};
if (mType.IsExplicitLayout || mType.IsSequentialLayout)
mType.ClassLayout = new ClassLayoutUser(1, (uint)type.Sizes.nativeSize);
mType.ClassLayout = new ClassLayoutUser(1, (uint)type.Sizes.NativeSize);
// Add nested types
foreach (var nestedType in type.DeclaredNestedTypes)
@@ -241,7 +242,7 @@ namespace Il2CppInspector.Outputs
AddMethod(module, mType, method);
// Add token attribute
if (type.Definition != null)
if (type.Definition.IsValid)
mType.AddAttribute(module, tokenAttribute, ("Token", $"0x{type.MetadataToken:X8}"));
// Add custom attribute attributes
@@ -269,7 +270,7 @@ namespace Il2CppInspector.Outputs
if (field.HasFieldRVA) {
// Attempt to get field size
var fieldSize = field.FieldType.Sizes.nativeSize;
var fieldSize = field.FieldType.Sizes.NativeSize;
var preview = model.Package.Metadata.ReadBytes((long) field.DefaultValueMetadataAddress, fieldSize);
mField.InitialValue = preview;
@@ -310,7 +311,7 @@ namespace Il2CppInspector.Outputs
// Add token attribute
// Generic properties and constructed properties (from disperate get/set methods) have no definition
if (prop.Definition != null)
if (prop.Definition.IsValid)
mProp.AddAttribute(module, tokenAttribute, ("Token", $"0x{prop.MetadataToken:X8}"));
// Add custom attribute attributes
@@ -432,8 +433,8 @@ namespace Il2CppInspector.Outputs
("Offset", string.Format("0x{0:X}", model.Package.BinaryImage.MapVATR(method.VirtualAddress.Value.Start))),
("VA", method.VirtualAddress.Value.Start.ToAddressString())
};
if (method.Definition.slot != ushort.MaxValue)
args.Add(("Slot", method.Definition.slot.ToString()));
if (method.Definition.Slot != ushort.MaxValue)
args.Add(("Slot", method.Definition.Slot.ToString()));
mMethod.AddAttribute(module, addressAttribute, args.ToArray());
}
@@ -591,7 +592,7 @@ namespace Il2CppInspector.Outputs
// Create folder for DLLs
Directory.CreateDirectory(outputPath);
if (model.Package.Version >= 29)
if (model.Package.Version >= MetadataVersions.V290)
{
// We can now apply all attributes directly.
directApplyAttributes = model.TypesByDefinitionIndex
@@ -648,7 +649,7 @@ namespace Il2CppInspector.Outputs
AddCustomAttribute(module, module.Assembly, ca);
// Add token attributes
module.AddAttribute(module, tokenAttribute, ("Token", $"0x{asm.ImageDefinition.token:X8}"));
module.AddAttribute(module, tokenAttribute, ("Token", $"0x{asm.ImageDefinition.Token:X8}"));
module.Assembly.AddAttribute(module, tokenAttribute, ("Token", $"0x{asm.MetadataToken:X8}"));
if (types.TryGetValue(module, out var shallowTypes))

View File

@@ -353,7 +353,7 @@ namespace Il2CppInspector.Outputs
foreach (var asm in assemblies) {
text.Append($"// Image {asm.Index}: {asm.ShortName} - Assembly: {asm.FullName}");
if (!SuppressMetadata)
text.Append($" - Types {asm.ImageDefinition.typeStart}-{asm.ImageDefinition.typeStart + asm.ImageDefinition.typeCount - 1}");
text.Append($" - Types {asm.ImageDefinition.TypeStart}-{asm.ImageDefinition.TypeStart + asm.ImageDefinition.TypeCount - 1}");
text.AppendLine();
// Assembly-level attributes
@@ -426,7 +426,7 @@ namespace Il2CppInspector.Outputs
sb.Append($" // Metadata: {field.DefaultValueMetadataAddress.ToAddressString()}");
// For static array initializers, output metadata address and preview
if (field.HasFieldRVA && !SuppressMetadata) {
var preview = model.Package.Metadata.ReadBytes((long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.nativeSize);
var preview = model.Package.Metadata.ReadBytes((long) field.DefaultValueMetadataAddress, field.FieldType.Sizes.NativeSize);
sb.Append($" // Static value (base64): {Convert.ToBase64String(preview)} - Metadata: {field.DefaultValueMetadataAddress.ToAddressString()}");
}
sb.Append("\n");

View File

@@ -38,14 +38,15 @@ namespace Il2CppInspector.Outputs
using var fs = new FileStream(typeHeaderFile, FileMode.Create);
_writer = new StreamWriter(fs, Encoding.ASCII);
const string decompilerIfDef = "#if !defined(_GHIDRA_) && !defined(_IDA_) && !defined(_IDACLANG_)";
using (_writer)
{
writeHeader();
// Write primitive type definitions for when we're not including other headers
writeCode($"""
#define IS_LIBCLANG_DECOMPILER (defined(_IDACLANG_) || defined(_BINARYNINJA_))
#define IS_DECOMPILER (defined(_GHIDRA_) || defined(_IDA_) || IS_LIBCLANG_DECOMPILER)
#if defined(_GHIDRA_) || defined(_IDA_)
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
@@ -57,7 +58,7 @@ namespace Il2CppInspector.Outputs
typedef __int64 int64_t;
#endif
#ifdef _IDACLANG_
#if IS_LIBCLANG_DECOMPILER
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
@@ -68,13 +69,13 @@ namespace Il2CppInspector.Outputs
typedef long int64_t;
#endif
#if defined(_GHIDRA_) || defined(_IDACLANG_)
#if defined(_GHIDRA_) || IS_LIBCLANG_DECOMPILER
typedef int{_model.Package.BinaryImage.Bits}_t intptr_t;
typedef uint{_model.Package.BinaryImage.Bits}_t uintptr_t;
typedef uint{_model.Package.BinaryImage.Bits}_t size_t;
#endif
{decompilerIfDef}
#if !IS_DECOMPILER
#define _CPLUSPLUS_
#endif
""");
@@ -114,7 +115,7 @@ namespace Il2CppInspector.Outputs
}
// C does not support namespaces
writeCode($"{decompilerIfDef}");
writeCode("#if !IS_DECOMPILER");
writeCode("namespace app {");
writeCode("#endif");
writeLine("");
@@ -124,7 +125,7 @@ namespace Il2CppInspector.Outputs
writeTypesForGroup("Application types from usages", "types_from_usages");
writeTypesForGroup("Application unused value types", "unused_concrete_types");
writeCode($"{decompilerIfDef}");
writeCode("#if !IS_DECOMPILER");
writeCode("}");
writeCode("#endif");
}
@@ -248,7 +249,7 @@ namespace Il2CppInspector.Outputs
using (_writer)
{
writeHeader();
writeCode($"#define __IL2CPP_METADATA_VERSION {_model.Package.Version * 10:F0}");
writeCode($"#define __IL2CPP_METADATA_VERSION {_model.Package.Version.Major * 10 + _model.Package.Version.Minor * 10:F0}");
}
// Write boilerplate code

View File

@@ -8,6 +8,7 @@ using System.IO;
using System.Text.Json;
using Il2CppInspector.Reflection;
using Il2CppInspector.Model;
using Il2CppInspector.Next;
namespace Il2CppInspector.Outputs
{
@@ -182,8 +183,8 @@ namespace Il2CppInspector.Outputs
// TODO: In the future, add data ranges for the entire IL2CPP metadata tree
writeArray("arrayMetadata", () => {
if (model.Package.Version >= 24.2) {
writeObject(() => writeTypedArray(binary.CodeRegistration.pcodeGenModules, binary.Modules.Count, "struct Il2CppCodeGenModule *", "g_CodeGenModules"));
if (model.Package.Version >= MetadataVersions.V242) {
writeObject(() => writeTypedArray(binary.CodeRegistration.CodeGenModules, binary.Modules.Count, "struct Il2CppCodeGenModule *", "g_CodeGenModules"));
}
}, "IL2CPP Array Metadata");
}

View File

@@ -22,7 +22,9 @@ namespace Il2CppInspector.Outputs
public static IEnumerable<string> GetAvailableTargets() {
var ns = typeof(PythonScript).Namespace + ".ScriptResources.Targets";
var res = ResourceHelper.GetNamesForNamespace(ns);
return res.Select(s => Path.GetFileNameWithoutExtension(s.Substring(ns.Length + 1))).OrderBy(s => s);
return res
.Select(s => Path.GetFileNameWithoutExtension(s[(ns.Length + 1)..]))
.OrderBy(s => s);
}
// Output script file
@@ -52,12 +54,11 @@ namespace Il2CppInspector.Outputs
var jsonMetadataRelativePath = getRelativePath(outputFile, jsonMetadataFile);
var ns = typeof(PythonScript).Namespace + ".ScriptResources";
var preamble = ResourceHelper.GetText(ns + ".shared-preamble.py");
var main = ResourceHelper.GetText(ns + ".shared-main.py");
var api = ResourceHelper.GetText($"{ns}.Targets.{target}.py");
var ns = $"{typeof(PythonScript).Namespace}.ScriptResources";
var baseScipt = ResourceHelper.GetText($"{ns}.shared_base.py");
var impl = ResourceHelper.GetText($"{ns}.Targets.{target}.py");
var script = string.Join("\n", new [] { preamble, api, main })
var script = string.Join("\n", baseScipt, impl)
.Replace("%SCRIPTFILENAME%", Path.GetFileName(outputFile))
.Replace("%TYPE_HEADER_RELATIVE_PATH%", typeHeaderRelativePath.ToEscapedString())
.Replace("%JSON_METADATA_RELATIVE_PATH%", jsonMetadataRelativePath.ToEscapedString())

View File

@@ -0,0 +1,284 @@
from binaryninja import *
#try:
# from typing import TYPE_CHECKING
# if TYPE_CHECKING:
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
# import json
# import os
# import sys
# from datetime import datetime
#except:
# pass
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
class BinaryNinjaDisassemblerInterface(BaseDisassemblerInterface):
# this is implemented,
# however the write API does not seem to work properly here (possibly a bug),
# so this is disabled for now
supports_fake_string_segment: bool = False
_status: BaseStatusHandler
_view: BinaryView
_undo_id: str
_components: dict[str, Component]
_type_cache: dict[str, Type]
_function_type_cache: dict[str, Type]
_address_size: int
_endianness: Literal["little", "big"]
def __init__(self, status: BaseStatusHandler):
self._status = status
def _get_or_create_type(self, type: str) -> Type:
if type.startswith("struct "):
type = type[len("struct "):]
elif type.startswith("class "):
type = type[len("class "):]
if type in self._type_cache:
return self._type_cache[type]
if type.endswith("*"):
base_type = self._get_or_create_type(type[:-1].strip())
parsed = PointerType.create(self._view.arch, base_type) # type: ignore
else:
parsed = self._view.get_type_by_name(type)
if parsed is None:
parsed, errors = self._view.parse_type_string(type)
self._type_cache[type] = parsed
return parsed
def get_script_directory(self) -> str:
return CURRENT_PATH
def on_start(self):
self._view = bv # type: ignore
self._undo_id = self._view.begin_undo_actions()
self._view.set_analysis_hold(True)
self._components = {}
self._type_cache = {}
self._function_type_cache = {}
self._address_size = self._view.address_size
self._endianness = "little" if self._view.endianness == Endianness.LittleEndian else "big"
self._status.update_step("Parsing header")
with open(os.path.join(self.get_script_directory(), "il2cpp.h"), "r") as f:
parsed_types, errors = TypeParser.default.parse_types_from_source(
f.read(),
"il2cpp.h",
self._view.platform if self._view.platform is not None else Platform["windows-x86_64"],
self._view,
[
"--target=x86_64-pc-linux",
"-x", "c++",
"-D_BINARYNINJA_=1"
]
)
if parsed_types is None:
log_error("Failed to import header")
log_error(errors)
return
self._status.update_step("Importing header types", len(parsed_types.types))
def import_progress_func(progress: int, total: int):
self._status.update_progress(1)
return True
self._view.define_user_types([(x.name, x.type) for x in parsed_types.types], import_progress_func)
def on_finish(self):
self._view.commit_undo_actions(self._undo_id)
self._view.set_analysis_hold(False)
self._view.update_analysis()
def define_function(self, address: int, end: int | None = None):
if self._view.get_function_at(address) is not None:
return
self._view.create_user_function(address)
def define_data_array(self, address: int, type: str, count: int):
parsed_type = self._get_or_create_type(type)
array_type = ArrayType.create(parsed_type, count)
var = self._view.get_data_var_at(address)
if var is None:
self._view.define_user_data_var(address, array_type)
else:
var.type = array_type
def set_data_type(self, address: int, type: str):
var = self._view.get_data_var_at(address)
dtype = self._get_or_create_type(type)
if var is None:
self._view.define_user_data_var(address, dtype)
else:
var.type = dtype
def set_function_type(self, address: int, type: str):
function = self._view.get_function_at(address)
if function is None:
return
if type in self._function_type_cache:
function.type = self._function_type_cache[type] # type: ignore
else:
#log_info(f"skipping function type setting for {address}, {type}")
#pass
function.type = type.replace("this", "`this`")
def set_data_comment(self, address: int, cmt: str):
self._view.set_comment_at(address, cmt)
def set_function_comment(self, address: int, cmt: str):
function = self._view.get_function_at(address)
if function is None:
return
function.comment = cmt
def set_data_name(self, address: int, name: str):
var = self._view.get_data_var_at(address)
if var is None:
return
if name.startswith("_Z"):
type, demangled = demangle_gnu3(self._view.arch, name, self._view)
var.name = get_qualified_name(demangled)
else:
var.name = name
def set_function_name(self, address: int, name: str):
function = self._view.get_function_at(address)
if function is None:
return
if name.startswith("_Z"):
type, demangled = demangle_gnu3(self._view.arch, name, self._view)
function.name = get_qualified_name(demangled)
#function.type = type - this does not work due to the generated types not being namespaced. :(
else:
function.name = name
def add_cross_reference(self, from_address: int, to_address: int):
self._view.add_user_data_ref(from_address, to_address)
def import_c_typedef(self, type_def: str):
self._view.define_user_type(None, type_def)
# optional
def _get_or_create_component(self, name: str):
if name in self._components:
return self._components[name]
current = name
if current.count("/") != 0:
split_idx = current.rindex("/")
parent, child = current[:split_idx], current[split_idx:]
parent = self._get_or_create_component(name)
component = self._view.create_component(child, parent)
else:
component = self._view.create_component(name)
self._components[name] = component
return component
def add_function_to_group(self, address: int, group: str):
return
function = self._view.get_function_at(address)
if function is None:
return
self._get_or_create_component(group).add_function(function)
def cache_function_types(self, signatures: list[str]):
function_sigs = set(signatures)
if len(function_sigs) == 0:
return
typestr = ";\n".join(function_sigs).replace("this", "_this") + ";"
res = self._view.parse_types_from_string(typestr)
for function_sig, function in zip(function_sigs, res.functions.values()): # type: ignore
self._function_type_cache[function_sig] = function
# only required if supports_fake_string_segment == True
def create_fake_segment(self, name: str, size: int) -> int:
last_end_addr = self._view.mapped_address_ranges[-1].end
if last_end_addr % 0x1000 != 0:
last_end_addr += (0x1000 - (last_end_addr % 0x1000))
self._view.add_user_segment(last_end_addr, size, 0, 0, SegmentFlag.SegmentContainsData)
self._view.add_user_section(name, last_end_addr, size, SectionSemantics.ReadOnlyDataSectionSemantics)
return last_end_addr
def write_string(self, address: int, value: str):
self._view.write(address, value.encode() + b"\x00")
def write_address(self, address: int, value: int):
self._view.write(address, value.to_bytes(self._address_size, self._endianness))
class BinaryNinjaStatusHandler(BaseStatusHandler):
def __init__(self, thread: BackgroundTaskThread):
self.step = "Initializing"
self.max_items = 0
self.current_items = 0
self.start_time = datetime.now()
self.step_start_time = self.start_time
self.last_updated_time = datetime.min
self._thread = thread
def initialize(self): pass
def update(self):
if self.was_cancelled():
raise RuntimeError("Cancelled script.")
current_time = datetime.now()
if 0.5 > (current_time - self.last_updated_time).total_seconds():
return
self.last_updated_time = current_time
step_time = current_time - self.step_start_time
total_time = current_time - self.start_time
self._thread.progress = f"Processing IL2CPP metadata: {self.step} ({self.current_items}/{self.max_items}), elapsed: {step_time} ({total_time})"
def update_step(self, step, max_items = 0):
self.step = step
self.max_items = max_items
self.current_items = 0
self.step_start_time = datetime.now()
self.last_updated_time = datetime.min
self.update()
def update_progress(self, new_progress = 1):
self.current_items += new_progress
self.update()
def was_cancelled(self): return False
def close(self):
pass
# Entry point
class Il2CppTask(BackgroundTaskThread):
def __init__(self):
BackgroundTaskThread.__init__(self, "Processing IL2CPP metadata...", False)
def run(self):
status = BinaryNinjaStatusHandler(self)
backend = BinaryNinjaDisassemblerInterface(status)
context = ScriptContext(backend, status)
context.process()
Il2CppTask().start()

View File

@@ -1,71 +1,31 @@
# Ghidra-specific implementation
from ghidra.app.cmd.function import ApplyFunctionSignatureCmd
from ghidra.app.script import GhidraScriptUtil
from ghidra.app.util.cparser.C import CParserUtils
from ghidra.program.model.data import ArrayDataType
from ghidra.program.model.symbol import SourceType
from ghidra.program.model.symbol import RefType
from ghidra.app.cmd.label import DemanglerCmd
xrefs = currentProgram.getReferenceManager()
#try:
# from typing import TYPE_CHECKING
# if TYPE_CHECKING:
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
# import json
# import os
# import sys
# from datetime import datetime
#except:
# pass
def set_name(addr, name):
if not name.startswith("_ZN"):
createLabel(toAddr(addr), name, True)
return
cmd = DemanglerCmd(currentAddress.getAddress(hex(addr)), name)
if not cmd.applyTo(currentProgram, monitor):
print("Failed to apply demangled name to %s at %s due %s, falling back to mangled" % (name, hex(addr), cmd.getStatusMsg()))
createLabel(toAddr(addr), name, True)
class GhidraDisassemblerInterface(BaseDisassemblerInterface):
supports_fake_string_segment = False
def make_function(start, end = None):
addr = toAddr(start)
# Don't override existing functions
fn = getFunctionAt(addr)
if fn is None:
# Create new function if none exists
createFunction(addr, None)
def get_script_directory(self) -> str:
return getSourceFile().getParentFile().toString()
def make_array(addr, numItems, cppType):
if cppType.startswith('struct '):
cppType = cppType[7:]
def on_start(self):
self.xrefs = currentProgram.getReferenceManager()
t = getDataTypes(cppType)[0]
a = ArrayDataType(t, numItems, t.getLength())
addr = toAddr(addr)
removeDataAt(addr)
createData(addr, a)
def define_code(code):
# Code declarations are not supported in Ghidra
# This only affects string literals for metadata version < 19
# TODO: Replace with creating a DataType for enums
pass
def set_function_type(addr, sig):
make_function(addr)
typeSig = CParserUtils.parseSignature(None, currentProgram, sig)
ApplyFunctionSignatureCmd(toAddr(addr), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram)
def set_type(addr, cppType):
if cppType.startswith('struct '):
cppType = cppType[7:]
try:
t = getDataTypes(cppType)[0]
addr = toAddr(addr)
removeDataAt(addr)
createData(addr, t)
except:
print("Failed to set type: %s" % cppType)
def set_comment(addr, text):
setEOLComment(toAddr(addr), text)
def set_header_comment(addr, text):
setPlateComment(toAddr(addr), text)
def script_prologue(status):
# Check that the user has parsed the C headers first
if len(getDataTypes('Il2CppObject')) == 0:
print('STOP! You must import the generated C header file (%TYPE_HEADER_RELATIVE_PATH%) before running this script.')
@@ -83,24 +43,82 @@ def script_prologue(status):
# Don't trigger decompiler
setAnalysisOption(currentProgram, "Call Convention ID", "false")
def get_script_directory(): return getSourceFile().getParentFile().toString()
def on_finish(self):
pass
def script_epilogue(status): pass
def add_function_to_group(addr, group): pass
def add_xref(addr, to):
xrefs.addMemoryReference(currentAddress.getAddress(hex(addr)), currentAddress.getAddress(hex(to)), RefType.DATA, SourceType.USER_DEFINED, 0)
def define_function(self, address: int, end: int | None = None):
address = toAddr(address)
# Don't override existing functions
fn = getFunctionAt(address)
if fn is None:
# Create new function if none exists
createFunction(address, None)
def process_string_literals(status, data):
for d in jsonData['stringLiterals']:
define_string(d)
def define_data_array(self, address: int, type: str, count: int):
if type.startswith('struct '):
type = type[7:]
# I don't know how to make inline strings in Ghidra
# Just revert back original impl
addr = parse_address(d)
set_name(addr, d['name'])
set_type(addr, r'struct String *')
set_comment(addr, d['string'])
t = getDataTypes(type)[0]
a = ArrayDataType(t, count, t.getLength())
address = toAddr(address)
removeDataAt(address)
createData(address, a)
status.update_progress()
def set_data_type(self, address: int, type: str):
if type.startswith('struct '):
type = type[7:]
class StatusHandler(BaseStatusHandler): pass
try:
t = getDataTypes(type)[0]
address = toAddr(address)
removeDataAt(address)
createData(address, t)
except:
print("Failed to set type: %s" % type)
def set_function_type(self, address: int, type: str):
make_function(address)
typeSig = CParserUtils.parseSignature(None, currentProgram, type)
ApplyFunctionSignatureCmd(toAddr(address), typeSig, SourceType.USER_DEFINED, False, True).applyTo(currentProgram)
def set_data_comment(self, address: int, cmt: str):
setEOLComment(toAddr(address), cmt)
def set_function_comment(self, address: int, cmt: str):
setPlateComment(toAddr(address), cmt)
def set_data_name(self, address: int, name: str):
address = toAddr(address)
if len(name) > 2000:
print("Name length exceeds 2000 characters, skipping (%s)" % name)
return
if not name.startswith("_ZN"):
createLabel(address, name, True)
return
cmd = DemanglerCmd(address, name)
if not cmd.applyTo(currentProgram, monitor):
print(f"Failed to apply demangled name to {name} at {address} due {cmd.getStatusMsg()}, falling back to mangled")
createLabel(address, name, True)
def set_function_name(self, address: int, name: str):
return self.set_data_name(address, name)
def add_cross_reference(self, from_address: int, to_address: int):
self.xrefs.addMemoryReference(toAddr(from_address), toAddr(to_address), RefType.DATA, SourceType.USER_DEFINED, 0)
def import_c_typedef(self, type_def: str):
# Code declarations are not supported in Ghidra
# This only affects string literals for metadata version < 19
# TODO: Replace with creating a DataType for enums
pass
class GhidraStatusHandler(BaseStatusHandler):
pass
status = GhidraStatusHandler()
backend = GhidraDisassemblerInterface()
context = ScriptContext(backend, status)
context.process()

View File

@@ -8,6 +8,8 @@ import ida_nalt
import ida_ida
import ida_ua
import ida_segment
import ida_funcs
import ida_xref
try: # 7.7+
import ida_srclang
@@ -23,17 +25,62 @@ try:
except ImportError:
FOLDERS_AVAILABLE = False
cached_genflags = 0
skip_make_function = False
func_dirtree = None
is_32_bit = False
fake_segments_base = None
#try:
# from typing import TYPE_CHECKING
# if TYPE_CHECKING:
# from ..shared_base import BaseStatusHandler, BaseDisassemblerInterface, ScriptContext
# import json
# import os
# from datetime import datetime
#except:
# pass
def script_prologue(status):
global cached_genflags, skip_make_function, func_dirtree, is_32_bit, fake_segments_base
TINFO_DEFINITE = 0x0001 # These only exist in idc for some reason, so we redefine it here
DEFAULT_TIL: "til_t" = None # type: ignore
class IDADisassemblerInterface(BaseDisassemblerInterface):
supports_fake_string_segment = True
_status: BaseStatusHandler
_type_cache: dict
_folders: list
_function_dirtree: "ida_dirtree.dirtree_t"
_cached_genflags: int
_skip_function_creation: bool
_is_32_bit: bool
_fake_segments_base: int
def __init__(self, status: BaseStatusHandler):
self._status = status
self._type_cache = {}
self._folders = []
self._cached_genflags = 0
self._skip_function_creation = False
self._is_32_bit = False
self._fake_segments_base = 0
def _get_type(self, type: str):
if type not in self._type_cache:
info = ida_typeinf.idc_parse_decl(DEFAULT_TIL, type, ida_typeinf.PT_RAWARGS)
if info is None:
print(f"Failed to create type {type}.")
return None
self._type_cache[type] = info[1:]
return self._type_cache[type]
def get_script_directory(self) -> str:
return os.path.dirname(os.path.realpath(__file__))
def on_start(self):
# Disable autoanalysis
cached_genflags = ida_ida.inf_get_genflags()
ida_ida.inf_set_genflags(cached_genflags & ~ida_ida.INFFL_AUTO)
self._cached_genflags = ida_ida.inf_get_genflags()
ida_ida.inf_set_genflags(self._cached_genflags & ~ida_ida.INFFL_AUTO)
# Unload type libraries we know to cause issues - like the c++ linux one
PLATFORMS = ["x86", "x64", "arm", "arm64"]
@@ -46,16 +93,16 @@ def script_prologue(status):
# Set name mangling to GCC 3.x and display demangled as default
ida_ida.inf_set_demnames(ida_ida.DEMNAM_GCC3 | ida_ida.DEMNAM_NAME)
status.update_step('Processing Types')
self._status.update_step('Processing Types')
if IDACLANG_AVAILABLE:
header_path = os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%")
header_path = os.path.join(self.get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%")
ida_srclang.set_parser_argv("clang", "-target x86_64-pc-linux -x c++ -D_IDACLANG_=1") # -target required for 8.3+
ida_srclang.parse_decls_with_parser("clang", None, header_path, True)
else:
original_macros = ida_typeinf.get_c_macros()
ida_typeinf.set_c_macros(original_macros + ";_IDA_=1")
ida_typeinf.idc_parse_types(os.path.join(get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%"), ida_typeinf.PT_FILE)
ida_typeinf.idc_parse_types(os.path.join(self.get_script_directory(), "%TYPE_HEADER_RELATIVE_PATH%"), ida_typeinf.PT_FILE)
ida_typeinf.set_c_macros(original_macros)
# Skip make_function on Windows GameAssembly.dll files due to them predefining all functions through pdata which makes the method very slow
@@ -64,165 +111,119 @@ def script_prologue(status):
print(".pdata section found, skipping function boundaries")
if FOLDERS_AVAILABLE:
func_dirtree = ida_dirtree.get_std_dirtree(ida_dirtree.DIRTREE_FUNCS)
self._function_dirtree = ida_dirtree.get_std_dirtree(ida_dirtree.DIRTREE_FUNCS)
is_32_bit = ida_ida.inf_is_32bit_exactly()
self._is_32_bit = ida_ida.inf_is_32bit_exactly()
def script_epilogue(status):
# Reenable auto-analysis
global cached_genflags
ida_ida.inf_set_genflags(cached_genflags)
def on_finish(self):
ida_ida.inf_set_genflags(self._cached_genflags)
# Utility methods
def set_name(addr, name):
ida_name.set_name(addr, name, ida_name.SN_NOWARN | ida_name.SN_NOCHECK | ida_name.SN_FORCE)
def make_function(start, end = None):
global skip_make_function
if skip_make_function:
def define_function(self, address: int, end: int | None = None):
if self._skip_function_creation:
return
ida_bytes.del_items(start, ida_bytes.DELIT_SIMPLE, 12) # Undefine x bytes which should hopefully be enough for the first instruction
ida_ua.create_insn(start) # Create instruction at start
if not ida_funcs.add_func(start, end if end is not None else ida_idaapi.BADADDR): # This fails if the function doesn't start with an instruction
print(f"failed to mark function {hex(start)}-{hex(end) if end is not None else '???'} as function")
ida_bytes.del_items(address, ida_bytes.DELIT_SIMPLE, 12) # Undefine x bytes which should hopefully be enough for the first instruction
ida_ua.create_insn(address) # Create instruction at start
if not ida_funcs.add_func(address, end if end is not None else ida_idaapi.BADADDR): # This fails if the function doesn't start with an instruction
print(f"failed to mark function {hex(address)}-{hex(end) if end is not None else '???'} as function")
TYPE_CACHE = {}
def define_data_array(self, address: int, type: str, count: int):
self.set_data_type(address, type)
def get_type(typeName):
if typeName not in TYPE_CACHE:
info = ida_typeinf.idc_parse_decl(None, typeName, ida_typeinf.PT_RAWARGS)
if info is None:
print(f"Failed to create type {typeName}.")
return None
TYPE_CACHE[typeName] = info[1:]
return TYPE_CACHE[typeName]
TINFO_DEFINITE = 0x0001 # These only exist in idc for some reason, so we redefine it here
def set_type(addr, cppType):
cppType += ';'
info = get_type(cppType)
if info is None:
return
if ida_typeinf.apply_type(None, info[0], info[1], addr, TINFO_DEFINITE) is None:
print(f"set_type({hex(addr)}, {cppType}); failed!")
def set_function_type(addr, sig):
set_type(addr, sig)
def make_array(addr, numItems, cppType):
set_type(addr, cppType)
flags = ida_bytes.get_flags(addr)
flags = ida_bytes.get_flags(address)
if ida_bytes.is_struct(flags):
opinfo = ida_nalt.opinfo_t()
ida_bytes.get_opcode(opinfo, addr, 0, flags)
entrySize = ida_bytes.get_data_elsize(addr, flags, opinfo)
ida_bytes.get_opinfo(opinfo, address, 0, flags)
entrySize = ida_bytes.get_data_elsize(address, flags, opinfo)
tid = opinfo.tid
else:
entrySize = ida_bytes.get_item_size(addr)
entrySize = ida_bytes.get_item_size(address)
tid = ida_idaapi.BADADDR
ida_bytes.create_data(addr, flags, numItems * entrySize, tid)
ida_bytes.create_data(address, flags, count * entrySize, tid)
def define_code(code):
ida_typeinf.idc_parse_types(code)
def set_data_type(self, address: int, type: str):
type += ';'
def set_comment(addr, comment, repeatable = True):
ida_bytes.set_cmt(addr, comment, repeatable)
info = self._get_type(type)
if info is None:
return
def set_header_comment(addr, comment):
func = ida_funcs.get_func(addr)
if ida_typeinf.apply_type(DEFAULT_TIL, info[0], info[1], address, TINFO_DEFINITE) is None:
print(f"set_type({hex(address)}, {type}); failed!")
def set_function_type(self, address: int, type: str):
self.set_data_type(address, type)
def set_data_comment(self, address: int, cmt: str):
ida_bytes.set_cmt(address, cmt, False)
def set_function_comment(self, address: int, cmt: str):
func = ida_funcs.get_func(address)
if func is None:
return
ida_funcs.set_func_cmt(func, comment, True)
ida_funcs.set_func_cmt(func, cmt, True)
def get_script_directory():
return os.path.dirname(os.path.realpath(__file__))
def set_data_name(self, address: int, name: str):
ida_name.set_name(address, name, ida_name.SN_NOWARN | ida_name.SN_NOCHECK | ida_name.SN_FORCE)
folders = []
def add_function_to_group(addr, group):
global func_dirtree, folders
def set_function_name(self, address: int, name: str):
self.set_data_name(address, name)
def add_cross_reference(self, from_address: int, to_address: int):
ida_xref.add_dref(from_address, to_address, ida_xref.XREF_USER | ida_xref.dr_I)
def import_c_typedef(self, type_def: str):
ida_typeinf.idc_parse_types(type_def, 0)
# optional
def add_function_to_group(self, address: int, group: str):
if not FOLDERS_AVAILABLE or True: # enable at your own risk - this is slow
return
if not FOLDERS_AVAILABLE:
return
if group not in self._folders:
self._folders.append(group)
self._function_dirtree.mkdir(group)
if group not in folders:
folders.append(group)
func_dirtree.mkdir(group)
name = ida_funcs.get_func_name(addr)
func_dirtree.rename(name, f"{group}/{name}")
def add_xref(addr, to):
ida_xref.add_dref(addr, to, ida_xref.XREF_USER | ida_xref.dr_I)
def write_string(addr, string):
encoded_string = string.encode() + b'\x00'
string_length = len(encoded_string)
ida_bytes.put_bytes(addr, encoded_string)
ida_bytes.create_strlit(addr, string_length, ida_nalt.STRTYPE_C)
def write_address(addr, value):
global is_32_bit
if is_32_bit:
ida_bytes.put_dword(addr, value)
else:
ida_bytes.put_qword(addr, value)
def create_fake_segment(name, size):
global is_32_bit
name = ida_funcs.get_func_name(address)
self._function_dirtree.rename(name, f"{group}/{name}")
# only required if supports_fake_string_segment == True
def create_fake_segment(self, name: str, size: int) -> int:
start = ida_ida.inf_get_max_ea()
end = start + size
ida_segment.add_segm(0, start, end, name, "DATA")
segment = ida_segment.get_segm_by_name(name)
segment.bitness = 1 if is_32_bit else 2
segment.bitness = 1 if self._is_32_bit else 2
segment.perm = ida_segment.SEGPERM_READ
segment.update()
return start
def process_string_literals(status, data):
total_string_length = 0
for d in data['stringLiterals']:
total_string_length += len(d["string"]) + 1
def write_string(self, address: int, value: str):
encoded_string = value.encode() + b'\x00'
string_length = len(encoded_string)
ida_bytes.put_bytes(address, encoded_string)
ida_bytes.create_strlit(address, string_length, ida_nalt.STRTYPE_C)
aligned_length = total_string_length + (4096 - (total_string_length % 4096))
segment_base = create_fake_segment(".fake_strings", aligned_length)
current_string_address = segment_base
for d in data['stringLiterals']:
define_string(d)
ref_addr = parse_address(d)
write_string(current_string_address, d["string"])
write_address(ref_addr, current_string_address)
set_type(ref_addr, r'const char* const')
current_string_address += len(d["string"]) + 1
status.update_progress()
def write_address(self, address: int, value: int):
if self._is_32_bit:
ida_bytes.put_dword(address, value)
else:
ida_bytes.put_qword(address, value)
# Status handler
class StatusHandler(BaseStatusHandler):
class IDAStatusHandler(BaseStatusHandler):
def __init__(self):
self.step = "Initializing"
self.max_items = 0
self.current_items = 0
self.start_time = datetime.datetime.now()
self.start_time = datetime.now()
self.step_start_time = self.start_time
self.last_updated_time = datetime.datetime.min
self.last_updated_time = datetime.min
def initialize(self):
ida_kernwin.show_wait_box("Processing")
@@ -231,7 +232,7 @@ class StatusHandler(BaseStatusHandler):
if self.was_cancelled():
raise RuntimeError("Cancelled script.")
current_time = datetime.datetime.now()
current_time = datetime.now()
if 0.5 > (current_time - self.last_updated_time).total_seconds():
return
@@ -254,8 +255,8 @@ Elapsed: {step_time} ({total_time})
self.step = step
self.max_items = max_items
self.current_items = 0
self.step_start_time = datetime.datetime.now()
self.last_updated_time = datetime.datetime.min
self.step_start_time = datetime.now()
self.last_updated_time = datetime.min
self.update()
def update_progress(self, new_progress = 1):
@@ -265,5 +266,10 @@ Elapsed: {step_time} ({total_time})
def was_cancelled(self):
return ida_kernwin.user_cancelled()
def close(self):
def shutdown(self):
ida_kernwin.hide_wait_box()
status = IDAStatusHandler()
backend = IDADisassemblerInterface(status)
context = ScriptContext(backend, status)
context.process()

View File

@@ -1,182 +0,0 @@
# Shared interface
def from_hex(addr): return int(addr, 0)
def parse_address(d): return from_hex(d['virtualAddress'])
def define_il_method(jsonDef):
addr = parse_address(jsonDef)
set_name(addr, jsonDef['name'])
set_function_type(addr, jsonDef['signature'])
set_header_comment(addr, jsonDef['dotNetSignature'])
add_function_to_group(addr, jsonDef['group'])
def define_il_method_info(jsonDef):
addr = parse_address(jsonDef)
set_name(addr, jsonDef['name'])
set_comment(addr, jsonDef['dotNetSignature'])
set_type(addr, r'struct MethodInfo *')
if 'methodAddress' in jsonDef:
add_xref(from_hex(jsonDef["methodAddress"]), addr)
def define_cpp_function(jsonDef):
addr = parse_address(jsonDef)
set_name(addr, jsonDef['name'])
set_function_type(addr, jsonDef['signature'])
def define_string(jsonDef):
addr = parse_address(jsonDef)
set_name(addr, jsonDef['name'])
set_comment(addr, jsonDef['string'])
def define_field(addr, name, type, ilType = None):
addr = from_hex(addr)
set_name(addr, name)
set_type(addr, type)
if ilType is not None:
set_comment(addr, ilType)
def define_field_from_json(jsonDef):
define_field(jsonDef['virtualAddress'], jsonDef['name'], jsonDef['type'], jsonDef['dotNetType'])
def define_array(jsonDef):
addr = parse_address(jsonDef)
make_array(addr, int(jsonDef['count']), jsonDef['type'])
set_name(addr, jsonDef['name'])
def define_field_with_value(jsonDef):
addr = parse_address(jsonDef)
set_name(addr, jsonDef['name'])
set_comment(addr, jsonDef['value'])
# Process JSON
def process_json(jsonData, status):
# Function boundaries
functionAddresses = jsonData['functionAddresses']
functionAddresses.sort()
count = len(functionAddresses)
status.update_step('Processing function boundaries', count)
for i in range(count):
start = from_hex(functionAddresses[i])
if start == 0:
status.update_progress()
continue
end = from_hex(functionAddresses[i + 1]) if i + 1 != count else None
make_function(start, end)
status.update_progress()
# Method definitions
status.update_step('Processing method definitions', len(jsonData['methodDefinitions']))
for d in jsonData['methodDefinitions']:
define_il_method(d)
status.update_progress()
# Constructed generic methods
status.update_step('Processing constructed generic methods', len(jsonData['constructedGenericMethods']))
for d in jsonData['constructedGenericMethods']:
define_il_method(d)
status.update_progress()
# Custom attributes generators
status.update_step('Processing custom attributes generators', len(jsonData['customAttributesGenerators']))
for d in jsonData['customAttributesGenerators']:
define_cpp_function(d)
status.update_progress()
# Method.Invoke thunks
status.update_step('Processing Method.Invoke thunks', len(jsonData['methodInvokers']))
for d in jsonData['methodInvokers']:
define_cpp_function(d)
status.update_progress()
# String literals for version >= 19
if 'virtualAddress' in jsonData['stringLiterals'][0]:
status.update_step('Processing string literals (V19+)', len(jsonData['stringLiterals']))
process_string_literals(status, jsonData)
# String literals for version < 19
else:
status.update_step('Processing string literals (pre-V19)')
litDecl = 'enum StringLiteralIndex {\n'
for d in jsonData['stringLiterals']:
litDecl += " " + d['name'] + ",\n"
litDecl += '};\n'
define_code(litDecl)
# Il2CppClass (TypeInfo) pointers
status.update_step('Processing Il2CppClass (TypeInfo) pointers', len(jsonData['typeInfoPointers']))
for d in jsonData['typeInfoPointers']:
define_field_from_json(d)
status.update_progress()
# Il2CppType (TypeRef) pointers
status.update_step('Processing Il2CppType (TypeRef) pointers', len(jsonData['typeRefPointers']))
for d in jsonData['typeRefPointers']:
define_field(d['virtualAddress'], d['name'], r'struct Il2CppType *', d['dotNetType'])
status.update_progress()
# MethodInfo pointers
status.update_step('Processing MethodInfo pointers', len(jsonData['methodInfoPointers']))
for d in jsonData['methodInfoPointers']:
define_il_method_info(d)
status.update_progress()
# FieldInfo pointers, add the contents as a comment
status.update_step('Processing FieldInfo pointers', len(jsonData['fields']))
for d in jsonData['fields']:
define_field_with_value(d)
status.update_progress()
# FieldRva pointers, add the contents as a comment
status.update_step('Processing FieldRva pointers', len(jsonData['fieldRvas']))
for d in jsonData['fieldRvas']:
define_field_with_value(d)
status.update_progress()
# IL2CPP type metadata
status.update_step('Processing IL2CPP type metadata', len(jsonData['typeMetadata']))
for d in jsonData['typeMetadata']:
define_field(d['virtualAddress'], d['name'], d['type'])
# IL2CPP function metadata
status.update_step('Processing IL2CPP function metadata', len(jsonData['functionMetadata']))
for d in jsonData['functionMetadata']:
define_cpp_function(d)
# IL2CPP array metadata
status.update_step('Processing IL2CPP array metadata', len(jsonData['arrayMetadata']))
for d in jsonData['arrayMetadata']:
define_array(d)
# IL2CPP API functions
status.update_step('Processing IL2CPP API functions', len(jsonData['apis']))
for d in jsonData['apis']:
define_cpp_function(d)
# Entry point
print('Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty)')
status = StatusHandler()
status.initialize()
try:
start_time = datetime.datetime.now()
status.update_step("Running script prologue")
script_prologue(status)
with open(os.path.join(get_script_directory(), "%JSON_METADATA_RELATIVE_PATH%"), "r") as jsonFile:
status.update_step("Loading JSON metadata")
jsonData = json.load(jsonFile)['addressMap']
process_json(jsonData, status)
status.update_step("Running script epilogue")
script_epilogue(status)
status.update_step('Script execution complete.')
print("Took: %s" % (datetime.datetime.now() - start_time))
except RuntimeError: pass
finally: status.close()

View File

@@ -1,14 +0,0 @@
# Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty)
# Target Unity version: %TARGET_UNITY_VERSION%
import json
import os
import sys
import datetime
class BaseStatusHandler:
def initialize(self): pass
def update_step(self, name, max_items = 0): print(name)
def update_progress(self, progress = 1): pass
def was_cancelled(self): return False
def close(self): pass

View File

@@ -0,0 +1,290 @@
# Generated script file by Il2CppInspectorRedux - https://github.com/LukeFZ (Original Il2CppInspector by http://www.djkaty.com - https://github.com/djkaty)
# Target Unity version: %TARGET_UNITY_VERSION%
import json
import os
from datetime import datetime
import abc
class BaseStatusHandler(abc.ABC):
def initialize(self): pass
def shutdown(self): pass
def update_step(self, name: str, max_items: int = 0): print(name)
def update_progress(self, progress: int = 1): pass
def was_cancelled(self): return False
class BaseDisassemblerInterface(abc.ABC):
supports_fake_string_segment: bool = False
@abc.abstractmethod
def get_script_directory(self) -> str: return ""
@abc.abstractmethod
def on_start(self): pass
@abc.abstractmethod
def on_finish(self): pass
@abc.abstractmethod
def define_function(self, address: int, end: int | None = None): pass
@abc.abstractmethod
def define_data_array(self, address: int, type: str, count: int): pass
@abc.abstractmethod
def set_data_type(self, address: int, type: str): pass
@abc.abstractmethod
def set_function_type(self, address: int, type: str): pass
@abc.abstractmethod
def set_data_comment(self, address: int, cmt: str): pass
@abc.abstractmethod
def set_function_comment(self, address: int, cmt: str): pass
@abc.abstractmethod
def set_data_name(self, address: int, name: str): pass
@abc.abstractmethod
def set_function_name(self, address: int, name: str): pass
@abc.abstractmethod
def add_cross_reference(self, from_address: int, to_address: int): pass
@abc.abstractmethod
def import_c_typedef(self, type_def: str): pass
# optional
def add_function_to_group(self, address: int, group: str): pass
def cache_function_types(self, function_types: list[str]): pass
# only required if supports_fake_string_segment == True
def create_fake_segment(self, name: str, size: int) -> int: return 0
def write_string(self, address: int, value: str): pass
def write_address(self, address: int, value: int): pass
class ScriptContext:
_backend: BaseDisassemblerInterface
_status: BaseStatusHandler
def __init__(self, backend: BaseDisassemblerInterface, status: BaseStatusHandler) -> None:
self._backend = backend
self._status = status
def from_hex(self, addr: str):
return int(addr, 0)
def parse_address(self, d: dict):
return self.from_hex(d['virtualAddress'])
def define_il_method(self, definition: dict):
addr = self.parse_address(definition)
self._backend.set_function_name(addr, definition['name'])
self._backend.set_function_type(addr, definition['signature'])
self._backend.set_function_comment(addr, definition['dotNetSignature'])
self._backend.add_function_to_group(addr, definition['group'])
def define_il_method_info(self, definition: dict):
addr = self.parse_address(definition)
self._backend.set_data_type(addr, r'struct MethodInfo *')
self._backend.set_data_name(addr, definition['name'])
self._backend.set_data_comment(addr, definition['dotNetSignature'])
if 'methodAddress' in definition:
method_addr = self.from_hex(definition["methodAddress"])
self._backend.add_cross_reference(method_addr, addr)
def define_cpp_function(self, definition: dict):
addr = self.parse_address(definition)
self._backend.set_function_name(addr, definition['name'])
self._backend.set_function_type(addr, definition['signature'])
def define_string(self, definition: dict):
addr = self.parse_address(definition)
self._backend.set_data_type(addr, r'struct String *')
self._backend.set_data_name(addr, definition['name'])
self._backend.set_data_comment(addr, definition['string'])
def define_field(self, addr: str, name: str, type: str, il_type: str | None = None):
address = self.from_hex(addr)
self._backend.set_data_type(address, type)
self._backend.set_data_name(address, name)
if il_type is not None:
self._backend.set_data_comment(address, il_type)
def define_field_from_json(self, definition: dict):
self.define_field(definition['virtualAddress'], definition['name'], definition['type'], definition['dotNetType'])
def define_array(self, definition: dict):
addr = self.parse_address(definition)
self._backend.define_data_array(addr, definition['type'], int(definition['count']))
self._backend.set_data_name(addr, definition['name'])
def define_field_with_value(self, definition: dict):
addr = self.parse_address(definition)
self._backend.set_data_name(addr, definition['name'])
self._backend.set_data_comment(addr, definition['value'])
def process_metadata(self, metadata: dict):
# Function boundaries
function_addresses = metadata['functionAddresses']
function_addresses.sort()
count = len(function_addresses)
self._status.update_step('Processing function boundaries', count)
for i in range(count):
start = self.from_hex(function_addresses[i])
if start == 0:
self._status.update_progress()
continue
end = self.from_hex(function_addresses[i + 1]) if i + 1 != count else None
self._backend.define_function(start, end)
self._status.update_progress()
# Method definitions
self._status.update_step('Processing method definitions', len(metadata['methodDefinitions']))
self._backend.cache_function_types([x["signature"] for x in metadata['methodDefinitions']])
for d in metadata['methodDefinitions']:
self.define_il_method(d)
self._status.update_progress()
# Constructed generic methods
self._status.update_step('Processing constructed generic methods', len(metadata['constructedGenericMethods']))
self._backend.cache_function_types([x["signature"] for x in metadata['constructedGenericMethods']])
for d in metadata['constructedGenericMethods']:
self.define_il_method(d)
self._status.update_progress()
# Custom attributes generators
self._status.update_step('Processing custom attributes generators', len(metadata['customAttributesGenerators']))
self._backend.cache_function_types([x["signature"] for x in metadata['customAttributesGenerators']])
for d in metadata['customAttributesGenerators']:
self.define_cpp_function(d)
self._status.update_progress()
# Method.Invoke thunks
self._status.update_step('Processing Method.Invoke thunks', len(metadata['methodInvokers']))
self._backend.cache_function_types([x["signature"] for x in metadata['methodInvokers']])
for d in metadata['methodInvokers']:
self.define_cpp_function(d)
self._status.update_progress()
# String literals for version >= 19
if 'virtualAddress' in metadata['stringLiterals'][0]:
self._status.update_step('Processing string literals (V19+)', len(metadata['stringLiterals']))
if self._backend.supports_fake_string_segment:
total_string_length = 0
for d in metadata['stringLiterals']:
total_string_length += len(d["string"]) + 1
aligned_length = total_string_length + (4096 - (total_string_length % 4096))
segment_base = self._backend.create_fake_segment(".fake_strings", aligned_length)
current_string_address = segment_base
for d in metadata['stringLiterals']:
self.define_string(d)
ref_addr = self.parse_address(d)
self._backend.write_string(current_string_address, d["string"])
self._backend.set_data_type(ref_addr, r'const char* const')
self._backend.write_address(ref_addr, current_string_address)
current_string_address += len(d["string"]) + 1
self._status.update_progress()
else:
for d in metadata['stringLiterals']:
self.define_string(d)
self._status.update_progress()
# String literals for version < 19
else:
self._status.update_step('Processing string literals (pre-V19)')
litDecl = 'enum StringLiteralIndex {\n'
for d in metadata['stringLiterals']:
litDecl += " " + d['name'] + ",\n"
litDecl += '};\n'
self._backend.import_c_typedef(litDecl)
# Il2CppClass (TypeInfo) pointers
self._status.update_step('Processing Il2CppClass (TypeInfo) pointers', len(metadata['typeInfoPointers']))
for d in metadata['typeInfoPointers']:
self.define_field_from_json(d)
self._status.update_progress()
# Il2CppType (TypeRef) pointers
self._status.update_step('Processing Il2CppType (TypeRef) pointers', len(metadata['typeRefPointers']))
for d in metadata['typeRefPointers']:
self.define_field(d['virtualAddress'], d['name'], r'struct Il2CppType *', d['dotNetType'])
self._status.update_progress()
# MethodInfo pointers
self._status.update_step('Processing MethodInfo pointers', len(metadata['methodInfoPointers']))
for d in metadata['methodInfoPointers']:
self.define_il_method_info(d)
self._status.update_progress()
# FieldInfo pointers, add the contents as a comment
self._status.update_step('Processing FieldInfo pointers', len(metadata['fields']))
for d in metadata['fields']:
self.define_field_with_value(d)
self._status.update_progress()
# FieldRva pointers, add the contents as a comment
self._status.update_step('Processing FieldRva pointers', len(metadata['fieldRvas']))
for d in metadata['fieldRvas']:
self.define_field_with_value(d)
self._status.update_progress()
# IL2CPP type metadata
self._status.update_step('Processing IL2CPP type metadata', len(metadata['typeMetadata']))
for d in metadata['typeMetadata']:
self.define_field(d['virtualAddress'], d['name'], d['type'])
# IL2CPP function metadata
self._status.update_step('Processing IL2CPP function metadata', len(metadata['functionMetadata']))
for d in metadata['functionMetadata']:
self.define_cpp_function(d)
# IL2CPP array metadata
self._status.update_step('Processing IL2CPP array metadata', len(metadata['arrayMetadata']))
for d in metadata['arrayMetadata']:
self.define_array(d)
# IL2CPP API functions
self._status.update_step('Processing IL2CPP API functions', len(metadata['apis']))
self._backend.cache_function_types([x["signature"] for x in metadata['apis']])
for d in metadata['apis']:
self.define_cpp_function(d)
def process(self):
self._status.initialize()
try:
start_time = datetime.now()
self._status.update_step("Running script prologue")
self._backend.on_start()
metadata_path = os.path.join(self._backend.get_script_directory(), "%JSON_METADATA_RELATIVE_PATH%")
with open(metadata_path, "r") as f:
self._status.update_step("Loading JSON metadata")
metadata = json.load(f)['addressMap']
self.process_metadata(metadata)
self._status.update_step("Running script epilogue")
self._backend.on_finish()
self._status.update_step('Script execution complete.')
end_time = datetime.now()
print(f"Took: {end_time - start_time}")
except RuntimeError: pass
finally: self._status.shutdown()

View File

@@ -113,7 +113,8 @@ namespace Il2CppInspector
// Global enable/disable flag for entire plugin system
// If set to false, all plugins will be unloaded
// Disable this if you want to create standalone apps using the API but without plugins
private static bool _enabled = true;
private static bool _enabled = false;
public static bool Enabled {
get => _enabled;
set {
@@ -123,7 +124,7 @@ namespace Il2CppInspector
}
// All of the detected plugins, including invalid/incompatible/non-loaded plugins
public ObservableCollection<ManagedPlugin> ManagedPlugins { get; } = new ObservableCollection<ManagedPlugin>();
public ObservableCollection<ManagedPlugin> ManagedPlugins { get; } = [];
// All of the plugins that are loaded and available for use
public static IEnumerable<IPlugin> AvailablePlugins => AsInstance.ManagedPlugins.Where(p => p.Available).Select(p => p.Plugin);
@@ -136,7 +137,7 @@ namespace Il2CppInspector
=> AsInstance.ManagedPlugins.Where(p => p.Available).ToDictionary(p => p.Plugin.Id, p => p);
// The relative path from the executable that we'll search for plugins
private static string pluginFolder = Path.GetFullPath(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + Path.DirectorySeparatorChar + "plugins");
private static string pluginFolder = Path.GetFullPath(Path.GetDirectoryName(Environment.ProcessPath) + Path.DirectorySeparatorChar + "plugins");
// A placeholder plugin to be used when the real plugin cannot be loaded for some reason
private class InvalidPlugin : IPlugin

View File

@@ -7,6 +7,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection {
public class Assembly
@@ -43,37 +45,38 @@ namespace Il2CppInspector.Reflection {
public Assembly(TypeModel model, int imageIndex) {
Model = model;
ImageDefinition = Model.Package.Images[imageIndex];
AssemblyDefinition = Model.Package.Assemblies[ImageDefinition.assemblyIndex];
AssemblyDefinition = Model.Package.Assemblies[ImageDefinition.AssemblyIndex];
if (AssemblyDefinition.imageIndex != imageIndex)
if (AssemblyDefinition.ImageIndex != imageIndex)
throw new InvalidOperationException("Assembly/image index mismatch");
MetadataToken = (int) AssemblyDefinition.token;
Index = ImageDefinition.assemblyIndex;
ShortName = Model.Package.Strings[ImageDefinition.nameIndex];
MetadataToken = (int) AssemblyDefinition.Token;
Index = ImageDefinition.AssemblyIndex;
ShortName = Model.Package.Strings[ImageDefinition.NameIndex];
// Get full assembly name
var nameDef = AssemblyDefinition.aname;
var name = Regex.Replace(Model.Package.Strings[nameDef.nameIndex], @"[^A-Za-z0-9_\-\.()]", "");
var culture = Model.Package.Strings[nameDef.cultureIndex];
var nameDef = AssemblyDefinition.Aname;
var name = Regex.Replace(Model.Package.Strings[nameDef.NameIndex], @"[^A-Za-z0-9_\-\.()]", "");
var culture = Model.Package.Strings[nameDef.CultureIndex];
if (string.IsNullOrEmpty(culture))
culture = "neutral";
var pkt = BitConverter.ToString(nameDef.publicKeyToken).Replace("-", "");
var pkt = Convert.ToHexString(nameDef.PublicKeyToken);
if (pkt == "0000000000000000")
pkt = "null";
var version = string.Format($"{nameDef.major}.{nameDef.minor}.{nameDef.build}.{nameDef.revision}");
var version = string.Format($"{nameDef.Major}.{nameDef.Minor}.{nameDef.Build}.{nameDef.Revision}");
FullName = string.Format($"{name}, Version={version}, Culture={culture}, PublicKeyToken={pkt.ToLower()}");
if (ImageDefinition.entryPointIndex != -1) {
if (ImageDefinition.EntryPointIndex != -1) {
// TODO: Generate EntryPoint method from entryPointIndex
}
// Find corresponding module (we'll need this for method pointers)
ModuleDefinition = Model.Package.Modules?[ShortName];
// Find corresponding module (we'll need this for method pointers on V24.2+)
if (Model.Package.Modules != null)
ModuleDefinition = Model.Package.Modules[ShortName];
// Generate types in DefinedTypes from typeStart to typeStart+typeCount-1
for (var t = ImageDefinition.typeStart; t < ImageDefinition.typeStart + ImageDefinition.typeCount; t++) {
for (var t = ImageDefinition.TypeStart; t < ImageDefinition.TypeStart + ImageDefinition.TypeCount; t++) {
var type = new TypeInfo(t, this);
// Don't add empty module definitions

View File

@@ -4,6 +4,7 @@
All rights reserved.
*/
using Il2CppInspector.Next;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -88,13 +89,13 @@ namespace Il2CppInspector.Reflection
var pkg = asm.Model.Package;
// Attribute type ranges weren't included before v21 (customASttributeGenerators was though)
if (pkg.Version < 21)
if (pkg.Version < MetadataVersions.V210)
yield break;
if (pkg.Version < 29)
if (pkg.Version < MetadataVersions.V290)
{
var range = pkg.AttributeTypeRanges[customAttributeIndex];
for (var i = range.start; i < range.start + range.count; i++)
for (var i = range.Start; i < range.Start + range.Count; i++)
{
var typeIndex = pkg.AttributeTypeIndices[i];
@@ -117,8 +118,8 @@ namespace Il2CppInspector.Reflection
var range = pkg.Metadata.AttributeDataRanges[customAttributeIndex];
var next = pkg.Metadata.AttributeDataRanges[customAttributeIndex + 1];
var startOffset = pkg.Metadata.Header.attributeDataOffset + range.startOffset;
var endOffset = pkg.Metadata.Header.attributeDataOffset + next.startOffset;
var startOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + range.StartOffset;
var endOffset = (uint)pkg.Metadata.Header.AttributeDataOffset + next.StartOffset;
var reader = new CustomAttributeDataReader(pkg, asm, pkg.Metadata, startOffset, endOffset);
if (reader.Count == 0)
@@ -142,13 +143,17 @@ namespace Il2CppInspector.Reflection
public static IList<CustomAttributeData> GetCustomAttributes(Assembly asm, int token, int customAttributeIndex) =>
getCustomAttributes(asm, asm.Model.GetCustomAttributeIndex(asm, token, customAttributeIndex)).ToList();
public static IList<CustomAttributeData> GetCustomAttributes(Assembly asm) => GetCustomAttributes(asm, asm.MetadataToken, asm.AssemblyDefinition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(EventInfo evt) => GetCustomAttributes(evt.Assembly, evt.MetadataToken, evt.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(FieldInfo field) => GetCustomAttributes(field.Assembly, field.MetadataToken, field.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(MethodBase method) => GetCustomAttributes(method.Assembly, method.MetadataToken, method.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo param) => GetCustomAttributes(param.DeclaringMethod.Assembly, param.MetadataToken, param.Definition.customAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(Assembly asm) => GetCustomAttributes(asm, asm.MetadataToken, asm.AssemblyDefinition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(EventInfo evt) => GetCustomAttributes(evt.Assembly, evt.MetadataToken, evt.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(FieldInfo field) => GetCustomAttributes(field.Assembly, field.MetadataToken, field.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(MethodBase method) => GetCustomAttributes(method.Assembly, method.MetadataToken, method.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(ParameterInfo param) => GetCustomAttributes(param.DeclaringMethod.Assembly, param.MetadataToken, param.Definition.CustomAttributeIndex);
public static IList<CustomAttributeData> GetCustomAttributes(PropertyInfo prop)
=> prop.Definition != null ? GetCustomAttributes(prop.Assembly, prop.MetadataToken, prop.Definition.customAttributeIndex) : new List<CustomAttributeData>();
public static IList<CustomAttributeData> GetCustomAttributes(TypeInfo type) => type.Definition != null? GetCustomAttributes(type.Assembly, type.MetadataToken, type.Definition.customAttributeIndex) : new List<CustomAttributeData>();
=> prop.Definition.IsValid
? GetCustomAttributes(prop.Assembly, prop.MetadataToken, prop.Definition.CustomAttributeIndex)
: new List<CustomAttributeData>();
public static IList<CustomAttributeData> GetCustomAttributes(TypeInfo type) => type.Definition.IsValid
? GetCustomAttributes(type.Assembly, type.MetadataToken, type.Definition.CustomAttributeIndex)
: new List<CustomAttributeData>();
}
}

View File

@@ -7,6 +7,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -41,25 +42,25 @@ namespace Il2CppInspector.Reflection
public EventInfo(Il2CppInspector pkg, int eventIndex, TypeInfo declaringType) :
base(declaringType) {
Definition = pkg.Events[eventIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = eventIndex;
Name = pkg.Strings[Definition.nameIndex];
Name = pkg.Strings[Definition.NameIndex];
rootDefinition = this;
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var eventType = pkg.TypeReferences[Definition.typeIndex];
eventTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.TypeIndex);
var eventType = pkg.TypeReferences[Definition.TypeIndex];
// Copy attributes
Attributes = (EventAttributes) eventType.attrs;
Attributes = (EventAttributes) eventType.Attrs;
// NOTE: This relies on methods being added to TypeInfo.DeclaredMethods in the same order they are defined in the Il2Cpp metadata
// add, remove and raise are method indices from the first method of the declaring type
if (Definition.add >= 0)
AddMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.add);
if (Definition.remove >= 0)
RemoveMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.remove);
if (Definition.raise >= 0)
RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.raise);
if (Definition.Add >= 0)
AddMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Add);
if (Definition.Remove >= 0)
RemoveMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Remove);
if (Definition.Raise >= 0)
RaiseMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Raise);
}
public EventInfo(EventInfo eventDef, TypeInfo declaringType) : base(declaringType) {

View File

@@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection {
public class FieldInfo : MemberInfo // L-TODO: Add support for [ThreadLocal] fields
@@ -96,9 +97,9 @@ namespace Il2CppInspector.Reflection {
public FieldInfo(Il2CppInspector pkg, int fieldIndex, TypeInfo declaringType) :
base(declaringType) {
Definition = pkg.Fields[fieldIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = fieldIndex;
Name = pkg.Strings[Definition.nameIndex];
Name = pkg.Strings[Definition.NameIndex];
rawOffset = pkg.FieldOffsets[fieldIndex];
if (0 > rawOffset)
@@ -109,11 +110,11 @@ namespace Il2CppInspector.Reflection {
rootDefinition = this;
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.typeIndex);
var fieldType = pkg.TypeReferences[Definition.typeIndex];
fieldTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.TypeIndex);
var fieldType = pkg.TypeReferences[Definition.TypeIndex];
// Copy attributes
Attributes = (FieldAttributes) fieldType.attrs;
Attributes = (FieldAttributes) fieldType.Attrs;
// Default initialization value if present
if (pkg.FieldDefaultValue.TryGetValue(fieldIndex, out (ulong address, object variant) value)) {
@@ -123,7 +124,7 @@ namespace Il2CppInspector.Reflection {
}
public FieldInfo(FieldInfo fieldDef, TypeInfo declaringType) : base(declaringType) {
if (fieldDef.Definition == null)
if (!fieldDef.Definition.IsValid)
throw new ArgumentException("Argument must be a bare field definition");
rootDefinition = fieldDef;

View File

@@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -117,9 +118,9 @@ namespace Il2CppInspector.Reflection
// Initialize a method from a method definition (MethodDef)
protected MethodBase(Il2CppInspector pkg, int methodIndex, TypeInfo declaringType) : base(declaringType) {
Definition = pkg.Methods[methodIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = methodIndex;
Name = pkg.Strings[Definition.nameIndex];
Name = pkg.Strings[Definition.NameIndex];
// Find method pointer
VirtualAddress = pkg.GetMethodPointer(Assembly.ModuleDefinition, Definition);
@@ -130,28 +131,28 @@ namespace Il2CppInspector.Reflection
rootDefinition = this;
// Generic method definition?
if (Definition.genericContainerIndex >= 0) {
if (Definition.GenericContainerIndex >= 0) {
IsGenericMethod = true;
// Store the generic type parameters for later instantiation
var container = pkg.GenericContainers[Definition.genericContainerIndex];
genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc)
var container = pkg.GenericContainers[Definition.GenericContainerIndex];
genericArguments = Enumerable.Range(container.GenericParameterStart, container.TypeArgc)
.Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray();
genericMethodInstances = new Dictionary<TypeInfo[], MethodBase>(new TypeInfo.TypeArgumentsComparer());
genericMethodInstances[genericArguments] = this;
}
// Copy attributes
Attributes = (MethodAttributes) Definition.flags;
MethodImplementationFlags = (MethodImplAttributes) Definition.iflags;
Attributes = (MethodAttributes) Definition.Flags;
MethodImplementationFlags = (MethodImplAttributes) Definition.ImplFlags;
// Add arguments
for (var p = Definition.parameterStart; p < Definition.parameterStart + Definition.parameterCount; p++)
for (var p = Definition.ParameterStart; p < Definition.ParameterStart + Definition.ParameterCount; p++)
DeclaredParameters.Add(new ParameterInfo(pkg, p, this));
}
protected MethodBase(MethodBase methodDef, TypeInfo declaringType) : base(declaringType) {
if (methodDef.Definition == null)
if (!methodDef.Definition.IsValid)
throw new ArgumentException("Argument must be a bare method definition");
rootDefinition = methodDef;

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -64,28 +65,28 @@ namespace Il2CppInspector.Reflection
if (paramIndex == -1) {
Position = -1;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.returnType);
MetadataToken = declaringMethod.Definition.returnParameterToken;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, declaringMethod.Definition.ReturnType);
MetadataToken = (int)declaringMethod.Definition.ReturnParameterToken;
Attributes |= ParameterAttributes.Retval;
return;
}
Definition = pkg.Params[Index];
MetadataToken = (int) Definition.token;
Name = pkg.Strings[Definition.nameIndex];
MetadataToken = (int) Definition.Token;
Name = pkg.Strings[Definition.NameIndex];
rootDefinition = this;
// Handle unnamed/obfuscated parameter names
if (string.IsNullOrEmpty(Name))
Name = string.Format($"param_{Index:x8}");
Position = paramIndex - declaringMethod.Definition.parameterStart;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.typeIndex);
Position = paramIndex - declaringMethod.Definition.ParameterStart;
paramTypeReference = TypeRef.FromReferenceIndex(declaringMethod.Assembly.Model, Definition.TypeIndex);
var paramType = pkg.TypeReferences[Definition.typeIndex];
var paramType = pkg.TypeReferences[Definition.TypeIndex];
// Copy attributes
Attributes = (ParameterAttributes) paramType.attrs;
Attributes = (ParameterAttributes) paramType.Attrs;
// Default initialization value if present
if (pkg.ParameterDefaultValue.TryGetValue(paramIndex, out (ulong address, object variant) value)) {

View File

@@ -8,6 +8,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection {
public class PropertyInfo : MemberInfo
@@ -52,25 +53,24 @@ namespace Il2CppInspector.Reflection {
base(declaringType) {
Index = propIndex;
Definition = pkg.Properties[propIndex];
MetadataToken = (int) Definition.token;
Name = pkg.Strings[Definition.nameIndex];
MetadataToken = (int) Definition.Token;
Name = pkg.Strings[Definition.NameIndex];
rootDefinition = this;
// Copy attributes
Attributes = (PropertyAttributes) Definition.attrs;
Attributes = (PropertyAttributes) Definition.Attrs;
// prop.get and prop.set are method indices from the first method of the declaring type
if (Definition.get >= 0)
GetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.get);
if (Definition.set >= 0)
SetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.methodStart + Definition.set);
if (Definition.Get >= 0)
GetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Get);
if (Definition.Set >= 0)
SetMethod = declaringType.DeclaredMethods.First(x => x.Index == declaringType.Definition.MethodIndex + Definition.Set);
}
// Create a property based on a get and set method
public PropertyInfo(MethodInfo getter, MethodInfo setter, TypeInfo declaringType) :
base(declaringType) {
Index = -1;
Definition = null;
rootDefinition = this;
Name = (getter ?? setter).Name.Replace(".get_", ".").Replace(".set_", ".");

View File

@@ -13,6 +13,8 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
namespace Il2CppInspector.Reflection
{
@@ -53,9 +55,9 @@ namespace Il2CppInspector.Reflection
return null;
if (IsArray)
return Assembly.Model.TypesByFullName["System.Array"];
if (Definition != null) {
if (Definition.parentIndex >= 0)
return Assembly.Model.TypesByReferenceIndex[Definition.parentIndex];
if (Definition.IsValid) {
if (Definition.ParentIndex >= 0)
return Assembly.Model.TypesByReferenceIndex[Definition.ParentIndex];
}
if (genericTypeDefinition != null) {
return genericTypeDefinition.BaseType.SubstituteGenericArguments(genericArguments);
@@ -73,15 +75,15 @@ namespace Il2CppInspector.Reflection
public override TypeInfo DeclaringType {
get {
if (Definition != null) {
if (Definition.IsValid) {
/* Type definition */
if (Definition.declaringTypeIndex == -1)
if (Definition.DeclaringTypeIndex == -1)
return null;
var type = Assembly.Model.TypesByReferenceIndex[Definition.declaringTypeIndex];
var type = Assembly.Model.TypesByReferenceIndex[Definition.DeclaringTypeIndex];
if (type == null) {
/* This might happen while initially setting up the types */
var typeRef = Assembly.Model.Package.TypeReferences[Definition.declaringTypeIndex];
type = Assembly.Model.TypesByDefinitionIndex[(int)typeRef.datapoint];
var typeRef = Assembly.Model.Package.TypeReferences[Definition.DeclaringTypeIndex];
type = Assembly.Model.TypesByDefinitionIndex[typeRef.Data.KlassIndex];
}
return type;
}
@@ -253,11 +255,11 @@ namespace Il2CppInspector.Reflection
public PropertyInfo GetProperty(string name) => DeclaredProperties.FirstOrDefault(p => p.Name == name);
public MethodBase[] GetVTable() {
if (Definition != null) {
if (!Definition.IsValid) {
MetadataUsage[] vt = Assembly.Model.Package.GetVTable(Definition);
MethodBase[] res = new MethodBase[vt.Length];
for (int i = 0; i < vt.Length; i++) {
if (vt[i] != null)
if (vt[i].IsValid)
res[i] = Assembly.Model.GetMetadataUsageMethod(vt[i]);
}
return res;
@@ -667,7 +669,7 @@ namespace Il2CppInspector.Reflection
private readonly TypeRef[] implementedInterfaceReferences;
public IEnumerable<TypeInfo> ImplementedInterfaces {
get {
if (Definition != null)
if (Definition.IsValid)
return implementedInterfaceReferences.Select(x => x.Value);
if (genericTypeDefinition != null)
return genericTypeDefinition.ImplementedInterfaces.Select(t => t.SubstituteGenericArguments(genericArguments));
@@ -687,7 +689,7 @@ namespace Il2CppInspector.Reflection
public bool IsEnum { get; }
public bool IsGenericParameter { get; }
public bool IsGenericType { get; }
public bool IsGenericTypeDefinition => (Definition != null) && genericArguments.Any();
public bool IsGenericTypeDefinition => (Definition.IsValid) && genericArguments.Any();
public bool IsImport => (Attributes & TypeAttributes.Import) == TypeAttributes.Import;
public bool IsInterface => (Attributes & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface;
public bool IsNested => (MemberType & MemberTypes.NestedType) == MemberTypes.NestedType;
@@ -746,13 +748,13 @@ namespace Il2CppInspector.Reflection
Definition = pkg.TypeDefinitions[typeIndex];
Sizes = pkg.TypeDefinitionSizes[typeIndex];
MetadataToken = (int) Definition.token;
MetadataToken = (int) Definition.Token;
Index = typeIndex;
Namespace = Regex.Replace(pkg.Strings[Definition.namespaceIndex], @"[^A-Za-z0-9_\-\.<>{}]", "");
Name = pkg.Strings[Definition.nameIndex];
Namespace = Regex.Replace(pkg.Strings[Definition.NamespaceIndex], @"[^A-Za-z0-9_\-\.<>{}]", "");
Name = pkg.Strings[Definition.NameIndex];
// Nested type?
if (Definition.declaringTypeIndex >= 0) {
if (Definition.DeclaringTypeIndex >= 0) {
MemberType |= MemberTypes.NestedType;
}
@@ -760,14 +762,14 @@ namespace Il2CppInspector.Reflection
Assembly.Model.TypesByDefinitionIndex[Index] = this;
// Generic type definition?
if (Definition.genericContainerIndex >= 0) {
if (Definition.GenericContainerIndex >= 0) {
IsGenericType = true;
IsGenericParameter = false;
// Store the generic type parameters for later instantiation
var container = pkg.GenericContainers[Definition.genericContainerIndex];
var container = pkg.GenericContainers[Definition.GenericContainerIndex];
genericArguments = Enumerable.Range((int)container.genericParameterStart, container.type_argc)
genericArguments = Enumerable.Range((int)container.GenericParameterStart, container.TypeArgc)
.Select(index => Assembly.Model.GetGenericParameterType(index)).ToArray();
genericTypeInstances = new Dictionary<TypeInfo[], TypeInfo>(new TypeArgumentsComparer());
genericTypeInstances[genericArguments] = this;
@@ -777,12 +779,12 @@ namespace Il2CppInspector.Reflection
Assembly.Model.TypesByFullName[FullName] = this;
// Copy attributes
Attributes = (TypeAttributes) Definition.flags;
Attributes = (TypeAttributes) Definition.Flags;
// Enumerations - bit 1 of bitfield indicates this (also the baseTypeReference will be System.Enum)
if (((Definition.bitfield >> 1) & 1) == 1) {
if (Definition.Bitfield.EnumType) {
IsEnum = true;
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.elementTypeIndex);
enumUnderlyingTypeReference = TypeRef.FromReferenceIndex(Assembly.Model, Definition.ElementTypeIndex);
}
// Pass-by-reference type
@@ -792,24 +794,24 @@ namespace Il2CppInspector.Reflection
IsByRef = false;
// Add all implemented interfaces
implementedInterfaceReferences = new TypeRef[Definition.interfaces_count];
for (var i = 0; i < Definition.interfaces_count; i++)
implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.interfacesStart + i]);
implementedInterfaceReferences = new TypeRef[Definition.InterfacesCount];
for (var i = 0; i < Definition.InterfacesCount; i++)
implementedInterfaceReferences[i] = TypeRef.FromReferenceIndex(Assembly.Model, pkg.InterfaceUsageIndices[Definition.InterfacesIndex + i]);
// Add all nested types
declaredNestedTypes = new TypeRef[Definition.nested_type_count];
for (var n = 0; n < Definition.nested_type_count; n++)
declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.nestedTypesStart + n]);
declaredNestedTypes = new TypeRef[Definition.NestedTypeCount];
for (var n = 0; n < Definition.NestedTypeCount; n++)
declaredNestedTypes[n] = TypeRef.FromDefinitionIndex(Assembly.Model, pkg.NestedTypeIndices[Definition.NestedTypeIndex + n]);
// Add all fields
declaredFields = new List<FieldInfo>();
for (var f = Definition.fieldStart; f < Definition.fieldStart + Definition.field_count; f++)
for (var f = Definition.FieldIndex; f < Definition.FieldIndex + Definition.FieldCount; f++)
declaredFields.Add(new FieldInfo(pkg, f, this));
// Add all methods
declaredConstructors = new List<ConstructorInfo>();
declaredMethods = new List<MethodInfo>();
for (var m = Definition.methodStart; m < Definition.methodStart + Definition.method_count; m++) {
for (var m = Definition.MethodIndex; m < Definition.MethodIndex + Definition.MethodCount; m++) {
var method = new MethodInfo(pkg, m, this);
if (method.Name == ConstructorInfo.ConstructorName || method.Name == ConstructorInfo.TypeConstructorName)
declaredConstructors.Add(new ConstructorInfo(pkg, m, this));
@@ -819,7 +821,7 @@ namespace Il2CppInspector.Reflection
// Add all properties
declaredProperties = new List<PropertyInfo>();
for (var p = Definition.propertyStart; p < Definition.propertyStart + Definition.property_count; p++)
for (var p = Definition.PropertyIndex; p < Definition.PropertyIndex + Definition.PropertyCount; p++)
declaredProperties.Add(new PropertyInfo(pkg, p, this));
// There are rare cases when explicitly implemented interface properties
@@ -856,7 +858,7 @@ namespace Il2CppInspector.Reflection
// Add all events
declaredEvents = new List<EventInfo>();
for (var e = Definition.eventStart; e < Definition.eventStart + Definition.event_count; e++)
for (var e = Definition.EventIndex; e < Definition.EventIndex + Definition.EventCount; e++)
declaredEvents.Add(new EventInfo(pkg, e, this));
// TODO: Events have the same edge case issue as properties above, eg. PoGo 0.35.0
@@ -937,21 +939,21 @@ namespace Il2CppInspector.Reflection
Namespace = declaringType.Namespace;
// Special constraints
GenericParameterAttributes = (GenericParameterAttributes)param.flags;
GenericParameterAttributes = (GenericParameterAttributes)param.Flags;
// Type constraints
genericParameterConstraints = new TypeRef[param.constraintsCount];
for (int c = 0; c < param.constraintsCount; c++)
genericParameterConstraints[c] = TypeRef.FromReferenceIndex(Assembly.Model, Assembly.Model.Package.GenericConstraintIndices[param.constraintsStart + c]);
genericParameterConstraints = new TypeRef[param.ConstraintsCount];
for (int c = 0; c < param.ConstraintsCount; c++)
genericParameterConstraints[c] = TypeRef.FromReferenceIndex(Assembly.Model, Assembly.Model.Package.GenericConstraintIndices[param.ConstraintsStart + c]);
// Base type of object (set by default)
// TODO: ImplementedInterfaces should be set to interface types constraints
// Name of parameter
Name = Assembly.Model.Package.Strings[param.nameIndex];
Name = Assembly.Model.Package.Strings[param.NameIndex];
// Position
GenericParameterPosition = param.num;
GenericParameterPosition = param.Num;
IsGenericParameter = true;
IsGenericType = false;

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