Figure 1   DumpBining AdvAPI.DBG

Type
Size
RVA
Pointer
coff
3984F 00000000
2060

misc
110 00000000
3B8B0
Image Name: obj\i386\advapi32.dll
fpo
4A20 00000000
3B9C0

cv
22660 00000000
403E0
Format: NB09


Figure 3   IMAGE_SEPARATE_DEBUG_HEADER Fields

.DBG File
Executable File Equivalent
Signature
N/A (Must be 0x4944 -> "DI")
Flags
N/A
Machine
IMAGE_FILE_HEADER.Machine
Characteristics
IMAGE_FILE_HEADER.Characteristics
TimeDateStamp
IMAGE_FILE_HEADER.TimeDateStamp
CheckSum
IMAGE_OPTIONAL_HEADER.CheckSum
ImageBase
IMAGE_OPTIONAL_HEADER.ImageBase
SizeOfImage
IMAGE_OPTIONAL_HEADER.SizeOfImage
NumberOfSections
IMAGE_FILE_HEADER.NumberOfSections
ExportedNamesSize
N/A
DebugDirectorySize
N/A
SectionAlignment
IMAGE_OPTIONAL_HEADER.SectionAlignment
Reserved[2]
N/A


Figure 4   IMAGE_DEBUG_DIRECTORY Fields

Field
Interpretation
Characteristics
Always 0, not used
TimeDateStamp
Matches TimeDateStamp of executable
MajorVersion
Always 0, except for OMAP information
MinorVersion
Always 0
Type
IMAGE_DEBUG_TYPE_xxx (see WINNT.H)
SizeOfData
Size of debug information
AddressOfRawData
0 if debug info in separate file, otherwise relative virtual address
PointerToRawData
Offset in .DBG file to debug information


