News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Check for valid PE file

Started by donkey, December 27, 2004, 05:33:22 PM

Previous topic - Next topic

donkey

I recently wanted to reliably determine if a given file was a valid PE file and this is what I came up with, it is in GoAsm syntax. It will return 0 if the file is a valid PE. If not it returns -1 and more information can be extracted using GetLastError.

IsPEFile FRAME pFilename
uses edi
LOCAL hPEFile :D
LOCAL hPEMapFile :D
LOCAL pPEMapFile :D
LOCAL fSize :D
LOCAL cbHigh :D

; Checks the file for indications that it is a valid Win32 PE file
; Returns 0 if the file is a valid Win32 executable
; -1 if there is an error, use GetLastError to obtain more information

invoke SetLastError,0
invoke CreateFileA,[pFilename],GENERIC_READ,FILE_SHARE_READ, \
NULL,OPEN_EXISTING,NULL,NULL
or eax,eax
jns >
ret
:
mov [hPEFile],eax

invoke GetFileSize,eax,offset cbHigh
mov [fSize],eax

invoke CreateFileMappingA,[hPEFile],0,PAGE_READONLY,0,0,0
mov [hPEMapFile],eax
or eax,eax
jnz >
invoke CloseHandle,[hPEFile]
xor eax,eax
dec eax
ret
:

invoke MapViewOfFile,[hPEMapFile],FILE_MAP_READ,0,0,0
mov [pPEMapFile],eax
or eax,eax
jnz >
invoke CloseHandle,[hPEMapFile]
invoke CloseHandle,[hPEFile]
xor eax,eax
dec eax
ret
:

mov edi,eax

cmp W[edi+IMAGE_DOS_HEADER.e_magic],"MZ"
jne >.INVALID_PE

mov edi,[edi+IMAGE_DOS_HEADER.e_lfanew]
add edi,[pPEMapFile]
jc >.INVALID_PE

; Get the size of the mapped file
mov eax,[pPEMapFile]
add eax,[fSize]
cmp edi, eax
ja >.INVALID_PE

cmp D[edi+IMAGE_NT_HEADERS.Signature],"PE"
jne >.INVALID_PE

invoke UnmapViewOfFile,[pPEMapFile]
invoke CloseHandle,[hPEMapFile]
invoke CloseHandle,[hPEFile]
xor eax,eax
RET

.INVALID_PE
invoke UnmapViewOfFile,[pPEMapFile]
invoke CloseHandle,[hPEMapFile]
invoke CloseHandle,[hPEFile]
invoke SetLastError,193 ; %1 is not a valid Win32 application. ERROR_BAD_EXE_FORMAT
xor eax,eax
dec eax
ret
ENDF


In case you don't have all the defs, here they are...
#Define FILE_MAP_READ 4h
#Define GENERIC_READ 080000000h
#Define FILE_SHARE_READ 1h
#Define OPEN_EXISTING 3
#Define PAGE_READONLY 2
#Define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

IMAGE_DOS_HEADER STRUCT
  e_magic           DW
  e_cblp            DW
  e_cp              DW
  e_crlc            DW
  e_cparhdr         DW
  e_minalloc        DW
  e_maxalloc        DW
  e_ss              DW
  e_sp              DW
  e_csum            DW
  e_ip              DW
  e_cs              DW
  e_lfarlc          DW
  e_ovno            DW
  e_res             DW   4 dup 0
  e_oemid           DW
  e_oeminfo         DW
  e_res2            DW  10 dup 0
  e_lfanew          DD
IMAGE_DOS_HEADER ENDS

IMAGE_OPTIONAL_HEADER32 STRUCT
  Magic                         DW
  MajorLinkerVersion            DB
  MinorLinkerVersion            DB
  SizeOfCode                    DD
  SizeOfInitializedData         DD
  SizeOfUninitializedData       DD
  AddressOfEntryPoint           DD
  BaseOfCode                    DD
  BaseOfData                    DD
  ImageBase                     DD
  SectionAlignment              DD
  FileAlignment                 DD
  MajorOperatingSystemVersion   DW
  MinorOperatingSystemVersion   DW
  MajorImageVersion             DW
  MinorImageVersion             DW
  MajorSubsystemVersion         DW
  MinorSubsystemVersion         DW
  Win32VersionValue             DD
  SizeOfImage                   DD
  SizeOfHeaders                 DD
  CheckSum                      DD
  Subsystem                     DW
  DllCharacteristics            DW
  SizeOfStackReserve            DD
  SizeOfStackCommit             DD
  SizeOfHeapReserve             DD
  SizeOfHeapCommit              DD
  LoaderFlags                   DD
  NumberOfRvaAndSizes           DD
  DataDirectory                 DQ IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup
IMAGE_OPTIONAL_HEADER32 ENDS

IMAGE_FILE_HEADER STRUCT
  Machine               DW
  NumberOfSections      DW
  TimeDateStamp         DD
  PointerToSymbolTable  DD
  NumberOfSymbols       DD
  SizeOfOptionalHeader  DW
  Characteristics       DW
IMAGE_FILE_HEADER ENDS


