PE文件和COFF文件格式分析——节信息
在《PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头3》中,我们看到一些区块的信息都有偏移指向。而我们本文讨论的节信息是没有任何偏移指向的,所以它是紧跟在可选文件头后面的。(转载请注明来源于breaksoftware的csdn博客)于是该节的起始位置要如此计算
再看下保存节信息的结构体IMAGE_SECTION_HEADER ,和上图对照看就容易理解了。
可以看到Stud_PE对第5节的名字的解析是错的的,那正确的是什么?现在我们要回顾《PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头1》,该文中我埋了一个伏笔,我把段提出来
PointerToSymbolTable是0x00000000,该字段记录了该PE文件中调试信息符号表。由于符号表信息是在程序运行时不需要加载进入内存的,所以这个偏移使用的是相对文件头偏移RA。目前微软推荐是:将映像文件调试符号表信息独立的放在PDB文件中,所以不会在PE文件中再保存调试符号表信息,于是这个字段应该为0。当然这并不是硬性要求,我发现我电脑上就存在很多该字段不为0的文件。刚开始时我也不是很明白它们为什么要使用这个字段,特别是其指向的字符表个数(NumberOfSymbols)为0!!你说既然大小为0,那你指向有什么意思呢?其实这种设计是非常有深意的,我会在之后的章节中介绍这种深意。
NumberOfSymbols是0x00000000,该字段记录了该PE文件中调试信息符号表元素个数。对于映像文件,该字段为0(非硬性要求),,理由在PointerToSymbolTable中已经说明。通过NumberOfSymbols和PointerToSymbolTable,我们可以找到字符串表起始位置,因为字符串表紧跟在符号表之后。看了这段后,我想你应该对那个伏笔有了解答。想想也挺有意思,微软不推荐在文件中包含调试信息,于是PointerToSymbolTable和NumberOfSymbols就是应该废弃的。可是这两个数据却关联着字符串表。字符串表大部分时候可以不使用,但是如果DLL中存在超过8byte的节名时又不得不用,于是只好让PointerToSymbolTable指向字符串表开始,而NumberOfSymbols为0。
现在我们来看下上面那个Stud_PE分析出错的文件的文件头信息
我们去0x001c1600+4的位置去寻找该节名字,该节名位.eh_frame,长度是9byte。
这儿要特别说明一点,可执行文件的节名长度是不会超过8的。即使obj文件中节名存在超过8的,也会在链接进入可执行文件时被截断。
VirtualSize属性是节加载进入内存后,节在内存中的大小。如果它比SizeOfRawData大,则多余的部分是用0x00填充的。这个性质非常重要,它是关系到RVA和RA之间换算的一个基础。
VirtualAddress属性是节加载进入内存后其第一个字节相对于映像基址的偏移(RVA)。
SizeOfRawData是磁盘映像文件中该节的已初始化数据的大小。对于可执行文件来说,它必须是IMAGE_OPTIONAL_HEADER32(64)::FileAlignment的倍数。.如果该节中仅包含未初始化的数据,则该字段为0。
PointerToRawData是磁盘映像文件中该节相对于映像基址的偏移(RA)。对于可执行文件来说,它的值要是IMAGE_OPTIONAL_HEADER32(64)::FileAlignment的倍数。如果该节中仅包含未初始化的数据,则该字段为0。
PointerToRelocations指向节中重定位项开头的相对映像基址的偏移(RA)。可执行文件或者不能重定向的文件该字段应该为0。
PointerToLinenumbers指向节中行号项的相对映像基址偏移(RA)。因为已经不推荐在PE文件中包含调试信息,所以该字段一般为0。
NumberOfRelocations是节中重定位项的个数。可执行文件和不可以重定位的文件该字段为0。
NumberOfLinenumbers是节中行号项的个数。因为已不推荐PE文件中包含调试信息,所以该字段一般为0。
Characteristics描述节的特征。
Flag
Value
Description
0x00000000
Reserved for future use.
0x00000001
Reserved for future use.
0x00000002
Reserved for future use.
0x00000004
Reserved for future use.
IMAGE_SCN_TYPE_NO_PAD
0x00000008
The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files.
0x00000010
Reserved for future use.
IMAGE_SCN_CNT_CODE
0x00000020
The section contains executable code.
IMAGE_SCN_CNT_INITIALIZED_DATA
0x00000040
The section contains initialized data.
IMAGE_SCN_CNT_UNINITIALIZED_ DATA
0x00000080
The section contains uninitialized data.
IMAGE_SCN_LNK_OTHER
0x00000100
Reserved for future use.
IMAGE_SCN_LNK_INFO
0x00000200
The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
0x00000400
Reserved for future use.
IMAGE_SCN_LNK_REMOVE
0x00000800
The section will not become part of the image. This is valid only for object files.
IMAGE_SCN_LNK_COMDAT
0x00001000
The section contains COMDAT data. For more information, see section 5.5.6, “COMDAT Sections (Object Only).” This is valid only for object files.
IMAGE_SCN_GPREL
0x00008000
The section contains data referenced through the global pointer (GP).
IMAGE_SCN_MEM_PURGEABLE
0x00020000
Reserved for future use.
IMAGE_SCN_MEM_16BIT
0x00020000
For ARM machine types, the section contains Thumb code. Reserved for future use with other machine types.
IMAGE_SCN_MEM_LOCKED
0x00040000
Reserved for future use.
IMAGE_SCN_MEM_PRELOAD
0x00080000
Reserved for future use.
IMAGE_SCN_ALIGN_1BYTES
0x00100000
Align data on a 1-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_2BYTES
0x00200000
Align data on a 2-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_4BYTES
0x00300000
Align data on a 4-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_8BYTES
0x00400000
Align data on an 8-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_16BYTES
0x00500000
Align data on a 16-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_32BYTES
0x00600000
Align data on a 32-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_64BYTES
0x00700000
Align data on a 64-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_128BYTES
0x00800000
Align data on a 128-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_256BYTES
0x00900000
Align data on a 256-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_512BYTES
0x00A00000
Align data on a 512-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_1024BYTES
0x00B00000
Align data on a 1024-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_2048BYTES
0x00C00000
Align data on a 2048-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_4096BYTES
0x00D00000
Align data on a 4096-byte boundary. Valid only for object files.
IMAGE_SCN_ALIGN_8192BYTES
0x00E00000
Align data on an 8192-byte boundary. Valid only for object files.
IMAGE_SCN_LNK_NRELOC_OVFL
0x01000000
The section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE
0x02000000
The section can be discarded as needed.
IMAGE_SCN_MEM_NOT_CACHED
0x04000000
The section cannot be cached.
IMAGE_SCN_MEM_NOT_PAGED
0x08000000
The section is not pageable.
IMAGE_SCN_MEM_SHARED
0x10000000
The section can be shared in memory.
IMAGE_SCN_MEM_EXECUTE
0x20000000
The section can be executed as code.
IMAGE_SCN_MEM_READ
0x40000000
The section can be read.
IMAGE_SCN_MEM_WRITE
0x80000000
The section can be written to.
IMAGE_SCN_LNK_NRELOC_OVFL 标志表明节中重定位项的个数超出了节头中为每个节保留的16 位所能表示的范围。如果设置了此标志并且节头中的NumberOfRelocations 域的值是0xffff,那么实际的重定位项个数被保存在第一个重定位项的VirtualAddress 域(32 位)中。如果设置了IMAGE_SCN_LNK_NRELOC_OVFL
标志但节中的重定位项的个数少于0xffff,则表示出现了错误。最后贴一下Section节的获取算法
BOOL CGetPEInfo::GetSectionHeaders(){GETPESTART();BOOL bSuc = FALSE;do { size_t unDwordSize = sizeof(DWORD); // PE\0\0 size_t unImgFileHeaderSize = sizeof(IMAGE_FILE_HEADER);LPVOID lpSectionHeaderStart = (LPBYTE)m_lpPEStart + unDwordSize + unImgFileHeaderSize + m_FileHeader.SizeOfOptionalHeader;size_t unSectionHeaderSize = sizeof( IMAGE_SECTION_HEADER );for ( int i = 0; i < m_FileHeader.NumberOfSections; i++ ) { IMAGE_SECTION_HEADER SectionHeader;if ( FALSE == SafeCopy( &SectionHeader, lpSectionHeaderStart, unSectionHeaderSize ) ) { break;} IMAGE_SECTION_HEADERINFO SectionHeaderInfo; if ( '/' == SectionHeader.Name[0]) { std::string szOffsetStringTable; szOffsetStringTable.assign( &SectionHeader.Name[1], &SectionHeader.Name[IMAGE_SIZEOF_SHORT_NAME-1]); int nOffsetString = atoi( szOffsetStringTable.c_str() ); LPSTR lpSetcionNameStart = (LPSTR)m_lpFileStart + m_FileHeader.PointerToSymbolTable; lpSetcionNameStart += (DWORD)(sizeof(IMAGE_SYMBOL) * m_FileHeader.NumberOfSymbols); lpSetcionNameStart += nOffsetString; SectionHeaderInfo.szSectionName = lpSetcionNameStart; if ( 0 != m_FileHeader.NumberOfSymbols ) { //_ASSERT(FALSE); } } m_vecSectionHeaders.push_back( SectionHeader ); SectionHeaderInfo.ImgSecHD = SectionHeader; m_vecSetcionHeaderInfo.push_back(SectionHeaderInfo); lpSectionHeaderStart = (LPBYTE)(lpSectionHeaderStart) + unSectionHeaderSize; } bSuc = TRUE;} while (0);return bSuc;}