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.

pe-formatstatic-analysisreference

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.

One field, any address. Those two things and nothing else, and replying to any of it removes you.