分享
 
 
 

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

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

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

翻译:Kendiv ( fcczj@263.net )

更新:Tuesday, May 03, 2005

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

CodeView子节(CodeView Subsections)

CodeView是微软自己的调试信息格式。随着微软C/C++编译器和链接器的发展它也经历了很多变化。有些CodeView版本之间的区别非常明显。不过,所有版本的CodeView在它们的开始位置都有一个32位的签名来唯一标识其采用的数据格式。Windows NT 4.0符号文件使用NB09格式,该格式由CodeView 4.10引入。Windows 2000的符号文件包含的则是NB10格式的CodeView数据,这一格式只是引入了独立.pdb文件,这一点我在前面已经提及过。

NB09版的CodeView中的数据被进一步细化为一个目录及其下属的目录项。就像Matt Pietrek在MSJ所发表的文章中所指出的那样, 对于.dbg文件,大多数基本的CodeView结构在SDK的一组示例性头文件中都有定义。如果你安装了SDK示例,你会在\Program Files\Microsoft Platform SDK\Samples\SdkTools\Image\Include目录下发现一组非常有趣的文件。你解析CodeView所需的文件名为:cvexefmt.h和cvinfo.h。很不幸的是,这些文件已经很长时间没有更新了,这些文件的日期还停留在09-07-1994。这些文件最引人注意的是定义在cvexefmt.h中结构体的名字,这些名字都以OMF开始,OMF表示Object Module Format。OMF是16位DOS和Windows的.obj、.lib文件使用的一种标准文件格式。从微软的32位开发工具开始,这一格式由通用对象文件格式(Common Object File Format,COFF)代替了。

尽管今天看来原始的OMF格式已经过时了,但它仍被公认为是一种灵活的文件格式。它的一个目标就是尽可能的减少对内存和磁盘空间的使用量。另一个非常重要的属性是:即使应用程序并不完全了解此种格式的所有部分,也可以成功的解析这个格式的文件。基本的OMF数据结构是一种带有标识的记录,开始处的标识字节给出了记录中所包含的数据的类型。这种设计使得OMF读取器可以在记录之间灵活的移动,以选出它们感兴趣的记录。微软的CodeView格式就采用了这种设计方案,cvexetmt.h中CodeView结构名称的OMF前缀可以说明这一点。尽管CodeView记录和原始的OMF记录一样仅包含了很少的数据,但它仍保留了此种格式的基本特性:不需要理解记录中的所有内容,就可以读取此种格式。

列表1-19给出了多个基本的CodeView结构,它们都来自w2k_img.h。其中的一些定义和cvexefmt.h和cvinfo.h中的定义类似,但它们可以满足w2k_img.dll的所有需求。在所有CodeView数据中都出现了CV_HEADER结构,但格式的版本除外。签名是一个32位的格式版本ID,它类似于CV_SIGNATURE_NB09或者CV_SIGNATURE_NB10。lOffset成员给出了CodeView目录相对于表头地址的偏移量。在Windows NT 4.0的NB09格式的符号文件中,这一偏移量似乎总是8,这表示紧随表头之后的就是CodeView目录结构。Windows 2000符号文件中这一偏移量则为0。在稍后我们将详细讨论这一格式。

#define CV_SIGNATURE_NB 'BN'

#define CV_SIGNATURE_NB09 '90BN'

#define CV_SIGNATURE_NB10 '01BN'

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

typedef union _CV_SIGNATURE

{

WORD wMagic; // 'BN'

DWORD dVersion; // 'xxBN'

BYTE abText [4]; // "NBxx"

}

CV_SIGNATURE, *PCV_SIGNATURE, **PPCV_SIGNATURE;

#define CV_SIGNATURE_ sizeof (CV_SIGNATURE)

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

typedef struct _CV_HEADER

{

CV_SIGNATURE Signature;

LONG lOffset;

}

CV_HEADER, *PCV_HEADER, **PPCV_HEADER;

#define CV_HEADER_ sizeof (CV_HEADER)

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

typedef struct _CV_DIRECTORY

{

WORD wSize; // in bytes, including this member

WORD wEntrySize; // in bytes

DWORD dEntries;

LONG lOffset;

DWORD dFlags;

}

CV_DIRECTORY, *PCV_DIRECTORY, **PPCV_DIRECTORY;

#define CV_DIRECTORY_ sizeof (CV_DIRECTORY)

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

#define sstModule 0x0120 // CV_MODULE

#define sstGlobalPub 0x012A // CV_PUBSYM

#define sstSegMap 0x012D // SV_SEGMAP

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

typedef struct _CV_ENTRY

{

WORD wSubSectionType; // sst*

WORD wModuleIndex; // -1 if not applicable

LONG lSubSectionOffset; // relative to CV_HEADER

DWORD dSubSectionSize; // in bytes, not including padding

}

