FOUNDATIONS · LESSON 01

Binary Anatomy

15 min

Learning Objectives

  • Identify PE/ELF magic bytes and validate file type
  • Map sections to memory permissions (R/W/X)
  • Locate entry point and understand its significance
  • Parse section headers manually in a hex editor
  • Explain the role of each PE header component
  • Compare PE vs ELF loading models

Every executable is a structured container. The OS loader reads this structure to map the binary into memory and begin execution.

PE Format (Windows)

DOS Header (64 bytes)
  └─ "MZ" magic → Validates as executable
  └─ e_lfanew → Offset to NT Headers

NT Headers (Signature + File Header + Optional Header)
  ├─ Signature: "PE\0\0"
  ├─ File Header (COFF)
  │   ├─ Machine: 0x8664 (x64)
  │   ├─ NumberOfSections
  │   ├─ TimeDateStamp
  │   └─ Characteristics (DLL, EXE, etc.)
  └─ Optional Header (PE32+)
      ├─ Magic: 0x20B (PE32+)
      ├─ EntryPoint (RVA)
      ├─ ImageBase (preferred load address)
      ├─ SectionAlignment / FileAlignment
      └─ DataDirectories[16] → Imports, Exports, Relocs, TLS, etc.

Section Table (array of IMAGE_SECTION_HEADER)
  ├─ Name (8 bytes, e.g., ".text", ".rdata", ".data")
  ├─ VirtualSize / VirtualAddress (RVA)
  ├─ SizeOfRawData / PointerToRawData (file offset)
  └─ Characteristics (MEM_READ, MEM_WRITE, MEM_EXECUTE)

DOS Header Deep Dive

OffsetFieldSizePurpose
0x00e_magic2 bytes“MZ” signature (0x5A4D)
0x02e_cblp2 bytesBytes on last page
0x04e_cp2 bytesPages in file
0x06e_crlc2 bytesRelocations
0x08e_cparhdr2 bytesHeader paragraphs
0x0Ae_minalloc2 bytesMin extra paragraphs
0x0Ce_maxalloc2 bytesMax extra paragraphs
0x0Ee_ss2 bytesInitial SS
0x10e_sp2 bytesInitial SP
0x12e_csum2 bytesChecksum
0x14e_ip2 bytesInitial IP
0x16e_cs2 bytesInitial CS
0x18e_lfarlc2 bytesRelocation table offset
0x1Ae_ovno2 bytesOverlay number
0x1Ce_res8 bytesReserved
0x24e_oemid2 bytesOEM identifier
0x26e_oeminfo2 bytesOEM info
0x28e_res220 bytesReserved
0x3Ce_lfanew4 bytesOffset to NT Headers (critical!)

The DOS header exists for backward compatibility. The stub at offset 0x40 typically prints “This program cannot be run in DOS mode” — but the space between 0x40 and e_lfanew is a classic hiding spot for data.

NT Headers: File Header (COFF)

OffsetFieldSizeValues
0x00Machine2 bytes0x8664 (x64), 0x014c (x86), 0xAA64 (ARM64)
0x02NumberOfSections2 bytesCount of section headers following
0x04TimeDateStamp4 bytesUnix timestamp (linker time) — often forged
0x08PointerToSymbolTable4 bytesCOFF debugging (usually 0)
0x0CNumberOfSymbols4 bytesCOFF debugging (usually 0)
0x10SizeOfOptionalHeader2 bytes0xE0 (PE32), 0xF0 (PE32+)
0x12Characteristics2 bytesFlags: 0x0001=relocs stripped, 0x0002=executable, 0x0004=line nums stripped, 0x0008=local syms stripped, 0x0020=large address aware, 0x2000=DLL

Optional Header (PE32+) — Key Fields

OffsetFieldSizeDescription
0x00Magic2 bytes0x10B (PE32), 0x20B (PE32+), 0x107 (ROM)
0x02Major/Minor Linker Version2 bytesLinker version
0x04SizeOfCode4 bytesSum of executable sections
0x08SizeOfInitializedData4 bytesSum of initialized data sections
0x0CSizeOfUninitializedData4 bytesSum of uninitialized data (.bss)
0x10AddressOfEntryPoint4 bytesRVA of entry point (where execution starts)
0x14BaseOfCode4 bytesRVA of .text
0x18ImageBase8 bytesPreferred load address (e.g., 0x140000000)
0x20SectionAlignment4 bytesMemory alignment (usually 0x1000)
0x24FileAlignment4 bytesDisk alignment (usually 0x200)
0x28Major/Minor OS Version4 bytesRequired OS version
0x2CMajor/Minor Image Version4 bytesBinary version
0x30Major/Minor Subsystem Version4 bytesSubsystem version
0x34Win32VersionValue4 bytesReserved (0)
0x38SizeOfImage4 bytesTotal memory size (aligned)
0x3CSizeOfHeaders4 bytesHeaders + section table (aligned)
0x40CheckSum4 bytesPE checksum (rarely verified)
0x44Subsystem2 bytes1=Native, 2=GUI, 3=CUI, 9=Windows CE
0x46DllCharacteristics2 bytes0x0040=ASLR, 0x0100=DEP, 0x0200=No Isolation, 0x0400=No SEH, 0x0800=No Bind, 0x2000=WDM Driver, 0x4000=Guard CF, 0x8000=Terminal Server Aware
0x48SizeOfStackReserve8 bytesInitial stack reservation
0x50SizeOfStackCommit8 bytesInitial stack commit
0x58SizeOfHeapReserve8 bytesHeap reservation
0x60SizeOfHeapCommit8 bytesHeap commit
0x68LoaderFlags4 bytesReserved (0)
0x6CNumberOfRvaAndSizes4 bytesAlways 16
0x70DataDirectory[16]128 bytesCritical: Imports, Exports, TLS, Relocs, etc.

