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 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

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 The second one

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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.

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