diff --git a/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Nso.cs b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Nso.cs index ee84e40..5305bbe 100644 --- a/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Nso.cs +++ b/Il2CppInspector.Common/FileFormatStreams/FormatLayouts/Nso.cs @@ -136,6 +136,9 @@ public enum DynamicTag : long DT_SYMENT = 0xb, DT_INIT = 0xC, DT_FINI = 0xD, + DT_SONAME = 0xe, + DT_RPATH = 0xf, + DT_SYMBOLIC = 0x10, DT_REL = 0x11, DT_RELSZ = 0x12, DT_RELENT = 0x13, @@ -152,11 +155,19 @@ public enum DynamicTag : long DT_FLAGS = 0x1E, DT_PREINIT_ARRAY = 0x20, DT_PREINIT_ARRAYSZ = 0x21, + DT_MAXPOSTAGS = 0x22, + DT_RELRSZ = 0x23, + DT_RELR = 0x24, + DT_RELRENT = 0x25, DT_LOOS = 0x6000000D, DT_ANDROID_REL = DT_LOOS + 2, DT_ANDROID_RELSZ = DT_LOOS + 3, DT_ANDROID_RELA = DT_LOOS + 4, - DT_ANDROID_RELASZ = DT_LOOS + 5 + DT_ANDROID_RELASZ = DT_LOOS + 5, + DT_ANDROID_RELR = 0x6fffe000, + DT_ANDROID_RELRSZ = 0x6fffe001, + DT_ANDROID_RELRENT = 0x6fffe003, + DT_ANDROID_RELRCOUNT = 0x6fffe005, } public struct SymbolEntry : IReadable diff --git a/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs b/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs index 26126d0..7c37fd9 100644 --- a/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs +++ b/Il2CppInspector.Common/FileFormatStreams/NsoReader.cs @@ -8,6 +8,8 @@ using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; using VersionedSerialization; namespace Il2CppInspector; @@ -84,6 +86,17 @@ internal class ByteArrayBackingBuffer : IDisposable .ReadPrimitive(); } + public ImmutableArray ReadPrimitiveArray(ulong address, long count) where T : unmanaged + { + Debug.Assert(Initialized); + + return IsLittleEndian + ? Reader.LittleEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit)) + .ReadPrimitiveArray(count) + : Reader.BigEndian(Data, TranslateVaToRva(address), new ReaderConfig(Is32Bit)) + .ReadPrimitiveArray(count); + } + public void WriteNUInt(ulong address, ulong value) { var region = Data.Slice(TranslateVaToRva(address), Is32Bit ? sizeof(uint) : sizeof(ulong)); @@ -269,6 +282,13 @@ public class NsoReader : FileFormatStream relocationRegions.Add((jmprelAddress, jmprelAddress + size)); } + if (_dynamicEntries.TryGetValue(DynamicTag.DT_RELR, out var relrAddress)) + { + var size = _dynamicEntries[DynamicTag.DT_RELRSZ]; + ApplyRelrRelocations(relrAddress, size); + relocationRegions.Add((relrAddress, relrAddress + size)); + } + _relocationEntryRegions = [.. relocationRegions]; // Clear out relocation sections in memory so searching is faster @@ -339,6 +359,64 @@ public class NsoReader : FileFormatStream return [.. _mappedExecutable.ReadObjectArray(ImageBase + rva, checked((int)entryCount), default) .Select(x => (x.Offset, x.Info, x.Addend))]; } + + void ApplyRelrRelocations(ulong address, ulong size) + { + if (Is32Bit) + { + ApplyRelrRelocationsImpl(); + } + else + { + ApplyRelrRelocationsImpl(); + } + + return; + + void ApplyRelrRelocationsImpl() where T : unmanaged, IUnsignedNumber, IBinaryNumber + { + Debug.Assert(typeof(T) == typeof(uint) || typeof(T) == typeof(ulong)); + + var entrySize = _dynamicEntries[DynamicTag.DT_RELRENT]; + var entryCount = size / entrySize; + Debug.Assert(entrySize == (uint)Unsafe.SizeOf()); + + var relrWords = _mappedExecutable.ReadPrimitiveArray(ImageBase + address, checked((int)entryCount)); + + var baseAddr = 0ul; + for (int i = 0; i < relrWords.Length; i++) + { + var word = ulong.CreateChecked(relrWords[i]); + ulong offset; + + if ((word & 1) == 0) + { + offset = word; + var value = ulong.CreateChecked(_mappedExecutable.ReadPrimitive(ImageBase + offset)); + _mappedExecutable.WriteNUInt(ImageBase + offset, ImageBase + value); + baseAddr = offset + entrySize; + } + else + { + offset = baseAddr; + while (word != 0) + { + word >>= 1; + + if ((word & 1) != 0) + { + var value = ulong.CreateChecked(_mappedExecutable.ReadPrimitive(ImageBase + offset)); + _mappedExecutable.WriteNUInt(ImageBase + offset, ImageBase + value); + } + + offset += entrySize; + } + + baseAddr += (8 * entrySize - 1) * entrySize; + } + } + } + } } public override uint[] GetFunctionTable() => []; @@ -352,6 +430,11 @@ public class NsoReader : FileFormatStream } fileOffset = checked((uint)(uiAddr - ImageBase)); + if (fileOffset > Length) + { + return false; + } + return true; }