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

本文详细介绍了Windows NT 4.0及Windows 2000系统中.dbg文件的内部结构,包括文件头、数据节及其组成部分。同时,提供了一系列API函数,用于解析.dbg文件中的关键信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

导读:

翻译: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)


    {


    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()函数



 





本文转自
http://www.tongyi.net/os/windows2000/1049375.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值