脱壳_01_虚拟机壳_VMP
写在前面的话:
上一篇文章中,带领大家一起分析了简单的压缩壳ASPACK,今天,就和大家一起来揭开VMP这道神秘的面纱;
【花指令】:扰乱调试器的,并不执行;
【混淆】:对原指令进行拆解或等价替换,会执行;
零、自己写个程序,加壳
0、为方便我们对VMP壳有更清楚的认识,这里,我们先自己写个程序,然后,加壳;
通过分析对比压缩前后的程序,辅助大家进行更深一层的了解,测试代码如下:
#include <windows.h>
int main(int argc, char** argv) {
MessageBox(, L"VMP Simple Example", L"Reginald", );
ExitProcess();
return ;
}
1、加壳






已经生成了,这里是问,要不要运行,点击YES,会运行我们加壳后的程序,我们看一下加壳后的程序:


加壳完成,下面,我们一起来分析下加壳后的程序;
一、分析加壳后的程序:
在我们进行脱壳时,如果事先不知道这是什么种类的壳,方法只能是单步跟踪了;
这里,在分析前,我们可以从宏观上看下,OEP处有何变化;
加密后的OEP;

加密前的OEP(VS编译的OEP特征 call xxx; jmp yyy)

因为在上面加壳的时候,我们选择了对OEP处的第一条指令加保护,所以,只有第一条指令发生了变化;
我们单步跟踪下这部分的代码:


遇到call,F7跟进去;

我们来看下:
1、保存寄存器环境;
2、将0xB50000 压栈;
3、ESI = *(ESP + 0x2C);
4、EBP = ESP,ESP -= 0xC0;开辟栈帧
5、EDI = ESP,EDI指向新栈顶
6、ESI += *(EBP);ESI内容加上原栈顶内容——我们一会动态分析下;
EAX = *(ESI);
ESI++;
7、跳转到*(EAX*4 + 0xF661E9);
目前,好像就是在做这些操作,我们貌似看不出什么了,但这里不免有几个疑问:
A) 0xB50000 有什么含义;
B) 最后跳转的时候,那个0xF661E9又有什么含义;
我们带着疑问继续动态分析:



目前,我们已经了解到了一些有用的信息:
1、EBP保存原始栈顶
2、EDI保存新栈顶(老栈顶 - 0xC0)
3、ESI处经过运算,将两个无意义的值,变化为了一个有效的地址值【距离VMP段首偏移0x78A处】
4、EAX从ESI处取值,每次1B;
5、进行跳转时,加的常数也是位于VMP区段的地址值【距离VMP段首偏移0x1E9处】
那我们就在010里瞅瞅,0x78A和0x1E9里到底有什么东西;(VMP段内偏移值)



到现在,我们可以做出如下假设:
0x1E9处相当于函数表首地址;
0x78A处取出下标,根据下标进行定位,找出该地址,跳转到此地址;
这些地址,也都位于VMP段,这也符合情况,稍后会给出解释;(什么叫符合情况);
这些地址,都是需要重定位的;
继续分析,验证假设:

我们在010里,0x1E9开始,找出0x1C个地址(地址4B):

0xF50000 + (0x4160B0 - 0x400000) = 0xF660B0(重定位后的地址),那我们要跳转的地址,是这个吗,在OD里走下:

接下来,我们继续分析:

为了方便我们的分析,希望大家将下面的栈图放在心中:

我们已经大致清楚了这部分的逻辑,那我们先把那些用到的下标,找出来,并且找出它所对应的地址,静态的先分析下:

1C 14 30 38 10 00 0C 34 18 3C 08 28
1C:0xF660B0
14:0xF660B0
30:0xF660B0
38:0xF660B0
10:0xF660B0
00:0xF660B0
0C:0xF660B0
34:0xF660B0
18:0xF660B0
3C:0xF660B0
08:0xF660B0
28:0xF660B0
这些都一样,我们瞅瞅是干嘛的:

0xF660B0:【ESP、EDI不变,*(EDI + EAX) = *EBP,EBP += 4】
执行完后:【ESP、EDI不变,EBP += 0x30】
再接着看下面的:
62 60 12 40 00
62:0xF666C0

