Inside the executable: a VB programmer's guide to the portable executable format
Introduction
The Portable Executable Format is the data structure that describes how the various parts of a Win32
executable file are held together. It allows the operating system to load the executable and to locate the dynamically
linked libraries required to run that executable and to navigate the code,data and resource sections compiled into that
executable. Getting over DOS
The PE Format was created for Windows but Microsoft had to make sure that running such an executable in DOS would
yield a meaningful error message and exit. To this end the very first bit of a windows executable file is actually a DOS
executable (sometimes known as the stub) which writes "This program requires Windows" or similar then exits.
The format of the DOS stub is:
VB Code:
Private Type IMAGE_DOS_HEADER
e_magic As Integer ''\\ Magic number
e_cblp As Integer ''\\ Bytes on last page of file
e_cp As Integer ''\\ Pages in file
e_crlc As Integer ''\\ Relocations
e_cparhdr As Integer ''\\ Size of header in paragraphs
e_minalloc As Integer ''\\ Minimum extra paragraphs needed
e_maxalloc As Integer ''\\ Maximum extra paragraphs needed
e_ss As Integer ''\\ Initial (relative) SS value
e_sp As Integer ''\\ Initial SP value
e_csum As Integer ''\\ Checksum
e_ip As Integer ''\\ Initial IP value
e_cs As Integer ''\\ Initial (relative) CS value
e_lfarlc As Integer ''\\ File address of relocation table
e_ovno As Integer ''\\ Overlay number
e_res(0 To 3) As Integer ''\\ Reserved words
e_oemid As Integer ''\\ OEM identifier (for e_oeminfo)
e_oeminfo As Integer ''\\ OEM information; e_oemid specific
e_res2(0 To 9) As Integer ''\\ Reserved words
e_lfanew As Long ''\\ File address of new exe header
End Type
The only field of this structure that is of interest to Windows is e_lfanew which is the file pointer to the new
Windows executable header. To skip over the DOS part of the program, set the file pointer to the value held in this field:
The NT header
The NT header holds the information needed by the windows program loader to load the program. It consists of the PE File signature
followed by an IMAGE_FILE_HEADER and IMAGE_OPTIONAL_HEADER records.
For applications designed to run under Windows (i.e. not OS/2 or VxD files) the four bytes of the PE File signature should equal &h4550.
The other defined signatures are:
VB Code:
Public Enum ImageSignatureTypes
IMAGE_DOS_SIGNATURE = &H5A4D ''\\ MZ
IMAGE_OS2_SIGNATURE = &H454E ''\\ NE
IMAGE_OS2_SIGNATURE_LE = &H454C ''\\ LE
IMAGE_VXD_SIGNATURE = &H454C ''\\ LE
IMAGE_NT_SIGNATURE = &H4550 ''\\ PE00
End Enum
Following the PE file signature is the IMAGE_NT_HEADERS structure that stores information about the target environment of the executable.
The structure is:
VB Code:
Private Type IMAGE_FILE_HEADER
Machine As Integer
NumberOfSections As Integer
TimeDateStamp As Long
PointerToSymbolTable As Long
NumberOfSymbols As Long
SizeOfOptionalHeader As Integer
Characteristics As Integer
End Type
The Machine member describes what target CPU the executable was compiled for. It can be one of:
IMAGE_FILE_MACHINE_ARM = &H1C0 ''\\ ARM Little-Endian
IMAGE_FILE_MACHINE_IA64 = &b00 ''\\ Intel 64
End Enum
The SizeOfOptionalHeader member indicates the size (in bytes) of the IMAGE_OPTIONAL_HEADER structure that immediatley follows it.
In practice this structure is not optional so that is a bit of a misnomer. This structure is defined as:
VB Code:
Private Type IMAGE_OPTIONAL_HEADER
Magic As Integer
MajorLinkerVersion As Byte
MinorLinkerVersion As Byte
SizeOfCode As Long
SizeOfInitializedData As Long
SizeOfUninitializedData As Long
AddressOfEntryPoint As Long
BaseOfCode As Long
BaseOfData As Long
End Type
and this in turn is immediately followed by the IMAGE_OPTIONAL_HEADER_NT structure:
VB Code:
Private Type IMAGE_OPTIONAL_HEADER_NT
ImageBase As Long
SectionAlignment As Long
FileAlignment As Long
MajorOperatingSystemVersion As Integer
MinorOperatingSystemVersion As Integer
MajorImageVersion As Integer
MinorImageVersion As Integer
MajorSubsystemVersion As Integer
MinorSubsystemVersion As Integer
Win32VersionValue As Long
SizeOfImage As Long
SizeOfHeaders As Long
CheckSum As Long
Subsystem As Integer
DllCharacteristics As Integer
SizeOfStackReserve As Long
SizeOfStackCommit As Long
SizeOfHeapReserve As Long
SizeOfHeapCommit As Long
LoaderFlags As Long
NumberOfRvaAndSizes As Long
DataDirectory(0 To 15) As IMAGE_DATA_DIRECTORY
End Type
The most useful field of this structure (to my purposes, anyhow) are the 16 IMAGE_DATA_DIRECTORY entries. These describe whereabouts
(if at all) the particular sections of the executable are located. The structure is defined thus:
Note that is an executable does not contain one of the sections (as is often the case) there will be an IMAGE_DATA_DIRECTORY for it but the
address and size will both be zero. The image data directories
The image data directories...continued The exports directory
The exports directory holds details of the functions exported by this executable. For example, if you were to look in the exports directory
of the MSVBVM50.dll it would list all the functions it exports that make up the visual basic 5 runtime environment.
This directory consists of some info to tell you how many exported functions there are followed by three parallel arrays which give you the
address, name and ordinal of the functions respectively. The structure is defined thus:
VB Code:
Private Type IMAGE_EXPORT_DIRECTORY
Characteristics As Long
TimeDateStamp As Long
MajorVersion As Integer
MinorVersion As Integer
lpName As Long
Base As Long
NumberOfFunctions As Long
NumberOfNames As Long
lpAddressOfFunctions As Long '\\ Three parrallel arrays...(LONG)
lpAddressOfNames As Long '\\ (LONG)
lpAddressOfNameOrdinals As Long '\\ (INTEGER)
End Type
And you can read this info from the executable thus:
VB Code:
Private Sub ProcessExportTable(ExportDirectory As IMAGE_DATA_DIRECTORY)
Dim deThis As IMAGE_EXPORT_DIRECTORY
Dim lBytesWritten As Long
Dim lpAddress As Long
Dim nFunction As Long
If ExportDirectory.VirtualAddress > 0 And ExportDirectory.Size > 0 Then
The imports directory
The imports directory lists the dynamic link libraries that this executable depends on and which functions it imports from that dynamic link library.
It consists of an array of IMAGE_IMPORT_DESCRIPTOR structures terminated by an instance of this structure where the lpName parameter is zero.
The structure is defined as:
VB Code:
Private Type IMAGE_IMPORT_DESCRIPTOR
lpImportByName As Long ''\\ 0 for terminating null import descriptor
TimeDateStamp As Long ''\\ 0 if not bound,
''\\ -1 if bound, and real date\time stamp
''\\ in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
''\\ O.W. date/time stamp of DLL bound to (Old BIND)
ForwarderChain As Long ''\\ -1 if no forwarders
lpName As Long
lpFirstThunk As Long ''\\ RVA to IAT (if bound this IAT has actual addresses)
End Type
And you can walk the import directory thus:
VB Code:
Private Sub ProcessImportTable(ImportDirectory As IMAGE_DATA_DIRECTORY)
Dim lpAddress As Long
Dim diThis As IMAGE_IMPORT_DESCRIPTOR
Dim byteswritten As Long
Dim sName As String
Dim lpNextName As Long
Dim lpNextThunk As Long
Dim lImportEntryIndex As Long
Dim nOrdinal As Integer
Dim lpFuncAddress As Long
'\\ If the image has an imports section...
If ImportDirectory.VirtualAddress > 0 And ImportDirectory.Size > 0 Then
The resource directory
The structure of the resource director is somewhat more involved. It consists of a root directory (defined by the structure IMAGE_RESOURCE_DIRECTORY immediately followed by a number of resource directory entries (defined by the structure
IMAGE_RESOURCE_DIRECTORY_ENTRY). These are defined thus:
VB Code:
Private Type IMAGE_RESOURCE_DIRECTORY
Characteristics As Long '\\Seems to be always zero?
TimeDateStamp As Long
MajorVersion As Integer
MinorVersion As Integer
NumberOfNamedEntries As Integer
NumberOfIdEntries As Integer
End Type
Private Type IMAGE_RESOURCE_DIRECTORY_ENTRY
dwName As Long
dwDataOffset As Long
CodePage As Long
Reserved As Long
End Type
Each resource directory entry can either point to the actual resource data or to another layer of resource directory entries. If the highest bit of dwDataOffset is set then this points to a directory otherwise it points to the resource data.
How is this information useful?
Once you know how an executable is put together you can use this information to peer into its workings. You can view the resources compiled into
it, the dlls it depends on and the actual functions it imports from them. More importantly you can attach to the executable as a debugger and
track down any of those really troublesome general protection faults. The next article will describe how to attach a debugger and use the PE file
format.
I know its a bit dated - this thread - but I could sure do with some help
Note
It's no virus, as in it's not malicious. All users will be aware of whats going on, though not how it's carried out. Hey, thats where I get my bread and butter from
The Prob.
All commericial and hacker tooled (and the crappy one I coded in VB) EXE Joiners rely solely on the System Temp or some other Hard Disk dump area, to de-bundle and execute the joined executables from that directory. No can do for my App, as its run off a CD-ROM with extensive "App.Path" dependencies.
The Q.
Would it be possible to crack open an EXE with what you have listed above and insert another arbitary EXE which is to be executed when the EXE is launched? If so, could you point me to how to get it done. If not, thanx for taking the time to answer.
Regards
Kaushik Janardhanan
"Brothers, you asked for it."
...Francisco Domingo Carlos Andres Sebastian D'Anconia
Introduction
[P]Windows NT (and successive operating system versions) expose a set of API functions and structures for debugging a running process. This article shows how these can be accessed from Visual Basic (version 5 or 6). It is recomended that the article [a href="http://www.thecodeproject.com/win32/vbdebugger.asp"]Inside the executable: an introduction to the Portable Executable format for VB programmers[/a] be read in conjunction with this article, and the source code attached to it has the code for this article included. [/P]
Getting a process to debug
[P]There are two ways to get a process to debug. Either (a) attach a debugger to a process that is already running or (b) start a new process with a debugger attached to it.[/P]
Starting a new process with a debugger attached
[P]To start a process you can use the CreateProcess API call:[/P]
VB Code:
Private Declare Function CreateProcess Lib _
"kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, _
ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, _
ByVal dwCreationFlags As ProcessCreationFlags, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
[P]In addition to the functionality of the Shell command this allows you to specify additional flags that affect how the process is created:[/p]
VB Code:
Public Enum ProcessCreationFlags
DEBUG_PROCESS = &H1
DEBUG_ONLY_THIS_PROCESS = &H2
CREATE_SUSPENDED = &H4
DETACHED_PROCESS = &H8
CREATE_NEW_CONSOLE = &H10
NORMAL_PRIORITY_CLASS = &H20
IDLE_PRIORITY_CLASS = &H40
HIGH_PRIORITY_CLASS = &H80
REALTIME_PRIORITY_CLASS = &H100
CREATE_NEW_PROCESS_GROUP = &H200
CREATE_UNICODE_ENVIRONMENT = &H400
CREATE_SEPARATE_WOW_VDM = &H800
CREATE_SHARED_WOW_VDM = &H1000
CREATE_FORCEDOS = &H2000
CREATE_DEFAULT_ERROR_MODE = &H4000000
CREATE_NO_WINDOW = &H8000000
End Enum
[P]In order to start the process with an attached debugger you specify the flags DEBUG_PROCESS + DEBUG_ONLY_THIS_PROCESS
[/P]
Attaching a debugger to an existing process
[P]To attach a debugger to a process which is already running you need to obtain a handle to it and then attach a debugger using the DebugActiveProcess API call[/P]
VB Code:
Private Declare Function DebugActiveProcess Lib "kernel32" (ByVal dwProcessId As Long) As Long
The debug loop
[P]Once you have attached your debugger to the process you need to go into a debug loop. This consists of waiting for a debug event, processing the event when it comes in and then allowing the debugee to continue.[/P] Waiting for a debug event to occur
[P]To wait for a debug event you call the WaitForDebugEvent API call:[/P]
VB Code:
Private Declare Function WaitForDebugEvent Lib "kernel32" (lpDebugEvent As DEBUG_EVENT_BUFFER, ByVal dwMilliseconds As Long) As Long
[P]This will return TRUE when a debug event has occured and fill out the DEBUG_EVENT_... structure, which depends on what event occured but always starts with a DEBUG_EVENT_HEADER:[/P]
VB Code:
Private Type DEBUG_EVENT_HEADER
dwDebugEventCode As DebugEventTypes
dwProcessId As Long
dwThreadId As Long
End Type
Processing the debug event
[P]How you deal with a debug event depends, naturally enough, on what event occured. The event types are:[/P]
VB Code:
Public Enum DebugEventTypes
EXCEPTION_DEBUG_EVENT = 1&
CREATE_THREAD_DEBUG_EVENT = 2&
CREATE_PROCESS_DEBUG_EVENT = 3&
EXIT_THREAD_DEBUG_EVENT = 4&
EXIT_PROCESS_DEBUG_EVENT = 5&
LOAD_DLL_DEBUG_EVENT = 6&
UNLOAD_DLL_DEBUG_EVENT = 7&
OUTPUT_DEBUG_STRING_EVENT = 8&
RIP_EVENT = 9&
End Enum
[P]EXCEPTION_DEBUG_EVENT[/P]
[P]This debug event is thrown whenever an exception occurs in the application being debugged. For example if there was code in that application that was attempting to divide by zero then you would get an EXCEPTION_DEBUG_EVENT. The buffer that is passed back for this event is:[/P]
ExceptionInformation(EXCEPTION_MAXIMUM_PARAMETERS) As Long
dwFirstChance As Long
End Type
[P]The exception flags tell you if it is possible to resume from the exception or not[/P]
[P]CREATE_THREAD_DEBUG_EVENT[/P]
[P]This event occurs whenever a new thread is created by the debugee application. The buffer that is passed in is:[/P]
VB Code:
Private Type DEBUG_CREATE_THREAD_DEBUG_INFO
Header As DEBUG_EVENT_HEADER
hThread As Long
lpThreadLocalBase As Long
lpStartAddress As Long
End Type
[P]This gives you a thread handle (for thread control API calls) and the base address and start address of the thread in the debugee process which is useful for analysing the memory of that application[/P]
CREATE_PROCESS_DEBUG_EVENT
This event occurs when the process is created. The buffer passed in is:
VB Code:
Private Type DEBUG_CREATE_PROCESS_DEBUG_INFO
Header As DEBUG_EVENT_HEADER
hfile As Long
hProcess As Long
hThread As Long
lpBaseOfImage As Long
dwDebugInfoFileOffset As Long
nDebugInfoSize As Long
lpThreadLocalBase As Long
lpStartAddress As Long
lpImageName As Long
fUnicode As Integer
End Type
You can use the file handle passed in as part of this buffer to find the different parts of the process (imports section, exports, debug information etc.) as per this article
EXIT_THREAD_DEBUG_EVENT
This event occurs when a thread exits. The buffer passed in is:
VB Code:
Private Type DEBUG_EXIT_THREAD_DEBUG_INFO
Header As DEBUG_EVENT_HEADER
dwExitCode As Long
End Type
The exit code is whatever the thread set it to but is usually set to be non zero if an error caused the thread exit
EXIT_PROCESS_DEBUG_EVENT
This event occurs when the process exits. The buffer passed in is:
VB Code:
Private Type DEBUG_EXIT_PROCESS_DEBUG_INFO
Header As DEBUG_EVENT_HEADER
dwExitCode As Long
End Type
The exit code is whatever the process set it to but is usually set to be non zero if an error caused the thread exit. You should stop the debug loop after you recieve this event.
LOAD_DLL_DEBUG_EVENT
This event occurs when teh application being debugged loads a dynamic link library. The buffer passed in is:
VB Code:
Private Type DEBUG_LOAD_DLL_DEBUG_INFO
Header As DEBUG_EVENT_HEADER
hfile As Long
lpBaseOfDll As Long
dwDebugInfoFileOffset As Long
nDebugInfoSize As Long
lpImageName As Long
fUnicode As Integer
End Type
You can use the file handle passed in as part of this buffer to find the different parts of the dll (imports section, exports, debug information etc.) as per this article
UNLOAD_DLL_DEBUG_EVENT
This event occurs when the process being debugged unloads a dll it had loaded. The buffer passed in is:
VB Code:
Private Type DEBUG_UNLOAD_DLL_DEBUG_INFO
Header As DEBUG_EVENT_HEADER
lpBaseOfDll As Long
End Type
And you can use the lpBaseOfDll value to identify which dll was unloaded
OUTPUT_DEBUG_STRING_EVENT
This event occurs when the debugee calls the API call OutputDebugString to send debugging information to a debugger (where one is attached). The buffer passed in is:
VB Code:
Private Type DEBUG_OUTPUT_DEBUG_STRING_INFO
Header As DEBUG_EVENT_HEADER
lpDebugStringData As Long
fUnicode As Integer
nDebugStringLength As Integer
End Type
And you can read the string from the debugee using the ReadProcessMemory API call
RIP_EVENT
This occurs if your process being debugged dies unexpectedly. The buffer passed in is:
VB Code:
Private Type DEBUG_RIP_INFO
Header As DEBUG_EVENT_HEADER
dwError As Long
dwType As Long
End Type
Resuming the debugee
Once you have extracted the information you need form the debug event you need to resume the debugee so that it can continue running. To do this you call the ContinueDebugEvent API call:
VB Code:
Public Enum DebugStates
DBG_CONTINUE = &H10002
DBG_TERMINATE_THREAD = &H40010003
DBG_TERMINATE_PROCESS = &H40010004
DBG_CONTROL_C = &H40010005
DBG_CONTROL_BREAK = &H40010008
DBG_EXCEPTION_NOT_HANDLED = &H80010001
End Enum
Private Declare Function ContinueDebugEvent Lib "kernel32" (ByVal dwProcessId As Long, ByVal dwThreadId As Long, ByVal dwContinueStatus As DebugStates) As Long
Further development
To expand on this framework and create a full debugger requires the ability to walk the memory and stack of the process being debugged and also to set breakpoints. I hope to get to this in the next article
Re: Inside the executable: a VB programmer's guide to the portable executable format
Talk about digging up old threads.
Merrion, I saw your code for the PE_Utilities.bas posted on another website can't remember where now. I was trying to convert it over to vb2005 but it is throwing to many errors for me. I'm a hobbiest programmer.
I was wondering if you ever converted this code over to vb.net or if you knew of a way to get header info from a file to determine if it is a exe, dll, sys or whichever executable type in vb.net