首页 > 代码库 > 手写PE文件(一)

手写PE文件(一)

DOS Header(IMAGE_DOS_HEADER)->64 Byte

DOS头部

                     DOS Stub 112字节

 

                    "PE"00(Signature) 4个字节

 

                    IMAGE_FILE_HEADER  20个字节

 

PE文件头       IMAGE_OPTIONAL_HEADER32  96个字节

 

                    数据目录表         16*8=128个字节

 

                    IMAGE_SECTION_HEADER    40个字节

 

块表              IMAGE_SECTION_HEADER    40个字节

 

                    IMAGE_SECTION_HEADER    40个字节

 

                    .text   512个字节

 

块                 .rdata  512个字节

 

                    .data   512个字节

                  

                    COFF 行号

 

调试信息        COFF符号表

 

                    Code View调试信息

1这部分可以使读者对PE文件整体结构有一个大概了解。

2方便审查自己的构造进度 本次构造的PE文件总共有3个块,他们分别用来存放可执行代 

  码,输入表信息,以及全局变量,接触过PE文件的朋友对区块的概念应该不陌生,我们知道windows下的很多应用程序的文件对齐制度,也就是大名鼎鼎的FileAlignment字段的值最多200个字节,也就是十进制的512字节,对于不足512个字节的,要填充00补完512个字节,对于超过512个字节,要多分配512个字节

 本例中的PE文件头64+112+4+20+224+40*3=544个字节但是显然要填充到1024个字节,整个文件体积就是512*2(PE文件头)+512*3(块)=2560个字节,这也就是我们构造的PE文件总的大小

提前搞清一些字段与区段的偏移量也是比较重要的,

 1 PE文件头开始位置B0

 2 IMAGE_OPTIONAL_HEADER32开始处000000C8h

 3 数据目录表开始处 00000128h

 4 块表开始处 000001A8h

 5 .text区段开始处 00000400

 6 .rdata区段开始处 00000600

 7 .data区段开始处 00000800

二:重点字段的介绍

 1 DOS头部

   1-1 DOS Header

       1-1-1 e_magic[WORD]-->4D5A(DOS可执行文件头标记)

       1-1-19 e_lfanew[WORD]->BO OO OO OO (指定PE文件头的偏移量64+112)

 2 PE文件头

   2-1 "PE"00

       2-1-1 Signature[DWORD]-->50450000(PE文件头标记)

          注释:此处的值总为0000h

 

   2-2 IMAGE_FILE_HEADER

       2-2-1 Machine[DWORD]--->4C 01(可执行文件的目标CPU类型)

          注释:次PE文件运行于哪个CUP下,其标志就为相应的值

          interl i386 14C h                  MIPS R3000 162h

          MIPS R4000  166h               Alpha AXP 184h

          Power PC 1F0h

       2-2-2  NumberOfSections[WORD]--->03 00区段数目

          注释:此值取决于PE文件的区块数目,本程序为3个区段

       2-2-3  SizeOfOptionalHeader[WORD]--->E0 00(PE 可选头大小)

          注释:此值表示PE可选头的大小

       2-2-7 Characteristics[WORD]--->OF O1(文件属性)

          注释: 此值为文件的执行属性,EXE文件的属性一般为010Fh,DLL文件般 

             为0210h

   2-3 IMAGE_OPTIONAL_HEADER

        2-3-1  Magic[WORD]--->OB 01(*标记字)

           注释:此处是一个标记字,用于描述PE文件的映像类型,ROM映像0107h,普通可 

                执行映像010Bh,PE32+则是020Bh。

        2-3-7 AddressOfEntryPoint[DWORD]--->00 10 00 00(*程序执行入口)

           注释:通俗的讲就是指向可执行代码区块(.text)的首地址

        2-3-10 ImageBase[DWORD]--->00 00 40 00(*程序默认装入基地址)

        2-3-11 SectionAlignment[DWORD]-内存中的对齐大小,也叫做块粒度,其默认值是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(*文件子系统)

           注释:标明可执行文件所期望的子系统

        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提示框) 

           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

      4-2 .rdata (该区块包含输入表)

             4-2-1 IMAGE_THUNK_DATA32(IAT1)

             注释:其实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中,所以要补充00 00 00 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空字节做结尾标记]

       4-2-5 IMAGE_THUNK_DATA32(INT 1)

          4-2-5-1 ForwarderString [DWORD]--->5C 20 00 00

          注释:直接指向相关API函数

          【由于本例子中两个API函数引自两个不同的DLL中,所以要补充00 00 00 00结束】

       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函数罗列完毕后,通常在最后一个AIP后面加00,并跟着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]--->00 00(保留字,一般都为确保对齐而保留)

        1-1-16 e_oemid[WORD]--->00 00(OEM标识符,相对于e_oemid)

        1-1-17 e_oeminfo[WORD]--->00 00(OEM信息,即OEM细节)

        1-1-18 e_res[WORD]--->00 00(保留字,一般为对齐而保留)

        1-1-19 e_lfanew[DWORD]--->B0 00 00 00(指向PE文件头的偏移量,BO=64+112)

