查看原文
其他

分析强壳的虚拟机原理

Buu 看雪学院 2019-09-18

本人是逆向界的一枚小学生,喜欢唱,跳,搜字符串,以及回收站,这个分析是好早前分析的,如有不对的地方,还请各位大佬海涵。



     

因为一直对强壳里面所谓的虚拟机感兴趣,但是反调试,反虚拟机等很烦人,所以当时找了个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折 优惠!





热门文章阅读

1、浅析:网友说的过火绒的远控到底是何方神圣?

2、SandHook 之 Native Inline Hook

3、技术分析 | 为什么一张图片就能苹果手机重启



公众号ID:ikanxue

官方微博:看雪安全

商务合作:wsc@kanxue.com


↙点击下方“阅读原文”,查看更多干货

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存