From 35cf3829e5f51e6c6324bfdaceccc16d26fd03cf Mon Sep 17 00:00:00 2001 From: LukeFZ <17146677+LukeFZ@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:57:32 +0100 Subject: [PATCH] map NSOs at the same base addresses as the IDA loader --- .../FileFormatStreams/NsoReader.cs | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs b/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs index 7e44259..26126d0 100644 --- a/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs @@ -1,5 +1,6 @@ #nullable enable +using Il2CppInspector.Next; using K4os.Compression.LZ4; using System.Buffers; using System.Buffers.Binary; @@ -7,7 +8,6 @@ using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using Il2CppInspector.Next; using VersionedSerialization; namespace Il2CppInspector; @@ -35,6 +35,12 @@ internal class ByteArrayBackingBuffer : IDisposable Initialized = true; } + public void SetBaseAddress(ulong baseAddress) + { + Debug.Assert(BaseAddress == 0); + BaseAddress = baseAddress; + } + protected int TranslateVaToRva(ulong address) { Debug.Assert(address >= BaseAddress); @@ -120,9 +126,6 @@ public class NsoReader : FileFormatStream public override string Arch => "ARM64"; public override string Format => "NSO"; - // NOTE: This does not work for some reason? - // public override ulong ImageBase => 0x7100000000; - protected override void Dispose(bool disposing) { if (disposing) @@ -181,13 +184,18 @@ public class NsoReader : FileFormatStream var thirdEntry = _mappedExecutable.ReadPrimitive(modInfoHeader.ModOffset + modHeader.DynamicOffset + (2 * 0x8)); _is32Bit = firstEntry > uint.MaxValue || thirdEntry > uint.MaxValue; + // These were pulled from nxo64.py, the IDA loader for switch objects + var baseAddress = Is32Bit ? 0x60000000u : 0x7100000000u; + GlobalOffset = baseAddress; + _mappedExecutable.SetBaseAddress(baseAddress); + var currentDynamicOffset = modInfoHeader.ModOffset + modHeader.DynamicOffset; var dynamicEntries = new Dictionary(); var dynamicEntrySize = (uint)DynamicEntry.StructSize(config: new ReaderConfig(Is32Bit)); while (true) { - var entry = _mappedExecutable.ReadObject(currentDynamicOffset, default); + var entry = _mappedExecutable.ReadObject(ImageBase + currentDynamicOffset, default); if (entry.Tag == DynamicTag.DT_NULL) break; @@ -266,7 +274,7 @@ public class NsoReader : FileFormatStream // Clear out relocation sections in memory so searching is faster foreach (var (start, end) in _relocationEntryRegions) { - _mappedExecutable.Clear(start, checked((long)(end - start))); + _mappedExecutable.Clear(ImageBase + start, checked((long)(end - start))); } return; @@ -283,12 +291,12 @@ public class NsoReader : FileFormatStream var offset = relocation.Offset; var type = (RelocationType)(Is32Bit ? relocation.Info & byte.MaxValue : relocation.Info & uint.MaxValue); var symbolIndex = (uint)(relocation.Info >> (Is32Bit ? 8 : 32)); - var addend = checked((ulong)(relocation.Addend ?? _mappedExecutable.ReadPrimitive(offset))); + var addend = checked((ulong)(relocation.Addend ?? _mappedExecutable.ReadPrimitive(ImageBase + offset))); if (!symbolCache.TryGetValue(symbolIndex, out var symbolEntry)) { var symtabEntryAddress = symtabAddress + symbolIndex * symtabEntrySize; - symbolEntry = _mappedExecutable.ReadObject(symtabEntryAddress, default); + symbolEntry = _mappedExecutable.ReadObject(ImageBase + symtabEntryAddress, default); symbolCache[symbolIndex] = symbolEntry; } @@ -305,7 +313,7 @@ public class NsoReader : FileFormatStream if (handled) { - _mappedExecutable.WriteNUInt(offset, value); + _mappedExecutable.WriteNUInt(ImageBase + offset, ImageBase + value); } else { @@ -314,24 +322,39 @@ public class NsoReader : FileFormatStream } } - List<(ulong, ulong, long?)> ParseRelSection(ulong address, ulong size) + List<(ulong, ulong, long?)> ParseRelSection(ulong rva, ulong size) { var entrySize = _dynamicEntries[DynamicTag.DT_RELENT]; var entryCount = size / entrySize; - return _mappedExecutable.ReadObjectArray(address, checked((int)entryCount), default) - .Select(x => (x.Offset, x.Info, null)) - .ToList(); + return [.. _mappedExecutable.ReadObjectArray(ImageBase + rva, checked((int)entryCount), default) + .Select(x => (x.Offset, x.Info, null))]; } - List<(ulong, ulong, long?)> ParseRelaSection(ulong address, ulong size) + List<(ulong, ulong, long?)> ParseRelaSection(ulong rva, ulong size) { var entrySize = _dynamicEntries[DynamicTag.DT_RELAENT]; var entryCount = size / entrySize; - return _mappedExecutable.ReadObjectArray(address, checked((int)entryCount), default) - .Select(x => (x.Offset, x.Info, x.Addend)) - .ToList(); + return [.. _mappedExecutable.ReadObjectArray(ImageBase + rva, checked((int)entryCount), default) + .Select(x => (x.Offset, x.Info, x.Addend))]; } } + + public override uint[] GetFunctionTable() => []; + + public override bool TryMapVATR(ulong uiAddr, out uint fileOffset) + { + if (uiAddr < ImageBase) + { + fileOffset = 0; + return false; + } + + fileOffset = checked((uint)(uiAddr - ImageBase)); + return true; + } + + public override ulong MapFileOffsetToVA(uint offset) + => ImageBase + offset; } \ No newline at end of file