首页
社区
课程
招聘
[原创]vmprotect某版本浅析
发表于: 2019-4-23 14:40 26304

[原创]vmprotect某版本浅析

2019-4-23 14:40
26304

先说下仅仅是一部分,从零开始分析一个vmprotect虚拟机。

Step1:偶然入手一个vmprotect的样本,我这里版本是vmprotect3.0.8,拖入ida,查看区段

      

啥也没看出来,估计真正入口在text段吧,暂时不管他。

Step2:尝试直接去入口分析


一看这种代码就很坑,慢慢来吧

Step3:尝试用ida画图看下,乱七八糟的


Step4:这么乱的流程图不是我们想要的,所以我们可以尝试写个程序清理出一块代码,基本来说,区分块就是用jmp edi和ret来截断了指令。看起来也很乱


Step5:由于并不清楚这个虚拟机的结构,于是尝试清理代码,去掉一些无用的花指令,先给第一个块去花。过程如下:


前面两个jmp是无效的,nop之


这种衔接cmp,无用,nop之


Step6:没意义的cmp与test,nop之……总是这些规律不难,需要注意的是xchg指令需要花点心思。直接给大家看下清理花指令以后的吧。


开头部分是一堆的push,可能是保存寄存器环境,最后的mov esi,[esp+28h]应该是取出一个key,继续往下分析

Step7:中间对esi和eax做了一系列的变化,


其中esi来源于一个key,eax也来源于一个const,然后一直变化

Step8:最后ret回去,来源于edi,而edi来源于esi和eax


Step9:根据前面的信息,我们大概就理清了一点思路,首先一堆push保存寄存器;然后ebp=esp以后esp就随意变换了。。尽管他也没使用过esp,也就是说,这时候的ebp相当于esp,就是个堆栈指针;最后根据2个key变换esi和eax,eax和esi仅仅用来计算edi,edi用来计算ret的地址。

Sep10:

Ebp:堆栈指针

Esi:数据源

Eax:临时变量

Edi:ret专用返回(跳转)地址

Step11:根据上面的结果,我们只是得到了一个大概,还需要继续分析block2(实际上,所有的block已经获取到),才能分清楚他的机构,以便后期的继续自动化。

Step12:看block2,也就是第二个块,仍旧有很多花指令


Step13:继续尝试根据上面的规律去掉这些花指令,去掉以后如下:


很显然,第二个block也是符合上面的规律的

ebp作为一个指针a,esp+eax作为一个指针b,eax作为一个临时变量可以不用管,edi作为衔接作用也暂时不用管,esi作为一个指向const区域的指针也可以不用管,这些相当于虚拟机的内部,我们都可以屏蔽,我们要做的,就是搞清楚这个block哪些部分是重要的。

Step14:继续观察代码,发现真正有用的代码,只有下图(之所以说其他没用,因为其他代码里面的eax,esi寄存器,都是用来计算下一个代码块的,这些代码对于我们分析虚拟机没什么作用)


Step15:没错,真正有效代码,就是上面这四句,其实就是做了一个从ebp到[esp+eax]位置的变量转移,也可以说说一个 x86寄存器=》vmp虚拟机寄存器的mov

。然后根据之前的分析结果,ebp是堆栈的话,那么这里的ebp+4相当于一个vmp的pop指令,esi作为指令指针继续指向下一条const,这句代码,可以简称为一个

“vmp_push reg”,之所以是push,是因为ebp+4了,而这里变动的堆栈,则是vmp的esp(听起来很绕),同时看出来,他的指令是倒着走的,vmp的eip会减1 ,至于这个reg对应哪个寄存器,因为我们这里仅仅是纯静态,所以很难确定,但是这里可以暂时猜测这一块代码,仅仅是做了一个操作,那就是:把x86的寄存器放入到vmp的区域,如果以后写一个伪指令调试器,则可以直接模拟一个push即可,其他的esi寄存器、ebp寄存器,eax寄存器,忘掉他吧。或者,直接写伪调试器的时候,不去写这几句,因为他们仅仅是在初始化vmp的堆栈,给他们一个初始值。

