DEV Community

loading...

Validating the PE Signature (My AV Flagged me) [Windows PE Internals]

wireless90
A Software Engineer trying to venture out to the Security world... Documenting my learning journey.
Updated on ・3 min read

Previous Windows PE Internals Writeups

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

image

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;
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Let's break it down.

(unsigned char*)imageDosHeader
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Anti-Virus Bleeping...

image

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.

image

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.

Next

Dissecting the PE Header

Discussion (0)