Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine whether a DLL is a managed assembly or native (prevent loading a native dll)?

Original title: How can I prevent loading a native dll from a .NET app?

Background:

My C# application includes a plugin framework and generic plugin loader.

The plugin loader enumerates the application directory in order to identify plugin dlls (essentially it searches for *.dll at this time).

Within the same application directory is a native (Windows, non-.net) dll, which, indirectly, one of the plugin dlls depends upon.

The plugin loader blindly assumes that the native.dll is a .NET Assembly dll, simply because it only checks the file extension. When it attempts to load the native dll, an exception is thrown:

"Could not load file or assembly 'native.dll' or one of its dependencies. The module was expected to contain an assembly manifest."

I basically create a diagnostic report if plugin loading fails, so I'm trying to avoid having this log filled up with messages about not being able to load the native dll (which I don't even want to attempt).

The question:

Is there some .NET API call that I can use to determine whether a binary happens to be a .NET assembly so that I don't attempt to load the native dll at all?

Perhaps longer term I will move my plugins to a subdirectory, but for now, I just want a work around that doesn't involve hard-coding the "native.dll" name inside my plugin loader.

I guess I'm looking for some kind of static Assembly.IsManaged() API call that I've overlooked.... presumably no such API exists?

like image 693
Mr. Wilby Avatar asked Dec 15 '08 08:12

Mr. Wilby


People also ask

How do I know if a DLL is native or managed?

To determine whether a DLL (or EXE) is managed or unmanaged, use dumpbin.exe with the /dependents switch. If you see mscoree. dll in the output, then the assembly is a managed assembly.

Is DLL managed or unmanaged?

dll is definitely unmanaged - it's part of Windows. If you want to check, try to add the file as a reference to your project - if it adds ok, it's managed.

What is difference between assembly and DLL?

An assembly is a collection of one or more files and one of them DLL or EXE. DLL contains library code to be used by any program running on Windows. A DLL may contain either structured or object oriented libraries. A DLL file can have a nearly infinite possible entry points.

What is a managed assembly?

Managed assemblies are located and loaded with an algorithm that has various stages. All managed assemblies except satellite assemblies and WinRT assemblies use the same algorithm.


1 Answers

Answer quoted by lubos hasko is good but it doesn't work for 64-bit assemblies. Here's a corrected version (inspired by http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311#ApiChange.Api/src/Introspection/CorFlagsReader.cs)

public static bool IsManagedAssembly(string fileName) {     using (Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))     using (BinaryReader binaryReader = new BinaryReader(fileStream))     {         if (fileStream.Length < 64)         {             return false;         }          //PE Header starts @ 0x3C (60). Its a 4 byte header.         fileStream.Position = 0x3C;         uint peHeaderPointer = binaryReader.ReadUInt32();         if (peHeaderPointer == 0)         {             peHeaderPointer = 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 (peHeaderPointer > fileStream.Length - 256)         {             return false;         }          // Check the PE signature.  Should equal 'PE\0\0'.         fileStream.Position = peHeaderPointer;         uint peHeaderSignature = binaryReader.ReadUInt32();         if (peHeaderSignature != 0x00004550)         {             return false;         }          // skip over the PEHeader fields         fileStream.Position += 20;          const ushort PE32 = 0x10b;         const ushort PE32Plus = 0x20b;          // Read PE magic number from Standard Fields to determine format.         var peFormat = binaryReader.ReadUInt16();         if (peFormat != PE32 && peFormat != PE32Plus)         {             return false;         }          // Read the 15th Data Dictionary RVA field which contains the CLI header RVA.         // When this is non-zero then the file contains CLI data otherwise not.         ushort dataDictionaryStart = (ushort)(peHeaderPointer + (peFormat == PE32 ? 232 : 248));         fileStream.Position = dataDictionaryStart;          uint cliHeaderRva = binaryReader.ReadUInt32();         if (cliHeaderRva == 0)         {             return false;         }          return true;     } } 

The missing piece was to offset to the data dictionary start differently depending on whether we are PE32 or PE32Plus:

    // Read PE magic number from Standard Fields to determine format.     var peFormat = binaryReader.ReadUInt16();     if (peFormat != PE32 && peFormat != PE32Plus)     {         return false;     }      // Read the 15th Data Dictionary RVA field which contains the CLI header RVA.     // When this is non-zero then the file contains CLI data otherwise not.     ushort dataDictionaryStart = (ushort)(peHeaderPointer + (peFormat == PE32 ? 232 : 248)); 
like image 57
Kirill Osenkov Avatar answered Oct 04 '22 12:10

Kirill Osenkov