Step16:对,你没看错,block1是保存了所有的x86寄存器,block2是开始初始化vmp的第一个寄存器,至于block3我们可以大胆猜测,是初始化vmp的第二个寄存器。而这些,如果在做还原的时候,其实是最好忽略掉的,因为来回的堆栈改变会影响我们的分析(这句现在说的很啰嗦,但是后面就知道,这句话会很重要)。

Step17:现在可以先停下来,重新梳理一下思路,已知他会先把所有寄存器保存下来,然后用新的ptr指针代替原操作,那么我们需要做的是,搞清楚他哪些ptr对应哪些寄存器,做了哪些操作。到这里,需要我们获取所有的block了,正好把他的反调试处理一下。

Step1:偶然入手一个vmprotect的样本,我这里版本是vmprotect3.0.8,拖入ida,查看区段

      

啥也没看出来,估计真正入口在text段吧,暂时不管他。

Step2:尝试直接去入口分析


一看这种代码就很坑,慢慢来吧

Step3:尝试用ida画图看下,乱七八糟的


Step4:这么乱的流程图不是我们想要的,所以我们可以尝试写个程序清理出一块代码,基本来说,区分块就是用jmp edi和ret来截断了指令。看起来也很乱


Step5:由于并不清楚这个虚拟机的结构,于是尝试清理代码,去掉一些无用的花指令,先给第一个块去花。过程如下:


前面两个jmp是无效的,nop之


这种衔接cmp,无用,nop之


Step6:没意义的cmp与test,nop之……总是这些规律不难,需要注意的是xchg指令需要花点心思。直接给大家看下清理花指令以后的吧。


开头部分是一堆的push,可能是保存寄存器环境,最后的mov esi,[esp+28h]应该是取出一个key,继续往下分析

Step7:中间对esi和eax做了一系列的变化,


其中esi来源于一个key,eax也来源于一个const,然后一直变化

Step8:最后ret回去,来源于edi,而edi来源于esi和eax


Step9:根据前面的信息,我们大概就理清了一点思路,首先一堆push保存寄存器;然后ebp=esp以后esp就随意变换了。。尽管他也没使用过esp,也就是说,这时候的ebp相当于esp,就是个堆栈指针;最后根据2个key变换esi和eax,eax和esi仅仅用来计算edi,edi用来计算ret的地址。

Sep10:

Ebp:堆栈指针

Esi:数据源

Eax:临时变量

Edi:ret专用返回(跳转)地址

Step11:根据上面的结果,我们只是得到了一个大概,还需要继续分析block2(实际上,所有的block已经获取到),才能分清楚他的机构,以便后期的继续自动化。

Step12:看block2,也就是第二个块,仍旧有很多花指令


Step13:继续尝试根据上面的规律去掉这些花指令,去掉以后如下:


很显然,第二个block也是符合上面的规律的

ebp作为一个指针a,esp+eax作为一个指针b,eax作为一个临时变量可以不用管,edi作为衔接作用也暂时不用管,esi作为一个指向const区域的指针也可以不用管,这些相当于虚拟机的内部,我们都可以屏蔽,我们要做的,就是搞清楚这个block哪些部分是重要的。

Step14:继续观察代码,发现真正有用的代码,只有下图(之所以说其他没用,因为其他代码里面的eax,esi寄存器,都是用来计算下一个代码块的,这些代码对于我们分析虚拟机没什么作用)


Step15:没错,真正有效代码,就是上面这四句,其实就是做了一个从ebp到[esp+eax]位置的变量转移,也可以说说一个 x86寄存器=》vmp虚拟机寄存器的mov

。然后根据之前的分析结果,ebp是堆栈的话,那么这里的ebp+4相当于一个vmp的pop指令,esi作为指令指针继续指向下一条const,这句代码,可以简称为一个

