Back to course

The Resource Directory

15 minadvancedModule 7: Resources & Runtime TablesContinues from pe-load-config
15 min · Module 7: Resources & Runtime Tables · Structures

Why This Matters

The icon you see in Explorer, the version string in a file properties dialog, the error dialog text in your language, and the application manifest that asks for per-user privileges all live inside the PE file itself. None of that is loose text — it is a lookup table the operating system reads with FindResource-style APIs.

The resource directory is also the structure malware analysts, localization tools, and installers touch every day: extract an icon, swap a dialog, read the manifest that decides whether a program runs elevated. If you cannot walk this tree, half of the PE file is unreadable to you.

Data directory index 2 (the Resource Table) points at this structure. In our 64-bit sample cli-64.exe it holds RVA 0x7000 with size 0x1E0, exactly the .rsrc section.

A Simple Model

Think of resources as a three-level filing cabinet, which is exactly how the PE/COFF specification describes it by convention:

  1. Type — what kind of thing it is: an icon, a version block, a manifest.
  2. Name — which instance of that type: icon 101, dialog MAIN.
  3. Language — which copy: English (United States), German, and so on.

Each level is a directory: a 16-byte header followed by 8-byte entries. An entry either points one level deeper (a subdirectory) or ends the walk (a leaf) that finally describes the raw bytes.

The model's missing piece: every level uses the same two structures, and a single high bit inside each entry decides which way it points. Learn one level and you have learned all three.

The Actual Detail

The specification chapter "The .rsrc Section" pins down every field:

StructureSizeContents
Resource Directory Table16 bytesCharacteristics, Time/Date Stamp, Major/Minor Version, Number of Name Entries, Number of ID Entries
Directory Entry8 bytesName Offset or Integer ID (4 bytes), then Data Entry Offset or Subdirectory Offset (4 bytes)
Resource Data Entry16 bytesData RVA, Size, Codepage, Reserved

Three rules from the spec control the whole walk: all Name entries precede all ID entries in every table; entries are sorted in ascending order (names by case-sensitive string, IDs by numeric value); and directory offsets are relative to the address of the resource data directory, not to the start of the file.

The high bit of the second entry field is the branch: clear means the low 31 bits address a leaf (Resource Data Entry), set means they address the next directory table. Windows preassigns the well-known Type IDs — for example RT_ICON is 3, RT_GROUP_ICON is 14, RT_VERSION is 16, RT_MANIFEST is 24 (Microsoft, "Resource Types").

Common mistake. The two offsets in a leaf mean different things. Directory-level offsets are relative to the resource directory start, but the leaf field Data RVA is a full image-relative virtual address — add ImageBase to run it. Mixing them up sends your parser into the wrong half of the address space.

A Real Example

The first 96 bytes of the .rsrc section in cli-64.exe (section file offset 0x3400) contain the entire walk for its one resource. The bytes are shown without the trailing ascii column:

00003400: 0000 0000 0000 0000 0000 0000 0000 0100
00003410: 1800 0000 1800 0080 0000 0000 0000 0000
00003420: 0000 0000 0000 0100 0100 0000 3000 0080
00003430: 0000 0000 0000 0000 0000 0000 0000 0100
00003440: 0904 0000 4800 0000 6070 0000 7d01 0000
00003450: 0000 0000 0000 0000 0000 0000 0000 0000

Walk-through:

  1. Bytes 0x3400-0x340F: type-level header. Number of Name Entries is 0, Number of ID Entries is 1 (the final 0100).
  2. Bytes 0x3410-0x3417: the single type entry. ID = 0x18 = 24 = RT_MANIFEST. Second word 0x80000018: high bit set, so a subdirectory sits 0x18 bytes from the directory start — file 0x3418.
  3. Bytes 0x3418-0x342F: name-level header with 1 ID entry. Entry 0100 0000 3000 0080: Name ID 1, high bit set, subdirectory at 0x30 — file 0x3430.
  4. Bytes 0x3430-0x343F: language-level header, 1 ID entry. Bytes 0x3440-0x3447: ID 0x0409 (1033, English United States), offset 0x48 with the high bit clear — a leaf at directory offset 0x48, file 0x3448.
  5. Bytes 0x3448-0x3457: the leaf. Data RVA = 0x7060, Size = 0x17D (381 bytes), Codepage = 0, Reserved = 0.

The bytes at RVA 0x7060 (file 0x3460, because .rsrc sits at RVA 0x7000 / file 0x3400) open with an XML declaration — this binary embeds a Windows application manifest:

00003460: 3c3f 786d 6c20 7665 7273 696f 6e3d 2731

3c 3f is an opening angle bracket followed by a question mark, and 78 6d 6c spells xml. The second sample, conpty.dll, shows the same tree with more breadth: its type table counts 2 ID entries — 0x10 (RT_VERSION, 16) and 0x18 (RT_MANIFEST, 24) — with leaves of size 0x358 and 0x17D.

Try It

Any Windows executable works. On Linux or macOS, find the .rsrc file offset from the section table, then dump it (0x3400 for our sample):

$ xxd -s 0x3400 -l 96 cli-64.exe
00003400: 0000 0000 0000 0000 0000 0000 0000 0100  ................
00003410: 1800 0000 1800 0080 0000 0000 0000 0000  ................

Decode by hand: read the last two bytes of the header as a little-endian count, then read each entry as two 32-bit little-endian words. Mask the second word with 0x7FFFFFFF to get the next offset, and test the top bit to learn whether it is a subdirectory or a leaf.

What to look for: the ID 0x18 should appear immediately, because almost every desktop executable ships a manifest. If you see IDs 3, 14, or 16 instead, you are looking at icons or a version block — the same tree, different Type ID.

Check Your Understanding

Without looking back: in a three-level resource walk, which level does the value 0x0409 identify?

Apply It

A directory entry reads 1800 0000 1800 0080. Before touching a hex editor again: where does this entry point, and relative to what?

The pattern: mask the high bit for the offset, use the high bit itself as the branch, and remember the base is the directory — not the file.

Connect

Resources are static data a program carries for the loader and the shell. The next table is the opposite: data the loader actively writes into the image while threads are being created.

Next: Thread-Local Storage — the TLS directory, its per-thread template, and the callback array.