VMP加壳(二):VMP的虚拟化原理

摘要:
由于CPU只能识别和执行二进制文件,并直接让硬件CPU执行虚拟机的二进制代码,因此只能考虑通过纯软件模拟虚拟机执行代码指令。为了在软件级别模拟CPU执行二进制代码指令,一般的虚拟机指令要么是操作寄存器,虚拟机指令的处理程序必须模拟这些函数。它用于指示虚拟CPU当前执行的代码。为了满足上述要点,2。VMP虚拟机的执行过程(1)考虑启动VT。

  介绍VMP虚拟化原理之前,先简单介绍一下计算机运行的原理。总所周知,现代计算机的核心部件是CPU、内存、磁盘、键盘、显示器等;最最最核心的就属CPU、内存和磁盘了。用户按开机键,CPU会把OS从磁盘加载到内存运行。由于CPU只能识别并执行二进制文件,所以代码、数据等都是以二进制存放在磁盘和内存的。

       1、为了在软件层面“虚拟化”出底层的硬件,让OS顺利在虚拟机运行,虚拟机也要提供CPU、内存和磁盘的“虚拟”环境,让二进制代码得以顺利执行。目前主流的虚拟化技术是intel/amd推出的VT技术,让VMware、virtualBox这种虚拟机可以“直接”让硬件CPU执行二进制代码,极大提升了虚拟机的效率和速度,减少了模拟的性能损耗。但这种虚拟机的功能相对较重,并且直接让硬件CPU执行虚拟机的二进制代码,达不到保护、混淆代码的目的,所以这种硬件虚拟化是不适合做代码保护的,只能考虑通过纯软件模拟虚拟机执行代码指令。

  为了在软件层面模拟CPU执行二进制的代码指令,需要有以下关键点:

  •   虚拟机的寄存器,用来存放各种临时数据
  •   虚拟机的堆栈,用来做各种数据交换
  •   虚拟机的指令。x86架构下,CPU一旦读取到0x55指令,就知道执行push ebp;一旦读取到0x8BEC,就知道执行mov ebp,esp; 同理,虚拟机也需要有自己的指令集,虚拟CPU才知道自己要干啥。一般虚拟机的指令要么是操作寄存器,要么是操作堆栈,要么做各种算数运算,虚拟机指令的handler都要模拟这些功能。那么问题来了,虚拟机的指令集能不能和物理CPU一样了? 显然是不行的! 两个原因:(1)如果一样,还要纯软件虚拟机干啥?  (2)如果一样,达不到混淆指令的目的
  •   虚拟机的EIP,用来指明虚拟CPU当前执行的代码

      为了满足以上关键点,VMP采取的方案:

  •   虚拟机的寄存器:在内存开辟一段连续的区域当成虚拟机的寄存器,业界称之为VM_CONTEXT,某些版本的VMP用EDI指向这个区域
  •   虚拟机的堆栈: 这个和物理机是一样的,直接在内存开辟就好。VMP还是用EBP指向栈顶
  •   虚拟机的指令:不同版本VMP的指令是不一样的,这样可以在一定程度上防止VMP本身被破解,业界俗称VM_DATA
  •   虚拟机的EIP:业界俗称vEIP,某些版本的VMP用ESI替代,指向VM_DATA,用以读取虚拟CPU需要执行的指令;

      2、VMP虚拟机的执行流程

  (1)想想启动VT时,是不是要先开辟一段内存空间,把当前guestOS部分寄存器的值保存好?VMP也一样,先保存物理寄存器的值,后续退出VM后才能还原

       (2)让vEIP从VM_DATA读取虚拟机的指令

       (3)由于虚拟机的指令和物理CPU完全不同,那么在指令读取后,该怎么去执行了?举个栗子:比如0x1表示入栈,0x2表示出栈,0x3表示寄存器之间互相传数据(当然实际的指令可能不会这么简单,VMP每个版本的指令集都不同),这些指令该怎么执行了?在VMP中,有个概念叫handler,专门根据不同的指令执行不同的操作(当然这些操作VMP事先都定义好了)。这个和VT中VMX的handler作用类似:根据不同的异常有不同的处理方法(我个人猜测VMP的作者肯定借鉴了VT的原理和思路);

          为了达到这种不同指令执行不同handler分支的效果,编码实现层面通常用switch+case实现,用于将不同的指令跳转到不同的分支执行,业界俗称dispatcher。具体到汇编代码,switch+case一般的汇编形式为:mov ecx,dword ptr ds:[eax*4+base] (注意寄存器可能会变成其他的,但这 xxx*4+基址的形式不会变), 这是比较明显的特征,用以用来定位VMP的dispatcher。

  (4)执行完一个handler,vEIP接着指向下VM_DATA的下一个指令,然后重复(2)-(4)这几个步骤;

       整个过程展示如下:

        VMP加壳(二):VMP的虚拟化原理第1张

       (5)综上所述,要想全面了解、分析和掌控VMP,必须要找准这么几个点:

  •  VM_DATA:虚拟机的指令都集中在这了
  •    VM_CONTEXT:虚拟寄存器都保存在这里
  •    diapatcher:所有指令都从这里路由到对应的handler执行(可以简单理解为管理层派发活的,不过3.x版本的VMP貌似去掉了统一的dispatcher,由上个handler直接跳转到下个handler,有点P2P、区块链去中心化的感觉)
  •    handler:具体模拟执行虚拟指令的分支(可以简单理解为具体干活的工具人)。handler之间跳转通过jmp esi 或 push esi ,ret等指令实现(不同版本使用的寄存器可能不同,但跳转实现的方式就这些)
  •   vEIP:当前执行的指令,需要明确是由那个物理寄存器保存的
  •   vStack:存放了临时数据用于各种交换,目前版本还是由

  3、上面讲了大段各种理论,下面说说具体怎么找这些关键点(注意: 下面OD分析的截图不是一次调试截取的,事实上我测试时反复调试了十几次,每次用OD打开样本的地址都不同,但不影响代码执行的顺序和关键点的分析)。

      (1)demo代码如下:这里模拟一个密码、lisence之类的场景:正确的密码是123,输入后输出ok;输入错误的密码就输出fail;

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