【EBP -= 4,*EBP = 0x401260(原ESP + 0x2C = 0x401260)】
【EBP VS (EDI + 0x50)】
【(原ESP + 0x2C) > (原ESP - 0x70)】end
这里,要注意这个地方:
1、原ESP + 0x2C,这个位置还记得么,就是最初进入VM的时候,push xxx; call yyy的那个push处的地方;
2、这个地址,0x401260,看着这么亲民,有什么意义吗;
这里,为了弄清楚这个地址的含义,我们先看看未VM保护前的程序,其实,这里不得不说单纯VM的缺陷了,
它只是在做特别厉害的混淆,但是,不论你怎么混淆,该做的,一定要做的,否则,程序就错了;
那它应该做什么呢?还记得,我们加VM保护的指令吗?
OEP:CALL xxx
OEP+5:jmp yyy
CALL xxx = push(OEP+5) && jmp xxx;
我们即使加了VM保护,但是,总要执行xxx处的代码吧,执行完后,又一定会回到OEP+5那吧;
我们如何得到xxx的地址呢,别慌,我们看下机器码,知道OEP的地址了,又有E8后的偏移,不是问题:
这是原来的OEP处的数据,E8 AD 03 00 00


第一:0xF5125B + 0x3AD + 5 = F5160D(执行函数)
第二:0xF5125B + 5 = F51260(返回地址)
到这一步,我们回过头来,再看看刚刚那个亲民的地址:0x401260
这么有感觉吧,像不像一个没有被污染(重定位)的VA,如果是的话,Offset就是0x1260;
再看看那个返回地址0xF51260-0xF50000(当前基址)=0x1260,因此,基本确定,这里就是在搞事情了;
把执行完原该执行的函数后,返回的地址,放到了原ESP+0x2C的位置;一切构造的是那么巧妙,现在知道为啥那个位置要来个push了吧,真是一箭双雕;
思路清晰的读者,也许心中还有一个疑问,别忙,接下来就帮你解答:
1E 2B 04 1E
1E:0xF66757


【0xFF660B0逆操作,EDI、ESP不变,EBP -= 4,*EBP = *(EDI + EAX)】
【每次EBP向反方向变化,都要进行如下比较】
【EBP VS EDI + 0x50】
【原ESP + 0x28 > 原ESP - 0x70】end
2B:0xF6619B(神来之笔)

【*(EBP + 4) += *EBP; *EBP = EFLAGS; EDI不变,ESP不变, EBP不变,前俩内容有变化】
这是在干嘛呢,我们一步步分析到这里的时候:
EBP = 原ESP + 0x28;
*(原ESP+0x2C) = 0x401260(未受污染的返回地址OEP+5);
解释下,就是在执行:
*(ESP+0x2C) += *(EBP)
EBP的值哪里来的,就是上一步过来的,EAX=0x1E 时的那步逆操作,注意EAX & 0x3C后和EDI相加的,也就是0x1C
【0xFF660B0逆操作,EDI、ESP不变,EBP -= 4,*EBP = *(EDI + EAX)】
由于EDI一直未有变化,我们只需要找到什么时候往EDI+0x1C里存东西,就可以了,就是最初的时候:那个1C:
最初的时候,EBP就是原栈顶,1C的时候,执行*(EDI+0x1C) = *EBP,其实就是把当时栈顶的数,放进去了,那当时栈顶的数是什么呢;说出来,你会惊呼它的神奇:
还记得当时,我们认定无效的两个数么,一个是push xxx; call yyy进入虚拟机的;一个是最后push的那个0xB50000

