%title缩略图

PE文件-dos存根

DOS存根实际上包含DOS头,一些文章将DOS头和DOS存根分开了,如果仔细阅读微软官方文档你会发现微软将DOS头和DOS程序代码统称为DOS存根。为了证言和学习,我决定忽略他们的文章,自己去找原始的资料并实验对比,这样来让PE文件的构成刻在脑海中。

PE文件整个结构暂时没有在我脑海中,因此一步一步来,凡事皆有头,我们先看PE文件头。

https://docs.microsoft.com/en-us/windows/win32/debug/pe-format

这是微软的官方文档,文档全是英文,从文件头的结构预览就可以看到,很明显第一部分是DOS存根,如下图:

%title插图%num

MS-DOS Stub翻译过来就是MS-DOS存根

%title插图%num

存根的意思就是留底,MS-DOS是微软早期的系统,微软目前还是保留着它了。意味着依然可以在DOS系统下运行,只不过会打印出一句提示,This program can not be run in DOS mode。不信咱可以试试,我这边把windows10下的pe文件放到dos模拟器执行,结果如下图:

%title插图%num

好了,回到正题。有的人可能注意到了MS-DOS Stub后面的括号里的内容,Image Only,Image即我们通常所见到的exe后缀或者.dll后缀的可执行文件,另外一种就是COFF文件,即Common Object File Format,公用对象文件(后续可能专门分析这种文件格式)。默认我们研究的是Image文件,即.exe后缀或者.dll的可执行文件,括号里的Image Only说明MS-DOS 存根仅仅会在.exe后缀或者.dll文件里存在。

MS-DOS存根具体内容是什么,占几个字节,这是我们本文的重点。

具体内容是什么,通过上面的运行截图大概也能知道,即让程序在dos系统里输出一句话:This program can not be run in DOS mode.

那么占几个字节呢,存根的结构是什么样的?很显然没有地方直接告诉你占几个字节,但是我们可以从微软的官方文档推算出

%title插图%num

从图中可以知道MS-DOS存根后面的内容是Signature,翻译即签名,那么我们只要知道签名信息在文件中的偏移量,就知道了MS-DOS存根的大小,因为签名信息在文件中的起始位置必然是MS-DOS存根的结束位置。

那么我们看看Signature签名信息的官方文档说明

%title插图%num
%title插图%num

简述就是在PE可执行文件的偏移量0x3c处保存的也是个偏移量,该偏移量处的值就是Signature签名信息,签名信息是4个字节,50450000,ASCII码即PE00

由此可以知道Signature签名信息在文件中的偏移量保存在0x3c处,那么我们只要知道0x3c处保存的内容是多少就知道MS-DOS存根的结束位置了

我们以一个测试文件为例,test.exe,看看0x3c处的内容是多少

%title插图%num

如图,0x3c处的内容是e8000000,即0xe8,说明文件test.exe在0xe8处开始是Signature签名信息,是不是我们看看0xe8处的内容就知道了,如果内容是50450000,就说明是对的

%title插图%num

是对的,0xe8开始是Signature签名信息。于此同时这也是MS-DOS存根的结束位置。

那么文件开头至0xe8这段内容就都是MS-DOS存根了。大小占0xe8个字节,即232个字节,注意,并不是所有的pe可执行文件的MS-DOS存根大小都是一样的

MS-DOS存根具体内容又分别是什么内容呢?带着疑问继续

%title插图%num

这是微软的官方文档,Overview翻译即总览,如图可以看到,PE Header上面框出来的集合应该都属于MS-DOS存根的一部分了,这些组合起来就是MS-DOS存根,我们可以逐步论证看看

先看看框里的第一部分MS-DOS 2.0 Compatible EXE Header,即DOS头,不明白很多文章喜欢把DOS头和DOS存根分开来讲,但凡实际上对比研究会发现所谓的DOS存根其实是个总称,其中包含DOS头部。

%title插图%num

这张图是来自winnt.h中定义的DOS头部结构体,WORD占2个字节,LONG占4个字节,一共19个字段,其中1个LONG类型、1个长度为4的WORD类型数组、1个长度长度为10的WORD类型数组和16个WORD类型的字段,1*4+4*2+10*2+16*2=64,由此可知DOS头占64个字节,我们逐个看看。

e_magic。注释是Magic number,即魔法数字,幻数。占2个字节,我们看看是什么

%title插图%num

依然用我的示例文件test.exe为例,原来是e_magic的内容是:MZ,即Magic number。

e_cblp。注释是Bytes on last page of file,表示的是文件最后一页实际占用的字节数,我这儿是0x90,即144个字节

%title插图%num

e_cp。注释是Pages in file,即文件的页数,即块的数量,每个块默认是512字节,我这儿是0x3,即3页

%title插图%num

e_crlc。注释是Relocations,即重定位项的数量。我这儿是0

%title插图%num

e_cparhdr。注释是Size of header in paragraphs,其实就是以段(16字节块)为单位指定可执行头的大小,我这儿是0x4

%title插图%num

e_minalloc。注释是Minimum extra paragraphs needed,即最少额外段落数,我这儿是0

e_maxalloc。注释是Maximum extra paragraphs needed,最大额外段落是,我这儿是0xffff

%title插图%num

正好是16位DOS系统下的CPU的寻址范围0-0xffff

e_ss。注释是Initial (relative) SS value,即ss栈段寄存器的初始值。我这儿是0

e_sp。注释是Initial SP value,即SP寄存器的初始值,我这儿是0x00b8

%title插图%num

e_csum。注释是Checksum,即dos可指向文件内容的校验和,我这儿是0

e_ip。注释是Initial IP value,即IP寄存器的初始值,我这儿是0

e_cs。注释是Initial (relative) CS value,即CS寄存器的初始值,我这儿是0

%title插图%num

e_lfarlc。注释是File address of relocation table,即重定位表的偏移地址,我这儿是0x40

%title插图%num

e_ovno。注释是Overlay number,指定覆盖数,通常为0。

e_res[4]。注释是Reserved words,即保留字段,4*2=8,占8个字节。

e_oemid。注释是OEM identifier (for e_oeminfo)。为e_oeminfo指定OEM的标识符。我这儿是0

%title插图%num

e_oeminfo。注释是OEM information; e_oemid specific。即为e_oeminfo的特定值指定OEM信息。我这儿是0

e_res2[10]。注释是Reserved words,即保留字段,10*2=20,占20个字节。

e_lfanew。注释是File address of new exe header。即真正的PE文件的头部偏移量,我这儿是0xe8

%title插图%num

这就是MS-DOS存根中的DOS头,一共64个字节,前文已经计算过整个MS-DOS存根的大小是232个字节,232-64=168,这168个字节其实就是DOS存根的代码和重定位表,如微软API的总览所示

%title插图%num

将exe文件放到DOS模拟器就可以证明如图这一点。

再下面就是unused的内容,即未被使用的内容,直到遇到PE Header结束,或者说遇到PE00这个签名结束,也或者说遇到NT_HEADERS结束。

发表回复