Refactor solution layout
This commit is contained in:
318
Il2CppInspector.Common/FileFormatReaders/ElfReader.cs
Normal file
318
Il2CppInspector.Common/FileFormatReaders/ElfReader.cs
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
|
||||
Copyright 2017-2020 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
internal class ElfReader32 : ElfReader<uint, elf_32_phdr, elf_32_sym, ElfReader32, Convert32>
|
||||
{
|
||||
public ElfReader32(Stream stream) : base(stream) {
|
||||
ElfReloc.GetRelocType = info => (Elf) (info & 0xff);
|
||||
ElfReloc.GetSymbolIndex = info => info >> 8;
|
||||
}
|
||||
|
||||
public override int Bits => 32;
|
||||
protected override Elf ArchClass => Elf.ELFCLASS32;
|
||||
|
||||
protected override void Write(BinaryWriter writer, uint value) => writer.Write(value);
|
||||
}
|
||||
|
||||
internal class ElfReader64 : ElfReader<ulong, elf_64_phdr, elf_64_sym, ElfReader64, Convert64>
|
||||
{
|
||||
public ElfReader64(Stream stream) : base(stream) {
|
||||
ElfReloc.GetRelocType = info => (Elf) (info & 0xffff_ffff);
|
||||
ElfReloc.GetSymbolIndex = info => info >> 32;
|
||||
}
|
||||
|
||||
public override int Bits => 64;
|
||||
protected override Elf ArchClass => Elf.ELFCLASS64;
|
||||
|
||||
protected override void Write(BinaryWriter writer, ulong value) => writer.Write(value);
|
||||
}
|
||||
|
||||
interface IElfReader
|
||||
{
|
||||
uint GetPLTAddress();
|
||||
}
|
||||
|
||||
internal abstract class ElfReader<TWord, TPHdr, TSym, TReader, TConvert> : FileFormatReader<TReader>, IElfReader
|
||||
where TWord : struct
|
||||
where TPHdr : Ielf_phdr<TWord>, new()
|
||||
where TSym : Ielf_sym<TWord>, new()
|
||||
where TConvert : IWordConverter<TWord>, new()
|
||||
where TReader : FileFormatReader<TReader>
|
||||
{
|
||||
private readonly TConvert conv = new TConvert();
|
||||
|
||||
// Internal relocation entry helper
|
||||
protected class ElfReloc
|
||||
{
|
||||
public Elf Type;
|
||||
public TWord Offset;
|
||||
public TWord? Addend;
|
||||
public TWord SymbolTable;
|
||||
public TWord SymbolIndex;
|
||||
|
||||
// Equality based on target address
|
||||
public override bool Equals(object obj) => obj is ElfReloc reloc && Equals(reloc);
|
||||
|
||||
public bool Equals(ElfReloc other) {
|
||||
return Offset.Equals(other.Offset);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => Offset.GetHashCode();
|
||||
|
||||
// Cast operators (makes the below code MUCH easier to read)
|
||||
public ElfReloc(elf_rel<TWord> rel, TWord symbolTable) {
|
||||
Offset = rel.r_offset;
|
||||
Addend = null;
|
||||
Type = GetRelocType(rel.r_info);
|
||||
SymbolIndex = GetSymbolIndex(rel.r_info);
|
||||
SymbolTable = symbolTable;
|
||||
}
|
||||
|
||||
public ElfReloc(elf_rela<TWord> rela, TWord symbolTable)
|
||||
: this(new elf_rel<TWord> { r_info = rela.r_info, r_offset = rela.r_offset }, symbolTable) =>
|
||||
Addend = rela.r_addend;
|
||||
|
||||
public static Func<TWord, Elf> GetRelocType;
|
||||
public static Func<TWord, TWord> GetSymbolIndex;
|
||||
}
|
||||
|
||||
// See also: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/sizeof
|
||||
private int Sizeof(Type type) {
|
||||
int size = 0;
|
||||
foreach (var i in type.GetTypeInfo().GetFields()) {
|
||||
if (i.FieldType == typeof(byte) || i.FieldType == typeof(sbyte))
|
||||
size += sizeof(byte);
|
||||
if (i.FieldType == typeof(long) || i.FieldType == typeof(ulong))
|
||||
size += sizeof(ulong);
|
||||
if (i.FieldType == typeof(int) || i.FieldType == typeof(uint))
|
||||
size += sizeof(uint);
|
||||
if (i.FieldType == typeof(short) || i.FieldType == typeof(ushort))
|
||||
size += sizeof(ushort);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
private TPHdr[] program_header_table;
|
||||
private elf_shdr<TWord>[] section_header_table;
|
||||
private elf_dynamic<TWord>[] dynamic_table;
|
||||
private elf_header<TWord> elf_header;
|
||||
|
||||
public ElfReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string Format => Bits == 32 ? "ELF" : "ELF64";
|
||||
|
||||
public override string Arch => (Elf) elf_header.e_machine switch {
|
||||
Elf.EM_386 => "x86",
|
||||
Elf.EM_ARM => "ARM",
|
||||
Elf.EM_X86_64 => "x64",
|
||||
Elf.EM_AARCH64 => "ARM64",
|
||||
_ => "Unsupported"
|
||||
};
|
||||
|
||||
public override int Bits => (elf_header.m_arch == (uint) Elf.ELFCLASS64) ? 64 : 32;
|
||||
|
||||
private elf_shdr<TWord> getSection(Elf sectionIndex) => section_header_table.FirstOrDefault(x => x.sh_type == (uint) sectionIndex);
|
||||
private IEnumerable<elf_shdr<TWord>> getSections(Elf sectionIndex) => section_header_table.Where(x => x.sh_type == (uint) sectionIndex);
|
||||
private TPHdr getProgramHeader(Elf programIndex) => program_header_table.FirstOrDefault(x => x.p_type == (uint) programIndex);
|
||||
private elf_dynamic<TWord> getDynamic(Elf dynamicIndex) => dynamic_table?.FirstOrDefault(x => (Elf) conv.ULong(x.d_tag) == dynamicIndex);
|
||||
|
||||
protected abstract Elf ArchClass { get; }
|
||||
|
||||
protected abstract void Write(BinaryWriter writer, TWord value);
|
||||
|
||||
protected override bool Init() {
|
||||
elf_header = ReadObject<elf_header<TWord>>();
|
||||
|
||||
// Check for magic bytes
|
||||
if ((Elf) elf_header.m_dwFormat != Elf.ELFMAG)
|
||||
return false;
|
||||
|
||||
// 64-bit not supported
|
||||
if ((Elf) elf_header.m_arch != ArchClass)
|
||||
return false;
|
||||
|
||||
program_header_table = ReadArray<TPHdr>(conv.Long(elf_header.e_phoff), elf_header.e_phnum);
|
||||
section_header_table = ReadArray<elf_shdr<TWord>>(conv.Long(elf_header.e_shoff), elf_header.e_shnum);
|
||||
|
||||
if (getProgramHeader(Elf.PT_DYNAMIC) is TPHdr PT_DYNAMIC)
|
||||
dynamic_table = ReadArray<elf_dynamic<TWord>>(conv.Long(PT_DYNAMIC.p_offset), (int) (conv.Long(PT_DYNAMIC.p_filesz) / Sizeof(typeof(elf_dynamic<TWord>))));
|
||||
|
||||
// Get offset of code section
|
||||
var codeSegment = program_header_table.First(x => ((Elf) x.p_flags & Elf.PF_X) == Elf.PF_X);
|
||||
GlobalOffset = conv.ULong(conv.Sub(codeSegment.p_vaddr, codeSegment.p_offset));
|
||||
|
||||
// Find all relocations; target address => (rela header (rels are converted to rela), symbol table base address, is rela?)
|
||||
var rels = new HashSet<ElfReloc>();
|
||||
|
||||
// Two types: add value from offset in image, and add value from specified addend
|
||||
foreach (var relSection in getSections(Elf.SHT_REL))
|
||||
rels.UnionWith(
|
||||
from rel in ReadArray<elf_rel<TWord>>(conv.Long(relSection.sh_offset), conv.Int(conv.Div(relSection.sh_size, relSection.sh_entsize)))
|
||||
select new ElfReloc(rel, section_header_table[relSection.sh_link].sh_offset));
|
||||
|
||||
foreach (var relaSection in getSections(Elf.SHT_RELA))
|
||||
rels.UnionWith(
|
||||
from rela in ReadArray<elf_rela<TWord>>(conv.Long(relaSection.sh_offset), conv.Int(conv.Div(relaSection.sh_size, relaSection.sh_entsize)))
|
||||
select new ElfReloc(rela, section_header_table[relaSection.sh_link].sh_offset));
|
||||
|
||||
// Relocations in dynamic section
|
||||
if (getDynamic(Elf.DT_REL) is elf_dynamic<TWord> dt_rel) {
|
||||
var dt_rel_count = conv.Div(getDynamic(Elf.DT_RELSZ).d_un, getDynamic(Elf.DT_RELENT).d_un);
|
||||
var dt_rel_list = ReadArray<elf_rel<TWord>>(MapVATR(conv.ULong(dt_rel.d_un)), conv.Int(dt_rel_count));
|
||||
var dt_symtab = getDynamic(Elf.DT_SYMTAB).d_un;
|
||||
rels.UnionWith(from rel in dt_rel_list select new ElfReloc(rel, dt_symtab));
|
||||
}
|
||||
|
||||
if (getDynamic(Elf.DT_RELA) is elf_dynamic<TWord> dt_rela) {
|
||||
var dt_rela_count = conv.Div(getDynamic(Elf.DT_RELASZ).d_un, getDynamic(Elf.DT_RELAENT).d_un);
|
||||
var dt_rela_list = ReadArray<elf_rela<TWord>>(MapVATR(conv.ULong(dt_rela.d_un)), conv.Int(dt_rela_count));
|
||||
var dt_symtab = getDynamic(Elf.DT_SYMTAB).d_un;
|
||||
rels.UnionWith(from rela in dt_rela_list select new ElfReloc(rela, dt_symtab));
|
||||
}
|
||||
|
||||
// Process relocations
|
||||
// WARNING: This modifies the stream passed in the constructor
|
||||
if (BaseStream is FileStream)
|
||||
throw new InvalidOperationException("Input stream to ElfReader is a file. Please supply a mutable stream source.");
|
||||
|
||||
var writer = new BinaryWriter(BaseStream);
|
||||
var relsz = Sizeof(typeof(TSym));
|
||||
|
||||
foreach (var rel in rels) {
|
||||
var symValue = ReadObject<TSym>(conv.Long(rel.SymbolTable) + conv.Long(rel.SymbolIndex) * relsz).st_value; // S
|
||||
|
||||
// Ignore relocations into memory addresses not mapped from the image
|
||||
try {
|
||||
Position = MapVATR(conv.ULong(rel.Offset));
|
||||
}
|
||||
catch (InvalidOperationException) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The addend is specified in the struct for rela, and comes from the target location for rel
|
||||
var addend = rel.Addend ?? ReadObject<TWord>(); // A
|
||||
|
||||
// Only handle relocation types we understand, skip the rest
|
||||
// Relocation types from https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-54839.html#scrolltoc
|
||||
// and https://studfiles.net/preview/429210/page:18/
|
||||
// and http://infocenter.arm.com/help/topic/com.arm.doc.ihi0056b/IHI0056B_aaelf64.pdf (AArch64)
|
||||
(TWord newValue, bool recognized) result = (rel.Type, (Elf) elf_header.e_machine) switch {
|
||||
(Elf.R_ARM_ABS32, Elf.EM_ARM) => (conv.Add(symValue, addend), true), // S + A
|
||||
(Elf.R_ARM_REL32, Elf.EM_ARM) => (conv.Add(conv.Sub(symValue, rel.Offset), addend), true), // S - P + A
|
||||
(Elf.R_ARM_COPY, Elf.EM_ARM) => (symValue, true), // S
|
||||
|
||||
(Elf.R_AARCH64_ABS64, Elf.EM_AARCH64) => (conv.Add(symValue, addend), true), // S + A
|
||||
(Elf.R_AARCH64_PREL64, Elf.EM_AARCH64) => (conv.Sub(conv.Add(symValue, addend), rel.Offset), true), // S + A - P
|
||||
(Elf.R_AARCH64_GLOB_DAT, Elf.EM_AARCH64) => (conv.Add(symValue, addend), true), // S + A
|
||||
(Elf.R_AARCH64_JUMP_SLOT, Elf.EM_AARCH64) => (conv.Add(symValue, addend), true), // S + A
|
||||
(Elf.R_AARCH64_RELATIVE, Elf.EM_AARCH64) => (conv.Add(symValue, addend), true), // Delta(S) + A
|
||||
|
||||
(Elf.R_386_32, Elf.EM_386) => (conv.Add(symValue, addend), true), // S + A
|
||||
(Elf.R_386_PC32, Elf.EM_386) => (conv.Sub(conv.Add(symValue, addend), rel.Offset), true), // S + A - P
|
||||
(Elf.R_386_GLOB_DAT, Elf.EM_386) => (symValue, true), // S
|
||||
(Elf.R_386_JMP_SLOT, Elf.EM_386) => (symValue, true), // S
|
||||
|
||||
(Elf.R_AMD64_64, Elf.EM_AARCH64) => (conv.Add(symValue, addend), true), // S + A
|
||||
|
||||
_ => (default(TWord), false)
|
||||
};
|
||||
|
||||
if (result.recognized) {
|
||||
Position = MapVATR(conv.ULong(rel.Offset));
|
||||
Write(writer, result.newValue);
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"Processed {rels.Count} relocations");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override Dictionary<string, ulong> GetSymbolTable() {
|
||||
// Three possible symbol tables in ELF files
|
||||
var pTables = new List<(TWord offset, TWord count, TWord strings)>();
|
||||
|
||||
// String table (a sequence of null-terminated strings, total length in sh_size
|
||||
var SHT_STRTAB = getSection(Elf.SHT_STRTAB);
|
||||
|
||||
if (SHT_STRTAB != null) {
|
||||
// Section header shared object symbol table (.symtab)
|
||||
if (getSection(Elf.SHT_SYMTAB) is elf_shdr<TWord> SHT_SYMTAB)
|
||||
pTables.Add((SHT_SYMTAB.sh_offset, conv.Div(SHT_SYMTAB.sh_size, SHT_SYMTAB.sh_entsize), SHT_STRTAB.sh_offset));
|
||||
|
||||
// Section header executable symbol table (.dynsym)
|
||||
if (getSection(Elf.SHT_DYNSYM) is elf_shdr<TWord> SHT_DYNSYM)
|
||||
pTables.Add((SHT_DYNSYM.sh_offset, conv.Div(SHT_DYNSYM.sh_size, SHT_DYNSYM.sh_entsize), SHT_STRTAB.sh_offset));
|
||||
}
|
||||
|
||||
// Symbol table in dynamic section (DT_SYMTAB)
|
||||
// Normally the same as .dynsym except that .dynsym may be removed in stripped binaries
|
||||
|
||||
// Dynamic string table
|
||||
if (getDynamic(Elf.DT_STRTAB) is elf_dynamic<TWord> DT_STRTAB) {
|
||||
if (getDynamic(Elf.DT_SYMTAB) is elf_dynamic<TWord> DT_SYMTAB) {
|
||||
// Find the next pointer in the dynamic table to calculate the length of the symbol table
|
||||
var end = (from x in dynamic_table where conv.Gt(x.d_un, DT_SYMTAB.d_un) orderby x.d_un select x).First().d_un;
|
||||
|
||||
// Dynamic symbol table
|
||||
pTables.Add((
|
||||
conv.FromUInt(MapVATR(conv.ULong(DT_SYMTAB.d_un))),
|
||||
conv.Div(conv.Sub(end, DT_SYMTAB.d_un), Sizeof(typeof(TSym))),
|
||||
DT_STRTAB.d_un
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Now iterate through all of the symbol and string tables we found to build a full list
|
||||
var symbolTable = new Dictionary<string, ulong>();
|
||||
|
||||
foreach (var pTab in pTables) {
|
||||
var symbol_table = ReadArray<TSym>(conv.Long(pTab.offset), conv.Int(pTab.count));
|
||||
|
||||
foreach (var symbol in symbol_table) {
|
||||
var name = ReadNullTerminatedString(conv.Long(pTab.strings) + symbol.st_name);
|
||||
|
||||
// Avoid duplicates
|
||||
symbolTable.TryAdd(name, conv.ULong(symbol.st_value));
|
||||
}
|
||||
}
|
||||
|
||||
return symbolTable;
|
||||
}
|
||||
|
||||
public override uint[] GetFunctionTable() {
|
||||
// INIT_ARRAY contains a list of pointers to initialization functions (not all functions in the binary)
|
||||
// INIT_ARRAYSZ contains the size of INIT_ARRAY
|
||||
|
||||
var init = MapVATR(conv.ULong(getDynamic(Elf.DT_INIT_ARRAY).d_un));
|
||||
var size = getDynamic(Elf.DT_INIT_ARRAYSZ).d_un;
|
||||
|
||||
return conv.UIntArray(ReadArray<TWord>(init, conv.Int(size) / (Bits / 8))).Select(x => MapVATR(x)).ToArray();
|
||||
}
|
||||
|
||||
// Map a virtual address to an offset into the image file. Throws an exception if the virtual address is not mapped into the file.
|
||||
// Note if uiAddr is a valid segment but filesz < memsz and the adjusted uiAddr falls between the range of filesz and memsz,
|
||||
// an exception will be thrown. This area of memory is assumed to contain all zeroes.
|
||||
public override uint MapVATR(ulong uiAddr) {
|
||||
// Additions in the argument to MapVATR may cause an overflow which should be discarded for 32-bit files
|
||||
if (Bits == 32)
|
||||
uiAddr &= 0xffff_ffff;
|
||||
var program_header_table = this.program_header_table.First(x => uiAddr >= conv.ULong(x.p_vaddr) && uiAddr <= conv.ULong(conv.Add(x.p_vaddr, x.p_filesz)));
|
||||
return (uint) (uiAddr - conv.ULong(conv.Sub(program_header_table.p_vaddr, program_header_table.p_offset)));
|
||||
}
|
||||
|
||||
// Get the address of the procedure linkage table (.got.plt) which is needed for some disassemblies
|
||||
public uint GetPLTAddress() => (uint) conv.ULong(getDynamic(Elf.DT_PLTGOT).d_un);
|
||||
}
|
||||
}
|
||||
153
Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs
Normal file
153
Il2CppInspector.Common/FileFormatReaders/FileFormatReader.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
public interface IFileFormatReader
|
||||
{
|
||||
BinaryObjectReader Stream { get; }
|
||||
double Version { get; set; }
|
||||
long Length { get; }
|
||||
uint NumImages { get; }
|
||||
IEnumerable<IFileFormatReader> Images { get; }
|
||||
IFileFormatReader this[uint index] { get; }
|
||||
long Position { get; set; }
|
||||
string Format { get; }
|
||||
string Arch { get; }
|
||||
int Bits { get; }
|
||||
ulong GlobalOffset { get; }
|
||||
Dictionary<string, ulong> GetSymbolTable();
|
||||
uint[] GetFunctionTable();
|
||||
U ReadMappedObject<U>(ulong uiAddr) where U : new();
|
||||
U[] ReadMappedArray<U>(ulong uiAddr, int count) where U : new();
|
||||
long[] ReadMappedWordArray(ulong uiAddr, int count);
|
||||
uint MapVATR(ulong uiAddr);
|
||||
|
||||
byte[] ReadBytes(int count);
|
||||
ulong ReadUInt64();
|
||||
ulong ReadUInt64(long uiAddr);
|
||||
uint ReadUInt32();
|
||||
uint ReadUInt32(long uiAddr);
|
||||
ushort ReadUInt16();
|
||||
ushort ReadUInt16(long uiAddr);
|
||||
byte ReadByte();
|
||||
byte ReadByte(long uiAddr);
|
||||
long ReadWord();
|
||||
long ReadWord(long uiAddr);
|
||||
U ReadObject<U>() where U : new();
|
||||
string ReadMappedNullTerminatedString(ulong uiAddr);
|
||||
List<U> ReadMappedObjectPointerArray<U>(ulong uiAddr, int count) where U : new();
|
||||
}
|
||||
|
||||
internal class FileFormatReader
|
||||
{
|
||||
// Helper method to try all defined file formats when the contents of the binary is unknown
|
||||
public static IFileFormatReader Load(string filename) => Load(new FileStream(filename, FileMode.Open, FileAccess.Read));
|
||||
|
||||
public static IFileFormatReader Load(Stream stream) {
|
||||
var types = Assembly.GetExecutingAssembly().DefinedTypes
|
||||
.Where(x => x.ImplementedInterfaces.Contains(typeof(IFileFormatReader)) && !x.IsGenericTypeDefinition);
|
||||
|
||||
foreach (var type in types) {
|
||||
if (type.GetMethod("Load", BindingFlags.FlattenHierarchy | BindingFlags.Static | BindingFlags.Public,
|
||||
null, new [] {typeof(Stream)}, null)
|
||||
.Invoke(null, new object[] { stream }) is IFileFormatReader loaded)
|
||||
return loaded;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class FileFormatReader<T> : BinaryObjectReader, IFileFormatReader where T : FileFormatReader<T>
|
||||
{
|
||||
public FileFormatReader(Stream stream) : base(stream) { }
|
||||
|
||||
public BinaryObjectReader Stream => this;
|
||||
|
||||
public long Length => BaseStream.Length;
|
||||
|
||||
public uint NumImages { get; protected set; } = 1;
|
||||
|
||||
public ulong GlobalOffset { get; protected set; }
|
||||
|
||||
public virtual string Format => throw new NotImplementedException();
|
||||
|
||||
public virtual string Arch => throw new NotImplementedException();
|
||||
|
||||
public virtual int Bits => throw new NotImplementedException();
|
||||
|
||||
public IEnumerable<IFileFormatReader> Images {
|
||||
get {
|
||||
for (uint i = 0; i < NumImages; i++)
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
|
||||
public static T Load(string filename) {
|
||||
using var stream = new FileStream(filename, FileMode.Open, FileAccess.Read);
|
||||
return Load(stream);
|
||||
}
|
||||
|
||||
public static T Load(Stream stream) {
|
||||
// Copy the original stream in case we modify it
|
||||
var ms = new MemoryStream();
|
||||
stream.Position = 0;
|
||||
stream.CopyTo(ms);
|
||||
|
||||
ms.Position = 0;
|
||||
var pe = (T) Activator.CreateInstance(typeof(T), ms);
|
||||
return pe.Init() ? pe : null;
|
||||
}
|
||||
|
||||
// Confirm file is valid and set up RVA mappings
|
||||
protected virtual bool Init() => throw new NotImplementedException();
|
||||
|
||||
// Choose a sub-binary within the image for multi-architecture binaries
|
||||
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<string, ulong> GetSymbolTable() => null;
|
||||
|
||||
// Find search locations in the machine code for Il2Cpp data
|
||||
public virtual uint[] GetFunctionTable() => throw new NotImplementedException();
|
||||
|
||||
// Map an RVA to an offset into the file image
|
||||
// No mapping by default
|
||||
public virtual uint MapVATR(ulong uiAddr) => (uint) uiAddr;
|
||||
|
||||
// Read a file format dependent word (32 or 64 bits)
|
||||
// The primitive mappings in Bin2Object will automatically read a uint if the file is 32-bit
|
||||
public long ReadWord() => ReadObject<long>();
|
||||
public long ReadWord(long uiAddr) => ReadObject<long>(uiAddr);
|
||||
|
||||
// Retrieve object(s) from specified RVA(s)
|
||||
public U ReadMappedObject<U>(ulong uiAddr) where U : new() => ReadObject<U>(MapVATR(uiAddr));
|
||||
|
||||
public U[] ReadMappedArray<U>(ulong uiAddr, int count) where U : new() => ReadArray<U>(MapVATR(uiAddr), count);
|
||||
|
||||
// Read a file format dependent array of words (32 or 64 bits)
|
||||
// The primitive mappings in Bin2Object will automatically read a uint if the file is 32-bit
|
||||
public long[] ReadMappedWordArray(ulong uiAddr, int count) => ReadArray<long>(MapVATR(uiAddr), count);
|
||||
|
||||
public string ReadMappedNullTerminatedString(ulong uiAddr) => ReadNullTerminatedString(MapVATR(uiAddr));
|
||||
|
||||
// Reads a list of pointers, then reads each object pointed to
|
||||
public List<U> ReadMappedObjectPointerArray<U>(ulong uiAddr, int count) where U : new() {
|
||||
var pointers = ReadMappedArray<ulong>(uiAddr, count);
|
||||
var array = new List<U>();
|
||||
for (int i = 0; i < count; i++)
|
||||
array.Add(ReadMappedObject<U>(pointers[i]));
|
||||
return array;
|
||||
}
|
||||
}
|
||||
}
|
||||
233
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs
Normal file
233
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/Elf.cs
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
Copyright 2017 Perfare - https://github.com/Perfare/Il2CppDumper
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
[Flags]
|
||||
public enum Elf : uint
|
||||
{
|
||||
// elf_header.m_dwFormat
|
||||
ELFMAG = 0x464c457f, // "\177ELF"
|
||||
|
||||
// elf_header.e_machine
|
||||
EM_386 = 0x03,
|
||||
EM_ARM = 0x28,
|
||||
EM_X86_64 = 0x3E,
|
||||
EM_AARCH64 = 0xB7,
|
||||
|
||||
// elf_header.m_arch
|
||||
ELFCLASS32 = 1,
|
||||
ELFCLASS64 = 2,
|
||||
|
||||
// PHTs
|
||||
PT_DYNAMIC = 2,
|
||||
DT_PLTGOT = 3,
|
||||
|
||||
PF_X = 1,
|
||||
|
||||
// SHTs
|
||||
SHT_SYMTAB = 2,
|
||||
SHT_STRTAB = 3,
|
||||
SHT_RELA = 4,
|
||||
SHT_REL = 9,
|
||||
SHT_DYNSYM = 11,
|
||||
|
||||
// dynamic sections
|
||||
DT_STRTAB = 5,
|
||||
DT_SYMTAB = 6,
|
||||
DT_RELA = 7,
|
||||
DT_RELASZ = 8,
|
||||
DT_RELAENT = 9,
|
||||
DT_REL = 17,
|
||||
DT_RELSZ = 18,
|
||||
DT_RELENT = 19,
|
||||
DT_INIT_ARRAY = 25,
|
||||
DT_INIT_ARRAYSZ = 27,
|
||||
|
||||
// relocation types
|
||||
R_ARM_ABS32 = 2,
|
||||
R_ARM_REL32 = 3,
|
||||
R_ARM_COPY = 20,
|
||||
|
||||
R_AARCH64_ABS64 = 0x101,
|
||||
R_AARCH64_PREL64 = 0x104,
|
||||
R_AARCH64_GLOB_DAT = 0x401,
|
||||
R_AARCH64_JUMP_SLOT = 0x402,
|
||||
R_AARCH64_RELATIVE = 0x403,
|
||||
|
||||
R_386_32 = 1,
|
||||
R_386_PC32 = 2,
|
||||
R_386_GLOB_DAT = 6,
|
||||
R_386_JMP_SLOT = 7,
|
||||
|
||||
R_AMD64_64 = 1
|
||||
}
|
||||
|
||||
#pragma warning disable CS0649
|
||||
internal class elf_header<TWord> where TWord : struct
|
||||
{
|
||||
// 0x7f followed by ELF in ascii
|
||||
public uint m_dwFormat;
|
||||
|
||||
// 1 - 32 bit
|
||||
// 2 - 64 bit
|
||||
public byte m_arch;
|
||||
|
||||
// 1 - little endian
|
||||
// 2 - big endian
|
||||
public byte m_endian;
|
||||
|
||||
// 1 is original elf format
|
||||
public byte m_version;
|
||||
|
||||
// set based on OS, refer to OSABI enum
|
||||
public byte m_osabi;
|
||||
|
||||
// refer to elf documentation
|
||||
public byte m_osabi_ver;
|
||||
|
||||
// unused
|
||||
[ArrayLength(FixedSize=7)]
|
||||
public byte[] e_pad;//byte[7]
|
||||
|
||||
// 1 - relocatable
|
||||
// 2 - executable
|
||||
// 3 - shared
|
||||
// 4 - core
|
||||
public ushort e_type;
|
||||
|
||||
// refer to isa enum
|
||||
public ushort e_machine;
|
||||
|
||||
public uint e_version;
|
||||
|
||||
public TWord e_entry;
|
||||
public TWord e_phoff;
|
||||
public TWord e_shoff;
|
||||
public uint e_flags;
|
||||
public ushort e_ehsize;
|
||||
public ushort e_phentsize;
|
||||
public ushort e_phnum;
|
||||
public ushort e_shentsize;
|
||||
public ushort e_shnum;
|
||||
public ushort e_shtrndx;
|
||||
}
|
||||
|
||||
internal interface Ielf_phdr<TWord> where TWord : struct
|
||||
{
|
||||
uint p_type { get; }
|
||||
TWord p_offset { get; }
|
||||
TWord p_filesz { get; }
|
||||
TWord p_vaddr { get; }
|
||||
uint p_flags { get; }
|
||||
}
|
||||
|
||||
internal class elf_32_phdr : Ielf_phdr<uint>
|
||||
{
|
||||
public uint p_type => f_p_type;
|
||||
public uint p_offset => f_p_offset;
|
||||
public uint p_filesz => f_p_filesz;
|
||||
public uint p_vaddr => f_p_vaddr;
|
||||
public uint p_flags => f_p_flags;
|
||||
|
||||
public uint f_p_type;
|
||||
public uint f_p_offset;
|
||||
public uint f_p_vaddr;
|
||||
public uint p_paddr;
|
||||
public uint f_p_filesz;
|
||||
public uint p_memsz;
|
||||
public uint f_p_flags;
|
||||
public uint p_align;
|
||||
}
|
||||
|
||||
internal class elf_64_phdr : Ielf_phdr<ulong>
|
||||
{
|
||||
public uint p_type => f_p_type;
|
||||
public ulong p_offset => f_p_offset;
|
||||
public ulong p_filesz => f_p_filesz;
|
||||
public ulong p_vaddr => f_p_vaddr;
|
||||
public uint p_flags => f_p_flags;
|
||||
|
||||
public uint f_p_type;
|
||||
public uint f_p_flags;
|
||||
public ulong f_p_offset;
|
||||
public ulong f_p_vaddr;
|
||||
public ulong p_paddr;
|
||||
public ulong f_p_filesz;
|
||||
public ulong p_memsz;
|
||||
public ulong p_align;
|
||||
}
|
||||
|
||||
internal class elf_shdr<TWord> where TWord : struct
|
||||
{
|
||||
public uint sh_name;
|
||||
public uint sh_type;
|
||||
public TWord sh_flags;
|
||||
public TWord sh_addr;
|
||||
public TWord sh_offset;
|
||||
public TWord sh_size;
|
||||
public uint sh_link;
|
||||
public uint sh_info;
|
||||
public TWord sh_addralign;
|
||||
public TWord sh_entsize;
|
||||
}
|
||||
|
||||
internal interface Ielf_sym<TWord> where TWord : struct
|
||||
{
|
||||
uint st_name { get; }
|
||||
TWord st_value { get; }
|
||||
}
|
||||
|
||||
internal class elf_32_sym : Ielf_sym<uint>
|
||||
{
|
||||
public uint st_name => f_st_name;
|
||||
public uint st_value => f_st_value;
|
||||
|
||||
public uint f_st_name;
|
||||
public uint f_st_value;
|
||||
public uint st_size;
|
||||
public byte st_info;
|
||||
public byte st_other;
|
||||
public ushort st_shndx;
|
||||
}
|
||||
|
||||
internal class elf_64_sym : Ielf_sym<ulong>
|
||||
{
|
||||
public uint st_name => f_st_name;
|
||||
public ulong st_value => f_st_value;
|
||||
|
||||
public uint f_st_name;
|
||||
public byte st_info;
|
||||
public byte st_other;
|
||||
public ushort st_shndx;
|
||||
public ulong f_st_value;
|
||||
public ulong st_size;
|
||||
}
|
||||
|
||||
internal class elf_dynamic<TWord> where TWord : struct
|
||||
{
|
||||
public TWord d_tag;
|
||||
public TWord d_un;
|
||||
}
|
||||
|
||||
internal class elf_rel<TWord> where TWord : struct
|
||||
{
|
||||
public TWord r_offset;
|
||||
public TWord r_info;
|
||||
}
|
||||
|
||||
internal class elf_rela<TWord> where TWord : struct
|
||||
{
|
||||
public TWord r_offset;
|
||||
public TWord r_info;
|
||||
public TWord r_addend;
|
||||
}
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
119
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs
Normal file
119
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/MachO.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
// Structures and enums: https://opensource.apple.com/source/cctools/cctools-870/include/mach-o/loader.h
|
||||
|
||||
public enum MachO : uint
|
||||
{
|
||||
MH_MAGIC = 0xfeedface,
|
||||
MH_CIGAM = 0xcefaedfe,
|
||||
MH_MAGIC_64 = 0xfeedfacf,
|
||||
MH_CIGAM_64 = 0xcffaedfe,
|
||||
|
||||
MH_EXECUTE = 0x2,
|
||||
|
||||
LC_SEGMENT = 0x1,
|
||||
LC_SYMTAB = 0x2,
|
||||
LC_DYSYMTAB = 0xb,
|
||||
LC_SEGMENT_64 = 0x19,
|
||||
LC_FUNCTION_STARTS = 0x26,
|
||||
|
||||
CPU_TYPE_X86 = 7,
|
||||
CPU_TYPE_X86_64 = 0x01000000 + CPU_TYPE_X86,
|
||||
CPU_TYPE_ARM = 12,
|
||||
CPU_TYPE_ARM64 = 0x01000000 + CPU_TYPE_ARM
|
||||
}
|
||||
|
||||
internal class MachOHeader<TWord> where TWord : struct
|
||||
{
|
||||
public uint Magic;
|
||||
public uint CPUType;
|
||||
public uint CPUSubType;
|
||||
public uint FileType;
|
||||
public uint NumCommands;
|
||||
public uint SizeOfCommands;
|
||||
public TWord Flags;
|
||||
}
|
||||
|
||||
internal class MachOLoadCommand
|
||||
{
|
||||
public uint Command;
|
||||
public uint Size;
|
||||
}
|
||||
|
||||
internal class MachOSegmentCommand<TWord> where TWord : struct
|
||||
{
|
||||
// MachOLoadCommand
|
||||
[String(FixedSize = 16)]
|
||||
public string Name;
|
||||
public TWord VirtualAddress;
|
||||
public TWord VirtualSize;
|
||||
public TWord ImageOffset;
|
||||
public TWord ImageSize;
|
||||
public uint VMMaxProt;
|
||||
public uint VMInitProt;
|
||||
public uint NumSections;
|
||||
public uint Flags;
|
||||
}
|
||||
|
||||
internal class MachOSection<TWord> where TWord : struct
|
||||
{
|
||||
[String(FixedSize = 16)]
|
||||
public string Name;
|
||||
[String(FixedSize = 16)]
|
||||
public string SegmentName;
|
||||
public TWord Address;
|
||||
public TWord Size;
|
||||
public uint ImageOffset;
|
||||
public uint Align;
|
||||
public uint ImageRelocOffset;
|
||||
public int NumRelocEntries;
|
||||
public uint Flags;
|
||||
public uint Reserved1;
|
||||
public TWord Reserved2;
|
||||
}
|
||||
|
||||
internal class MachOLinkEditDataCommand
|
||||
{
|
||||
// MachOLoadCommand
|
||||
public uint Offset;
|
||||
public uint Size;
|
||||
}
|
||||
|
||||
internal class MachOSymtabCommand
|
||||
{
|
||||
public uint SymOffset;
|
||||
public uint NumSyms;
|
||||
public uint StrOffset;
|
||||
public uint StrSize;
|
||||
}
|
||||
|
||||
internal class MachO_nlist<TWord> where TWord : struct
|
||||
{
|
||||
public uint n_strx;
|
||||
public byte n_type;
|
||||
public byte n_sect;
|
||||
public ushort n_desc;
|
||||
public TWord n_value;
|
||||
}
|
||||
|
||||
internal class MachO_relocation_info
|
||||
{
|
||||
public int r_address;
|
||||
public uint r_data;
|
||||
|
||||
public uint r_symbolnum => r_data & 0x00ffffff;
|
||||
public bool r_pcrel => ((r_data >> 24) & 1) == 1;
|
||||
public uint r_length => (r_data >> 25) & 3;
|
||||
public bool r_extern => ((r_data >> 27) & 1) == 1;
|
||||
public uint r_type => r_data >> 28;
|
||||
}
|
||||
}
|
||||
148
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/PE.cs
Normal file
148
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/PE.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
// Source: https://github.com/dotnet/llilc/blob/master/include/clr/ntimage.h
|
||||
|
||||
public enum PE : uint
|
||||
{
|
||||
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b,
|
||||
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
|
||||
}
|
||||
|
||||
#pragma warning disable CS0649
|
||||
// _IMAGE_FILE_HEADER
|
||||
internal class COFFHeader
|
||||
{
|
||||
public ushort Machine;
|
||||
public ushort NumberOfSections;
|
||||
public uint TimeDateStamp;
|
||||
public uint PointerToSymbolTable;
|
||||
public uint NumberOfSymbols;
|
||||
public ushort SizeOfOptionalHeader;
|
||||
public ushort Characteristics;
|
||||
}
|
||||
|
||||
// _IMAGE_OPTIONAL_HEADER
|
||||
internal interface IPEOptHeader
|
||||
{
|
||||
PE ExpectedMagic { get; }
|
||||
ushort Magic { get; }
|
||||
ulong ImageBase { get; }
|
||||
uint BaseOfCode { get; }
|
||||
RvaEntry[] DataDirectory { get; }
|
||||
}
|
||||
|
||||
internal class PEOptHeader32 : IPEOptHeader
|
||||
{
|
||||
public PE ExpectedMagic => PE.IMAGE_NT_OPTIONAL_HDR32_MAGIC;
|
||||
public ushort Magic => f_Magic;
|
||||
public ulong ImageBase => f_ImageBase;
|
||||
public uint BaseOfCode => f_BaseOfCode;
|
||||
public RvaEntry[] DataDirectory => f_DataDirectory;
|
||||
|
||||
public ushort f_Magic;
|
||||
public byte MajorLinkerVersion;
|
||||
public byte MinorLinkerVersion;
|
||||
public uint SizeOfCode;
|
||||
public uint SizeOfInitializedData;
|
||||
public uint SizeOfUninitializedData;
|
||||
public uint AddressOfEntryPoint;
|
||||
public uint f_BaseOfCode;
|
||||
public uint BaseOfData;
|
||||
public uint f_ImageBase;
|
||||
public uint SectionAlignment;
|
||||
public uint FileAlignment;
|
||||
public ushort MajorOSVersion;
|
||||
public ushort MinorOSVersion;
|
||||
public ushort MajorImageVersion;
|
||||
public ushort MinorImageVersion;
|
||||
public ushort MajorSubsystemVersion;
|
||||
public ushort MinorSubsystemVersion;
|
||||
public uint Win32VersionValue;
|
||||
public uint SizeOfImage;
|
||||
public uint SizeOfHeaders;
|
||||
public uint Checksum;
|
||||
public ushort Subsystem;
|
||||
public ushort DLLCharacteristics;
|
||||
public uint SizeOfStackReserve;
|
||||
public uint SizeOfStackCommit;
|
||||
public uint SizeOfHeapReserve;
|
||||
public uint SizeOfHeapCommit;
|
||||
public uint LoaderFlags;
|
||||
public uint NumberOfRvaAndSizes;
|
||||
[ArrayLength(FieldName = "NumberOfRvaAndSizes")]
|
||||
public RvaEntry[] f_DataDirectory;
|
||||
}
|
||||
|
||||
// _IMAGE_OPTIONAL_HEADER64
|
||||
internal class PEOptHeader64 : IPEOptHeader
|
||||
{
|
||||
public PE ExpectedMagic => PE.IMAGE_NT_OPTIONAL_HDR64_MAGIC;
|
||||
public ushort Magic => f_Magic;
|
||||
public ulong ImageBase => f_ImageBase;
|
||||
public uint BaseOfCode => f_BaseOfCode;
|
||||
public RvaEntry[] DataDirectory => f_DataDirectory;
|
||||
|
||||
public ushort f_Magic;
|
||||
public byte MajorLinkerVersion;
|
||||
public byte MinorLinkerVersion;
|
||||
public uint SizeOfCode;
|
||||
public uint SizeOfInitializedData;
|
||||
public uint SizeOfUninitializedData;
|
||||
public uint AddressOfEntryPoint;
|
||||
public uint f_BaseOfCode;
|
||||
public ulong f_ImageBase;
|
||||
public uint SectionAlignment;
|
||||
public uint FileAlignment;
|
||||
public ushort MajorOSVersion;
|
||||
public ushort MinorOSVersion;
|
||||
public ushort MajorImageVersion;
|
||||
public ushort MinorImageVersion;
|
||||
public ushort MajorSubsystemVersion;
|
||||
public ushort MinorSubsystemVersion;
|
||||
public uint Win32VersionValue;
|
||||
public uint SizeOfImage;
|
||||
public uint SizeOfHeaders;
|
||||
public uint Checksum;
|
||||
public ushort Subsystem;
|
||||
public ushort DLLCharacteristics;
|
||||
public ulong SizeOfStackReserve;
|
||||
public ulong SizeOfStackCommit;
|
||||
public ulong SizeOfHeapReserve;
|
||||
public ulong SizeOfHeapCommit;
|
||||
public uint LoaderFlags;
|
||||
public uint NumberOfRvaAndSizes;
|
||||
[ArrayLength(FieldName = "NumberOfRvaAndSizes")]
|
||||
public RvaEntry[] f_DataDirectory;
|
||||
}
|
||||
|
||||
internal class RvaEntry
|
||||
{
|
||||
public uint VirtualAddress;
|
||||
public uint Size;
|
||||
}
|
||||
|
||||
// _IMAGE_SECTION_HEADER
|
||||
internal class PESection
|
||||
{
|
||||
[String(FixedSize=8)]
|
||||
public string Name;
|
||||
public uint VirtualSize; // Size in memory
|
||||
public uint VirtualAddress; // Base address in memory (RVA)
|
||||
public uint SizeOfRawData; // Size in file
|
||||
public uint PointerToRawData; // Base address in file
|
||||
public uint PointerToRelocations;
|
||||
public uint PointerToLinenumbers;
|
||||
public ushort NumberOfRelocations;
|
||||
public ushort NumberOfLinenumbers;
|
||||
public uint Characteristics;
|
||||
}
|
||||
#pragma warning restore CS0649
|
||||
}
|
||||
33
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/UB.cs
Normal file
33
Il2CppInspector.Common/FileFormatReaders/FormatLayouts/UB.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2017 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
// Structures and enums: https://cocoaintheshell.whine.fr/2009/07/universal-binary-mach-o-format/
|
||||
|
||||
public enum UB : uint
|
||||
{
|
||||
FAT_MAGIC = 0xcafebabe
|
||||
}
|
||||
|
||||
// Big-endian
|
||||
internal class FatHeader
|
||||
{
|
||||
public uint Magic;
|
||||
public uint NumArch;
|
||||
}
|
||||
|
||||
// Big-endian
|
||||
internal class FatArch
|
||||
{
|
||||
public uint CPUType;
|
||||
public uint CPUSubType;
|
||||
public uint Offset;
|
||||
public uint Size;
|
||||
public uint Align;
|
||||
}
|
||||
}
|
||||
176
Il2CppInspector.Common/FileFormatReaders/MachOReader.cs
Normal file
176
Il2CppInspector.Common/FileFormatReaders/MachOReader.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
internal class MachOReader32 : MachOReader<uint, MachOReader32, Convert32>
|
||||
{
|
||||
public MachOReader32(Stream stream) : base(stream) { }
|
||||
|
||||
public override int Bits => 32;
|
||||
|
||||
protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC;
|
||||
protected override bool checkMagicBE(MachO magic) => magic == MachO.MH_CIGAM;
|
||||
|
||||
protected override MachO lc_Segment => MachO.LC_SEGMENT;
|
||||
|
||||
public override uint MapVATR(ulong uiAddr) {
|
||||
var section = sections.First(x => uiAddr >= x.Address && uiAddr <= x.Address + x.Size);
|
||||
return (uint) uiAddr - (section.Address - section.ImageOffset);
|
||||
}
|
||||
}
|
||||
|
||||
internal class MachOReader64 : MachOReader<ulong, MachOReader64, Convert64>
|
||||
{
|
||||
public MachOReader64(Stream stream) : base(stream) { }
|
||||
|
||||
public override int Bits => 64;
|
||||
|
||||
protected override bool checkMagicLE(MachO magic) => magic == MachO.MH_MAGIC_64;
|
||||
protected override bool checkMagicBE(MachO magic) => magic == MachO.MH_CIGAM_64;
|
||||
|
||||
protected override MachO lc_Segment => MachO.LC_SEGMENT_64;
|
||||
public override uint MapVATR(ulong uiAddr) {
|
||||
var section = sections.First(x => uiAddr >= x.Address && uiAddr <= x.Address + x.Size);
|
||||
return (uint) (uiAddr - (section.Address - section.ImageOffset));
|
||||
}
|
||||
}
|
||||
|
||||
// We need this convoluted generic TReader declaration so that "static T FileFormatReader.Load(Stream)"
|
||||
// is inherited to MachOReader32/64 with a correct definition of T
|
||||
internal abstract class MachOReader<TWord, TReader, TConvert> : FileFormatReader<TReader>
|
||||
where TWord : struct
|
||||
where TReader : FileFormatReader<TReader>
|
||||
where TConvert : IWordConverter<TWord>, new()
|
||||
{
|
||||
private readonly TConvert conv = new TConvert();
|
||||
|
||||
private MachOHeader<TWord> header;
|
||||
protected readonly List<MachOSection<TWord>> sections = new List<MachOSection<TWord>>();
|
||||
private MachOSection<TWord> funcTab;
|
||||
private MachOSymtabCommand symTab;
|
||||
|
||||
protected MachOReader(Stream stream) : base(stream) { }
|
||||
|
||||
public override string Format => "Mach-O " + (Bits == 32 ? "32-bit" : "64-bit");
|
||||
|
||||
public override string Arch => (MachO)header.CPUType switch
|
||||
{
|
||||
MachO.CPU_TYPE_ARM => "ARM",
|
||||
MachO.CPU_TYPE_ARM64 => "ARM64",
|
||||
MachO.CPU_TYPE_X86 => "x86",
|
||||
MachO.CPU_TYPE_X86_64 => "x64",
|
||||
_ => "Unsupported"
|
||||
};
|
||||
|
||||
protected abstract bool checkMagicLE(MachO magic);
|
||||
protected abstract bool checkMagicBE(MachO magic);
|
||||
protected abstract MachO lc_Segment { get; }
|
||||
|
||||
protected override bool Init() {
|
||||
// Detect endianness - default is little-endianness
|
||||
MachO magic = (MachO)ReadUInt32();
|
||||
|
||||
if (checkMagicBE(magic))
|
||||
Endianness = Endianness.Big;
|
||||
|
||||
if (!checkMagicBE(magic) && !checkMagicLE(magic))
|
||||
return false;
|
||||
|
||||
header = ReadObject<MachOHeader<TWord>>(0);
|
||||
|
||||
// Must be executable file
|
||||
if ((MachO)header.FileType != MachO.MH_EXECUTE)
|
||||
return false;
|
||||
|
||||
// Process load commands
|
||||
for (var c = 0; c < header.NumCommands; c++) {
|
||||
var startPos = Position;
|
||||
var loadCommand = ReadObject<MachOLoadCommand>();
|
||||
|
||||
switch ((MachO) loadCommand.Command) {
|
||||
|
||||
// Segments
|
||||
case MachO cmd when cmd == lc_Segment:
|
||||
var segment = ReadObject<MachOSegmentCommand<TWord>>();
|
||||
if (segment.Name == "__TEXT" || segment.Name == "__DATA") {
|
||||
for (int s = 0; s < segment.NumSections; s++) {
|
||||
var section = ReadObject<MachOSection<TWord>>();
|
||||
sections.Add(section);
|
||||
if (section.Name == "__text") {
|
||||
GlobalOffset = (ulong) Convert.ChangeType(section.Address, typeof(ulong)) - section.ImageOffset;
|
||||
}
|
||||
|
||||
// Initialization (pre-main) functions
|
||||
if (section.Name == "__mod_init_func") {
|
||||
funcTab = section;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Location of static symbol table
|
||||
case MachO.LC_SYMTAB:
|
||||
symTab = ReadObject<MachOSymtabCommand>();
|
||||
break;
|
||||
|
||||
case MachO.LC_DYSYMTAB:
|
||||
// TODO: Implement Mach-O dynamic symbol table
|
||||
break;
|
||||
}
|
||||
|
||||
// There might be other data after the load command so always use the specified total size to step forwards
|
||||
Position = startPos + loadCommand.Size;
|
||||
}
|
||||
|
||||
// Must find __mod_init_func
|
||||
if (funcTab == null)
|
||||
return false;
|
||||
|
||||
// Process relocations
|
||||
foreach (var section in sections) {
|
||||
var rels = ReadArray<MachO_relocation_info>(section.ImageRelocOffset, section.NumRelocEntries);
|
||||
|
||||
// TODO: Implement Mach-O relocations
|
||||
if (rels.Any()) {
|
||||
Console.WriteLine("Mach-O file contains relocations (feature not yet implemented)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override uint[] GetFunctionTable() => ReadArray<TWord>(funcTab.ImageOffset, conv.Int(funcTab.Size) / (Bits / 8)).Select(x => MapVATR(conv.ULong(x)) & 0xffff_fffe).ToArray();
|
||||
|
||||
public override Dictionary<string, ulong> GetSymbolTable() {
|
||||
var symbols = new Dictionary<string, ulong>();
|
||||
|
||||
// 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
|
||||
|
||||
var symbolList = ReadArray<MachO_nlist<TWord>>(symTab.SymOffset, (int) symTab.NumSyms);
|
||||
|
||||
// This is a really naive implementation that ignores the values of n_type and n_sect
|
||||
// which may affect the interpretation of n_value
|
||||
foreach (var symbol in symbolList) {
|
||||
Position = symTab.StrOffset + symbol.n_strx;
|
||||
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);
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
Il2CppInspector.Common/FileFormatReaders/PEReader.cs
Normal file
125
Il2CppInspector.Common/FileFormatReaders/PEReader.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
// References:
|
||||
// PE Header file: https://github.com/dotnet/llilc/blob/master/include/clr/ntimage.h
|
||||
// PE format specification: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format?redirectedfrom=MSDN
|
||||
internal class PEReader : FileFormatReader<PEReader>
|
||||
{
|
||||
private COFFHeader coff;
|
||||
private IPEOptHeader pe;
|
||||
private PESection[] sections;
|
||||
private uint pFuncTable;
|
||||
|
||||
public PEReader(Stream stream) : base(stream) {}
|
||||
|
||||
public override string Format => pe is PEOptHeader32 ? "PE32" : "PE32+";
|
||||
|
||||
public override string Arch => coff.Machine switch {
|
||||
0x8664 => "x64", // IMAGE_FILE_MACHINE_AMD64
|
||||
0x1C0 => "ARM", // IMAGE_FILE_MACHINE_ARM
|
||||
0xAA64 => "ARM64", // IMAGE_FILE_MACHINE_ARM64
|
||||
0x1C4 => "ARM", // IMAGE_FILE_MACHINE_ARMINT (Thumb-2)
|
||||
0x14C => "x86", // IMAGE_FILE_MACHINE_I386
|
||||
0x1C2 => "ARM", // IMAGE_FILE_MACHINE_THUMB (Thumb)
|
||||
_ => "Unsupported"
|
||||
};
|
||||
|
||||
// IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20B
|
||||
// IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10B
|
||||
// Could also use coff.Characteristics (IMAGE_FILE_32BIT_MACHINE) or coff.Machine
|
||||
public override int Bits => (PE) pe.Magic == PE.IMAGE_NT_OPTIONAL_HDR64_MAGIC ? 64 : 32;
|
||||
|
||||
protected override bool Init() {
|
||||
// Check for MZ signature "MZ"
|
||||
if (ReadUInt16() != 0x5A4D)
|
||||
return false;
|
||||
|
||||
// Get offset to PE header from DOS header
|
||||
Position = ReadUInt32(0x3C);
|
||||
|
||||
// Check PE signature "PE\0\0"
|
||||
if (ReadUInt32() != 0x00004550)
|
||||
return false;
|
||||
|
||||
// Read COFF Header
|
||||
coff = ReadObject<COFFHeader>();
|
||||
|
||||
// Ensure presence of PE Optional header
|
||||
// Size will always be 0x60 (32-bit) or 0x70 (64-bit) + (0x10 ' 0x8) for 16 RVA entries @ 8 bytes each
|
||||
if (!((coff.SizeOfOptionalHeader == 0xE0 ? 32 :
|
||||
coff.SizeOfOptionalHeader == 0xF0 ? (int?) 64 : null) is var likelyWordSize))
|
||||
return false;
|
||||
|
||||
// Read PE optional header
|
||||
pe = likelyWordSize switch {
|
||||
32 => ReadObject<PEOptHeader32>(),
|
||||
64 => ReadObject<PEOptHeader64>(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
// Confirm architecture magic number matches expected word size
|
||||
if ((PE) pe.Magic != pe.ExpectedMagic)
|
||||
return false;
|
||||
|
||||
// Get IAT
|
||||
var IATStart = pe.DataDirectory[12].VirtualAddress;
|
||||
var IATSize = pe.DataDirectory[12].Size;
|
||||
|
||||
// Get sections table
|
||||
sections = ReadArray<PESection>(coff.NumberOfSections);
|
||||
|
||||
// Confirm that .rdata section begins at same place as IAT
|
||||
var rData = sections.First(x => x.Name == ".rdata");
|
||||
if (rData.VirtualAddress != IATStart)
|
||||
return false;
|
||||
|
||||
// Calculate start of function pointer table
|
||||
pFuncTable = rData.PointerToRawData + IATSize;
|
||||
|
||||
// Skip over __guard_check_icall_fptr and __guard_dispatch_icall_fptr if present, then the following zero offset
|
||||
Position = pFuncTable;
|
||||
if (pe is PEOptHeader32) {
|
||||
while (ReadUInt32() != 0)
|
||||
pFuncTable += 4;
|
||||
pFuncTable += 4;
|
||||
}
|
||||
else {
|
||||
while (ReadUInt64() != 0)
|
||||
pFuncTable += 8;
|
||||
pFuncTable += 8;
|
||||
}
|
||||
|
||||
// Get base of code
|
||||
GlobalOffset = pe.ImageBase + pe.BaseOfCode - sections.First(x => x.Name == ".text").PointerToRawData;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override uint[] GetFunctionTable() {
|
||||
Position = pFuncTable;
|
||||
var addrs = new List<uint>();
|
||||
ulong addr;
|
||||
while ((addr = pe is PEOptHeader32? ReadUInt32() : ReadUInt64()) != 0)
|
||||
addrs.Add(MapVATR(addr) & 0xfffffffc);
|
||||
return addrs.ToArray();
|
||||
}
|
||||
|
||||
public override uint MapVATR(ulong uiAddr) {
|
||||
if (uiAddr == 0)
|
||||
return 0;
|
||||
|
||||
var section = sections.First(x => uiAddr - pe.ImageBase >= x.VirtualAddress &&
|
||||
uiAddr - pe.ImageBase < x.VirtualAddress + x.SizeOfRawData);
|
||||
return (uint) (uiAddr - section.VirtualAddress - pe.ImageBase + section.PointerToRawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Il2CppInspector.Common/FileFormatReaders/UBReader.cs
Normal file
46
Il2CppInspector.Common/FileFormatReaders/UBReader.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2017-2019 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using NoisyCowStudios.Bin2Object;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
internal class UBReader : FileFormatReader<UBReader>
|
||||
{
|
||||
private FatHeader header;
|
||||
|
||||
public UBReader(Stream stream) : base(stream) { }
|
||||
|
||||
protected override bool Init() {
|
||||
// Fat headers are always big-endian regardless of architectures
|
||||
Endianness = Endianness.Big;
|
||||
|
||||
header = ReadObject<FatHeader>();
|
||||
|
||||
if ((UB) header.Magic != UB.FAT_MAGIC)
|
||||
return false;
|
||||
|
||||
NumImages = header.NumArch;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override IFileFormatReader this[uint index] {
|
||||
get {
|
||||
Position = 0x8 + 0x14 * index; // sizeof(FatHeader), sizeof(FatArch)
|
||||
Endianness = Endianness.Big;
|
||||
|
||||
var arch = ReadObject<FatArch>();
|
||||
|
||||
Position = arch.Offset;
|
||||
Endianness = Endianness.Little;
|
||||
|
||||
using var s = new MemoryStream(ReadBytes((int) arch.Size));
|
||||
return (IFileFormatReader) MachOReader32.Load(s) ?? MachOReader64.Load(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Il2CppInspector.Common/FileFormatReaders/WordConversions.cs
Normal file
54
Il2CppInspector.Common/FileFormatReaders/WordConversions.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2019-2020 Katy Coe - http://www.hearthcode.org - http://www.djkaty.com
|
||||
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace Il2CppInspector
|
||||
{
|
||||
// NOTE: What we really should have done here is add a TWord type parameter to FileFormatReader<T>
|
||||
// then we could probably avoid most of this
|
||||
interface IWordConverter<TWord> where TWord : struct
|
||||
{
|
||||
TWord Add(TWord a, TWord b);
|
||||
TWord Sub(TWord a, TWord b);
|
||||
TWord Div(TWord a, TWord b);
|
||||
TWord Div(TWord a, int b);
|
||||
TWord FromUInt(uint a);
|
||||
int Int(TWord a);
|
||||
long Long(TWord a);
|
||||
ulong ULong(TWord a);
|
||||
bool Gt(TWord a, TWord b);
|
||||
uint[] UIntArray(TWord[] a);
|
||||
}
|
||||
|
||||
internal class Convert32 : IWordConverter<uint>
|
||||
{
|
||||
public uint Add(uint a, uint b) => a + b;
|
||||
public uint Sub(uint a, uint b) => a - b;
|
||||
public uint Div(uint a, uint b) => a / b;
|
||||
public uint Div(uint a, int b) => a / (uint)b;
|
||||
public uint FromUInt(uint a) => a;
|
||||
public int Int(uint a) => (int)a;
|
||||
public long Long(uint a) => a;
|
||||
public ulong ULong(uint a) => a;
|
||||
public bool Gt(uint a, uint b) => a > b;
|
||||
public uint[] UIntArray(uint[] a) => a;
|
||||
}
|
||||
|
||||
internal class Convert64 : IWordConverter<ulong>
|
||||
{
|
||||
public ulong Add(ulong a, ulong b) => a + b;
|
||||
public ulong Sub(ulong a, ulong b) => a - b;
|
||||
public ulong Div(ulong a, ulong b) => a / b;
|
||||
public ulong Div(ulong a, int b) => a / (uint)b;
|
||||
public ulong FromUInt(uint a) => a;
|
||||
public int Int(ulong a) => (int)a;
|
||||
public long Long(ulong a) => (long)a;
|
||||
public ulong ULong(ulong a) => a;
|
||||
public bool Gt(ulong a, ulong b) => a > b;
|
||||
public uint[] UIntArray(ulong[] a) => Array.ConvertAll(a, x => (uint)x);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user