Skip to content

Advance the symbol cursor in the Weak Externals aux handler - #228

Open
arpitjain099 wants to merge 1 commit into
trailofbits:masterfrom
arpitjain099:fix/aux-format3-offset
Open

arpitjain099 wants to merge 1 commit into
trailofbits:masterfrom
arpitjain099:fix/aux-format3-offset

Conversation

@arpitjain099

Copy link
Copy Markdown

Every COFF auxiliary record is SYMTAB_RECORD_LEN, 18 bytes. Four of the five format handlers advance offset by exactly that. Format 3 advances it by 10, because the two readDword calls are issued against the same offset with no advance between or after them:

// Read line number
if (!readDword(p->fileBuffer, offset, asym.tagIndex)) {         // reads offset+0..3
  ...
}
// Read characteristics
if (!readDword(p->fileBuffer, offset, asym.characteristics)) {  // reads offset+0..3 again
  ...
}
// Skip unused 10 bytes
offset += sizeof(std::uint8_t) * 10;

Counting the siblings, each of which comes to 18: Function Definitions is 4+4+4+6, .bf/.ef is 4+2+6+6, Files is a single 18, Section Definitions is 4+2+2+4+2+1+3. Weak Externals is 0+0+10.

So there are two defects in six lines. The cursor desynchronizes by 8 bytes for the remainder of that symbol's auxiliary records, and characteristics is loaded from the tagIndex slot, so it can never hold anything but a second copy of tagIndex. The // Read line number comment above the first read is copy-paste from the Format 2 handler.

Why it has not been caught: getSymbolTable returns immediately when FileHeader.PointerToSymbolTable == 0, which is true of every normally linked PE image, so the whole symbol parser is dead code for ordinary input. Of the 219 valid seeds in the corkami corpus, 12 have a nonzero pointer and all 12 carry a garbage value that fails the first readQword and aborts the parse before reaching any auxiliary branch.

Verification uses the project's own diagnostic. Built at 9e4e358 with -DPEPARSE_LIBRARY_WARNINGS=ON, which enables the offset != nextSymbolOffset check, and crafted a PE from stock tests/assets/example.exe with a 10-record symbol table appended and PointerToSymbolTable/NumberOfSymbols patched to point at it. It holds one symbol of each of the five auxiliary formats, each with numberOfAuxSymbols = 1, so a correct parser walks a uniform 36-byte stride and says nothing.

before   Warning: Invalid internal offset (current: 0x1d264, expected: 0x1d26c)
after    (no warning)

One warning for one of the five symbols, and the arithmetic pins it to the weak external: the record header ends at 0x1d25a, 0x1d25a + 10 = 0x1d264 is the reported current, 0x1d25a + 18 = 0x1d26c is the expected, and the 8-byte gap is exactly the two missing advances. The other four formats are silent on both sides, which is the control: the warning path works and does not fire for them.

dump-pe on the stock example.exe is unchanged.

Every COFF auxiliary record is SYMTAB_RECORD_LEN, 18 bytes. Four of the five
format handlers advance offset by exactly that. Format 3 advances it by 10,
because the two readDword calls are issued against the same offset with no
advance between or after them:

  f1 Function Definitions   4 + 4 + 4 + 6          = 18
  f2 .bf and .ef            4 + 2 + 6 + 6          = 18
  f3 Weak Externals         0 + 0 + 10             = 10
  f4 Files                  18                     = 18
  f5 Section Definitions    4 + 2 + 2 + 4 + 2 + 1 + 3 = 18

Two consequences in six lines. The cursor desynchronizes by 8 bytes for the
rest of that symbol's auxiliary records, and characteristics is loaded from
the tagIndex slot so it can never differ from tagIndex. The comment
"// Read line number" above the tagIndex read is copy-paste from the Format 2
handler.

This branch is rarely exercised: getSymbolTable returns immediately when
PointerToSymbolTable is 0, which is the case for every normally linked PE
image, and none of the 219 corkami seeds reaches it with a well-formed table.

Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant