---
# System prepended metadata

title: Export in PE File

---

---
__Prolouge__: The purpose of this page is in order to figure out what is export table in PE file and how the OS find the export function used for others program

This post is related to PE HEADER knowledges!

## Introduction
First of all we need to understand what is data directory. Basically, Data directory is just an attribute in the `IMAGE_OPTIONAL_HEADER` which belongs to `IMAGE_NT_HEADER` which also be coordinated by `IMAGE_DOS_HEADER` `e_lfanew` attribute.

Deep into data directory element, the data directory itself, is an array or a table contains lots of thing used to manage many stuff by operating system. And the first one is export table which hold two value. The RVA lead to the `IMAGE_EXPORT_DIRECTORY` and the size of `Export Table`.

Geninuelly, the `IMAGE_EXPORT_DIRECTORY` is a struct that remain those values
![image](https://hackmd.io/_uploads/By_uSLAMbg.png)
Source: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#export-directory-table

The value that we must take a look here is from `Ordinal Base` to `Ordinal Table RVA`

Now the `IMAGE_EXPORT_DIRECTORY` contains 3 table.
1. Export Address Table *EAT* contains an array of DWORD that each lead to the specific function in the program or a string if that is a forwarded export function. This table has `Addres Table Entries` size
2. Name Pointer RVA, which is an array of DWORD (4 bytes) stores the RVA directly to the name of the function with null terminated char.
3. The Ordinal Table RVA which is an array of WORD (2 bytes) stores the index (0-based index) which is used for locating the function in EAT 
    *Both 2 and 3 has `Number of Name Pointers` size*

Now what is actually Ordinal Base. Well bascially, by concept there are two different ordinal base. The one used as importer to search suitable function (importer request) called Ordinal Number, others is used for locating the function in the program (export table lookup value) called Ordinal index. However, The formular and relationship of those is.
> `Ordinal Number` = `Ordinal Base` + `Ordinal Index`
> `Ordinal Index` = `Ordinal Number` - `Ordinal Base`

The process of tracing function in the exporter program.
1. For example you require a function HelloWorld(). Now first of all the OS will find the function name in the AddressOfName (Name Pointer RVA) and filter out the index in that table called X.
2. After that extracting the ordinal index from Ordinal Table RVA using the value X, call this value is Y
3. The value Y will be used in order to access the function in the EAT

>[!Note]
Edge Case: For example you don't have the function name like `HelloWorld` but you just have an ordinal number. The OS will do some calculation with Ordinal Base. More specifically, your ordinal number will be minus to ordinal base then that value will be used as the X value *(skip the step 1)*

## Script
All thing is clear now, lets try to extract those attribute clearly. We will write a small script using Ipyida or Idapython i don't really know the name lol. 

Now first of all we have to  add type library MSSDK in ida like this. This is just for using struct like `IMAGE_DOS_HEADER`, ... etc
![image](https://hackmd.io/_uploads/SJR70I0MWl.png)
*The second one*

```python 
import idaapi as api

dos_tp = api.Appcall.typedobj('IMAGE_DOS_HEADER;')
idd_tp = api.Appcall.typedobj('IMAGE_DATA_DIRECTORY;')
inh_tp = api.Appcall.typedobj('IMAGE_NT_HEADERS32;')
ied_tp = api.Appcall.typedobj('IMAGE_EXPORT_DIRECTORY;')

def main(base):
    ok, dos = dos_tp.retrieve(base)
    if not ok:
        return
    
    inh_ea = dos.e_lfanew + base
    ok, inh = inh_tp.retrieve(inh_ea)
    if not ok:
        return
    
    eid = inh.OptionalHeader.DataDirectory['0']
    ied_start = eid.VirtualAddress + base
    ied_end = ied_start + eid.Size
    
    print(f'INH: {inh_ea:x} Start {ied_start:x} End: {ied_end:x} EID SZ: {eid.Size:x}')
    
    ok, ied = ied_tp.retrieve(ied_start)
    if not ok:
        return
    eat = ied.AddressOfFunctions + base
    namesAddress = ied.AddressOfNames + base
    ordinalsName = ied.AddressOfNameOrdinals + base
    ordinalBase = ied.Base
    numNames = ied.NumberOfNames
    
    print(f'EAT {eat:x} NameAddress {namesAddress:x} OrdinalsName {ordinalsName:x} numNames {numNames}')
    
    ntable = {}
    otable = {}
    
    #Print all current function in the EAT with their address
    if numNames != 0:
        for i in range(numNames):
            name_ea = base + api.get_wide_dword(namesAddress + 4 * i)
            name_str = idc.get_strlit_contents(name_ea).decode()
            print(f'Address {name_ea:x} has function {name_str}')
            ntable[i] = name_str
            otable[api.get_wide_word(ordinalsName + i * 2)] = i
            
    # Loop through functions
    for i in range(ied.NumberOfFunctions):
        func_addr = base + api.get_wide_dword(eat + 4 * i)
        if func_addr == base:
            continue
        ordinalNumber = ordinalBase + i
            
        lookup = otable.get(i, -1)
        if ied_start <= func_addr <= ied_end:
            # This is forward function
            fw_name = idc.get_strlit_contents(func_addr).decode()
            print(f'Ord: {ordinalNumber} Address: {func_addr} Name {ntable[lookup]} forward to {fw_name}') 
            continue
        
        # This is function that belongs to this program
        if lookup == -1:
            print(f'Ord: {ordinalNumber} Address: {func_addr:x}')
        else:
            print(f'Ord: {ordinalNumber} Address: {func_addr:x} Name: {ntable[lookup]}')
        
        
api.msg_clear()
main(api.get_imagebase())

```

You can open abitrary DLL file on your window computer or download on the internet and load it into IDA and use this script for testing.

>[!Note]
If you found any misleading knowledge or something about my understanding, please comment I will fix it!

This post references: https://www.youtube.com/watch?v=VjxPWbUJI9A