本文为看雪论坛优秀文章
看雪论坛作者ID:ookkaa
我用的这个版本应该是2.0几吧,据我所知2.x都一样,虚拟机特征很明显,就是一个大循环,不断的取opcode,下面可以看得到,特别明显,而且定位到这个取指令到handler的过程也很简单。3.x和2.x的差距太大了,3.x大大增加了静态分析的难度。只要是个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中白忙活也没用啊。vPop vR6
vPush imm32
vAdd imm
vPop vR16
vPop vR3
vPop vR14
vPop vR13
vPop vR10
vPop vR5
vPop vR21
vPop vR9
vPop vR15
vPop vR23
vPop vR7
vPop vR2
vPop vR18
vPop vR11
vPop vR0
vPop vR12
vPop vR20
vPop vR17
vPop vR4
vPop vR8
vPop vR22
vPush imm64
vPush vR6
vAdd imm
vPop vR19
vPop vR22
vPush imm64
vPush vR6
vAdd imm
vPop vR16
vPush vR6
vPush imm64
vAdd imm
vPop vR16
vPush vR4
vPush vR17
vPush vR20
vPush vR12
vPush vR0
vPush vR11
vPush vR18
vPush vR2
vPush vR7
vPush vR23
vPush vR15
vPush vR22
vPush vR21
vPush vR5
vPush vR10
vPush vR13
vPush vR14
vPush vR6
vPush vR5
pop all virtual regs to physical regs(vm exited) Physical RIP 1400010b0
Physical RCX 140003638
vPop vR15
vPush imm32
vAdd imm
vPop vR2
vPop vR14
vPop vR8
vPop vR16
vPop vR6
vPop vR4
vPop vR2
vPop vR23
vPop vR11
vPop vR20
vPop vR3
vPop vR21
vPop vR7
vPop vR5
vPop vR13
vPop vR17
vPop vR9
vPop vR0
vPop vR12
vPop vR1
vPop vR1
vPush imm64
vPush vR15
vAdd imm
vPop vR10
vPush vR12
vPush vR0
vPush vR9
vPush vR17
vPush vR13
vPush vR5
vPush vR7
vPush vR21
vPush vR3
vPush vR20
vPush vR11
vPush vR23
vPush vR2
vPush vR4
vPush vR6
vPush vR16
vPush vR8
vPush vR19
vPush vR23
pop all virtual regs to physical regs(vm exited) Physical RIP 14000108b
Physical RCX 140003638
2ae6 : 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 原创,转载请注明来自看雪社区
文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458428142&idx=1&sn=28df738183d948cd2270f63c3f874dde&chksm=b18f926486f81b72d4cc7862f2304b3719a07a603055adddd9e482d78bed72cb2900b3c00eae#rd
如有侵权请联系:admin#unsafe.sh