PE文件----詳解

今天筆試看到一題:問如下哪個不是PE文件(C)

A. exe B sys.C.inf  D.dll

爲探個究竟,特搜索如下文章:

來自於:http://www.qqtz.com/read-htm-tid-135233.html




一直以來都在學習PE文件結構,從不敢輕視,但是即使如此還是發現自己在這方面有所不足,於是便想到了用純手工方式打造一個完整的可執行的PE文件。在這期間我也查了大量資料,但是這些資料都有一個通病就是不完整,看雪得那個只翻譯了一部分,加解密技術內幕介紹的更是籠統,而且是打造一個只有180字節的PE文件,是高手們茶餘飯後的怡情小遊戲。


       鑑於此,心想爲什麼不自己摸索着手工打造一個完整些的呢?一是加強一下自己對於PE文件的瞭解,二是寫出一篇參考性比較強的文章,給有志於在此發展的朋友們鋪一鋪路,也算是幹了一件利國利民的好事。

       對於手工打造PE文件,我個人認爲至少要分爲三篇文章來闡述,每篇相對獨立,合起來形成一個相對的體系。第一篇文章(也就是本文)用來介紹怎樣用手工打造一個最典型、最簡單的PE文件,而後兩篇文章的問世還要引用潘愛民先生的一句話“還需要時日與機緣”。

       本文介紹的PE文件手工編輯方式,是本着以下三個原則所寫的,望讀者注意:
1、完整性:對於手工打造PE文件所不需注意的字段也進行了必要的介紹,因此整文可能顯得非常臃腫。
2、典型性:完全按照典型的PE文件結構構造,因此對於某些不常見的PE文件結構有一定差距。
3、易學性:對於字段之間的邏輯關係進行了比較細緻的介紹,因此對於一部分底子比較好的讀者來說可能顯得有些囉嗦。

       爲了方便各位閱讀與查閱,我將文章分成了三各部分,以便各位讀者各取所需,不用把寶貴的精力浪費在查找上。
1、PE文件整體信息,提供了一個剖析PE文件的圖表,以便於讀者對於PE文件有一個整體的瞭解,並監督自己的工作進度。
2、對於重點字段的介紹,以及字段之間的邏輯關係,建議首先從這裏開始看。
3、手工構造PE文件字段清單,此清單包含構造一個完整PE文件的每一個字節,跟着這個清單走就可以構造一個PE文件。

        對於第一次手工打造PE文件的朋友們來說,你們可以以“一、整體性息”爲大綱,並參考第三部分一塊一塊的慢慢打造,如果有不懂的地方就去看第二部分。


選讀:爲什麼要手工打造PE文件?

        我們知道,往往從一個系統可執行文件結構上,就可以看整個操作系統的一些特性。也就是說PE裏有Windows操作系統結構與運行機理的影子。由此可見,PE文件必然是一個非常龐雜且邏輯複雜的結構,那麼爲什麼我們還要“自取其辱”來手工製造一個PE文件呢?這就要從PE文件的重要性說起了。
    
        我們現今組成Windows大家庭的主要成員就是PE文件了,裏面包括EXE、DLL、OCX、SYS等一切最有價值的文件都是PE文件格式,出於對版權的考慮或對某種技術的渴求,任何一種與Windows系統相關的行爲最終都要歸集到這裏--PE文件。

        特別是對於想學習加殼、破解、搞虛擬機的朋友們來說,熟知PE文件結構更是必不可少的基本功!

        但也正是由於PE文件的複雜性,我們纔要採取一些特別的辦法來攻克它,其中手工打造PE文件就是一條捷徑。

        你可以想像一下,如果你都可以手工打造PE文件的話,那麼對於PE文件的瞭解更是可見一斑了。但是我還想提醒一下各位讀者,即便是如此,我們所瞭解的也僅僅是一部分,不過一般情況下已經足夠了。


一、整體性息

    這部分以圖表的形式表示PE文件的整體結構。

-------------*-------------------------------------------------*
                 | DOS Header(IMAGE_DOS_HEADER) | -->64 Byte
DOS頭部  --------------------------------------------------
                 | DOS Stub                                            | -->112 Byte
-------------*-------------------------------------------------*
                 | "PE"00 (Signature)                              | -->4 Byte
                  -------------------------------------------------
                 | IMAGE_FILE_HEADER                        | -->20 Byte
PE文件頭 --------------------------------------------------
                 | IMAGE_OPTIONAL_HEADER32          | -->96 Byte
                 ---------------------------------------------------
                 | 數據目錄表                                           | -->128 Byte
-------------*--------------------------------------------------*
                 | IMAGE_SECTION_HEADER                 | -->40 Byte
                 ---------------------------------------------------
   塊表       | IMAGE_SECTION_HEADER                 | -->40 Byte
                  --------------------------------------------------
                 | IMAGE_SECTION_HEADER                 | -->40 Byte  