CV_ENTRY, *PCV_ENTRY, **PPCV_ENTRY;

#define CV_ENTRY_ sizeof (CV_ENTRY)

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

typedef struct _CV_NB09 // CodeView 4.10

{

CV_HEADER Header;

CV_DIRECTORY Directory;

CV_ENTRY Entries [];

}

CV_NB09, *PCV_NB09, **PPCV_NB09;

#define CV_NB09_ sizeof (CV_NB09)

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

typedef struct _CV_NB10 // PDB reference

{

CV_HEADER Header;

DWORD dSignature; // seconds since 01-01-1970

DWORD dAge; // 1++

BYTE abPdbName []; // zero-terminated

}

CV_NB10, *PCV_NB10, **PPCV_NB10;

#define CV_NB10_ sizeof (CV_NB10)

列表1-19. CodeView的数据结构

NB09版的CodeView目录由一个CV_DIRECTORY结构和紧随其后的一个CV_ENTRY数组构成。列表1-19中的CV_NB09结构反映了这种结构特点。CV_NB09结构包含CodeView表头、目录和一个Entry数组。CV_DIRECTORY结构的dEntries成员给出了Entries[]数组的大小。数组中的每个CV_ENTRY都指向CodeView的一个子节,该子节的类型由CV_ENTRY结构的wSubSectionType成员给出。Cvexefmt.h中定义了至少21种子节类型。不过,Windows NT 4.0仅使用其中的3个:sstModule(0x0120)、sstGlobalPub(0x012A)和sstSegMap(0x012D)。通常你会在符号文件发现多个sstModule类型的子节,而sstGlobalPub和sstSegMap类型的子节仅有一个(在一个符号文件中)。就像它们的名字所暗示的,sstGlobalPub表示在该类型的子节中,我们可以找到对应模块的公开的全局符号信息。

列表1-20中给出的imgCvEntry()函数可以方便的按类型查找CodeView目录项。该函数的pc09参数指向一个CV_NB09结构,这意味着,该参数将指向.dbg文件中签名为NB09的CodeView数据块。dType参数用于指定CodeView子节的类型ID(形如sst*),dIndex参数用于表示在多个类型相同的子节中选择哪个子节的实例。因此,仅当dType为sstModule时,dIndex才能被设置为一个非0值。

PCV_ENTRY WINAPI imgCvEntry (PCV_NB09 pc09,

DWORD dType,

DWORD dIndex)

{

DWORD i, j;

PCV_ENTRY pce = NULL;

if ((pc09 != NULL) &&

(pc09->Header.Signature.dVersion == CV_SIGNATURE_NB09))

{

for (i = j = 0; i < pc09->Directory.dEntries; i++)

{

if ((pc09->Entries [i].wSubSectionType == dType) &&

(j++ == dIndex))

{

pce = pc09->Entries + i;

break;

}

}

}

return pce;

}

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

PCV_PUBSYM WINAPI imgCvSymbols (PCV_NB09 pc09,

PDWORD pdCount,

PDWORD pdSize)

{

PCV_ENTRY pce;

PCV_PUBSYM pcp1;

DWORD i;

DWORD dCount = 0;

DWORD dSize = 0;

PCV_PUBSYM pcp = NULL;

if ((pce = imgCvEntry (pc09, sstGlobalPub, 0)) != NULL)

{

pcp = CV_PUBSYM_DATA ((PBYTE) pc09

+ pce->lSubSectionOffset);

dSize = pce->dSubSectionSize;

for (i = 0; dSize - i >= CV_PUBSYM_;

i += CV_PUBSYM_SIZE (pcp1))

{

pcp1 = (PCV_PUBSYM) ((PBYTE) pcp + i);

if (dSize - i < CV_PUBSYM_SIZE (pcp1)) break;

if (pcp1->Header.wRecordType == CV_PUB32) dCount++;

}

}

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

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

return pcp;

}

列表1-20. imgCvEntry()和imgCvSymbols()函数

CodeView符号

列表1-20底部的imgCvSymbols()函数将返回一个指向首个CodeView符号记录的指针。sstGlobalPub子节包含一个长度确定的CV_SYMHASH表头,紧随其后的是一组长度可变的CV_PUBSYM记录。列表1-21给出了这两个结构的定义。首先,imgCvSymbols()调用imgCvEntry()来找出CV_ENTRY,该结构的wSubSectionType成员将被设置为sstGlobalPub。如果imgCvEntry()返回一个CV_ENTRY结构,则将使用列表1-4底部的CV_PUBSYM_DATA()宏来跳过前导的CV_SYMHASH结构。最后,imgCvSymbols()通过遍历CV_PUBSYM记录列表来统计符号的个数,并使用CV_PUBSYM_SIZE()宏(参见列表1-21)计算每个记录的大小。

