分析强壳的虚拟机原理
本人是逆向界的一枚小学生,喜欢唱,跳,搜字符串,以及回收站,这个分析是好早前分析的,如有不对的地方,还请各位大佬海涵。
因为一直对强壳里面所谓的虚拟机感兴趣,但是反调试,反虚拟机等很烦人,所以当时找了个demo,只有虚拟机,没有其他乱七八糟的东西,当然这个虚拟机可能跟其他强壳比起来弱爆了,但是原理大致相通,也有可能现在的虚拟机都不这样玩了,但是作为学习来说还是能学到比较多的思路的,当时对壳还是比较感兴趣的,但是这个分析完以后,emmmmmmm
废话不多说,开搞。
我们先运行程序,可以看到就是输出两段字符串,程序很简单,第二个输出字符串的函数是被VM了(虚拟机执行的函数我暂时称为被VM的代码),所以我们对第二个函数分析就行了。
因为是控制台程序,所以我们直接定位到main函数,可以看到,框出的地方就是VM的代码了。
还犹豫什么,F7进去盘他。
这里进去看到压了一个参数,我们内存窗口看看。
我们发现压入的是字符串,好了,今天的分析就到这了,我们下次再见,我还要去拯救苇名。
好了,我们看下到底压了个什么鬼进去了。
好家伙,这都是什么鬼,没字符串我还逆个屁啊。
我们继续走,可以看到这里就是主要代码了,可以看到这里首先保存了一堆寄存器,紧接着两次开辟栈空间,我们上面说了,既然是虚拟机,那么寄存器环境和栈环境什么的,都要模拟出来,这里第一次抬0x200的大小是构造一个栈空间,第二次抬0x40是构造寄存器环境。
往下走,可以看到给esi赋值了,就是刚才我们不知道什么鬼的那些东西,我们暂且把这些称为中间码,毕竟他也要面子的嘛。
这里我画一个图,助于理解,第一次抬了0x200后把栈顶给了edi,那么说明虚拟机的栈是用edi来访问的,寄存器环境是用esp来访问的。
继续走,我们看到拿出了第一个中间码,然后跳了。
可以看到这里是在取开始压入的寄存器的值,然后给虚拟机的寄存器环境赋值。
那么我们先记录下EDI:VMContext栈底寄存器环境。
+0 EFLAGS
+4 EDX
+10 ESP
+18 ESI
+1C EBX
+20 EAX
+24 EDI
+28 ECX
+2C EBP
中间码,F6,初始化寄存器环境。
执行完以后这里在判断下栈够不够,够就继续循环取中间码,如果不够就在扩充,因为有可能执行被VM后的代码导致开始开辟0x200的栈不够,所以需要判断,如果不够就在开,然后把寄存器环境在从新拷贝。
然后第二次可以看到把中间码3D拿出来,然后esi+1,然后跳过去。
跳过来以后拿出了中间码0x2C给EAX。
然后用EDI+2C,根据上面分析的可以看到EDI+2C是原本的EBP,然后将原本的EBP压入栈顶。
然后在跳回来,取出中间码0x59,继续在跳走执行
然后取出刚刚压进去原来的EBP给EAX,然后把原来程序用的EBP-4,然后把eax MOV进去,然后就完成了push ebp的操作。
好家伙,折腾这么大一圈就完成了一个push ebp。
我们都知道,函数进来得保存栈的环境,给当前函数开辟新的栈空间和栈底等,那么push ebp完了以后就该是 mov ebp,esp了,我们接着看这虚拟机的骚操作。
然后在回来,这次拿出0x65。
然后我们跳过来,在取中间码2C给EAX,然后把刚开始压入栈顶的EBP弹到虚拟机用的寄存器环境里的EBP。
还是画个图,不然容易乱。
然后在回来,拿出9A。
继续JMP过来,可以看到把栈顶又抬了0x4的大小。
然后在回来,这次拿出中间码0x27。
然后跳过来后把原来程序用的EBP给虚拟机的寄存器环境里+0X10的位置,因为刚才push了,然后EBP-4了,也就是说现在EBP是原来程序用的栈的栈顶, 然后这块虚拟机用的寄存器环境的栈顶也得移动栈针,也得-4,所以把他原来的拿过来赋值。
然后在跳回去,在拿出3D。
跳过来拿出0x10给EAX,然后把EDI+10在压入栈顶,EDI+10就是上一步赋值过来的,这里看的迷糊的看最开始的分析,EDI+0X10就是真实程序用的ESP。
继续跳回去,还是拿出3D,3D是压寄存器操作。
继续执行,拿出2C,还是上面的操作,把EBP压入栈顶。
然后拿出0xB5。
JMP过去,因为刚才把EBP和ESP都压人栈顶了,这个时候访问栈顶,把ESP给EBP,完成mov EBP,ESP的操作,然后也把标志寄存器更新了一下。
好了,这两步现在执行完了,我为啥要去分析这种东西......
然后在跳回去,拿出0x65。
JMP过来,拿出中间码0x65,根据上面的分析,0x65就是把当前栈顶的值更新到虚拟寄存器环境中。
这个时候在跳过来,拿出0x2C,然后把栈顶的值弹给EDI+EAX , EDI+EAX就是虚拟寄存器里的EBP。
然后在跳回来,拿出9A,根据上面分析的,9A是抬栈。
跳过来可以看到是抬栈,因为栈顶的值已经没用了。
然后在回来,拿出中间码0x27。
根据上面分析,0x27是把程序用的EBP给虚拟的ESP。
在跳回去,拿出操作数0x9D,开始压栈。
跳过来以后,可以看到在拿出操作数,压入当前的栈顶。
得来个图了。
然后在跳回去,拿出操作数CD。
然后跳过来,从栈顶拿出刚压的,然后程序用的EBP抬4个字节,然后把字符串压进去,这个时候才完成了把一个字符串地址压入程序用的栈的操作。
心累。。。
在跳回去,拿出操作数0x9A,因为刚才压到栈顶的字符串没用了,要弹走。
在回来(执行完抬栈了),然后在回来看到又是一个抬栈,因为上面mov ebp,esp的时候,抬的时候只抬了一个,这个时候再把另外一个抬走。
然后再回来,拿出操作数0x27,更新虚拟寄存器环境里的esp。
这个时候在压栈。
我们跟过来看下,可以看到压入printf的地址了。
跳回来看到又是压栈。
跳过来给可以看到给栈顶压了一个1。
然后在跳回来,拿出操作数0x9D,继续压。
调过来看到又压入0xFFFFFFFF。
跳回来又看到压栈。
这里又压了一个0xFFFFFFFF。
跳回来这次拿到0x31。
JMP过来,可以看到ADD esp,0x10,把栈顶刚压入的4个参数弹走,然后从新把printf的地址压进去,现在栈顶只有一个printf的地址。
然后回来,拿出中间码0xC0。
跳过来,然后把printf的地址给eax,然后在拿出中间码给edx。
然后我们可以分析出每个中间码的作用分别对应的是什么了,这里我没记完,实在太恶心了,没心情记了。
EDI:VMContext栈底 寄存器环境:
+0 EFLAGS
+4 EDX
+10 ESP
+18 ESI
+1C EBX
+20 EAX
+24 EDI
+28 ECX
+2C EBP
字节码:
9A 抬栈
9D 压栈
3D 压寄存器
65 更新虚拟寄存器环境
27 程序用的EBP给虚拟寄存器环境里的ESP.
cd 给程序用的栈压栈
如果我们把大多数的中间码都翻译了,那么这个虚拟机也就废了,因为我们可以在跳转那看中间码就行了,但是这也是可以防的,这种对抗说下去就说不完了,但是他并不能战胜我,因为我打算不和他玩了。
至此,这个函数就分析完了,我想说发明这种加固方法的,我40米的大刀可以让你先跑39米。
文章中也许有错误或者错别字的地方,各位大佬凑活看吧。我算是又温习了一下这个,又被恶心了一遍,有些地方真实环境和虚拟环境可能没说清楚,但是我觉得认真看了自己也能分出来,我实在不想看了,就饶了我吧。
能用大佬的插件就用插件吧,这种东西还是别头铁了。。。。。最后祝各位大佬身体健康,万事如意。
- End -
看雪ID:Buu
https://bbs.pediy.com/user-756791.htm
本文由看雪论坛 Buu 原创
转载请注明来自看雪社区
戳
⚠️ 注意
2019 看雪安全开发者峰会门票正在热售中!
长按识别下方二维码,即可享受 2.5折 优惠!
热门文章阅读
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com
↙点击下方“阅读原文”,查看更多干货