diff --git a/src/LightInject/LightInject.cs b/src/LightInject/LightInject.cs index 4c647047..f7f3a78b 100644 --- a/src/LightInject/LightInject.cs +++ b/src/LightInject/LightInject.cs @@ -6706,6 +6706,252 @@ protected virtual string GetAssemblyCodeBasePath() /// public class AssemblyLoader : IAssemblyLoader { + [Flags] + private enum Characteristics : ushort + { + /// + /// Image only, Windows CE, and Microsoft Windows NT and later. This indicates that the + /// file does not contain base relocations and must therefore be loaded at its preferred + /// base address. If the base address is not available, the loader reports an error. + /// + /// + /// The default behavior of the linker is to strip base relocations from executable (EXE) files. + /// + IMAGE_FILE_RELOCS_STRIPPED = 0x0001, + + /// + /// Image only. This indicates that the image file is valid and can be run. + /// + /// If this flag is not set, it indicates a linker error. + IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002, + + /// + /// COFF line numbers have been removed. + /// + /// This flag is deprecated and should be zero. + [Obsolete("This flag is deprecated and should be zero.")] + IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004, + + /// + /// COFF symbol table entries for local symbols have been removed. + /// + /// This flag is deprecated and should be zero. + [Obsolete("This flag is deprecated and should be zero.")] + IMAGE_FILE_LOCAL_SYMS_STRIPPED = 0x0008, + + /// + /// Aggressively trim working set. + /// + /// This flag is deprecated for Windows 2000 and later and must be zero. + [Obsolete("This flag is deprecated for Windows 2000 and later and must be zero.")] + IMAGE_FILE_AGGRESSIVE_WS_TRIM = 0x0010, + + /// + /// Application can handle > 2 GB addresses + /// + IMAGE_FILE_LARGE_ADDRESS_AWARE = 0x0020, + + /// + /// This flag is reserved for future use. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1700:DoNotNameEnumValuesReserved")] + Reserved = 0x0040, + + /// + /// Little endian: the least significant bit (LSB) precedes the most significant bit + /// (MSB) in memory. + /// + /// This flag is deprecated and should be zero. + [Obsolete("This flag is deprecated and should be zero.")] + IMAGE_FILE_BYTES_REVERSED_LO = 0x0080, + + /// + /// Machine is based on a 32-bit-word architecture. + /// + IMAGE_FILE_32BIT_MACHINE = 0x0100, + + /// + /// Debugging information is removed from the image file. + /// + IMAGE_FILE_DEBUG_STRIPPED = 0x0200, + + /// + /// If the image is on removable media, fully load it and copy it to the swap file. + /// + IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP = 0x0400, + + /// + /// If the image is on network media, fully load it and copy it to the swap file. + /// + IMAGE_FILE_NET_RUN_FROM_SWAP = 0x0800, + + /// + /// The image file is a system file, not a user program. + /// + IMAGE_FILE_SYSTEM = 0x1000, + + /// + /// The image file is a dynamic-link library (DLL). Such files are considered executable + /// files for almost all purposes, although they cannot be directly run. + /// + IMAGE_FILE_DLL = 0x2000, + + /// + /// The file should be run only on a uniprocessor machine. + /// + IMAGE_FILE_UP_SYSTEM_ONLY = 0x4000, + + /// + /// Big endian: the MSB precedes the LSB in memory. + /// + /// This flag is deprecated and should be zero. + [Obsolete("This flag is deprecated and should be zero.")] + IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 + } + + private enum MachineType : ushort + { + /// + /// The contents of this field are assumed to be applicable to any machine type + /// + IMAGE_FILE_MACHINE_UNKNOWN = 0x0, + + /// + /// Matsushita AM33 + /// + IMAGE_FILE_MACHINE_AM33 = 0x1d3, + + /// + /// Intel x64 + /// + IMAGE_FILE_MACHINE_AMD64 = 0x8664, + + /// + /// ARM little endian + /// + IMAGE_FILE_MACHINE_ARM = 0x1c0, + + /// + /// ARM Thumb-2 little endian + /// + IMAGE_FILE_MACHINE_ARMNT = 0x1c4, + + /// + /// EFI byte code + /// + IMAGE_FILE_MACHINE_EBC = 0xebc, + + /// + /// Intel 386 or later processors and compatible processors + /// + IMAGE_FILE_MACHINE_I386 = 0x14c, + + /// + /// Intel Itanium processor family + /// + IMAGE_FILE_MACHINE_IA64 = 0x200, + + /// + /// Mitsubishi M32R little endian + /// + IMAGE_FILE_MACHINE_M32R = 0x9041, + + /// + /// MIPS16 without FPU + /// + IMAGE_FILE_MACHINE_MIPS16 = 0x266, + + /// + /// MIPS with FPU + /// + IMAGE_FILE_MACHINE_MIPSFPU = 0x366, + + /// + /// MIPS16 with FPU + /// + IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466, + + /// + /// Power PC little endian + /// + IMAGE_FILE_MACHINE_POWERPC = 0x1f0, + + /// + /// Power PC with floating point support + /// + IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1, + + /// + /// MIPS little endian + /// + IMAGE_FILE_MACHINE_R4000 = 0x166, + + /// + /// RISC-V 32-bit address space + /// + IMAGE_FILE_MACHINE_RISCV32 = 0x5032, + + /// + /// RISC-V 64-bit address space + /// + IMAGE_FILE_MACHINE_RISCV64 = 0x5064, + + /// + /// RISC-V 128-bit address space + /// + IMAGE_FILE_MACHINE_RISCV128 = 0x5128, + + /// + /// Hitachi SH3 + /// + IMAGE_FILE_MACHINE_SH3 = 0x1a2, + + /// + /// Hitachi SH3 DSP + /// + IMAGE_FILE_MACHINE_SH3DSP = 0x1a3, + + /// + /// Hitachi SH4 + /// + IMAGE_FILE_MACHINE_SH4 = 0x1a6, + + /// + /// Hitachi SH5 + /// + IMAGE_FILE_MACHINE_SH5 = 0x1a8, + + /// + /// Thumb + /// + IMAGE_FILE_MACHINE_THUMB = 0x1c2, + + /// + /// MIPS little-endian WCE v2 + /// + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 + } + + private enum PEType : ushort + { + None = 0, + + /// + /// ROM Image + /// + ROMImage = 0x107, + + /// + /// Normal executable + /// + PE32 = 0x10B, + + /// + /// PE32+ executable + /// + PE32Plus = 0x20B + } + /// /// Loads a set of assemblies based on the given . /// @@ -6731,7 +6977,141 @@ public IEnumerable Load(string searchPattern) /// true if the file can be loaded, otherwise false. protected virtual bool CanLoad(string fileName) { - return true; + return IsManagedDll(fileName) ?? false; + } + + /// + /// Indicates if the current represent a managed assembly (.exe or .dll). + /// + /// The name of the target file. + /// true if the dll is Managed, false if it is native, and null if unknown + private static bool? IsManagedDll(string fileName) + { + try + { + var pe = GetDllMachineType(fileName); + + // The 15th directory consist of CLR header! if its 0, its not a CLR file :) + return pe.DataDictionaryRVA[14] != 0; + } +#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + catch +#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + { + } + + return null; + } + + private static IMAGE_FILE_HEADER GetDllMachineType(string dllPath) + { + // See http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx Offset to PE + // header is always at 0x3C. The PE header starts with "PE\0\0" = 0x50 0x45 0x00 0x00, + // followed by a 2-byte machine type field (see the document above for the enum). + var result = IMAGE_FILE_HEADER.Empty; + + FileStream stream = null; + try + { + stream = new FileStream(dllPath, FileMode.Open, FileAccess.Read); + using (var reader = new BinaryReader(stream)) + { + stream = null; + + // PE Header starts @ 0x3C (60). Its a 4 byte header. + stream.Seek(0x3c, SeekOrigin.Begin); + var peHeaderOffset = reader.ReadInt32(); + if (peHeaderOffset == 0) + { + peHeaderOffset = 0x80; + } + + // Ensure there is at least enough room for the following structures: 24 byte PE + // Signature & Header 28 byte Standard Fields (24 bytes for PE32+) 68 byte NT Fields + // (88 bytes for PE32+) >= 128 byte Data Dictionary Table + if (peHeaderOffset > stream.Length - 256) + { + throw new BadImageFormatException("File either is not a PE/COFF file or is corrupted.", dllPath); + } + + // Moving to PE Header start location... + stream.Seek(peHeaderOffset, SeekOrigin.Begin); + var peHeaderSignature = reader.ReadUInt32(); + + // Check the PE signature. Should equal 'PE\0\0'. + if (peHeaderSignature != 0x00004550) + { + throw new BadImageFormatException("Can't find PE header", dllPath); + } + + result.Machine = (MachineType)reader.ReadUInt16(); + result.NumberOfSections = reader.ReadUInt16(); + var seconds = reader.ReadUInt32(); + result.TimeDateStamp = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(seconds); + result.PointerToSymbolTable = reader.ReadUInt32(); + result.NumberOfSymbols = reader.ReadUInt32(); + result.SizeOfOptionalHeader = reader.ReadUInt16(); + result.Characteristics = (Characteristics)reader.ReadUInt16(); + + // Now we are at the end of the PE Header and from here, the PE Optional Headers + // starts... Read PE magic number from Standard Fields to determine format. + result.PEFormat = (PEType)reader.ReadUInt16(); + if (result.PEFormat != PEType.PE32 && result.PEFormat != PEType.PE32Plus) + { + throw new BadImageFormatException("Found neither PE nor PE+ magic numbers", dllPath); + } + + // we'll increase the stream's current position to with 96 for PE headers and 112 for + // PE+ headers we want to skip these structures: 28 byte Standard Fields (24 bytes + // for PE32+) 68 byte NT Fields (88 bytes for PE32+) + var dataDictionaryStart = peHeaderOffset + (result.PEFormat == PEType.PE32 ? 96 : 112); + + // To go directly to the datadictionary + stream.Position = stream.Position + dataDictionaryStart; + + // DataDictionay has 16 directories in total, doing simple maths 128/16 = 8. So each + // directory is of 8 bytes. In this 8 bytes, 4 bytes is of RVA and 4 bytes of Size. + for (int i = 0; i < 15; i++) + { + result.DataDictionaryRVA[i] = reader.ReadUInt32(); + result.DataDictionarySize[i] = reader.ReadUInt32(); + } + } + } +#pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + catch +#pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + { + } + finally + { + stream?.Dispose(); + } + + return result; + } + + private struct IMAGE_FILE_HEADER + { + public static readonly IMAGE_FILE_HEADER Empty = Initialize(); + public Characteristics Characteristics; + public uint[] DataDictionaryRVA; + public uint[] DataDictionarySize; + public MachineType Machine; + public ushort NumberOfSections; + public uint NumberOfSymbols; + public PEType PEFormat; + public uint PointerToSymbolTable; + public ushort SizeOfOptionalHeader; + public DateTime TimeDateStamp; + + public static IMAGE_FILE_HEADER Initialize() + { + var result = default(IMAGE_FILE_HEADER); + result.DataDictionaryRVA = new uint[16]; + result.DataDictionarySize = new uint[16]; + return result; + } } ///