QL-Win/QuickLook · error · PEImageParseException

Section headers incomplete.

Error message

Section headers incomplete.

What it means

After the data directory table, the PE format stores a section header table consisting of NumberOfSections entries (from the COFF header), each exactly 40 bytes. The parser checks that NumberOfSections * 40 bytes remain; if not, the section header table is incomplete and section names, virtual addresses, and raw data pointers cannot be read.

Source

Thrown at QuickLook.Plugin/QuickLook.Plugin.PEViewer/PEImageParser/PEImage.cs:203

                NumberOfRvaAndSizes = reader.ReadUInt32()
            };
        }
        else if (magic == 0x107)
        {
            throw new PEImageParseException((int)reader.BaseStream.Position - 2, "Optional header for ROM's is not supported.");
        }
        else
        {
            throw new PEImageParseException((int)reader.BaseStream.Position - 2, "Optional header magic value of '0x" + magic.ToString("x4") + "' unknown.");
        }

        // Data Directories
        if (reader.BaseStream.Length - reader.BaseStream.Position < OptionalHeader.NumberOfRvaAndSizes * 8) throw new PEImageParseException((int)reader.BaseStream.Position, "Data directories incomplete.");

        OptionalHeader.DataDirectories = Create.Array((int)OptionalHeader.NumberOfRvaAndSizes, i => new ImageDataDirectory((ImageDataDirectoryName)i, reader.ReadUInt32(), reader.ReadUInt32()));

        // Section Headers
        if (reader.BaseStream.Length - reader.BaseStream.Position < CoffHeader.NumberOfSections * 40) throw new PEImageParseException((int)reader.BaseStream.Position, "Section headers incomplete.");

        Sections = Create
            .Enumerable(CoffHeader.NumberOfSections, i => new ImageSectionHeader
            {
                Name = reader.ReadBytes(8).TakeWhile(c => c != 0).ToArray().ToUTF8String(),
                VirtualSize = reader.ReadUInt32(),
                VirtualAddress = reader.ReadUInt32(),
                SizeOfRawData = reader.ReadUInt32(),
                PointerToRawData = reader.ReadUInt32(),
                PointerToRelocations = reader.ReadUInt32(),
                PointerToLineNumbers = reader.ReadUInt32(),
                NumberOfRelocations = reader.ReadUInt16(),
                NumberOfLineNumbers = reader.ReadUInt16(),
                Characteristics = (ImageSectionFlags)reader.ReadUInt32()
            })
            .Select(header =>
            {
                return new ImageSection(header);

View on GitHub (pinned to cb5d9c429c)

Solutions

  1. Use a PE viewer to verify NumberOfSections in the COFF header matches the actual number of sections present
  2. Re-download the file and verify its integrity
  3. Pre-validate by reading NumberOfSections from the COFF header and checking that PEHeaderOffset + 4 + 20 + optionalHeaderSize + dataDirectorySize + NumberOfSections * 40 does not exceed the file length
  4. Wrap the parse call in try-catch(PEImageParseException) to handle malformed PE files gracefully
Defensive patterns

Strategy: validation

Validate before calling

// Sanity-check NumberOfSections against remaining file size
static bool HasValidSectionCount(string path)
{
    byte[] b = File.ReadAllBytes(path);
    if (b.Length < 0x40) return false;
    int peOff = BitConverter.ToInt32(b, 0x3C);
    int coffOff = peOff + 4;
    if (coffOff + 20 > b.Length) return false;
    // NumberOfSections is at COFF offset + 2 (2 bytes)
    ushort numSections = BitConverter.ToUInt16(b, coffOff + 2);
    // Each section header is 40 bytes; typical executables have < 20 sections
    return numSections > 0 && numSections <= 96
        && (long)b.Length >= (long)coffOff + 20 + (long)numSections * 40;
}

Try / catch

try
{
    var image = PEImage.FromFile(path);
}
catch (PEImageParseException ex) when (ex.Message.Contains("Section headers"))
{
    // NumberOfSections is corrupt or file is truncated before section headers
    logger.Warn($"Corrupt section header table: {ex.Message} at offset {ex.Offset}");
}

Prevention

When it happens

Trigger: CoffHeader.NumberOfSections multiplied by 40 exceeds the remaining bytes in the stream. This happens when NumberOfSections is corrupted to an abnormally large value, or when the file is physically truncated before all section headers are present.

Common situations: A corrupted NumberOfSections field in the COFF header (byte damage causing it to be much larger than the actual section count); a file truncated between the data directories and the end of the section header table; a malformed binary from an obfuscator or packer.

Related errors


AI-assisted analysis of QL-Win/QuickLook@cb5d9c429c (2026-08-13). Data as JSON: /api/errors/31a8463c2a042c9c. Report an issue: GitHub.