分享
 
 
 

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

王朝system·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

第一章 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.0和Windows 2000的.dbg文件的内容仍是相同的。

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

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

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

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

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

#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文件中剩余数据项的偏移量。PointerToRaw和SizeOfData成员分别指出了相关数据块的偏移量和大小。

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

本书的CD中包含一个示例DLL----w2k_img.dll,该DLL用于解析.dbg和.pdb文件并导出了多个用于开发内核调试工具的重要函数。可在本书的\src\w2k_img目录下找到该DLL的源代码。w2k_img.dll的一个重要属性是:它所有Win32平台上都可以运行。这不只包括Windows 2000、Windows NT 4.0还包括Windows 9x。像所有Win32世界中的好市民一样,这个DLL的每个函数为支持ANSI和Unicode字符串均提供了独立的接口。默认情况下,客户端使用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_SRC和OMAP_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()函数

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有