PE文件结构(五)

来源:互联网 发布:国内互联网网络股票 编辑:程序博客网 时间:2024/06/05 19:52
PE文件结构(五)
下一个使节头。首先我们做代码节的,代码节将包含前面所编的汇编语句。它有32字节长,所以代码节也就是这么长。节头从0x138处开始,有0x28字节长:

     Name             2e 63 6f 64 65 00 00 00      ; ".code"的ASCII码值
     VirtualSize          00 00 00 00              ; 未用
     VirtualAddress       ?? ?? ?? ??              ; 待定
     SizeOfRawData        20 00 00 00              ; 代码的大小
     PointerToRawData     ?? ?? ?? ??              ; 待定
     PointerToRelocations 00 00 00 00             ; 未用
     PointerToLinenumbers 00 00 00 00             ; 未用
     NumberOfRelocations   00 00                   ; 未用
     NumberOfLinenumbers   00 00                   ; 未用
     Characteristics      20 00 00 60              ; 代码节,可执行,可读

第二节将包含数据。节头开始于0x160处,有0x28字节长:

     Name             2e 64 61 74 61 00 00 00      ; ".data"的ASCII码值
     VirtualSize          00 00 00 00              ; 未用
     VirtualAddress       ?? ?? ?? ??              ; 待定
     SizeOfRawData        ?? ?? ?? ??              ; 待定
     PointerToRawData     ?? ?? ?? ??              ; 待定
     PointerToRelocations 00 00 00 00             ; 未用
     PointerToLinenumbers 00 00 00 00             ; 未用
     NumberOfRelocations   00 00                   ; 未用
     NumberOfLinenumbers   00 00                   ; 未用
     Characteristics      40 00 00 c0              ; 已初始化的,可读,可写

下一个字节位于0x188处,但节需要按32字节(的倍数)对齐(因为我是这样选择的),所以我们需要添一些(0)字节直到0x1a0处:

     00 00 00 00 00 00        ; 填充的
     00 00 00 00 00 00
     00 00 00 00 00 00
     00 00 00 00 00 00

现在第一节,就是上面所汇编的代码节,“到”了。它开始于0x1a0处,有0x20字节长:
     6A 00                     ; push       0x00000000
     68 ?? ?? ?? ??            ; push       offset _written
     6A 0D                     ; push       0x0000000d
     68 ?? ?? ?? ??            ; push       offset hello_string
     6A F5                     ; push       0xfffffff5
     2E FF 15 ?? ?? ?? ??      ; call       dword ptr cs:__imp__GetStdHandle@4
     50                        ; push       eax
     2E FF 15 ?? ?? ?? ??      ; call       dword ptr cs:__imp__WriteConsoleA@20
     C3                        ; ret      

因为这一节的长度(刚好32字节),在下一节(数据节)前我们不需要填充任何字节。下一节到了,从0x1c0处开始:

     68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A   ; "hello, world\n"的ASCII码值
     00 00 00                                 ; 填充几个0以和_written对齐
     00 00 00 00                              ; _written

现在剩下的只有输入目录了。本文件将从"kernel32.dll"库中输入2个函数,输入目录将从本节的变量后面立即开始。首先我们先将上面的数据按32字节对齐:

     00 00 00 00 00 00 00 00 00 00 00 00      ; 填充的

在0x1e0处开始输入描述(IMAGE_IMPORT_DESCRIPTOR):
     OriginalFirstThunk       ?? ?? ?? ??      ; 待定
     TimeDateStamp            00 00 00 00      ; 未绑定
     ForwarderChain           ff ff ff ff      ; 无中转
     Name                     ?? ?? ?? ??      ; 待定
     FirstThunk               ?? ?? ?? ??      ; 待定

我们需要用一个0字节项来结束输入目录(我们现在位于0x1f4):
     OriginalFirstThunk       00 00 00 00      ; 结束符号
     TimeDateStamp            00 00 00 00      ;
     ForwarderChain           00 00 00 00      ;
     Name                     00 00 00 00      ;
     FirstThunk               00 00 00 00      ;

现在只剩下DLL名字,还有2个换长,以及换长数据和函数名字了。但现在我们真的很快就要完成了。

DLL名字,以0结尾,开始于0x208处:
     6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00   ; "kernel32.dll"的ASCII码值
     00 00 00                                 ; 填充到32位边界

原始第一个换长,开始于0x218处:
     AddressOfData    ?? ?? ?? ??              ; "WriteConsoleA"函数名的RVA
     AddressOfData    ?? ?? ?? ??              ; "GetStdHandle"函数名的RVA
                     00 00 00 00              ; 结束符号