也就是说,当时栈顶的数据,是当前基址-默认基址,现在那些有疑问的朋友,也许如拨云见日了吧;
*(原ESP+0x2C) = 0x401260(未受污染的返回地址OEP+5);
*(原ESP+0x2C) += 0xB50000;(进行重定位)
04:0xF660B0
1E:0xF66757 由于两个对EBP而言是互逆的,因此,不再分析了;EBP还是原ESP+0x28
接下来,我们继续分析,还有最后一处让人惊艳的(加法结合律)
D7 0D 16 40 00
D7:0xF666C0
【EBP -= 4,*EBP = 0x40160D 原ESP + 0x24 = 0x40160D】
【EBP VS EDI + 0x50】
【原ESP + 0x24 > 原ESP - 0x70】end
这个套路,我们已经分析过了,继续走:
4A:0xF6619B
我们已经知道,这是修复重定位的,但是,现在存放该执行函数地址的,是原ESP+0x24的位置,返回地址位置是ESP+0x2C;
乌云啊,是我们分析错了吗,别急,我们也许忽略了一些常识,人最容易忽视的,其实是司空见惯的东西;自认为常识的东西;
重新审视修复重定位的函数:
它执行的操作是什么呢:
*(EBP+4) += *(EBP)
*EBP = EFLAGS;
现在*EBP存放的是0x40160D(有意义的地址);那*(EBP+4)里是啥呢,刚刚在分析上一个重定位的时候,已经知道了,这个位置,是(当前基址 - 默认基址) = 0xB50000
哦,原来如此,这不就是一个结合律的问题吗,谁先加谁的问题,一个主动的,一个被动的,我们把目光都放在了那个更有意义的点了,忽略了另外的也是有意义的;
至此,我们得出结论:
原ESP+0x2C <=> 应该返回的位置;
原ESP+0x28 <=> 应该执行的位置;
分析至此,可以欣慰以下了,接下来,继续走,既然准备工作基本完成,猜测,该退出VM了;
04 3E 1A 36 0E 02 12 3A 32 16 1E
04:0xF660B0
3E:0xF66757 互逆
1A:0xF66757
【原ESP + 0x20 > 原ESP - 0x70】end
36:0xF66757
【原ESP + 0x1C > 原ESP - 0x70】end
0E:0xF66757
【原ESP + 0x18 > 原ESP - 0x70】end
02:0xF66757
【原ESP + 0x14 > 原ESP - 0x70】end
12:0xF66757
【原ESP + 0x10 > 原ESP - 0x70】end
3A:0xF66757
【原ESP + 0xC > 原ESP - 0x70】end
32:0xF66757
【原ESP + 0x8 > 原ESP - 0x70】end
16:0xF66757
【原ESP + 0x4 > 原ESP - 0x70】end
1E:0xF66757
【原ESP > 原ESP - 0x70】end
到这里,EBP就是原ESP了,见到曙光了,再接着走:
3B
3B:0xF66091

POP次,ESP的内容刚刚好,0x28(该执行的位置) && 0x2C(该返回的位置)
如此,我们静态分析了调用过程,为了验证猜想,直接再这个地方下断,看下栈结构:

