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

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

翻译:Kendiv ( [email protected] )

更新: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)

    {

    DWORD                  dCount = 0;

    PIMAGE_DEBUG_DIRECTORY pidd   = NULL;

 

 

    if (pid != NULL)

        {

        pidd   = (PIMAGE_DEBUG_DIRECTORY)

                 ((PBYTE) pid

                  + IMG_DBG__ (pid->Header.NumberOfSections)

                  + pid->Header.ExportedNamesSize);

 

 

        dCount = pid->Header.DebugDirectorySize

                 / IMAGE_DEBUG_DIRECTORY_;

        }

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

    return pidd;

    }

 

 

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

 

 

PIMAGE_DEBUG_DIRECTORY WINAPI imgDbgDirectory (PIMG_DBG pid,

                                               DWORD    dType)

    {

    DWORD                  dCount, i;

    PIMAGE_DEBUG_DIRECTORY pidd = NULL;

 

 

    if ((pidd = imgDbgDirectories (pid, &dCount)) != NULL)

        {

        for (i = 0; i < dCount; i++, pidd++)

            {

            if (pidd->Type == dType) break;

            }

        if (i == dCount) pidd = NULL;

        }

    return pidd;

    }

列表1-16.  imgDbgDirectories()imgDbgDirectory() API函数

 

 

imgDbgDirectories()函数可用来查找.dbg文件中的CodeView数据。列表1-17中的imgDbgCv()函数完成了这一任务。imgDbgCv()函数使用IMAGE_DEBUG_TYPE_CODEVIEW调用imgDbgDirectories(),并使用IMG_DBG_DATA()宏将IMAGE_DEBUG_DIRECTORY项提供的偏移量转化为绝对线性地址。该宏只是简单的将IMG_DBG结构的基地址与给定的偏移量相加然后再将结果转型(typecast)为PVOID类型的指针。如果pdSize参数不为NULL,则imgDbgCv()CodeView子节的大小保存到该参数中。接下来我们将讨论CodeView数据的内部结构。

 

 

针对其他数据子节(data subsection)的函数非常类似。列表1-18给出了imgDbgOmapToSrc()imgDbgOmapFromSrc()函数以及它们使用的OMAP_TO_SRCOMAP_FROM_SRC结构。稍后,我们将使用这些结构来计算位于CodeView子节中的符号的线性地址。因为OMAP数据结构是一个长度固定的数组,所以这两个API函数并不返回子节的大小,而是计算数组中的项数。该项数将被保存到*pdcount参数中(如果该参数不为NULL的话)。

 

 

PCV_DATA WINAPI imgDbgCv (PIMG_DBG pid,

                          PDWORD   pdSize)

    {

    PIMAGE_DEBUG_DIRECTORY pidd;

    DWORD                  dSize = 0;

    PCV_DATA               pcd   = NULL;

 

 

    if ((pidd = imgDbgDirectory (pid, IMAGE_DEBUG_TYPE_CODEVIEW))

        != NULL)

        {

        pcd   = IMG_DBG_DATA (pid, pidd);

        dSize = pidd->SizeOfData;

        }

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

    return pcd;

    }

列表1-17.  imgDbgCv()函数

 

 

typedef struct _OMAP_TO_SRC

    {

    DWORD dTarget;

    DWORD dSource;

    }

    OMAP_TO_SRC, *POMAP_TO_SRC, **PPOMAP_TO_SRC;

 

 

#define OMAP_TO_SRC_ sizeof (OMAP_TO_SRC)

 

 

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

 

 

typedef struct _OMAP_FROM_SRC

    {

    DWORD dSource;

    DWORD dTarget;

    }

    OMAP_FROM_SRC, *POMAP_FROM_SRC, **PPOMAP_FROM_SRC;

 

 

#define OMAP_FROM_SRC_ sizeof (OMAP_FROM_SRC)

 

 

 

 

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

 

 

POMAP_TO_SRC WINAPI imgDbgOmapToSrc (PIMG_DBG pid,

                                     PDWORD   pdCount)

    {

    PIMAGE_DEBUG_DIRECTORY pidd;

    DWORD                  dCount = 0;

    POMAP_TO_SRC           pots   = NULL;

 

 

    if ((pidd = imgDbgDirectory (pid,

                                 IMAGE_DEBUG_TYPE_OMAP_TO_SRC))

        != NULL)

        {

        pots   = IMG_DBG_DATA (pid, pidd);

        dCount = pidd->SizeOfData / OMAP_TO_SRC_;

        }

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

    return pots;

    }

 

 

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

 

 

POMAP_FROM_SRC WINAPI imgDbgOmapFromSrc (PIMG_DBG pid,

                                         PDWORD   pdCount)

    {

    PIMAGE_DEBUG_DIRECTORY pidd;

    DWORD                  dCount = 0;

    POMAP_FROM_SRC         pofs   = NULL;

 

 

    if ((pidd = imgDbgDirectory (pid,

                                 IMAGE_DEBUG_TYPE_OMAP_FROM_SRC))

        != NULL)

        {

        pofs   = IMG_DBG_DATA (pid, pidd);

        dCount = pidd->SizeOfData / OMAP_FROM_SRC_;

        }

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

    return pofs;

    }

列表1-18.  imgDbgOmapToSrc()imgDbgOmapFromSrc()函数

 

 

你可能感兴趣的:(《Undocumented Windows 2000 Secrets》翻译 --- 第一章(补充:PDB格式)(2))