1

I'm trying to explore the functions exported by profapi.dll and I came across a behavior that I can't explain.

Reading about the PE binary format I gather that the export table has 3 tables (arrays): one array containing function pointers of exported functions, another containing names (strings) of exported functions, and a third array containing the integer ordinal numbers of the exported functions. The linker needs the information in the name and/or the ordinal table to patch calls to exported functions in other DLLs. The name table pointer can be NULL if the DLL exports its functions using only ordinal numbers.

However, for profapi.dll, I see that both the name table and the ordinal table pointers are NULL; yet the DLL exports 6 functions (verified using IDAPro). In fact, shell32.dll calls one of the functions in profapi.dll using its ordinal number and the linker somehow resolves the address. I'm not sure how the linker is able to resolve the call in shell32.dll. Moreover, pefile, the Python library I am using to parse the DLL reports that profapi.dll does not export any functions since both the name and ordinal table pointers are NULL. What am I missing?

I'm using profapi.dll version 6.1.7600.16385 and shell32.dll version 6.1.7601.2278 on Windows 7. I'm using pefile version 1.2.10-139.

1 Answer 1

3

The "ordinal table" (also known as the Export Ordinal Table or AddressOfNameOrdinals) is used in conjunction with the "name table" (also known as the Export Name Pointer Table or AddressOfNames) if-and-only-if functions are exported by name.

From the official PE-COFF documentation:

The export name pointer table and the export ordinal table form two parallel arrays that are separated to allow natural field alignment. These two tables, in effect, operate as one table, in which the Export Name Pointer column points to a public (exported) name and the Export Ordinal column gives the corresponding ordinal for that public name. A member of the export name pointer table and a member of the export ordinal table are associated by having the same position (index) in their respective arrays.

But if there are no functions exported by name, then there is no Export Name Pointer Table, and if there's no Export Name Pointer Table then there's on need for the Export Ordinal Table either, which is why both fields are NULL in the Export Directory Table.

In fact, shell32.dll calls one of the functions in profapi.dll using its ordinal number and the linker somehow resolves the address.

The build-time linker (part of the compiler toolkit) doesn't resolve the address; the run-time loader (part of Windows) resolves the address. The loader see that shell32.dll imports a profapi.dll function by a given ordinal number (let's say 105, for example), so it subtracts profapi.dll's Ordinal Base (let's say 101, for example) from the ordinal number and uses the result (4 in this example) as an index into the Export Address Table to find the RVA of the imported function. Neither the Export Ordinal Table nor the Export Name Pointer Table are needed.

Moreover, pefile, the Python library I am using to parse the DLL reports that profapi.dll does not export any functions since both the name and ordinal table pointers are NULL. What am I missing?

As discussed here, the pefile Python library is broken, or at least older versions of it are known to be broken with respect to ordinal handling.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.