【EXE PE】初識pe結構--手寫可執行程序

原文地址http://bbs.pediy.com/showthread.php?t=48590

【文章標題】: 手寫可執行程序

【文章作者】: dncwbc
【作者郵箱】: [email protected]
【作者QQ號】: 182445917
【軟件名稱】: Hello World!
【軟件大小】: 2.5K
【下載地址】: 自己搜索下載
【編寫語言】: 機器碼
【使用工具】: VC++ 6.0
【操作平臺】: Winxp
【作者聲明】: 只是感興趣,沒有其他目的。失誤之處敬請諸位大俠賜教!
--------------------------------------------------------------------------------
【詳細過程】
      
        最近,學習PE結構的知識。之後深有感觸,隨即便萌發了不依賴任何開發環境和編譯器,純手工寫一個小程序的念
  頭。爲了簡單而又令所有學習程序開發的人感到親切,就寫一個Hello World! 程序吧...
    
        在這裏,我們首先複習一下Win32可執行程序的大體結構,就是通常所說的PE結構。
    PE 的意思就是Portable Executable(可移植的執行體)。
    PE結構如下圖:
    
    ︱ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄︱
    ︱     MS-DOS       ︱
    ︱    MZ  頭部      ︱--------------> 64 byte
    ︱                  ︱
    ︱  ̄  ̄  ̄  ̄  ̄  ̄︱
    ︱     MS-DOS       ︱
    ︱ 實模式殘餘程序   ︱--------------> 112 byte 
    ︱                  ︱
    ︱ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄︱
    ︱    PE文件標誌    ︱--------------> 4 byte
    ︱                  ︱
    ︱ ̄  ̄  ̄  ̄  ̄  ̄ ︱
    ︱     PE文件頭     ︱--------------> 20 byte
    ︱                  ︱
    ︱ ̄  ̄  ̄  ̄  ̄  ̄ ︱
    ︱   PE文件可選頭   ︱--------------> 224 byte
    ︱                  ︱
    ︱ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄︱
    ︱     各段頭部     ︱--------------> n * 40 byte
    ︱                  ︱
      ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
    
    DOS MZ header:所有PE文件(甚至32位的DLLs) 必須以一個簡單的DOS MZ header 開始。有了它,一旦程序在DOS下執
  行,DOS就能識別出這是有效的執行體,然後運行緊隨MZ header 之後的DOS程序。以此達到對Dos系統的兼容。
    (DOS MZ header總共佔用64byte)
    
    MS-DOS 實模式殘餘程序:實際上是個有效的EXE,在不支持PE文件格式的操作系統中,它將簡單顯示一個錯誤提示,
    大多數情況下它是由彙編器/編譯器自動生成。通常,它簡單調用中斷21h服務9來顯示字符串
    "This program cannot run in DOS mode"。(在我們寫的程序中,他不是必須的,可以不於以實現,但是要保留其大
  小,大小爲112byte,爲了簡潔,我們就用00來填充。)
    
    PE文件標誌:是PE文件結構的起始標誌。(長度4byte, Windows程序此值必須爲0x50450000)
    
    PE文件頭:是PE相關結構 IMAGE_NT_HEADERS 的簡稱,其中包含了許多PE裝載器用到的重要域。執行體在支持PE文件
  結構的操作系統中執行時,PE裝載器將從DOS MZ header中找到PE header的起始偏移量,跳過了MS-DOS 實模式殘餘程序 ,
  直接定位到真正的文件頭PE header, 長度20byte)。
    
    PE文件可選頭:雖然它的名字是“可選頭部”,但是請確信:這個頭部並非“可選”,而是“必需”的。
    (長度 224byte )
     
    各段頭部:一個Windows NT的應用程序典型地擁有9個預定義段,它們是.text、.bss、.rdata、.data、.rsrc、
    .edata、.idata、.pdata和.debug。一些應用程序不需要所有的這些段,同樣還有一些應用程序爲了自己特殊
    的需要而定義了更多的段。(每段佔40byte,我們這裏也不需要所有的段,僅需3個段。)
    
        以上僅僅是對PE結構各部分的大體講解,先熱熱身而已。接下來在手寫這個Hello World!程序中,我會詳細介紹
  每個字節的含義。
        
        首先準備一下工具(肯定有人要問了,不是純手寫嗎?怎麼還要準備工具啊?),畢竟一個十六進制編輯器是少
  不了的,否則在哪寫啊?我在這裏就使用VC++ 6.0所攜帶的十六進制編輯器。好了,工具暫時就這個了,開工了。
    
        打開VC,選擇文件,新建菜單項,然後選擇一個二進制文件,確定。一切就緒了,下面就開始我們的手寫可執行
  程序旅程吧......

        首先我們來完成“DOS MZ header”,“DOS MZ header”的功能前面已經講過,在這裏就不多說了,我們直接來
  看一下如何實現他。“DOS MZ header”總共64byte,他對應的結構是IMAGE_DOS_HEADER ,在WINNT.H文件中有定義。通過
  這個結構我們可以看到,這64字節被分成19個成員,每個成員都有他的功能,與其說我們在一個字節一個字節的手寫可執行
  程序,倒不如說我們是在一個成員一個成員的寫。因爲單獨的一個字節並不一定具有什麼意義。我們在學習過程中,就是要
  把整個部分拆分成幾個成員,然後一個成員一個成員的去學習。(實際上並不是我們去拆分,人家已經幫我們拆分好了。我
  們只需按照標準的結構去學習就可以了。所有的結構都定義在WINNT.H文件中。)

  typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
      WORD   e_magic;                     // Magic number
      WORD   e_cblp;                      // Bytes on last page of file
      WORD   e_cp;                        // Pages in file
      WORD   e_crlc;                      // Relocations
      WORD   e_cparhdr;                   // Size of header in paragraphs
      WORD   e_minalloc;                  // Minimum extra paragraphs needed
      WORD   e_maxalloc;                  // Maximum extra paragraphs needed
      WORD   e_ss;                        // Initial (relative) SS value
      WORD   e_sp;                        // Initial SP value
      WORD   e_csum;                      // Checksum
      WORD   e_ip;                        // Initial IP value
      WORD   e_cs;                        // Initial (relative) CS value
      WORD   e_lfarlc;                    // File address of relocation table
      WORD   e_ovno;                      // Overlay number
      WORD   e_res[4];                    // Reserved words
      WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
      WORD   e_oeminfo;                   // OEM information; e_oemid specific
      WORD   e_res2[10];                  // Reserved words
      LONG   e_lfanew;                    // File address of new exe header
    } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
    
        第一個成員佔2個字節,它被用於表示一個MS-DOS兼容的文件類型,他的值是固定的----“4D5A”
    (注意:因爲我們是在十六進制編輯器下寫數據,所以所有的數據格式都是十六進制式的。但是我們在開發環境中默認
    都是十進制的,所以必須在數據前加 0x ,即:0x4D5A。而我爲了方便,就直接寫成“4D5A”,也就是直接輸入到編輯器
    中的值,是十六進制,後面的都照此規定書寫。)
    
        第2個成員到第18個成員總共58個字節,是對DOS程序環境的初始化等操作,對於我們這個程序來說,沒什麼影響,
    我們通通用“00”來填充。(如果讀者想對其進行詳細瞭解,請查閱相關書籍。)注意:因爲我們不可能把PE結構所
    有的東西都面面俱到,他十分的龐大。當然也沒有必要都去記他,只需掌握關鍵的地方就可以了。以後我們都將把不
    影響程序執行的成員填充爲零,這樣做,一方面使程序看起來簡潔,另一方面可以使您快速定位PE結構中要重點掌握
    的地方。
    
        第19個成員非常重要,他佔4個字節,用來表示“PE文件標誌”在文件中的偏移,單位是byte。而從上圖中可以看到
  “PE文件標誌”緊隨“MS-DOS 實模式殘餘程序”其後。知道這一點,我們就可以計算一下了,我們的“DOS MZ header”總
   共64 byte,後面的“MS-DOS 實模式殘餘程序”佔112 byte, 64 + 112 = 176 byte,但是要注意,我們這裏的176可是十進制
   的,轉化成十六進制是B0,對了,就是這個值,因爲是4個字節,所以我們應該填“B0000000”。

      接下來我們來完成“MS-DOS 實模式殘餘程序”,我們已經知道,他是用在DOS下執
  行的,我們這裏可以直接用“00”來填充,注意總共112 byte。 這兩部分完成之後代碼如下:
    
    00000  4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00  MZ..............
    00010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00030  00 00 00 00 00 00 00 00 00 00 00 00 B0 00 00 00  ............?...
    00040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    00090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    000a0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
    
     接下來我們便進入主題,開始寫真正的PE結構部分:微軟將“PE文件標誌”,“PE文件頭 ”,“PE文件可選頭 ”
  這三個部分用一個結構來定義,即:IMAGE_NT_HEADERS32(WINNT.H中有定義,後面象這樣的結構均在WINNT.H中有定義),

  typedef struct _IMAGE_NT_HEADERS {
      DWORD Signature;
      IMAGE_FILE_HEADER FileHeader;
      IMAGE_OPTIONAL_HEADER32 OptionalHeader;
  } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

  這個結構含有3個成員:
    
        第一個成員表示“PE文件標誌”,可以看到他是一個DWORD類型,因此佔4個字節,它是PE開始的標記,對Windows程
  序這個值必須爲“50450000”。
    
        第二個成員表示“PE文件頭 ”,他的類型是一個IMAGE_FILE_HEADER的結構。也就是說“PE文件頭”的20個字節被
        定義爲IMAGE_FILE_HEADER結構,

  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;

  這個結構具有7個成員:
        
        成員1,佔2個字節,表示該文件運行所要求的CPU。對於Intel平臺,該值是“4C01”。
        成員2,佔2個字節,表示該文件中段的總數,我們這裏計劃寫3個段,(.text(代碼段)、.rdata(只讀數據段)、 
        .data(全局變量數據段))。所以此處值是“0300”。
        成員3,佔4個字節,表示文件創建日期和時間,從1970.1.1 00:00:00以來的秒數,我們這裏填“0000”即可。
        成員4,佔4個字節,表示符號表的指針,主要用於調試,在這裏填“0000”。
        成員5,佔4個字節,表示符號的數目,主要用於調試,在這裏填“0000”。
        成員6,佔2個字節,表示後面的“PE文件可選頭 ”部分所佔空間大小,我們已經知道“PE文件可選頭 ”的大小是
        224 byte,轉換成十六進制就是E0,所以這裏的值爲“E000”
        成員7,佔2個字節,表示關於文件信息的標記,比如文件是exe還是dll。這個值實際上是二進制位進行或運算得到的值。
        各二進制位表示的意義如下:
    
         Bit 0 :置1表示文件中沒有重定向信息。每個段都有它們自己的重定向信息。這個標誌在可執行文件中沒有使用,
                 在可執行文件中是用一個叫做基址重定向目錄表來表示重定向信息的,這將在下面介紹。
         Bit 1 :置1表示該文件是可執行文件(也就是說不是一個目標文件或庫文件)。
         Bit 2 :置1表示沒有行數信息;在可執行文件中沒有使用。
         Bit 3 :置1表示沒有局部符號信息;在可執行文件中沒有使用。
         Bit 4 :
         Bit 7 
         Bit 8 :表示希望機器爲32位機。這個值永遠爲1。
         Bit 9 :表示沒有調試信息,在可執行文件中沒有使用。
         Bit 10:置1表示該程序不能運行於可移動介質中(如軟驅或CD-ROM)。在這種情況下,OS必須把文件拷貝到
                 交換文件中執行。
         Bit 11:置1表示程序不能在網上運行。在這種情況下,OS必須把文件拷貝到交換文件中執行。
         Bit 12:置1表示文件是一個系統文件例如驅動程序。在可執行文件中沒有使用。
         Bit 13:置1表示文件是一個動態鏈接庫(DLL)。
         Bit 14:表示文件被設計成不能運行於多處理器系統中。
         Bit 15:表示文件的字節順序如果不是機器所期望的,那麼在讀出之前要進行
                 交換。在可執行文件中它們是不可信的(操作系統期望按正確的字節順序執行程序)。
    
         注意,因爲我們寫的是可執行程序,所以Bit 1必須置爲1,其他的按照需要置位即可,這裏我們僅將第二位置位,
         由此得到成員7的值爲“0200”。

         第三個成員,表示“PE文件可選頭 ”,他的類型是一個IMAGE_OPTIONAL_HEADER32結構。也就是說“PE文件頭 ”
  的224個字節被定義爲IMAGE_OPTIONAL_HEADER32結構,

  typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
  } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

  具有31個成員:
  
         成員1,佔2個字節,表示文件的格式,值爲0x010B表示.EXE文件,爲0x0107表示ROM映像,因爲我們寫的是一個可執
  行程序,所以此值應該爲“0B01”。
         成員2,佔1個字節,表示鏈接器的主版本號,此值不會影響程序的執行,我們這裏填充零,此值爲“00”。
         成員3,佔1個字節,表示鏈接器的幅版本號,此值不會影響程序的執行,我們這裏填充零,此值爲“00”。
         成員4,佔4個字節,表示可執行代碼的長度,此值不會影響程序的執行,我們這裏填充零,此值爲“00000000”。
         成員5,佔4個字節,表示初始化數據的長度(數據段)。此值不會影響程序的執行,我們這裏填充零,此值爲“00000000”。
         成員6,佔4個字節,表示未初始化數據的長度(bss段)。此值不會影響程序的執行,我們這裏填充零,此值爲“00000000”。

         (在介紹成員7之前,有必要了解一個很重要的知識------文件映射到內存。在可執行程序運行之前,PE加載器將
  把PE文件加載到進程空間的內存中去,並且初始化每個段實體。那麼加載到內存中的哪個地址去呢?這將由
  IMAGE_OPTIONAL_HEADER32結構的成員10的值指出加載的起始地址(又叫基地址)。這個值通常是“00400000”, 那麼PE
  文件的首地址“00000”就被映射到內存地址“00400000”處,那麼相對於文件偏移10個字節的地址爲“00010”,被映射
  到內存後的偏移也應該是10個字節,映射後的地址應該爲“00400010”。)
        成員7,4個字節,表示代碼的入口RVA(文件映射到內存的偏移地址)地址,程序從這兒開始執行。PE裝載器準備
  運行的PE文件的第一個指令的RVA。若您要改變整個執行的流程,可以將該值指定到新的RVA,這樣新RVA處的指令首先被
  執行。那麼這個值我們怎麼得到呢?我們知道在文件中有個.text段,他包含了所有的代碼,我們可以從中找到我們的入口
  地址,在這裏就是.text段裏的第一行代碼,也就是.text段的首地址,而在.text段頭部就給出了他映射到內存後的首地
  址的偏移,我們找到他取出添到此處,這裏爲“00100000”。(此處不理解沒關係,我們講完段結構後自能迎刃而解。)
        成員8,4個字節,表示可執行代碼起始位置。當然就是.text段的首地址,此值不會影響程序的執行,我們這裏
  填充零,此值爲“00000000”。
        成員9,4個字節,表示初始化數據的起始位置,此值不會影響程序的執行,我們這裏填充零,此值爲“00000000”。
        成員10,4個字節,就是上面所講的文件映射到內存是的基地址。PE文件的優先裝載地址。通常設爲“00400000”,
  PE裝載器將嘗試把文件裝到虛擬地址空間的00400000h處。字眼"優先"表示若該地址區域已被其他模塊佔用,那PE裝載器會
  選用其他空閒地址。我們這裏的值設爲“00400000”。
        成員11,4個字節,表示段加載後在內存中的對齊方式。內存中節對齊的粒度。例如,如果該值是4096 (1000h),
  那麼每節的起始地址必須是4096的倍數。若第一節從401000h開始且大小是10個字節,則下一節必定從402000h開始,
  即使401000h和402000h之間還有很多空間沒被使用。因爲Windows管理內存採用分頁管理的方式,而每頁的大小爲4k,也
  就是1000h,所以我們這個值爲“00100000”。
        成員12,4個字節,表示段在文件中的對齊方式。文件中節對齊的粒度。例如,如果該值是(200h),,那麼每節的起
  始地址必須是512的倍數。若第一節從文件偏移量200h開始且大小是10個字節,則下一節必定位於偏移量400h: 即使偏移
  量512和1024之間還有很多空間沒被使用。此值最好設爲200h,所以該成員的值爲“00020000”。
        成員13,2個字節,表示操作系統主版本號,此值不會影響程序的執行,我們這裏填充零,此值爲“0000”。
        成員14,2個字節,表示操作系統副版本號,此值不會影響程序的執行,我們這裏填充零,此值爲“0000”。
        成員15,2個字節,表示程序主版本號,此值不會影響程序的執行,我們這裏填充零,此值爲“0000”。
        成員16,2個字節,表示程序副版本號,此值不會影響程序的執行,我們這裏填充零,此值爲“0000”。
        成員17,2個字節,表示子系統主版本號。win32子系統版本。PE文件是專門爲Win32設計的,該子系統版本必定是
  4.0那麼此處值爲“04”。
        成員18,2個字節,表示子系統副版本號,根據上面所說,此值應爲“00”。
        成員19,2個字節,此值一般爲“00”。
        成員20,4個字節,表示程序調入後佔用內存大小(字節),等於所有段的長度之和。所有頭和節經過節對齊處理後
  的大小。我們知道,我們文件PE結構總長小於1000h,但是內存中的對齊粒度是1000h,所以PE結構被映射後要佔1000h,盡
  管很多空間沒有使用,另外我們有3個段,每個段的長度小於1000h,但是被映射後同樣要佔1000h,所以總共佔用內存的大
  小爲1000h + 3 * 1000h = 4000h,因此此值爲“00400000”。
        成員21,4個字節,表示所有文件頭的長度之和(從文件開始到第一個段之間的大小)。所有頭+節表的大小,也就等
  於文件尺寸減去文件中所有節的尺寸。可以以此值作爲PE文件第一節的文件偏移量。那麼我們怎麼得到這個值呢?我們的
  PE文件結構總大小爲:64 + 112 + 4 + 20 + 224 + 3 * 40 = 544 byte 轉化成十六進制爲220h,那麼此值就是220h嗎?
  不是的,因爲我們文件中的對齊粒度是200h,那麼220h實際上要佔用400h的空間,所以此值爲“00040000”。
        成員22,4個字節,表示校驗和。它僅用在驅動程序中,在可執行文件中可能爲0。它的計算方法Microsoft不公開,
  在imagehelp.dll中的CheckSumMappedFile()函數可以計算它,此處我們設爲填充零,此值爲“00000000”。
        成員23,2個字節,表示NT子系統,可能是以下的值:
      IMAGE_SUBSYSTEM_NATIVE (1) 不需要子系統。用在驅動程序中。
      IMAGE_SUBSYSTEM_WINDOWS_GUI(2) WIN32 graphical程序(它可用AllocConsole()來打開一個控制檯,但是不能在一
  開始自動得到)。
      IMAGE_SUBSYSTEM_WINDOWS_CUI(3) WIN32 console程序(它可以一開始自動建立)。
      IMAGE_SUBSYSTEM_OS2_CUI(5) OS/2 console程序(因爲程序是OS/2格式,所以它很少用在PE)。
      IMAGE_SUBSYSTEM_POSIX_CUI(7) POSIX console程序。
      Windows程序總是用WIN32子系統,所以只有2和3是合法的值。也就是說此值必須爲2或3,如果是3,那麼程序運行後
  會自動打開一個控制檯,我們爲了看一下效果,這裏設爲3,此值爲“0300“。
        成員24,2個字節,表示Dll狀態,我們這裏填充零,此值爲“0000”。
        成員25,4個字節,保留堆棧大小,我們這裏填充零,此值爲“00000000”。
        成員26,4個字節,啓動後實際申請的堆棧數,可隨實際情況變大,我們這裏填充零,此值爲“00000000”。
        成員27,4個字節,保留堆大小,我們這裏填充零,此值爲“00000000”。
        成員28,4個字節,實際堆大小,我們這裏填充零,此值爲“00000000”。
        成員29,4個字節,裝載標誌,我們這裏填充零,此值爲“00000000”。
        成員30,4個字節,在講這個成員之前,我們應該先了解成員31,成員31實際上是一個IMAGE_DATA_DIRECTORY結構
  的數組,成員30的值就是表示該數組的大小。通常有16個元素,所以此值爲:“10000000”。

  typedef struct _IMAGE_DATA_DIRECTORY {
      DWORD   VirtualAddress;
      DWORD   Size;
  } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

        成員31,128個字節,上面說過他是一個IMAGE_DATA_DIRECTORY結構的數組,通常具有16個元素。
  IMAGE_DATA_DIRECTORY結構有兩個成員,各佔4個字節,那麼也就得到成員31的總大小:2 * 4 * 16 = 128byte。16個元素
  中每個元素代表一個目錄表,每個目錄表表示的目錄如下:
  
      IMAGE_DIRECTORY_ENTRY_EXPORT (0)導出目錄用於DLL    
      IMAGE_DIRECTORY_ENTRY_IMPORT (1導入目錄    
      IMAGE_DIRECTORY_ENTRY_RESOURCE (2)資源目錄    
      IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)異常目錄    
      IMAGE_DIRECTORY_ENTRY_SECURITY (4)安全目錄    
      IMAGE_DIRECTORY_ENTRY_BASERELOC (5)重定位表    
      IMAGE_DIRECTORY_ENTRY_DEBUG (6)調試目錄
      IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)描述版權串    
      IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)機器值    
      IMAGE_DIRECTORY_ENTRY_TLS (9)Thread local storage目錄
      IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)Load configuration 目錄    
      IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)Bound import directory目錄    
      IMAGE_DIRECTORY_ENTRY_IAT (12)Import Address Table輸入地址表目錄
      IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
      IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor
  
  是不是所有的目錄表都要關心呢?其實要把這些目錄表都研究清楚是個很大的課題,對於我們這個程序,只需關心第2個
  元素,導入目錄,它標識了我們的程序從其他模塊導入的函數信息。因爲我們要顯示一個消息框,所以要導入user32.dll
  庫中的MessageBoxA函數,程序退出,又要導入kernel32.dll庫中的ExitProcess函數,這個目錄表需要使用。然而上面已
  說明每個目錄是一個IMAGE_DATA_DIRECTORY結構,該結構具有兩個成員,第一個成員表示目錄表的起始RVA地址,第二個
  成員表示目錄表的長度。這兩個值要根據.rdata段實體來確定,暫時先不填寫。爲了記錄該位置,我們先都填寫爲x,即:
  “xxxxxxxx","xxxxxxxx"。其餘的統統添零即可。
  
       接下來是各段頭部,我們這裏有3個段,.text(代碼段), .rdata(只讀數據段),data(全局變量數據段)。每段是一
  個IMAGE_SECTION_HEADER 結構,具有10個成員。首先我們來看.text段。

  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;

       成員1,8個字節,表識該段的名稱,我們這裏是.text,那麼此值是他的ASKII碼應該爲“2E74657874000000”。
       成員2,4個字節,表示有效代碼所佔的字節數。我們這裏所有代碼數一下總共26h個,固此值爲“26000000”。
       成員3,4個字節,表示在.text段映射到內存中的起始地址,那麼這個值如何得來呢?我們知道.text是緊跟PE結構後
  的,然後整個PE結構映射到內存後佔的大小爲1000h(因爲PE結構小於1000h個字節,而對齊力度粒度是1000h),那麼此值
  便得到了,爲“00100000”。
       成員4,4個字節,表示.text段在文件中所佔的大小。因爲我們的實際代碼只有26h個字節,那麼這個值是不是26h呢?
  並不是,一定要注意段在文件中的對齊粒度是200h,所以此值爲“00020000”。
       成員5,4個字節,表示.text段在文件中的起始地址,上面已經計算過PE文件的總長度爲400h,他實際上也就是.text
  的起始偏移地址,此值爲“00040000”。
       成員6,7,8,9,均佔4個字節,都僅用於目標文件,我們這裏統統填爲零。
       成員10,4個字節。包含標記以指示節屬性,比如節是否含有可執行代碼、初始化數據、未初始數據,是否可寫、
  可讀等。這個值實際上是二進制位進行或運算得到的值。各二進制位表示的意義如下:
  
      bit 5 (IMAGE_SCN_CNT_CODE),置1,節內包含可執行代碼。    
      bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA)置1,節內包含的數據在執行前是確定的。    
      bit 7 (IMAGE_SCN_CNT_UNINITIALIZED_DATA) 置1,本節包含未初始化的數據,執行前即將被初始化爲0。一般是BSS.
      bit 9 (IMAGE_SCN_LNK_INFO) 置1,節內不包含映象數據除了註釋,描述或者其他文檔外,是一個目標文件的一部分,
  可能是針對鏈接器的信息。比如哪個庫被需要。
      bit 11 (IMAGE_SCN_LNK_REMOVE) 置1,在可執行文件鏈接後,作爲文件一部分的數據被清除。
      bit 12 (IMAGE_SCN_LNK_COMDAT) 置1,節包含公共塊數據,是某個順序的打包的函數。
      bit 15 (IMAGE_SCN_MEM_FARDATA) 置1,不確定。
      bit 17 (IMAGE_SCN_MEM_PURGEABLE) 置1,節的數據是可清除的。
      bit 18 (IMAGE_SCN_MEM_LOCKED) 置1,節不可以在內存內移動。
      bit 19 (IMAGE_SCN_MEM_PRELOAD)置1, 節必須在執行開始前調入。
      Bits 20 to 23指定對齊。一般是庫文件的對象對齊。
      bit 24 (IMAGE_SCN_LNK_NRELOC_OVFL) 置1, 節包含擴展的重定位。
      bit 25 (IMAGE_SCN_MEM_DISCARDABLE) 置1,進程開始後節的數據不再需要。
      bit 26 (IMAGE_SCN_MEM_NOT_CACHED) 置1,節的 數據不得緩存。
      bit 27 (IMAGE_SCN_MEM_NOT_PAGED) 置1,節的 數據不得交換出去。
      bit 28 (IMAGE_SCN_MEM_SHARED) 置1,節的數據在所有映象例程內共享,如DLL的初始化數據。
      bit 29 (IMAGE_SCN_MEM_EXECUTE) 置1,進程得到“執行”訪問節內存。
      bit 30 (IMAGE_SCN_MEM_READ) 置1,進程得到“讀出”訪問節內存。
      bit 31 (IMAGE_SCN_MEM_WRITE)置1,進程得到“寫入”訪問節內存。
  
      在我們這裏,因爲這是代碼段,所以bit 5 (IMAGE_SCN_CNT_CODE)位置1,一般代碼段都含有初始化數據,那麼
  bit 6 (IMAGE_SCN_CNT_INITIALIZED_DATA)位置1,有因爲代碼段的代碼可以執行的,所以
  bit 29 (IMAGE_SCN_MEM_EXECUTE) 置1,那麼這3個二進制位進行或運算最終得到此成員值“20000060”。
  這個整個.text頭就編寫完畢,按照上面的方法,分別在編寫.rdata段和.data段。因爲要對齊,所以後面的代碼用零補齊。
  最後的編寫結果如下:
  
     000B0  50 45 00 00 4C 01 03 00 00 00 00 00 00 00 00 00  PE..L.........
     000C0  00 00 00 00 E0 00 02 00 0B 01 00 00 00 00 00 00  ....?.......
     000D0  00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00  ...............
     000E0  00 00 00 00 00 00 40 00 00 10 00 00 00 02 00 00  ......@.......
     000F0  00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00  ...............
     00100  00 40 00 00 00 04 00 00 00 00 00 00 03 00 00 00  .@............
     00110  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00120  00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00  ...............
     00130  10 20 00 00 3C 00 00 00 00 00 00 00 00 00 00 00   ..<...........
     00140  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00150  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00160  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00170  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00180  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00190  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     001A0  00 00 00 00 00 00 00 00 2E 74 65 78 74 00 00 00  .........text...
     001B0  26 00 00 00 00 10 00 00 00 02 00 00 00 04 00 00  &............
     001C0  00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60  ............ ..`
     001D0  2E 72 64 61 74 61 00 00 92 00 00 00 00 20 00 00  .rdata..?... ..
     001E0  00 02 00 00 00 06 00 00 00 00 00 00 00 00 00 00  ..............
     001F0  00 00 00 00 40 00 00 40 2E 64 61 74 61 00 00 00  ....@[email protected]...
     00200  16 00 00 00 00 30 00 00 00 02 00 00 00 08 00 00  ....0........
     00210  00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 C0  ............@..
     00220  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00230  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00240  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00250  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00260  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00270  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00280  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00290  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     002A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     002B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     002C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     002D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     002E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     002F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00300  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00310  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00320  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00330  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00340  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00350  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00360  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00370  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00380  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00390  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     003A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     003B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     003C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     003D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     003E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     003F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  
     至此,我們已經完成了PE結構的編寫。是不是已經大功告成了呢?彆着急,爲了讓我們寫的程序可以運行,我們還要
  完成.text(代碼段), .rdata(只讀數據段),data(全局變量數據段)三個段的實體部分。
     首先編寫.text段,他緊接着PE結構後面,但是我們如何編寫這些內容呢?前面已經說過,.text段中存放所有的可執行
  代碼(機器碼),我們可以通過先編寫彙編指令(調用MessageBoxA和ExitProcess兩個函數),然後反彙編出機器代碼抄
  到這裏就可以了。這裏有一點要注意,我們在爲MessageBoxA函數傳遞參數時,如何將“Hello World!”字符串以及消息
  框的標題“消息框”字符串傳過去呢?這就要用到我們的.data(全局變量數據段)了,我們可以把這兩個字符串放到這個段
  中,然後把字符串的偏移首地址作爲參數傳給MessageBoxA即可。因爲要以200h對齊,所以剩餘部分用零補齊,最終得到
  的代碼如下:
  
     00400  6A 00 68 00 30 40 00 68 07 30 40 00 6A 00 E8 07  [email protected]@.j.?
     00410  00 00 00 6A 00 E8 06 00 00 00 FF 25 08 20 40 00  ...j.?...% @.
     00420  FF 25 00 20 40 00 00 00 00 00 00 00 00 00 00 00  %. @...........
     00430  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00440  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00450  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00460  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00470  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00480  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00490  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     004A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     004B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     004C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     004D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     004E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     004F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00500  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00510  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00520  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00530  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00540  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00550  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00560  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00570  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00580  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     00590  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     005A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     005B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     005C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     005D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     005E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     005F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  
     接下來完成.rdata段,這個段非常重要,也有些繁瑣。因爲這個程序我們只用完成數據目錄數組的第二個元素導入表
  目錄,這個值我們當時沒有填寫,暫時添的x作爲標記,現在我們要一併解決這個問題。前面已經說過,每個數據目錄具有
  兩個成員,第一個成員表示目錄表的起始RVA地址,第二個成員表示目錄表的長度。對於我們這個導入表目錄來說,他指的
  就是導入表了,這個導入表實際上是一個IMAGE_IMPORT_DESCRIPTOR 結構數組,每個結構包含PE文件引入函數的一個相關
  DLL的信息。比如,如果該PE文件從10個不同的DLL中引入函數,那麼這個數組就有10個成員。該數組以一個全0的成員結尾。
  那麼.rdata段的內容應該就是IMAGE_IMPORT_DESCRIPTOR 結構數組了,我們這裏用到user32.dll和kennel32.dll兩個庫,
  那麼這個結構數組的元素個數就是2了。第一個元素的成員1的地址是不是就是.rdata的首地址呢?然後依次完成其餘成員?
  不是這樣的,這裏有個規律,我們導入了多少個函數,那麼就要空出8乘以導入函數個數個字符的空間(爲什麼要空些空間
  呢?要放什麼呢?一會便可知曉),在其後纔是IMAGE_IMPORT_DESCRIPTOR結構成員的首地址。在這裏我們導入了2個函數,
  那麼應該空2 * 8 = 16個字符,.rdata段的首地址是600h,那麼IMAGE_IMPORT_DESCRIPTOR結構成員的首地址應該是610h。
  這時我們便得到了數據目錄數組的第二個元素導入表目錄結構成員1的值,是不是就是“10060000”呢?不是的,因爲這裏
  的地址是RVA(映射後內存的地址),而文件的600h被映射爲2000h,所以610h被映射後的RVA爲2010h,故此值爲
  “10200000”,我們可以把其中的“xxxxxxxx”替換爲“10200000”。數據目錄數組的第二個元素導入表目錄結構成員
  2的值是指IMAGE_IMPORT_DESCRIPTOR 結構數組的長度,IMAGE_IMPORT_DESCRIPTOR 結構長度是20個字節,我們這裏的
  MAGE_IMPORT_DESCRIPTOR 結構數組元素是2個,並且以全零結尾,那麼總長度就是3 * 20 = 60byte,轉換成十六進制是
  3C,我們可以把另一個“xxxxxxxx”替換爲“3C000000”。
      下面我們填寫導入表即IMAGE_IMPORT_DESCRIPTOR 結構數組的成員。第一個數組元素的有關user32.dll信息,5個成員
  中:
      成員1,4個字節,他實際上是指向一個 IMAGE_THUNK_DATA 結構數組的RVA,而IMAGE_THUNK_DATA 結構數組記錄所有從
  某個.dll庫中導入函數名稱的RVA。IMAGE_THUNK_DATA 結構位於IMAGE_IMPORT_DESCRIPTOR 結構之後,由
  IMAGE_IMPORT_DESCRIPTOR 結構數組的起始地址和長度可以得到IMAGE_THUNK_DATA 結構數組的起始地址。即610h + 3Ch =
  64Ch,那麼這個IMAGE_IMPORT_DESCRIPTOR 結構數組的第一個元素的成員1的值是不是就是64Ch呢?並不是的,因爲這是一個
  RVA值,所以映射後的值應該是“4C200000”。
      成員2,成員3各4各字節,用處不大,我們用零填充。
      成員4,4個字節,是指向DLL名字的RVA,即指向DLL名字的指針,也是一個ASCIIZ字符串。我們將在後面編寫
  “user32.dll”字符串,計算得到其RVA爲“6A200000”。
      成員5,4個字節,指向一個 IMAGE_THUNK_DATA 結構數組的RVA,同成員1一樣,但是此IMAGE_THUNK_DATA 數組結構保
  存了所有導入函數的指針。PE文件被裝載到內存時,用引入函數真實地址來替代由這裏的IMAGE_THUNK_DATA 數組裏的元
  素值。那麼這裏填哪裏的地址呢?這就要用到我們剛剛空下的空間了。我們將此值設爲“08200000”。
  
      按照上面方法填寫導入表即IMAGE_IMPORT_DESCRIPTOR 結構數組的成員。第二個數組元素的有關kennel.dll信息的5個
  成員的值,我們這裏分別爲“5420000”,“00000000”,“00000000”,“84200000”,“00200000”。
  
      最後以全零結尾這個IMAGE_IMPORT_DESCRIPTOR 結構數組,即:
  “00000000”,“00000000”,“00000000”,“00000000”,“00000000”。
      
      下面填寫有關user32.dll信息的IMAGE_IMPORT_DESCRIPTOR 結構數組的IMAGE_THUNK_DATA結構數組。 他記錄了所有從
  user32.dll庫中導入函數名稱的RVA。我們這裏只有MessageBoxA函數,我們可以在後面編寫此字符串,並得到他的地址,
  這裏爲“5C200000”。緊接着以“00000000”結束user32.dll中導入函數的輸入。
  
      然後是kennel32.dll信息的IMAGE_IMPORT_DESCRIPTOR 結構數組的IMAGE_THUNK_DATA結構數組。他記錄了所有從
  kennel32.dll庫中導入函數名稱的RVA。我們這裏只有ExitProcess函數,我們可以在後面編寫此字符串,並得到他的地址,
  這裏爲“76200000”。緊接着以“00000000”結束kennel32.dll中導入函數的輸入。 
  
      最後完成這些字符串的輸入,之後要對齊.rdata段的剩餘部分,剩餘部分用零填充。最終得到的代碼如下:
  
     000600  A2 CA 81 7C 00 00 00 00 8A 05 D5 77 00 00 00 00  ⑹亅....?誻....
     000610  4C 20 00 00 00 00 00 00 00 00 00 00 6A 20 00 00  L ..........j ..
     000620  08 20 00 00 54 20 00 00 00 00 00 00 00 00 00 00   ..T ..........
     000630  84 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00  ?... ..........
     000640  00 00 00 00 00 00 00 00 00 00 00 00 5C 20 00 00  ............\ ..
     000650  00 00 00 00 76 20 00 00 00 00 00 00 9D 01 4D 65  ....v ......?Me
     000660  73 73 61 67 65 42 6F 78 41 00 75 73 65 72 33 32  ssageBoxA.user32
     000670  2E 64 6C 6C 00 00 80 00 45 78 69 74 50 72 6F 63  .dll..€.ExitProc
     000680  65 73 73 00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C  ess.kernel32.dll
     000690  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0006A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0006B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0006C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0006D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0006E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0006F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000700  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000710  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000720  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000730  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000740  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000750  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000760  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000770  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000780  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000790  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0007A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0007B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0007C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0007D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0007E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0007F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  
     最後一個是.data段,這個段非常簡單,就是MessageBoxA所需的參數,消息框的標題和內容,最終代碼如下:
     (注意對齊問題,補足200h字節。)
  
     000800  CF FB CF A2 BF F2 00 48 65 6C 6C 6F 2C 20 57 6F  消息框.Hello, Wo
     000810  72 6C 64 20 21 00 00 00 00 00 00 00 00 00 00 00  rld !...........
     000820  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000830  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000840  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000850  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000860  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000870  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000880  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000890  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0008A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0008B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0008C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0008D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0008E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0008F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000900  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000910  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000920  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000930  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000940  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000950  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000960  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000970  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000980  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     000990  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0009A0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0009B0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0009C0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0009D0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0009E0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
     0009F0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  
  
     好了,到此爲止一個完整的顯示Hello World!的可執行程序就完成了,趕快雙擊運行一下吧...費了這麼大勁就這麼個小
  功能,是不是有點事倍功半呢?其實手寫這麼個程序只是爲了更加熟練掌握PE結構,相信當您真正的手工完成了這個程序,
  您對PE結構一定有一個非常深刻的理解。(提示:請按照以上步驟完成這個程序,之後再回頭從頭到尾聯繫上下文仔細看一遍,
  因爲很多地方都是前後關聯緊密的,只作一遍,或只讀一遍是很難融匯貫通的。)
    
     由於水平和時間的原因,難免有許多問題,或者表述不清楚的地方,請高手不吝賜教。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章