|
Luevelsmeyer 的pe.txt总算是看完了,感谢ah007的翻译。
最后译者说道精华在于附录中,我略作修改重建一个文件,但不管怎么调,都无法运行。
一开始还以为是什么地方没有写对,可运行附的hello.exe也是报错。
看来用0x20对齐在XP下是不行的。这篇附录中就是太老了...
我来说说我改写的过程吧。
1.先写一段简单的代码
;MASMPlus 代码模板 - 普通的 Windows 程序代码
.386
.Model Flat, StdCall
Option Casemap :None
Include user32.inc
Include kernel32.inc
IncludeLib user32.lib
IncludeLib kernel32.lib
.data
MsgBoxText db "Hello.",0
MsgCaption db "Test",0
.CODE
START:
invoke MessageBox, NULL,addr MsgBoxText, addr MsgCaption, MB_OK
invoke ExitProcess,NULL
END START
顺便提一下,我用的是MASMPlus,可以在http://www.aogosoft.com/下载。
2.编译完之后用OD看一看
004****1000 >/$ 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004****1002 |. 68 073****4000 push 004****3007 ; |Title = "Test"
004****1007 |. 68 003****4000 push 004****3000 ; |Text = "Hello."
00***00C |. 6A 00 push 0 ; |hOwner = NULL
00***00E |. E8 070****0000 call <jmp.&user32.MessageBoxA> ; \MessageBoxA
004****1013 |. 6A 00 push 0 ; /ExitCode = 0
004****1015 \. E8 060****0000 call <jmp.&kernel32.ExitProcess> ; \ExitProcess
00***01A $- FF25 082****4000 jmp dword ptr [<&user32.MessageBoxA>> ; user32.MessageBoxA
004****1020 .- FF25 002****4000 jmp dword ptr [<&kernel32.ExitProces> ; kernel32.ExitProcess
不错,以后代码照这个抄就可以了。
发现用到了两个DLL的函数,稍微复杂了一点。
再看看原文中的代码
6A 00 ; push 0x000****0000
68 d0 01 10 00 ; push offset _written
6A 0D ; push 0x00***00d
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 &n
bsp; ; push eax
2E FF 15 24 02 10 00 ; call dword ptr cs:__imp__WriteConsoleA@20
C3 ; ret
注意两个call对应的机器码:E8和2E的不同,看来E8后面跟的是一个偏移量,
00***01A $- FF25 082****4000 jmp dword ptr [<&user32.MessageBoxA>> ; user32.MessageBoxA
这条命令算是跳板的跳板。不想这样写,还是用2E FF 15 。
估算一下,代码长度在0x20之内。
3.建一个Hello.exe,用WinHex来改。
a) 先大约有个思路。
文件对齐 0x00 00 02 00;
区段对齐 0x00 00 10 00;
ImageBase 0x00 40 00 00;
只要两个段,一个代码段(Raw:0x200 / Rva:0x1000),把上面的代码放进去就可以了;
一个数据段(Raw:0x400 / Rva:0x2000),一上来先放两个字符串,然后是输入目录。
这样看来最多0x600个字节就可以搞定了,打开WinHex,加这么多的0。
然后就可以开始改了。
b)Dos Stub 这部分很简单,只要有MZ,然后设一下e_lfanew就可以了。
000****0000 4D 5A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 MZ..............
000****0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0030 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 ............@...
c)NT Header 部分这样看肯定一头雾水...
000****0040 50 45 00 00 4C 01 02 00 00 00 00 00 00 00 00 00 PE..L...........
000****0050 00 00 00 00 E0 00 02 01 0B 01 00 00 20 00 00 00 ....?...... ...
000****0060 D0 00 00 00 00 00 00 00 00 10 00 00 00 10 00 00 ?..............
000****0070 00 20 00 00 00 00 40 00 00 10 00 00 00 02 00 00 . ....@.........
000****0080 04 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 ................
000****0090 00 30 00 00 00 02 00 00 00 00 00 00 02 00 00 00 .0..............
00***00A0 00 00 10 00 00 10 00 00 00 00 10 00 00 10 00 00 ................
00***00B0 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ................
00***00C0 20 20 00 00 B0 00 00 00 00 00 00 00 00 00 00 00 ..?..........
00***00D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00***00E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00***00F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0130 00 00 00 00 00 00 00 00 2E 63 6F 64 65 00 00 00 .........code...
000****0140 00 00 00 00 00 10 00 00 20 00 00 00 00 02 00 00 &
nbsp; ........ .......
000****0150 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 ............ ..`
000****0160 2E 64 61 74 61 00 00 00 00 00 00 00 00 20 00 00 .data........ ..
000****0170 C0 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 ?..............
000****0180 00 00 00 00 40 00 00 C0 ....@..
具体说一下:
Signature 0x4550 ;直接在右边输入PE就可以了
文件头部分:
Machine 0x14c ;Intel 386
NumberOfSections 0x2 ;2个区段
TimeDateStamp 0x0
PointerToSymbolTable 0x0
NumberOfSymbols 0x0
SizeOfOptionalHeader 0xe0 ;可选头大小,永远是0xe0
Characteristics 0x102 ;具体看pe说明
----------------------------------------
可选头部分:
Magic 0x10b ;32-位
MajorLinkerVersion 0x0
MinorLinkerVersion 0x0
SizeOfCode 0x20 ;随便写 0x0~0xFF FF FF FF都可以
SizeOfInitializedData 0xd0 ;随便写 0x0~0xFF FF FF FF都可以
SizeOfUninitializedData 0x0
AddressOfEntryPoint 0x1000 ;入口,这里就是代码段开头处的Rva
BaseOfCode 0x1000 ;代码段开头处的Rva
BaseOfData 0x2000 ;数据段开头处的Rva
ImageBase 0x40***00 ;写代码是要注意把Base加上
SectionAlignment 0x1000 ;区段对齐
FileAlignment 0x200 ;文件对齐
MajorOperatingSystemVersion 0x4 ;NT 4.0
MinorOperatingSystemVersion 0x0
MajorImageVersion 0x0
MinorImageVersion 0x0
MajorSubsystemVersion 0x4 ;Win32 4.0
MinorSubsystemVersion 0x0
Reserved1 0x0
SizeOfImage 0x3000 ;请看下面的话
SizeOfHeaders 0x200 ;文件中第一个区段的偏移量
/*
这里插段话,在原文的附录中,关于这两个字段是这
描述的。
SizeOfImage c0 00 00 00 ; sum of all section sizes
SizeOfHeaders a0 01 00 00 ; offset to 1st section
这个描述很不好,首先all section是包括Headers这个Section的
但下面的1st section,显然指的是Headers后的第一个区段。
这两个section含义不一致。
我就被误导了,一开始这里填了0x2000,就没有通过。
其实原文前面的解释比较清楚:
...它就是所有头和节的长度的总和。
*/
CheckSum 0x0
Subsystem 0x2 ;Win GUI
DllCharacteristics 0x0
SizeOfStackReserve 0x10***00 ;就这样写吧
SizeOfStackCommit 0x1000 ;
SizeOfHeapReserve 0x10***00 ;
SizeOfHeapCommit 0x1000 ;
LoaderFlags 0x0
NumberOfRvaAndSizes 0x10 ;总是这个值,下面有多少个目录
----------------------------------------
数据目录部分
Directory 0
Size 0x0
VirtualAddress 0x0
----------------------------------------
Directory 1
Size 0xb0 ;随便写 0x0~0xFF FF FF FF都可以
VirtualAddress 0x2020 ;两个字符串0x20总够了吧
----------------------------------------
. ;还有14个,全是0
.
.
----------------------------------------
区段头
Section 0
Name .code ;区段的名字
VirtualAddress 0x1000 ;内存中的位置
SizeOfRawData 0x20 ;随便写 0x1~0x10 00都可以
PointerToRawData 0x200 ;文件中的位置
PointerToRelocations 0x0
PointerToLinenumbers 0x0
NumberOfRelocations 0x0
NumberOfLinenumbers 0x0
Characteristics 0x60***02 ;具体看pe说明
----------------------------------------
Section 1
Name .data ;区段的名字
VirtualAddress 0x2000 ;内存中的位置
SizeOfRawData 0xc0 ;写大一点就可以了
PointerToRawData 0x400 ;文件中的位置
PointerT
ToRelocations 0x0
PointerToLinenumbers 0x0
NumberOfRelocations 0x0
NumberOfLinenumbers 0x0
Characteristics 0xc00***04 ;具体看pe说明
----------------------------------------
关于“随便写”的地方,都是size。
看来.code段无所谓size,别超过段大小就行。
.data段就不行,不能少,但可以多,能多多少就要看文件的大小。
SizeOfCode 0x20 ;随便写 0x0~0xFF FF FF FF都可以
SizeOfInitializedData 0xd0 ;随便写 0x0~0xFF FF FF FF都可以
Directory 1
Size 0xb0 ;随便写 0x0~0xFF FF FF FF都可以
这3个有点夸张,但可以正常运行。
c)现在不妨跳过.code段,先搞.data段
000****0400 50 45 D1 A7 CF B0 00 00 BF B4 B5 BD C4 E3 C1 CB PE学习..看到你了
000****0410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0420 5C 20 00 00 00 00 00 00 FF FF FF FF 7C 20 00 00 \ ......??| ..
000****0430 64 20 00 00 6C 20 00 00 00 00 00 00 FF FF FF FF d ..l ......??
000****0440 8C 20 00 00 74 20 00 00 00 00 00 00 00 00 00 00 ?..t ..........
000****0450 00 00 00 00 00 00 00 00 00 00 00 00 AC 20 00 00 ............?..
000****0460 00 00 00 00 AC 20 00 00 00 00 00 00 BC 20 00 00 ....?......?..
000****0470 00 00 00 00 BC 20 00 00 00 00 00 00 75 73 65 72 ....?......user
000****0480 33 32 2E 64 6C 6C 00 00 00 00 00 00 6B 65 72 6E 32.dll......kern
000****0490 65 6C 33 32 2E 64 6C 6C 00 00 00 00 00 00 00 00 el32.dll........
00***04A0 00 00 00 00 00 00 00 00 00 00 00 00 01 00 4D 65 ..............Me
00***04B0 73 73 61 67 65 42 6F 78 41 00 00 00 02 00 45 78 ssageBoxA.....Ex
00***04C0 69 74 50 72 6F 63 65 73 73 00 00 00 00 00 00 00 itProcess.......
00***04D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00***04E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00***04F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
上来两个字符串,注意最后加'\0'。对应的Rva是0x00 40 20 00和0x00 40 20 08
从0x420开始是输入目录
因为有2个DLL,所以先留3*0x14=0x3c的地方,先把4个FF写好,以后找起来方便些,一一。
OriginalFirstThunk 0x205c
TimeDateStamp 0x0 ;
ForwarderChain 0xffffffff ;不中转
Name 0x207c
user32.dll
FirstThunk 0x2064
----------------------------------------
OriginalFirstThunk 0x206c
TimeDateSt
amp 0x0 ;
ForwarderChain 0xffffffff ;不中转
Name 0x208c
kernel32.dll
FirstThunk 0x2074
----------------------------------------
全0
----------------------------------------
之后我放的是4组Thunk,注意每组最后一个元素都是0。
这样上面两个OriginalFirstThunk和FirstThunk都可以定下来了。
之后是两个DLL的名字,于是Name可以定下来了。
然后是两个IMAGE_IMPORT_BY_NAME
WORD Hint ;随便写一个
BYTE Name[1] ;函数名
MessageBoxA是user32.dll的。
ExitProcess是kernel32.dll的。
这样上面的Thunk可以定了。
好,数据段的大小也定了。回到上面去改一下。
d)最后写代码。
000****0200 6A 00 68 00 20 40 00 68 08 20 40 00 6A 00 2E FF j.h. @.h. @.j..?
000****0210 15 64 20 40 00 6A 00 2E FF 15 74 20 40 00 00 00 .d @.j..?t @...
000****0220 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000****0230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004****1000 >/$ 6A 00 push 0 ; /Style = MB_OK|MB_APPLMODAL
004****1002 |. 68 002****4000 push 004****2000 ; |Title = "PE学?,B0,""
004****1007 |. 68 082****4000 push 004****2008 ; |Text = "?,B4,"?,BD,"你了"
00***00C |. 6A 00 push 0 ; |hOwner = NULL
00***00E |. 2E:FF15 642****4000> call dword ptr cs:[<&user32.MessageBoxA> ; \MessageBoxA
004****1015 |. 6A 00 push 0
004****1017 \. 2E:FF15 742****4000> call dword ptr cs:[<&kernel32.ExitProcess>] ; kernel32.ExitProcess
代码很简单,00 20 40 00也就是 00 40 20 00,是第一个字符串的地址。
要注意“高高低低”的规则。
64 20 40 00 是00 40 20 64是user32.dll的FirstThunk,指向的就是MessageBoxA
其他:
学习pe的时候,用c写了段读pe结构的代码,对于学习还是很有帮助的。
winnt.h包含了pe结构的说明,但一开始include之后一排错。
原来还要include <window.h>。
终于结束了,打完收工... |
|