内嵌补丁
需求
有时候我们直接修改OEP代码是不行的,因为该区域有可能被运行时压缩或者被加密过,直接改会引发程序异常,
这个时候我们可以换个思路,将写好的补丁放入内存中空的区域,然后再解密之后,先JMP到我们配置的区域,完成了我们的代码后再JMP到OEP处运行程序
运行时压缩流程
1.很多个解压缩循环(解码OEP)
2.配置IAT
3.JMP到OEP
加密代码执行流程
1.解密(解密OEP代码)
2.校验解密之后的和
3.如果校验和正确,则JMP到OEP,不正确,则程序异常
开始内嵌补丁之旅
先来看看程序的样子:
下面开始
解密循环
首先,打开实例程序
会看到很多汇编指令都是经过加密的,这里用search for all references strings也没用,字符串都是经过加密的
这里我们只能先进入004010E9,如下图:
接着进入0040109B
这里我们会看到给ECX赋值了154,然后给EBX地址处的值进行XOR 44运算,接着ECX减少,EXB增加,再比较ECX和0,然后是个条件跳转
这里我们可以知道,是将EBX地址处的值解密处理,每次处理一个字节,范围是EBX的最初值+154-1
看一下EBX的最初值:
004010F5,
那么这个解密的区域就是004010F5~00401248
接着往下调式,进入CALL 4010BD:
可以看到又是两个解密循环
同上一样得到这2个解密区域分别为:
401007~401085
4010F5~401248
到这里我们发现第三次的解密区域和第一次的相同,说明程序给4010F5~401248进行了两次解密,第一次解密是XOR 44,第二次解密是XOR 11
校验求和
接着往下看,遇到CALL 401039
进入:
调式到401046:
观察寄存器的值,发现EBX又是4010F5了,这不就是刚才解密2次的那个区域的起始地址吗,
接着看,EDX值为0
从401039到40104F区域的意思就是将EBX所在地址的值以每次4个字节的方式与EDX相加,最终求得4010F5~401248区域所有值的总和,保存在EDX
思考一下上面这个过程:
4010F5~401248区域就是解密2次的区域,将里面所有值求和放入EDX,为的就是校验和,查看解密区域代码是否被改动过
继续调式,来看一下EDX的最终值:
EDX=31EB8DB0,先记下这个值
继续调试,遇到CALL 40108A,进入:
这里可以看到,又是一个解码循环,区域为:
40124A~401280
再接着就是CMP EDX,31EB8DB0,这个就是比较EDX的值是否为31EB8DB0,接着4个PUSH然后接一个CALL,很容易想到是调用弹窗,这里调用弹窗,那么肯定是为了显示校验和正确或错误的信息,从右边字符串ERROR也可以猜到,那么显然,这里就是校验和错误会进入的地方,接着调用了一个401274,我猜是程序退出的API,
如果校验和正确,那么会执行JMP 40121E,这个地址就是我们要找的OEP
进入OEP
进入OEP,如下图:
(这里有个小技巧,如果进入OEP是加密状态,可以使用OD的Analysis code功能)
可以看到这个程序的实际主要功能就是一个弹出对话框API DialogBoxParamA(),
API DialogBoxParamA()的第四个参数是用来指出Dialog Box Procedure的地址的,也就是说这个地址就是该程序弹出对话框的地址(对话框里有我们想要修改的字符串)
查看4010F5:
我们需要修改的字符串都在这里
通过上图我们可以得出
“You must unpack me!!!”的地址在0040110A
“You must patch this NGA!!!”的地址在00401123
小结
通过上述分析,这里总结一下该程序结构:
401000~401006:EP代码
401007~401085:第一次解码区域(假设叫A区)
40108A~4010F4:解码区域
4010F5~401248:解码2次的区域(B)
40124A~401280:第四次解码区域(C)
OEP:40121E,位于B区域
EP代码的作用就是调用解码区域的代码
制作内嵌补丁
找到放补丁的地方
想要制作内嵌补丁,那么先要找到设置补丁代码的地方
常用三种方法:
1.找文件节区空白处(因为PE头基本是没有可利用的区域了)
2.扩展最后一个节区
3.新增节区
来看看第一种方法:
找文件节区的空白区域,
从上图第一个节区头中的信息可以看到,第一节区在内存中的大小是280,在文件中的大小是400,我们知道每个节区最后部分会有一片NULL区域的,那么400变280,很明显,文件中第一节区还有120大小的空间是空白的,我们来看看,如下图:
第一节区文件偏移400,映射到内存的有280,正好到680,后面的全为空白区域,也就是说我们可以将补丁放置此处
用OD打开,转到此处:
证实以上推断,从401280处开始后面为空白区域
编写补丁代码
注意点:
这里编写补丁代码的时候要先解码到这个地方,也就是说要先调式到OEP处
改变原来的JMP OEP
如上图,机器码E9 9601是解过密的
这里为什么只看到E9 9601 呢?
因为根据上面一系列分析,这部分的解密区域是属于:
401007~401085:第一次解码区域(假设叫A区)
第一次解密区域(A区)到401085就结束了,401083到401085就3个字节,所以E9 9601是解密的后面并未加密解密
注意:这里的E9 9601是经过A区解密之后的,也就是经过XOR 7解密之后的代码
回到原文件中:
可以看到解密之前也就是加密状态下的该位置,机器码是EE 9106
那么再来看看我们的JMP 401280对应的机器码是什么:
可以看到为:E9 F801
将其还原成加密状态,XOR 7,就成了:
EE FF06
所以,在文件中将其改写并保存,如图:
知识点:
为什么这里不在OD中直接改了保存,而要在文件中修改?
因为在映射中保存的是解密后的机器码,并不是加密状态的机器码,如果这样直接保存,会导致加密区解密之后的错误,所以直接在文件中将其改为加密的原状态