PE文件和COFF文件格式分析——节信息
? ? ? ? 在《PE文件和COFF文件格式分析——簽名、COFF文件頭和可選文件頭3》中,我們看到一些區(qū)塊的信息都有偏移指向。而我們本文討論的節(jié)信息是沒有任何偏移指向的,所以它是緊跟在可選文件頭后面的。(轉(zhuǎn)載請注明來源于breaksoftware的csdn博客)于是該節(jié)的起始位置要如此計(jì)算
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;
? ? ? ? 有了起始地址,那么結(jié)束地址呢?在《PE文件和COFF文件格式分析——簽名、COFF文件頭和可選文件頭1》中,我們描述過,IMAGE_FILE_HEADER::NumberOfSections就是用于指定該節(jié)信息的個(gè)數(shù)的。這樣我們便可以得到節(jié)信息
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;}// 不夠嚴(yán)謹(jǐn)哦!不要這么用! m_vecSectionHeaders.push_back( SectionHeader );lpSectionHeaderStart = (LPBYTE)(lpSectionHeaderStart) + unSectionHeaderSize;
}
? ? ? ? 像Stud_PE和PE Explorer等都是這么做的。但是我這兒說一下,這樣做是不嚴(yán)謹(jǐn)?shù)?#xff0c;我會在之后論述這樣草率的做法為什么不對。
? ? ? ? 我先拿我電腦上notepad.exe為例,看看它的節(jié)信息
? ? ? ? 再看下保存節(jié)信息的結(jié)構(gòu)體IMAGE_SECTION_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 SizeOfRawData;DWORD PointerToRawData;DWORD PointerToRelocations;DWORD PointerToLinenumbers;WORD NumberOfRelocations;WORD NumberOfLinenumbers;DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
? ? ? ? Name是使用UTF-8編碼,以\0結(jié)尾,大小為8Byte的字符串。它是節(jié)的名稱,如上圖中的.text、.data和.rsrc??吹絅ame的長度為8,你是不是在想到:小于,等于和大于?現(xiàn)在我們就討論下如果節(jié)名長度小于、等于和大于8的情況。
? ? ? ? 節(jié)名長度小于8 的情況。這個(gè)場景最簡單了,不足的位用\0填充。
? ? ? ? 節(jié)名長度等于8的情況。因?yàn)榻Y(jié)構(gòu)大小是固定的,所以我們不可能找到一個(gè)空余的位置放置\0,那么這8byte就全部填充名字了。
? ? ? ? 以下是我收集的節(jié)名信息
IMAGESETCTIONNAME g_ImageSectionName[] = {{'.','b','s','s','\0','\0','\0','\0'},{'.','c','o','r','m','e','t','a'},{'.','d','a','t','a','\0','\0','\0'},{'.','d','e','b','u','g','$','F'},{'.','d','e','b','u','g','$','P'},{'.','d','e','b','u','g','$','S'},{'.','d','e','b','u','g','$','T'},{'.','d','r','e','c','t','v','e'},{'.','e','d','a','t','a','\0','\0'},{'.','i','d','a','t','a','\0','\0'},{'.','i','d','l','s','y','m','\0'},{'.','p','d','a','t','a','\0','\0'},{'.','r','d','a','t','a','\0','\0'},{'.','r','e','l','o','c','\0','\0'},{'.','r','s','r','c','\0','\0','\0'},{'.','s','b','s','s','\0','\0','\0'},{'.','s','d','a','t','a','\0','\0'},{'.','s','r','d','a','t','a','\0'},{'.','s','x','d','a','t','a','\0'},{'.','t','e','x','t','\0','\0','\0'},{'.','t','l','s','\0','\0','\0','\0'},{'.','t','l','s','$','\0','\0','\0'},{'.','v','s','d','a','t','a','\0'},{'.','x','d','a','t','a','\0','\0'}
};
? ? ? ? ?像.debug$F這樣的就是占用了8個(gè)byte的
? ? ? ? 節(jié)名長度大于8的情況。這個(gè)場景怎么辦?結(jié)構(gòu)體大小固定,我們不能越界寫!那我們只能在其他地方去寫了,然后在這個(gè)位置保存我們寫入數(shù)據(jù)的偏移即可!是的,PE規(guī)范就是采用的這樣的思想,只是稍微有點(diǎn)不同:以/開始,其后跟著一個(gè)表示偏移量的十進(jìn)制數(shù)字字符串,如/4(0x2f 0 0x34 0x00 0x00?0x00?0x00?0x00?0x00)。這個(gè)數(shù)字是相對字符串表起始位置的偏移RA,我們的真實(shí)的節(jié)名就保存在字符串表中。我在我電腦上找到一個(gè)這樣的文件avcodec-52.dll。我們先看Stud_PE的分析結(jié)果
? ? ? ? 可以看到Stud_PE對第5節(jié)的名字的解析是錯的的,那正確的是什么?現(xiàn)在我們要回顧《PE文件和COFF文件格式分析——簽名、COFF文件頭和可選文件頭1》,該文中我埋了一個(gè)伏筆,我把段提出來
? ? ? ? PointerToSymbolTable是0x00000000,該字段記錄了該P(yáng)E文件中調(diào)試信息符號表。由于符號表信息是在程序運(yùn)行時(shí)不需要加載進(jìn)入內(nèi)存的,所以這個(gè)偏移使用的是相對文件頭偏移RA。目前微軟推薦是:將映像文件調(diào)試符號表信息獨(dú)立的放在PDB文件中,所以不會在PE文件中再保存調(diào)試符號表信息,于是這個(gè)字段應(yīng)該為0。當(dāng)然這并不是硬性要求,我發(fā)現(xiàn)我電腦上就存在很多該字段不為0的文件。剛開始時(shí)我也不是很明白它們?yōu)槭裁匆褂眠@個(gè)字段,特別是其指向的字符表個(gè)數(shù)(NumberOfSymbols)為0!!你說既然大小為0,那你指向有什么意思呢?其實(shí)這種設(shè)計(jì)是非常有深意的,我會在之后的章節(jié)中介紹這種深意。
? ? ? ? NumberOfSymbols是0x00000000,該字段記錄了該P(yáng)E文件中調(diào)試信息符號表元素個(gè)數(shù)。對于映像文件,該字段為0(非硬性要求),,理由在PointerToSymbolTable中已經(jīng)說明。通過NumberOfSymbols和PointerToSymbolTable,我們可以找到字符串表起始位置,因?yàn)樽址砭o跟在符號表之后。 ?
? ? ? ? 看了這段后,我想你應(yīng)該對那個(gè)伏筆有了解答。想想也挺有意思,微軟不推薦在文件中包含調(diào)試信息,于是PointerToSymbolTable和NumberOfSymbols就是應(yīng)該廢棄的。可是這兩個(gè)數(shù)據(jù)卻關(guān)聯(lián)著字符串表。字符串表大部分時(shí)候可以不使用,但是如果DLL中存在超過8byte的節(jié)名時(shí)又不得不用,于是只好讓PointerToSymbolTable指向字符串表開始,而NumberOfSymbols為0。
? ? ? ? 現(xiàn)在我們來看下上面那個(gè)Stud_PE分析出錯的文件的文件頭信息
? ? ? ? 我們?nèi)?x001c1600+4的位置去尋找該節(jié)名字,該節(jié)名位.eh_frame,長度是9byte。
? ? ? ? 這兒要特別說明一點(diǎn),可執(zhí)行文件的節(jié)名長度是不會超過8的。即使obj文件中節(jié)名存在超過8的,也會在鏈接進(jìn)入可執(zhí)行文件時(shí)被截?cái)唷?/p>
? ? ? ? VirtualSize屬性是節(jié)加載進(jìn)入內(nèi)存后,節(jié)在內(nèi)存中的大小。如果它比SizeOfRawData大,則多余的部分是用0x00填充的。這個(gè)性質(zhì)非常重要,它是關(guān)系到RVA和RA之間換算的一個(gè)基礎(chǔ)。
? ? ? ??
? ? ? ? VirtualAddress屬性是節(jié)加載進(jìn)入內(nèi)存后其第一個(gè)字節(jié)相對于映像基址的偏移(RVA)。
? ? ? ? SizeOfRawData是磁盤映像文件中該節(jié)的已初始化數(shù)據(jù)的大小。對于可執(zhí)行文件來說,它必須是IMAGE_OPTIONAL_HEADER32(64)::FileAlignment的倍數(shù)。.如果該節(jié)中僅包含未初始化的數(shù)據(jù),則該字段為0。
? ? ? ? PointerToRawData是磁盤映像文件中該節(jié)相對于映像基址的偏移(RA)。對于可執(zhí)行文件來說,它的值要是IMAGE_OPTIONAL_HEADER32(64)::FileAlignment的倍數(shù)。如果該節(jié)中僅包含未初始化的數(shù)據(jù),則該字段為0。
? ? ? ??PointerToRelocations指向節(jié)中重定位項(xiàng)開頭的相對映像基址的偏移(RA)??蓤?zhí)行文件或者不能重定向的文件該字段應(yīng)該為0。
? ? ? ? PointerToLinenumbers指向節(jié)中行號項(xiàng)的相對映像基址偏移(RA)。因?yàn)橐呀?jīng)不推薦在PE文件中包含調(diào)試信息,所以該字段一般為0。
? ? ? ? NumberOfRelocations是節(jié)中重定位項(xiàng)的個(gè)數(shù)??蓤?zhí)行文件和不可以重定位的文件該字段為0。
? ? ? ? NumberOfLinenumbers是節(jié)中行號項(xiàng)的個(gè)數(shù)。因?yàn)橐巡煌扑]PE文件中包含調(diào)試信息,所以該字段一般為0。
? ? ? ? Characteristics描述節(jié)的特征。
?
| 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 標(biāo)志表明節(jié)中重定位項(xiàng)的個(gè)數(shù)超出了節(jié)頭中為每個(gè)節(jié)保留的16 位所能表示的范圍。如果設(shè)置了此標(biāo)志并且節(jié)頭中的NumberOfRelocations 域的值是0xffff,那么實(shí)際的重定位項(xiàng)個(gè)數(shù)被保存在第一個(gè)重定位項(xiàng)的VirtualAddress 域(32 位)中。如果設(shè)置了IMAGE_SCN_LNK_NRELOC_OVFL
標(biāo)志但節(jié)中的重定位項(xiàng)的個(gè)數(shù)少于0xffff,則表示出現(xiàn)了錯誤。
? ? ? ? 最后貼一下Section節(jié)的獲取算法
BOOL CGetPEInfo::GetSectionHeaders()
{GETPESTART();BOOL bSuc = FALSE;do {size_t unDwordSize = sizeof(DWORD); // PE\0\0size_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;
}
?
總結(jié)
以上是生活随笔為你收集整理的PE文件和COFF文件格式分析——节信息的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PE文件和COFF文件格式分析——签名、
- 下一篇: PE文件和COFF文件格式分析——RVA