【文章标题】: 手写PE问题
【文章作者】: ALL
【作者主页】: hi.baidu.com/8oahck
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
手工打造PE文件
学习PE有些时间了,今天决定把所学的东西复习总结一下,所以自己手写一个PE文件。
我准备写一个只有两个节的PE文件,
[提前申明,这个写的PE程序可能会有点小问题,需要大家去找出来,因为这是我给大家留的问题哦!]
.text 存放代码
.data 存放数据变量等
PE的大致结构如下:
----------
MS-DOS |
MZ文件头|
----------
PE标记|
--------
文件头|
--------
可选头|
----------
数据目录|
----------
节表头|
------------
节一[.text]|
------------
节二[.data]|
------------
从上面大家应该可以看出,我的文件中将DOS-Stub省略了,DOS-Stub是DOS下的执行体,当Win32的可执行体文件,载入DOS时,执行的是这部分内容。
一般情况下为"This program cannot be run in DOS mode....",在这里为了尽量缩小程序,所以省略。
IMAGE_DOS_HEADER[DOS头]
我们需要的是MZ标记和e_lfanew指针。至于其他的一些值,在这里就不介绍了,因为DOS下的执行体已经被省略了,而这些参数基本都和他有关。
每个程序的开始两个字节都是MZ[4D 5A]
由于省略了DOS-Stub,所以直接让e_lfanew指向下面的位置00000040,e_lfanew指向的就是PE标记。
IMAGE_DOS_HEADER开始于0x00,总计0x40字节
PE标记为50 45 00 00,开始于0x40,总计4个字节。
00000000 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 MZ..............
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 ............@...
00000040 50 45 00 00 PE..
在IMAGE_DOS_HEADER后面的是IMAGE_FILE_HEADER,开始于0x44,总计0x14.
Machine WORD 4C 01 ; I386 注意地址的排列不同于[高-地]
NumberOfSections WORD 02 00 ; 只设两个节,分别为 .text .data
TimeDateStamp DWORD 00 00 00 00 ; 不清楚这时间有什么作用,可以置零
PointerToSymbolTable DWORD 00 00 00 00 ; 符号表指针,一般为0[不知道它的具体作用]
NumberOfSymbols DWORD 00 00 00 00 ; 符号数,一般为0 [不知道有什么作用]
SizeOfOptionalHeader WORD E0 00 ; OptionalHeader大小是224,即0xE0
Characteristics WORD 02 01 ; executable on 32-bit-machine
00000040 50 45 00 00 4C 01 02 00 00 00 00 00 00 00 00 00 PE..L...........
00000050 00 00 00 00 E0 00 02 01 ....?..
在IMAGE_FILE_HEADER后面的是IMAGE_OPTIONAL_HEADER,开始于0x54,总计0x60 + 0x80[数据目录] = 0xE0 这就是前面的SizeOfOptionalHeader.
Magic WORD 0B 01 ; 作用不清楚
MajorLinkerVersion BYTE 07 ; 链接器的主版本号[随意]
MinorLinkerVersion BYTE 01 ; 链接器的小版本号[随意]
SizeOfCode DWORD ?? ?? ?? ?? ; 代码区域的大小
SizeOfInitializedData DWORD ?? ?? ?? ?? ; 以初始化的数据的大小
SizeOfUninitializedData DWORD 00 00 00 00 ; 未初始化的数据大小 .bss[一般存放静态变量]
AddressOfEntryPoint DWORD ?? ?? ?? ?? ; 程序的入口点
BaseOfCode DWORD ?? ?? ?? ?? ; 代码基址[已载入的镜像文件中.text的相对ImageBase偏移量]
BaseOfData DWORD ?? ?? ?? ?? ; 数据基址[已载入的镜像文件中.data的相对ImageBase偏移量]
ImageBase DWORD 00 00 00 10 ; 将一个可执行文件镜像到进程中的优先基地址
SectionAlignment DWORD 20 00 00 00 ; 载入后一个节能拥有的最小的空间数量
FileAlignment DWORD 20 00 00 00 ; 镜像文件中信息块间的最小间隔尺寸
MajorOperatingSystemVersion WORD 04 00 ; 系统的主版本号
MinorOperatingSystemVersion WORD 00 00 ; 系统的小版本号
MajorImageVersion WORD 00 00 ; 镜像的主版本号
MinorImageVersion WORD 00 00 ; 镜像的小版本号
MajorSubSystemVersion WORD 00 00 ; 子系统的主版本号
MinorSubSystemVersion WORD 00 00 ; 子系统的小版本号
Win32VersionValue DWORD 00 00 00 00 ; 不知道作用
SizeOfImage DWORD ?? ?? ?? ?? ; 镜像文件的大小[最后一个节的VirtualSize + SizeOfRawData]
SizeOfHeader DWORD ?? ?? ?? ?? ; 所有头的大小
CheckSum DWORD 00 00 00 00 ; 不清楚是何作用
Subsystem WORD 02 00 ; 窗体程序 或 控制台程序
DllCharacteristics WORD 00 00 ; DLL特性说明[DLL_PROCESS_ATTACH | DLL_PROCESS_DETACH | DLL_THREAD_ATTACH | DLL_THREAD_DEATCH]
SizeOfStackReserve DWORD 00 00 10 00 ; 栈的预留空间
SizeOfStackCommit DWORD 00 10 00 00 ; 指定栈的大小[以其为基本单位增长]
SizeOfHeapReserve DWORD 00 00 10 00 ; 堆的预留空间
SizeOfHeapCommit DWORD 00 10 00 00 ; 指定堆的大小[以其为基本单位增长]
LoaderFlags DWORD 00 00 00 00 ; 判断载入时是否中断或者调试
NumberOfRvaAndSizes DWORD 10 00 00 00 ; 标示后面的数据目录的大小,而非实际数量,PE中定义了16个目录,一般只用13个
00000050 0B 01 07 01 00 00 00 00 ........
00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070 00 00 00 00 00 00 00 01 20 00 00 00 20 00 00 00 ........ ... ...
00000080 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000090 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 ................
000000A0 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................
000000B0 00 00 00 00 10 00 00 00 ........
在IMAGE_OPTIONAL_HEADER后面的是数据目录,起始于0xB8,总计大小为[0x80=16*8]
16个目录,每个目录指针为8字节。
Address Size
00 00 00 00 00 00 00 00 ; IMAGE_DIRECTORY_ENTRY_EXPORT (0)
?? ?? ?? ?? ?? ?? ?? ?? ; 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
此处我们只填写输入表指针,其他的我们不填写!
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
000000B0 00 00 00 00 00 00 00 00 ........
000000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000130 00 00 00 00 00 00 00 00 ........
接下来的是节表头IMAGE_SECTION_HEADER,每个节在节表头中都会有一个项与之对应。
Name 2E 74 65 78 74 00 00 00 ; .text
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 ; code, executable, readable
第二个节包括数据,起始于0x160,长度0x28
Name 2e 64 61 74 61 00 00 00 ; ".data"
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 ; initialized, readable, writeable
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000130 2E 74 65 78 74 00 00 00 .text...
00000140 00 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 ........ .......
00000150 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ............ ..`
00000160 2E 63 6F 64 65 00 00 00 00 00 00 00 00 00 00 00 .code...........
00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000180 00 00 00 00 40 00 00 C0 ....@..
节表头后面的就是节数据了。有的材料说这里要添零使什么对其,一直不懂,这里我不填充大家看看结果!
但是后面的节数据,我们就需要按照SectionAligment与FileAlignment来对齐。
书写后代码节[.text]的数据是
00000180 6A 00 68 00 00 00 68 00 j.h...h.
00000190 00 00 6A 00 2E FF 15 00 00 00 00 6A 00 2E FF 15 ..j.......j...
000001A0 00 00 00 00 C3 ....
由于要遵循对齐的要求,所以需要填充3个字节的00,因为前面的对齐大小为32字节。
00000180 6A 00 68 00 00 00 68 00 j.h...h.
00000190 00 00 6A 00 2E FF 15 00 00 00 00 6A 00 2E FF 15 ..j.......j...
000001A0 00 00 00 00 C3 00 00 00 ....?..
同样的道理,数据节[.data]书写完成后,如下:
000001A0 43 72 65 61 74 65 20 50 Create P
000001B0 45 20 53 75 63 63 65 73 73 66 75 6C 6C 75 79 21 E Successfulluy!
000001C0 20 42 79 20 41 4C 4C 00 By ALL.
节后面的是输入表目录IMAGE_IMPORT_DESCRIPTOR,起始于0x1C8,由于我只用到了一个API--MessageBoxA,程序的问题就出在这里。所以只需要一个目录数组。
OriginalFirstThunk ?? ?? ?? ??
TimeDateStamp 00 00 00 00
ForwarderChain ff ff ff ff
Name ?? ?? ?? ??
FirstThunk ?? ?? ?? ??
填零使其满足对齐的要求。
000001C0 00 00 00 00 00 00 00 00 ........
000001D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000001E0 00 00 00 00 00 00 00 00 ........
接下来我们只需指定DLL名,Thunk地址于Thunk的名称就OK了!
DLL名开始于0x1E8,
000001E0 75 73 65 72 33 32 2E 64 user32.d
000001F0 6C 6C 00 00 00 00 00 00 ll......
DLL名称后面我有填0操作。
OriginalFirstThunk数组中的1个成员,起始于0x218:
AddressOfData ?? ?? ?? ?? ; RVA to Function name "GetMessageA"
AddressOfData 00 00 00 00 ; RVA to Function name "xxxxxxxxxxx"
00 00 00 00 ; terminator
000001F0 00 00 00 00 00 00 00 00 ........
00000200 00 00 00 00 00 00 00 00 ........
FirstThunk含有同样的列表,起始于0x208:
AddressOfData ?? ?? ?? ?? ; RVA to function name "GetMessageA"
AddressOfData ?? ?? ?? ?? ; RVA to function name "xxxxxxxxxxx"
00 00 00 00 ; terminator
00000200 00 00 00 00 00 00 00 00 ........
00000210 00 00 00 00 00 00 00 00 ........
接下来指定函数名IMAGE_IMPORT_BY_NAME,就OK了!
00000210 4D 65 73 73 61 67 Messag
00000220 65 42 6F 78 41 00 00 00 eBoxA...
PE草稿到这里已经结束了,现在我们只需要将那些问号填充,然后将程序保存,就可以知道PE是否成功创建!
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 MZ..............
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 ............@...
00000040 50 45 00 00 4C 01 02 00 00 00 00 00 00 00 00 00 PE..L...........
00000050 00 00 00 00 E0 00 02 01 0B 01 07 01 20 00 00 00 ....?...... ...
00000060 20 00 00 00 00 00 00 00 88 01 00 00 88 01 00 00 .......?..?..
00000070 A8 01 00 00 00 00 00 01 20 00 00 00 20 00 00 00 ?...... ... ...
00000080 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000090 C8 01 00 00 88 01 00 00 00 00 00 00 02 00 00 00 ?..?..........
000000A0 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................
000000B0 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ................
000000C0 C8 01 00 00 60 00 00 00 00 00 00 00 00 00 00 00 ?..`...........
000000D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000000F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000130 00 00 00 00 00 00 00 00 2E 74 65 78 74 00 00 00 .........text...
00000140 00 00 00 00 88 01 00 00 20 00 00 00 88 01 00 00 ....?.. ...?..
00000150 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ............ ..`
00000160 2E 63 6F 64 65 00 00 00 00 00 00 00 A8 01 00 00 .code.......?..
00000170 20 00 00 00 A8 01 00 00 00 00 00 00 00 00 00 00 ...?..........
00000180 00 00 00 00 40 00 00 C0 6A 00 68 00 00 00 68 00 ....@..纉.h...h.
00000190 00 00 6A 00 2E FF 15 00 00 00 00 6A 00 2E FF 15 ..j.......j...
000001A0 00 00 00 00 C3 00 00 00 43 72 65 61 74 65 20 50 ....?..Create P
000001B0 45 20 53 75 63 63 65 73 73 66 75 6C 6C 75 79 21 E Successfulluy!
000001C0 20 42 79 20 41 4C 4C 00 F8 01 00 00 00 00 00 00 By ALL.?......
000001D0 FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ............
000001E0 00 00 00 00 00 00 00 00 75 73 65 72 33 32 2E 64 ........user32.d
000001F0 6C 6C 00 00 00 00 00 00 18 02 00 00 18 02 00 00 ll..............
00000200 00 00 00 00 00 00 00 00 18 02 00 00 18 02 00 00 ................
00000210 FF FF FF F0 00 00 00 00 01 00 4D 65 73 73 61 67 ?.....Messag
00000220 65 42 6F 78 41 00 00 00 00 00 00 00 00 00 00 00 eBoxA...........
问题应该出在Import处,谁能帮我分析一下,到底如何解决??
谢谢!
--------------------------------------------------------------------------------
【版权声明】: 版权只属于原作者,如果涉及版权,请于作者联系!
2010年06月02日 18:50:20
[课程]Linux pwn 探索篇!