char buf[1204];

void main()
{
    while (1)
    {
        scanf("%s", buf);
        if (!strcmp(buf, "123"))
            printf("ok
");
        else
            printf("fail
");
    }

}

  (2)这里只把main函数虚拟保护即可:

    VMP加壳(二):VMP的虚拟化原理第2张

   (3)虚拟化后的结果:从40K增加到602K,增加了15倍;

   VMP加壳(二):VMP的虚拟化原理第3张

    用OD打开:VMP0段564K,膨胀的部分都集中在这里了;

       VMP加壳(二):VMP的虚拟化原理第4张

   (4)刚进入OD,看到的全是jmp。根据之前的理论分析,这些jmp构成了handler表:

  VMP加壳(二):VMP的虚拟化原理第5张

   在刚才那个内存视图,选中vmp0段,右键选择内存访问断点,后续只要访问这个段就会立即断下来。然后开始运行,断到下面:push xxxx,call xxxx这是非常明显的VMP3.xx版本的壳特征

       VMP加壳(二):VMP的虚拟化原理第6张

      不停F7单步进入后来到这里:看到了好多push 寄存器的指令,这里就是保存原始物理寄存器的值的,为后续进入VM加壳做准备;可以看到在push指令之间加载着大量没用的垃圾指令;

      VMP加壳(二):VMP的虚拟化原理第7张

      这些push指令执行完后,栈如右图所示;注意这里一行关键代码已经标红:  esp+0x28处的值赋给了edi,edi指向了VM_DATA,也就是虚拟指令集(后面会进一步解密edi)

   VMP加壳(二):VMP的虚拟化原理第8张

     经过下面add edi,edx后,得到真正的VM_DATA地址:

     VMP加壳(二):VMP的虚拟化原理第9张

     继续往下:又有一条关键指令:sub esp,0xC0; 这里把esp往上开辟192byte的空间,作为VM_CONTEXT(也就是虚拟存器)。后续会用esp+edx来读取这些虚拟寄存器;此时堆栈图如右边:下面保存的是物理寄存器,上面是VM_CONTEXT,中间以ebp隔开

  VMP加壳(二):VMP的虚拟化原理第10张

    继续F7(后续能看到大量这类似的代码):通过ebp从栈取原物理寄存器的值,然后通过edi取指令,接着根据指令对取出的数做各种运算:

     VMP加壳(二):VMP的虚拟化原理第11张

    计算完毕后写回VM_CONTEXT:

  VMP加壳(二):VMP的虚拟化原理第12张

  接着继续取指令和栈的值:

    VMP加壳(二):VMP的虚拟化原理第13张

    计算完毕后继续写回VM_CONTEXT:

    VMP加壳(二):VMP的虚拟化原理第14张

   期间还检查虚拟栈是否填满:

   VMP加壳(二):VMP的虚拟化原理第15张

    栈顶和栈底比较,看看谁大:VMP加壳(二):VMP的虚拟化原理第16张

    如果栈够用就通过jmp继续执行下个handler:

    VMP加壳(二):VMP的虚拟化原理第17张

     如此往复好多次,都快把VM_CONTEXT填满为止,终于到了while循环。这时不知道密码是多少,先随便输入,看到程序输出了fail,突破点就在这了:  通过ASCII在内存中找fail,然后下个访问的断点,成功断下来:

    VMP加壳(二):VMP的虚拟化原理第18张

    这里回溯栈:在栈中保存了函数的调用关系,这里肯定有while循环、判断对比的函数:这里把password在栈上所有的函数都轮询个遍(kernerl32、ucrtbase这些windows系统自带的API就没必要了),挨个下断点,一共下了十几个:

    VMP加壳(二):VMP的虚拟化原理第19张

    这里用辨识度比较高的字符串"aaaaaaaaaaaaa"输入,然后挨个断点地检查,终于在一个断点处找到了密码字符:这个大概率是strcmp函数

    VMP加壳(二):VMP的虚拟化原理第20张

总结:1、大段代码被人为切割成一小块一小块地执行,代码之间通过jmp、call、push+ret方式跳转

           2、统一的dispatcher是真的没有了,handler倒是有一个大表(整个程序的入口点也改到这里了),里面全是jmp语句,对应不同的handler分支

           3、esp指向VM_CONTEXT,edx是虚拟寄存器的偏移,通过esp+edx定位虚拟寄存器的位置;读取的虚拟寄存器保存在ecx,各种处理后通过ecx写回虚拟寄存器;

                 ebp指向虚拟栈顶,通过ebp+偏移挨个读写虚拟栈的数据;

           4、取指令、解密、执行、跳转到下一个handler:很多代码都是一样的,重复生成了好多次

参考:1、https://bbs.pediy.com/thread-225262.htm  新手篇VMProtect 1.81 Demo

           2、https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458296943&idx=1&sn=8ba937d6216a37025d5f97d8a4989f4a    VMProtect 3.3.1虚拟机&代码混淆机制入门

           3、https://bbs.pediy.com/thread-225803.htm   如何分析虚拟机(2):进阶篇 VMProtect 2.13.8

           4、https://bbs.pediy.com/thread-224732.htm 谈谈VMP的爆破

免责声明:文章转载自《VMP加壳(二):VMP的虚拟化原理》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇IntelliJ IDEA 版本控制(svn、git) 修改文件后,所属目录的颜色也变化react 中echarts-for-react使用 自适应div下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Android系统架构(图解)

下图是 Android 操作系统的架构,架构包括 4 层,由上到下依次是应用程序层、应用程序框架层、核心类库和 Linux 内核。其中,核心类库中包含系统库及 Android 运行环境。 图1  Android 操作系统的架构 应用程序层 Android 装配了一个核心应用程序集合,包括 E-mail 客户端、SMS 短消息程序、日历、地图、浏览器、联...

(转)ATOM介绍和使用

一,Atom介绍 Atom 是 Github 开源的文本编辑器,这个编辑器完全是使用Web技术构建的(基于Node-Webkit)。启动速度快,提供很多常用功能的插件和主题,可以说Atom已经足以胜任“半个IDE”了。   个人感觉有如下几个优点: (1)多平台支持(Windows、Mac、Linux) (2)界面美观、现代化,使用舒适 (3)多文件管理...

vmware 安装 centos7 记录笔记

1, 安装centos 7, 到阿里云镜像下载centos 7 1、虚拟机的创建: 打开安装好的VMware (如果大家安装的VMware是英文版的也没关系,对号入座即可),选择“文件--->新建虚拟机”,如图: 这里选择“自定义”,下一步: 选择默认,下一步: 选择“第三项“稍后安装操作系统”,以后安装,方便进行配置。下一步: (注:上图中,...

kestrel Server的源码分析

今天这一篇博客讲的是.net core 自带的kestrel server,当你开发微服务k8s部署在linux环境下,一般默认开启这个高性能服务,如果大家之前看过我的owin katana的博客,会发现.net core 的好多实现在之前.net standard 的版本已经实现过了,当时开发的asp.net 程序与IIS紧紧耦合在一起,后来的微软团队意...

安卓模拟器

作者:匿名用户链接:https://www.zhihu.com/question/24815978/answer/130805145来源:知乎著作权归作者所有,转载请联系作者获得授权。目前市面上安卓模拟器软件看着种类繁多,但其实只有两大流派:Bluestacks和Virutalbox。Bluestacks的历史可以追溯到2011年,是最早在PC上实现流畅运...

Handler 机制(一)—— Handler的实现流程

   由于Android采用的是单线程模式,开发者无法在子线程中更新 UI,所以系统给我提供了 Handler 这个类来实现 UI 更新问题。本贴主要说明 Handler 的工作流程。 1. Handler 的作用 在Android为了保障线程安全,规定只能由主线程来更新UI信息。而在实际开发中,会经常遇到多个子线程都去操作UI信息的情况,那么就会导致U...