-------------*--------------------------------------------------*
                 |.text                                                       | -->512 Byte
                 ---------------------------------------------------
    塊          |.rdata                                                   | -->512 Byte
                 ---------------------------------------------------
                 |.data                                                     | -->512 Byte
-------------*-------------------------------------------------*
                 | COFF行號                                            | -->NULL
                 ---------------------------------------------------
調試信息 | COFF符號表                                         | -->NULL
                 ---------------------------------------------------
                 | Code View 調試信息                             | -->NULL
-------------*--------------------------------------------------*

這部分內容的意義有二:
1、對於PE文件有一個整體的認識。
2、方便審查自己的構造進度。

       這裏我們重點介紹怎樣用其審查自己的構造進度,首先希望各位讀者明白我們將要手工構造的一個體積爲2560字節的這個小傢伙,對於初次上手的讀者們來說並不是一件小的工程,因此有必要知道自己現在正做什麼,以及做到哪裏了。

       記得我少年學畫時老師教我們構圖就要從整體到局部,後來自學編程仍然是先實現大的框架再去解決每一個細節問題。OK,現在到了這裏,很顯然我們仍然需要本着從整體到局部的思想來構造我們的PE文件。

       那好,我們先搞明白第一個問題“我們的文件體積是怎麼計算出來的呢”。

       首先我們要知道,PE文件自始至終都是以一種節的思想來構造的,那麼我們就要從節開始。

       對於本文所講述的PE文件來講總共有三個區塊(節),他們分別用來存放可執行代碼、輸入表信息以及全局變量,接觸過PE文件的朋友對於區塊的概念應該不陌生,我們知道Windows下的很多應用程序的文件對齊粒度,也就是大名鼎鼎的FileAlignment字段的值多爲200h Byte,也就是十進制的512 Byte。我們同樣應該知道,對於不足512字節的區段,餘下部分要用00h填充到512字節大小,對與超過部分(例如513字節的區段)我們就要在多分配給他512字節個空間。

       當然,這些基礎知識我想有一部分讀者應該比較熟悉,那麼對於PE文件頭部分呢?也是如此嗎?例如本例中的PE頭就佔用了544個字節,但是很顯然這要使其填充到1024(400h)字節處才能開始第一個區段.text段。

       正是如此,我們整個文件的體積就是PE頭+3個區段的體積之和,也就是PE頭(512*2)+3個區段(512*3)=2560,這也就是我們所構造的PE文件的最終大小了。

       其次我們提前搞清楚一些字段與區段的偏移量也是比較重要的,這裏對於計算方式不再多說,請大家直接看下面的表:
1、PE頭開始處 000000B0h
2、IMAGE_OPTIONAL_HEADER32開始處 000000C8h
3、數據目錄表開始處 00000128h
4、塊表開始處 000001A8h
5、.text塊開始處 00000400h
6、.rdata塊開始處 00000600h
7、.data塊開始處 00000800h

       當然,上面的那些Offset只是針對本文件而言,並不絕對,具體情況還要具體分析。

       到此,本段就告一段落了,剩下的兩段爲了提高效率我並沒有加以潤色,全都是乾貨,希望各位讀者能吃下這兩塊營養豐富的壓縮餅乾……


二、重點字段介紹

       這裏只對需手工構造的字段進行着重介紹,詳細的PE文件結構字段清單請見第三部分。

1、DOS頭部
  1-1  DOS Header
    1-1-1   e_magic  [WORD]  -->4D 5A (* DOS可執行文件頭標記)
    註釋:此處值總是爲MZ的16進制碼。

    1-1-19  e_lfanew  [DWORD]  -->B0 00 00 00 (* 指向PE文件頭的偏移量。0xB0=64+112)
    註釋:此處的值正好爲爲DOS頭部的大小,因爲DOS頭部後面就是PE文件頭部分了。

