Implement new GUI and CLI, fix misc. smaller issues (#22)

* Initial commit of new UI c# component

* Initial commit of new UI frontend component

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

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

* fix workflow errors

* update dependencies and remove cxxdemangler, as it was outdated

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

* smaller tweaks, hack around loops in cpp type layouting

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

* add basic support for processing LC_DYLD_CHAINED_FIXUPS

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

* fix symbol table loading in some modified elfs

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

* embed ui executable directly into c# assembly

* only build tauri component in c# release builds

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

* fix and enable binary ninja fake string segment support

* add support for metadata

* unify logic for getting element type index

* fix new ui not allowing script exports other than ida

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

* fix toAddr calls in ghidra script target

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

* tweak symbol reading a bit and remove sht relocation reading

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

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

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

* remove debug log line

* fix spelling mistakes in gui outputs

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

* change the default port for new ui dev to 5000

* show current version and hash in new ui footer

* seperate redux ui impl into FrontendCore project

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

* add redux CLI based on redux GUI output formats

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

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

* disable aot publish and enable single file for redux cli
This commit is contained in:
Luke
2025-08-15 21:13:32 +02:00
committed by GitHub
parent e161e0f226
commit 3439ca912b
184 changed files with 13425 additions and 964 deletions

View File

@@ -6,12 +6,13 @@
*/
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using Spectre.Console;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Reflection;
using System.Text.RegularExpressions;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using VersionedSerialization;
namespace Il2CppInspector
@@ -191,7 +192,7 @@ namespace Il2CppInspector
var symbols = Image.GetSymbolTable();
if (symbols.Any()) {
Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
AnsiConsole.WriteLine($"Symbol table(s) found with {symbols.Count} entries");
symbols.TryGetValue("g_CodeRegistration", out var code);
symbols.TryGetValue("g_MetadataRegistration", out var metadata);
@@ -202,13 +203,13 @@ namespace Il2CppInspector
symbols.TryGetValue("_g_MetadataRegistration", out metadata);
if (code != null && metadata != null) {
Console.WriteLine("Required structures acquired from symbol lookup");
AnsiConsole.WriteLine("Required structures acquired from symbol lookup");
return (code.VirtualAddress, metadata.VirtualAddress);
} else {
Console.WriteLine("No matches in symbol table");
AnsiConsole.WriteLine("No matches in symbol table");
}
} else if (symbols != null) {
Console.WriteLine("No symbol table present in binary file");
AnsiConsole.WriteLine("No symbol table present in binary file");
} else {
Console.WriteLine("Symbol table search not implemented for this binary format");
}
@@ -227,12 +228,12 @@ namespace Il2CppInspector
var (code, metadata) = ConsiderCode(Image, loc);
if (code != 0) {
RegistrationFunctionPointer = loc + Image.GlobalOffset;
Console.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
AnsiConsole.WriteLine("Required structures acquired from code heuristics. Initialization function: 0x{0:X16}", RegistrationFunctionPointer);
return (code, metadata);
}
}
Console.WriteLine("No matches via code heuristics");
AnsiConsole.WriteLine("No matches via code heuristics");
return null;
}
@@ -244,11 +245,11 @@ namespace Il2CppInspector
var (codePtr, metadataPtr) = ImageScan(Metadata);
if (codePtr == 0) {
Console.WriteLine("No matches via data heuristics");
AnsiConsole.WriteLine("No matches via data heuristics");
return null;
}
Console.WriteLine("Required structures acquired from data heuristics");
AnsiConsole.WriteLine("Required structures acquired from data heuristics");
return (codePtr, metadataPtr);
}
@@ -274,8 +275,8 @@ namespace Il2CppInspector
var pointerSize = Image.Bits == 32 ? 4u : 8u;
Console.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration));
Console.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
AnsiConsole.WriteLine("CodeRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? codeRegistration & 0xffff_ffff : codeRegistration, Image.MapVATR(codeRegistration));
AnsiConsole.WriteLine("MetadataRegistration struct found at 0x{0:X16} (file offset 0x{1:X8})", Image.Bits == 32 ? metadataRegistration & 0xffff_ffff : metadataRegistration, Image.MapVATR(metadataRegistration));
// Root structures from which we find everything else
CodeRegistration = Image.ReadMappedVersionedObject<Il2CppCodeRegistration>(codeRegistration);
@@ -337,7 +338,15 @@ namespace Il2CppInspector
}
// Read method invoker pointer indices - one per method
MethodInvokerIndices.Add(module, Image.ReadMappedPrimitiveArray<int>(module.InvokerIndices, (int) module.MethodPointerCount));
try
{
MethodInvokerIndices.Add(module,
Image.ReadMappedPrimitiveArray<int>(module.InvokerIndices, (int)module.MethodPointerCount));
}
catch (InvalidOperationException)
{
MethodInvokerIndices.Add(module, [..new int[(int)module.MethodPointerCount]]);
}
}
}
@@ -398,12 +407,21 @@ namespace Il2CppInspector
var type = TypeReferences[i];
if (type.Type.IsTypeDefinitionEnum())
{
type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize;
if (type.Data.Type.PointerValue >= baseDefinitionPtr)
type.Data.Value = (type.Data.Type.PointerValue - baseDefinitionPtr) / definitionSize;
Debug.Assert(Metadata!.Types.Length > type.Data.KlassIndex);
}
else if (type.Type.IsGenericParameterEnum())
{
type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize;
if (type.Data.Type.PointerValue >= baseGenericPtr)
type.Data.Value = (type.Data.Type.PointerValue - baseGenericPtr) / genericParameterSize;
Debug.Assert(Metadata!.GenericParameters.Length > type.Data.KlassIndex);
}
Debug.Assert((long)type.Data.Value >= 0);
builder.Add(type);
}
TypeReferences = builder.MoveToImmutable();

