• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            aurain
            技術(shù)文摘
            posts - 137,  comments - 268,  trackbacks - 0
            來源:安全中國(guó)

            零 前言

                PE格式,是Windows的可執(zhí)行文件的格式。Windows中的 exe文件,dll文件,都是PE格式。PE 就是Portable Executable 的縮寫。Portable 是指對(duì)于不同的Windows版本和不同的CPU類型上PE文件的格式是一樣的,當(dāng)然CPU不一樣了,CPU指令的二進(jìn)制編碼是不一樣的。只是文件中各種東西的布局是一樣的。

             

            圖 1.1

                圖1.1是 JIURL PEDUMP 打開 Win2K 中的 explorer.exe 的截圖。JIURL PEDUMP 是我寫的一個(gè)小工具,從文件開始的 Dos Header 一直到 Section Table,打開PE文件之后,點(diǎn)擊相應(yīng)結(jié)構(gòu),就會(huì)高亮顯示文件中相應(yīng)的部分。不過沒有Sections。對(duì)了解 PE 格式有所幫助,可以很好的配合后面的介紹。可以到我的主頁 http://jiurl.yeah.net/ 上下載。

            一   PE文件格式概述

            PE文件結(jié)構(gòu)的總體層次分布如下所示

             --------------
            |DOS MZ Header |
            |--------------|
            |DOS Stub      |
            |--------------|
            |PE Header     |
            |--------------|
            |Section Table |
            |--------------|
            |Section 1     |
            |--------------|
            |Section 2     |
            |--------------|
            |Section ...   |
            |--------------|
            |Section n     |
             --------------

             

            1.1 DOS Header

                PE文件最開始是一個(gè)簡(jiǎn)單的 DOS MZ header,它是一個(gè) IMAGE_DOS_HEADER 結(jié)構(gòu)。有了它,一旦程序在DOS下執(zhí)行,DOS就能識(shí)別出這是有效的執(zhí)行體,然后運(yùn)行緊隨 MZ Header 之后的 DOS Stub。

             1.2  DOS Stub   

                DOS Stub 是一個(gè)有效的 DOS 程序。當(dāng)程序在DOS下運(yùn)時(shí),輸出象 "This program cannot be run in DOS mode" 這樣的提示。在 圖1.1中就可以看到字符串 "This program cannot be run in DOS mode"。這是編譯器生成的默認(rèn)stub程序。你也可以通過鏈接選項(xiàng) /STUB:filename 指定任何有效的MS-DOS可執(zhí)行文件來替換它。

            1.3 PE Header

                緊接著 DOS Stub 的是 PE Header。它是一個(gè) IMAGE_NT_HEADERS 結(jié)構(gòu)。其中包含了很多PE文件被載入內(nèi)存時(shí)需要用到的重要域。執(zhí)行體在支持PE文件結(jié)構(gòu)的操作系統(tǒng)中執(zhí)行時(shí),PE裝載器將從 DOS MZ header 中找到 PE header 的起始偏移量。因而跳過了 DOS stub 直接定位到真正的文件頭 PE header。

            1.4 Section Table

                PE Header 接下來的數(shù)組結(jié)構(gòu) Section Table (節(jié)表)。如果PE文件里有5個(gè)節(jié),那么此 Section Table 結(jié)構(gòu)數(shù)組內(nèi)就有5個(gè)成員,每個(gè)成員包含對(duì)應(yīng)節(jié)的屬性、文件偏移量、虛擬偏移量等。圖1中的節(jié)表有4個(gè)成員。

            1.5 Sections

                PE文件的真正內(nèi)容劃分成塊,稱之為sections(節(jié))。Sections 是以其起始位址來排列,而不是以其字母次序來排列。通過節(jié)表提供的信息,我們可以找到這些節(jié)。圖1.1所示的 explorer.exe 中有4個(gè)節(jié)。程序的代碼,資源等等就放在這些節(jié)中。

            二  PE文件格式中的結(jié)構(gòu)及其作用

            這部分內(nèi)容請(qǐng)參考下面的幾篇文章,使用工具 JIURL PEDUMP 有助于快速了解。
            大家不要因此,而失望不看,本文重點(diǎn)在后三篇,本篇只是為了有個(gè)交代,和介紹些相關(guān)內(nèi)容。
            注意,在WINNT.H中,有所有PE相關(guān)結(jié)構(gòu)的定義。我們用到的結(jié)構(gòu)定義都來自那里。

            Microsoft Portable Executable and Common Object File Format Specification
            MSDN 

            《Windows95系統(tǒng)程式設(shè)計(jì)大奧秘》
            第8章 PE 與COFF OBJ 檔案格式
            Matt Pietrek 著 侯杰譯

            Iczelion的PE教程

            PE學(xué)習(xí)筆記(一) rivershan
            PE學(xué)習(xí)筆記(二) rivershan

            Inside Windows 
            An In-Depth Look into the Win32 Portable Executable File Format 
            Matt Pietrek 
            已經(jīng)被人翻譯了。

            Inside Windows 
            An In-Depth Look into the Win32 Portable Executable File Format 
            Matt Pietrek

            三 幾個(gè)要注意的問題

            3.1 文件中大量的空白

                在 PE Header結(jié)構(gòu) 中的 OptionalHeader 結(jié)構(gòu)中的成員 FileAlignment 的值是文件中節(jié)的對(duì)齊粒度,單位是字節(jié),這個(gè)值應(yīng)該是2的n次方,范圍從512到64k。如果這里的值是512,那么PE文件中的節(jié)的長(zhǎng)度都是512字節(jié)的整數(shù)倍,內(nèi)容不夠的部分用0填充。比如一個(gè)PE文件的 FileAlignment 為200h(十進(jìn)制512),它的第一個(gè)節(jié)在400h處,長(zhǎng)度為100h,那么從文件400h到500h中為這一節(jié)的內(nèi)容,而文件對(duì)齊粒度是200h,所以為了使這一節(jié)長(zhǎng)度為FileAlignment的整數(shù)倍,500h到600h會(huì)被用零填充。而下一個(gè)節(jié)的開始地址為600h。用16進(jìn)制編輯器打開PE文件,就可以看到這種情況,PE文件頭的內(nèi)容結(jié)束到第一個(gè)節(jié)開始之間的地方,每一個(gè)節(jié)中內(nèi)容結(jié)束到下一節(jié)開始的地方都會(huì)有大量的空白。VC6編譯鏈接時(shí)默認(rèn)的FileAlignment為1000h(4k),可以使用鏈接選項(xiàng) /ALIGN:number 來改變這個(gè)值。比如把4k改成512時(shí),可以明顯減小生成文件的大小。

            3.2 big-endian和little-endian

                PE Header中的 FileHeader 的成員 Machine 中的值,根據(jù)WINNT.H中的定義,對(duì)于 Intel CPU 應(yīng)該為 0x014c。但是你用16進(jìn)制編輯器打開PE文件,看到這個(gè)WORD顯示的卻是 4c 01 。你看到的并沒有錯(cuò),你看到的 4c 01 就是 0x014c,只不過由于 intel cpu 是ittle-endian,所以顯示出來是這樣的。對(duì)于 big-endian 和 little-endian,請(qǐng)看下面的例子。

            比如一個(gè)整形int變量。長(zhǎng)為四個(gè)字節(jié)。
            這個(gè)變量的地址比如為n。
            則這個(gè)變量的4個(gè)字節(jié)地址分別為n,n+1,n+2,n+3。

            當(dāng) 這個(gè)整形變量 的值為 0x12345678 時(shí),

            對(duì)于 big-endian 來說
            地址n+0的那個(gè)字節(jié)中的值為 0x12
            地址n+1的那個(gè)字節(jié)中的值為 0x34
            地址n+2的那個(gè)字節(jié)中的值為 0x56
            地址n+3的那個(gè)字節(jié)中的值為 0x78

            按如下方式就會(huì)顯示為
            n n+1 n+2 n+3 
            12 34 56 78

            對(duì)于 ittle-endian 來說
            地址n+0的那個(gè)字節(jié)中的值為 0x78
            地址n+1的那個(gè)字節(jié)中的值為 0x56
            地址n+2的那個(gè)字節(jié)中的值為 0x34
            地址n+3的那個(gè)字節(jié)中的值為 0x12

            按如下方式就會(huì)顯示為
            n n+1 n+2 n+3 
            78 56 34 12

            Intel使用的是 ittle-endian 。

            一個(gè)整形 int 變量 i,的地址是&i,那么這個(gè)i的四個(gè)字節(jié)是&i,&i+1,&i+2,&i+3。
            可以用這樣一個(gè)程序看到。

            #include <stdio.h>
            #include <conio.h>

            void main()
            {
            int i;
            char* p;
            p=(char*)&i;

            printf("i: ");
            scanf("%x",&i);
            printf("\n");

            printf("&i+0: %x\n",*p);
            printf("&i+1: %x\n",*(p+1));
            printf("&i+2: %x\n",*(p+2));
            printf("&i+3: %x\n",*(p+3));

            printf("\n");
            printf("&i-4: %x\n",*(p-4));
            printf("&i-3: %x\n",*(p-3));
            printf("&i-2: %x\n",*(p-2));
            printf("&i-1: %x\n",*(p-1));

            printf("\n");
            printf("&i+4: %x\n",*(p+4));
            printf("&i+5: %x\n",*(p+5));
            printf("&i+6: %x\n",*(p+6));
            printf("&i+7: %x\n",*(p+7));

            getch();

            }

            當(dāng)我們輸入 12345678 的時(shí)候可以看到,輸出

            i: 12345678

            &i+0: 78
            &i+1: 56
            &i+2: 34
            &i+3: 12

            &i-4: 7c
            &i-3: ffffffff
            &i-2: 12
            &i-1: 0

            &i+4: ffffffc0
            &i+5: ffffffff
            &i+6: 12
            &i+7: 0
            正是&i,&i+1,&i+2,&i+3這四個(gè)字節(jié)中儲(chǔ)存了i的值。

            對(duì)于int,WORD,DWORD等等都要注意 big-endian 和 little-endian 。

            3.3 RVA (Relative Virtual Address) 相對(duì)虛擬地址

                RVA是一個(gè)簡(jiǎn)單的相對(duì)于PE載入點(diǎn)的內(nèi)存偏移。比如,PE載入點(diǎn)為0X400000,那么代碼節(jié)中的地址0X401000的RVA為(target address) 0x401000 - (load address)0x400000 = (RVA)0x1000.換句話說 RVA是0x1000,載入點(diǎn)為0X400000,那么該RVA的在內(nèi)存中的實(shí)際地址就是0X401000。注意一下RVA是指內(nèi)存中,不是指文件中。是指相對(duì)于載入點(diǎn)的偏移而不是一個(gè)內(nèi)存地址,只有RVA加上載入點(diǎn)的地址,才是一個(gè)實(shí)際的內(nèi)存地址。

            3.4 三種不同的地址

                PE的各種結(jié)構(gòu)中,涉及到很多地址,偏移。有些是指在文件中的偏移,有的是指在內(nèi)存中的偏移。一定要搞清楚,這個(gè)地址或者是偏移,是指在文件中,還是指在內(nèi)存中。第一種,文件中的地址。比如用16進(jìn)制編輯器打開PE文件,看到的地址(偏移)就是文件中的地址,我們使用某個(gè)結(jié)構(gòu)的文件地址,就可以在文件中找到該結(jié)構(gòu)。第二種,文件被整個(gè)映射到內(nèi)存時(shí),比如某些PE分析軟件,把整個(gè)PE文件映射到內(nèi)存中,這時(shí)是內(nèi)存中的地址,如果知道某一個(gè)結(jié)構(gòu)在文件中的地址的話,那么這個(gè)PE文件被映射到內(nèi)存之后該結(jié)構(gòu)的在內(nèi)存中的地址,可以用文件中的地址加上映射內(nèi)存的地址,就可以得到在該結(jié)構(gòu)內(nèi)存中的地址。第三種,執(zhí)行PE時(shí),PE文件會(huì)被載入器載入內(nèi)存,這時(shí)經(jīng)常需要的是RVA。比如知道一個(gè)結(jié)構(gòu)的RVA,那么載入點(diǎn)加上RVA就可以得到內(nèi)存中該結(jié)構(gòu)的實(shí)際地址。比如,某個(gè)程序,我們用16進(jìn)制編輯器打開它,看到PE Header開始在16進(jìn)制編輯器顯示為000000C8的地方。于是我們?cè)?6進(jìn)制編輯器顯示為000000FC的地方找到了OptionalHeader的ImageBase,值為400000h,那么當(dāng)這個(gè)程序被執(zhí)行時(shí),如果內(nèi)存中400000h處沒有使用,該程序就會(huì)被載入到那里。而我用CreateFileMapping將這個(gè)PE文件映射到內(nèi)存中時(shí),可以得到塊內(nèi)存的地址為5505024。對(duì)于映射入內(nèi)存的這個(gè)PE文件,我們就可以在內(nèi)存中000000FCh+05505024h=5505120處找到這個(gè)PE的OptionalHeader的ImageBase。

            3.5 幾個(gè)重要結(jié)構(gòu)的說明

            PE Header 的 FileHeader 的 NumberOfSections:這是一個(gè)很重要的字段,用來確定文件中節(jié)的數(shù)目。

            PE Header 的 OptionalHeader 的 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
            #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
            DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]:一個(gè)IMAGE_DATA_DIRECTORY 結(jié)構(gòu)數(shù)組。到目前為止這個(gè)數(shù)組的長(zhǎng)度是固定的,有16個(gè)元素,這16個(gè)元素分別代表
            #define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
            #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
            #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
            #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
            #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
            #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
            #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
            // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
            #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
            #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
            #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
            #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
            #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
            #define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
            #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
            #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
            每個(gè)元素是一個(gè)IMAGE_DATA_DIRECTORY結(jié)構(gòu),IMAGE_DATA_DIRECTORY定義如下。
            typedef struct _IMAGE_DATA_DIRECTORY {
            DWORD VirtualAddress;
            DWORD Size;
            } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
            第一個(gè)字段是一個(gè)RVA,第二個(gè)字段是一個(gè)大小。

            Section Table 節(jié)表緊跟在OptionalHeader之后,是一個(gè)IMAGE_SECTION_HEADER結(jié)構(gòu)的數(shù)組。該數(shù)組中成員的個(gè)數(shù)由 File Header (IMAGE_FILE_HEADER) 結(jié)構(gòu)中 NumberOfSections 域的域值來定。節(jié)表中的成員是IMAGE_SECTION_HEADER 結(jié)構(gòu),IMAGE_SECTION_HEADER 結(jié)構(gòu)的長(zhǎng)度固定,長(zhǎng)40個(gè)字節(jié)。整個(gè)Section Table 的長(zhǎng)度不固定,等于 NumberOfSections*sizeof(IMAGE_SECTION_HEADER)。IMAGE_SECTION_HEADER 結(jié)構(gòu)中,
            VirtualAddress:本節(jié)的RVA(相對(duì)虛擬地址)。
            PointerToRawData:這是本節(jié)基于文件的偏移量。

            3.6 DOS MZ Header 中的 MZ

                MZ是MZ格式的主要作者 Mark Zbikowski 的名字的縮寫。

            posted on 2009-06-29 13:45 閱讀(918) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Windows開發(fā)

            <2010年7月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(17)

            隨筆分類(138)

            隨筆檔案(137)

            網(wǎng)絡(luò)開發(fā)

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 499069
            • 排名 - 36

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            久久精品国产99国产精偷| 久久精品麻豆日日躁夜夜躁| 久久国产精品77777| 99久久国语露脸精品国产| 久久se精品一区二区| 青春久久| 精品无码久久久久久午夜| 国产成人久久777777| 亚洲欧洲久久av| 国产美女久久久| 国产成人综合久久精品红| 97久久超碰成人精品网站| 久久综合五月丁香久久激情| 亚洲AV乱码久久精品蜜桃| 久久久久国产视频电影| 国产精品久久网| 亚洲AV无码久久寂寞少妇| 亚洲欧美国产日韩综合久久| 久久免费小视频| 久久久精品人妻无码专区不卡 | 久久国产成人| 亚洲综合日韩久久成人AV| 久久久久国产精品嫩草影院 | A级毛片无码久久精品免费| www久久久天天com| 亚洲国产精品无码久久98| 亚洲欧美一区二区三区久久| 精品人妻伦九区久久AAA片69| 国产成人精品白浆久久69| AV无码久久久久不卡蜜桃| 亚洲精品成人久久久| 久久综合色区| 国产精品久久久久久久久久影院| 久久久国产精品| 久久99精品九九九久久婷婷| 国产99久久九九精品无码| 久久免费美女视频| 久久久精品视频免费观看| 亚洲欧美一级久久精品| 中文字幕乱码人妻无码久久| 亚洲国产精品无码久久久不卡|