首页 >> 操作系统 >> Windows 2000

《Undocumented Windows 2000 Secrets》翻译 --- 第一章(补充:PDB格式)(2)

作者:Kendiv

第一章  Windows 2000对调试技术的支持

翻译:Kendiv ( fcczj@263.net )

更新:Tuesday, May 03, 2005

 

 

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

 

 

.dbg文件的内部结构

Windows NT 4.0组件的符号化信息均保存在扩展名为.dbg的文件中。如果假设符号文件所在的根目录为:d:\winnt\symbols,则组件filename.ext的符号文件的全路径就是:d:\winnt\symbols\filename.dbg。例如,内核符号可以在文件d:\winnt\symbols\exe\ntoskrnl.dbg中找到。Windows 2000也使用.dbg文件。不过,在Windows 2000下符号化的信息被移动到了独立的.pdb文件中。因此,每个Windows 2000组件在符号文件根目录下都有一个相关的ext\filename.dbg和一个附加的ext\filename.pdb文件,除此之外,Windows NT 4.0Windows 2000.dbg文件的内容仍是相同的。

 

 

幸运的是,有关.dbg文件的内部信息的文档还是有一小部分的。Win32 SDK头文件winnt.h提供了核心的常量和类型定义,在MSDN中也有一些有关.dbg文件格式的很有帮助的文章。其中最具有启发性的是Matt Pietrek19993月的MSJMicrosoft Systems JournalMSJ,现在该命位 MSDN Magazine)的“Under the Hood”专栏中发表的文章。基本上,一个.dbg文件包含一个文件头和数据节。这两部分的大小并不固定,并且将来可能会进一步划分。文件头中包含四个主要的分段(subsections):

1.         一个IMAGE_SEPARATE_DEBUG_HEADER结构,该结构以两个标志性字符“DI”开始。(见列表1-13

 

 

2.         一个IMAGE_SECTION_HEADER类型的数组,该数组中的每个结构都位于对应组件的PE文件中。该数组的大小由IMAGE_SEPARATE_DEBUG_HEADERNumberOfSections成员指定。

 

 

3.         一组以零结尾的ANSI字符串(每个ANSI字符串占8个字节),这些字符串均是导出符号的解码格式(undecorated form)。 IMAGE_SEPARATE_DEBUG_HEADERExportedNameSize成员指出了一共有多少个字符串。如果模块没有导出任何符号,ExportedNameSize将为0,并且该分段也将不存在。

 

 

4.         一个IMAGE_DEBUG_DIRECTORY类型的数组,这些结构用来描述随后部分的格式以及它们的位置。IMAGE_SEPARATE_DEBUG_HEADERDebugDirectorySize成员给出了该数组的大小。

 

 

#define IMAGE_SEPARATE_DEBUG_SIGNATURE 0x4944 // "DI"

 

 

typedef struct _IMAGE_SEPARATE_DEBUG_HEADER

{

       WORD Signature;

       WORD Flags ;

       WORD Machine;

       WORD Characteristics;

       DWORD TimeDateStamp;

       DWORD Checksum;

       DWORD ImageBase;

       DWORD SizeOf Image;

       DWORD NurnberOf Sections ;

       DWORD ExportedNamesSize ;

       DWORD DebugDirectorySize;

       DWORD SectionAlignment;

       DWORD Reserved [2 ];

}

IMAGE_SEPARATE_DEBUG_HEADER, *PIMAGE_SEPARATE_DEBUG_HEADER;

 

 

//-----------------------------------------------------------

 

 

#define IMAGE_SIZEOF_SHORT_NAME 8

typedef struct _IMAGE_SECTION_HEADER

       BYTE Name[IMAGE_SIZEOF_SHORT_NAME] ;

       union

       (

              DWORD PhysicalAddress;

              DWORD VirtualSize;

       } Misc;

              DWORD VirtualAddress;

              DWORD SizeOf RawData;

              DWORD PointerToRawData;

              DWORD PointerToRelocations;

              DWORD PointerToLinenumbers ;

              WORD NumberOf Relocations;

              WORD NumberOf Linenumbers ;

              DWORD Characteristics;

       }

IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

 

 

#define IMAGE_DEBUG_TYPE_UNKNOWN       0

#define IMAGE_DEBUG_TYPE_COFF          1

#define IMAGE_DEBUG_TYPE_CODEVIEW      2

#define IMAGE_DEBUG_TYPE_FPO           3

#define IMAGE_DEBUG_TYPE_MISC          4

#define IMAGE_DEBUG_TYPE_EXCEPTION     5

