Monday, June 23, 2014

Determining if App Executable is 32-bit or 64-bit

Need to find out if your application executable is 32-bit or 64-bit reliably pro-grammatically in C#

My first attempt was to use the GetBinaryType() - using DLL Import
but reading comment in the MSDN Library reference indicates it is not reliable solution. 

So how can we examine a file to determine "bitness"? 

Again we head back to the source in a issue of February 2002 issue of MSDN Magazine "
Win32 Portable Executable File Format"

The format of an operating system's executable file is in many ways a mirror of the operating system. Although studying an executable file format isn't usually high on most programmers' list of things to do, a great deal of knowledge can be gleaned this way. In this article, I'll give a tour of the Portable Executable (PE) file format that Microsoft has designed for use by all their Win32®-based systems: Windows NT®, Win32s™, and Windows® 95. 

PE File Structure

      Now let's dig into the actual format of PE files. I'll start from the beginning of the file, and describe the data structures that are present in every PE file. Afterwards, I'll describe the more specialized data structures (such as imports or resources) that reside within a PE's sections. All of the data structures that I'll discuss below are defined in WINNT.H, unless otherwise noted.
      In many cases, there are matching 32 and 64-bit data structures—for example, IMAGE_NT_HEADERS32 and IMAGE_NT_HEADERS64.

The IMAGE_NT_HEADERS Header

      The IMAGE_NT_HEADERS structure is the primary location where specifics of the PE file are stored. Its offset is given by the e_lfanew field in the IMAGE_DOS_HEADER at the beginning of the file. There are actually two versions of the IMAGE_NT_HEADER structure, one for 32-bit executables and the other for 64-bit versions. The differences are so minor that I'll consider them to be the same for the purposes of this discussion. The only correct, Microsoft-approved way of differentiating between the two formats is via the value of the Magic field in the IMAGE_OPTIONAL_HEADER (described shortly).

An IMAGE_NT_HEADER is comprised of three fields:

 
 typedef struct _IMAGE_NT_HEADERS {
      DWORD Signature;
     IMAGE_FILE_HEADER FileHeader;
     IMAGE_OPTIONAL_HEADER32 OptionalHeader;
  } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

In a valid PE file, the Signature field is set to the value 0x00004550, which in ASCII is "PE00". A #define, IMAGE_NT_SIGNATURE, is defined for this value. The second field, a struct of type IMAGE_FILE_HEADER, predates PE files.


The PE File IMAGE_FILE_HEADER referenced in the above article as Figure 3 is quoted below. 

Figure 3
 IMAGE_FILE_HEADER (http://msdn.microsoft.com/en-us/magazine/bb985997.aspx)
  
Size
 
Field
 
Description
 
Meaning or Bitness
WORD
 
Machine
 
The target CPU for this executable. Common values are:
IMAGE_FILE_MACHINE_I386    0x014c // Intel 386
IMAGE_FILE_MACHINE_IA64    0x0200 // Intel 64

x86 (32-bit)
x64 (64-bit)
  

And this seems great we have found a way to distinguish x32 or x64 files. 

BUT, Accroding to MSDN Windows Dev Center documentation we have conflicting values. 
http://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
Constant Hex Value Meaning


IMAGE_FILE_MACHINE_I386
0x014c
x86


IMAGE_FILE_MACHINE_IA64
0x0200
Intel Itanium


IMAGE_FILE_MACHINE_AMD64
0x8664
x64

IMAGE_FILE_MACHINE_AMD64  is not Advanced Micro Devices (64-bit) processor indication, 
but correctly will give us where or not a dll or exe is 64-bit or it's "bitness".


Now coding this in C# we have  

using System;
using System.IO; 

public enum MachineType {Unknown = 0, 
                          x86 = 0x014c, 
                          i64 = 0x0200, 
                          x64 = 0x8664 }  

public string GetAppCompiledMachineType(string fileName)
{
            const int PE_POINTER_OFFSET = 60;            
            const int MACHINE_OFFSET = 4;
            byte[] data = new byte[4096];

            using (Stream s = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
                s.Read(data, 0, 4096);
            }

            // dos header is 64 bytes, last element, long (4 bytes) is the address of the PE header
            int PE_HEADER_ADDR = BitConverter.ToInt32(data, PE_POINTER_OFFSET);
            int machineUint = BitConverter.ToUInt16(data, PE_HEADER_ADDR + MACHINE_OFFSET);
            return ((MachineType)machineUint).ToString();
}


Using the above code yields the proper results. I have tried this on numerous known 64-bit apps and it works properly, x64 is returned for 64-bit apps.




No comments:

Post a Comment