Exploring the Export Table [Windows PE Internals]
Tags: cybersecurity, security
Published: 2021-05-26 11:37:04

# Previous Windows PE Internals Writeups
1. [Creating a Windows Project in Visual Studio](https://dev.to/wireless90/creating-a-windows-project-in-visual-studio-windows-pe-internals-2p6e)
2. [Getting a Handle to a Dynamically Linked Library ](https://dev.to/wireless90/getmodulehandlea-windows-pe-internals-1b0l)
3. [Validating the MZ Signature](https://dev.to/wireless90/getting-the-windows-pe-internals-kka/)
4. [Validating the PE Signature](https://dev.to/wireless90/validating-the-pe-signature-my-av-flagged-me-windows-pe-internals-2m5o/)
5. [Dissecting the PE Header](https://dev.to/wireless90/dissecting-the-pe-header-windows-pe-internals-5113/)

# Previously

In the [previous article](https://dev.to/wireless90/dissecting-the-pe-header-windows-pe-internals-5113/), we learnt about the contents of the `PE Header`. 

Let's now take a look at the `Optional Header`, more particularly, the `Export Table`. 

# Let's Begin

An export table contains functions that have been exported and could be used by other programs. We will be focusing on the exported functions of the library `user32.dll`.

In the previous article, we managed to retrieve the `IMAGE_NT_HEADERS`.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z3y2hdtu29snt4t8c8ff.png)

We can see that this structure contains the `FileHeader`, `OptionalHeader` and `Signature`. 

We are interested in the `OptionalHeader`.

```cpp
PIMAGE_OPTIONAL_HEADER imageOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&imageNtHeaders->OptionalHeader;
```

If we take a look at the [PE File Format](https://upload.wikimedia.org/wikipedia/commons/1/1b/Portable_Executable_32_bit_Structure_in_SVG_fixed.svg),

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ahb7ncw4rop0ogid77t2.png)
 
we can see that the `OptionalHeader` consist of a segment called `DataDirectories`. We want to get the `ExportTable Data Directory`.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6n3zys1l324w3jcrwoip.png)
 
There is a `DataDirectory` array in the `OptionalHeader`. We can then obtain by using the `Export Table Index Macro` called `IMAGE_DIRECTORY_ENTRY_EXPORT` defined inside `winnt.h`.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0r5x1x2kx5cujey36ey8.png)

Thus to get the export table directory, we would do

```cpp
PIMAGE_DATA_DIRECTORY imageExportDataDirectory = &(imageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
``` 

Our `Export Data Directory` contains,
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/34uqfnuvlnv9a7ydbbur.png)
just as seen from 

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqdmnwf7n3v3z8fvhjz7.png).

The field `VirtualAddress` is relative to the PE Base address.

As we learnt from the previous article, the Relative Virtual Address(RVA) has to be added to the pe base address to get to the actual structure.

In order to obtain the Export Directory structure, we would thus do,

```cpp
PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)peBase + imageExportDataDirectory->VirtualAddress);
```

Now what we are interested is in the `ExportAddressTable`, `NameOrdinalsArray` and `NameAddressArray`.

They can be retrived via
```cpp
DWORD numberOfNames = imageExportDirectory->NumberOfNames;
PDWORD exportAddressTable = (PDWORD)((unsigned char*)peBase + imageExportDirectory->AddressOfFunctions);
PWORD nameOrdinalsPointer = (PWORD)((unsigned char*)peBase + imageExportDirectory->AddressOfNameOrdinals);
PDWORD exportNamePointerTable = (PDWORD)((unsigned char*)peBase + imageExportDirectory->AddressOfNames);
```

Now how do we work with these? This can be pretty confusing.
Our objective for this article, is to find the pointer to the `MessageBoxA` function through the export address table, and invoke it.

Since we know the `numberOfNames` Let's first create a loop.

```cpp
int nameIndex = 0;
for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
{
}
```
The `exportNamePointerTable` above contains an array of RVAs to function names.

We can get the name of through the RVA from the `exportNamePointerTable`.

```cpp
int nameIndex = 0;
for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
{
        char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
}
```

We can check if it is the function that we want.

```cpp
int nameIndex = 0;
for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
{
     char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
     if (strcmp("MessageBoxA", name) == 0)
     {
     }
}
```

Now for the tricky part, the we need to get the correct index to get the address of the function from our `exportAddressTable`. The index is called, an `ordinal`. This `ordinal` is retrieved by using the `nameIndex` as an index to the `nameOrdinalsPointer` above.

```cpp
int nameIndex = 0;
for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
{
     char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
     if (strcmp("MessageBoxA", name) == 0)
     {
            WORD ordinal = nameOrdinalsPointer[nameIndex];
            PDWORD targetFunctionAddress = (PDWORD)((unsigned char*)peBase + exportAddressTable[ordinal]);
     }
}
```

Next, we can create a function pointer that follows the signature of the function that we want to invoke.

```cpp
int nameIndex = 0;
for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
{
     char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
     if (strcmp("MessageBoxA", name) == 0)
     {
            WORD ordinal = nameOrdinalsPointer[nameIndex];
            PDWORD targetFunctionAddress = (PDWORD)((unsigned char*)peBase + exportAddressTable[ordinal]);

            typedef int (WINAPI* MyFunction)(HWND, LPCSTR, LPCSTR, UINT);
            MyFunction myFunction = (MyFunction)targetFunctionAddress;
            (*myFunction)(0, "asd", 0, 0);
     }
}
```

The overall code looks like,

```cpp
#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_OPTIONAL_HEADER imageOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&imageNtHeaders->OptionalHeader;
    
    PIMAGE_DATA_DIRECTORY imageExportDataDirectory = &(imageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
    
    PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)peBase + imageExportDataDirectory->VirtualAddress);
    
    DWORD numberOfNames = imageExportDirectory->NumberOfNames;

    PDWORD exportAddressTable = (PDWORD)((unsigned char*)peBase + imageExportDirectory->AddressOfFunctions);
    PWORD nameOrdinalsPointer = (PWORD)((unsigned char*)peBase + imageExportDirectory->AddressOfNameOrdinals);
    PDWORD exportNamePointerTable = (PDWORD)((unsigned char*)peBase + imageExportDirectory->AddressOfNames);
    
    char buffer[1024 * 20] = { 0 };
    int nameIndex = 0;
    for (nameIndex = 0; nameIndex < numberOfNames; nameIndex++)
    {
        char* name = (char*)((unsigned char*)peBase + exportNamePointerTable[nameIndex]);
        if (strcmp("MessageBoxA", name) == 0)
        {
            WORD ordinal = nameOrdinalsPointer[nameIndex];
            PDWORD targetFunctionAddress = (PDWORD)((unsigned char*)peBase + exportAddressTable[ordinal]);

            typedef int (WINAPI* MyFunction)(HWND, LPCSTR, LPCSTR, UINT);
            MyFunction myFunction = (MyFunction)targetFunctionAddress;
            (*myFunction)(0, "asd", 0, 0);
        }
    }

    return 0;
}
```