IMAGE_NT_HEADERS STRUCT
  Signature         DD
  FileHeader        IMAGE_FILE_HEADER         <>
  OptionalHeader    IMAGE_OPTIONAL_HEADER32   <>
ENDS


"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Jibz

Nice work :U.

Not that it's likely to be a problem, but you might want to check that e_lfanew is not something evil like 0xffffff00 -- you only check if the resulting pointer is above the mapped image. Also if you want to be certain it doesn't crash, you should make sure there's room for the 4 bytes you compare there.

Btw, you can use or eax,-1 instead of xor eax,eax; dec eax.

Relvinian

Quote from: Jibz on December 29, 2004, 07:37:47 PM
Btw, you can use or eax,-1 instead of xor eax,eax; dec eax.

Jibz,

Does this kind of trick work for setting it to 1 also?

or eax, 1  = 1 ????   instead of xor eax, eax, inc eax

Relvinian

rea

I not think that, normally a -1 in complement 2 will be the size byte, word, dword... but will all bits sets..

-1 = 0x11..11 Then the or will set all :).. my question in this case why not only mov eax,-1?

Jibz

Relvinian,

or only works for setting it to -1, i.e. all bits set.

rea,

You could use mov eax,-1 just the same, the only difference is size -- or eax,-1 and xor eax,eax; dec eax are 3 bytes, mov eax,-1 is 5 bytes.

hutch--

 :bg

In times past I have used a barbarian approach that worked pefectly for determining if a file was a PE file or not, scan the first 1k for the signature "PE00". I don't remember an inctance where it ever failed.

I like Donkey's code, its clear, simple and can be used for many purposes.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

donkey

#6
Quote from: Jibz on December 29, 2004, 07:37:47 PM
Nice work :U.

Not that it's likely to be a problem, but you might want to check that e_lfanew is not something evil like 0xffffff00 -- you only check if the resulting pointer is above the mapped image. Also if you want to be certain it doesn't crash, you should make sure there's room for the 4 bytes you compare there.

Btw, you can use or eax,-1 instead of xor eax,eax; dec eax.

Hi Jibz,

Thanks, I did consider negative numbers,that is the reasoning behind ja instead of jg, if a negative number is added the compare is an unsigned one. As long as the value is beyond the end of the file or there are at least 4 bytes of data there it will not fail. Since memory maps are allocated in pages, I am taking the chance that the end of the PE in not directly at a page boundary so I can read beyond it just a bit.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Jibz

#7
Quote from: donkey on December 30, 2004, 12:27:20 AM
Thanks, I did consider negative numbers,that is the reasoning behind ja instead of jg, if a negative number is added the compare is an unsigned one. As long as the value is beyond the end of the file or there are at least 4 bytes of data there it will not fail. Since memory maps are allocated in pages, I am taking the chance that the end of the PE in not directly at a page boundary so I can read beyond it just a bit.

The problem is that a value like 0xffffff00 will not result in a negative number in the comparison, it will just give you a pointer to right before your mapped area. I cannot get the attachment function to work, so I'll e-mail you an example that crashes your function.

On a side note, a valid PE file can also have a 'ZM' signature in the DOS header.

donkey

#8
Hi Jibz,

Yes, I see now, I will fix up the code when I get home from work. Thanks  :U

By the way, it seems I am not the only one who has the problem. Both Hotmail and my firewall refused to download the stub, I had to turn off my firewall in order to get it...

image removed
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Jibz

Unknown virus scanner failure virus .. I wonder if that's related to the much feared No good virus scanner programmer virus :green.

The stub program is a valid DOS 16-bit executable, so if they flag that then they probably flag other valid DOS programs as well based on their code being buggy .. nice approach ;).

donkey

Yeah, probably means exactly that  :toothy

The solution is adding a JC to check to see if the address has wrapped...

mov edi,[edi+IMAGE_DOS_HEADER.e_lfanew]
add edi,[pPEMapFile]
jc >.INVALID_PE
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

donkey

Since this bug was in Files.LIB I have updated that library on my website with the jump if carry (jc)
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

sheep

SetLastError is a pretty simple API, you could avoid the call overhead by inlining it:


mov eax,fs:[18h]
mov DWORD PTR [eax+34h],193

Jibz

sheep,

I think that would only work on NT+.

It's better to use standard methods in a library function like that. And since there are plenty of other API calls in that function I think you'd gain little except for possible incompatibilities with future or past windows versions :U.

donkey

#14
Quote from: sheep on December 30, 2004, 07:50:42 PM
SetLastError is a pretty simple API, you could avoid the call overhead by inlining it:


mov eax,fs:[18h]
mov DWORD PTR [eax+34h],193


There are actually quite a few of those, I don't generally like to use them unless I am absolutely forced to, for the reasons Jibz gave, they may be changed in future versions of Windows. Especially the sparsely documented TIB and PDB. I posted a few others on the Win32ASM board a while back...

http://board.win32asmcommunity.net/viewtopic.php?t=18797

BTW, that is for NT+ only, for Win9x it's...

fs mov eax, [18h]
mov eax, [eax+60h]
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable