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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user