之前在写了写壳基础篇,现在就来完成写壳高级篇。没有基础篇的知识,那理解高级篇就比较困难。有了写壳基础后,才能在其基础上逐步实现高级功能,加壳的目的主要是防止别人破解,而想要别人很难破解,我认为要在花指令、混淆和指令虚拟化上大量的时间及脑力才能做到,这个比较费脑力费时间。我在此就谈谈一些能快速入门的反调试技术,下面说的难度将逐渐提升。
主要工具: VS2017、x64dbg、OD
实验平台:win10 64位
实现功能:反调试、IAT加密、Hash加密、动态解密。
一、反调试
顾名思义,就是阻止别人调试程序,在PEB结构中有一个BegingDebugged标志位专门用于检测是否处于调试状态,为1则处于调试状态,用VS2017测试下列程序:
敲完代码后按Ctrl+F5直接运行可以输出Hello World!,按F5以调试方式运行程序则会弹出反调试窗口,说明这种方法检测是否正在被调试成功!
但是坏消息是用某些论坛的OD运行这程序依然能正常运行,检测不到反调试,现在的各个平台的OD基本都有一个插件是StrongOD,他能干掉PEB中所有检测反调试的标志位!所以以上方法基本被淘汰了。
所以这里介绍一个可以在OD中也能反调试的方法,有一个可以同时在0环和3环运行的函数NtQueryInformationProcess,它的主要作用是查看进程相关的各种信息,在这把它用于检测调试。
我们给它第二个参数传入ProcessDebugPort,当输出的查询信息为0xFFFFFFFF时,则此程序处于被调试状态。代码如下:
编译生成程序后使用OD打开再运行,弹出窗口,反调试成功,我在这测试了15PB的OD和吾爱破解的OD均能有效。掌握了反调试的方法,我们可以把它放在壳代码的各个角落,检测到调试就马上退出程序,多放置几个阴人位置,这样就能增加破解的难度了!
二、IAT加密
要对IAT加密前提条件是对PE文件比较熟悉。
IAT也就是导入函数的地址表,程序在加载到内存后IAT中填充的都是函数的地址,使用OD打开随意exe文件,我这就打开QQ.exe,找到第一个HEX数据是FF15开头的CALL代码,右键查看内存地址。
从图中知:如果IAT没加密反汇编代码一目了然就能看见用的的什么API,加密的目的就是即使调用函数也不能一眼就看出调用的是什么函数(有字符串提示)。
内存窗口的地址是IAT每个元素的地址,数值一列是IAT存储的数据,注释中也解释了它们是什么API的地址。
IAT加密原理就是:
遍历导入表获取每个函数的IAT地址(对应上图内存栏中地址的值)
取出IAT地址的内容,就是函数的地址(上图内存栏中数值的值),把该函数地址进行加密后得到一个数据(我这就是异或了一个值0x13973575进行了加密)
申请一段内存,其中存放解密上述的数据得到真地址,然后调用该地址的代码。
把申请的内存地址放入IAT地址对应的数值中。
完成以上步骤后IAT就被加密了,当然第3步当中可以进行适当的混淆和加花指令别人就更加看不出来了。
加密后再查看反汇编代码就没有字符串提示了,再查看该地址内存数据也没有字符串注释了,效果图如下:
在汇编窗口Ctrl+G输入04A90000查看,其中的代码有花指令如下:
去掉花指令后其汇编代码最终成功调用真正函数的地址了,就是:
具体操作就是在壳代码解压缩,解密后,再进行IAT修复加密,遍历IAT代码我就不解释了。IAT加密源码:
三、Hash加密
为什么要进行Hash加密?因为在逆向工作者逆向的过程中,字符串信息对他们来说很重要,如果看见了一个API函数的字符串,那么他大概就能知道这段代码大概的功能了,轻而易举就能破解掉,为了阻止这种事件发生,那么Hash加密在这就能发挥出很大作用。
众所周知1个字节是8位,这代表他表示2的8次方个数,也就是256种可能,如果我们把它的一个数据代表一个系统中的函数(API),相当于给函数一个序号,那么1个字节就能存储256个函数的信息,那2个字节就能存储2的16次方也就是65536个API函数,这真是大大的好消息, windows系统中的API函数也就几千个,2个字节存储其全部API函数信息真是绰绰有余。
而让这2个字节的数据代表一个函数,这个数据我们称它为Hash值,因此需要设计一个算法。我在这设计是方法是定义一个2字节类型(short)的数据,分别把nHash值先左移11位再右移5位后相加,再加上API函数中一个字符的Ascii码,以此循环遍历完整个API函数的所有字符,得到一个我们需要的Hash值。在之前写壳基础篇中提到过壳代码中的API是动态获取的,那么我们在动态获取的时候使用Hash值更能提高隐蔽性,使破解者不易发现我们所要使用的是哪个函数。
具体Hash加密代码如下:
使用方法是首先使用上述代码对我们需要使用API函数进行Hash加密得到Hash值,然后再写一个Hash值对比字符串的函数(解密),使用该值和系统中的API函数对比,和谁相等,我们就把这个函数的地址获取取出。这样我们就隐晦的得到了所需的函数的地址。
Hash解密代码如下,需要传入2个参数,1是对比函数的地址,2是Hash值:
上述代码有大大的优化空间,比较懒我就不弄了。
有了Hash加解密,就可以自己实现一个GetProcAddress函数了,在这之后需要获取任何API函数就用自己实现的GetProcAddress函数,这样就是达到更加隐蔽的获取API函数的目的,学会了Hash加解密咱也就脱离了小白的行列了。
代码如下,参数1是所需API的模块基址,参数2是Hash值:(纯汇编获取更能锻炼基本功!)
四、动态解密
加入动态解密的壳,这无疑是强度较高的壳了,它能够在目标程序运行起来之后,动态的对代码段进行解密。先运行一段代码解密后一部分的代码,然后再运行解密后的代码,可以往复循环,这样破解者只能看见运行着的代码的附近的代码,隔得远的代码处于加密状态,这样就需要花费大量的时间才能破解了,当然想要实现这种高强度,还是需要花费很多时间去设计的,而且要求我们对x86汇编语言有比较深刻理解,这我就分享下我对动态解密理解。
下面我直接根据一个案列来分析动态解密流程:
为了方便演示效果,我在VS中用汇编以动态获取API方式写了一段功能是弹窗的代码,效果如图
将生成EXE文件用0x32dbg(或者OD)打开后找到弹窗功能的汇编代码,扣取出该段代码16进制字节。扣取字节的操作是在x32dbg中选中该段代码后,右键->复制->数据->C样式ShellCode字符串
OD中选中代码后右键->数据转换->C++->字节
把这些字节存在一个字符串数组中,直接用我这个就行了:
在0x32dbg(或者OD)中选中汇编代码段后右下角会显示选中的字节大小。
下面写一个加密该字符串的代码,编译的时候VS项目属性中配置“C/C++ -> 代码生成 -> 安全检查(禁用GS)”,“连接器 -> 高级 -> 数据执行保护DEP(关闭)”。
加密后会得到一个字节数组,我们把它复制下来存到另一个数组中,然后把他放在解密代码的屁股后面。
开始写解密代码,解密代码才是动态解密中的核心点,重中之重。
这里要说一下GetPC技术,GetPC技术翻译为中文也就是获取指针计数器。在x86汇编中实际上就是获取当前代码EIP的技术。我这用的是call 指令,call xxx指令相当于 push 下一行代码的EIP + jmp xxx。 那么我们直接把XXX改为下一行指令的地址就能获取当前EIP 内联汇编代码为:
这里要注意的是这段代码后面要紧跟加密后的代码。
在实际的壳代码中,先把需要加密的代码加密后和解密代码组装起来就可以达到动态解密的功能。
本文由看雪论坛 九阳道人 原创
原文链接:[原创]C++写壳之高级篇-『加壳脱壳』-看雪安全论坛