就整体思路来说,这个想法是完全正确的,但是需要实际操作起来却不是向说得那么轻松。1、我们是基于二进制文件修改,需要对PE结构有足够了解,虽说winnt.h里面定义了与PE结构有的结构体,但处理起来也不是想当然的。2、需要让重定位壳程序的代码以及IAT(但是笔者为了方便一些使用了ShellCode)。
接下来说说很多未接触过加壳的朋友都比较感兴趣的话题,加壳的一些重要操作:(笔者只是为了简单加壳,故未用到很高深的加密原来,只是简单的xor)
首先是ShellCode的编写(因为我用的是ShellCode,就简略说一下吧),ShellCode的核心就是动态查找API,然后模拟GetProcAddress函数的操作(因为大部分杀软会hook这个函数,故需要模拟一个类似的),首先利用fs寄存器,获取TEB,然后再根据TEB获取Kernel32的基地址然后利用导出表的序号索引、名字索引、地址索引确定我们所需的
API函数(必须用到序号索引,因为笔者实践发现win7以后kernel32导出表的序号并不是顺序排列),然后push相应参数,并call该API基址即可调用该API。
ShellCode写好以后,确定可以运行,然后加入标志用C32Asm取出二进制代码作为byte数组使用即可。(目前我只发现看雪的C32Asm可以以C形式拷贝,不明觉厉)
接下来就是壳的编写了,·首先我们把整个文件载入加壳程序的内存里,首先先根据文件的DOS获取PE头的位置,然后根据IMAGE_OPTIONAL_HEADER中的SizeOfHeader读入DOS头以及PE头(又称NT头),然后在挨个区块读入。
当然在读入之前需要需要判断一下是否为可执行文件,判断依据有3个:1、标志MZ,是否有DOS头,2、标志PE,是否有NT头3、根据IMAGE_FILE_HEADER里面的Characteristics判断是exe还是dll文件。然后我们需要在原文件的基础上修改其文件名,并创建加壳后的程序,名为原名_pack.exe,我用了一个比较偷懒的办法
接下来便进入了我们壳编写的最重要的四个步骤:1、添加区块信息。2、在末尾实际添加一个区块。3、编辑新区快,也就是我们壳的代码。4、重定位信息,跳转回远OEP。(因为用的ShellCode所以未涉及IAT以及hook的问题,这样虽未能到达保护的目的,但还是能加深对于壳的理解)
第一步,添加新区快信息,使用winnt.h提供的宏IMAGE_FIRST_SECTION(pNtHeader)获取第一个区块的指针,然后根据IMAGE_FILE_HEADER的区块数,找到最后一个区块,再在其后添加我们自己的区块(本应该检查一下其后那段区域是否包含其他信息,但是应为怠惰~)
随带一提以为是用的是ShellCode所以可以直接把OEP设置为新区块,如果是用引导段来做壳代码话,需要使用公式当前入口点=引导段RVA+(引导程序代码段入口点RVA引导程序代码段所在区块RVA),以及重定公式新地址VA = (需要重定位的VA - 引导程序ImageBase - 引导程序代码段所在区块RVA) + 待加壳程序的Imagebase + 新区段的RVA。这是使用引导程序的做法,但是如果使用ShellCode的话,不需要修改原程序IAT并重定位,但是取而代之的是我们需要在重定位表末尾添加一个重定位节。
那么问题来了为何要修改重定位表,原因是因为我们加载PE文件的时候并不都能载入我们预想的ImageBase,故需要对用到ImageBase的地方进行重定位。重定位表是数据目录表的第五个(好像是),当你可以使用IMAGE_DIRECTORY_ENTRY_BASERELOC(不知道什么意思的放VS里面右键查看定义即可)。
然后观察重定位表结构:
重定位表由若干个IMAGE_BASE_RELOCATION构成,以VirtualAddress为空的标识结束,其中有原始内存页的RVA,以及相对于此的RVA表TyprOffset(这里注释掉的原因是因为winnt.h里面是这样的,为了好看,但是实际PE机构是存在的),我们利用IMAGE_BASE_RELOCATION的首地址以及SizeOfBlock便可得到下一个IMAGE_BASE_RELOCATION,以此类推找到最后那个VirtualAddress为0的IMAGE_BASE_RELOCATION,并添加我们的重定位信息即可。
但是与此同时我们需要修改在数据目录表里面的重定位表大小的(因为PE加载器要调用这个信息,但是LoadPE不用,于是我郁闷了好久)
然后是编辑壳区段,没什么好说的,直接把ShellCode丢进去仅可(文后我会附ShellCode和加壳程序的源码)
最后,也是最重要的步骤,对代码段程序进行加密。很多人对这个步骤有很大的误解,他们都以为程序先解密,然后程序运行,再然后加密原程序。其实在普通壳中只需要有前两个步走即可,因为加载进内存并不改变原程序在磁盘中的信息。当然最后加密源程序并写入磁盘的壳也是有的,不过此类壳大都为了实现多态以及时时变型的功能。再加密原程序的方法我并没有查找到有关文档,但是我有个没经过实践的办法,就是用hook-IAT,hook的对象是ExitProcess,在此之前再加密原程序然后在执行ExitProcess。当然,现在市面流通的大都是骨肉壳,自然不需要这么复杂的做法。
那么至此,整个加壳过程就结束了,细节方面请参考源代码。
现在然我们看看加壳程序在IAD pro以及OllyDBG里面的情况。
首先是IDA pro:
未加壳之前:
然后是加壳后在IDA中的显示:
因为此壳程序之针对代码段加密,故IDA还是可以通过分析IAT知道程序调用了MessageBox。
接下来我们看看在OllyDBG中的反应:
在OllyDBG中未加密:
首先我们看到的是壳的代码,执行完这段代码(也就是解密完以后,但是我心情很好的加了个Neg,为了证明自己会ShellCode),再跳转回原OEP处,在这里我们直接使用ctrl+g查看原OEP处的程序(解密完了和上面一样,还看什么看。。。)
然后就是这样哒~
最后附上陪伴我做完这个程序的PE结构详解图:
注:开发环境:win10家庭中文版,使用VS2013,MFC开发
调试工具:OllyDBG、IDA pro、LoadPE、UltraEdit、NopePad++
资料来源:www.pediy.com、www.fishc.com
参看教程:老狼外壳编写教程、自己动手用VC编写加壳程序、鱼C论坛加密与解密系列教程
参考书籍:加密与解密(第三版)、windows PE权威指南
云盘:https://pan.baidu.com/s/1Oh-6IDAzxYxChGz73Jcn8A