Windows PE NumberofNames, AddressOfNames,AddressOfFunctions & AddressOfNameOrdinals (windbg)
Table of Contents
A quick step by step tutorial to resolve function address through important fields in IMAGE EXPORT DIRECTORY. The following attributes of the structure are very important the NumberofNames, AddressOfNames, NumberOfFuntions, AddressOfFunctions, AddressOfNameOrdinals.
By finding the symbol position in AddressOfNames we can find the index number of FunctionsAddress in AddressOfNameOrdinals and this structure is a 2 bytes. With the 2 bytes index number we can then find the function address RVA in AddressOfFunction.
I will walk through with the windbg, but before we need to find the value of e_elfanew (0x3c) and offset to IMAGE OPTIONAL HEADERS (0x18) and Export Directory Table. You can check the previous post Windows PE File structure (windbg) and Export Directory Table
Unnfortunately the windbg does not come with symbols for IMAGE EXPORT DIRECTORY, but we can see the structure as showing below:
References: doxygen reactos & Dr. Fu’s Security Blog
IMAGE EXPORT DIRECTORY STRUCTURE #
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; //offset 0x0
DWORD TimeDateStamp; //offset 0x4
WORD MajorVersion; //offset 0x8
WORD MinorVersion; //offset 0xa
DWORD Name; //offset 0xc
DWORD Base; //offset 0x10
DWORD NumberOfFunctions; //offset 0x14
DWORD NumberOfNames; //offset 0x18
DWORD AddressOfFunctions; //offset 0x1c
DWORD AddressOfNames; //offset 0x20
DWORD AddressOfNameOrdinals; //offset 0x24
}
FYI, you can find Export Directory Table with command below: !dh kernelbase -f then get the VMA with ? kernelbase+1EAAA0.
FIND EXPORT DIRECTORY TABLE #
0:003> !dh kernelbase -f
File Type: DLL
FILE HEADER VALUES
14C machine (i386)
6 number of sections
...snipped...
Guard
1EAAA0 [ EF52] address [size] of Export Directory
1FEB2C [ 64] address [size] of Import Directory
205000 [ 548] address [size] of Resource Directory
0 [ 0] address [size] of Exception Directory
231600 [ 75E0] address [size] of Security Directory
...snipped...
We will use kernelbase module as an example, after finding the Export Directory VMA.We can dump all the attributes values of Export Directory Table structure as showing below:
EXPORT DIRECTORY TABLE Structure #
0:003> dd (KERNELBASE+1EAAA0)
7566aaa0 00000000 2c1f03ea 00000000 001ef578
7566aab0 00000001 00000778 00000778 001eaac8
7566aac0 001ec8a8 001ee688 001c4b60 00130260
NUMBEROFNAMES #
0:003> dd (KERNELBASE+1EAAA0)+0x18 l1
7559aab8 00000778
ADDRESSOFNAMES (Array) RVA #
Here we can dump the AddressOfNames array at offset 0x20.
0:003> dd (KERNELBASE+1EAAA0)+0x20 l1
7566aac0 001ec8a8
ADDRESSOFFUNCTIONS (Array) RVA #
Here we can dump the AddressOfFunctions array at offset 0x1c.
0:003> dd (KERNELBASE+1EAAA0)+0x1c l1
7566aabc 001eaac8
ADDRESSOFNAMEORDINALS (Array) RVA #
0:003> dd (KERNELBASE+1EAAA0)+0x24 l1
7566aac4 001ee688
Steps to find address of function #
Finding the symbol position in NamesOfFunction #
Lets say we are interested to find the function address for AccessCheckByType which is located as third position with RVA value in the NamesOfFunction array: 001ef64b which the symbol is translated to “AccessCheckByType”. Remember that the NamesOfFunction RVA is 001ec8a8 found earlier.
0:003> dd (KERNELBASE+001ec8a8)
7566c8a8 001ef625 001ef631 001ef64b 001ef65d
7566c8b8 001ef67d 001ef699 001ef6cb 001ef6f5
7566c8c8 001ef72e 001ef761 001ef772 001ef781
7566c8d8 001ef795 001ef7ab 001ef7c5 001ef7d8
7566c8e8 001ef7ed 001ef806 001ef80d 001ef81f
7566c8f8 001ef833 001ef84b 001ef85c 001ef86d
7566c908 001ef890 001ef8a0 001ef8b3 001ef8c3
7566c918 001ef8da 001ef8e7 001ef8ff 001ef91a
0:003> da kernelbase+001ef64b
7566f64b "AccessCheckByType"
Find the index number in AddressOfNameOrdinals #
To find that we know the RVA for AddressOfNameOrdinals is 001ee688 found earlier, with kernelbase base address we know the VMA and dump the values.
Something to notice is that this structure is 2 bytes and not 4 bytes, the first position value is 0007, the second value is 0006 and so on due to how CPU architecture presents data to memory (little endian) the highest order is presented in memory, network we know uses big endian.
As seen above the symbol “AccessCheckByType” is the third position in NamesOfFunction, we need to find the third position value in AddressofNameOrdinals. The index value is 0009, now we can go to next step to find that in AddressOfFunctions
0:003> dd kernelbase+001ee688
7566e688 00070006 00090008 000b000a 000d000c
7566e698 000f000e 00110010 00130012 00150014
7566e6a8 00170016 00190018 001b001a 001d001c
7566e6b8 001f001e 00210020 00230022 00250024
7566e6c8 00270026 00290028 002b002a 002d002c
7566e6d8 002f002e 00310030 00330032 00350034
7566e6e8 00370036 00390038 003b003a 003d003c
7566e6f8 003f003e 00410040 00430042 00450044
Looking for desired function RVA in AddressOfFunctions #
The RVA for AddressofFunctions discovered earlier is 001eaac8, we can dump the RVA of the functions. The index position we are interested is 0x9 (discovered earlier). Here is the RVA (0x00148b50) of function address for AccessCheckByType symbol.
0:003> dd kernelbase+001eaac8
7566aac8 001c4b60 00130260 00100ff0 001ef5dd
7566aad8 001601b0 0014c6f0 0010f650 0014f380
7566aae8 00148b50 001d8cf0 001d8da0 001d8e10
7566aaf8 001d8ed0 001ef70d 001ef743 0015ffb0
7566ab08 0014eba0 0014ff70 001566b0 001d8f90
7566ab18 00161920 001d8fe0 001d9030 00150c10
7566ab28 001d9080 001d90c0 001d9110 001d55a0
7566ab38 001d56d0 00174eb0 001678a0 001c60c0
Resolving RVA of AccessCheckByType #
We can see that we successfully resolved the function address for AccessCheckByType symbol in kernelbase module. Just combining the RVA of AccessCheckByType with Base Address of KernelBase. The u command resolves the address displaying the symbol along with the assembly instructions.
0:003> u kernelbase+0x00148b50
KERNELBASE!AccessCheckByType:
755c8b50 8bff mov edi,edi
755c8b52 55 push ebp
755c8b53 8bec mov ebp,esp
755c8b55 51 push ecx
755c8b56 8d45fc lea eax,[ebp-4]
755c8b59 50 push eax
755c8b5a ff752c push dword ptr [ebp+2Ch]
755c8b5d ff7528 push dword ptr [ebp+28h]