第一个换长就是同样的列表,开始于0x224处:
(__imp__WriteConsoleA@20, at 0x224)
     AddressOfData    ?? ?? ?? ??              ; "WriteConsoleA"函数名的RVA
(__imp__GetStdHandle@4, at 0x228)
     AddressOfData    ?? ?? ?? ??              ; "GetStdHandle"函数名的RVA
                     00 00 00 00              ; 结束符号

现在剩下的只有输入名字(IMAGE_IMPORT_BY_NAME)形式的两个函数名了。我们现处于0x230字节。
     01 00                                       ; 序数,不需要正确
     57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00   ; "WriteConsoleA"的ASCII码值
     02 00                                       ; 序数,不需要正确
     47 65 74 53 74 64 48 61 6e 64 6c 65 00      ; "GetStdHandle"的ASCII码值

Ok, 这就全部结束了。下一个字节,我们并不真正需要,是0x24f。我们必须将节填充到0x260处:
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 填充的
     00

------------

我们已经完成了。因为我们已经知道了所有的字节偏移量,我们可以应用我们的修正到所有原先被用“??”符号标为“未知”的地址和大小了。
我将不强迫你一步一步地去读它(很好懂的),只直接给出结果来:

------------

DOS-头, 开始于0x0:
     00 | 4d 5a 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     10 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     20 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
     30 | 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00

签名, 开始于0x40:
         50 45 00 00

文件头, 开始于0x44:
     Machine                      4c 01        ; i386
     NumberOfSections             02 00        ; 代码和数据
     TimeDateStamp                00 00 00 00 ; 谁管它?
     PointerToSymbolTable         00 00 00 00 ; 未用
     NumberOfSymbols              00 00 00 00 ; 未用
     SizeOfOptionalHeader         e0 00        ; 常量
     Characteristics              02 01        ; 可执行于32位机器上

可选头, 开始于0x58:
     Magic                        0b 01        ; 常量
     MajorLinkerVersion           00           ; 我是 0.0版 :-)
     MinorLinkerVersion           00           ;
     SizeOfCode                   20 00 00 00 ; 32字节代码
     SizeOfInitializedData        a0 00 00 00 ; 数据节大小
     SizeOfUninitializedData      00 00 00 00 ; 我们没有 BSS节
     AddressOfEntryPoint          a0 01 00 00 ; 代码节的开始处
     BaseOfCode                   a0 01 00 00 ; 代码节的RVA
     BaseOfData                   c0 01 00 00 ; 数据节的RVA
     ImageBase                    00 00 10 00 ; 1 MB, 任意选择
     SectionAlignment             20 00 00 00 ; 32字节对齐
     FileAlignment                20 00 00 00 ; 32字节对齐
     MajorOperatingSystemVersion   04 00       ; NT 4.0
     MinorOperatingSystemVersion   00 00       ;
     MajorImageVersion            00 00        ; 0.0版本
     MinorImageVersion            00 00        ;
     MajorSubsystemVersion        04 00        ; Win32 4.0
     MinorSubsystemVersion        00 00        ;
     Win32VersionValue            00 00 00 00 ; 未用?
     SizeOfImage                  c0 00 00 00 ; 所有节大小的总数
     SizeOfHeaders                a0 01 00 00 ; 第一节的偏移量
     CheckSum                     00 00 00 00 ; 非驱动程序不须用
     Subsystem                    03 00        ; Win32控制台程序
     DllCharacteristics           00 00        ; 未用(不是一个DLL)
     SizeOfStackReserve           00 00 10 00 ; 1 MB 栈
     SizeOfStackCommit            00 10 00 00 ; 开始时4 KB
     SizeOfHeapReserve            00 00 10 00 ; 1 MB 堆
     SizeOfHeapCommit             00 10 00 00 ; 开始时4 KB
     LoaderFlags                  00 00 00 00 ; 未知
     NumberOfRvaAndSizes          10 00 00 00 ; 常量

数据目录,开始于 0xb8:
       地址             大小
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
     e0 01 00 00     6f 00 00 00          ; IMAGE_DIRECTORY_ENTRY_IMPORT (1)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_SECURITY (4)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_DEBUG (6)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_TLS (9)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
00 00 00 00 00 00 00 00 ; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
     00 00 00 00     00 00 00 00          ; IMAGE_DIRECTORY_ENTRY_IAT (12)
     00 00 00 00     00 00 00 00          ; 13
     00 00 00 00     00 00 00 00          ; 14
     00 00 00 00     00 00 00 00          ; 15

