网络上大部分资料对PE文件的节表的定位方式都是下面这样的
1 (LPVOID)((BYTE *)a + ((PIMAGE_DOS_HEADER)a)->e_lfanew + SIZE_OF_NT_SIGNATURE + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER):
其意思是这样的:
为了在解释上面的代码之前:首先要介绍些PE头的结构。
在PE文件中开始是DOS头,之后是Stub dos,再之后是NT头,在之后是节表头。也就是我们要定位的。
其中NT头的定义是这样的
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
他包括三个部分。
1》PE文件标识
2》文件头
3》可选头
现在就可以理解最开始的那条语句的含义
首先
((PIMAGE_DOS_HEADER)a)->e_lfanew 指向NT头的起始文件偏移(FOA)
之后
SIZE_OF_NT_SIGNATURE + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER) //NT头的大小
加上NT头的大小。前面我们说过节表头紧跟在NT头之后,这样定位似乎很合理,很正确。开始我也是这么以为的,但是知道我碰到无法定位节表头的情况下我才仔细对比了字节码中的数据。
我是打开的eclipse.exe(java开发的朋友应该很了解吧,^_^).我发现在节表头和可选头的中间有16个空余,它既不在IMAGE_OPTION_HEADER32中有定义,也不再节表中定义。但是有的PE文件中并没有者16个字节。这就是我们用上面的定位方式无法准确的定位所有的PE文件的原因。
哎,找到这里我深深地擦了一把大汗,我这时陷入了困境,但是我突然回想起来,在IMAGE_FILE_HEADER中有一个字段指明了可选头的大小。它就是
1 typedef struct _IMAGE_FILE_HEADER 2 { 3 WORD Machine; //运行平台 4 WORD NumberOfSections; //文件的区块数目 5 DWORD TimeDateStamp; //文件创建日期和事件 6 DWORD PointerToSymbolTable; //只想符号表(主要用于调试) 7 DWORD NumberOfSymbols; //符号表中的符号个数(同上) 8 WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32结构大小
红色标注的部分。之后我在此对比了eclipse的字节码,发现使用该字段来定位,就ok了
那么正确的定位方法就是这样了。
IMAGE_DOS_HEADER.e_lfanew //文件头FOA + 4//PE文件标识 + sizeof(PEStructs::IMAGE_FILE_HEADER) //文件头大小 + pTemFileHeader->SizeOfOptionalHeader, //可选头大小
哎,看到这里,功夫总算不负有心人。拿出来分享下,免得其它童鞋,也误入歧途。