How to find if a native DLL file is compiled as x64 or x86?

Viewed 118235

I want to determine if a native assembly is complied as x64 or x86 from a managed code application (C#).

I think it must be somewhere in the PE header since the OS loader needs to know this information, but I couldn't find it. Of course I prefer to do it in managed code, but if it necessary, I can use native C++.

12 Answers

There is an easy way to do this with CorFlags. Open the Visual Studio Command Prompt and type "corflags [your assembly]". You'll get something like this:

c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC>corflags "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll"

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

You're looking at PE and 32BIT specifically.

  • Any CPU:

    PE: PE32
    32BIT: 0

  • x86:

    PE: PE32
    32BIT: 1

  • x64:

    PE: PE32+
    32BIT: 0

The Magic field of the IMAGE_OPTIONAL_HEADER (though there is nothing optional about the header in Windows executable images (DLL/EXE files)) will tell you the architecture of the PE.

Here's an example of grabbing the architecture from a file.

public static ushort GetImageArchitecture(string filepath) {
    using (var stream = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
    using (var reader = new System.IO.BinaryReader(stream)) {
        //check the MZ signature to ensure it's a valid Portable Executable image
        if (reader.ReadUInt16() != 23117) 
            throw new BadImageFormatException("Not a valid Portable Executable image", filepath);

        // seek to, and read, e_lfanew then advance the stream to there (start of NT header)
        stream.Seek(0x3A, System.IO.SeekOrigin.Current); 
        stream.Seek(reader.ReadUInt32(), System.IO.SeekOrigin.Begin);

        // Ensure the NT header is valid by checking the "PE\0\0" signature
        if (reader.ReadUInt32() != 17744)
            throw new BadImageFormatException("Not a valid Portable Executable image", filepath);

        // seek past the file header, then read the magic number from the optional header
        stream.Seek(20, System.IO.SeekOrigin.Current); 
        return reader.ReadUInt16();
    }
}

The only two architecture constants at the moment are:

0x10b - PE32
0x20b - PE32+

Cheers

UPDATE It's been a while since I posted this answer, yet I still see that it gets a few upvotes now and again so I figured it was worth updating. I wrote a way to get the architecture of a Portable Executable image, which also checks to see if it was compiled as AnyCPU. Unfortunately the answer is in C++, but it shouldn't be too hard to port to C# if you have a few minutes to look up the structures in WinNT.h. If people are interested I'll write a port in C#, but unless people actually want it I wont spend much time stressing about it.

#include <Windows.h>

#define MKPTR(p1,p2) ((DWORD_PTR)(p1) + (DWORD_PTR)(p2))

typedef enum _pe_architecture {
    PE_ARCHITECTURE_UNKNOWN = 0x0000,
    PE_ARCHITECTURE_ANYCPU  = 0x0001,
    PE_ARCHITECTURE_X86     = 0x010B,
    PE_ARCHITECTURE_x64     = 0x020B
} PE_ARCHITECTURE;

LPVOID GetOffsetFromRva(IMAGE_DOS_HEADER *pDos, IMAGE_NT_HEADERS *pNt, DWORD rva) {
    IMAGE_SECTION_HEADER *pSecHd = IMAGE_FIRST_SECTION(pNt);
    for(unsigned long i = 0; i < pNt->FileHeader.NumberOfSections; ++i, ++pSecHd) {
        // Lookup which section contains this RVA so we can translate the VA to a file offset
        if (rva >= pSecHd->VirtualAddress && rva < (pSecHd->VirtualAddress + pSecHd->Misc.VirtualSize)) {
            DWORD delta = pSecHd->VirtualAddress - pSecHd->PointerToRawData;
            return (LPVOID)MKPTR(pDos, rva - delta);
        }
    }
    return NULL;
}

PE_ARCHITECTURE GetImageArchitecture(void *pImageBase) {
    // Parse and validate the DOS header
    IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)pImageBase;
    if (IsBadReadPtr(pDosHd, sizeof(pDosHd->e_magic)) || pDosHd->e_magic != IMAGE_DOS_SIGNATURE)
        return PE_ARCHITECTURE_UNKNOWN;

    // Parse and validate the NT header
    IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)MKPTR(pDosHd, pDosHd->e_lfanew);
    if (IsBadReadPtr(pNtHd, sizeof(pNtHd->Signature)) || pNtHd->Signature != IMAGE_NT_SIGNATURE)
        return PE_ARCHITECTURE_UNKNOWN;

    // First, naive, check based on the 'Magic' number in the Optional Header.
    PE_ARCHITECTURE architecture = (PE_ARCHITECTURE)pNtHd->OptionalHeader.Magic;

    // If the architecture is x86, there is still a possibility that the image is 'AnyCPU'
    if (architecture == PE_ARCHITECTURE_X86) {
        IMAGE_DATA_DIRECTORY comDirectory = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR];
        if (comDirectory.Size) {
            IMAGE_COR20_HEADER *pClrHd = (IMAGE_COR20_HEADER*)GetOffsetFromRva(pDosHd, pNtHd, comDirectory.VirtualAddress);
            // Check to see if the CLR header contains the 32BITONLY flag, if not then the image is actually AnyCpu
            if ((pClrHd->Flags & COMIMAGE_FLAGS_32BITREQUIRED) == 0)
                architecture = PE_ARCHITECTURE_ANYCPU;
        }
    }

    return architecture;
}

The function accepts a pointer to an in-memory PE image (so you can choose your poison on how to get it their; memory-mapping or reading the whole thing into memory...whatever).