“vmp_push reg”,之所以是push,是因为ebp+4了,而这里变动的堆栈,则是vmp的esp(听起来很绕),同时看出来,他的指令是倒着走的,vmp的eip会减1 ,至于这个reg对应哪个寄存器,因为我们这里仅仅是纯静态,所以很难确定,但是这里可以暂时猜测这一块代码,仅仅是做了一个操作,那就是:把x86的寄存器放入到vmp的区域,如果以后写一个伪指令调试器,则可以直接模拟一个push即可,其他的esi寄存器、ebp寄存器,eax寄存器,忘掉他吧。或者,直接写伪调试器的时候,不去写这几句,因为他们仅仅是在初始化vmp的堆栈,给他们一个初始值。

Step16:对,你没看错,block1是保存了所有的x86寄存器,block2是开始初始化vmp的第一个寄存器,至于block3我们可以大胆猜测,是初始化vmp的第二个寄存器。而这些,如果在做还原的时候,其实是最好忽略掉的,因为来回的堆栈改变会影响我们的分析(这句现在说的很啰嗦,但是后面就知道,这句话会很重要)。

Step17:现在可以先停下来,重新梳理一下思路,已知他会先把所有寄存器保存下来,然后用新的ptr指针代替原操作,那么我们需要做的是,搞清楚他哪些ptr对应哪些寄存器,做了哪些操作。到这里,需要我们获取所有的block了,正好把他的反调试处理一下。

Step18:刚才说到了反调试。现在就按照严格的顺序,按部就班的说一下反调试怎么处理(不要打开任何反反调试插件,要么就自己写,我们要的是纯手工)
    IsDebuggerPresent下断点,这将使第一个反调试点,可以利用它回溯一下堆栈,会看到一句 call eax,这里将是vmprptect调用反调试函数的入口;
    CheckRemoteDebuggerPresent,这也也很好处理;返回值改1
    ZwQueryInformationProcess,这时的[esp+4]是0x1e,处理的方法是,把返回值eax改成0xC0000353,把[esp+8]的buffer改成0;
    NtSetInformationThread 这个需要改他的传递参数,0x11改成别的;
    NtQuerySystemInformation 调用号为0x23,这里需要改他的返回OUT参数为0;
    NtQuerySystemInformation 调用号为0x0b,这里他会遍历系统模块,一些sys和dll等,这个处理。我么不能直接全部清0,需要和没调试状态的对比一份修改    ,可能有人说直接清零缓冲区就可以。但是在我的系统里面,这样依然会被检测到,也可能是我姿势不对吧,继续
Step19:好了,关于反调试本身现在暂时到此为止,我们要做的,并不满足于如何过掉他,而是看他如何检测和调用的,如果想要过掉他直接用个插件就可以了。回到正题,先以第一个IsDebuggerPresent为例;我们看看他是怎么调用过来的,这对于理解vmp虚拟机,是有好处的。那么根据之前总结的结果,我们知道,代码都是一块一块的,由0xc3和0xffe7截断,也就是一些ret和jmp edi,我们现在,可以获取目前为止的所有block了,先说下结果,经过ida trace,从入口到IsDebuggerPresent,一共调用了1022个block,我们要做的,就是把这1022个block,尽可能的读懂。好了,现在可以开始尝试读混淆过的虚拟机代码了。