Figure 5    CoClassSymsDbgFile.cpp

 //==================================================
 // CoClassSymsDbgFile - Matt Pietrek 1999
 // Microsoft Systems Journal, March 1999
 // FILE: CoClassSymsDbgFile
 //==================================================
 #include <windows.h>
 #include <stdio.h>
 #include <imagehlp.h>
 #include <stdlib.h>
 #include <stddef.h>
 #include "CoClassSymsCallouts.h"
 
 // From Platform SDK, \SAMPLES\SDKTOOLS\IMAGE\INCLUDE\ directory
 #include "cvexefmt.h"
 
 //============================================================================
 
 FILE * g_pOutFile = 0;
 LOADED_IMAGE g_liExe;
 unsigned long g_CVInfoOffset;
 unsigned g_cbPublicSymbols = sizeof(OMFSymHash);
 unsigned g_lfoGlobalPub = 0;
 unsigned g_lfoSstModule = 0;
 unsigned g_cbSstModule = 0;
 
 char g_szModuleName[] = { '9', "BobIn2020" };    // Hardcoded length!
 
 //============================================================================
 
 void CalculateCVInfoOffsts( void );
 
 void WriteDBGHeader( FILE * pFile, PIMAGE_NT_HEADERS pNtHdr );
 void WriteSectionTable( FILE * pFile, PIMAGE_SECTION_HEADER pSectionHdr,
                         ULONG nSections );
 void WriteDbgDirectory(FILE * pFile, PIMAGE_NT_HEADERS pNtHdr, DWORD cbCVInfo);
 
 unsigned AddPublicSymbolSymbol32( unsigned short seg, unsigned long offset,
                                  char * pszSymbol, FILE * pFile );
 
 unsigned WriteRemainingCVInfo( FILE * pFile );
 
 BOOL WriteSstModule( FILE *pFile );
 
 unsigned WriteSegMap( FILE * pFile, unsigned long fileOffset,
                      PIMAGE_SECTION_HEADER pSectionHdr, ULONG nSections );
 
 //============================================================================
 // First API called by CoClassSyms.  Does initial work to prepare for writing
 // the .DBG file.
 //============================================================================
 BOOL __stdcall CoClassSymsBeginSymbolCallouts( PSTR pszExecutable )
 {
     // Map the executable into memory, since we're going to need all sorts
     // of information from it, such as the section table.
     if ( !MapAndLoad( pszExecutable, 0, &g_liExe, FALSE, TRUE ) )
     {
         // Ooops!  Something's wrong.  Bail out.
         printf( "Unable to access or load executable\n" );
         return FALSE;
     }
 
     // If we get here, the executable looks OK.  Synthesize the name of the
     // .DBG file we'll be creating by stripping off the existing extension and
     // adding ".DBG"
     char szExeBaseName[MAX_PATH];
     char szDBGFileName[MAX_PATH];
     _splitpath( pszExecutable, 0, 0, szExeBaseName, 0 );
     sprintf( szDBGFileName, "%s.DBG", szExeBaseName );
 
     // Open the new .DBG file for writing        
     g_pOutFile = fopen( szDBGFileName, "wb" );
     if ( !g_pOutFile )
     {
         printf( "Error opening output file\n" );
         return FALSE;
     }
 
     // Figure out where most of the various CV fields and structures will be
     // located in the .DBG file.  Saves the values away in global variables...
     CalculateCVInfoOffsts();
     
     return TRUE;
 }
 
 //============================================================================
 // API called once for each public symbol.  The passed symbol name and address
 // is written as an S_PUB32 record to the sstGlobalPub subsection
 //============================================================================
 BOOL __stdcall  CoClassSymsAddSymbol(
         unsigned short section,
         unsigned long offset,
         PSTR pszSymbolName )
 {
     g_cbPublicSymbols +=
         AddPublicSymbolSymbol32( section, offset, pszSymbolName, g_pOutFile );
 
     return TRUE;
 }
 
 //============================================================================
 // API called when all the public symbols have been written to the .DBG file.
 // What remains is to write the rest of the CV info, the .DBG file header
 // stuff, and then clean up.
 //============================================================================
         
 BOOL __stdcall CoClassSymsSymbolsFinished( void )
 {
     DWORD cbCVInfo;
     cbCVInfo = WriteRemainingCVInfo( g_pOutFile );
     
     WriteDBGHeader( g_pOutFile, g_liExe.FileHeader );
     WriteSectionTable( g_pOutFile, g_liExe.Sections, g_liExe.NumberOfSections );
     WriteDbgDirectory( g_pOutFile, g_liExe.FileHeader, cbCVInfo );
             
     fclose( g_pOutFile );
 
     UnMapAndLoad( &g_liExe );
         
     return TRUE;
 }
 
 //=============================================================================
 // Calculates all the offsets to various places in the CV info that we'll 
 // initially need.  Other offsets can only be calculated later, after all the
 // public symbols have been processed and added.
 //=============================================================================
 void CalculateCVInfoOffsts( void )
 {
     //
     // Calculate where the CV symbol table will begin.  This will be after:
     //        IMAGE_SEPARATE_DEBUG_HEADER
     //        Section table (IMAGE_SECTION_HEADER * nSections)
     //        IMAGE_DEBUG_DIRECTORY
     //
     g_CVInfoOffset = sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
         + (g_liExe.NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
         + sizeof(IMAGE_DEBUG_DIRECTORY);
 
     unsigned long cDir = 3;    // An sstModule, an sstGlobalPub and a sstSegMap
 
     // Calculate where the sstModule subsection will go.  This is after:
     //        'NB09'
     //        Offset to subsection directory
     //        Subsection directory header
     //        Subsection directory entries (3 subsections)
     g_lfoSstModule = sizeof(OMFSignature) + sizeof(OMFDirHeader) +
                             (cDir * sizeof(OMFDirEntry));
 
     // Calculate the size of the sstModule, which is dependent on the number
     // of sections in the executable
     g_cbSstModule=  offsetof(OMFModule,SegInfo)
                     + (g_liExe.NumberOfSections * sizeof(OMFSegDesc))
                     + sizeof(g_szModuleName);
 
     // Calculate where the sstGlobalPub subsection will go.  This is
     // immediately after the sstModule subsection
     g_lfoGlobalPub = g_lfoSstModule + g_cbSstModule;    
 }
 
 //=============================================================================
 // Writes a public symbol (S_PUB32) record to the next spot in the
 // sstGlobalPub area.  Called by CoClassSymsAddSymbol, which is the exported
 // API for adding public symbols.
 //=============================================================================
 unsigned AddPublicSymbolSymbol32( unsigned short seg, unsigned long offset,
                                   char * pszSymbol, FILE * pFile )
 {
     BYTE buffer[512];
     
     PUBSYM32* pPubSym32 = (PUBSYM32*)buffer;
 
     DWORD cbSymbol = lstrlen( pszSymbol );
     DWORD realRecordLen = sizeof(PUBSYM32) + cbSymbol;
             
     pPubSym32->reclen = (unsigned short)(realRecordLen - 2);
     pPubSym32->rectyp = S_PUB32;
     pPubSym32->off = offset;
     pPubSym32->seg = seg;
     pPubSym32->typind = 0;
     pPubSym32->name[0] = (unsigned char)cbSymbol;
     lstrcpy( (PSTR)&pPubSym32->name[1], pszSymbol );
     
     fseek(  pFile,
             g_CVInfoOffset + g_lfoGlobalPub + g_cbPublicSymbols,
             SEEK_SET );
 
     fwrite( pPubSym32, realRecordLen, 1, pFile );
     
     return realRecordLen;
 }
 
 //=============================================================================
 // After all the public symbol (S_PUB32) records have been written, this
 // routine writes the header at the beginning of the sstGlobalPub area
 //=============================================================================
 void WritePublicSymbolsHeader( FILE * pFile )
 {
     OMFSymHash omfSymHash;
     
     omfSymHash.cbSymbol = g_cbPublicSymbols - sizeof(OMFSymHash);
 
     omfSymHash.symhash = 0;        // No symbol or address hash tables...
     omfSymHash.addrhash = 0;
     omfSymHash.cbHSym = 0;
     omfSymHash.cbHAddr = 0;
 
     // Seek to begining of sstGlobalPub to write OMFSymHash record
     fseek( pFile, g_CVInfoOffset + g_lfoGlobalPub, SEEK_SET );
     fwrite( &omfSymHash, sizeof(omfSymHash), 1, pFile );
 }
 
 //=============================================================================
 // Writes all CV info, except for the actual public symbols (S_PUB32)
 // records.  It writes the CV subsection directory, as well as the sstModule
 // and sstSegMap blobs.
 //=============================================================================
 unsigned WriteRemainingCVInfo( FILE * pFile )
 {
     unsigned long cDir = 3;    // An sstModule, an sstGlobalPub and a sstSegMap
     unsigned long cbSegMap;
     unsigned long lfoSegMap;
     lfoSegMap = g_lfoGlobalPub + g_cbPublicSymbols;
     
     WritePublicSymbolsHeader( g_pOutFile );
 
     WriteSstModule( pFile );
 
      cbSegMap = WriteSegMap( pFile, g_CVInfoOffset + lfoSegMap,
                              g_liExe.Sections, g_liExe.NumberOfSections );
 
     // Restore current position to write rest of dir entries
     fseek( pFile, g_CVInfoOffset, SEEK_SET );
 
     //
     // Write 'NB09' signature and offset to subsection directory
     //
     OMFSignature omfsig = { {'N','B','0','9'}, sizeof(omfsig) };
     fwrite( &omfsig, sizeof(omfsig), 1, pFile );
 
     //
     // Write subsection directory header
     //        
     OMFDirHeader omfdirhdr;
 
     omfdirhdr.cbDirHeader = sizeof(omfdirhdr);
     omfdirhdr.cbDirEntry = sizeof(OMFDirEntry);
     omfdirhdr.cDir = cDir;
     omfdirhdr.lfoNextDir = 0;
     omfdirhdr.flags = 0;
 
     fwrite( &omfdirhdr, sizeof(omfdirhdr), 1, pFile );
 
     //
     // Write subsection directory entry for the sstModule
     //
     OMFDirEntry omfdirentry;
     omfdirentry.SubSection = sstModule;
     omfdirentry.iMod = 1;
     omfdirentry.lfo = g_lfoSstModule;
     omfdirentry.cb = g_cbSstModule;
     fwrite( &omfdirentry, sizeof(omfdirentry), 1, pFile );
 
     //
     // Write subsection directory entry for the sstGlobalPub
     //
     omfdirentry.SubSection = sstGlobalPub;
     omfdirentry.iMod = 0xFFFF;
     omfdirentry.lfo = g_lfoGlobalPub;
     omfdirentry.cb = g_cbPublicSymbols;
     fwrite( &omfdirentry, sizeof(omfdirentry), 1, pFile );
 
     //
     // Write subsection directory entry for the sstSegMap
     //
     omfdirentry.SubSection = sstSegMap;
     omfdirentry.iMod = 0xFFFF;
     omfdirentry.lfo = lfoSegMap;
     omfdirentry.cb = cbSegMap;
     fwrite( &omfdirentry, sizeof(omfdirentry), 1, pFile );
 
     return lfoSegMap + cbSegMap;
 }
 
 //=============================================================================
 // Writes the sstModule info, which precedes the sstGlobalPub info in the file
 //=============================================================================
 BOOL WriteSstModule( FILE *pFile )
 {
     // Seek to place to write sstModule at
     fseek( pFile, g_CVInfoOffset + g_lfoSstModule, SEEK_SET );
 
     //
     // Initialize and write the sstModule header
     //
     OMFModule omfmodule;
     
     omfmodule.ovlNumber = 0;
     omfmodule.iLib = 0;
     omfmodule.cSeg = (unsigned short)g_liExe.NumberOfSections;
     omfmodule.Style[0] = 'C';
     omfmodule.Style[1] = 'V';
 
     fwrite( &omfmodule, offsetof(OMFModule,SegInfo), 1, pFile );
 
     //
     // Initialize and write an OMFSegDesc for each executable section
     //
     for ( unsigned i = 0; i < g_liExe.NumberOfSections; i++ )
     {
         OMFSegDesc omfsegdesc;
 
         omfsegdesc.Seg = i+1;
         omfsegdesc.pad = 0;
         omfsegdesc.Off = 0;
         omfsegdesc.cbSeg = g_liExe.Sections[i].Misc.VirtualSize;
 
         fwrite( &omfsegdesc, sizeof(omfsegdesc), 1, pFile );    
     }
 
     // Subsection ends with the name of the module
     fwrite( g_szModuleName, sizeof(g_szModuleName), 1, pFile );
 
     return TRUE;        
 }
 
 //=============================================================================
 // Writes the sstSegMap info, which follows the sstGlobalPub info in the file
 //=============================================================================    
 unsigned WriteSegMap( FILE * pFile, unsigned long fileOffset,
                       PIMAGE_SECTION_HEADER pSectionHdr, ULONG nSections )
 {
     // Seek to place to write sstSegMap at
     fseek( pFile, fileOffset, SEEK_SET );
 
     //
     // Initialize and write the sstSegMap header
     //
     OMFSegMap omfSegMap = {(unsigned short)nSections,(unsigned short)nSections};
 
     fwrite( &omfSegMap, sizeof(OMFSegMap), 1, pFile );
 
     //
     // Initialize and write an OMFSegMapDesc for each executable section
     //
     for ( unsigned i = 1; i <= nSections; i++ )
     {
         OMFSegMapDesc omfSegMapDesc;
 
         omfSegMapDesc.flags = 0;
         omfSegMapDesc.ovl = 0;
         omfSegMapDesc.group = 0;
         omfSegMapDesc.frame = i;
         omfSegMapDesc.iSegName = 0xFFFF;
         omfSegMapDesc.iClassName = 0xFFFF;
         omfSegMapDesc.offset = 0;
         omfSegMapDesc.cbSeg = pSectionHdr[i-1].Misc.VirtualSize;
         
         fwrite( &omfSegMapDesc, sizeof(OMFSegMapDesc), 1, pFile );
     }
 
     return sizeof(OMFSegMap) + (sizeof(OMFSegMapDesc) * nSections);
 }
 
 //============================================================================
 // Writes the 'DI' (IMAGE_SEPARATE_DEBUG_HEADER) header at the very beginning
 // of the file
 //============================================================================
 void WriteDBGHeader( FILE * pFile, PIMAGE_NT_HEADERS pNtHdr )
 {
     fseek( pFile, 0, SEEK_SET );
     
     IMAGE_SEPARATE_DEBUG_HEADER    isdh;
         
     isdh.Signature = IMAGE_SEPARATE_DEBUG_SIGNATURE;
     isdh.Flags = 0;
     isdh.Machine = pNtHdr->FileHeader.Machine;
     isdh.Characteristics = pNtHdr->FileHeader.Characteristics;
     isdh.TimeDateStamp = pNtHdr->FileHeader.TimeDateStamp;
     isdh.CheckSum = pNtHdr->OptionalHeader.CheckSum;
     isdh.ImageBase = pNtHdr->OptionalHeader.ImageBase;
     isdh.SizeOfImage = pNtHdr->OptionalHeader.SizeOfImage;
     isdh.NumberOfSections = pNtHdr->FileHeader.NumberOfSections;
     isdh.ExportedNamesSize = 0;
     isdh.DebugDirectorySize = sizeof(IMAGE_DEBUG_DIRECTORY);
     isdh.SectionAlignment = pNtHdr->OptionalHeader.SectionAlignment;
 
     fwrite( &isdh, sizeof(isdh), 1, pFile );
 }
 
 //============================================================================
 // Writes the section table that immediately follows the 'DI' header
 //============================================================================
 void WriteSectionTable( FILE * pFile, PIMAGE_SECTION_HEADER pSectionHdr,
                         ULONG nSections )
 {
     // Basically, a block copy of the section table out of the executable image
     fwrite( pSectionHdr, sizeof(IMAGE_SECTION_HEADER), nSections, pFile );
 }
 
 //============================================================================
 // Writes the debug directory that follows the section table
 //============================================================================
 void WriteDbgDirectory(FILE * pFile, PIMAGE_NT_HEADERS pNtHdr, DWORD cbCVInfo)
 {
     IMAGE_DEBUG_DIRECTORY idd;
 
     idd.Characteristics = 0;
     idd.TimeDateStamp = pNtHdr->FileHeader.TimeDateStamp;
     idd.MajorVersion = 0;
     idd.MinorVersion = 0;
     idd.Type = IMAGE_DEBUG_TYPE_CODEVIEW;
     idd.SizeOfData = cbCVInfo;
     idd.AddressOfRawData = 0;
     idd.PointerToRawData = g_CVInfoOffset;
 
     fwrite( &idd, sizeof(idd), 1, pFile );
 }