For an unmanaged DLL file, you need to first check if it is a 16-bit DLL file (hopefully not). Then check the IMAGE\_FILE_HEADER.Machine field.

Someone else took the time to work this out already, so I will just repeat here:

To distinguish between a 32-bit and 64-bit PE file, you should check IMAGE_FILE_HEADER.Machine field. Based on the Microsoft PE and COFF specification below, I have listed out all the possible values for this field: http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.doc

IMAGE_FILE_MACHINE_UNKNOWN 0x0 The contents of this field are assumed to be applicable to any machine type

IMAGE_FILE_MACHINE_AM33 0x1d3 Matsushita AM33

IMAGE_FILE_MACHINE_AMD64 0x8664 x64

IMAGE_FILE_MACHINE_ARM 0x1c0 ARM little endian

IMAGE_FILE_MACHINE_EBC 0xebc EFI byte code

IMAGE_FILE_MACHINE_I386 0x14c Intel 386 or later processors and compatible processors

IMAGE_FILE_MACHINE_IA64 0x200 Intel Itanium processor family

IMAGE_FILE_MACHINE_M32R 0x9041 Mitsubishi M32R little endian

IMAGE_FILE_MACHINE_MIPS16 0x266 MIPS16

IMAGE_FILE_MACHINE_MIPSFPU 0x366 MIPS with FPU

IMAGE_FILE_MACHINE_MIPSFPU16 0x466 MIPS16 with FPU

IMAGE_FILE_MACHINE_POWERPC 0x1f0 Power PC little endian

IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 Power PC with floating point support

IMAGE_FILE_MACHINE_R4000 0x166 MIPS little endian

IMAGE_FILE_MACHINE_SH3 0x1a2 Hitachi SH3

IMAGE_FILE_MACHINE_SH3DSP 0x1a3 Hitachi SH3 DSP

IMAGE_FILE_MACHINE_SH4 0x1a6 Hitachi SH4

IMAGE_FILE_MACHINE_SH5 0x1a8 Hitachi SH5

IMAGE_FILE_MACHINE_THUMB 0x1c2 Thumb

IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 MIPS little-endian WCE v2

Yes, you may check IMAGE_FILE_MACHINE_AMD64|IMAGE_FILE_MACHINE_IA64 for 64bit and IMAGE_FILE_MACHINE_I386 for 32bit.

Because of third-party tool always installing in %Program files (x86)% (even if x64 installation!) and requiring appropriate x32|x64 fortran runtime to be first listed on the %path% to run correctly, I gathered c++ and powershell solutions into matlab to return:

  • Kind: Executable|Library|Other
  • CodeType: Native|Mixed|Managed
  • Platform: x32|x64|AnyCpu|x32Preferred|Other

It should be easy to adapt to other languages once having raw PE structures in memory.

function [simplifiedInfo] = GetPortableExecutableSimplifiedInfo(filename)
%[
    % Checking arguments
    if (nargin <1), error('MATLAB:minrhs', 'Not enough input argments.'); end

    % Initializing simplified info    
    simplifiedInfo.Kind = 'Other';
    simplifiedInfo.CodeType = 'Other';
    simplifiedInfo.Platform = 'Other';

    % Obtaining raw info
    [rawInfo, PEConstants] = GetPortableExecutableRawInfo(filename);

    % Determining 'Kind' of PE
    if (isfield(rawInfo, 'PEOptionalHeader') && (rawInfo.COFFHeader.Characteristics.IMAGE_FILE_EXECUTABLE_IMAGE))
        if (rawInfo.COFFHeader.Characteristics.IMAGE_FILE_DLL)
            simplifiedInfo.Kind = 'Library';
        else
            simplifiedInfo.Kind = 'Executable';
        end
    else
        % No optional header or no IMAGE_FILE_EXECUTABLE_IMAGE flag ... 
        % Maybe just .obj or other thing
        simplifiedInfo.Kind = 'Other';
    end

    % Determining 'CodeType'
    % NB: 'COR20Header' is present for MSIL code, but not for native code
    if (isfield(rawInfo, 'COR20Header'))
        if (rawInfo.COR20Header.Flags.COMIMAGE_FLAGS_ILONLY)
            simplifiedInfo.CodeType = 'Managed';
        else
            simplifiedInfo.CodeType = 'Mixed';
        end
    else
        simplifiedInfo.CodeType = 'Native';
    end

    % Determining platform
    if (rawInfo.COFFHeader.Machine == PEConstants.IMAGE_FILE_MACHINE_AMD64)
        simplifiedInfo.Platform = 'x64';
    elseif (rawInfo.COFFHeader.Machine == PEConstants.IMAGE_FILE_MACHINE_I386)
        if (isfield(rawInfo, 'COR20Header'))
            % PE contains MSIL code, need more checks
            if (rawInfo.COR20Header.Flags.COMIMAGE_FLAGS_32BITREQUIRED)
                if (rawInfo.COR20Header.Flags.COMIMAGE_FLAGS_32BITPREFERRED)
                    simplifiedInfo.Platform = 'x32Preferred';
                else
                    simplifiedInfo.Platform = 'x32';
                end
            else
                simplifiedInfo.Platform = 'AnyCpu';
            end
        else
            % This is native code so ...
            simplifiedInfo.Platform = 'x32';
        end
    else
        % ARM, ...
        simplifiedInfo.Platform = 'Other';
    end
%]
end

Source code for internal GetPortableExecutableRawInfo function can be obtained here.

Apparently you can find it in the header of the portable executable. The corflags.exe utility is able to show you whether or not it targets x64. Hopefully this helps you find more information about it.

Related