// ---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // ---------------------------------------------------------------------------- // // Simple PE Loader for Singularity // // Currently does not support: // x64 // sections loaded at separate addresses // loading at preferred address // #define verbose namespace Microsoft.Singularity.Loader { using Microsoft.Singularity; using Microsoft.Singularity.Io; using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; [CLSCompliant(false)] internal class ImportTable { private IoMemory mem; private DirectoryEntry entry; internal ImportTable(IoMemory mem, DirectoryEntry entry) { this.mem = mem; this.entry = entry; if (entry.size == 0) { DebugStub.WriteLine("// No import table."); return; } } #if verbose [Conditional("DEBUG")] public void DumpIAT(String title) { Tracing.Log(Tracing.Debug, "// {0}", title); if (entry.size == 0) { Tracing.Log(Tracing.Debug, "// No data."); return; } int importOffset = (int)entry.virtualAddress; Tracing.Log(Tracing.Debug, " import offset={0:x8}", importOffset); // // start at the importOffset specified above creating and filling out ImportDescriptors // There is no indication in the header as to how many descriptors are present. // In the last descriptor all fields will be 0; this code checks just the firstChunk field. // for (;;) { ImportDescriptor importDescriptor = new ImportDescriptor(mem, ref importOffset); //Use hit/Name array pointed to by "characteristics" // FirstChunk points to the IAT table which is modified at load time to // to reflect the fixed-up thunk to access the function if (importDescriptor.characteristics == 0) { return; } String name = null; if (importDescriptor.name != 0) { name = mem.ReadAsciiZeroString((int)importDescriptor.name); } Tracing.Log(Tracing.Debug, "// {0}", name); importDescriptor.DumpToStream(); Tracing.Log(Tracing.Debug, "//"); int importTableOffset = (int)importDescriptor.characteristics; for (;;) { int importTableID = (int) mem.Read32(importTableOffset); if (importTableID == 0) { break; } Tracing.Log(Tracing.Debug, "importTableID is {0:x8}", importTableID); int nameStringOffset = importTableID & 0x7fffffff; importTableOffset += 4; if ((importTableID & 0x8000000) != 0) { Tracing.Log(Tracing.Debug, "// {0:x8} by ordinal {1:x8}", mem.Read16(importTableOffset), (importTableID & 0x7ffffff)); } else { Tracing.Log(Tracing.Debug, "// {1:x8} {0:x8}", mem.ReadAsciiZeroString(nameStringOffset+2), mem.Read16(importTableOffset)); } } Tracing.Log(Tracing.Debug, ""); } //throw new BadImageFormatException(""); } #endif public void ResolveImports(ExportTable exports) { if (entry.size == 0) { Tracing.Log(Tracing.Debug, "// No imports to resolve."); return; } int importOffset = (int)entry.virtualAddress; //Tracing.Log(Tracing.Debug, " import offset={0:x8}", importOffset); // // start at the importOffset specified above creating and filling out ImportDescriptors // There is no indication in the header as to how many descriptors are present. // In the last descriptor all fields will be 0; this code checks just the firstChunk field. // for (;;) { ImportDescriptor importDescriptor = new ImportDescriptor(mem, ref importOffset); if (importDescriptor.firstChunk == 0) { return; } ushort hint = 0; String name = null; if (importDescriptor.name != 0) { name = mem.ReadAsciiZeroString((int)importDescriptor.name); } #if verbose Tracing.Log(Tracing.Debug, "// {0}", name); importDescriptor.DumpToStream("// "); Tracing.Log(Tracing.Debug, "//"); #endif UIntPtr offsetHintTable = UIntPtr.Zero; if (0 == importDescriptor.characteristics) { offsetHintTable = (UIntPtr)importDescriptor.characteristics; } else { offsetHintTable = (UIntPtr)importDescriptor.firstChunk; } UIntPtr offsetIAT = (UIntPtr)importDescriptor.firstChunk; #if PTR_SIZE_64 // // AIFIX: Add hint logic. // for (;;) { ulong ImportEntry = (ulong) mem.Read64((int) offsetIAT); if (ImportEntry == 0) { break; } if ((ImportEntry >> 63) != 0) { throw new BadImageFormatException("Import Ordinal encountered"); } name = mem.ReadAsciiZeroString(((int) (ImportEntry & 0x7FFFFFFF)) + 2); UIntPtr Address; Address = exports.Resolve(hint, name); if (Address == 0) { throw new BadImageFormatException("Import not found"); } mem.Write64((int) offsetIAT, (ulong) Address); offsetIAT += 8; } #else for (;;) { // read elements in the hint array for processing // the hint array terminates when the its content is 0 int importByNameEntry = (int) mem.Read32((int)offsetHintTable); if (importByNameEntry == 0) break; #if verbose Tracing.Log(Tracing.Debug, "importByName entry is {0:x8}", importByNameEntry); #endif int nameStringOffset = importByNameEntry & 0x7fffffff; if ((importByNameEntry & 0x8000000) != 0) { // should never happen in Singularity (no Ordinals) throw new BadImageFormatException("Import Ordinal encountered"); } else { name = mem.ReadAsciiZeroString((int)nameStringOffset+2); hint = mem.Read16Unchecked(nameStringOffset); //Tracing.Log(Tracing.Debug, " function to lookup is {0}", name); UIntPtr addr = exports.Resolve(hint, name); if (0 != addr) { //Overwrite ptr in IAT with address of function in the // IAT thunk table #if verbose int meth = name.IndexOf('@'); int rest = name.IndexOf('@', meth + 1) + 1; int clas = name.LastIndexOf('_', rest, rest - meth) + 1; Tracing.Log(Tracing.Debug, " import: {1:x8} is {2:x8} {0}", name.Substring(0, meth) + "@" + name.Substring(clas, rest - clas) + "@" name.Substring(rest) (uint)offsetIAT, (uint)addr); #endif mem.Write32((int) offsetIAT,(uint)addr); } else { Tracing.Log(Tracing.Debug, " Import not found: {0}", name); throw new BadImageFormatException("Import not found"); } } // increment "array indices" offsetIAT += 4; offsetHintTable +=4; } //Tracing.Log(Tracing.Debug, ""); #endif } //throw new BadImageFormatException(""); } internal struct ImportDescriptor { internal ImportDescriptor(IoMemory mem, ref int offset) { if (offset + 20 > mem.Length) { Error.AccessOutOfRange(); } this.characteristics = mem.Read32Unchecked(offset + 0); this.timeDateStamp = mem.Read32Unchecked(offset + 4); this.forwarderChain = mem.Read32Unchecked(offset + 8); this.name = mem.Read32Unchecked(offset + 12); this.firstChunk = mem.Read32Unchecked(offset + 16); offset += 20; } [Conditional("DEBUG")] internal void DumpToStream() { Tracing.Log(Tracing.Debug, "{0:x8} Import Address Table", firstChunk); Tracing.Log(Tracing.Debug, "{0:x8} Import Name Table", name); Tracing.Log(Tracing.Debug, "{0:x8} Time date stamp", timeDateStamp); Tracing.Log(Tracing.Debug, "{0:x8} Index of first forwarder reference", forwarderChain); } // State internal readonly uint characteristics; internal readonly uint timeDateStamp; internal readonly uint forwarderChain; internal readonly uint name; internal readonly uint firstChunk; } } // ImportTable class }