The Export Section
The following image is from the Win32 Programmer's reference:
Functions can be exported by a DLL in two ways - "by name" or "by ordinal only". An ordinal is a 16-bit number that uniquely identifies a function in a particular DLL. The number is unique only within the DLL it refers to.
If a function is exported by name, when other DLLs or functions want to call they use either the name or its ordinal in GetProcAddress which returns the address of the function in the DLL.
A screenshot of GetProcAddress from MSDN:
GetProcAddress can do this because the names and addresses of exported functions are stored in a well defined structure in the Export Directory. We can find the export directory since its the first element in the data directory and the RVA to it is contained at offset 78h from the start of the PE header.
The export structure is called IMAGE_EXPORT_DIRECTORY.
nName - The internal name of the module. This is necessary as the name of the file can be changed by the user. If this happens, the PE loader will use this internal name.
nBase - Starting ordinal number (needed to get into the addresses-of-function array - see below)
NumberOfFunctions- Total no. of functions exported by this module
NumberOfNames - Number of symbols that are exported by name. The value is not the number of all functions/symbols in the module. For that you need to check NumberOfFunctions. It can be 0. In that case, the module may export by ordinal only. If there is no function/symbol to be exported, the RVA of export table in the data directory will be 0.
AddressOfFunctions - An RVA that points to an array of pointers to (RVAs of) the functions in the module - the EAT. In other words, the RVAs to all functions in the module are kept in an array and this field points to the head of that array.
AddressOfNames - An RVA that points to an array of RVAs of the names of the functions in the module. - the Export Name Table (ENT)
AddressOfNameOrdinals - An RVA that points to a 16-bit array that contains the ordinal of the named functions - the Export Ordinal Table (EOT)
The IMAGE_EXPORT_DIRECTORY structure points to three arrays and a table of ASCII strings. The important array is the EAT, which is an array of function pointers that contain the address of exported functions. The other 2 arrays (ENT and EOT) run parallel in ascending order based on the name of the function. So, a binary search for a function's name can be performed and the result is its ordinal being found in the other array. The ordinal is simply the index into the EAT for that function.
Since the EOT array exists as a linkage between the names and the addresses, it cannot contain more elements than the ENT array i.e. each name can have only one associated address. The reverse is not true: an address may have several names associated with it.
e.g. if a DLL exports 40 functions it must have 40 members in the array pointed to by AddressOfFunctions (the EAT) and the NumberOfFunctions field must contain the value 40. To find the address of the function from its name, the OS must obtain values of NumberOfFunctions and NumberOfNames in the Export Directory. Next, it walks the arrays pointed to by the AddressOfNames and AddressOfNameOrdinals in parallel searching for the function name. If the name is found in the ENT, the value of that in EOT is extracted and used as an index into the EAT.
e.g. FunctionX is the 39th element in the ENT. The corresponding 39th element in the EOT is 5. We look at the 5th element in the EAT to find the RVA of FunctionX
If you already have the ordinal, you can directly go to the EAT. However, if the DLL is updated, the ordinals are altered and if the program depends on that, the DLL will break.
Exporting By Ordinal Only
When a function is exported by ordinal, its not present in the ENT.
But if there are 70 functions and only 40 are present in the ENT,. How do we find the remaining 30 functions which are exported by ordinal only?
You must find out by exclusion i.e. the entries in the EAT that are not referenced by the EOT contain the RVAs of functions that are exported by ordinal only.
The programmer specifies the starting ordinal number in a .def file. So, the tables above could start at 200. If order to prevent 200 empty entries in the array, the nBase holds the starting value and the loader subtracts the ordinal numbers from it to obtain the true index into the EAT.
Export Forwarding
Sometimes function appear to be exported by a DLL but actually reside in a completely different DLL. This is called export forwarding. e.g. in Windows, the kernel32.dll function HeapAlloc is forwarded to RtHeapAlloc exported by ntdll.dll. NTDLL.dll contains the native API set which is a direct interface to the kernel. Forwarding is performed at link time by a special instruction in the .def file.
When packed executables which have been unpacked and had their import tables constructed manually on one OS do not run on other OS, it because the API forwarding system or some other detail has been altered.
When a function is forwarded by its RVA clearly cant be code or data address in the current module. Instead, the EAT contains a pointer to an ASCII string of the DLL and function name to which its forwarded.
If therefore the EAT entry for a function points to an address inside the Exports section (i.e ASCII string) rather than outside into another DLL, you know the function is forwarded.




No comments:
Post a Comment