Step20:这里可能会有人问如何获取这么多block,其实办法很多,run trace可以,但是最好还是静态解析,然后下断,再f9,这样中途可以截获一些指令,而且效率也比较高一些。好了,现在先贴一下这1022个block的块头

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-5-9 21:21 被白菜大哥编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (18)
雪    币: 2182
活跃值: (3236)
能力值: (RANK:260 )
在线值:
发帖
回帖
粉丝
2
没有章法,建议你从pe结构开始学起,理解一下系统是如何加载镜像,启动进程的,有些混淆你去认真看你就输了
2019-4-24 14:18
0
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
xiaohang 没有章法,建议你从pe结构开始学起,理解一下系统是如何加载镜像,启动进程的,有些混淆你去认真看你就输了
谢谢指点,这部分后面会有的。之所以搞混淆和vm,主要是不喜欢那种一个bp VirtualProtect然后怎么怎么就脱壳的教程。想用自己的方式,把其中的原理阐述清楚。让大家在看到有些高手,几个bp就脱壳之后,了解原理,而不是导致有些人不求甚解的一个“esp定律”就脱。
2019-4-29 16:48
0
雪    币: 7
活跃值: (285)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
不知道你的逆向水平如何(不知道是否有点班门弄斧了),也不知道我说得对错(欢迎指正)。
反正呢,我的理解来说,不管什么加壳,都不能脱离PE结构,如果你有想法,好好的学好逆向,建议好好的学习一下PE结构,再好好学一下编程。
网上有一份内存加载DLL的代码,值得你好好的研究一下。
一般人所使用的编程等级,基本上都不高,所以不管用什么加壳,最终都是对系统API的调用,他自己实现的API,目前这些强壳估计他也处理不了得。
另外,了解一下什么叫做堆栈平衡、回溯什么的。总结下来,基本上就是一下一些手法。
1、对PE头乱搞,让你的调试器检测玩玩,不过呢,最终还是得遵守PE文件规则,所以很好处理得。
2、对区段乱搞,对其中的结构进行加密、破坏处理等等,但是呢,最终都得还原,所以,bp VirtualProtect 就是那最后设置执行属性的一步。不然这个程序执行就会挂。
3、对输入输出表乱搞,其目的嘛,自然就是破坏原有的对系统调用过程,所以网上有些脱壳才会有补区段这种说法。最终结果还得遵守堆栈平衡原理。所以两头一卡,就露馅了。
4、其他的一些乱搞,什么文件校验、内存校验、反调试啊、双进程保护啊、等等,都是基于系统基本规则得,所以嘛,处理起来也会很简单。无非就是分析对方的手法然后对抗而已。

另外,说说破解,能够到这一步,前面的分析工作应该是处理得差不多了。也就知道该怎么修改代码了。
然后就可以了解一下什么异常处理啊、断点原理之类的,反正都是这么些东西。至于修改这些,无非就是那几个简单的函数调用,太简单了,我就不多说了。
2019-4-29 17:57
2
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
lijunlin 不知道你的逆向水平如何(不知道是否有点班门弄斧了),也不知道我说得对错(欢迎指正)。 反正呢,我的理解来说,不管什么加壳,都不能脱离PE结构,如果你有想法,好好的学好逆向,建议好好的学习一下PE结构 ...
谢谢,受益匪浅。如果是为了脱壳,确实是应该更注重系统调用和pe规范。退一万步讲,他所有的api都sysenter了,也还是可以ssdt hook搞。再不行挂个windbg或者softice,也能下断了。本文目的,仅仅是提供一个最笨的办法,高手可能很多不屑于看,但是如果新手认真看完,实验完(最后提供demo),那么vmprotect和upx,他都不会害怕,能可以按照自己的思路脱壳。
2019-4-29 18:20
1
雪    币: 7
活跃值: (285)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
白菜大哥 谢谢,受益匪浅。如果是为了脱壳,确实是应该更注重系统调用和pe规范。退一万步讲,他所有的api都sysenter了,也还是可以ssdt hook搞。再不行挂个windbg或者softice,也能下断了 ...
很赞成你的说法,网上现有的很多方法、都显得零散,建议可以做一套教程出来,从零突破VMP,然后让大家都可以学习,让技术传承下去。
2019-4-30 20:50
0
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
lijunlin 很赞成你的说法,网上现有的很多方法、都显得零散,建议可以做一套教程出来,从零突破VMP,然后让大家都可以学习,让技术传承下去。
限于个人水平不足。并没有按照传统方式,先把pe讲一遍,再几个bp 断点。而是按照自己遇到一个壳子的过程,一点一点的来。pe结构什么的,用到的时候会提及。
2019-5-1 00:02
0
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
xiaohang 没有章法,建议你从pe结构开始学起,理解一下系统是如何加载镜像,启动进程的,有些混淆你去认真看你就输了
你好,你看这样符合吗。这是正面刚vmp,从一开始啥也不懂,连vmp调用啥api也不懂。逐步分析 过反调试,最后贴了所有vmp308的api序列,最终用最直接的方式解释了,为什么脱壳vmp一定是对VirtualProtect下断而不是别的函数。当然还会更新,包括之后如何找oep修复iat
2019-5-7 17:36
0
雪    币: 2182
活跃值: (3236)
能力值: (RANK:260 )
在线值:
发帖
回帖
粉丝
9
并不是说你不能在别的函数下断,像三楼的所说的,任何事情都是有它的内在规律和规则的,别人在VirtualProtect下断,他们为什么要这么做?
理解规则能少走弯路,不然我怕你的热情没多久就会被失败磨灭了
2019-5-8 15:29
0
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
xiaohang 并不是说你不能在别的函数下断,像三楼的所说的,任何事情都是有它的内在规律和规则的,别人在VirtualProtect下断,他们为什么要这么做? 理解规则能少走弯路,不然我怕你的热情没多久就会被失败磨 ...
没有磨灭。现在找oep搞定了,没写上来,iat修复不难,正在写脚本。快结贴了
2019-5-8 15:59
1
雪    币: 2182
活跃值: (3236)
能力值: (RANK:260 )
在线值:
发帖
回帖
粉丝
11
没有就好,期待你的帖子
2019-5-9 10:09
0
雪    币: 47
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
这个帖子很好啊,  跟我几个月前硬刚vmp虚拟化的经历几乎完全一样了(我之前也是没有仔细分析过任何壳的)
我觉得没有搞过vmp虚拟化的同学照着这个流程走, 最后都能实现还原opcode的

