PE文件格式詳解,第二講,NT頭文件格式,以及文件頭格式


        PE文件格式詳解,第二講,NT頭文件格式,以及文件頭格式

作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)

PS:本篇博客默認你已經有了匯編基礎,所以會使用32位匯編編寫最小PE進行講解

今天詳解NT 頭格式,以及文件頭格式,以及作用, 關於DOS頭文件格式,以及DOSStub昨天的博客已經寫過了.主要是分散講解.便於理解.

一丶最小PE的生成,以及標准PE的生成

ps: (如果直接學習NT頭,文件頭,請不用看這個生成PE,直接看下面講解即可)

1.標准PE的生成

為了便於學習PE文件格式,所以這里寫出一個最小PE,還有一個最小的標准PE,讓大家理解.

32位匯編編寫.(匯編是能編寫最小PE的)

首先我們先寫一段基本的匯編代碼,然后一層一層的優化

32位匯編代碼:

.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc      ;包含各種lib庫以及頭文件
includelib user32.lib
includelib kernel32.lib

.data
    g_szHello db "Hello",0dh,0ah,00    ;定義Hello字符串
.code
start:
  invoke MessageBoxA,NULL,offset g_szHello ,NULL,MB_OK ;彈出信息框
  invoke ExitProcess,0    ;退出程序
end start

很簡單的匯編代碼

看下EXE的大小,以及內容

2.50KB有點大了.

可以繼續優化,但是比如手動敲命令行了.注意,這里使用的masm32的link連接器

首先我們要去掉分區,因為這里的EXE主要是分區太多.所以去掉.

怎么去掉? 只需要把上面的匯編代碼修改一下即可.

修改為:

.386
.model flat,stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
includelib user32.lib
includelib kernel32.lib

.code
  g_szHello db "Hello",0dh,0ah,00   ;將數據段的數據,放到代碼區中
start:
  invoke MessageBoxA,NULL,offset g_szHello ,NULL,MB_OK
  invoke ExitProcess,0
end start

很簡單,就是把.data去掉即可.

這個就是標准PE了,看下文件大小.

注意一下,這里我使用的是RadAsm集成開發環境,

編譯器是Masm32的link連接器. 如果使用VC6.0以及以上的,文件會變的很大,可能會有16.KB,28.KB,不利於大家學習.如果不會配置RadAsm集成開發環境,請參考以前的帖子.自己配置一下即可.

2.最小PE的生成

 區合並和內存對齊優化,生成最小PE(不通用)

首先我們要知道PE中的區在哪里,以及怎么使內存對齊縮小,不至於讓PE很大.

首先看下我們的標准PE格式的二進制.(使用Winhex,或者010 Editor都可以)

可以看出,生成的時候默認會為我們生成.const常量區,那么我們可以讓它和代碼區合並嗎?

注意,如果是別的程序,是不可以合並的,因為常量區很有用,但是如果生成最小PE那么你需要合並,

最后一個Hello的位置,則是代碼區

手工連接,使其合並分區,變為最小PE

命令行參數

/ALIGN:內存對齊(2的倍數即可,默認是4096)

/MERGE: 區 = 區  (合並分區)

例如link加上 寫成下面這樣

/ALIGN:4 /MERGE:.rdata=.text

手工編譯連接看下.

不過這樣寫還要另外加選項,不能保證她是否是能運行,不通用,所以使用標准pe講解

 二丶NT頭

首先看下NT頭和文件頭的結構體.

NT頭:

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;              //4個字節的PE標志
    IMAGE_FILE_HEADER FileHeader;      //文件頭
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;//可選頭
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

NT 頭第一個成員,對應的是PE位置,4個字節.

內存分布圖:

在這里,建議大家使用010編輯器,可以使用自定義PE模版,解析PE各個位置內容.

下面模版自動點擊則可以解析

三丶文件頭

文件頭結構體:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                   //機器型號,作用是區別這個exe是哪個CPU可以跑的.重要.
    WORD    NumberOfSections;              //節的數量  (可以理解為匯編中區的個數)現在我們有兩個,一個.rdata 一個.text
    DWORD   TimeDateStamp;               //程序的編譯時間,參考用,沒有實際作用
    DWORD   PointerToSymbolTable;          //符號表地址  我們使用的PDB文件(里面有函數嗎什么的)都存放在這個表中,不過微軟是單獨生成的PDB文件,所以這個字段沒用,主要是給別人用
    DWORD   NumberOfSymbols;             //符號表大小
    WORD    SizeOfOptionalHeader;          //可選頭大小,這個字段很重要.因為要通過這個字段,才知道可選頭是多大,而不懂PE的人求選項頭都是用sizeof()求出來的.所以真正的選項頭大小要靠這個字段
    WORD    Characteristics;            //文件屬性,描述文件信息的.
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

上面只是簡單的寫了下各個成員的作用.

在這里需要注意的是   可選頭大小,文件屬性,以及機器型號.  其余的自己看看.

1.機器型號:

機器型號,在PE中的定義,在VC++6.0中已經給出了.

代碼:

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB             0x01c2
#define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64

看下PE中怎么存儲的.

按照小尾方式,則是 0x014C ,那么對應上面的宏則是386的程序(看注釋),而我們的匯編編譯出來的標准PE也正是標准PE,如果學習PE,自己可以去看下PE存儲

2.可選頭大小

這個地方是我計算偏移得出,根據結構體的類型大小,可以自己計算偏移得出.

可以看出,可選頭的大小是0x00E0 大小,換算成10進制就可以知道,E0是224字節大小,所以根據這個,可以計算出可選頭大小

3.文件屬性

 文件屬性緊跟在E0 00 后面,它是0F 01

文件屬性是按照位來的.

什么意思?

先看下宏定義:

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved externel references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

首先按照小尾方式查看.

0x010f

那么先看第一個1,不看后面的,找百位為1的那么就是 0x100 在上面則可以找到對應的宏,它的注釋是: 

32 bit word machine. 代表了他是一個32位程序
那么看個位是F,那么就找F,但是需要注意,他因為是位運算,所以是 | 連接起來了,那么F 代表了
0x0001  | 0x0002 | 0x0004 | 0x0008  那么分別就對應前4個宏
那么最終想要表示的結果是
32 bit word machine.
Relocation info stripped from file.
File is executable  (i.e. no unresolved externel references)
Line nunbers stripped from file
Local symbols stripped from file.

翻譯過來就是 這是一個32位程序,是一個可執行程序....
那么訓練一下,我隨便寫一個
0x2102
那么 按照 個 十 百 千 位去尋找
先找千位   0x2000 // File is a DLL.                                 說明這是一個DLL文件
再找百位   0x100  // 32 bit word machine.                            說明是一個32位可執行程序
再找十位   十位為零,則沒有.
再找個位   0x0002 // File is executable (i.e. no unresolved externel references).     說明是一個可執行程序
那么總結一下,說明了這個文件是一個 DLL文件,是一個32位程序,是一個可執行程序

總的來說很簡單,主要是熟練運用,在不使用工具的前提下,明白各個位置代表的作用

作者:IBinary
出處:http://www.cnblogs.com/iBinary/
版權所有,歡迎保留原文鏈接進行轉載:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM