zl程序教程

您现在的位置是:首页 >  其他

当前栏目

编程实现小型虚拟机保护并逆向分析及其保护

保护编程虚拟机 实现 分析 及其 逆向 小型
2023-09-27 14:26:10 时间
本文讲的是编程实现小型虚拟机保护并逆向分析及其保护,虚拟机保护技术已经出现了10多年,目前已有较多、较为成熟的商业化虚拟机保护产品,如VMProtect、Themida等。这些产品实现的虚拟机过于复杂,而本文仅仅是逆向分析和编程实现小型虚拟机保护,这种小型虚拟机仅用于说明虚拟机保护这种技术,可以用于开发CrackMe,但与真正的虚拟机保护还有非常远的路要走。

虚拟机保护技术已经出现了10多年,目前已有较多、较为成熟的商业化虚拟机保护产品,如VMProtect、Themida等。这些产品实现的虚拟机过于复杂,而本文仅仅是逆向分析和编程实现小型虚拟机保护,这种小型虚拟机仅用于说明虚拟机保护这种技术,可以用于开发CrackMe,但与真正的虚拟机保护还有非常远的路要走。

2017年看雪CTF大赛正在火热征题中,防守组的各位参赛者随手写一个适用于自己CrackMe的虚拟机,相信可以如虎添翼。

GitHub确实是个好地方,写代码之前可以先去逛一逛。笔者原本打算自行写一个小型虚拟机,不过在GitHub找到了非常理想的代码,所以干脆把这份代码拿出来说一说。这份代码出自NWMonster大神的CrackMe仓库。

本文的后续按照如下方式组织:首先逆向分析一下这个CrackMe,进而分析一下源码并实现自己的小型虚拟机,最后是一个小小的总结。

2.逆向分析

在逆向分析之前,了解一点关于小型虚拟机的程序结构还是很有帮助的。一般来讲,一个小型的虚拟机保护结构图如下:

编程实现小型虚拟机保护并逆向分析及其保护

上图中,VM_code代表虚拟机可以解读的字节码,VM_register为虚拟机使用的寄存器,在一次VM_loop中,虚拟机读取VM_code,并根据读取的内容选择并执行相应的VM_handler。为了明确被虚拟机保护的代码所实现的功能,我们需要了解每个VM_handler的含义,从而理解被隐藏的代码。

需要注意的是,该流程图并不适用于商业虚拟机保护软件,商业的虚拟机保护往往更复杂一些。

通常情况下,可以使用OllyDbg (OD)的trace功能,找到虚拟机的VM_loop。但本次我们使用IDA完成分析,这主要因为IDA的图形界面更加清晰明了。为了方便调试,笔者将源码重新编译为32位windows的debug版本。后文会把所有的材料都上传到Github,需要的读者可自行下载。

用IDA打开CrackMe,不难发现其207字节的VM_code:

编程实现小型虚拟机保护并逆向分析及其保护

上图中的VM_code被赋值给[eax + 20h],如下图:

编程实现小型虚拟机保护并逆向分析及其保护

根据这些代码,可以推测VM_register的结构如下:

Structure VM_register{

+0x00     ??

+0x04     ??

+0x08     ??

+0x0C     ??

+0x10     ??

+0x14     ??

+0x18     ??

+0x1C     ??

+0x20    IP register

}

其中,eax指向VM_register,[eax + 20h]指向虚拟机的指令寄存器。紧接着就可以找到完整的VM_loop,如下图:

编程实现小型虚拟机保护并逆向分析及其保护

数一下上图中的方块数量,可以推测这里有大约23个VM_handler。下面开始根据VM_code的顺序逐一分析这些VM_handler。VM_code的第一个字节为0xDE,该字节会使虚拟机执行VM_handler_defaultcase,如下图所示:

编程实现小型虚拟机保护并逆向分析及其保护

而VM_handler_defaultcase的详细内容如下:

编程实现小型虚拟机保护并逆向分析及其保护

上图中,[this + 0x24]即为指令寄存器,因为VM_register是VM类中的第一个元素,其伪代码如下:

class   VM{

+0x00   point to virtual table

+0x04   VM_register  + 0x20    IP register

}

所以,在0x20的基础上又增加了0x4字节的偏移。这段代码并无实际功能,只增加了指令寄存器,故可理解为NOP指令。VM_code中的第2,3,4字节同样会令虚拟机执行VM_handler_defaultcase分支,故可以如下代码重写VM_code:

1                0xDE                         NOP

2                0xAD                         NOP

3                0xC0                         NOP

4                0xDE                         NOP

VM_code的第5个字节为0x7C,该字节会使虚拟机执行VM_handler_22分支,该分支的具体内容如下:

编程实现小型虚拟机保护并逆向分析及其保护

该段代码的含义是:将后续0xF字节的VM_code与0x66异或。暂不清楚其异或的目的为何。可以用如下方式表示该指令:

5                0x7C          0x2E 0x27 … 0x23   0x32  XOR the following 0xF bytes with   0x66