CV_PUBSYM列表和OMF对象文件的内容有些相似。前面已经提到过,一个OMF数据流由长度可变的记录组成,每个记录的开始位置的第一个字节为标志,紧随其后的一个WORD存放的是该记录的大小。CV_PUBSYM记录与之类似。CV_PUBSYM记录的开始位置是一个OMF_HEADER结构,该结构由wRecordSize和wRecordType成员构成。可看出这OMF非常相似,不同之处是标志字节之后的存放长度的WORD被扩展为16位。CV_PUBSYM结构的最后一部分是符号名,该符号名采用的是PASCAL格式,这一格式是OMF记录常用的格式。PASCAL格式的字符串的第一个字节用来记录该字符串的长度,其后的8位用来存储字符。和C风格的字符串不同,它并不以0表示结束。在符号名结束之后,CV_PUBSYM还将占有其后的16个位,这样做是为了到达32位边界。这16位由OMF_HEADER结构中的wRecordSize成员使用。要特别注意的是,wRecordSize给出的CV_PUBSYM结构的大小,并不包括wRecordSize自己占用的空间。这也是列表1-21中的CV_PUBSYM_SIZE()宏会在wRecordSize之上再加上sizeof(WORD),以获取整个记录体的大小。

typedef struct _CV_SYMHASH

{

WORD wSymbolHashIndex;

WORD wAddressHashIndex;

DWORD dSymbolInfoSize;

DWORD dSymbolHashSize;

DWORD dAddressHashSize;

}

CV_SYMHASH, *PCV_SYMHASH, **PPCV_SYMHASH;

#define CV_SYMHASH_ sizeof (CV_SYMHASH)

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

typedef struct _OMF_HEADER

{

WORD wRecordSize; // in bytes, not including this member

WORD wRecordType;

}

OMF_HEADER, *POMF_HEADER, **PPOMF_HEADER;

#define OMF_HEADER_ sizeof (OMF_HEADER)

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

typedef struct _OMF_NAME

{

BYTE bLength; // in bytes, not including this member

BYTE abName [];

}

OMF_NAME, *POMF_NAME, **PPOMF_NAME;

#define OMF_NAME_ sizeof (OMF_NAME)

#define S_PUB32 0x0203

#define S_ALIGN 0x0402

#define CV_PUB32 S_PUB32

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

typedef struct _CV_PUBSYM

{

OMF_HEADER Header;

DWORD dOffset;

WORD wSegment; // 1-based section index

WORD wTypeIndex; // 0

OMF_NAME Name; // zero-padded to next DWORD

}

CV_PUBSYM, *PCV_PUBSYM, **PPCV_PUBSYM;

#define CV_PUBSYM_ sizeof (CV_PUBSYM)

#define CV_PUBSYM_DATA(_p) ((PCV_PUBSYM) ((PBYTE) (_p) + CV_SYMHASH_))

#define CV_PUBSYM_SIZE(_p) ((DWORD) (_p)->Header.wRecordSize + sizeof (WORD))

#define CV_PUBSYM_NEXT(_p) ((PCV_PUBSYM) ((PBYTE) (_p) + CV_PUBSYM_SIZE (_p)))

列表1-21. CV_SYMHASH和CV_PUBSYM结构

如果你要扫描CV_PUBSYM流,一般情况下,你会遇到两种类型的记录:S_PUB32(0x0203)或者S_ALIGN(0x0402)。最后一种可以安全的忽略它,因为它仅仅是为了满足32位平台的字节对其方式。S_PUB32记录中则包含实际的符号信息。除符号名之外,记录中的wSegment和dOffset成员也非常有用。wSegment给出了PE文件中包含该符号的Section的索引。使用该索引的负数可以作为IMAGE_SECTION_HEADER数组(此数组位于.dbg文件的开始位置)的索引。dOffset是符号名相对于PE Section的偏移量。这里的符号名指的是与函数的入口点或者全局变量的基地址相关的符号。通常,将dOffset与对应IMAGE_SECTION_HEADER结构的虚拟地址相加即可得到该符号相对于模块基地址的偏移量。不过,如果.dbg文件还包含IMAGE_DEBUG_TYPE_OMAP_TO_SRC和IMAGE_DEBUG_TYPE_OMAP_FROM_SRC子节,那么dOffset必须经过一个附加的转换层。OMAP表的使用方式将在介绍完PDB文件的格式后再来讨论。

符号在CodeView的sstGlobalPub子节中的顺序有些随机。我还不清楚它的深层含义。不过,我可以肯定地说,符号并不是按照section的序号、偏移量或名字的顺序来存放的。不要依赖对符号存放顺序的假设,你必须自己完成符号记录的排序。w2k_img.dll示例库提供了三个默认的符号顺序:按照地址、按照名字(大小写敏感)和按照名字(忽略大小写)。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有