记录一次vmp2.xdemo的分析
2022-2-8 17:59:0 Author: mp.weixin.qq.com(查看原文) 阅读量:12 收藏


本文为看雪论坛优秀文章

看雪论坛作者ID:ookkaa

我用的这个版本应该是2.0几吧,据我所知2.x都一样,虚拟机特征很明显,就是一个大循环,不断的取opcode,下面可以看得到,特别明显,而且定位到这个取指令到handler的过程也很简单。3.x和2.x的差距太大了,3.x大大增加了静态分析的难度。
 
我这个demo还是采用普通的局部混淆方式。
 
只要是个vmp2.x的都有mov rdx,[r12+rax*8]这个取handler的指令。
这个其实稍微研究一下就能定位这个指令了。
 
而且比较奇怪,我这里的vRip是从后往前取的,别人的都是从前往后。
 
vmp2.x进入的时候还是照常push 寄存器的,因为这里的寄存器是为了执行我们的代码做准备的。
 
开辟堆栈后,直到开始分配handler,堆栈大致是这么一回事。
 
 
这些文字都是前几天的思考过程,大家随便看看。在0x14FE18上面都是vm_context,至于这个名字是别人取的,这里的作用就是虚拟机运算中转,就像x86寄存器一样,计算总得有个放的地方,0x14FE18下面叫vm_stack,也是别人取得名字,作用就是存放真实物理寄存器。
 
因为据我的分析,
 
 
这是其中一个handler,rbp是虚拟机得栈顶,很多地方都看得出来,这是其中一个,执行到这个handler的时候就指向0x14fe18,从虚拟机栈依次还原物理寄存器,这里标志着此次VM退出。因为我们是用VMProtectBegin标记保护的代码块,VM退出之后就会执行我们保护的代码块,因为我们的代码块是肯定对真实x86寄存器操作的,他不退出虚拟机在vm-context中白忙活也没用啊。
 
后面单步分析是不太现实的,借用一下uniorn。
vPop vR6vPush imm32vAdd immvPop vR16vPop vR3vPop vR14vPop vR13vPop vR10vPop vR5vPop vR21vPop vR9vPop vR15vPop vR23vPop vR7vPop vR2vPop vR18vPop vR11vPop vR0vPop vR12vPop vR20vPop vR17vPop vR4vPop vR8vPop vR22vPush imm64vPush vR6vAdd immvPop vR19vPop vR22vPush imm64vPush vR6vAdd immvPop vR16vPush vR6vPush imm64vAdd immvPop vR16vPush vR4vPush vR17vPush vR20vPush vR12vPush vR0vPush vR11vPush vR18vPush vR2vPush vR7vPush vR23vPush vR15vPush vR22vPush vR21vPush vR5vPush vR10vPush vR13vPush vR14vPush vR6vPush vR5pop all virtual regs to physical regs(vm exited) Physical RIP 1400010b0Physical RCX 140003638vPop vR15vPush imm32vAdd immvPop vR2vPop vR14vPop vR8vPop vR16vPop vR6vPop vR4vPop vR2vPop vR23vPop vR11vPop vR20vPop vR3vPop vR21vPop vR7vPop vR5vPop vR13vPop vR17vPop vR9vPop vR0vPop vR12vPop vR1vPop vR1vPush imm64vPush vR15vAdd immvPop vR10vPush vR12vPush vR0vPush vR9vPush vR17vPush vR13vPush vR5vPush vR7vPush vR21vPush vR3vPush vR20vPush vR11vPush vR23vPush vR2vPush vR4vPush vR6vPush vR16vPush vR8vPush vR19vPush vR23pop all virtual regs to physical regs(vm exited) Physical RIP 14000108bPhysical RCX 1400036382ae6 : call  qword ptr [rip + 0x1164]Failed on uc_emu_start() with error returned 8: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)

这是从vmp开始保护开始仿真到调用外部api出现UC_ERR_FETCH_UNMAPPED的错误的流程。
 
vPop、vR几是啥意思大家应该是知道的,顺便再说说。
 
vPop就是其中一个handler,handler大致做哪些事在上面的图片中。
rax是寄存器索引,这个索引是根据opcode解密算出来的,解密算法handler里就有。
 

 
然后我们首先得知道我们vmprotectbegin保护的代码从哪里开始吧?不然等于是分析虚拟机了。
 
上面控制台打印了2个Rip,这两个rip就是虚拟机退出,该执行我们保护的代码块的rip,咋获得的:
因为我们已经有了opcode,可以照着opcode,找到所有dispatch handler,
这里又说到dispatch handler,

这里的r12存着handler table。
 
不同2.x版本只是把这个table本身地址加加密或者啥的,或者把这个handler table里面的地址再加加密。
 
比如下面这个用bswap解密,好像是最大保护吧,忘了。
 
用ror解密,
然后回到rip的问题,我们在ida跟踪这两个rip看看0x1400010b0,0x14000108b
 
其实就我们源码中加密的代码,如果代码再多点,保护再变强,vmp在这个地方也会变异,比如把逻辑运算改一改,用复杂指令替换,但是至少我们得分出哪些是用户写的代码,哪些是虚拟机本身的代码。不然在那搞了半天,还原虚拟机的代码干什么。不过要想把流程还原出原本本身x86的流程,就要研究虚拟机了。
 
因为我们写的代码足够简单,所以直接根据原本的代码还原成x86代码就可以。
 
在140001073处,vmp要进自己的虚拟机,替我们执行保护的代码,我们直接不进虚拟机,自己执行自己的代码。
 
 
不过还原是很麻烦的,这么点代码有几个寄存器不对就崩溃。

上面那些mov寄存器的操作,是需要执行在vm退出的时候,把寄存器提取出来给他恢复上。

上图是还原的。下图是原本程序的流程图:
 
 
因为我不想在研究的时候预先知道原本程序的样子,所以这个原demo是最后比较用才放进IDA的。
 
以上思路搞搞少代码或者crackme还是可以的,真正的实战太麻烦了(反调试 虚拟机检测 内存保护 IAT保护 重定位 代码膨胀太大 handler数量过多 等等等等等等等等等)。
 
因为是一次性写的帖子,有说不清楚的地方很正常,
https://github.com/aobfucated/Vmp2.xDemo-Antiobfucated
所有我用到的东西或者思考过程都在上面了。

 

看雪ID:ookkaa

https://bbs.pediy.com/user-home-865434.htm

*本文由看雪论坛 ookkaa  原创,转载请注明来自看雪社区

# 往期推荐

1.详解七句汇编获取Kernel32模块地址

2.保护模式学习笔记之分页机制

3.非华为电脑安装华为电脑管家分析

4.某DEX_VMP安全分析与还原

5.怎样制作一个防止重打包的APK【反脱壳反HOOK】

6.CVE-2019-9081 Laravel5.7 反序列化 RCE复现

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458428142&idx=1&sn=28df738183d948cd2270f63c3f874dde&chksm=b18f926486f81b72d4cc7862f2304b3719a07a603055adddd9e482d78bed72cb2900b3c00eae#rd
如有侵权请联系:admin#unsafe.sh