I'm writing an analog of GetProcAddress function. When looking inside the export table I see the exports like this in advapi32.dll for example:

.text:4C362BAA aEventregister  db 'EventRegister',0    ; DATA XREF: .text:off_4C35FE10o
.text:4C362BB8                                         ; Exported entry 1290. EventRegister
.text:4C362BB8                 public EventRegister
.text:4C362BB8 EventRegister   db 'ntdll.EtwEventRegister', 0

So it is like a redirect to ntdll function. How to process these entries and how to detect if they lead to another library call?

Currently I just find the function ordinal by name and get its address, but for exports like this addresses are invalid (inside the address there is junk code).

Do I need to just read the string ntdll.EtwEventRegister at the ordinal address, split it by dot and get dll/function names?

If this is the case, how do I detect that the export address is just a string with this dll/function name? I need to somehow check if there is a valid string there, there should be other way, like some flag etc.

3 Answers 3


This is called export forwarding and you can explore it here with nice explanation:

The PE file format

The next 32-bit-value 'AddressOfFunctions' is a RVA to the list of exported items. It points to an array of 'NumberOfFunctions' 32-bit-values, each being a RVA to the exported function or variable.

There are 2 quirks about this list: First, such an exported RVA may be 0, in which case it is unused. Second, if the RVA points into the section containing the export directory, this is a forwarded export. A forwarded export is a pointer to an export in another binary; if it is used, the pointed-to export in the other binary is used instead. The RVA in this case points, as mentioned, into the export directory's section, to a zero-terminated string comprising the name of the pointed-to DLL and the export name separated by a dot, like "otherdll.exportname", or the DLL's name and the export ordinal, like "otherdll.#19".

  • This solution doesn't work with all DLLs. See my answer for the proper way of identifying forwarded exports. Apr 10, 2019 at 14:38

The other two answers are wrong. I reversed link.exe and the way it works is that if the “function” points into the export directory

(NTHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress <= func_ptr < NTHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + NTHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)

it is a forwarded export (i.e. you will find a string there).

The method from the other two answers used to work because the linker used to put the export directory as well as the forward strings into the .data section, but that is no longer the case. Newer linkers put the export directory and the forward strings into the .text section (where all the “actual” functions reside as well), so checking whether the “function” is in the same section as the export directory will no longer work.


Short answer: No, you don't have to guess it's a string. There's a 'trick'.

But first, some background: export forwarding is a legitimate and useful functionality piece. There are a handful of documentation describing how imports are loaded by the PE loader unfortunately export forwarding documentation is a little harder to come by and most of articles only mention export forwarding.

The key to identifying if you're processing an export forward is, a described here:

Forwarding is achieved by making an RVA in the AddressOfFunctions array point into the section which contains the export directory, something that normal exports should not do. At that location, there should be a zero terminated ASCII string of format "LibraryName.ExportName" for the appropriate place to forward this export to.

If the RVA in the AddressOfFunctions array points to the exports section, that function is forwarded and can't be located in the DLL being loaded. Instead, the value pointed should be interpreted as a null-terminated string, another module should be loaded and the loading process should be repeated.

In case the dot is followed by the hash sign, the forward entry points to an export by ordinal, not name, and the digits following the hash sign are the ordinal number of target function.

Please note an API may be forwarded multiple times before being reached.

Additionally, if you're handling bound imports you should make sure to treat ForwarderChain properly, see the following from the same article:

Binding can of course be a problem if the bound library / module forwards its exports to another module. In these cases, the non-forwarded imports can be bound, but the values which get forwarded must be identified so the loader can resolve them. This is done through the ForwarderChain member of the import descriptor. The value of "ForwarderChain" is an index into the FirstThunk and OriginalFirstThunk arrays. The OriginalFirstThunk for that index identifies the IMAGE_IMPORT_BY_NAME structure for a import that needs to be resolved, and the FirstThunk for that index is the index of another entry that needs to be resolved. This continues until the FirstThunk value is -1, indicating no more forwarded values to import.

For additional reading about the PE format and imports in particular, I recommend this resource.

Your Answer

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

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