2  PE文件頭
  2-1  "PE"00
    2-1-1  Signature  [DWORD] -->50450000h (* PE文件頭標記)
    註釋:此處的值總是爲PE的16進制值加0000h。

  2-2  IMAGE_FILE_HEADER
    2-2-1  Machine  [WORD]  -->4C 01 (* 可執行文件的目標CPU類型)
    註釋:此PE文件可以運行於哪個CPU下,其標誌就爲相應的值。
    *------------------------------*
    |     機器            |  標誌   |
    -------------------------------
    | Intel i386         | 14Ch   |
    -------------------------------
    | MIPS R3000    | 162h   |
    -------------------------------
    | MIPS R4000    | 166h   |
    -------------------------------
    | Alpha AXP       | 184h   |
    -------------------------------
    | Power PC        | 1F0h   |
    *-----------------------------*

    2-2-2  NumberOfSections  [WORD]  -->03 00 (* 區塊數目)
    註釋:此值決定此PE文件的區塊數目,本文件爲3個區塊。

    2-2-6  SizeOfOptionalHeadr  [WORD]  -->E0 00 (* PE頭(IMAGE_OPTIONAL_HEADER32)大小)
    註釋:此值表示PE文件頭的大小。

    2-2-7  Characteristics  [WORD]  -->0F 01 (* 文件屬性)
    註釋:此值爲文件的執行屬性。EXE文件此值一般爲010Fh,DLL文件此值一般爲0210h。

  2-3  IMAGE_OPTIONAL_HEADER32
    2-3-1   Magic  [WORD]  -->0B 01 (* 標記字)
    註釋:此處是一個標記字,用於描述次PE文件的映像類型。ROM映像爲0107h;普通可執行映像010Bh;PE32+則是020Bh。

    2-3-7   AddressOfEntryPoint  [DWORD] -->00 10 00 00 (* 程序執行入口RAV)
    註釋:通俗的說就是指向可執行代碼區塊(例如.text)的首地址。

    2-3-10  ImageBase  [DWORD] -->00 00 40 00 (* 程序默認裝入基地址)
    註釋:是指文件在內存中首選的裝入地址。

    2-3-11  SectionAlignment  [DWORD] -->00 10 00 00 (* 內存中區塊對齊值)
    註釋:PE文件被裝入內存中時的塊對齊大小,也叫做塊粒度。其默認的對其尺寸是CPU的頁尺寸。

    2-3-12  FileAlignment  [DWORD] -->00 02 00 00 (* 文件中區塊對齊值)
    註釋:磁盤上PE文件的區塊對齊大小。這個值必須是2的冪,並且最小爲200h。

    2-3-17  MajorSubsystemVersion  [WORD]  -->04 00 (* 運行所需最低子系統主版本號)
    註釋:要求最低的子系統主版本號,一般情況下都爲4。

    2-3-18  MinorSubsystemVersion  [WORD]  -->00 00 (* 運行所需最低子系統次版本號)
    註釋:要求最低的子系統次版本號,一般情況下都爲0。

    2-3-20  SizeOfImage  [DWORD] -->00 40 00 00 (* 映像裝入內存後的總尺寸)
    註釋:指的是裝入文件從Image Base到最後一個區塊的總大小。

    2-3-21  SizeOfHeaders  [DWORD] -->00 04 00 00 (* DOS頭、PE頭、區塊表的總大小)
    註釋:指的是DOS頭、PE頭與區塊表的總大小,並且所有這些項目都出現在PE文件中任何代碼或數據塊之前。此值遵守文件對齊粒度。

    2-3-23  Subsystem [WORD]  -->03 00 (* 文件子系統)
    註釋:標明可執行文件所期望的子系統(用戶界面類型)。
          *----*-------------------------------------*
          | 值 |          子系統                          |
          *----*-------------------------------------*
          | 0  | 未知                                        |
          --------------------------------------------
          | 1  | 不需要子系統(如驅動程序) |
          ---------------------------------------------
          | 2  | 圖形接口子系統(GUI)          |
          ---------------------------------------------
          | 3  | 字符子系統(CUI)                 |
          ---------------------------------------------
          | 5  | OS/2字符子系統                      |
          ---------------------------------------------
          | 7  | POSIX字符子系統                    |
          ---------------------------------------------
          | 8  | 保留                                         |
          ---------------------------------------------
          | 9  | Windows CE圖形界面              |
          *----*--------------------------------------*

    2-3-30  NumberOfRvaAndSizes  [DWORD] -->10 00 00 00 (* 數據目錄標的項數,默認總爲16)
    註釋:數據目錄的項數。這個字段字NT系統發佈以來就一直是16。

  2-4  數據目錄表
    2-4-2   Import Table
    註釋:輸入表

      2-4-2-1  VirtualAddress [DWORD] -->10 20 00 00 (* 數據塊的起始RAV)
      註釋:輸入表的起始地址,要去除IAT所佔空間,直接從第一個IID開始。

      2-4-2-2  Size  [DWORD] -->3C 00 00 00 (* 數據塊的長度)
      註釋:從第一個IID到最後一個IMAGE_IMPORT_BY_NAME的總長度。

