PE加壳原理以及实现

     对于很多高手来说,加壳是一件微不足道的小事,但是对于大部分从未接触过的朋友都是有点懵。新手对于壳的理解大都就是在执行原程序代码前率先取得控制权的代码,这些代码可以对原代码or数据进行解密(在未解密前是看不懂的),此方法常用于干扰静态反汇编,记得早些时候壳也被称为SMC加密。

    就整体思路来说,这个想法是完全正确的,但是需要实际操作起来却不是向说得那么轻松。1、我们是基于二进制文件修改,需要对PE结构有足够了解,虽说winnt.h里面定义了与PE结构有的结构体,但处理起来也不是想当然的。2、需要让重定位壳程序的代码以及IAT(但是笔者为了方便一些使用了ShellCode)。

PE加壳原理以及实现_第1张图片

接下来说说很多未接触过加壳的朋友都比较感兴趣的话题,加壳的一些重要操作:(笔者只是为了简单加壳,故未用到很高深的加密原来,只是简单的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头),然后在挨个区块读入。

PE加壳原理以及实现_第2张图片


当然在读入之前需要需要判断一下是否为可执行文件,判断依据有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:


   未加壳之前:


PE加壳原理以及实现_第3张图片PE加壳原理以及实现_第4张图片

然后是加壳后在IDA中的显示:

PE加壳原理以及实现_第5张图片

    因为此壳程序之针对代码段加密,故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


你可能感兴趣的:(PE结构,加密,加壳)