pkit
pkit

Reputation: 8301

Determine if an executable (or library) is 32 -or 64-bits (on Windows)

I am trying to find out if a given executable (or library) is compiled for 32-bits or 64-bits from Python. I am running Vista 64-bits and would like to determine if a certain application in a directory is compiled for 32-bits or 64-bits.

Is there a simple way to do this using only the standard Python libraries (currently using 2.5.4)?

Upvotes: 13

Views: 10104

Answers (5)

iFarbod
iFarbod

Reputation: 639

I've edited Martin B's answer to work with Python 3, added with statements and ARM/ARM64 support:

import struct

IMAGE_FILE_MACHINE_I386 = 332
IMAGE_FILE_MACHINE_IA64 = 512
IMAGE_FILE_MACHINE_AMD64 = 34404
IMAGE_FILE_MACHINE_ARM = 452
IMAGE_FILE_MACHINE_AARCH64 = 43620

with open('foo.exe', 'rb') as f:
    s = f.read(2)
    if s != b'MZ':
        print('Not an EXE file')
    else:
        f.seek(60)
        s = f.read(4)
        header_offset = struct.unpack('<L', s)[0]
        f.seek(header_offset + 4)
        s = f.read(2)
        machine = struct.unpack('<H', s)[0]

        if machine == IMAGE_FILE_MACHINE_I386:
            print('IA-32 (32-bit x86)')
        elif machine == IMAGE_FILE_MACHINE_IA64:
            print('IA-64 (Itanium)')
        elif machine == IMAGE_FILE_MACHINE_AMD64:
            print('AMD64 (64-bit x86)')
        elif machine == IMAGE_FILE_MACHINE_ARM:
            print('ARM eabi (32-bit)')
        elif machine == IMAGE_FILE_MACHINE_AARCH64:
            print('AArch64 (ARM-64, 64-bit)')
        else:
            print(f'Unknown architecture {machine}')

Upvotes: 3

Ross Boylan
Ross Boylan

Reputation: 21

  1. Using Python 3.7, 32 bit on 64 bit Win 7, the first code fragment in the top answer doesn't run for me. It fails because GetBinaryType is an unknown symbol. Solution is to use win32file.GetBinaryType.
  2. Also running it on a .pyd file doesn't work, even if it is renamed to a .dll. See next:

    import shutil
    
    import win32file
    from pathlib import Path
    
    myDir = Path("C:\\Users\\rdboylan\\AppData\\Roaming\\Python\\Python37\\site-packages\\pythonwin")
    for fn in ("Pythonwin.exe", "win32ui.pyd"):
        print(fn, end=": ")
        myf = myDir / fn
        if myf.suffix == ".pyd":
            mytemp = myf.with_suffix(".dll")
            if mytemp.exists():
                raise "Can not create temporary dll since {} exists".format(mytemp)
            shutil.copyfile(myf, mytemp)
            type = win32file.GetBinaryType(str(mytemp))
            mytemp.unlink()
        else:
            type=win32file.GetBinaryType(str(myf))
        if type==win32file.SCS_32BIT_BINARY:
            print("32 bit")
        else:
            print("Something else")
        # And so on 
    

    Results in

    Pythonwin.exe: 32 bit
    win32ui.pyd: Traceback (most recent call last):
      File "C:/Users/rdboylan/Documents/Wk devel/bitness.py", line 14, in <module>
        type = win32file.GetBinaryType(str(mytemp))
    pywintypes.error: (193, 'GetBinaryType', '%1 is not a valid Win32 application.')
    

Upvotes: 0

thesis
thesis

Reputation: 11

I was able to use Martin B's answer successfully in a Python 3.5 program after making this adjustment:

s=f.read(2).decode(encoding="utf-8", errors="strict")

Originally it worked just fine with my program in Python 2.7, but after making other necessary changes, I discovered I was getting b'MZ', and decoding it appears to fix this.

Upvotes: 1

Jason R. Coombs
Jason R. Coombs

Reputation: 42694

If you're running Python 2.5 or later on Windows, you could also use the Windows API without pywin32 by using ctypes.

from ctypes import windll, POINTER
from ctypes.wintypes import LPWSTR, DWORD, BOOL

SCS_32BIT_BINARY = 0 # A 32-bit Windows-based application
SCS_64BIT_BINARY = 6 # A 64-bit Windows-based application
SCS_DOS_BINARY = 1 # An MS-DOS-based application
SCS_OS216_BINARY = 5 # A 16-bit OS/2-based application
SCS_PIF_BINARY = 3 # A PIF file that executes an MS-DOS-based application
SCS_POSIX_BINARY = 4 # A POSIX-based application
SCS_WOW_BINARY = 2 # A 16-bit Windows-based application

_GetBinaryType = windll.kernel32.GetBinaryTypeW
_GetBinaryType.argtypes = (LPWSTR, POINTER(DWORD))
_GetBinaryType.restype = BOOL

def GetBinaryType(filepath):
    res = DWORD()
    handle_nonzero_success(_GetBinaryType(filepath, res))
    return res

Then use GetBinaryType just like you would with win32file.GetBinaryType.

Note, you would have to implement handle_nonzero_success, which basically throws an exception if the return value is 0.

Upvotes: 5

Martin B
Martin B

Reputation: 24170

The Windows API for this is GetBinaryType. You can call this from Python using pywin32:

import win32file
type=GetBinaryType("myfile.exe")
if type==win32file.SCS_32BIT_BINARY:
    print "32 bit"
# And so on

If you want to do this without pywin32, you'll have to read the PE header yourself. Here's an example in C#, and here's a quick port to Python:

import struct

IMAGE_FILE_MACHINE_I386=332
IMAGE_FILE_MACHINE_IA64=512
IMAGE_FILE_MACHINE_AMD64=34404

f=open("c:\windows\explorer.exe", "rb")

s=f.read(2)
if s!="MZ":
    print "Not an EXE file"
else:
    f.seek(60)
    s=f.read(4)
    header_offset=struct.unpack("<L", s)[0]
    f.seek(header_offset+4)
    s=f.read(2)
    machine=struct.unpack("<H", s)[0]

    if machine==IMAGE_FILE_MACHINE_I386:
        print "IA-32 (32-bit x86)"
    elif machine==IMAGE_FILE_MACHINE_IA64:
        print "IA-64 (Itanium)"
    elif machine==IMAGE_FILE_MACHINE_AMD64:
        print "AMD64 (64-bit x86)"
    else:
        print "Unknown architecture"

f.close()

Upvotes: 24

Related Questions