Previous Windows PE Internals Writeups
- Creating a Windows Project in Visual Studio
- Getting a Handle to a Dynamically Linked Library
- Validating the MZ Signature
Previously
Previously, we learnt about how do we validate the MZ
signature, which tells us that the file is very likely, a Portable Executable (PE) file.
or a MS DOS Executable
.
Let's Begin
Now, lets proceed to navigate to another structure within the PE called the NT_HEADERS
aka PE HEADERS
.
The NT_HEADERS
consist of 3 main subsections.
- NT Signature aka PE Signature
- File Header aka PE Header
- Optional Header
Today we will validate the NT Signature
.
Continuing with our previous article, let's relook at the PE image above and the definition of the IMAGE_DOS_HEADER
.
typedef struct _IMAGE_DOS_HEADER
{
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
LONG e_lfanew; //Use this to jump to PIMAGE_NT_HEADER
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
We can see that there is a property e_lfanew
. It contains a Relative Virtual Address (RVA)
whose offset leads us to the PIMAGE_NT_HEADER
.
e_lfanew
simply means the long file address of the new executable header
, kinda saying the IMAGE_DOS_HEADER
is old as its for MS-DOS
.
An RVA
is basically an offset(in bytes) from the Base Address
. Meaning you would only compute the destination address by taking the Base Address
, which is our start of PE Address, and add it to the RVA
to get the destination address.
Let's now compute our PIMAGE_NT_HEADER
by adding PIMAGE_DOS_HEADER
to the RVA
in e_lfanew
.
The code below shows how to do that.
PIMAGE_NT_HEADERS imageNtHeaders = (PIMAGE_NT_HEADERS)((unsigned char*)imageDosHeader + imageDosHeader->e_lfanew);
Let's break it down.
(unsigned char*)imageDosHeader
As imageDosHeader
is of a type pointer to IMAGE_DOS_HEADER
, we cant do pointer arithmetic byte-wise. Meaning we want to shift the pointer byte by byte as the RVA
is the offset in bytes.
Hence we casted it to a (unsigned char*)
which allows the pointer to move byte by byte as a char
is 1 byte
.
(unsigned char*)imageDosHeader + imageDosHeader->e_lfanew
Next we moved the pointer by imageDosHeader->e_lfanew
number of bytes.
PIMAGE_NT_HEADERS imageNtHeaders = (PIMAGE_NT_HEADERS)((unsigned char*)imageDosHeader + imageDosHeader->e_lfanew);
Then we re-casted it back to PIMAGE_NT_HEADERS
which is basically a macro for pointer to IMAGE_NT_HEADERS
aka IMAGE_NT_HEADERS*
.
Now we can easily get our signature from PIMAGE_NT_HEADERS
and compare it with the macro IMAGE_NT_SIGNATURE
.
if (imageNtHeaders->Signature == IMAGE_NT_SIGNATURE)
{
//omitted
}
IMAGE_NT_SIGNATURE
is a macro found in winnt.h
which defines the PE signature to be PE\0\0
which ends with 2 null bytes.
//From winnt.h
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
Hence overall, our code would look like the following.
#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;
}
MessageBoxA(0, "user32.dll has the right PE Signature!", "Success", MB_OK | MB_ICONINFORMATION);
return 0;
}
Anti-Virus Bleeping...
I did not expect my antivirus to catch up very quickly that what I am trying to do might be suspicious. Many malwares have similar type of function calls to perform DLL Injection
. We might discuss that in the future.
I have temporarily disabled it.
In this exercise, I learnt how to navigate from the DOS HEADER
to the PE HEADER
via the e_lfanew
property. Following that, I successfully verfied the PE Header. Interestingly, we also saw my Antivirus going off and suspecting my code to be malicious.
Top comments (0)