How To: determine if a file was compiled for 32 or 64 bits

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

How To: determine if a file was compiled for 32 or 64 bits

#1 Post by aGerman » 10 Aug 2014 13:57

I slightly changed an example that I originally posted in another thread.
The script is able to determine the target CPU of a Portable Executable file. You will find the CPU identifiers in the msdn specification. Basically the ID for 32 bit files is 0x014C and for 64 bit files is 0x8664.
If you pass certain system files (like explorer.exe) to the sub routine you can also draw conclusions if you run a 32 or 64 bit OS.

I added some comments that should explain how it works. Anyway if you have questions just ask me ...

Code: Select all

@echo off &setlocal
echo file           errorlevel    CPU ID
echo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

call :getPETarget "%SystemRoot%\explorer.exe"
echo explorer.exe       %errorlevel%        %=ExitCode%

call :getPETarget "%SystemRoot%\system32\chcp.com"
echo chcp.com           %errorlevel%        %=ExitCode%

call :getPETarget "%SystemRoot%\system32\vbscript.dll"
echo vbscript.dll       %errorlevel%        %=ExitCode%

call :getPETarget "nothing"
echo nothing            %errorlevel%        %=ExitCode%

call :getPETarget "test1.txt"
echo test1.txt          %errorlevel%        %=ExitCode%

call :getPETarget "test2.txt"
echo test2.txt          %errorlevel%        %=ExitCode%

call :getPETarget "test3.txt"
echo test3.txt          %errorlevel%        %=ExitCode%

echo(
pause
goto :eof


:getPETarget FilePath
:: ~~~~~~~~~~~~~~~~~~~~~~
:: Errorlevel
::   0 Success
::   1 File Not Found
::   2 Wrong Magic Number
::   3 Out Of Scope
::   4 No PE File
:: ~~~~~~~~~~~~~~~~~~~~~~
:: =ExitCode
::   CPU identifier

setlocal DisableDelayedExpansion
set "File=%~1"
set Cmp="%temp%\%random%.%random%.1KB"
set Dmp="%temp%\%random%.%random%.dmp"

REM write 1024 times 'A' into a temporary file
if exist "%File%" (
  >%Cmp% (
    for /l %%i in (1 1 32) do <nul set /p "=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
  )
  setlocal EnableDelayedExpansion
) else (endlocal &cmd /c exit 0 &exit /b 1)

REM generate a HEX dump of the executable file (first 1024 Bytes)
set "X=1"
>!Dmp! (
  for /f "skip=1 tokens=1,2 delims=: " %%i in ('fc /b "!File!" !Cmp!^|findstr /vbi "FC:"') do (
    set /a "Y=0x%%i"
    for /l %%k in (!X! 1 !Y!) do echo 41
    set /a "X=Y+2"
    echo %%j
  )
)
del !Cmp!

REM read certain values out of the HEX dump
set "err="
<!Dmp! (
  set /p "A="
  set /p "B="
  REM magic number has to be "MZ"
  if "!A!!B!" neq "4D5A" (set "err=2") else (
    REM skip next 58 bytes
    for /l %%i in (3 1 60) do set /p "="
    REM bytes 61-64 contain the offset to the PE header in little endian order
    set /p "C="
    set /p "D="
    set /p "E="
    set /p "F="
    REM check if the beginning of the PE header is part of the HEX dump
    if 0x!F!!E!!D!!C! lss 1 (set "err=3") else (
      if 0x!F!!E!!D!!C! gtr 1018 (set "err=3") else (
        REM skip the offset to the PE header
        for /l %%i in (65 1 0x!F!!E!!D!!C!) do set /p "="
        REM next 4 bytes have to contain the signature of the PE header
        set /p "G="
        set /p "H="
        set /p "I="
        set /p "J="
        REM next 2 bytes contain the CPU identifier in little endian order
        set /p "K="
        set /p "L="
      )
    )
  )
)
del !Dmp!
if defined err (endlocal &endlocal &cmd /c exit 0 &exit /b %err%)

REM was the signature ("PE\0\0") of the PE header found
if "%G%%H%%I%%J%"=="50450000" (
  REM calculate the decimal value of the CPU identifier
  set /a "CPUID=0x%L%%K%"
) else (endlocal &endlocal &cmd /c exit 0 &exit /b 4)
endlocal &endlocal &cmd /c exit %CPUID% &exit /b 0

I used some example files.
"nothing" does not exist.

"test1.txt"

Code: Select all

test*test*test

"test2.txt"

Code: Select all

MZ0000000000000000000000000000000000000000000000000000000000!!!!

"test3.txt"

Code: Select all

MZ0000000000000000000000000000000000000000000000000000000000!

... a single line each without line break.

The output under my Win7 x86:

Code: Select all

file           errorlevel    CPU ID
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
explorer.exe       0        0000014C
chcp.com           0        0000014C
vbscript.dll       0        0000014C
nothing            1        00000000
test1.txt          2        00000000
test2.txt          3        00000000
test3.txt          4        00000000

Drücken Sie eine beliebige Taste . . .


Maybe somebody finds it useful :)

Regards
aGerman

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: How To: determine if a file was compiled for 32 or 64 bi

#2 Post by Squashman » 10 Aug 2014 19:30

I do! This is cool stuff. Makes me want to revisit my Linux bash script that found executable files in the students home directories and rewrite it in Windows batch.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: How To: determine if a file was compiled for 32 or 64 bi

#3 Post by aGerman » 11 Aug 2014 15:21

I actually thought it's not of so much interest unless you would use it to determine the OS architecture where it is pretty safe and also downward compatible.

I'm glad at least you have a use for it :wink:

Post Reply