2

I have a C# program that needs to copy over a user provided dll for another program to load and use. In the case of the program running on a 64 bit machine, the user should not be allowed to pass a 32 bit dll and should inform the user that they've provided an incorrect dll. So how can I find the architecture of a dll?

I saw a couple similar questions and they mentioned DUMPBIN and Corflags.exe, but there is no example code, so where do I find these programs and how do I use these?

Michael Holman
  • 901
  • 1
  • 10
  • 26
  • What are you trying to detect? Another .NET assembly? It should be easy enough to attempt to read the assembly's metadata. – Robert Harvey Oct 26 '12 at 00:14
  • 1
    Take a look: http://stackoverflow.com/a/1002672/453348 – tttony Oct 26 '12 at 00:24
  • For anyone looking for easier solutions than those provided here, this functionality is handled in the .Net AssemblyName class: https://msdn.microsoft.com/en-us/library/system.reflection.assemblyname.processorarchitecture(v=vs.110).aspx – Thick_propheT Jan 28 '15 at 22:14

3 Answers3

5

Code example

This is the complete code of a C# console application that can detect dll architectures that also includes the ones you wanted.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MachineType type = GetDllMachineType("path/to/MyAssembly.dll");

            if (type.Equals(MachineType.IMAGE_FILE_MACHINE_I386)) 
            {
                Console.WriteLine("Dll architecture: x86/32bit");
            }
            else if (type.Equals(MachineType.IMAGE_FILE_MACHINE_IA64)) 
            {
                Console.WriteLine("Dll architecture: x64/64bit");
            }

            Console.ReadKey();
        }

        public static MachineType GetDllMachineType(string dllPath)
        {
            //see http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
            //offset to PE header is always at 0x3C
            //PE header starts with "PE\0\0" =  0x50 0x45 0x00 0x00
            //followed by 2-byte machine type field (see document above for enum)
            FileStream fs = new FileStream(dllPath, FileMode.Open, FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);
            fs.Seek(0x3c, SeekOrigin.Begin);
            Int32 peOffset = br.ReadInt32();
            fs.Seek(peOffset, SeekOrigin.Begin);
            UInt32 peHead = br.ReadUInt32();
            if (peHead != 0x00004550) // "PE\0\0", little-endian
                throw new Exception("Can't find PE header");
            MachineType machineType = (MachineType)br.ReadUInt16();
            br.Close();
            fs.Close();
            return machineType;
        }

        public enum MachineType : ushort
        {
            IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
            IMAGE_FILE_MACHINE_AM33 = 0x1d3,
            IMAGE_FILE_MACHINE_AMD64 = 0x8664,
            IMAGE_FILE_MACHINE_ARM = 0x1c0,
            IMAGE_FILE_MACHINE_EBC = 0xebc,
            IMAGE_FILE_MACHINE_I386 = 0x14c,
            IMAGE_FILE_MACHINE_IA64 = 0x200,
            IMAGE_FILE_MACHINE_M32R = 0x9041,
            IMAGE_FILE_MACHINE_MIPS16 = 0x266,
            IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
            IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
            IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
            IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
            IMAGE_FILE_MACHINE_R4000 = 0x166,
            IMAGE_FILE_MACHINE_SH3 = 0x1a2,
            IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
            IMAGE_FILE_MACHINE_SH4 = 0x1a6,
            IMAGE_FILE_MACHINE_SH5 = 0x1a8,
            IMAGE_FILE_MACHINE_THUMB = 0x1c2,
            IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169,
        }

        // returns true if the dll is 64-bit, false if 32-bit, and null if unknown
        public static bool? UnmanagedDllIs64Bit(string dllPath)
        {
            switch (GetDllMachineType(dllPath))
            {
                case MachineType.IMAGE_FILE_MACHINE_AMD64:
                case MachineType.IMAGE_FILE_MACHINE_IA64:
                    return true;
                case MachineType.IMAGE_FILE_MACHINE_I386:
                    return false;
                default:
                    return null;
            }
        }
    }
}

Using Corflags...

You wrote about this and, just to know, this will help you to get some information regarding your assembly (dll) but this is not C#! this is a tool that can be used in Visual Studio console.

Just open Visual Studio console and use this command:

C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC>corflags C:/path/to/MyAssembly.dll

This will be the output:

Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 3.5.21022.8 Copyright (c) Microsoft Corporation. All rights reserved.

Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 24
ILONLY : 0
32BIT : 0
Signed : 1

