PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头1
? ? ? ? 本文將討論PE文件中非常重要的一部分信息。(轉(zhuǎn)載請指明來源于breakSoftware的CSDN博客)
? ? ? ? 首先說一下VC中對應(yīng)的數(shù)據(jù)結(jié)構(gòu)。“簽名、COFF文件頭和可選文件頭”這三部分信息組合在一起是一個叫IMAGE_NT_HEADERS的結(jié)構(gòu)體。
typedef struct _IMAGE_NT_HEADERS64 {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
? ? ? ? 其中Signature對應(yīng)于“簽名”,FileHeader對應(yīng)于“COFF文件頭”,OptionalHeader對應(yīng)于“可選文件頭”。
? ? ? ? 對于PE鏡像文件,Signature對應(yīng)的數(shù)據(jù)是0x00004550(‘PE\0\0’)。對于如何找到這個位置,在前一篇文章中已經(jīng)有了解說:從文件頭偏移0x3C讀取一個DWORD大小的數(shù)據(jù),從文件頭偏移該數(shù)據(jù)長度,就到了Signature的起始位置。
? ? ? ? 看一下COFF文件頭結(jié)構(gòu)
typedef struct _IMAGE_FILE_HEADER {WORD Machine;WORD NumberOfSections;DWORD TimeDateStamp;DWORD PointerToSymbolTable;DWORD NumberOfSymbols;WORD SizeOfOptionalHeader;WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
? ? ? ? 以notepad為例
? ? ? ? Machine字段為0x014C,其對應(yīng)的信息是“Intel 386或其后續(xù)處理器及兼容處理器”。
? ? ? ? NumberOfSections是0x0003,它是個非常重要的字段,表示節(jié)的數(shù)目。PE文件是由一系列“節(jié)”構(gòu)成的,比較常見的是.text和.data等節(jié),這樣的獨立的區(qū)塊是用來存儲“代碼”、“數(shù)據(jù)”和“資源”等信息的。如xp上notepad,從數(shù)據(jù)中我們可以看到它有3個節(jié),我們用其他工具分析得到它確實存在如下3個節(jié)。
? ? ? ??TimeDateStamp是0x41107CC3,該字段記錄的是文件創(chuàng)建時間離1970年1月1日00:00的秒數(shù)。
? ? ? ??PointerToSymbolTable是0x00000000,該字段記錄了該PE文件中調(diào)試信息符號表。由于符號表信息是在程序運行時不需要加載進入內(nèi)存的,所以這個偏移使用的是相對文件頭偏移RA。目前微軟推薦是:將映像文件調(diào)試符號表信息獨立的放在PDB文件中,所以不會在PE文件中再保存調(diào)試符號表信息,于是這個字段應(yīng)該為0。當然這并不是硬性要求,我發(fā)現(xiàn)我電腦上就存在很多該字段不為0的文件。剛開始時我也不是很明白它們?yōu)槭裁匆褂眠@個字段,特別是其指向的字符表個數(shù)(NumberOfSymbols)為0!!你說既然大小為0,那你指向有什么意思呢?其實這種設(shè)計是非常有深意的,我會在之后的章節(jié)中介紹這種深意。
? ? ? ??NumberOfSymbols是0x00000000,該字段記錄了該PE文件中調(diào)試信息符號表元素個數(shù)。對于映像文件,該字段為0(非硬性要求),,理由在PointerToSymbolTable中已經(jīng)說明。通過NumberOfSymbols和PointerToSymbolTable,我們可以找到字符串表起始位置,因為字符串表緊跟在符號表之后。
? ? ? ??SizeOfOptionalHeader是0x00E0,該字段用于描述“可選文件頭”的大小。之后會看到“可選文件頭”的中有個具有16個元素是數(shù)組,該數(shù)組保存了一系列“塊信息”,但是并不是所有文件都有全部的“塊信息”,于是鏈接器在鏈接生成PE文件時,也是根據(jù)實際存在的“塊信息”位置(以后會說明為什么是位置而不是數(shù)量)去填充這個數(shù)組的。也就是說我們可能只是填充了1個元素,而剩下的15個元素直接被砍掉,而不是在內(nèi)存中使用0來填充。
? ? ? ? 這兒就引入一個問題,就是我們不能從“簽名”位置開始,就直接memcpy一段IMAGE_NT_HEADERS大小的空間到一個IMAGE_NT_HEADERS對象中。因為“可選文件頭”還要看“COFF文件頭”中的SizeOfOptionalHeader數(shù)據(jù)。
? ? ? ??Characteristics字段用于標記該文件屬性,notepad.exe該字段值為0x010F。下面我們來解釋下該組合屬性
| 標志 | 值 | 說明 |
|---|---|---|
| IMAGE_FILE_RELOCS_STRIPPED | 0x0001 | 僅適用于映像文件。它表明此文件不包含機制重定位信息,于是它只能被加載到其首選基地址。如果首選基地址不可用,則加載器會報錯。鏈接器默認會移除可執(zhí)行文件中的重定位信息。一般情況下,Exe文件會設(shè)置該值(如notepad.exe,但ntoskrnl.exe就沒設(shè)置),而因為DLL文件為了其良好的兼容性是不會去設(shè)置這個值的(如Kernel32.dll、User32.dll等)。 |
| IMAGE_FILE_EXECUTABLE_IMAGE | 0x0002 | 僅適用于映像文件。它用于表明該文件是合法的,可以被運行。如果沒有設(shè)置,則代表鏈接出現(xiàn)問題。這個一般都會設(shè)置。 |
| IMAGE_FILE_LINE_NUMS_STRIPPED | 0x0004 | COFF行號信息已經(jīng)被移除。不贊成使用該標志。但是我發(fā)現(xiàn)notepad.exe、Kernel32.dll、User32.dll等都設(shè)置了該標志。而一般我們編譯的PE文件是不設(shè)置該項的。 |
| IMAGE_FILE_LOCAL_SYMS_STRIPPED | 0x0008 | COFF符號表中有關(guān)局部符號的項已經(jīng)被移除。不贊成使用該標志。但是我發(fā)現(xiàn)notepad.exe、Kernel32.dll、User32.dll等都設(shè)置了該標志。而一般我們編譯的PE文件是不設(shè)置該項的。 |
| IMAGE_FILE_AGGRESSIVE_WS_TRIM | 0x0010 | 該標志已經(jīng)被廢棄。 |
| IMAGE_FILE_LARGE_ADDRESS_ AWARE | 0x0020 | 應(yīng)用程序可以處理大于2GB的地址。 |
| ? | 0x0040 | 為未來保留的字段。 |
| IMAGE_FILE_BYTES_REVERSED_LO | 0x0080 | 小尾,LSB在MSB前面。不贊成使用該標志。windows xp就是小尾。 |
| IMAGE_FILE_32BIT_MACHINE | 0x0100 | 適用于32位系統(tǒng)。我的xp系統(tǒng)上DLL和Exe文件基本都設(shè)置了該標志。 |
| IMAGE_FILE_DEBUG_STRIPPED | 0x0200 | 調(diào)試信息已經(jīng)從該映像文件中移除。 |
| IMAGE_FILE_REMOVABLE_RUN_ FROM_SWAP | 0x0400 | 如果該文件是在移動介質(zhì)上,需要將其完全加載到交換文件中。 |
| IMAGE_FILE_NET_RUN_FROM_SWAP | 0x0800 | 如果該文件是在網(wǎng)絡(luò)介質(zhì)上,需要將其完全加載到交換文件中。 |
| IMAGE_FILE_SYSTEM | 0x1000 | 該映像文件是一個系統(tǒng)文件,不是一個用戶文件。 |
| IMAGE_FILE_DLL | 0x2000 | 此文件是DLL文件。 |
| IMAGE_FILE_UP_SYSTEM_ONLY | 0x4000 | 該文件僅能運行于單處理機器上。 |
| IMAGE_FILE_BYTES_REVERSED_HI | 0x8000 | 大尾,LSB在MSB后面。 |
? ? ? ? 我觀察了我系統(tǒng)上幾個文件,發(fā)現(xiàn)以下規(guī)律:
? ? ? ?1 Sys和Exe的該屬性為0x010E或者0x010F。
? ? ? ?2 DLL文件該屬性一般為0x210E。DLL文件一般不會設(shè)IMAGE_FILE_RELOCS_STRIPPED(0x0001),因為它為了良好的兼容性,不能設(shè)置它必須要被加載的地址。一個Exe可能會加載多個DLL,如果系統(tǒng)“不小心”把某個DLL加載到0x70000000,那么如果有某個DLL設(shè)置了IMAGE_FILE_RELOCS_STRIPPED并將其首選加載地址正好也設(shè)置為0x70000000,那么系統(tǒng)為該Exe加載這個DLL將會失敗。但是的確存在這樣的文件,比如我電腦上ResourceCache.dll。DLL文件肯定要設(shè)置IMAGE_FILE_DLL。所以即使某個DLL文件的后綴名改了,你可以結(jié)合這個“特征碼”來還原其真面目。
? ? ? ?這兒我還要說一個認知的誤區(qū)。 IMAGE_FILE_32BIT_MACHINE標志可以用于標志這個文件是適用于32位系統(tǒng),但是如果僅僅通過該標志就去鑒別這個文件是32位文件還是64位文件是不正確的。我也不知道微軟為什么設(shè)計了該標志而沒有嚴格限制這個標志。我通過掃描我電腦里所有文件,發(fā)現(xiàn)了一個可能具有指導(dǎo)性的鑒別策略:
? ? ? ?1 如果沒有設(shè)置?IMAGE_FILE_32BIT_MACHINE但是設(shè)置了IMAGE_FILE_LARGE_ADDRESS_ AWARE的文件是64位文件。沒有設(shè)置IMAGE_FILE_32BIT_MACHINE意味著該文件可能是64位程序,而設(shè)置了IMAGE_FILE_LARGE_ADDRESS_ AWARE,則說明該文件可以處理大于2G的空間的內(nèi)存,則該文件是64位文件。如我本機上wwst64.exe。
? ? ? ?2 除了以上判斷之外的其他可能標志該文件是32位文件。
? ? ? ? ? 比如設(shè)置了IMAGE_FILE_32BIT_MACHINE而沒有設(shè)置IMAGE_FILE_LARGE_ADDRESS_ AWARE,則說明這個文件可以處理2G以內(nèi)內(nèi)存空間,是32位文件;
? ? ? ? ? 比如沒有設(shè)置IMAGE_FILE_32BIT_MACHINE和IMAGE_FILE_LARGE_ADDRESS_ AWARE,怎么解釋呢?反正它不是64位文件,因為不能處理大于2G內(nèi)存空間,那它只能是32位文件了。如我本機上文件sqlite3.dll。
? ? ? ? ?比如設(shè)置了IMAGE_FILE_32BIT_MACHINE和IMAGE_FILE_LARGE_ADDRESS_ AWARE,那說明這是個可以處理大于2G內(nèi)存空間的32位文件。如我本機上AcroBroker.exe。
BOOL CGetPEInfo::GetFileType() {CHECKFILETYPE();GETFILEHEADER();m_eFileType = E32Bit;m_bIsDllFile = ( m_FileHeader.Characteristics & IMAGE_FILE_DLL ) ? TRUE : FALSE;if ( !( IMAGE_FILE_32BIT_MACHINE & m_FileHeader.Characteristics ) && !( IMAGE_FILE_LARGE_ADDRESS_AWARE & m_FileHeader.Characteristics ) ) {//_ASSERT(FALSE);}else if ( !( IMAGE_FILE_32BIT_MACHINE & m_FileHeader.Characteristics ) && ( IMAGE_FILE_LARGE_ADDRESS_AWARE & m_FileHeader.Characteristics ) ){m_eFileType = E64Bit;}else if ( ( IMAGE_FILE_32BIT_MACHINE & m_FileHeader.Characteristics ) && ( IMAGE_FILE_LARGE_ADDRESS_AWARE & m_FileHeader.Characteristics ) ) {}return TRUE;
}
總結(jié)
以上是生活随笔為你收集整理的PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头1的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PE文件和COFF文件格式分析--MS-
- 下一篇: 一种在注入进程中使用WTL创建无焦点不在