Friday, June 8, 2012

Loading COFF Symbols

In this post, i am going to share with you a case that i met while trying to debug a tiny webserver. In the beginning i have to clarify there is no security issue or anti-debug trick discussed here.

The first thing i did with this file was trying to resolve symbol names using IDA 6.2 Demo. Unfortunately,  few symbols were resolved and that was not satisfying at all. OllyDbg couldn't do anything with that, either.

The next thing i tried was opening the executable in Stud_PE. I was amazed to see the "PointerToSymbolTable" and "NumberOfSymbols" fields of the "_IMAGE_FILE_HEADER" structure set to valid values.

So, if COFF debug info exists why does not IDA read it?. Trying to answer this question, i built an executable with COFF debug info using MS VC++ 6 and loaded it in IDA. Strange enough, IDA successfully read COFF debug info from the MSVC-built executable.

Trying harder to solve that, i compared PE headers of both executables. I found that the MSVC-built executable has the "_IMAGE_DEBUG_DIRECTORY" structure set, while the executable of question does not.

Now i figured out that to have IDA reading COFF debug info i have to manually create the "_IMAGE_DEBUG_DIRECTORY" structure.

And here are the steps i followed to create the new "_IMAGE_DEBUG_DIRECTORY" structure:

1) Searching for any 0x1C unused bytes in any section of the executable.  
N.B. 0x1C is the sizeof(_IMAGE_DEBUG_DIRECTORY). A good candidate place is at the end of the resources section.

2) Filling the important fields of this structure with corresponding values. The structure is defined in winnt.h, see below.

The fields in question are:
a) Type. Set it to IMAGE_DEBUG_TYPE_COFF (0x1).

b) SizeOfData. Set it to ( (file size - PointerToSymbolTable) + 0x20 ).

N.B. 0x20 is sizeof(_IMAGE_COFF_SYMBOLS_HEADER). we will see that later.

c) PointerToRawData. Set it to the value of the "PointerToSymbolTable" field.
The rest of fields can be left zeros.
N.B. Coff debug info is usually tailing the executable.

3) Fill  the "VirtualAddress" and "Size" fields in the "_IMAGE_OPTIONAL_HEADER" structure.

4) Displace data at PointerToSymbolTable forward by 0x20 bytes to create gap for the "_IMAGE_COFF_SYMBOLS_HEADER" structure. This can be easily done using HexEditor Neo in the "insert mode".

5) Fill fields of the "_IMAGE_COFF_SYMBOLS_HEADER" structure so that first field is set to the number of COFF symbols (as found in the "NumberOfSymbols" field of the "_IMAGE_FILE_HEADER" structure) and second field is set to 0x20, sizeof(_IMAGE_COFF_SYMBOLS_HEADER). The rest are not important.

After the aforementioned five steps were taken, IDA read COFF debug info successfully.

Loading the fixed executable in OllyDbg v1.10, i again got no symbols resolved. To discover why, i debugged both IDA and OllyDbg v1.10 and found out that OllyDbg uses the "Dbghelp.SymLoadModule" function to read COFF debug info while IDA uses its own routine.

As you can see in the image above, the "Dbghelp.SymGetModuleInfo" function returned IMAGE_DEBUG_TYPE_UNKNOWN in the "SymType" field of the "IMAGEHLP_MODULE" structure and this is why OllyDbg cancelled symbol loading.

I quickly moved to XP SP3 and tried the fixed executable and again nothing. After renaming the dbghelp.dll residing in OllyDbg directory, OllyDbg loaded symbols like a charm.

So, the conclusion here is that Microsoft has dropped support for COFF symbols in newer versions of Dbghelp.dll.

N.B. For more info. about reading COFF debug info, you can refer to this link.

Any comments are very welcome. Also, if you see any mistake or misconception, please don't hesitate to contact me.

You can follow me on Twitter @waleedassar


  1. maybe the dbghelp.dll on your system is actually newer?
    can olly read the symbols for your generated .exe?

    1. After the PE was fixed, IDA read it since IDA uses its own routines.

      For Olly, it needed an older version of Dbghelp.dll e.g. 5.x to read the fixed PE.

      You can try this with executables generated with DevC++.

  2. Yes, COFF symbols are not supported anymore.
    PDB is the expected format now.

    1. cant *drop* support just like that??
      what about older executables that still use that format?!

      What`s the disadvantage of backward compatibility?

  3. There is a nice open source utility CV2PDB which converts old fashioned CodeView symbols to PDB file.

  4. Any idea how would you go about forcing IDA to load external COFF symbols from a LIB for an executable that's statically linked?