// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.Diagnostics; using dnlib.PE; namespace dnlib.DotNet { /// /// All native vtables /// [DebuggerDisplay("RVA = {RVA}, Count = {VTables.Count}")] public sealed class VTableFixups : IEnumerable { RVA rva; IList vtables; /// /// Gets/sets the RVA of the vtable fixups /// public RVA RVA { get => rva; set => rva = value; } /// /// Gets all s /// public IList VTables => vtables; /// /// Default constructor /// public VTableFixups() => vtables = new List(); /// /// Constructor /// /// Module public VTableFixups(ModuleDefMD module) => Initialize(module); void Initialize(ModuleDefMD module) { var info = module.Metadata.ImageCor20Header.VTableFixups; if (info.VirtualAddress == 0 || info.Size == 0) { vtables = new List(); return; } rva = info.VirtualAddress; vtables = new List((int)info.Size / 8); var peImage = module.Metadata.PEImage; var reader = peImage.CreateReader(); reader.Position = (uint)peImage.ToFileOffset(info.VirtualAddress); ulong endPos = (ulong)reader.Position + info.Size; while ((ulong)reader.Position + 8 <= endPos && reader.CanRead(8U)) { var tableRva = (RVA)reader.ReadUInt32(); int numSlots = reader.ReadUInt16(); var flags = (VTableFlags)reader.ReadUInt16(); var vtable = new VTable(tableRva, flags, numSlots); vtables.Add(vtable); var pos = reader.Position; reader.Position = (uint)peImage.ToFileOffset(tableRva); uint slotSize = vtable.Is64Bit ? 8U : 4; while (numSlots-- > 0 && reader.CanRead(slotSize)) { vtable.Methods.Add(module.ResolveToken(reader.ReadUInt32()) as IMethod); if (slotSize == 8) reader.ReadUInt32(); } reader.Position = pos; } } /// public IEnumerator GetEnumerator() => vtables.GetEnumerator(); /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); } /// /// See COR_VTABLE_XXX in CorHdr.h /// [Flags] public enum VTableFlags : ushort { /// /// 32-bit vtable slots /// Bit32 = 0x01, /// /// 64-bit vtable slots /// Bit64 = 0x02, /// /// Transition from unmanaged code /// FromUnmanaged = 0x04, /// /// Also retain app domain /// FromUnmanagedRetainAppDomain = 0x08, /// /// Call most derived method /// CallMostDerived = 0x10, } /// /// One VTable accessed by native code /// public sealed class VTable : IEnumerable { RVA rva; VTableFlags flags; readonly IList methods; /// /// Gets/sets the of this vtable /// public RVA RVA { get => rva; set => rva = value; } /// /// Gets/sets the flags /// public VTableFlags Flags { get => flags; set => flags = value; } /// /// true if each vtable slot is 32 bits in size /// public bool Is32Bit => (flags & VTableFlags.Bit32) != 0; /// /// true if each vtable slot is 64 bits in size /// public bool Is64Bit => (flags & VTableFlags.Bit64) != 0; /// /// Gets the vtable methods /// public IList Methods => methods; /// /// Default constructor /// public VTable() => methods = new List(); /// /// Constructor /// /// Flags public VTable(VTableFlags flags) { this.flags = flags; methods = new List(); } /// /// Constructor /// /// RVA of this vtable /// Flgas /// Number of methods in vtable public VTable(RVA rva, VTableFlags flags, int numSlots) { this.rva = rva; this.flags = flags; methods = new List(numSlots); } /// /// Constructor /// /// RVA of this vtable /// Flgas /// Vtable methods public VTable(RVA rva, VTableFlags flags, IEnumerable methods) { this.rva = rva; this.flags = flags; this.methods = new List(methods); } /// public IEnumerator GetEnumerator() => methods.GetEnumerator(); /// System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); /// public override string ToString() { if (methods.Count == 0) return $"{methods.Count} {(uint)rva:X8}"; return $"{methods.Count} {(uint)rva:X8} {methods[0]}"; } } }