From 00c2e8ad4442779ffa32a9228b5f0e280da6ecc3 Mon Sep 17 00:00:00 2001 From: Katy Coe Date: Sun, 9 Aug 2020 00:30:18 +0200 Subject: [PATCH] Unify symbol table format and implement for Elf and Mach-O --- .../FileFormatReaders/ElfReader.cs | 17 ++++++--- .../FileFormatReaders/FileFormatReader.cs | 4 +-- .../FileFormatReaders/FormatLayouts/Elf.cs | 23 ++++++++++-- .../FileFormatReaders/FormatLayouts/MachO.cs | 33 +++++++++++++++-- .../FileFormatReaders/MachOReader.cs | 35 +++++++++++++++---- .../FileFormatReaders/Symbol.cs | 26 ++++++++++++++ Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs | 10 +++--- 7 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 Il2CppInspector.Common/FileFormatReaders/Symbol.cs diff --git a/Il2CppInspector.Common/FileFormatReaders/ElfReader.cs b/Il2CppInspector.Common/FileFormatReaders/ElfReader.cs index 2809a5b..37d6f1d 100644 --- a/Il2CppInspector.Common/FileFormatReaders/ElfReader.cs +++ b/Il2CppInspector.Common/FileFormatReaders/ElfReader.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -131,7 +132,7 @@ namespace Il2CppInspector private TPHdr getProgramHeader(Elf programIndex) => program_header_table.FirstOrDefault(x => x.p_type == (uint) programIndex); private elf_dynamic getDynamic(Elf dynamicIndex) => dynamic_table?.FirstOrDefault(x => (Elf) conv.ULong(x.d_tag) == dynamicIndex); - private Dictionary symbolTable = new Dictionary(); + private Dictionary symbolTable = new Dictionary(); private List exports = new List(); protected abstract Elf ArchClass { get; } @@ -318,7 +319,7 @@ namespace Il2CppInspector xorRange(conv.Int(section.sh_offset), conv.Int(section.sh_size), xorValue); } - public override Dictionary GetSymbolTable() => symbolTable; + public override Dictionary GetSymbolTable() => symbolTable; public override IEnumerable GetExports() => exports; private void processSymbols() { @@ -366,10 +367,18 @@ namespace Il2CppInspector foreach (var symbol in symbol_table) { var name = ReadNullTerminatedString(conv.Long(pTab.strings) + symbol.st_name); + var type = symbol.type == Elf.STT_FUNC? SymbolType.Function + : symbol.type == Elf.STT_OBJECT || symbol.type == Elf.STT_COMMON? SymbolType.Name + : SymbolType.Unknown; + + if (symbol.st_shndx == (ushort) Elf.SHN_UNDEF) + type = SymbolType.Import; + // Avoid duplicates - symbolTable.TryAdd(name, conv.ULong(symbol.st_value)); + var symbolItem = new Symbol {Name = name, Type = type, VirtualAddress = conv.ULong(symbol.st_value) }; + symbolTable.TryAdd(name, symbolItem); if (symbol.st_shndx != (ushort) Elf.SHN_UNDEF) - exportTable.TryAdd(name, new Export {Name = name, VirtualAddress = conv.ULong(symbol.st_value)}); + exportTable.TryAdd(name, new Export {Name = symbolItem.DemangledName, VirtualAddress = conv.ULong(symbol.st_value)}); } } diff --git a/Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs b/Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs index f30e5c8..473780c 100644 --- a/Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs +++ b/Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs @@ -27,7 +27,7 @@ namespace Il2CppInspector int Bits { get; } ulong GlobalOffset { get; } // The virtual address where the code section (.text) would be loaded in memory ulong ImageBase { get; } // The virtual address of where the image would be loaded in memory (same as GlobalOffset except for PE) - Dictionary GetSymbolTable(); + Dictionary GetSymbolTable(); uint[] GetFunctionTable(); IEnumerable GetExports(); U ReadMappedObject(ulong uiAddr) where U : new(); @@ -140,7 +140,7 @@ namespace Il2CppInspector public virtual IFileFormatReader this[uint index] => (index == 0)? this : throw new IndexOutOfRangeException("Binary image index out of bounds"); // Find search locations in the symbol table for Il2Cpp data - public virtual Dictionary GetSymbolTable() => null; + public virtual Dictionary GetSymbolTable() => new Dictionary(); // Find search locations in the machine code for Il2Cpp data public virtual uint[] GetFunctionTable() => throw new NotImplementedException(); diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs b/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs index 0ee44a1..518aa6b 100644 --- a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs +++ b/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs @@ -46,6 +46,19 @@ namespace Il2CppInspector // SHNs SHN_UNDEF = 0, + // STTs + STT_NOTYPE = 0, + STT_OBJECT = 1, + STT_FUNC = 2, + STT_SECTION = 3, + STT_FILE = 4, + STT_COMMON = 5, + STT_LOOS = 10, + STT_HIOS = 12, + STT_LOPROC = 13, + STT_SPARC_REGISTER = 13, + STT_HIPROC = 15, + // dynamic sections DT_STRTAB = 5, DT_SYMTAB = 6, @@ -192,6 +205,8 @@ namespace Il2CppInspector uint st_name { get; } TWord st_value { get; } ushort st_shndx { get; } + Elf st_info { get; } + Elf type { get; } } internal class elf_32_sym : Ielf_sym @@ -199,11 +214,13 @@ namespace Il2CppInspector public uint st_name => f_st_name; public uint st_value => f_st_value; public ushort st_shndx => f_st_shndx; + public Elf st_info => (Elf) f_st_info; + public Elf type => (Elf) (f_st_info & 0xf); public uint f_st_name; public uint f_st_value; public uint st_size; - public byte st_info; + public byte f_st_info; public byte st_other; public ushort f_st_shndx; } @@ -213,9 +230,11 @@ namespace Il2CppInspector public uint st_name => f_st_name; public ulong st_value => f_st_value; public ushort st_shndx => f_st_shndx; + public Elf st_info => (Elf) f_st_info; + public Elf type => (Elf) (f_st_info & 0xf); public uint f_st_name; - public byte st_info; + public byte f_st_info; public byte st_other; public ushort f_st_shndx; public ulong f_st_value; diff --git a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs b/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs index 143c467..b71c3f5 100644 --- a/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs +++ b/Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs @@ -4,6 +4,7 @@ All rights reserved. */ +using System; using NoisyCowStudios.Bin2Object; namespace Il2CppInspector @@ -33,7 +34,34 @@ namespace Il2CppInspector CPU_TYPE_X86 = 7, CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86, CPU_TYPE_ARM = 12, - CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM + CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM, + } + + [Flags] + public enum MachO_NType : byte + { + // n_type masks + N_STAB = 0xe0, + N_PEXT = 0x10, + N_TYPE = 0x0e, + N_EXT = 0x01, + + // n_stab bits + N_UNDF = 0x00, + N_ABS = 0x02, + N_SECT = 0x0e, + N_PBUD = 0x0c, + N_INDR = 0x0a, + + // n_type bits when N_STAB has some bits set + N_GSYM = 0x20, + N_FNAME = 0x22, + N_FUN = 0x24, + N_STSYM = 0x26, + N_BNSYM = 0x2E, + N_ENSYM = 0x4E, + N_SO = 0x64, + N_OSO = 0x66, } internal class MachOHeader where TWord : struct @@ -124,8 +152,9 @@ namespace Il2CppInspector internal class MachO_nlist where TWord : struct { + public MachO_NType n_type => (MachO_NType) f_n_type; public uint n_strx; - public byte n_type; + public byte f_n_type; public byte n_sect; public ushort n_desc; public TWord n_value; diff --git a/Il2CppInspector.Common/FileFormatReaders/MachOReader.cs b/Il2CppInspector.Common/FileFormatReaders/MachOReader.cs index 6507b4c..8596efc 100644 --- a/Il2CppInspector.Common/FileFormatReaders/MachOReader.cs +++ b/Il2CppInspector.Common/FileFormatReaders/MachOReader.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using NoisyCowStudios.Bin2Object; @@ -228,8 +229,8 @@ namespace Il2CppInspector public override uint[] GetFunctionTable() => ReadArray(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray(); - public override Dictionary GetSymbolTable() { - var symbols = new Dictionary(); + public override Dictionary GetSymbolTable() { + var symbols = new Dictionary(); // https://opensource.apple.com/source/cctools/cctools-795/include/mach-o/nlist.h // n_sect: https://opensource.apple.com/source/cctools/cctools-795/include/mach-o/stab.h @@ -243,11 +244,33 @@ namespace Il2CppInspector var name = (symbol.n_strx != 0) ? ReadNullTerminatedString() : ""; var value = (ulong) Convert.ChangeType(symbol.n_value, typeof(ulong)); - // Ignore duplicates for now, also ignore symbols with no address - if (value != 0) - symbols.TryAdd(name, value); - } + // Ignore symbols with no address or name + if (value == 0 || name.Length == 0) + continue; + // Mask out the N_EXT and N_PEXT bits because we don't care about it + var ntype = (MachO_NType) ((byte)symbol.n_type & ~(byte)MachO_NType.N_EXT & ~(byte)MachO_NType.N_PEXT); + + // For non-debugging symbols (no bits of N_STAB set), just leave the N_TYPE + // Otherwise leave the whole n_type field (with N_EXT and N_PEXT removed) + var dbg = (symbol.n_type & MachO_NType.N_STAB) != 0; + + if (dbg) + if (ntype == MachO_NType.N_BNSYM || ntype == MachO_NType.N_ENSYM + || ntype == MachO_NType.N_SO || ntype == MachO_NType.N_OSO) + continue; + + var type = ntype == MachO_NType.N_FUN? SymbolType.Function + : ntype == MachO_NType.N_STSYM || ntype == MachO_NType.N_GSYM || ntype == MachO_NType.N_SECT? SymbolType.Name + : SymbolType.Unknown; + + if (type == SymbolType.Unknown) { + Console.WriteLine($"Unknown symbol type: {((int) ntype):x2} {value:x16} " + CxxDemangler.CxxDemangler.Demangle(name)); + } + + // Ignore duplicates + symbols.TryAdd(name, new Symbol { Name = name, VirtualAddress = value, Type = type }); + } return symbols; } diff --git a/Il2CppInspector.Common/FileFormatReaders/Symbol.cs b/Il2CppInspector.Common/FileFormatReaders/Symbol.cs new file mode 100644 index 0000000..b09613d --- /dev/null +++ b/Il2CppInspector.Common/FileFormatReaders/Symbol.cs @@ -0,0 +1,26 @@ +/* + Copyright 2020 Katy Coe - http://www.djkaty.com - https://github.com/djkaty + + All rights reserved. +*/ + +namespace Il2CppInspector +{ + public enum SymbolType + { + Function, + Name, + Import, + Unknown + } + + // A code file function export + public class Symbol + { + public ulong VirtualAddress { get; set; } + public string Name { get; set; } + public SymbolType Type { get; set; } + + public string DemangledName => CxxDemangler.CxxDemangler.Demangle(Name); + } +} diff --git a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs index 2affd05..e3c24de 100644 --- a/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs +++ b/Il2CppInspector.Common/IL2CPP/Il2CppBinary.cs @@ -148,20 +148,20 @@ namespace Il2CppInspector // Try searching the symbol table var symbols = Image.GetSymbolTable(); - if (symbols?.Any() ?? false) { + if (symbols.Any()) { Console.WriteLine($"Symbol table(s) found with {symbols.Count} entries"); symbols.TryGetValue("g_CodeRegistration", out var code); symbols.TryGetValue("g_MetadataRegistration", out var metadata); - if (code == 0) + if (code == null) symbols.TryGetValue("_g_CodeRegistration", out code); - if (metadata == 0) + if (metadata == null) symbols.TryGetValue("_g_MetadataRegistration", out metadata); - if (code != 0 && metadata != 0) { + if (code != null && metadata != null) { Console.WriteLine("Required structures acquired from symbol lookup"); - Configure(code, metadata); + Configure(code.VirtualAddress, metadata.VirtualAddress); return true; } else {