The linker lays out cli-64.exe as if it will live at 0x140000000, and bakes that assumption into every absolute address — pointers in .data, jump tables, vtable slots. But another process may already have claimed 0x140000000, and Windows would rather load your image somewhere else. If the loader cannot move those baked-in addresses, it cannot randomize your image, and ASLR — the single most basic exploit mitigation on Windows — is off.
The base relocation table is the repair list. It records, for each absolute address the linker emitted, the page it sits on and how many bits to patch. The loader computes one number — the difference between where the image wanted to be and where it landed — and adds it to every recorded location. Without this table an image must load at its exact preferred base or not at all: the spec says the loader reports an error when the base address is unavailable and the file has no relocations.
The table is a list of pages, and each page holds a list of tiny instructions:
To apply: delta = actual base - preferred base, then for each entry, target = page RVA + offset, and patch according to the type. If the image landed exactly at ImageBase, delta is zero and the loader may skip the work entirely — the spec notes the loader is not required to process relocations unless the image cannot load at its preferred base.
The model's missing piece: type 0 is not a patch at all. IMAGE_REL_BASED_ABSOLUTE means "skip" — padding to align the next block. A table full of type 0 is normal, not corruption.
Base relocation block header (8 bytes), from the spec's "Base Relocation Block":
| Offset | Size | Field | What it says |
|---|---|---|---|
| 0 | 4 | Page RVA | Image base plus this page RVA, plus each entry's offset, gives the VA to patch |
| 4 | 4 | Block Size | Total bytes in the block, including the header and all type/offset words |
| 8 | 2 each | Type/Offset entries | High 4 bits = type, low 12 bits = offset within the page |
The relocation types you will actually meet, from the spec's "Base Relocation Types" table:
| Value | Constant | What it patches |
|---|---|---|
| 0 | IMAGE_REL_BASED_ABSOLUTE | Skipped — used to pad a block |
| 1 | IMAGE_REL_BASED_HIGH | Adds the high 16 bits of delta to a 16-bit field |
| 2 | IMAGE_REL_BASED_LOW | Adds the low 16 bits of delta to a 16-bit field |
| 3 | IMAGE_REL_BASED_HIGHLOW | Adds all 32 bits of delta to a 32-bit field — the PE32 workhorse |
| 4 | IMAGE_REL_BASED_HIGHADJ | High 16 bits plus a following adjustment word (consumes two slots) |
| 10 | IMAGE_REL_BASED_DIR64 | Applies delta to a 64-bit field — the PE32+ workhorse |
DYNAMIC_BASE alone enables ASLR. The spec explicitly notes that the linker's default behavior is to strip base relocations from executable (EXE) files, and COFF Characteristics bit 0x0001 (IMAGE_FILE_RELOCS_STRIPPED) is the on-disk record of exactly that: no relocations, must load at the preferred base, loader reports an error otherwise. DllCharacteristics 0x0040 (DYNAMIC_BASE) promises "this image can be relocated at load time" — a promise it cannot keep without this table. Check both fields, and remember type 3 is a 32-bit patch (PE32) while type 10 is a 64-bit patch (PE32+); mixing them up silently corrupts half an address.The entire .reloc section of cli-64.exe — data directory index 5 says RVA 0x8000, size 0x30, and the section header puts it at file offset 0x3600:
00003600: 0030 0000 3000 0000 50a2 58a2 60a2 68a2 .0..0...P.X.`.h. 00003610: 70a2 80a2 90a2 a8a2 b0a2 a8a3 b0a3 28a4 p.............(. 00003620: 40a4 48a4 d0a4 e8a4 f0a4 f8a4 00a5 08a5 @.H.............
0030 0000 = 0x00003000 — Page RVA, the .rdata page.3000 0000 = 0x00000030 — Block Size 48 bytes. Header is 8, so (0x30 - 8) / 2 = 20 entries, and the section is exactly this one block.50 a2 = word 0xA250: high nibble 0xA = 10 = IMAGE_REL_BASED_DIR64, low 12 bits = 0x250. Patch target RVA = 0x3000 + 0x250 = 0x3250.08 a5 = 0xA508 → DIR64 at offset 0x508, RVA 0x3508.All twenty entries decode as type 10 — this is a 64-bit image patching 64-bit addresses. The COFF Characteristics of this file are 0x0022 (EXECUTABLE_IMAGE | LARGE_ADDRESS_AWARE): bit 0x0001 is clear, so relocations were kept, and DllCharacteristics 0x8160 includes DYNAMIC_BASE. The three facts line up: the binary claims it can move, and the repair list to make that true is present.
objdump decodes the blocks for you — types, offsets, and the resulting RVAs:
$ objdump -x cli-64.exe | sed -n '/PE File Base Relocations/,+10p'
PE File Base Relocations (interpreted .reloc section contents)
Virtual Address: 00003000 Chunk size 48 (0x30) Number of fixups 20
reloc 0 offset 250 [3250] DIR64
reloc 1 offset 258 [3258] DIR64
reloc 2 offset 260 [3260] DIR64
reloc 3 offset 268 [3268] DIR64
reloc 4 offset 270 [3270] DIR64
reloc 5 offset 280 [3280] DIR64
reloc 6 offset 290 [3290] DIR64
reloc 7 offset 2a8 [32a8] DIR64
reloc 8 offset 2b0 [32b0] DIR64Or decode the block yourself:
$ python3 -c "
import struct
d=open('cli-64.exe','rb').read()
b=d[0x3600:0x3630]
va,sz=struct.unpack('<II',b[:8])
n=(sz-8)//2
ents=struct.unpack('<'+'H'*n, b[8:])
print(hex(va), hex(sz), 'entries', n, 'first', 'type', ents[0]>>12, 'off', hex(ents[0]&0xfff))"
0x3000 0x30 entries 20 first type 10 off 0x250What to look for: does every entry's high nibble match the image's bitness, does (SizeOfBlock - 8) divide evenly by 2, and does the sum of all blocks equal the directory size? A mismatch there means you are looking at corrupted or stripped relocation data.
Without looking back: decode the relocation word 0xA250.
A relocation block reports Page RVA = 0x9000 and SizeOfBlock = 0x18. How many type/offset entries does it contain, and what is the last entry's page offset range?
The pattern: SizeOfBlock always includes its own 8-byte header. Subtract first, then divide — that one subtraction is the difference between a correct parser and one that reads two garbage entries past every block.
Now that the image can move, the hardening questions begin: did the binary opt into ASLR and DEP? Which subsystem runs it? Is it signed? Those answers live in three small header fields — COFF Characteristics, Subsystem, and DllCharacteristics — plus the certificate table at the end of the file.
Next: Security & Subsystem Flags — every bit that decides how (and whether) Windows will run you.