手工构造典型PE文件2

来源:互联网 发布:java获取spring bean 编辑:程序博客网 时间:2024/05/04 05:55
-->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! jayzou.com
      【由于FileAlignment为0x200大小,而此时整个PE头未超过200,所以要填0到0x9FF处】

    到此,介绍手工构造典型PE文件的知识就讲完了,虽然过程枯燥,但是当第一个first_PE.EXE从你手中诞生时,你会觉得这一切都太值了!这点你应该相信我,因为我当时就是这种感觉,最后祝各位成功!