#define IMAGE_DEBUG_TYPE_FIXUP         6

#define IMAGE_DEBUG_TYPE_OMAP_TO_SRC   7

#define IMAGE_DEBUG_TYPE_OMAP_FROM_SRC 8

#define IMAGE_DEBUG_TYPE_BORLAND       9

#define IMAGE_DEBUG_TYPE_RESERVED10    10

#define IMAGE_DEBUG_TYPE_CLSID         11

 

 

//-----------------------------------------------------------

 

 

typedef struct _IMAGE_DEBUG_DIRECTORY

{

       DWORD Characteristics;

       DWORD TimeDateStamp;

       WORD MajorVersion;

       WORD MinorVersion;

       DWORD Type;

       DWORD SizeOfData;

       DWORD AddressOfRawData;

       DWORD PointerToRawData;

}

IMAGE_DEBUG_DIRECTORY , * PIMAGE_DEBUG_DIRECTORY ;

列表1-13.  .dbg文件的文件头结构

 

 

由于文件头中的表头分段的大小不能确定,因此它们在.dbg文件中的绝对位置必须通过它们前面的分段的大小来计算出来。一个.dbg文件分析器通常采用如下算法:

l         IMAGE_SEPARATE_DEBUG_HEADER结构总是位于文件的开始位置。

 

 

l         第一个IMAGE_SECTION_HEADER结构紧随IMAGE_SEPARATE_DEBUG_HEADER结构之后,因此总是可以在文件偏移量为0x30的位置找到该结构。

 

 

l         IMAGE_SECTION_HEADER结构的大小与该结构的个数相乘然后加上第一个IMAGE_SECTION_HEADER结构在文件中的偏移量就可得到第一个导出符号的偏移量。即第一个导出字符串的位置是:0x30+(NumberOfSections*0x28)

 

 

l         通过将ExportedNameSize与导出符号分段的偏移量相加即可得到第一个IMAGE_DEBUG_DIRECTORY结构的位置。

 

 

l         通过IMAGE_DEBUG_DIRECTORY项可确定.dbg文件中剩余数据项的偏移量。PointerToRawSizeOfData成员分别指出了相关数据块的偏移量和大小。

 

 

列表1-13给出了IMAGE_DEBUG_TYPE_*结构的定义。这些结构反映了.dbg文件中所包含的多种数据格式。不过,Windows NT 4.0的符号文件通常仅包含这些结构中的四个:IMAGE_DEBUG_TYPE_COFFIMAGE_DEBUG_TYPE_CODEVIEWIMAGE_DEBUG_TYPE_FPOIMAGE_DEBUG_TYPE_MISCWindows 2000.dbg文件通常会增加IMAGE_DEBUG_TYPE_OMAP_TO_SRCIMAGE_DEBUG_TYPE_OMAP_FROM_SRC以及一个未文档化的类型ID0x1000的结构。如果你仅对解析或浏览符号感兴趣,那么你只需要了解目录项结构:IMAGE_DEBUG_TYPE_CODEVIEWIMAGE_DEBUG_TYPE_OMAP_TO_SRCIMAGE_DEBUG_TYPE_OMAP_FROM_SRC

 

 

本书的CD中包含一个示例DLL----w2k_img.dll,该DLL用于解析.dbg.pdb文件并导出了多个用于开发内核调试工具的重要函数。可在本书的\src\w2k_img目录下找到该DLL的源代码。w2k_img.dll的一个重要属性是:它所有Win32平台上都可以运行。这不只包括Windows 2000Windows NT 4.0还包括Windows 9x。像所有Win32世界中的好市民一样,这个DLL的每个函数为支持ANSIUnicode字符串均提供了独立的接口。默认情况下,客户端使用ANSI版的函数。如果应用程序的源文件中包含了#define UICODE,那么将选择Unicode版的函数。运行于Win32平台上的程序最好选择ANSI版的函数。针对Windows NT/2000开发的程序则可选择Unicode版的函数以获取更好的性能。

 

 

在本书CD中还包含一个名为“SBS Windows 2000 CodeView Decompiler”的示例程序,可在CD\src\w2k_cv目录下找到该程序的Visual C/C++项目文件。该程序是一个简单的用于分析.dbg.pdb文件,并在Windows控制台中显示它们的内容。在阅读本节时,你可以使用该程序来查看我们正在讨论的这些数据结构。w2k_cv.exe大量使用了w2k_img.dll中的API函数。

 

 

