The Big Picture: A Menu and an Order Form
Two files must agree on what is being shared. The DLL says, "Here is what I offer." The program says, "Here is what I need." These two lists are the export table and the import table, and the loader matches them up.
Think of a restaurant. The menu is the export table: the dishes the kitchen can make. The customer's order is the import table: the dishes they want. The waiter (the loader) carries each order to the kitchen and brings back the food (the address of the function).
program.exe LOADER kernel32.dll
┌──────────────────┐ ┌───────────────┐ ┌──────────────────┐
│ IMPORT TABLE │ │ matches names │ │ EXPORT TABLE │
│ "I need: │ ──────> │ with exports, │ <─────── │ "I offer: │
│ CreateFileW │ │ gets each │ │ CreateFileW │
│ ReadFile │ │ address │ │ ReadFile │
│ CloseHandle" │ └───────┬───────┘ │ CloseHandle ... │
└──────────────────┘ │ └──────────────────┘
▼
IAT slots filled with
real addresses
5.1 The Export Table: What a DLL Offers
What it is: The export table (or export directory) is a section inside a DLL's PE file. It lists the functions and data the DLL makes available to others. Anything not listed there is private to the DLL and cannot be found by GetProcAddress or by an import.
Each entry holds:
| Part | Meaning |
|---|---|
| Name | The function's text name, such as CreateFileW |
| Ordinal | A number that identifies it (see 5.3) |
| Address | Where in the DLL the code starts (stored as an offset, a distance from the DLL's start) |
Forwarders. An export does not always hold code. It can be a forwarder: a note saying "this function really lives in another DLL". For example, some functions you call in kernel32.dll are forwarded to the same function name in another system DLL. The loader follows the note for you. This lets Microsoft move code around inside Windows without breaking programs.
Data exports. A DLL can also export a variable, not just a function. This is rarer.
5.2 The Import Table: What a Program Needs
What it is: The import table is a section inside an EXE or DLL that lists, DLL by DLL, the functions it needs.
Import table of notepad.exe (simplified example, not exact)
kernel32.dll: CreateFileW, ReadFile, WriteFile, CloseHandle ...
user32.dll: CreateWindowExW, GetMessageW, DispatchMessageW ...
gdi32.dll: TextOutW, CreateFontW ...
The IAT again. Next to this list is the IAT (Import Address Table): the empty slots the loader fills with real addresses (shown in Section 3.1). Every call to an imported function goes through its IAT slot.
Why a slot and not a direct address? Because the DLL's address is not known when the EXE is built. It can change each time because of ASLR. The slot lets the loader fix it once, in one place, rather than changing every call in the program.
Imports are matched by name or by ordinal. The import entry says either "the function named ReadFile" or "the function number 1234".
Where to look at imports on your PC:
- File Explorer: right-click a program → Properties → Details tab shows version, description and company, but not the import list.
dumpbin /dependents file.exeshows which DLLs a program imports.dumpbinis part of the Visual Studio tools, so it only works if you installed them (then open "Developer Command Prompt"). It is optional.- Process Explorer (free from Microsoft Sysinternals) shows loaded DLLs for a running program (Practical Lab).
- PowerShell shows what is actually loaded:
Get-Process -Name notepad | Select-Object -ExpandProperty Modules(A module is the name Windows uses for "an EXE or DLL loaded in a process".)
If you have Visual Studio tools,
dumpbin /exports C:\Windows\System32\kernel32.dlllists the export table. It is a long list, so try| more. Without those tools, you can skip it, because nothing in this chapter depends on it.
5.3 Ordinals vs Names
What it is: A function can be found in two ways.
| Method | How it works | Good | Bad |
|---|---|---|---|
| By name | The loader searches the export table's names for the text ReadFile | Easy to read; stays correct if the DLL is rebuilt and order changes | Takes extra space; a tiny bit slower |
| By ordinal | The program uses the function's number in the DLL (for example 17) | Small and quick | The number can change when a DLL is rebuilt, so programs break; hard to read |
An ordinal is just "position number in the export table". Think of a menu where you can order "Pizza" or "Dish 17". Names are safer, because if the kitchen reshuffles its menu, "Dish 17" is suddenly a different meal.
Export table of mylib.dll (example)
Ordinal Name Address
─────── ─────────── ────────
1 Open 0x1000
2 Read 0x1040
3 Close 0x1090
Who uses ordinals? A few old or special system DLLs do. Microsoft's own APIs are almost always imported by name, since the names are the promise that stays stable between Windows versions.
C++ names and decoration. C++ allows two functions to share a name, so compilers change the name into a longer decorated (or mangled) name such as ?Open@Foo@@QEAAHXZ. To avoid this ugliness, library authors export plain C-style names so every language can find them.
How GetProcAddress uses these: It accepts either a name or an ordinal. It walks the export table, finds the entry, follows any forwarder, and gives back the address.
Summary
| Topic | Remember this |
|---|---|
| Export table | The DLL's menu: names, ordinals and addresses of what it offers |
| Import table | The program's order: which functions from which DLLs |
| IAT | Slots in the program that the loader fills with real addresses |
| Forwarder | An export that points to a function in another DLL |
| Name vs ordinal | Names are stable and readable; ordinals are numbers that may change |
| Tools | Get-Process ... Modules and Process Explorer show what is loaded; dumpbin only if Visual Studio tools are installed |