網絡上大部分資料對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, //可選頭大小
哎,看到這里,功夫總算不負有心人。拿出來分享下,免得其它童鞋,也誤入歧途。