VMProtect 3.31的OEP之旅
最近试着分析找了一下VMP3.31保护下的oep,以前从没分析过,第一次分析还是挺有意思的,打算和大家说说我找oep的旅途,错误纰漏处还请指正包涵。
准备工作
我找oep前还是先做好准备工作,把ep第一行nop掉(避免首字节int3检测),ep第二行改成 jmp 0x45e53e,使程序只跑在ep这一段代码。
这样做有2个好处:
1. 尽量减少原程序的干扰,例如API的调用。
2. 跑起来后,直接下断停住,观察寄存器和和堆栈有什么“特点”,同时还可以与初始环境对比。
确定OEP的出现
一开始我的想法还是很“正经”,想试试内存断点快速找到OEP。一试,要么被抓到(反调试),要么程序没停住。
根据第二种情况,我又试图下断VirtualProtect,看看有没有改写text区段的属性。结果断是断下了很多次,但是并没有看见对text区段的操作,这时我猜测可能模拟wow64直接走syscall了,但这个应付起来有点麻烦,暂且不管,先试试别的方法。现在,出现在我脑袋里的是第二个方法:对比环境,左图为初始环境,右图为oep环境。
先扫一遍寄存器,都跟OEP无关。再看ebp和esp,发现没有改变,这是个好事。再看堆栈,esp-4处出现了OEP。我的第一反应是,是push oep,然后retn过来的吗?
为了证实这个猜测,我把VMP的反调试选项关闭(解除硬件断点检测的干扰),重新加壳,直接对esp-4处下硬件访问断点。
一路run,run到了oep。同时多次重新加壳,重复这个操作,都没问题。至此,oep已经能锁定。
找暂停点1
找暂停点,不仅仅是为了下硬件断点,更重要的是尽可能接近OEP,避开壳的各种检测。由于之前VirtualProtect成功停下过,先试试这个。
下断,run9次后跑飞。那就在run8次后停下,下硬件断点,run,看看会不会检测到。
很幸运,此时已经躲过硬件断点检测了,成功到达oep。同时,我又想到一个念头。既然躲过了硬件断点检测,那内存断点检测是否也不在了。
对此,我在text下内存执行断点,run、run、run。结果一直被停在text区段各个retn上面(应该是vmp为了对付内存断点的手段),run到后面还被反调试查出来了。这个方法宣告死亡。
找暂停点2
之前第一个方法用VirtualProtect。感觉能找到API,运气成分很大。换别的壳可能就找不到这种API了。遂想试试第二种方法,稳妥点找到一个API。要尽可能接近OEP(避开各种检测),那就用工具看看VMP最后调用了哪些API。
虽然vmp从3.1开始有wow64模拟,但是现在看来,wow64模拟也没有模拟全部API。
找API时尽量按以下要求找:
1. 从后往前找,越后面调用的API越好;
2. 尽量不选被检测的API;
3. 下断API后,run到跑飞的次数要尽可能是固定的(需要多次测试);
4. 需要run的次数越少越好(但检测必须已run过去)。
对比,我一直从下往上找,找到了一个适用的。
但是这个API有点“不完美”,或者说太底层了。每次重新加载程序,都会在这里停10次左右。
对此,我停住后往下单步走,跟到了壳调用的API。
经过多次测试,这个API就很不错,适合做暂停点。不管是默认加壳,还是全保护加壳,都只需要断2次(VirtualProtect会因此需要改变中断次数),便可以下硬件断点。至此,默认OEP保护方面就没什么好闲谈的了。
VM OEP保护
由于se没有vmoep,顶多是偷掉第一个字节,所以vmoep的保护也是我第一次研究。内心难免充满了许多疑问:
“vmp把oepvm了,仅仅只是防止别人看出OEP或者直接搜特征码吗?”
“都说vm和壳是两回事,是不是意味着跑到vm后的oep直接dump就可以了,vm的oep和壳没关系?”
“如果vm后的oep和壳有“联系”,会是怎么联系的?”
带着这些疑问,我用之前的找oep方法来尝试vmoep后的程序。结果,在下硬件断点后一直run,并没有run到oep,而是直接跑起来了,而且也没有停在我对OEP附近设置的断点上。这时,我心里基本就有答案了。vm后的oep和壳“联系”在一起了。
此时,vm模拟着45e53e处的jmp,在不停的跑(尽管他不知道自己永远也跑不到尽头,哈哈)。
无意间,我随手暂停住程序,就发现了希望。EIP是在vmp0区段,而不是vmp1区段。有了这个“跨区段”的差别,我就可以试着用内存断点停在vm后的oep。那么我接下来的操作还是用“对比法”,并且有了以下流程:
1. VM OEP保护:
下断 CreateToolhelp32Snapshot,run2次,对vmp0下内存执行断点,并且run。停在vmp0区段。
2. 默认OEP保护:
下断 CreateToolhelp32Snapshot,run2次,对vmp0下内存执行断点,并且run。也停在vmp0区段。
两种情况下都停在了vmp0区段,这个地点应该不是oep。还需要更接近oep才行,为此我又对2个程序下之前的硬件断点,run一波,然后停在硬件断点处,再对vmp0下内存执行断点,继续run下去。
这时出现分叉了,默认保护下跑飞(没有再跑到vmp0),VM OEP保护下,又一次跑到了vmp0。
再看看出现在我眼前的代码,我以为此时应该是push xxxxx,call xxxxx这种进入虚拟机的代码。但却是这种代码。
看见这种代码,我内心是有点虚的。这意味着要么我找错了,要么此时的OEP有什么东西和壳“联系”起来了(最大可能就是opcode)。
为了判断找对地方没,我选择将OEP的保护由“虚拟”改成“变异”重新加壳,重新按上述操作。再看看此时的代码样子。
OK,错不了了,这里就是VMP后的OEP。
再来看看去除混淆后的这条hander。这条hander每次加壳都会变,但是换汤不换药。
mov eax,dword ptr ds:[edi] // edi为vm_esp,此时存放着opcode地址
add edi,0x4
mov esi,eax
mov ebx,esi
lea ebp,dword ptr ds:[0x78C2E9]
sub esi,0x4
mov edx,dword ptr ds:[esi]
xor edx,ebx
bswap edx
dec edx
bswap edx
add edx,0x2B2E1207
xor ebx,edx
add ebp,edx
push ebp
ret
这条hander大致就是起到“连接”的作用。此时直接dump程序,写一个hook把esp或者ebp赋给edi,再对[edi]写入opcode地址,vm oep方面就解决了。
想测试能不能成功运行,需要先把VMP的“导入保护信息”和“资源保护”选项关闭再重新加壳。此时在CreateToolhelp32Snapshot也只需要中断1次。
到此为止,"OEP之旅"就结束了。
- End -
看雪ID:Lixinist
https://bbs.pediy.com/user-758886.htm
本文由看雪论坛 Lixinist 原创
转载请注明来自看雪社区
戳
热门文章阅读
1、借助gdb调试glibc代码学习House of Orange
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com
↙点击下方“阅读原文”,查看更多干货