内嵌补丁练习

内嵌补丁

内嵌补丁即内嵌代码补丁(Inline Code Patch),当难以直接修改指定代码时,插入并运行被称为“洞穴代码”(Code Cave)的补丁代码后,对程序实现打补丁。该技术经常用于对象程序经过运行时压缩(或加密处理)而难以直接修改的情况。


如图,左边是经典的运行时压缩代码(或加密代码),EP代码先将加密的OEP代码解码,再跳转到OEP代码处执行。若要打补丁的代码存在于经过加密的OEP区域时,是难以实现打补丁的,因而右边是解决该问题的内嵌代码补丁,即在文件中另外设置被称为洞穴代码的补丁代码,在EP代码解码后修改其JMP指令使其跳转到洞穴代码处而不是OEP,执行完洞穴代码后再跳转至OEP执行,因此这种方法可以达到每次运行时都会对进程内存的代码打补丁的效果。

代码补丁和内嵌补丁的区别如下表:



补丁代码设置的位置:

1、文件的空白区域;

2、最后一个节区的扩展区域;

3、添加新节区。


内嵌补丁练习PatchMe

该小程序从ap0x的Reversing Labs页面下载。

运行程序查看:


显示“你必须给这个Nag打上补丁”,即让你修改弹框显示的字符串内容,点击确定:


对话框中显示有一个字符串要求解压其本身。

整个程序即要求修改两处字符串,然而这两处字符串都处于加密状态。

分析解密循环代码以及需要修改的字符串所在位置

使用Ollydbg打开该文件:


可以看到,地址00401007之后的代码均为加密代码。

尝试使用字符串检索法查找:


可以看到,所有字符串处于加密状态,无法找到弹框显示的字符串。

跟踪进入00401001地址处CALL指令调用的函数,执行一段时间后遇到如下代码:


这段代码即为解码循环,XOR命令对特定区域(4010F5~401248)进行解密。

跟踪进入004010B0处CALL指令调用的函数:


可以看到,这里存在两个解密循环。地址004010C8处的XOR指令解密00401007~401085区域,然后再执行地址004010DB处的XOR指令解密004010F5~00401248区域,可以发现该区域和上一个图中解密区域是一致的,可以知道004010F5~00401248区域是经过双重加密处理的。

接着返回到004010B6处CALL指令,跟踪进入:


注意到一个ADD指令的循环,其是校验和计算循环,00401041地址处的指令先将EDX初始化为0,然后使用ADD指令从004010F5~00401248区域以4个字节为单位依次读取值,将进行加法运算后将累加的结果保存到EDX寄存器中。校验和计算区域为双重加密区域,可知要修改的字符串在此区域中。

中间的CALL指令是对0040124A区域进行解码处理,进入其中查看:


接着,00401062和00401068地址处的CMP指令和JE指令用来计算得到的校验值与31EB8DB0比较,若相等则跳转到00401083地址处的JMP指令再跳转到OEP,否则输出错误信息并终止程序。

这种校验方法用于检测特定区域的代码或数据是否被改动过。因此,当修改了指定区域的代码或数据时,需要修改校验和比较部分的代码。

接着调试进OEP代码处:


可以发现,这里显示的仍是加密的代码。只需右键>Analysis>Analyse code即可:


可以看到OEP代码先调用GetModuleHandleA() API获取模块句柄,再调用DialogBoxParamA() API弹出对话框,该API含有5个参数,其中第四个参数DlgProc用来指出Dialog Box Procedure的地址(004010F5)。

进入该Dialog Box Procedure地址查看:


可以看到,上面的一个大框框住的即为需要修改的字符串保存的位置。下面两个框的指令即调用该地址处的字符串。该字符串区域时经过两次加密处理的,并且对该区域计算校验值以坚持字符串是否被修改。

这里小结一下,EP代码只是用来调用解码代码的,接着通过XOR指令先后对00401007~00401085、004010F5~00401248、00401007~00401085区域进行解密(即00401007~00401085区域进行了两次解密操作),再运行004010F5~00401248区域的代码求得两次解密区域的校验值,检测该区域是否被修改过。接着对0040124A区域进行解码,最后跳转到OEP处执行代码。


内嵌补丁打补丁

由上述分析可知,实际要打补丁的字符串都保存在经过两次加密的区域。

整个的操作过程为:先在文件适合的位置插入洞穴代码,该补丁代码更改字符串并通过JMP指令跳转至OEP处,再在004010F5~00401248区域将JMP OEP指令修改为JMP补丁代码。

当补丁代码较少时,使用第一种方法即在文件空白的区域插入补丁代码。

使用PEView查看文件的第一节区的节区头:


可以看到,第一节区的在磁盘文件的大小为400,映射到内存的大小为280,即第一节区从磁盘文件加载到内存后有大小为180(RAW 680~800区域 > RVA 1280~1400区域)的区域并未使用,该区域为空白区域(NULL-Padding),可以用来填写补丁代码。另外需要注意的是,1E4的属性值中添加的IMAGE_SCN_MEM_WRITE(可写属性),在程序进行解码操作时,必须在该节区头添加可写属性以获得相应内存的可写权限,否则会引发非法访问的异常。对于一个普通的PE文件,其代码节是无写权限的,但是压缩工具、Crypter等文件的代码节都有可写权限。

注意,内存中的节区大小为280,但实际节区的内存大小并非280,而是以Section Alignment的整数倍扩展,即实际大小为1000。

使用Win Hex打开该空白区域查看,可以看到该区域全部用0填充,称为Null-padding区域:



下面开始制作补丁代码。

运行至OEP处,由RVA 1280~1400区域得VA 401280~401400区域,查找该区域:


可以看到,Null-Padding区域从401280地址开始。

接着只需要在该地址开始编写补丁代码即可:


上述两条REP MOVS指令用于修改字符串,将之前的两个字符串分别修改为“SKI12”和“Unpacked”。接着JMP指令跳转至OEP处。后面的两行地址是保存用于替换的字符串。

最后,修改00401083地址处的JMP OEP指令为JMP洞穴代码指令即可,需要注意的是,该区域是原来的加密区域,是使用XOR 7进行加密的区域,现在看到的是解码后的代码:


而文件中实际的加密形态为(VA 00401083 > RVA 1083 > RAW 483):


EE XOR 7 = E9,91 XOR 7 = 96,06 XOR 7 = 01,则“EE 91 06”经过XOR 7解密之后为“E9 96 01”。

XOR运算的特点:如果对一个值连续做两次XOR运算,会返回这个值本身。

因而,可以对洞穴代码地址进行一次XOR 7运算即为加密后的代码地址:

“E9 F8 01”经过XOR 7解密之后为“EE FF 06”,将00401083地址处的Hex值进行修改即可:


保存为新的文件。

当然也可以直接使用Win Hex打开来更方便地修改:


最后运行查看:



可见,通过内嵌补丁的技术成功修改了加密文件中的字符串。

猜你喜欢

转载自blog.csdn.net/ski_12/article/details/80670045