Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Read header to detect executable filetype(EXE/SYS/DLL/VXD), 32 v 64bit, GUI v Console

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin/PBCC Read header to detect executable filetype(EXE/SYS/DLL/VXD), 32 v 64bit, GUI v Console

    Here i present my function GetExeType(), which provides a stronger alternative to kernel32!GetBinaryType() (a function that is seriously lacking!)

    Why? The kernel32!GetBinaryType() function basically allows you to check what type of binary a supposed executable file might be ... ie. DOS EXE, Windows EXE, etc ... BUT not very well!!!

    I was quite surprised and disappointed to find out how sadly lacking this API function actually is ... I'm not even sure what the point of its existence is, given this range of flaws:
    - it doesn't support OS's prior to XP
    - it doesn't support .DLLs (MSDN: "If the file is a DLL, the last error code is ERROR_BAD_EXE_FORMAT")
    - it doesn't support .EXE/.SYS differentiation (it views an .EXE and a .SYS as the same type) - likewise with Win9x .VXD
    - it has very limited detection in regards to whether it's 16 or 32 or 64bit
    - it doesn't support non-PE formats such as LE, NE
    - it doesn't support differentiation of different NE formats such as EXE, DLL, DRV
    - it doesn't support other Windows subsystems such as OS/2 or POSIX
    - it doesn't let you know if the exe is GUI, Console, driver or otherwise
    - it maps the entire file into memory to do this simple check, even though the DOS & NT headers at the start of the file only occupy 64 & 248 bytes respectively.
    ... and all these issues, when the DOS and PE/extended headers are so trivial to read. *scratches head*

    Anyway, hopefully i've addressed ALL of these issues!

    NOTE - neither my code or GetBinaryType() detects .COM files (nor should they - they're not MZ-based executables ... they don't even have a header!) - such files will be returned as = 0 from GetExeType(). Likewise, other executable types such as Linux ELF executables aren't detected (also returned as 0) - we're only concerned about DOS/Windows executables. This is expected behavior.

    Code:
    #COMPILE EXE
    #INCLUDE "win32api.inc"
    
    %IMAGE_FILE_MACHINE_AMD64 = &h08664  '// not defined in earlier releases of win32api.inc
    
    %EXETYPE_DOSMZ16        = 1
    %EXETYPE_WINNEDLL16     = 2
    %EXETYPE_WINNEDRV16     = 3
    %EXETYPE_WINNEEXE16     = 4
    %EXETYPE_WINEXEGUI32    = 5
    %EXETYPE_WINEXECON32    = 6
    %EXETYPE_WINDLL32       = 7
    %EXETYPE_WINSYS32       = 8
    %EXETYPE_WINPE32        = 9
    %EXETYPE_WINVXD         = 10
    %EXETYPE_WINEXEGUI64    = 11
    %EXETYPE_WINEXECON64    = 12
    %EXETYPE_WINDLL64       = 13
    %EXETYPE_WINSYS64       = 14
    %EXETYPE_WINPE64        = 15
    %EXETYPE_POSIXCON       = 16
    %EXETYPE_OS2CON         = 17
    %EXETYPE_PEMACHINE      = 18
     
     
    FUNCTION GetExeType (szFile AS ASCIIZ * %MAX_PATH) AS DWORD
     LOCAL sBuf AS STRING, hFile AS DWORD, DOSHdr AS IMAGE_DOS_HEADER PTR, NTHdr AS IMAGE_NT_HEADERS PTR
     hFile = FREEFILE
     ERRCLEAR
     OPEN szFile FOR BINARY ACCESS READ LOCK SHARED AS #hFile
      IF ERR THEN EXIT FUNCTION                                                   '// Error - couldn't open file
      IF LOF(#hFile) <= SIZEOF(IMAGE_DOS_HEADER) THEN CLOSE #hFile: EXIT FUNCTION '// Error - file isnt large enough to even contain DOShdr
      GET$ #hFile, 4096, sBuf                                                     '// Read up to 4096 bytes (LOF if file is smaller) - easily enough for both headers
     CLOSE #hFile
     DOSHdr = STRPTR(sBuf)
     IF @DOSHdr.e_magic <> %IMAGE_DOS_SIGNATURE THEN EXIT FUNCTION
     FUNCTION = %EXETYPE_DOSMZ16                                                  '// It at least has a valid header + valid size
     IF @DOSHdr.e_lfanew = 0 OR @DOSHdr.e_lfanew => (LEN(sBuf) - 1) OR LEN(sBuf) < (SIZEOF(IMAGE_DOS_HEADER) + SIZEOF(IMAGE_NT_HEADERS)) THEN FUNCTION = 1: EXIT FUNCTION
     NTHdr = DOSHdr + @DOSHdr.e_lfanew
     'STDOUT " NThdr.Subsystem = 0x" & HEX$(@NTHdr.OptionalHeader.Subsystem)      '// Subsystem: ie. GUI (graphical), CUI (console), Native (driver)
     'STDOUT " NThdr.Machine   = 0x" & HEX$(@NTHdr.FileHeader.Machine)            '// CPU: ie. x86 (32-bit), x64 (64-bit)
     'STDOUT " NThdr.Signature = 0x" & HEX$(@NTHdr.Signature)                     '// Extended header format: ie. PE, LE, NE
     SELECT CASE LOWRD(@NTHdr.Signature)
    
       CASE %IMAGE_NT_SIGNATURE:                                                           '// PE
          SELECT CASE @NTHdr.FileHeader.Machine
    
             CASE %IMAGE_FILE_MACHINE_I386:                                                '// PE, 32bit
                 IF (@NTHdr.FileHeader.Characteristics AND %IMAGE_FILE_DLL) THEN
                     FUNCTION = %EXETYPE_WINDLL32
                 ELSE
                     SELECT CASE @NTHdr.OptionalHeader.Subsystem
                       CASE %IMAGE_SUBSYSTEM_NATIVE:      FUNCTION = %EXETYPE_WINSYS32
                       CASE %IMAGE_SUBSYSTEM_WINDOWS_GUI: FUNCTION = %EXETYPE_WINEXEGUI32
                       CASE %IMAGE_SUBSYSTEM_WINDOWS_CUI: FUNCTION = %EXETYPE_WINEXECON32
                       CASE %IMAGE_SUBSYSTEM_POSIX_CUI:   FUNCTION = %EXETYPE_POSIXCON
                       CASE %IMAGE_SUBSYSTEM_OS2_CUI:     FUNCTION = %EXETYPE_OS2CON
                       CASE ELSE:                         FUNCTION = %EXETYPE_WINPE32
                     END SELECT
                 END IF
    
             CASE %IMAGE_FILE_MACHINE_IA64, %IMAGE_FILE_MACHINE_AMD64:                     '// PE, 64bit
                 IF (@NTHdr.FileHeader.Characteristics AND %IMAGE_FILE_DLL) THEN
                                                          FUNCTION = %EXETYPE_WINDLL64
                 ELSE
                     SELECT CASE @NTHdr.OptionalHeader.Subsystem
                       CASE %IMAGE_SUBSYSTEM_NATIVE:      FUNCTION = %EXETYPE_WINSYS64
                       CASE %IMAGE_SUBSYSTEM_WINDOWS_GUI: FUNCTION = %EXETYPE_WINEXEGUI64
                       CASE %IMAGE_SUBSYSTEM_WINDOWS_CUI: FUNCTION = %EXETYPE_WINEXECON64
                       CASE ELSE:                         FUNCTION = %EXETYPE_WINPE64
                     END SELECT
                 END IF
             CASE ELSE:
                                                          FUNCTION = %EXETYPE_PEMACHINE    '// PE, but neither x86 or x64
          END SELECT
    
       CASE %IMAGE_OS2_SIGNATURE:                                                          '// NE
           LOCAL bPtr AS BYTE PTR
           bPtr = NTHdr + &h0D
           IF BIT(@bPtr, 7) = 1 THEN
               bPtr = NTHdr + &h0C
               IF BIT(@bPtr, 3) = 1 THEN FUNCTION = %EXETYPE_WINNEDRV16 ELSE FUNCTION = %EXETYPE_WINNEDLL16
           ELSE
               FUNCTION = %EXETYPE_WINNEEXE16
           END IF
    
       CASE %IMAGE_VXD_SIGNATURE:                                                          '// LE
           FUNCTION = %EXETYPE_WINVXD
    
     END SELECT
    END FUNCTION
     
     
     
    '####### DEMO USAGE ...
     
    FUNCTION GetExeTypeName (BYVAL dwExeType AS DWORD) AS STRING
     SELECT CASE dwExeType
      CASE %EXETYPE_DOSMZ16:     FUNCTION = "DOS MZ .EXE program, 16bit"
      CASE %EXETYPE_WINNEDLL16:  FUNCTION = "Win NE .DLL library, 16bit"
      CASE %EXETYPE_WINNEDRV16:  FUNCTION = "Win NE .DRV driver, 16bit"
      CASE %EXETYPE_WINNEEXE16:  FUNCTION = "Win NE .EXE program, 16bit"
      CASE %EXETYPE_WINVXD:      FUNCTION = "Win LE .VXD driver, 32bit"
      CASE %EXETYPE_WINDLL32:    FUNCTION = "Win PE .DLL library, 32bit"
      CASE %EXETYPE_WINEXEGUI32: FUNCTION = "Win PE .EXE program, 32bit (GUI)"
      CASE %EXETYPE_WINEXECON32: FUNCTION = "Win PE .EXE program, 32bit (Console)"
      CASE %EXETYPE_WINSYS32:    FUNCTION = "Win PE .SYS driver, 32bit"
      CASE %EXETYPE_WINPE32:     FUNCTION = "Win PE executable, 32bit (other or unknown subsystem)"
      CASE %EXETYPE_WINDLL64:    FUNCTION = "Win PE .DLL library, 64bit"
      CASE %EXETYPE_WINEXEGUI64: FUNCTION = "Win PE .EXE program, 64bit (GUI)"
      CASE %EXETYPE_WINEXECON64: FUNCTION = "Win PE .EXE program, 64bit (Console)"
      CASE %EXETYPE_WINSYS64:    FUNCTION = "Win PE .SYS driver, 64bit"
      CASE %EXETYPE_WINPE64:     FUNCTION = "Win PE executable, 64bit (other or unknown subsystem)"
      CASE %EXETYPE_POSIXCON:    FUNCTION = "POSIX executable program (Console)"
      CASE %EXETYPE_OS2CON:      FUNCTION = "OS/2 executable program (Console)"
      CASE %EXETYPE_PEMACHINE:   FUNCTION = "Win PE executable, other CPU (not x86 or x64)"
      CASE ELSE:                 FUNCTION = "Unknown, error, or not an executable"
     END SELECT
    END FUNCTION
    
    
    SUB ShowExeType (szFile AS ASCIIZ)
     STDOUT "FILE: " & szFile & $CRLF & _
            "TYPE: " & GetExeTypeName(BYVAL GetExeType (szFile)) & $CRLF
    END SUB
    
    
    FUNCTION WinDir() AS STRING '// just used in this demo as \Windows\ has a Smörgåsbord of executable flavors
     STATIC szWinDir AS ASCIIZ * %MAX_PATH, lInit AS LONG
     IF lInit = 0 THEN GetWindowsDirectory(szWindir, BYVAL %MAX_PATH): lInit = 1
     FUNCTION = szWinDir
    END FUNCTION
     
    
    FUNCTION PBMAIN() AS LONG
     ShowExeType WinDir & "\winhelp.exe"
     ShowExeType WinDir & "\twain.dll"
     ShowExeType WinDir & "\twain_32.dll"
     ShowExeType WinDir & "\twunk_16.exe"
     ShowExeType WinDir & "\twunk_32.exe"
     ShowExeType WinDir & "\system32\xcopy.exe"
     ShowExeType WinDir & "\system32\keyboard.drv"
     ShowExeType WinDir & "\system32\drivers\videoprt.sys"
     WAITKEY$
    END FUNCTION
    Last edited by Wayne Diamond; 24 Jul 2014, 07:15 AM. Reason: Corrected %IMAGE_FILE_MACHINE_AMD64, added DLLvsEXEvsDRV distinction of 16bit NE files, and OS/2 & POSIX subsystem detection
    -

  • #2
    Hi Wayne,

    Nice code, thank.

    If I may, under some OS like Se7en/64 you have to disable Wow64 redirection to get valid result for certain files.

    Have a look at Files or Dlls, are they 16bit, 32bit, 64bit?

    Next is what I got with and without redirection for videoprt.sys under Se7en/64...

    Pierre
    Code:
    --------------------------- 
    *** Wow64 redirection enabled ***
    Bit:      0 bit
    File:     C:\Windows\System32\drivers\videoprt.sys
    Header:   Unable to open C:\Windows\System32\drivers\videoprt.sys!
    Size:     0 bytes
    C:\Windows\SysWOW64) (In reality we are in 32 bit folder
    --------------------------- 
    *** Wow64 redirection disabled ***
    Bit:    64 bit
    File:   C:\Windows\System32\drivers\videoprt.sys
    Header: IMAGE_FILE_MACHINE_AMD64
    Size:   129,024 bytes
    ---------------------------
    Kernel32 - 32 and 64 bit under Se7en 64 bit
    Code:
    ---------------------------
    SysNative folder
    ---------------------------
    *** Wow64 redirection enabled ***
    System32 folder
    Bit:    32 bit
    File:   C:\Windows\system32\Kernel32.dll
    Header: IMAGE_FILE_MACHINE_I386
    Size:   1,114,112 bytes
    ---------------------------
    *** Wow64 redirection disabled ***
    SysNative folder (A special alias)
    Bits:   64 bit
    File:   C:\Windows\SysNative\Kernel32.dll
    Header: IMAGE_FILE_MACHINE_AMD64
    Size:   1,163,264 bytes
    ---------------------------
    Last edited by Pierre Bellisle; 22 Jul 2014, 10:13 PM.

    Comment

    Working...
    X