节头(代码节), 开始于0x138:
     Name             2e 63 6f 64 65 00 00 00      ; ".code"
     VirtualSize          00 00 00 00              ; 未用
     VirtualAddress       a0 01 00 00              ; 代码节的RVA
     SizeOfRawData        20 00 00 00              ; 代码的大小
     PointerToRawData     a0 01 00 00              ; 代码节的文件偏移量
     PointerToRelocations 00 00 00 00             ; 未用
     PointerToLinenumbers 00 00 00 00             ; 未用
     NumberOfRelocations   00 00                   ; 未用
     NumberOfLinenumbers   00 00                   ; 未用
     Characteristics      20 00 00 60              ; 代码节,可执行,可读

节头(数据节),开始于0x160:
     Name             2e 64 61 74 61 00 00 00      ; ".data"
     VirtualSize          00 00 00 00              ; 未用
     VirtualAddress       c0 01 00 00              ; 数据节的RVA
     SizeOfRawData        a0 00 00 00              ; 数据节的大小
     PointerToRawData     c0 01 00 00              ; 数据节的文件偏移量
     PointerToRelocations 00 00 00 00             ; 未用
     PointerToLinenumbers 00 00 00 00             ; 未用
     NumberOfRelocations   00 00                   ; 未用
     NumberOfLinenumbers   00 00                   ; 未用
     Characteristics      40 00 00 c0              ; 已初始化,可读,可写

(填充)
     00 00 00 00 00 00        ; 填充的
     00 00 00 00 00 00
     00 00 00 00 00 00
     00 00 00 00 00 00

代码节, 开始于0x1a0:
     6A 00                     ; push       0x00000000
     68 d0 01 10 00            ; push       offset _written
     6A 0D                     ; push       0x0000000d
     68 c0 01 10 00            ; push       offset hello_string
     6A F5                     ; push       0xfffffff5
     2E FF 15 28 02 10 00      ; call       dword ptr cs:__imp__GetStdHandle@4
     50                        ; push       eax
     2E FF 15 24 02 10 00      ; call       dword ptr cs:__imp__WriteConsoleA@20
     C3                        ; ret      

数据节,开始于0x1c0:
     68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0A   ; "hello, world\n"
     00 00 00                                 ; 填充到和_written对齐
     00 00 00 00                              ; _written
填充:
     00 00 00 00 00 00 00 00 00 00 00 00      ; 填充的

输入描述(IMAGE_IMPORT_DESCRIPTOR),开始于0x1e0:
     OriginalFirstThunk       18 02 00 00      ; 原始第一个换长的RVA
     TimeDateStamp            00 00 00 00      ; 未绑定
     ForwarderChain           ff ff ff ff      ; -1,无中转
     Name                     08 02 00 00      ; DLL名字的RVA
     FirstThunk               24 02 00 00      ; 第一个换长的RVA
结束标志(0x1f4):
     OriginalFirstThunk       00 00 00 00      ; 结束标志
     TimeDateStamp            00 00 00 00      ;
     ForwarderChain           00 00 00 00      ;
     Name                     00 00 00 00      ;
     FirstThunk               00 00 00 00      ;

DLL名字, 开始于0x208:
     6b 65 72 6e 65 6c 33 32 2e 64 6c 6c 00   ; "kernel32.dll"
     00 00 00                                 ; 填充到32位边界

原始第一个换长, 开始于0x218:
     AddressOfData    30 02 00 00              ; 函数名"WriteConsoleA"的RVA
     AddressOfData    40 02 00 00              ; 函数名"GetStdHandle"的RVA
                     00 00 00 00              ; 结束标志

第一个换长,开始于0x224:
     AddressOfData    30 02 00 00              ; 函数名"WriteConsoleA"的RVA
     AddressOfData    40 02 00 00              ; 函数名"GetStdHandle"的RVA
                     00 00 00 00              ; 结束标志

输入函数名称(IMAGE_IMPORT_BY_NAME),开始于0x230:
     01 00                                       ; 序数,不需要正确
     57 72 69 74 65 43 6f 6e 73 6f 6c 65 41 00   ; "WriteConsoleA"的ASCII码值

IMAGE_IMPORT_BY_NAME,开始于0x240:
     02 00                                       ; 序数,不需要正确
     47 65 74 53 74 64 48 61 6e 64 6c 65 00      ; "GetStdHandle"的ASCII码值
(填充)
     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; 填充的
     00
第一个未使用字节开始于: 0x260

--------------


噢,这个文件能在NT上却不能在windows 95上运行。windows 95不能运行按32字节节对齐的应用程序,它要求节对齐为4 KB;并且很明显的,文件对齐也应为512字节。因此要想在windows 95上运行,你得插入很多的0字节(为了对齐)并调整RVA。感谢D. Binette在windows 95上的(运行)试验。


         -- 全文结束 --