Then, focus on PE:PE32, this will describe your assembly architecture:

So, according to this...

  • AnyCPU means -> PE: PE32 -> 32BIT: 0

  • x86 means -> PE: PE32 -> 32BIT: 1

  • x64 means -> PE: PE32+ -> 32BIT: 0

The architecture of MyAssembly.dll is 32bit


Idea...

Well, if you want to simplify all this, an idea could be to create a background process using C# then in the arguments use the command I gave you above and print the output of PE:XX to get the assembly architecture and according to that value tell your application what to do.


I just made some research, hope this helps :-)

Oscar Jara
  • 14,129
  • 10
  • 62
  • 94
3

A while back we wanted to run 64bit mixed mode unit tests in build without changing the build environment. I wrote a tool that creates a proxy assembly using reflection emit. Here is the utility class I wrote that detects if assembly is 64bit. This is different then the flags controlled by corflags

namespace MstestRunner.TestProxyGenerator { using System; using System.Globalization; using System.IO; using System.Reflection;

/// <summary>
/// TODO: Update summary.
/// </summary>
public static class AssemblyUtility
{
    /// <summary>
    /// The value 'PE\0\0'
    /// </summary>
    private const uint PeHeaderValue = 0x4550;

    /// <summary>
    /// Image file value found at start of PE header that indicates assembly is 64bit.
    /// </summary>
    private const ushort ImageFileMachineAmd64 = 0x8664;

    /// <summary>
    /// The offset to PIMAGE_DOS_HEADER->e_lfanew
    /// </summary>
    private const int DosHeaderLfaNewOffset = 0x3c;

    /// <summary>
    /// Checks to see if the module is a 64 bit
    /// </summary>
    /// <param name="path">The path to the assembly.</param>
    /// <returns>
    /// True if is 64bit
    /// </returns>
    public static bool Is64BitImage(string path)
    {
        return ReadImageMachineType(path) == MachineType.ImageFileMachineAMD64;
    }

    /// <summary>
    /// Reads the machine type from the pe header.
    /// </summary>
    /// <param name="path">The path to the image.</param>
    /// <returns>The assembly machinetype.</returns>
    public static MachineType ReadImageMachineType(string path)
    {
        // The memory layout varies depending on 32/64 bit.  The portions of the PE header we are reading should be the same though regardless.
        byte[] buffer = new byte[4];
        using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            // skip to PIMAGE_DOS_HEADER->e_lfanew of dos header.
            fileStream.Seek(DosHeaderLfaNewOffset, SeekOrigin.Begin);

            // read and jump to offset in PIMAGE_DOS_HEADER->e_lfanew.  This is start of PIMAGE_NT_HEADERS
            fileStream.Read(buffer, 0, 4);
            fileStream.Seek(BitConverter.ToUInt32(buffer, 0), SeekOrigin.Begin);

            // Validate PE\0\0 header.
            fileStream.Read(buffer, 0, 4);
            if (BitConverter.ToUInt32(buffer, 0) != PeHeaderValue)
            {
                throw new TestRunnerException(string.Format(CultureInfo.InvariantCulture, "The specified assembly '{0}' does not appear to be valid.", path));
            }

            // Read the PIMAGE_FILE_HEADER->Machine value. 
            fileStream.Read(buffer, 0, 2);
            return (MachineType)BitConverter.ToUInt16(buffer, 0);
        }
    }

    /// <summary>
    /// Safely loads the assembly.
    /// </summary>
    /// <param name="path">The path to the assembly to load.</param>
    /// <returns>The loaded assembly</returns>
    public static Assembly SafeLoadAssembly(string path)
    {
        try
        {
            return Assembly.Load(path);
        }
        catch (ArgumentNullException)
        {
        }
        catch (FileNotFoundException)
        {
        }
        catch (FileLoadException)
        {
        }
        catch (BadImageFormatException)
        {
        }

        return null;
    }
}

}

AbdElRaheim
  • 1,384
  • 6
  • 8
0

Just try to load the dll with imageload

http://msdn.microsoft.com/en-us/library/ms680209(v=vs.85).aspx

Of course unload it or get last error if this function fails so you can study the output and decide after that if this dll is same architecture like your c# application or not.

Mahmoud Fayez
  • 3,398
  • 2
  • 19
  • 36