Previous Windows PE Internals Writeups
- Creating a Windows Project in Visual Studio
- Getting a Handle to a Dynamically Linked Library
- Validating the MZ Signature
- Validating the PE Signature
Previously
In the previous article, we learnt about how do we validate the PE Signature
, also commonly referred to as the File Signature
, which tells us that the file is very likely, a Portable Executable (PE) file.
This signature is PE\0\0
(the letters "P" and "E" followed by two null bytes).
We have also seen how to navigate our way from the IMAGE_DOS_HEADER
into the IMAGE_NT_HEADER
We also saw that the IMAGE_NT_HEADER
comprises of 3 parts.
- PE Signature
- PE Header
- Optional Header
We will be dissecting the PE Header
in this article.
Let's Begin
Following the PE Signature, we have the PE Header.
As the PE Header
is one of the subsection of the IMAGE_NT_HEADER
, we can easily retrieve as such.
PIMAGE_FILE_HEADER imageFileHeader = &imageNtHeaders->FileHeader;
Taking a deeper look into the PE Header, we have the following sections.
In visual studio, we can see the matching fields as well.
Let's write some code to visualize these information
wsprintfA(c + strlen(c), "Number of Symbols: %d\n", imageFileHeader->NumberOfSymbols);
wsprintfA(c + strlen(c), "Pointer to Symbol Table: 0x%02X\n", imageFileHeader->PointerToSymbolTable);
wsprintfA(c + strlen(c), "Number of Sections: %hd\n", imageFileHeader->NumberOfSections);
wsprintfA(c + strlen(c), "TimeDateStamp: %d\n", imageFileHeader->TimeDateStamp);
wsprintfA(c + strlen(c), "Size of Optional Header: %hd\n", imageFileHeader->SizeOfOptionalHeader);
wsprintfA(c + strlen(c), "Machine: 0x%02X\n", imageFileHeader->Machine);
wsprintfA(c + strlen(c), "Characteristix: 0x%02X\n", imageFileHeader->Characteristics);
MessageBoxA(0, c, "PE Header", MB_OK | MB_ICONINFORMATION);
And the result is,
Number of Symbols
This is deprecated and should contain 0.
Pointer to symbol table
This is also deprecated and should contain 0.
Number of sections
Sections will be covered in another article. This field basically stores the number of sections.
TimeDateStamp
The number of seconds that has passed from epoch since the file creation.
Size of Optional Headers
The Optional Header
comes after the PE Header
. We will discuss it in other articles.
Machine
The number that identifies the type of target machine that this executable was compiled for.
Looking into winnt.h
,
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
// omitted
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
Visual studio always runs in a 32bit emulator while debugging even though I am using a 64 bit machine. Thus it shows as 0x014c
which is a 32 bit
executable.
Characteristics
We can use tools like CFF Explorer
to understand this.
As expected, all of these macros can be found in winnt.h
.
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Aggressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
The overall code would look like,
#include <Windows.h>
int WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
HMODULE peBase = GetModuleHandleA("user32.dll");
if (peBase == NULL)
{
MessageBoxA(0, "Can't load user32.dll", "Error", MB_OK | MB_ICONERROR);
return 1;
}
PIMAGE_DOS_HEADER imageDosHeader = (PIMAGE_DOS_HEADER)peBase;
if (imageDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
MessageBoxA(0, "user32.dll has the wrong Image Dos Signature!", "Error", MB_OK | MB_ICONERROR);
return 1;
}
PIMAGE_NT_HEADERS imageNtHeaders = (PIMAGE_NT_HEADERS)((unsigned char*)imageDosHeader + imageDosHeader->e_lfanew);
if (imageNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
MessageBoxA(0, "user32.dll has the wrong PE Signature!", "Error", MB_OK | MB_ICONERROR);
return 1;
}
PIMAGE_FILE_HEADER imageFileHeader = &imageNtHeaders->FileHeader;
char buffer[1024 * 20] = { 0 };
wsprintfA(buffer + strlen(buffer), "Number of Symbols: %d\n", imageFileHeader->NumberOfSymbols);
wsprintfA(buffer + strlen(buffer), "Pointer to Symbol Table: 0x%02X\n", imageFileHeader->PointerToSymbolTable);
wsprintfA(buffer + strlen(buffer), "Number of Sections: %hd\n", imageFileHeader->NumberOfSections);
wsprintfA(buffer + strlen(buffer), "TimeDateStamp: %d\n", imageFileHeader->TimeDateStamp);
wsprintfA(buffer + strlen(buffer), "Size of Optional Header: %hd\n", imageFileHeader->SizeOfOptionalHeader);
wsprintfA(buffer + strlen(buffer), "Machine: 0x%02X\n", imageFileHeader->Machine);
wsprintfA(buffer + strlen(buffer), "Characteristix: 0x%02X\n", imageFileHeader->Characteristics);
MessageBoxA(0, buffer, "PE Header", MB_OK | MB_ICONINFORMATION);
return 0;
}
In this article, we have understood the meaning behind each field in the PE Header.
Top comments (0)