3  塊表
  3-1  IMAGE_SECTION_HEADER (1 .text)
    3-1-1   Name  [BYTE]  -->2E 74 65 78 74 00 00 00 (* 8個字節的塊名)
    註釋:此區塊的名稱,限制在8字節以內。

    3-1-2   VirtualSize  [DWORD] -->26 00 00 00 (* 被實際使用的區塊大小)
    註釋:此區塊包含數據的大小。

    3-1-10  Characteristics  [DWORD] -->20 00 00 60 (* 該區塊的讀寫、執行屬性)
    註釋:

         *----------------------------------------------------------------*
          |  字段值   |              用 途                                         |
          -----------------------------------------------------------------
          | 00000020h | 包含代碼,常與10000000h一起設置 |
          -----------------------------------------------------------------
          | 00000040h | 包含已初始化數據                              |
          -----------------------------------------------------------------
          | 00000080h | 包含未初始化數據                              |
          -----------------------------------------------------------------
          | 02000000h | 可以被丟棄                                         |
          -----------------------------------------------------------------
          | 10000000h | 共享塊                                                |
          -----------------------------------------------------------------
          | 20000000h | 可執行                                                |
          -----------------------------------------------------------------
          | 40000000h | 可讀                                                   |
          -----------------------------------------------------------------
          | 80000000h | 可寫                                                   |
          -----------------------------------------------------------------

4  塊
  4-1  .text (* 此區段是一段彙編代碼的16進制形式,功能是彈出一個MessageBox提示框)
    -->HEX
    6A 00 68 00 30 40 00 68  07 30 40 00 6A 00 E8 07
    00 00 00 6A 00 E8 06 00  00 00 FF 25 08 20 40 00
    FF 25 00 20 40 00
    註釋:這是下面彙編指令的16進制模式。

    -->ASM
    00401000  6A 00         PUSH 0
    00401002  68 00304000   PUSH first_PE.00403000
    00401007  68 07304000   PUSH first_PE.00403007
    0040100C  6A 00         PUSH 0
    0040100E  E8 07000000   CALL <JMP.&user32.MessageBoxA>
    00401013  6A 00         PUSH 0
    00401015  E8 06000000   CALL <JMP.&kernel32.ExitProcess>
    0040101A  FF25 08204000 JMP DWORD PTR DS:[<&user32.MessageBoxA>]
    00401020  FF25 00204000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>]

  4-2  .rdata (* 該區塊包含輸入表)
    4-2-1  IMAGE_THUNK_DATA32 (IAT 1)
    註釋:這裏有很多人都搞不明白,其實IMAGE_THUNK_DATA32是一個聯合體,可以同時代表IAT與INT。

      4-2-1-1 AddressOfData [DWORD] -->76 20 00 00 (* 指向IMAGE_IMPORT_BY_NAME的RVA)
      註釋:作爲IAT時我們就會使用他的AddressOfData成員,用來存放指向IMAGE_IMPORT_BY_NAME的RVA,當程序裝入內存後,只與IAT交換信息,輸入表的其他部分就不再需要了。

      【由於本例子兩個API函數引自與兩個不同的DLL文件,所以要補00000000h結束。】
       註釋:這裏同樣有很多人不明白,每當屬於某一個DLL文件的API羅列完畢後,就要輸入00加以結束。

    4-2-3  IMAGE_IMPORT_DESCRIPTOR (IID 1)
    註釋:這裏稍微複雜些,它的作用就是使用INT指定某個DLL文件中的API函數,並配合IAT指向相關API的地址。

      4-2-3-1 OriginalFirstThunk [DWORD] -->4C 20 00 00 (* 指向輸入名稱表INT的RVA)
      註釋:這裏指定某個系統DLL文件中的API函數。

      4-2-3-4 Name  [DWORD] -->6A 20 00 00 (* 指向DLL名字的RVA與指針)
      註釋:這裏指定某個系統DLL文件。

      4-2-3-5 FirstThunk  [DWORD] -->08 20 00 00 (* 指向輸入地址表IAT的RVA)
      註釋:這裏指定相關的IAT,並由IAT在IMAGE_IMPORT_BY_NAME中獲得相應API的地址。

    4-2-4  IMAGE_IMPORT_DESCRIPTOR (IID 2)
      4-2-4-1 OriginalFirstThunk [DWORD] -->54 20 00 00 (* 指向輸入名稱表INT的RVA)
      4-2-4-4 Name               [DWORD] -->84 20 00 00 (* 指向DLL名字的RVA與指針)
      4-2-4-5 FirstThunk         [DWORD] -->00 20 00 00 (* 指向輸入地址表IAT的RVA)
      【填充20個00h空字節做結尾標記】
      註釋:關於填充20個字節作爲IID的結尾標記只是一個規律,還沒找到相關資料證實。

    4-2-5  IMAGE_THUNK_DATA32 (INT 1)
      4-2-5-1  ForwarderString [DWORD] -->5C 20 00 00 (* 指向一個轉向字符的RVA)
      註釋:直接指向相關API函數。

      【由於本例子兩個API函數引自與兩個不同的DLL文件,所以要補00000000h結束。】
       註釋:與上面的IAT原理一樣,就是這樣的一個格式。

    4-2-7  IMAGE_IMPORT_BY_NAME ( 1 )
      4-2-7-2  Name [BYTE] -->4D 65 73 73 61 67 65 42 6F 78 41 (* MessageBoxA的16進制碼)
      註釋:相關API函數的16進制代碼。

      【後跟輸出此函數的DLL名稱16進制碼00 75 73 65 72 33 32 2E 64 6C 6C 00 00 user32.dll】
      註釋:相關係統的API函數羅列完畢後,通常都在最後一個API後空00h,並跟着這個DLL名稱的16進制數據。

      總結:這裏羅列了一些需要手工構建的部分結構,如果想看更加詳細的請關注下面的內容。