VM_code的第21字节为0x70,与之相应的VM_handler_10的内容如下:

编程实现小型虚拟机保护并逆向分析及其保护

这个VM_handler的作用是明显是入栈操作,即将后续4字节的VM_code压入虚拟机的栈,其[this + 20h]为栈指针。由此,可以进一步推断出VM_register结构如下:

Structure VM_register{

+0x00     ??

+0x04     ??

+0x08     ??

+0x0C     ??

+0x10     ??

+0x14     ??

+0x18     ??

+0x1C   SP   register

+0x20   IP   register

}

该虚拟机指令可表示如下:

21             0x70          0x00 0x00 0x00 0x2F                         push 0x2F

到此为止,笔者已经分析了25字节的VM_code和3个VM_handler以及一部分VM_register,后续内容还是留给感兴趣的读者。此外,其被虚拟机保护的代码被还原为等价的C代码也上传到了Github,供读者参考。

3.编程实现小型虚拟机

根据已有的源代码,可以很方便的构造小型虚拟机。首先来看一下该源代码,虚拟机使用的VM_register结构如下图:

编程实现小型虚拟机保护并逆向分析及其保护

其中,cf为标志位,*db指向用户输入数据。虚拟机中的VM_loop是通过一个while-switch结构实现的:

编程实现小型虚拟机保护并逆向分析及其保护

上图中的r.ip指向虚拟机的VM_code,该字节码被定义为一个字节数组,如图:

编程实现小型虚拟机保护并逆向分析及其保护

而完整的虚拟机类,即VM类如下图:

编程实现小型虚拟机保护并逆向分析及其保护

其中,结构体REG即为VM_register,为VM类的第一个成员。虚拟机中的各个VM_handler也可以在该类中找到。在每次VM_loop中,虚拟机读取 VM_code,并选择执行响应的VM_handler。

基于这些已有代码,构造一个定制的虚拟机变得非常简单。可以使用已有的VM_handler和VM_register,也可以增加更多的VM_handler,并适当地增加一些混淆。增加混淆的VM_handler会使虚拟机保护更加难以分析。

在已有的框架之上,定制虚拟机的问题是变为设计一段VM_code用来实现所需目的即可。比如说,如果我们需要检查用户输入是否为0x27字节的16进制数字,则可以使用如下VM_code:

       PUSHD,  0x00, 0x00, 0x00, 0x2f,

       POP,    0x30,

       MOVD,   0x00,     //loop begin

       XOR,    0x22,

       CMP,    0x02,

       JZ,     0x33,

       INCD,

       PUSHD,  0x00, 0x00, 0x00, 0x46,

       POP,    0x10,

       CMP,    0x01,

       JG,     0x27,     //error               user-input   ‘F’  ascII:0x46

       PUSHD,  0x00, 0x00, 0x00, 0x30,

       POP,    0x10,

       CMP,    0x01,

       JL,     0x16,     //error               user-input   ‘0’  ascII:0x30

       PUSHD,  0x00, 0x00, 0x00, 0x39,

       POP,    0x10,

       CMP,    0x01,

       JL,     0x0b,     //correct             user-input   ’9’  ascII:0x39

       PUSHD,  0x00, 0x00, 0x00, 0x41,

       POP,    0x01,

       CMP,    0x01,

       JL,     0x06,     //error               user-input   ‘A’  ascII:0x41

       XOR,    0x00,

       CMP,    0x00,

       JZ,     0x03,

       XOR,    0x00,     //error out  R0 =   0         

       END,

       LOOP,   0x3E,     //loop end

       XOR,    0x00,     //correct out  R0   = 1

       INC,    0x00,

       END,

这部分代码是取自源文件中的VM_code,并加以相应的修改。在以上列表中,令用户输入的每一位与‘F’, ‘0’, ’9’, ’A’进行比较,从而判断出是否为16进制数字。当全部满足条件时,虚拟机运行结束后,R0寄存机为1,否则为0。

最后,用这段VM_code替换原先的代码,调试运行确认没有bug之后,就可以正常使用了。

4.小结

本文主要讨论了小型虚拟机保护的相关内容,前一部分逆向分析了一个使用虚拟机保护的CrackMe,后一部分讨论了如果定制一些被虚拟机保护的代码。在分析虚拟机保护过程中,最主要的步骤是分析每一个VM_handler的作用,从而理解被保护的代码含义;而在定制小型虚拟机保护是,关键是设计一段VM_code使之能够顺利完成其目的。

原文发布时间为:2017年5月22日 本文作者:胡一米 本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。 原文链接
windows使用vmware安装三台虚拟机,配置好网络环境 - VMWare虚拟机软件是一个“虚拟[PC](https://baike.baidu.com/item/PC/107)”软件,它使你可以在一台机器上同时运行多个系统。 - 可以通过Vmware来安装我们的linux虚拟机,然后通过linux虚拟机来进行集群的安装。Vmware的安装步骤省略。只要点击安装之后,一路下一步即