22

How to check if Windows executable is 64-bit reading only its binary. Without executing it and not using any tools like the SDK tool dumpbin.exe with the /headers option.

4 Answers 4

26

Executable type is indicated by PE header, download documentation.

The first word (two bytes) of PE header indicates target machine, here is a list of possible values:

0x0000 - The contents of this field are assumed to be applicable to any machine type
0x01d3 - Matsushita AM33
0x8664 - x64
0x01c0 - ARM little endian
0x01c4 - ARMv7 (or higher) Thumb mode only
0xaa64 - ARMv8 in 64-bit mode
0x0ebc - EFI byte code
0x014c - Intel 386 or later processors and compatible processors
0x0200 - Intel Itanium processor family
0x9041 - Mitsubishi M32R little endian
0x0266 - MIPS16
0x0366 - MIPS with FPU
0x0466 - MIPS16 with FPU
0x01f0 - Power PC little endian
0x01f1 - Power PC with floating point support
0x0166 - MIPS little endian
0x01a2 - Hitachi SH3
0x01a3 - Hitachi SH3 DSP
0x01a6 - Hitachi SH4
0x01a8 - Hitachi SH5
0x01c2 - ARM or Thumb (“interworking”)
0x0169 - MIPS little-endian WCE v2

So to check if it is 64-bit, we need to look for:

0x8664 - x64
0xaa64 - ARMv8 in 64-bit mode
0x0200 - Intel Itanium processor family

And as Bob mentioned, here is a list of some more machine types (see 11 pg.), however it is not very likely to find them.

4
  • 6
    Don't forget 0x0200 (Itanium). That would be IA-64, as opposed to x86-64 (AMD64). There's a couple more "64-bit" machine types possible, but you're very unlikely to find them in the wild (0x0284 is the 64-bit Alpha AXP, from the 1999 documentation, and not supported by Windows after NT4/2000RC).
    – Bob
    Commented Aug 8, 2014 at 10:55
  • For a visual reference, image showing the location of the PE header with 0x8664 (highlighted): i.imgur.com/yHvcgdn.png (notepad++ hex-editor plugin)
    – zamnuts
    Commented Aug 8, 2014 at 14:47
  • 1
    To quickly find the target machine value: Take the DWORD at offset 0x3C. Add four to this value. That is the offset of the Machine WORD. (Both are little endian) Commented Aug 25, 2014 at 21:45
  • Where do you find 0xaa64 - ARMv8 in 64-bit mode? I have not such #define in winnt.h.
    – SerG
    Commented Feb 12, 2015 at 14:34
7

It's easier to check Optional Header magic number.

For a valid exe, only two values are possible:

0x10B => PE32  => 32 bit
0x20B => PE32+ => 64 bit
1
  • Might be worth mentioning the offset to find it at (0x18 bytes from the NT Header which starts at the "PE" magic).
    – Jordan
    Commented Aug 6, 2019 at 23:02
5

Here's a fun, quick little trick if in a Windows environment and you're checking for an x86 or x64 binary:

  1. Right-click on the application.
  2. Click Properties.
  3. Click the Compatibility tab.
  4. In the Compatibility mode section, check the Run this program in compatibility mode for: box.
  5. If you see Windows 95 as an option from the drop-down, then it's a 32-bit application. If you do not, then it's a 64-bit application.
3

dumping the contents of ST3's answer into a powershell snippet in a ready to use format

if($args.Count -eq 0) { "provide a file name or path to file";exit }
if((test-path -path $args) -ne $true) { "file doesnt seem to exist" ; exit }
$fs = New-Object IO.Filestream($args , [Io.FileMode]::Open)
$br = New-Object IO.BinaryReader($fs)
if($br.Readchar()-ne'M'){"no mz";exit};if($br.Readchar()-ne'Z'){"no mz";exit}
$fs.Seek(0x3c,[IO.SeekOrigin]::Begin) | Out-Null
$elfaw_new = $br.ReadUInt32();
$peheader=$fs.Seek($elfaw_new,[IO.SeekOrigin]::Begin) 
if($br.Readchar()-ne'P'){"no pe";exit};if($br.Readchar()-ne'E'){"no pe";exit}
$mctypeoff = $fs.seek($peheader+4,[IO.SeekOrigin]::Begin)
$mctype= $br.ReadUInt16()
switch($mctype) {
  0x0000 { "{0:x4} {1}" -f  $mctype , "Unknown machine type"}
  0x01d3 { "{0:x4} {1}" -f  $mctype , "Matsushita AM33"}
  0x8664 { "{0:x4} {1}" -f  $mctype , "x64"}
  0x01c0 { "{0:x4} {1}" -f  $mctype , "ARM little endian"}
  0x01c4 { "{0:x4} {1}" -f  $mctype , "ARMv7 (or higher) Thumb mode only"}
  0xaa64 { "{0:x4} {1}" -f  $mctype , "ARMv8 in 64-bit mode"}
  0x0ebc { "{0:x4} {1}" -f  $mctype , "EFI byte code"}
  0x014c { "{0:x4} {1}" -f  $mctype , "Intel 386 or later family processors"}
  0x0200 { "{0:x4} {1}" -f  $mctype , "Intel Itanium processor family"}
  0x9041 { "{0:x4} {1}" -f  $mctype , "Mitsubishi M32R little endian"}
  0x0266 { "{0:x4} {1}" -f  $mctype , "MIPS16"}
  0x0366 { "{0:x4} {1}" -f  $mctype , "MIPS with FPU"}
  0x0466 { "{0:x4} {1}" -f  $mctype , "MIPS16 with FPU"}
  0x01f0 { "{0:x4} {1}" -f  $mctype , "Power PC little endian"}
  0x01f1 { "{0:x4} {1}" -f  $mctype , "Power PC with floating point support"}
  0x0166 { "{0:x4} {1}" -f  $mctype , "MIPS little endian"}
  0x01a2 { "{0:x4} {1}" -f  $mctype , "Hitachi SH3"}
  0x01a3 { "{0:x4} {1}" -f  $mctype , "Hitachi SH3 DSP"}
  0x01a6 { "{0:x4} {1}" -f  $mctype , "Hitachi SH4"}
  0x01a8 { "{0:x4} {1}" -f  $mctype , "Hitachi SH5"}
  0x01c2 { "{0:x4} {1}" -f  $mctype , "ARM or Thumb (`“interworking`”)"}
  0x0169 { "{0:x4} {1}" -f  $mctype , "MIPS little-endian WCE v2"}
};$fs.close()

usage as follows

:\>powershell -f binstreamtest.ps1
provide a file name or path to file

:\>powershell -f binstreamtest.ps1 1
file doesnt seem to exist

:\>powershell -f binstreamtest.ps1 shell32.dll
014c Intel 386 or later family processors

:\>powershell -f binstreamtest.ps1 c:\WINDOWS\system32\ntkrnlpa.exe
014c Intel 386 or later family processors

:\>powershell -f binstreamtest.ps1 xxx\test32.exe
014c Intel 386 or later family processors

:\>powershell -f binstreamtest.ps1 xxx\test64.exe
8664 x64
1
  • Note exit will actually kill the parent terminal, if you replace it with return, it will work. In Powershell, return is really more for control-flow, because values are implicitly outputted . The only time it doesn't, is when writing a class
    – ninMonkey
    Commented Aug 20, 2022 at 1:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.