三、PE文件結構字段清單

1  DOS頭部
  1-1  DOS Header
    1-1-1   e_magic     [WORD]            -->4D 5A (* DOS可執行文件頭標記)
    1-1-2   e_cblp        [WORD]              ->00 00 (文件最後頁的字節數)
    1-1-3   e_cp           [WORD]              ->00 00 (文件頁數)
    1-1-4   e_crlc         [WORD]               ->00 00 (重定位元素個數)
    1-1-5   e_cparhdr  [WORD]               ->00 00 (以段落爲單位的頭部大小)
    1-1-6   e_minalloc  [WORD]              ->00 00 (所需的最小附加段)
    1-1-7   e_maxalloc [WORD]               ->00 00 (所需的最大附加段)
    1-1-8   e_ss           [WORD]              ->00 00 (初始的堆棧段(SS)相對偏移量值)
    1-1-9   e_sp           [WORD]              ->00 00 (初始的堆棧指針(SP)值)
    1-1-10  e_csum     [WORD]               ->00 00 (校驗和)
    1-1-11  e_ip           [WORD]               ->00 00 (初始的指令指針(IP)值)
    1-1-12  e_cs          [WORD]               ->00 00 (初始的代碼段(CS)相對偏移量值)
    1-1-13  e_lfarlc      [WORD]               ->00 00 (重定位表在文件中的偏移地址)
    1-1-14  e_ovno      [WORD]               ->00 00 (覆蓋號)
    1-1-15  e_res         [WORD] 4 dup     ->00 00 (保留字,一般都是爲確保對齊而預留)
    1-1-16  e_oemid     [WORD]              ->00 00 (OEM 標識符,相對於 e_oeminfo)
    1-1-17  e_oeminfo  [WORD]              ->00 00 (OEM 信息,即 e_oemid 的細節)
    1-1-18  e_res2       [WORD] 10 dup  ->00 00 (保留字,一般都是爲確保對齊而預留)
    1-1-19  e_lfanew    [DWORD]          -->B0 00 00 00 (* 指向PE文件頭的偏移量。0xB0=64+112)
  1-2  DOS Stub
    全部填00h