二、总结:
0、单纯的VMP壳,只是在混淆代码(不妨试试,在IAT地址处搞一搞,就清楚了)
1、最初push xxx; call yyy; 这个push一方面和另外的基址之差定位到下标;另一方面,填充一个栈位(其实就是占位的),最终会被call 指令本应push的地址给替换;
2、进入里面后,最后一个push的值,是基址之差,用来修复call重定位的
3、VMP里,大多是混淆后,变着法的操作原堆栈,在操作完成后,就退出了;
4、退出OEP特征码:89 EC ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? C3
5、其实,经过这方分析后,感觉特征仍有不少,另外,静态分析,也变成了可能;知道VMP段的RVA后 x4 00,也能帮助定位;
6、对于CALL指令,VM保护,就是如上的,也可以分析别的指令,了解其处理方法,总体而言,单纯的VM,仍然离VM有些距离;
希望,能有所帮助;
转载请注明出处,TKS;
脱壳_01_虚拟机壳_VMP的更多相关文章
- 关于《加密与解密》的读后感----对dump脱壳的一点思考
偶然翻了一下手机日历,原来今天是夏至啊,时间过的真快.ISCC的比赛已经持续了2个多月了,我也跟着比赛的那些题目学了2个月.......虽然过程很辛苦,但感觉还是很幸运的,能在大三的时候遇到ISCC, ...
- 对dump脱壳的一点思考
对dump脱壳的一点思考 偶然翻了一下手机日历,原来今天是夏至啊,时间过的真快.ISCC的比赛已经持续了2个多月了,我也跟着比赛的那些题目学了2个月.......虽然过程很辛苦,但感觉还是很幸运的,能 ...
- 浅谈"壳"(一)
壳,即坚硬的外皮,当壳的厚度与其曲面率半径的比值小于0.5时.称为"薄壳".反之称为"厚壳".由壳演化来的胸甲,盾牌. 在计算机这个注重创意又不失从文化科技中汲 ...
- android脱壳之DexExtractor原理分析[zhuan]
http://www.cnblogs.com/jiaoxiake/p/6818786.html内容如下 导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的 ...
- android脱壳之DexExtractor原理分析
导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的原理,使用这种方法脱壳的有2个缺点: 1. 需要动态调试 2. 对抗反调试方案 为了提高工作效率, ...
- 脱壳系列_0_FSG壳_详细版
---恢复内容开始--- 1 查看信息 使用ExeInfoPe查看此壳程序 可以看出是很老的FSG壳. 分析: Entry Point : 000000154,熟悉PE结构的知道,入口点(代码)揉进P ...
- Android FART脱壳机流程分析
本文首发于安全客 链接:https://www.anquanke.com/post/id/219094 0x1 前言 在Android平台上,程序员编写的Java代码最终将被编译成字节码在Androi ...
- DexHunter在ART虚拟机模式下的脱壳原理分析
本文博客地址: http://blog.csdn.net/qq1084283172/article/details/78494620 DexHunter脱壳工具在Dalvik虚拟机模式下的脱壳原理分析 ...
- “.Net 社区虚拟大会”(dotnetConf) 2016 Day 3 Keynote: Scott Hanselman
美国时间 6月7日--9日,为期三天的微软.NET社区虚拟大会正式在 Channel9 上召开,美国时间6.9 是第三天, Scott Hanselman 做Keynote.今天主题围绕的是.NET ...
随机推荐
- 厌倦了“正在输入…”的客服对话,是时候pick视频客服了
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯云视频发表于云+社区专栏 关注公众号"腾讯云视频",一键获取 技术干货 | 优惠活动 | 视频方案 什么?! ...
- docker-compose部署elk+apm
1.安装docker 参考我的另外的一篇博客:https://www.cnblogs.com/cuishuai/p/9485939.html 2.安装docker-compose # yum -y i ...
- JAVA WEB 三器之过滤器(Filter)
过滤器(Filter) 1. 简介 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息,它是 Servlet 技术中最实用的技术,属于系统级别,主要是利用函数的回调实现.对 Jsp, ...
- Java设计模式学习记录-代理模式
代理模式 代理模式是常见设计模式的一种,代理模式的定义是:为其他对象提供一种代理以控制对这个对象的访问. 在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...
- 微信小程序入门篇
微信小程序入门篇: 准备工作 IDE搭建 就不多说了,没有内测码去下载个破解版吧,我用了一下,学习完全够了!IDE破解版+安装教程 图片发自简书App 知识准备 JavaScrip还是要看看的,推荐教 ...
- Scott用户的表结构
在Oracle的学习之中,重点使用的是SQL语句,而所有的SQL语句都要在scott用户下完成,这个用户下一共有四张表,可以使用: SELECT * FROM tab; 查看所有的数据表的名称,如果现 ...
- js 实现 Base64 编码的相互转换
干脆点,直接贴代码 1. 代码 function Base64() { // private property _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZab ...
- Redraw Beautiful Drawings(hdu4888)网络流+最大流
Redraw Beautiful Drawings Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/O ...
- EF数据库优先模式(二)
接着上一节的内容,建立好EF数据模型(DataFirst)之后,创建一个借口类,将公用的借口放到接口类里面 public interface IObjectLoader<T,TM> { ...
- Linux常用基本命令(cut)
cut命令 作用:从文件的每一行剪切字节,字符或者字段,类似与编程语言中的字符串截取函数 格式:cut [option] [file] -b:仅显示行中指定直接范围的内容: -c:仅显示行中指定范围的 ...