4.6 添加节区
添加节区在很多场合都会用到,比如在加壳中,在免杀中都会经常使用到对PE文件添加一个节区。添加一个节区的方法有4个步骤,第1个步骤是在节表的最后面添加一个 IMAGE_SECTION_HEADER,第 2个步骤是更新 IMAGE_FILE_HEADER中的 NumberOfSections字段,第3步是更新IMAGE_OPTIONAL_HEADER中的SizeOfImage字段,最后一步则是添加文件的数据。当然了,前3个步骤是没有先后顺序的,但是最后一个步骤一定要明确如何改变。
4.6.1 手动添加一个节区
先来进行一次手动添加节区的操作,手动添加节区的过程是一个让我们熟悉上述步骤的过程。在网上有很多现成的添加节区的工具,我们自己编写工具的目的是掌握和了解其实现方法,锻炼我们的编程能力;手动添加节区是为了巩固前面的知识,熟悉添加节区的步骤。
接下来还是使用前面的那个测试程序。使用C32ASM用十六进制编辑方式打开这个程序,并定位到其节表处,如图4-35所示。
图4-35 节表位置信息
从图中可以看到该PE文件有3个节表,直接看十六进制信息可能很不方便,为了直观方便地查看节表中IMAGE_SECTION_HEADER的信息,那么使用LordPE来进行查看,如图4-36所示。
图4-36 使用LordPE查看该节表信息
用LordPE工具查看的确直观多了。对照LordPE显示的节表信息来添加一个节区。回顾一下IMAGE_SECTION_HEADER结构体的定义,定义如下:
IMAGE_SECTION_HEADER结构体的成员很多,但是真正要使用的就只有6个,分别是Name、VirtualSize、VritualAddress、SizeOfRawData、PointerToRawData和 Characteristics。这6项刚好与LordPE显示的6项相同。其实IMAGE_SECTION_HEADER结构体中其余的成员几乎不被使用。下面分别来介绍如何添加这些内容。
IMAGE_SECTION_HEADER的长度为40个字节,是十六进制的0×28。在C32ASM中占用2行半的内容,我们一次把这两行半的内容手动添加进去。回到C32ASM中,在最后一个节表的位置处开始添加内容,首先把光标放到右边的ASCII字符中,输入“.test”,如图4-37所示。
图4-37 添加".test"节名
接下来在00000240位置处添加节的大小,该大小直接是对齐后的大小即可。由于文件对齐是0×1000字节,也就是4096个字节。那么就采用最小值即可,使该值为0×1000。不知道大家是否还记得前面提到的字节顺序的问题,在C32ASM中添加时,正确的添加应当是“00 10 00 00”,以后添加时也要注意字节顺序,在添加后面几个成员时,不再提示注意字节顺序,大家应时刻清楚这点。在添加该值时,应当将光标定位在十六进制编辑处,而不是刚才所在的ASCII字符处。顺便要把VirutalAddress也添加上,VirtualAddress的值是前一个节区的起始位置加上上一个节对齐后的长度的值,上个节区的起始位置为0×5000,上个节区对齐后的长度为0×1000,因此新节区的起始位置为0×6000。添加VirtualSize和VirtualAddress后如图4-38所示。
图4-38 添加VirtualSize和 VirtualAddress
接下来的两个字段分别是SizeOfRawData和PointerToRawData,这两个字段的添加方法类似前面两个字段的添加方法,这里就不细说了,分别要添加“0×6000”和“0×1000”两个值,如图4-39所示。
图4-39 添加SizeOfRawData和PointerToRawData
PointerToRawData后面的12个字节都可以为0,只要修改最后4个字节的内容,也就是 Characteristics的值即可,这个值直接使用上个节区的值即可,实际添加时应根据所要节的属性给值,这里为了省事而直接使用上个节区的属性,如图4-40所示。
图4-40 添加Characteristics
整个节表需要添加的地方就添加完成了,接下来需要修改该PE文件的节区数量了,当前节区数量是3,这里要修改为4。虽然可以通过LordPE等修改工具完成,但是这里仍然使用手动修改。对于修改的位置请大家自行定位找到,修改如图4-41所示。
图4-41 修改节区个数为4
除了节区数量以外还要修改文件映像的大小,也就是前面提到的SizeOfImage的值。由于新添加了节区,那么应该把该节区的大小加上SizeOflmage的大小,即为新的SizeOflmage的大小。现在的SizeOflmage的大小为0×6000,加上新添加节区的大小为0×7000。SizeOflmage的位置请自行查找,修改如图4-42所示。
图4-42 修改SizeOflmage的值为0×70000
对于修改PE结构字段的内容都已经做完了,最后一步就是要添加真实的数据了,由于这个节区不使用,因此填充0值就可以了,文件的起始位置为0×6000,长度为0×1000。把光标移到文件的末尾,单击菜单“编辑”-> “插入数据”命令,在插入数据大小文本框中输入十进制的4096,也就是十六进制的0×1000,如图4-43所示。
图4-43 “插入数据”对话框的设置
单击“确定”按钮,可以看到在刚才的光标处插入了很多0值,这样我们的工作也完成了,单击“保存”按钮进行保存,提示是否备份,选择“是”。然后用LordPE查看一下添加节区的情况,如图4-44所示。
图4-44 添加新的节区信息
对比一下前后两个文件的大小,如图4-45所示。
图4-45 添加节区前后文件的大小
从图4-45中可以看出,添加节区后的文件比原来的文件大了4KB,也就是添加了4096个字节的0值的原因。也许最关心的不是大小问题,而在于软件添加了大小后是否真的可以运行,我们试运行一下,是可以运行的。
上面的整个过程就是手动添加一个新节区的全部过程,除了特有的几个步骤以外,就是要注意新节区的内存起始位置和文件起始位置的值。相信通过上面手动添加节区对此已经非常熟悉了。那么下面就开始通过编程来完成添加节区的任务吧。
4.6.2 通过编程添加节区
通过编程添加一个新的节区无非就是文件相关的操作,只是多了一个对PE文件的解析而已。添加节区的步骤和手动添加节区的步骤是一样的,只要一步一步按照上面的步骤写代码就可以了。在开始写代码前,首先修改一下FileCreate()这个函数中的部分代码,修改代码如下:
在这里,要把SEC_IMAGE这个值注释掉,因为要修改内存文件映射,有这个值会使添加节区失败,因此要将其注释掉或者删除掉。
程序的界面如图4-46所示。
图4-46 添加节区界面
首先来编写“添加”按钮响应事件,代码如下:
按钮事件中最关键的地方是AddSec()这个函数,该函数有两个参数,分别是添加节的名称与添加节的大小。这个大小无论输入多大,最后都会按照对齐方式进行向上对齐。我们看一下AddSec()函数的代码。
代码中每一步都按照相应的步骤来完成,其中用到了两个函数分别是AlignSize()和AddSecData()。前者是用来进行对齐的,后者是在文件中添加实际的数据内容的。这两个函数非常简单,代码如下所示:
整个添加节区的代码就完成了,仍然使用最开始的那个简单程序进行测试,看是否可以添加一个节区,如图4-47所示。
从图4-47中可以看出,添加节区是成功的。试着运行一下添加节区后的文件,可以正常运行,而且添加节区的文件比原文件大了4KB,和前面手动添加的效果是一样的。
至此,对PE文件结构的介绍就结束了。其实PE文件结构还有很多比较重要的内容,但是这里只介绍了一些基础的知识,至于其他的内容请大家自行学习。PE结构查看器最关键的是兼容性,PE结构是可以进行各种变形的,常规的PE结构也许比较好解析,但是经过变形的PE结构解析起来就可能会出错,因此要不断地尝试去解析不同的PE文件结构,PE查看器兼容性才会不断地完善。前面介绍了通过C32ASM进行手动分析PE文件结构,这种方法是有助于完善PE查看器的。这就好比数据恢复一样,数据恢复高手绝对不是通过数据恢复工具来进行的,虽然高手们也在使用工具,但是如果遇到较为复杂的情况,数据恢复工具可能就会显得无力,那么手动分析分区格式将是唯一的方法。
图4-47 添加节区