* 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
181 lines
6.7 KiB
C#
181 lines
6.7 KiB
C#
using NoisyCowStudios.Bin2Object;
|
|
using System.Text;
|
|
using System.Diagnostics;
|
|
using Il2CppInspector.Next;
|
|
using Il2CppInspector.Next.BinaryMetadata;
|
|
using Il2CppInspector.Next.Metadata;
|
|
using Spectre.Console;
|
|
|
|
namespace Il2CppInspector.Utils;
|
|
|
|
public static class BlobReader
|
|
{
|
|
public static object GetConstantValueFromBlob(Il2CppInspector inspector, Il2CppTypeEnum type, BinaryObjectStreamReader blob)
|
|
{
|
|
const byte kArrayTypeWithDifferentElements = 1;
|
|
|
|
object value = null;
|
|
|
|
switch (type)
|
|
{
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_BOOLEAN:
|
|
value = blob.ReadBoolean();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_U1:
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_I1:
|
|
value = blob.ReadByte();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_CHAR:
|
|
// UTF-8 character assumed
|
|
value = (char)blob.ReadPrimitive<short>();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_U2:
|
|
value = blob.ReadUInt16();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_I2:
|
|
value = blob.ReadInt16();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_U4:
|
|
value = ReadUInt32();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_I4:
|
|
value = ReadInt32();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_U8:
|
|
value = blob.ReadUInt64();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_I8:
|
|
value = blob.ReadInt64();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_R4:
|
|
value = blob.ReadSingle();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_R8:
|
|
value = blob.ReadDouble();
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_STRING:
|
|
var uiLen = ReadInt32();
|
|
if (uiLen != -1)
|
|
value = Encoding.UTF8.GetString(blob.ReadBytes(uiLen));
|
|
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_SZARRAY:
|
|
var length = ReadInt32();
|
|
if (length == -1)
|
|
break;
|
|
|
|
// This is only used in custom arguments.
|
|
// We actually want the reflection TypeInfo here, but as we do not have it yet
|
|
// we store everything in a custom array type to be changed out later in the TypeModel.
|
|
|
|
var arrayElementType = ReadEncodedTypeEnum(inspector, blob, out var arrayElementDef);
|
|
var arrayElementsAreDifferent = blob.ReadByte();
|
|
|
|
var array = new ConstantBlobArrayElement[length];
|
|
if (arrayElementsAreDifferent == kArrayTypeWithDifferentElements)
|
|
{
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
var elementType = ReadEncodedTypeEnum(inspector, blob, out var elementTypeDef);
|
|
array[i] = new ConstantBlobArrayElement(elementTypeDef, GetConstantValueFromBlob(inspector, elementType, blob), elementType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
array[i] = new ConstantBlobArrayElement(arrayElementDef, GetConstantValueFromBlob(inspector, arrayElementType, blob), arrayElementType);
|
|
}
|
|
}
|
|
|
|
value = new ConstantBlobArray(arrayElementDef, array, arrayElementType);
|
|
|
|
break;
|
|
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_CLASS:
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_OBJECT:
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_GENERICINST:
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_IL2CPP_TYPE_INDEX:
|
|
var index = blob.ReadCompressedInt32();
|
|
if (index != -1)
|
|
value = inspector.TypeReferences[index];
|
|
|
|
break;
|
|
case Il2CppTypeEnum.IL2CPP_TYPE_VALUETYPE:
|
|
break;
|
|
default:
|
|
Debugger.Break();
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
|
|
int ReadInt32()
|
|
{
|
|
if (blob.Version >= MetadataVersions.V290)
|
|
{
|
|
var address = blob.Position;
|
|
|
|
try
|
|
{
|
|
return blob.ReadCompressedInt32();
|
|
}
|
|
catch (InvalidDataException)
|
|
{
|
|
AnsiConsole.WriteLine($"Found invalid compressed int at metadata address 0x{address:x8}. Reading as normal int.");
|
|
return blob.ReadInt32(address);
|
|
}
|
|
}
|
|
|
|
return blob.ReadInt32();
|
|
}
|
|
|
|
uint ReadUInt32()
|
|
{
|
|
if (blob.Version >= MetadataVersions.V290)
|
|
{
|
|
var address = blob.Position;
|
|
|
|
try
|
|
{
|
|
return blob.ReadCompressedUInt32();
|
|
}
|
|
catch (InvalidDataException)
|
|
{
|
|
AnsiConsole.WriteLine($"Found invalid compressed uint at metadata address 0x{address:x8}. Reading as normal uint.");
|
|
return blob.ReadUInt32(address);
|
|
}
|
|
}
|
|
|
|
return blob.ReadUInt32();
|
|
}
|
|
}
|
|
|
|
public static Il2CppTypeEnum ReadEncodedTypeEnum(Il2CppInspector inspector, BinaryObjectStream blob,
|
|
out Il2CppTypeDefinition enumType)
|
|
{
|
|
enumType = default;
|
|
|
|
var typeEnum = (Il2CppTypeEnum)blob.ReadByte();
|
|
if (typeEnum == Il2CppTypeEnum.IL2CPP_TYPE_ENUM)
|
|
{
|
|
var typeIndex = blob.ReadCompressedInt32();
|
|
var typeHandle = inspector.TypeReferences[typeIndex].Data.KlassIndex;
|
|
enumType = inspector.TypeDefinitions[typeHandle];
|
|
|
|
var elementTypeIndex = enumType.GetEnumElementTypeIndex(inspector.Version);
|
|
|
|
var elementTypeHandle = inspector.TypeReferences[elementTypeIndex].Data.KlassIndex;
|
|
var elementType = inspector.TypeDefinitions[elementTypeHandle];
|
|
typeEnum = inspector.TypeReferences[elementType.ByValTypeIndex].Type;
|
|
}
|
|
// This technically also handles SZARRAY (System.Array) and all others by just returning their system type
|
|
|
|
return typeEnum;
|
|
}
|
|
|
|
public record ConstantBlobArray(Il2CppTypeDefinition ArrayTypeDef, ConstantBlobArrayElement[] Elements, Il2CppTypeEnum ArrayTypeEnum);
|
|
|
|
public record ConstantBlobArrayElement(Il2CppTypeDefinition TypeDef, object Value, Il2CppTypeEnum TypeEnum);
|
|
} |