上面文章说了一些有关PE中会常用到的术语,接下来咱们就能从宏观的角度观察伟大的PE结构了:D
老规矩,说话前先看图:
为了兼容16位的MS-DOS程序,MS在PE结构中包含了MS-DOS程序必须的结构(DOS MZ Header和DOS Stub),接下来会详细描述这部分内容.
1.DOS MZ Header
在PE文件中,DOS MZ Header的结构如下定义:
IMAGE_DOS_HEADER STRUCT e_magic WORD ? ;EXE标志,"MZ" e_cblp WORD ? e_cp WORD ? e_crlc WORD ? e_cparhdr WORD ? e_minalloc WORD ? e_maxalloc WORD ? e_ss WORD ? ;初始的SS值 e_sp WORD ? ;初始的SP值 e_csum WORD ? e_ip WORD ? ;初始的IP值 e_cs WORD ? ;初始的CS值 e_lfarlc WORD ? e_ovno WORD ? e_res WORD 4 dup (?) e_oemid WORD ? e_oeminfo WORD ? e_res2 WORD 10 dup (?) e_ifanew WORD ? ;PE头的FOA IMAGE_DOS_HEADER ENDS
DOS MZ Header结构的大小为64btytes,结构的最后一个成员指定了PE文件头起始的FOA,紧跟着此结构后面的便是DOS Stub.
2.DOS Stub
既然兼容MS-DOS程序,那么一定有地方存放代码,毫无疑问,就是此处.
先写一段简单的代码:
1 .386 2 .model flat,stdcall 3 option casemap:none 4 5 include windows.inc 6 include kernel32.inc 7 include user32.inc 8 9 includelib kernel32.lib 10 includelib user32.lib 11 12 .const 13 szTitle db 'test',0 14 szContext db 'hello,msgbox',0 15 16 .code 17 18 start: 19 invoke MessageBox,NULL,offset szTitle,offset szContext,MB_OK 20 invoke ExitProcess,NULL 21 end start 22
编译链接,生成了最终的PE文件,msgbox.exe.将其载入16进制编辑器(我使用的UltraEdit),看到如下:
红色方框内为MZ DOS Header,蓝色方框则为DOS Stub,紫色箭头便是MZ DOS Header的最后一个成员e_ifanew所指向PE头所在的FOA.
现在将这个程序放入cmd中,运行debug msgbox.exe:
debug下键入d命令,会查看cs:ip处的起始字节码,同时也发现了与UltraEdit蓝色部分,即DOS stub中的字节码完全一样.
现在再使用u命令,则会查看cs:ip处的机器码翻译成汇编的语句,如下:
很明显,是16位汇编,这段汇编的功能很简单,就是通过21号中断的9号功能(mov ah,09 int 21)来实现对ds:dx指向的字符串(This program cannot be run in DOS mode.)进行输出.
最后输入g命令,运行程序进行测试,查看结果如下:
综上所述,只要在DOS Stub中添加了正确的代码,便可以在MS-DOS中看到我们期望的结果. :D