列表1-14给出了w2k_img.h中定义的一个最基本的数据结构---IMG_DBG,该结构是由.dbg文件头中的前两个分段串联而成,也就是说,该结构由一个大小固定的基本表头和一组PE节的表头构成。给定PE节表头的数目就可通过IMG_DBG__()宏计算出该结构的实际大小。这一大小还确定了导出符号节(exported-names subsections)在文件中的偏移量。

 

 

W2k_img.dll中有几个函数需要一个指向已初始化的IMG_DBG结构的指针。imgDbgLoad()函数可分配一个IMG_DBG结构,并对该结构进行适当的初始化(该函数会用指定的.dbg文件的内容填充该结构)。imgDbgLoad()会对数据进行严格的完整性检查以确定指定的.dbg文件是有效和完整的。imgDbgLoad()函数返回的IMG_DBG结构可传递给多个分析函数,通过这些分析函数我们可得到一些经常使用的线性地址。例如,imgDbgExports()函数可计算出导出符号节(该节紧随IMAGE_SECTION_HEADER数组之后)的线性地址。该函数还可通过扫描整个导出符号节来统计有效符号名的个数,并可通过pdcount参数来返回统计的结果(可选)。

 

 

typedef struct _IMG_DBG

{

       IMAGE_SEPARATE_DEBUG_HEADER  Header;

       IMAGE_SECTION_HEADER         aSections[];

}

IMG_DBG, *PIMG_DBG, **PPIMG_DBG;

 

 

#define IMG_DBG_ sizeof(IMG_DBG)

#define IMG_DBG__(_n) (IMG_DBG_+((_n)*IMAGE_SECTION_HEADER_))

 

 

#define IMG_DBG_DATA(_p,_d) \

        ((PVOID)((PBYTE)(_p) + (_d)->PointerToRawData))

列表1-14.  IMG_DBG结构以及相关的宏定义

 

 

 

 

PVOID WINAPI imgDbgLoadA (PBYTE  pbPath,

                          PDWORD pdSize)

    {

    DWORD dOffset = (pdSize != NULL ? *pdSize : 0);

    DWORD dSize   = dOffset;

    PBYTE pbData  = imgFileLoadA (pbPath, &dSize);

 

 

    if ((pbData != NULL) &&

        (!imgDbgVerify ((PIMG_DBG) (pbData + dOffset), dSize)))

        {

        pbData = imgMemoryDestroy (pbData);

        }

    if (pdSize != NULL) *pdSize = dSize;

    return pbData;

    }

 

 

// -----------------------------------------------------------------

 

 

PVOID WINAPI imgDbgLoadW (PWORD  pwPath,

                          PDWORD pdSize)

    {

    DWORD dOffset = (pdSize != NULL ? *pdSize : 0);

    DWORD dSize   = dOffset;

    PBYTE pbData  = imgFileLoadW (pwPath, &dSize);

 

 

    if ((pbData != NULL) &&

        (!imgDbgVerify ((PIMG_DBG) (pbData + dOffset), dSize)))

        {

        pbData = imgMemoryDestroy (pbData);

        }

    if (pdSize != NULL) *pdSize = dSize;

    return pbData;

    }

 

 

// -----------------------------------------------------------------

 

 

PBYTE WINAPI imgDbgExports (PIMG_DBG pid,

                            PDWORD   pdCount)

    {

    DWORD i, j;

    DWORD dCount    = 0;

    PBYTE pbExports = NULL;

 

 

    if (pid != NULL)

        {

        pbExports = (PBYTE) pid->aSections

                    + (pid->Header.NumberOfSections

                       * IMAGE_SECTION_HEADER_);

 

 

        for (i = 0; i < pid->Header.ExportedNamesSize; i = j)

            {

            if (!pbExports [j = i]) break;

 

 

            while ((j < pid->Header.ExportedNamesSize) &&

                   pbExports [j++]);

 

 

            if ((j > i) && (!pbExports [j-1])) dCount++;

            }

        }

    if (pdCount != NULL) *pdCount = dCount;

    return pbExports;

    }

列表1-15.  imgDbgLoad()imgDbgExports()函数

 

 

列表1-16定义了两个可根据ID(这里的ID形如:IMAGE_DEBGU_TYPE_*)来定位相应的调试目录项(debug directory entry)的API函数。imgDbgDirectories()返回IMAGE_DEBUG_DIRECTORY数组的基地址,imgDbgDirectory()返回指向给定ID所对应的第一个目录项的指针,如果不存在这样的目录项,则返回NULL

 

 

PIMAGE_DEBUG_DIRECTORY WINAPI imgDbgDirectories (PIMG_DBG pid,

                                                 PDWORD   pdCount)

    {