A field guide to PE section characteristics
What each section flag means, what the normal combinations look like, and which pairings are worth a second look.
Section flags are among the cheapest signals available in a PE file, and among the least read. This is a reference rather than an argument: the table is the point.
The flags that matter
IMAGE_SECTION_HEADER.Characteristics is a bitfield. These are the bits worth
reading, with the constant names as they appear in winnt.h.
| Flag | Value | Means |
|---|---|---|
CNT_CODE |
0x00000020 |
Contains executable code |
CNT_INITIALIZED_DATA |
0x00000040 |
Contains initialised data |
CNT_UNINITIALIZED_DATA |
0x00000080 |
Reserved space, no file backing |
MEM_DISCARDABLE |
0x02000000 |
Can be discarded after load |
MEM_NOT_CACHED |
0x04000000 |
Cannot be cached |
MEM_SHARED |
0x10000000 |
Shared across processes |
MEM_EXECUTE |
0x20000000 |
Executable at runtime |
MEM_READ |
0x40000000 |
Readable at runtime |
MEM_WRITE |
0x80000000 |
Writable at runtime |
What normal looks like
A compiler-produced binary is boringly consistent. Deviation is not proof of anything, but it is cheap to notice.
| Section | Usual characteristics | Contains |
|---|---|---|
.text |
CNT_CODE, MEM_EXECUTE, MEM_READ |
Compiled code |
.rdata |
CNT_INITIALIZED_DATA, MEM_READ |
Constants, import tables |
.data |
CNT_INITIALIZED_DATA, MEM_READ, MEM_WRITE |
Mutable globals |
.bss |
CNT_UNINITIALIZED_DATA, MEM_READ, MEM_WRITE |
Zero-initialised globals |
.rsrc |
CNT_INITIALIZED_DATA, MEM_READ |
Version info, icons, manifests |
.reloc |
CNT_INITIALIZED_DATA, MEM_DISCARDABLE, MEM_READ |
Base relocations |
.tls |
CNT_INITIALIZED_DATA, MEM_READ, MEM_WRITE |
Thread-local storage |
.edata |
CNT_INITIALIZED_DATA, MEM_READ |
Exports |
Combinations worth a second look
| Observation | Why it stands out |
|---|---|
MEM_WRITE and MEM_EXECUTE on the same section |
Self-modifying code or a runtime unpacking stub. Compilers do not emit this. |
High entropy in .text |
Compiled code has structure. Near-random bytes suggest packing or encryption. |
| Section name not in the standard set | UPX0, .themida, .aspack name their packer outright; single-character or random names are their own signal. |
SizeOfRawData of zero with a large VirtualSize |
Space allocated at load and filled at runtime: the classic unpacking shape. |
| Very few sections on a large file | Packers often collapse everything into one or two. |
.text not the first section |
Unusual layout, usually meaning the file was rewritten after linking. |
Executable .rsrc or .data |
Resources and data have no reason to be executable. |
The caveat that matters
None of these is a verdict. Legitimate software packs itself. Installers, commercial tools with anti-tamper, and anything that embeds a runtime all produce shapes that look identical to the ones above. Plenty of malware, by contrast, is not packed at all and looks entirely ordinary in this table.
These flags are useful for the same reason a version resource is useful: they narrow the question cheaply and they are honest about what they cannot settle. Read them as one input among several, weight them accordingly, and be explicit about the confidence you end up with.
More of this
New posts on what a file tells you before you run it: headers, signatures, manifests, overlays, the fields most inventory never reads. Only when there is something worth the write-up.
We intend to offer a tool that does this across every file on an endpoint. You get one message when you can use it.