Data Directories (Index → Purpose)

IndexNamePurpose
0ExportFunctions this binary provides
1ImportFunctions this binary needs (IAT)
2ResourceIcons, manifests, version info
3Exception.pdata (unwind info for x64)
4SecurityAuthenticode signature
5Base RelocationASLR fixups
6DebugPDB path, timestamp
7ArchitectureReserved
8Global PtrRISC
9TLSThread Local Storage callbacks
10Load ConfigCFG, CET, Guard CF, RFG
11Bound ImportPre-bound IAT timestamps
12IATRuntime-resolved import addresses
13Delay ImportLoad on first call
14COM Descriptor.NET metadata
15Reserved—

Section Headers (IMAGE_SECTION_HEADER) — 40 bytes each

OffsetFieldSizeDescription
0x00Name8 bytesNull-padded (e.g., “.text\0\0\0”)
0x08VirtualSize4 bytesSize in memory
0x0CVirtualAddress4 bytesRVA in memory
0x10SizeOfRawData4 bytesSize on disk (file-aligned)
0x14PointerToRawData4 bytesFile offset (file-aligned)
0x18PointerToRelocations4 bytesCOFF relocations (obj files)
0x1CPointerToLinenumbers4 bytesCOFF line numbers
0x20NumberOfRelocations2 bytesCOFF
0x22NumberOfLinenumbers2 bytesCOFF
0x24Characteristics4 bytesPermissions + flags

Section Characteristics (flags):

  • 0x00000020 = CODE
  • 0x00000040 = INITIALIZED_DATA
  • 0x00000080 = UNINITIALIZED_DATA
  • 0x20000000 = MEM_EXECUTE (X)
  • 0x40000000 = MEM_READ (R)
  • 0x80000000 = MEM_WRITE (W)
  • Common combos: .text = 0x60000020 (R+X+CODE), .rdata = 0x40000040 (R+INIT_DATA), .data = 0xC0000040 (R+W+INIT_DATA)

Key Sections

SectionTypical ContentPermissionsCharacteristics
.textCode (instructions)R-X0x60000020
.rdataRead-only data, imports, relocationsR—0x40000040
.dataInitialized global/static variablesRW-0xC0000040
.pdataException handling (x64)R—0x40000040
.rsrcResources (icons, manifests)R—0x40000040
.relocBase relocation tableR—0x42000040
.tlsThread Local Storage dataRW-0xC0000040
.bssUninitialized dataRW-0xC0000080

Hands-On: Parse a PE Header

Exercise: In the hex viewer above:

  1. Find the MZ signature at offset 0x0
  2. Read e_lfanew at 0x3C (little-endian) → jump to NT Headers
  3. Verify PE\0\0 signature
  4. Count sections in File Header
  5. Locate Entry Point RVA in Optional Header
  6. Match section table entries to the hex dump

Why This Matters

  • Packers modify headers to hide sections / change entry point
  • Malware abuses Characteristics (e.g., RWX sections)
  • Entry Point is where execution actually starts — not main
  • Data Directories reveal capabilities (imports = what it calls, exports = what it provides)
  • TimeDateStamp often forged — check for 0 or future dates
  • ImageBase collisions trigger relocations (ASLR)

ELF (Linux) — Quick Comparison

ELF Header (64 bytes)
  ├─ e_ident: 0x7F "ELF" + class (32/64) + endianness
  ├─ e_type: ET_EXEC / ET_DYN
  ├─ e_entry: Entry point (virtual address)
  ├─ e_phoff / e_shoff: Program/Section header table offsets
Program Headers → Segments (loader view, RWX perms)
Section Headers → Sections (linker view, semantic names)

Key Differences:

AspectPE (Windows)ELF (Linux)
Loading UnitSectionSegment (Program Header)
SectionsMandatory, semantic namesOptional (strip removes)
PermissionsPer-sectionPer-segment (PT_LOAD)
Entry PointRVA (Relative Virtual Address)Virtual Address (absolute)
RelocationsBase Reloc Directory.rela.dyn / .rela.plt
Dynamic LinkingImport Directory (IAT).dynamic + .got.plt + .plt
TLSTLS Directory (DataDir[9])PT_TLS segment

Same concepts, different layout. PE = Section-centric. ELF = Segment-centric for loading.


Takeaway

The header is a map. Read it before you enter the territory.

Lab 1: Parse headers manually → Hidden String Hunt


Practice Exercises

  1. Header Forensics: Given a PE with TimeDateStamp = 0x5A5A5A5A, NumberOfSections = 0, Characteristics = 0x010F — what’s suspicious?
  2. Section Mismatch: A section has VirtualSize=0x1000 but SizeOfRawData=0. What does this indicate? (Answer: .bss — uninitialized data)
  3. RWX Hunt: Scan a directory of binaries for sections with MEM_WRITE | MEM_EXECUTE. Why is this dangerous?
  4. Entry Point Trace: In Ghidra, navigate to the Entry Point RVA. Is it the CRT startup code (__security_init_cookie, __tmainCRTStartup) or custom?

Interactive

LAB

lab-01

Apply what you learned in a hands-on challenge.

Open Lab