View File

@@ -5,8 +5,11 @@
*/
using Il2CppInspector.Next;
using Il2CppInspector.Next.BinaryMetadata;
using Il2CppInspector.Next.Metadata;
using Il2CppInspector.Utils;
using NoisyCowStudios.Bin2Object;
using Spectre.Console;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -14,8 +17,6 @@ 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
@@ -410,7 +411,7 @@ namespace Il2CppInspector
if (metadataFile != null) {
// Extract the metadata file to memory
if (!silent)
Console.WriteLine($"Extracting metadata from (archive){Path.DirectorySeparatorChar}{metadataFile.FullName}");
AnsiConsole.WriteLine($"Extracting metadata from (archive){Path.DirectorySeparatorChar}{metadataFile.FullName}");
metadataMemoryStream = new MemoryStream();
using var metadataStream = metadataFile.Open();
@@ -428,7 +429,7 @@ namespace Il2CppInspector
// IPAs will only have one binary (which may or may not be a UB covering multiple architectures)
if (ipaBinaryFolder != null) {
if (!silent)
Console.WriteLine($"Extracting binary from {zipStreams.First()}{Path.DirectorySeparatorChar}{binaryFiles.First().FullName}");
AnsiConsole.WriteLine($"Extracting binary from {zipStreams.First()}{Path.DirectorySeparatorChar}{binaryFiles.First().FullName}");
// Extract the binary file or package to memory
binaryMemoryStream = new MemoryStream();
@@ -531,7 +532,7 @@ namespace Il2CppInspector
return null;
}
Console.WriteLine("Detected metadata version " + metadata.Version);
AnsiConsole.WriteLine("Detected metadata version " + metadata.Version);
// Load the il2cpp code file (try all available file formats)
IFileFormatStream stream;
@@ -559,16 +560,16 @@ namespace Il2CppInspector
var processors = new List<Il2CppInspector>();
foreach (var image in stream.Images) {
Console.WriteLine("Container format: " + image.Format);
Console.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
Console.WriteLine("Architecture word size: {0}-bit", image.Bits);
Console.WriteLine("Instruction set: " + image.Arch);
Console.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
AnsiConsole.WriteLine("Container format: " + image.Format);
AnsiConsole.WriteLine("Container endianness: " + ((BinaryObjectStream) image).Endianness);
AnsiConsole.WriteLine("Architecture word size: {0}-bit", image.Bits);
AnsiConsole.WriteLine("Instruction set: " + image.Arch);
AnsiConsole.WriteLine("Global offset: 0x{0:X16}", image.GlobalOffset);
// Architecture-agnostic load attempt
try {
if (Il2CppBinary.Load(image, metadata, statusCallback) is Il2CppBinary binary) {
Console.WriteLine("IL2CPP binary version " + image.Version);
AnsiConsole.WriteLine("IL2CPP binary version " + image.Version);
processors.Add(new Il2CppInspector(binary, metadata));
}

View File

@@ -92,7 +92,7 @@ namespace Il2CppInspector
// Set object versioning for Bin2Object from metadata version
Version = new StructVersion(Header.Version);
if (Version < MetadataVersions.V160 || Version > MetadataVersions.V310) {
if (Version < MetadataVersions.V160 || Version > MetadataVersions.V350) {
throw new InvalidOperationException($"The supplied metadata file is not of a supported version ({Header.Version}).");
}
@@ -215,9 +215,26 @@ namespace Il2CppInspector
else {
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, (int)stringLiteralList[i].Length);
if (Version >= MetadataVersions.V350)
{
StringLiterals = new string[stringLiteralList.Length - 1];
for (var i = 0; i < stringLiteralList.Length; i++)
{
var currentStringDataIndex = stringLiteralList[i].DataIndex;
var nextStringDataIndex = stringLiteralList[i + 1].DataIndex;
var stringLength = nextStringDataIndex - currentStringDataIndex;
StringLiterals[i] = ReadFixedLengthString(Header.StringLiteralDataOffset + currentStringDataIndex, stringLength);
}
}
else
{
StringLiterals = new string[stringLiteralList.Length];
for (var i = 0; i < stringLiteralList.Length; i++)
StringLiterals[i] = ReadFixedLengthString(Header.StringLiteralDataOffset + stringLiteralList[i].DataIndex, (int)stringLiteralList[i].Length);
}
}
// Post-processing hook