2 PE文件头

  2-1 PE 00

     2-1-1 Signature[DWORD]--->50 45 00 00(*PE文件头标记)

  2-2 IMAGE_FILE_HEADER

     2-2-1 Machine[WORD]--->4C 01(可执行文件的目标CPU类型)

     2-2-2NumberOfSections[WORD]--->03 00区块数目

     2-2-3TimeDateStamp [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 SizeOfOptionalHeader[WORD]--->E0 00(PE头IMAGE_OPTIONAL_HEADER32)大小

     2-2-7 Characteristics [WORD]--->OF O1 (*文件属性)

2-3 IMAGE_OPTIONAL_HEADER32

     2-3-1 Magic [WORD]--->OB 01(*标记字)
     2-3-2 MajorLinkVersion [BYTE] 00(连接程序主版本号)
     2-3-3 MinorLinkVersion [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 00 10 00 00(程序入口点RAV)
     2-3-8 BaseOfCode 00 00 00 00(代码区块起始RVA)
     2-3-9 BaseOfData 00 00 00 00(数据区块起始RVA)
     2-3-10 ImageBase 00 00 40 00(程序默认装入基地址)
     2-3-11 SectionAlignment 00 10 00 00 内存中区块对齐值
     2-3-12 FileAlignment 00 02 00 00文件中区块对齐值
     2-3-13 MajorOperatingSystemVersion 00 00操作系统主版本号
     2-3-14 MinorOperatingSystemVersion 00 00操作系统次版本号
     2-3-15 MajorImageVersion 00 00用户定义主版本号
     2-3-16 MinorImageVersion 00 00用户定义次版本号
     2-3-17 MajorSubSystemVersion 04 00运行所需最低子系统主版本号
     2-3-18 MinorSubSystemVersion 00 00运行所需最低子系统次版本号
     2-3-19 Win32VersionValue 00 00 00 00保留值,通常为0
     2-3-20 SizeOFimage 00 40 00 00映像装入内存后的总尺寸
     2-3-21 SizeOfHeaders 00 04 00 00DOS头 PE头 区块表的总大小
     2-3-22 CheckSum 00 00 00 00映像校验和
     2-3-23 SubSystem 03 00文件子系统
     2-3-24 DllCharacteristics 00 00显示DLL特性的旗标
     2-3-25 SizeOfStackReserve 00 00 00 00初始话堆栈总大小
     2-3-26 SizeOfStackCommit 00 00 00 00初始话提交堆栈大小
     2-3-27 SizeOfHeapReserve 00 00 00 00 初始话保留堆栈大小
     2-3-28 SizeOfHeapCommit 00 00 00 00 初始话实际保留堆栈大小
     2-3-29 LoaderFlags 00 00 00 00与调试有关,默认值为0
     2-3-30 NumberOfRvaAndSizes 10 00 00 00 数据目录表的数目
 2-4数据目录表
     2-4-1 Export Table
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小

    

        

     2-4-10 Threda local storage(TLS)
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
          
     2-4-11 Load configuration
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-12 Bound import
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-13 Import Address Table(IAT)
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-14 Delay import   
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-15 COM descriptor
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-1 保留
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 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 26 00 00 00被实际使用的区块大小
        3-1-3 VirtualAddress 00 10 00 00区块的RVA地址
        3-1-4 SizeOfRawData 00 02 00 00 该块在磁盘中占的大小
        3-1-5 PointerToRawData 00 04 00 00该块在文件中的偏移
        3-1-6 PointerToRelocations 00 00 00 00在OBJ文件中使用,调试中使用
        3-1-7 PointerToLinenumbers00 00 00 00行号表的偏移,调试中使用
        3-1-8 NumberOfRelocations00 00在OBJ文件中使用,重定位项数目
        3-1-9 NumberOfLinenumbers 00 00行号表中行号数目
        3-1-10 Characteristics 20 00 00 60该区的执行读写属性
     3-2 IMAGE_SECTION_HEADER(1.rdata)
        3-1-1 Name[BYTE]-->2E 72 64 61 74 61 00 00(8个自己的块名)
        3-1-2 VirtualSize 92 00 00 00被实际使用的区块大小
        3-1-3 VirtualAddress 00 20 00 00区块的RVA地址
        3-1-4 SizeOfRawData 00 02 00 00 该块在磁盘中占的大小
        3-1-5 PointerToRawData 00 06 00 00该块在文件中的偏移
        3-1-6 PointerToRelocations 00 00 00 00在OBJ文件中使用,调试中使用
        3-1-7 PointerToLinenumbers00 00 00 00行号表的偏移,调试中使用
        3-1-8 NumberOfRelocations00 00在OBJ文件中使用,重定位项数目
        3-1-9 NumberOfLinenumbers 00 00行号表中行号数目
        3-1-10 Characteristics 40 00 00 40该区的执行读写属性
     3-3 IMAGE_SECTION_HEADER(1.data)
        3-1-1 Name[BYTE]-->2E 64 61 74 61 00 00 00(8个自己的块名)
        3-1-2 VirtualSize 3E 00 00 00被实际使用的区块大小
        3-1-3 VirtualAddress 00 30 00 00区块的RVA地址
        3-1-4 SizeOfRawData 00 02 00 00 该块在磁盘中占的大小
        3-1-5 PointerToRawData 00 08 00 00该块在文件中的偏移
        3-1-6 PointerToRelocations 00 00 00 00在OBJ文件中使用,调试中使用
        3-1-7 PointerToLinenumbers00 00 00 00行号表的偏移,调试中使用
        3-1-8 NumberOfRelocations00 00在OBJ文件中使用,重定位项数目
        3-1-9 NumberOfLinenumbers 00 00行号表中行号数目
        3-1-10 Characteristics 40 00 00 C0该区的执行读写属性
4块
     4-1 .text(该区块是一段汇编代码16进制形式,功能是弹出一个MessageBox提示框)
           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
     4-2 .rdata(该区块包括输入表)
         4-2-1 IMAGE_THUNK_DATA32(IAT1)
           4-2-1-1 AddressOfData 76 20 00 00(*指向IMAGE_IMPORT_BY_NAME的RVA    由于本例子中两个API函数引自两个不同DLL,所以要补充00 00 00 00结束
         4-2-2 IMAGE_THUNK_DATA(IAT2)
            4-2-2-1 AddressOfData 5C 20 00 00(*指向IMAGE_IMPORT_BY_NAME的RVA 由于本例子中两个API函数引自两天不同DLL,所以要补充00 00 00 00结束
         4-2-3 IMAGE_IMPORT_DESCRIPTOR(IID1)
           4-2-3-1 OriginalFirstThunk 4C 20 00 00指向输入名称表的RVA
           4-2-3-2 TimeDateStamp 00 00 00 00 32时间标志
           4-2-3-3 ForwarderChain 00 00 00 00被转向API索引
           4-2-3-4 Name 6A 20 00 00 指向DLL名称的RVA与指针
           4-2-3-5 FirstThunk 08 20 00 00 指向输入地址表IAT的RVA
        4-2-4 IMAGE_IMPORT_DESCRIPTOR(IID2)
           4-2-3-1 OriginalFirstThunk 54 20 00 00指向输入名称表的RVA
           4-2-3-2 TimeDateStamp 00 00 00 00 32时间标志
           4-2-3-3 ForwarderChain 00 00 00 00被转向API索引
           4-2-3-4 Name 84 20 00 00 指向DLL名称的RVA与指针
           4-2-3-5 FirstThunk 00 20 00 00 指向输入地址表IAT的RVA
            填充20个00h作为结尾标记
        4-2-5 IMAGE_THUNK_DATA32(INT1)
           4-2-5-1 ForwarderString 5C 20 00 00 (*指向一个转向字符的RVA)
               由于两个API函数引自两个不同DLL文件,所以要补充00 00 00 00结束
        4-2-6 IMAGE_THUNK_DATA32(INT1)
           4-2-6-1 ForwarderString 76 20 00 00(*指向一个转向符的RVA)
               由于两个API函数引自两个不同DLL文件,所以要补充00 00 00 00 结束
        4-2-7 IMAGE_IMPORT_BY_NAME(1)
          4-2-7-1 Hint 00 00此函数所驻留DLL的输出表序号
          4-2-7-2 Name 4D 65 73 73 61 67 65 42 6F 78 41 (MessageBox的16进制代码,后面跟着此函数的DLL名称16进制代码00 75 73 65 72 33 32 2E 64 6C 6C 00 00
        4-2-8 IMAGE_IMPORT_BY_NAME(2)
          4-2-8-1 Hint 00 00此函数所驻留DLL的输出表序号
          4-2-8-2 Name 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
    填充至00 08 00 00
 4-3  .data
          CF FB CF A2 BF F2 00 48 65 6C 6C 6F 2C 77 6F 72 64 00填充至00 10 00 00
至此第一个首先PE文件完毕,好累呀

 

     2-4-2Import Table
        2-4-1VirtualAddress 10 20 00 00数据块的起始RVA
        2-4-2 Size 3C 00 00 00 数据块大小
    
     2-4-3Resorce Table
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-4 Exception Table
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-5 Security Table
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-6Base relocation Table
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-7 Debug
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
    
     2-4-8 CopyRight
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小
     
     2-4-9 Global ptr
        2-4-1 VirtualAddress 00 00 00 00数据块的起始RVA
        2-4-2 Size 00 00 00 00 数据块大小

手写PE文件(一)