2  PE文件頭
  2-1  "PE"00
    2-1-1  Signature [DWORD] -->50450000h (* PE文件頭標記)
  2-2  IMAGE_FILE_HEADER
    2-2-1  Machine                        [WORD]    -->4C 01 (* 可執行文件的目標CPU類型)
    2-2-2  NumberOfSections        [WORD]    -->03 00 (* 區塊數目)
    2-2-3  TimeDateStamp            [DWORD]   ->00 00 00 00 (文件創建的時間與日期)
    2-2-4  PointerToSymbolTable [DWORD]   ->00 00 00 00 (指向符號表,用於調試)
    2-2-5  NumberOfSymbols        [DWORD]   ->00 00 00 00 (符號表中的符號個數,用於調試)
    2-2-6  SizeOfOptionalHeadr    [WORD]    -->E0 00 (* PE頭(IMAGE_OPTIONAL_HEADER32)大小)
    2-2-7  Characteristics              [WORD]    -->0F 01 (* 文件屬性)
  2-3  IMAGE_OPTIONAL_HEADER32
    2-3-1   Magic                                       [WORD]     -->0B 01 (* 標記字)
    2-3-2   MajorLinkerVersion                  [BYTE]        ->00 (連接程序主版本號)
    2-3-3   MinorLinkerVersion                  [BYTE]        ->00 (連接程序次版本號)
    2-3-4   SizeOfCode                              [DWORD]   ->00 00 00 00 (所有含代碼區塊的總大小)
    2-3-5   SizeOfInitializedData                 [DWORD]   ->00 00 00 00 (所有初始化數據區塊大總大小)
    2-3-6   SizeOfUninitializedData             [DWORD]   ->00 00 00 00 (所有未初始化數據區塊大總大小)
    2-3-7   AddressOfEntryPoint                [DWORD]  -->00 10 00 00 (* 程序執行入口RAV)
    2-3-8   BaseOfCode                             [DWORD]   ->00 00 00 00 (代碼區塊起始RAV)
    2-3-9   BaseOfData                              [DWORD]   ->00 00 00 00 (數據區塊起始RAV)
    2-3-10  ImageBase                               [DWORD]  -->00 00 40 00 (* 程序默認裝入基地址)
    2-3-11  SectionAlignment                      [DWORD]  -->00 10 00 00 (* 內存中區塊對齊值)
    2-3-12  FileAlignment                            [DWORD]  -->00 02 00 00 (* 文件中區塊對齊值)
    2-3-13  MajorOperatingSystemVersion [WORD]      ->00 00 (操作系統主版本號)
    2-3-14  MinorOperatingSystemVersion [WORD]      ->00 00 (操作系統次版本號)
    2-3-15  MajorImageVersion                   [WORD]      ->00 00 (用戶自定義主版本號)
    2-3-16  MinorImageVersion                   [WORD]      ->00 00 (用戶自定義次版本號)
    2-3-17  MajorSubsystemVersion           [WORD]     -->04 00 (* 運行所需最低子系統主版本號)
    2-3-18  MinorSubsystemVersion           [WORD]     -->00 00 (* 運行所需最低子系統次版本號)
    2-3-19  Win32VersionValue                  [DWORD]    ->00 00 00 00 (保留值,通常爲0)
    2-3-20  SizeOfImage                             [DWORD]   -->00 40 00 00 (* 映像裝入內存後的總尺寸)
    2-3-21  SizeOfHeaders                         [DWORD]   -->00 04 00 00 (* DOS頭  PE頭  區塊表的總大小)
    2-3-22  CheckSum                                [DWORD]    ->00 00 00 00 (映像效驗和)
    2-3-23  Subsystem                                [WORD]     -->03 00 (* 文件子系統)
    2-3-24  DllCharacteristics                      [WORD]      ->00 00 (顯示DLL特性的旗標)
    2-3-25  SizeOfStackReserve                 [DWORD]    ->00 00 00 00 (初始化堆棧總大小)
    2-3-26  SizeOfStackCommit                   [DWORD]   ->00 00 00 00 (初始化實際提交堆棧大小)
    2-3-27  SizeOfHeapReserve                  [DWORD]   ->00 00 00 00 (初始化保留堆棧大小)
    2-3-28  SizeOfHeapCommit                   [DWORD]   ->00 00 00 00 (初始化實際保留堆棧大小)
    2-3-29  LoaderFlags                              [DWORD]  ->00 00 00 00 (與調試相關,默認值爲0)
    2-3-30  NumberOfRvaAndSizes             [DWORD] -->10 00 00 00 (* 數據目錄標的項數,默認總爲16)
  2-4  數據目錄表
    2-4-1   Export Table
      2-4-1-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-1-2   Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-2   Import Table
      2-4-2-1   VirtualAddress [DWORD] -->10 20 00 00 (* 數據塊的起始RAV)
      2-4-2-2   Size                 [DWORD] -->3C 00 00 00 (* 數據塊的長度)
    2-4-3   Resources Table
      2-4-3-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-3-2   Size                 [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-4   Exception Table
      2-4-4-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-4-2   Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-5   Security Table
      2-4-5-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-5-2   Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-6   Base relocation Table
      2-4-6-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-6-2   Size                 [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-7   Debug
      2-4-7-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-7-2   Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-8   Copyright
      2-4-8-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-8-2   Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-9   Global ptr
      2-4-9-1   VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-9-2   Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-10  Threda local storage(TLS)
      2-4-10-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-10-2  Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-11  Load configuration
      2-4-11-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-11-2  Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-12  Bound import
      2-4-12-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-12-2  Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-13  Import Address Table(IAT)
      2-4-13-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-13-2  Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-14  Delay import  
      2-4-14-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-14-2  Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-15  COM descriptor
      2-4-15-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-15-2  Size                 [DWORD]  ->00 00 00 00 (數據塊的長度)
    2-4-16  保留
      2-4-16-1  VirtualAddress [DWORD]  ->00 00 00 00 (數據塊的起始RAV)
      2-4-16-2  Size                  [DWORD]  ->00 00 00 00 (數據塊的長度)

3  塊表
  3-1  IMAGE_SECTION_HEADER (1 .text)
    3-1-1   Name                            [BYTE]       -->2E 74 65 78 74 00 00 00 (* 8個字節的塊名)
    3-1-2   VirtualSize                     [DWORD]  -->26 00 00 00 (* 被實際使用的區塊大小)
    3-1-3   VirtualAddress              [DWORD]  -->00 10 00 00 (* 區塊的RAV地址)
    3-1-4   SizeOfRawData             [DWORD]  -->00 02 00 00 (* 該塊在磁盤中所佔的大小)
    3-1-5   PointerToRawData        [DWORD]  -->00 04 00 00 (* 該塊在磁盤文件中的偏移)
    3-1-6   PointerToRelocations    [DWORD]   ->00 00 00 00 (在OBJ文件中使用,重定位偏移)
    3-1-7   PointerToLinenumbers  [DWORD]   ->00 00 00 00 (行號表的偏移,調試中使用)
    3-1-8   NumberOfRelocations    [WORD]     ->00 00 (在OBJ文件中使用,重定位項數目)
    3-1-9   NumberOfLinenumbers  [WORD]     ->00 00 (行號表中行號的數目)
    3-1-10  Characteristics              [DWORD] -->20 00 00 60 (* 該區塊的讀寫  執行屬性)
  3-2  IMAGE_SECTION_HEADER (2 .rdata)
    3-2-1   Name                            [BYTE]     -->2E 72 64 61 74 61 00 00 (* 8個字節的塊名)
    3-2-2   VirtualSize                    [DWORD] -->92 00 00 00 (* 被實際使用的區塊大小)
    3-2-3   VirtualAddress              [DWORD] -->00 20 00 00 (* 區塊的RAV地址)
    3-2-4   SizeOfRawData             [DWORD] -->00 02 00 00 (* 該塊在磁盤中所佔的大小)
    3-2-5   PointerToRawData        [DWORD] -->00 06 00 00 (* 該塊在磁盤文件中的偏移)
    3-2-6   PointerToRelocations   [DWORD]   ->00 00 00 00 (在OBJ文件中使用,重定位偏移)
    3-2-7   PointerToLinenumbers  [DWORD]  ->00 00 00 00 (行號表的偏移,調試中使用)
    3-2-8   NumberOfRelocations    [WORD]    ->00 00 (在OBJ文件中使用,重定位項數目)
    3-2-9   NumberOfLinenumbers  [WORD]    ->00 00 (行號表中行號的數目)
    3-2-10  Characteristics              [DWORD] -->40 00 00 40 (* 該區塊的讀寫  執行屬性)
  3-3  IMAGE_SECTION_HEADER (3 .data)
    3-3-1   Name                             [BYTE]      -->2E 64 61 74 61 00 00 00 (* 8個字節的塊名)
    3-3-2   VirtualSize                     [DWORD]  -->3E 00 00 00 (* 被實際使用的區塊大小)
    3-3-3   VirtualAddress               [DWORD]  -->00 30 00 00 (* 區塊的RAV地址)
    3-3-4   SizeOfRawData             [DWORD]  -->00 02 00 00 (* 該塊在磁盤中所佔的大小)
    3-3-5   PointerToRawData        [DWORD]  -->00 08 00 00 (* 該塊在磁盤文件中的偏移)
    3-3-6   PointerToRelocations    [DWORD]   ->00 00 00 00 (在OBJ文件中使用,重定位偏移)
    3-3-7   PointerToLinenumbers  [DWORD]   ->00 00 00 00 (行號表的偏移,調試中使用)
    3-3-8   NumberOfRelocations    [WORD]     ->00 00 (在OBJ文件中使用,重定位項數目)
    3-3-9   NumberOfLinenumbers  [WORD]     ->00 00 (行號表中行號的數目)
    3-3-10  Characteristics              [DWORD] -->40 00 00 C0 (* 該區塊的讀寫、執行屬性)
    【由於FileAlignment爲0x200大小,而此時整個PE頭已經超過200,所以要填0到0x400處】

4  塊
  4-1  .text (* 此區段是一段彙編代碼的16進制形式,功能是彈出一個MessageBox提示框)
    -->HEX
    6A 00 68 00 30 40 00 68  07 30 40 00 6A 00 E8 07
    00 00 00 6A 00 E8 06 00  00 00 FF 25 08 20 40 00
    FF 25 00 20 40 00
    -->ASM
    00401000  6A 00         PUSH 0
    00401002  68 00304000   PUSH first_PE.00403000
    00401007  68 07304000   PUSH first_PE.00403007
    0040100C  6A 00         PUSH 0
    0040100E  E8 07000000   CALL <JMP.&user32.MessageBoxA>
    00401013  6A 00         PUSH 0
    00401015  E8 06000000   CALL <JMP.&kernel32.ExitProcess>
    0040101A  FF25 08204000 JMP DWORD PTR DS:[<&user32.MessageBoxA>]
    00401020  FF25 00204000 JMP DWORD PTR DS:[<&kernel32.ExitProcess>]
    【由於FileAlignment爲0x200大小,而此時整個PE頭未超過200,所以要填0到0x600處】
  4-2  .rdata (* 該區塊包含輸入表)
    4-2-1  IMAGE_THUNK_DATA32 (IAT 1)
      4-2-1-1 AddressOfData [DWORD] -->76 20 00 00 (* 指向IMAGE_IMPORT_BY_NAME的RVA)
      【由於本例子兩個API函數引自與兩個不同的DLL文件,所以要補00000000h結束。】
    4-2-2  IMAGE_THUNK_DATA32 (IAT 2)
      4-2-1-2 AddressOfData [DWORD] -->5C 20 00 00 (* 指向IMAGE_IMPORT_BY_NAME的RVA)
      【由於本例子兩個API函數引自與兩個不同的DLL文件,所以要補00000000h結束。】
    4-2-3  IMAGE_IMPORT_DESCRIPTOR (IID 1)
      4-2-3-1 OriginalFirstThunk  [DWORD] -->4C 20 00 00 (* 指向輸入名稱表INT的RVA)
      4-2-3-2 TimeDateStamp      [DWORD]  ->00 00 00 00 (32位的時間標誌)
      4-2-3-3 ForwarderChain      [DWORD]  ->00 00 00 00 (被轉向API的索引)
      4-2-3-4 Name                      [DWORD] -->6A 20 00 00 (* 指向DLL名字的RVA與指針)
      4-2-3-5 FirstThunk              [DWORD] -->08 20 00 00 (* 指向輸入地址表IAT的RVA)
    4-2-4  IMAGE_IMPORT_DESCRIPTOR (IID 2)
      4-2-4-1 OriginalFirstThunk  [DWORD] -->54 20 00 00 (* 指向輸入名稱表INT的RVA)
      4-2-4-2 TimeDateStamp      [DWORD]  ->00 00 00 00 (32位的時間標誌)
      4-2-4-3 ForwarderChain      [DWORD]  ->00 00 00 00 (被轉向API的索引)
      4-2-4-4 Name                      [DWORD] -->84 20 00 00 (* 指向DLL名字的RVA與指針)
      4-2-4-5 FirstThunk              [DWORD] -->00 20 00 00 (* 指向輸入地址表IAT的RVA)
      【填充20個00h空字節做結尾標記】
    4-2-5  IMAGE_THUNK_DATA32 (INT 1)
      4-2-5-1  ForwarderString [DWORD] -->5C 20 00 00 (* 指向一個轉向字符的RVA)
      【由於本例子兩個API函數引自與兩個不同的DLL文件,所以要補00000000h結束。】
    4-2-6  IMAGE_THUNK_DATA32 (INT 2)
      4-2-6-1  ForwarderString [DWORD] -->76 20 00 00 (* 指向一個轉向字符的RVA)
      【由於本例子兩個API函數引自與兩個不同的DLL文件,所以要補00000000h結束。】
    4-2-7  IMAGE_IMPORT_BY_NAME ( 1 )
      4-2-7-1  Hint    [WORD] -->00 00 (* 此函數所駐留DLL的輸出表序號)
      4-2-7-2  Name [BYTE]   -->4D 65 73 73 61 67 65 42 6F 78 41 (* MessageBoxA的16進制碼)
      【後跟輸出此函數的DLL名稱16進制碼00 75 73 65 72 33 32 2E 64 6C 6C 00 00 user32.dll】
    4-2-8  IMAGE_IMPORT_BY_NAME ( 2 )
      4-2-8-1  Hint    [WORD] -->00 00h  (* 此函數所駐留DLL的輸出表序號)
      4-2-8-2  Name [BYTE]   -->45 78 69 74 50 72 6F 63 65 73 73 (* ExitProcess的16進制碼)
      【後跟輸出此函數的DLL名稱16進制碼00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C 00 00 kernel32.dll】
      【由於FileAlignment爲0x200大小,而此時整個PE頭未超過200,所以要填0到0x800處】
  4-3  .data
       填充數據
       -->HEX
       CF FB CF A2 BF F2 00 54  68 65 20 66 69 72 73 74
       20 50 45 20 66 69 6C 65  21 20 42 59 3A 41 31 50
       61 73 73 20 48 74 74 70  3A 2F 2F 61 31 70 61 73
       73 2E 62 6C 6F 67 2E 31  36 33 2E 63 6F 6D 00
       -->TEXT
       消息框 The first PE file! BY:A1Pass Http://a1pass.
      【由於FileAlignment爲0x200大小,而此時整個PE頭未超過200,所以要填0到0x9FF處】

    到此,介紹手工構造典型PE文件的知識就講完了,雖然過程枯燥,但是當第一個first_PE.EXE從你手中誕生時,你會覺得這一切都太值了!這點你應該相信我,因爲我當時就是這種感覺,最後祝各位成功!
          我是看不怎麼懂哦~
                         看的懂的就頂下帖子撒!!!!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章