不过就算还原了vcode, 想要还原成源代码也还是非常难的
2019-5-9 10:43
0
雪    币: 47
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
附上一个vmp3.0.x从启动开始的还原了vcode的runtrace
加密样本来源
上传的附件:
2019-5-9 10:56
0
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
frdGo 这个帖子很好啊, 跟我几个月前硬刚vmp虚拟化的经历几乎完全一样了(我之前也是没有仔细分析过任何壳的) 我觉得没有搞过vmp虚拟化的同学照着这个流程走, 最后都能实现还原opcode的 不过 ...
现在我自己可以手动修复iat了,但是写程序太麻烦。半天写不完这个程序,很烦。。主要code能力太差。其实剩下的很简单了。所有需要修复的地方,以及修复的方法都很明确的,比如0x00400010改0x10为0x20,很精确的,所有位置和改法都有了。。就是我写程序,总是bug。全是些编译错误。很尴尬。。
最后于 2019-5-9 11:31 被白菜大哥编辑 ,原因:
2019-5-9 11:28
0
雪    币: 12502
活跃值: (3058)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
frdGo 附上一个vmp3.0.x从启动开始的还原了vcode的runtrace加密样本来源[原创]追踪还原vmp3.0.9-VMProtectDecryptStringA
我没用run trace,这种会被他带入误区。我是动态中的静态分析。
2019-5-9 11:29
0
雪    币: 9251
活跃值: (1640)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
16
写得不错,回复的总结也不错…
2019-5-16 13:32
0
雪    币: 783
活跃值: (1121)
能力值: ( LV5,RANK:78 )
在线值:
发帖
回帖
粉丝
17
这么好的帖子我竟然现在才发现...罪过 马克一下.真的不错.分析的很透彻 深入浅出.适合我这种初学者.
2019-9-10 00:55
0
雪    币: 1705
活跃值: (676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
老铁们你们说的是不是windows的,不是移动的把?
2019-9-11 21:20
0
雪    币: 73
活跃值: (923)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
领悟到不少,有感觉,谢谢楼主
2020-4-1 17:01
0
游客
登录 | 注册 方可回帖
返回
//