概要
基本原理
这里的虚拟机当然并不是指VMWare或者VirtualBox之类的虚拟机,而是指的意思是一种解释执行系统或者模拟器(Emulator)。所以虚拟机保护技术,是将程序可执行代码转化为自定义的中间操作码(OperationCode,如果操作码是一个字节,一般可以称为Bytecode),用以保护源程序不被逆向和篡改,opcode通过emulator解释执行,实现程序原来的功能。在这种情况下,如果要逆向程序,就需要对整个emulator结构进行逆向,理解程序功能,还需要结合opcode进行分析,整个程序逆向工程将会十分繁琐。这是一个一般虚拟机结构:
这种虚拟化的思想,广泛用于计算机科学其他领域。从某种程度上来说,解释执行的脚本语言都需要有一个虚拟机,例如LuaVM,PythonInterpreter。静态语言类似Java通过JVM实现了平台无关性,每个安装JVM的机器都可以执行Java程序。这些虚拟机可以提供一种平台无关的编程环境,将源程序翻译为平台无关的中间码,然后翻译执行,这是JVM虚拟机的基本架构
分析方法
从这里开始,本文所指的虚拟机都是私有实现的小型虚拟机,并不是指成熟的商用虚拟软件,例如VMProtect、Themida等,这些保护软件的逆向工作研究工作很多,不过比较完善和系统的解决方案还没有出现。
在目前的CTF比赛中,虚拟机题目常常有两种考法:
·给可执行程序和opcode,逆向emulator,结合opcode文件,推出flag
·只给可执行程序,逆向emulator,构造opcode,读取flag
拿到一个虚拟机之后,一般有以下几个逆向过程:
·分析虚拟机入口,搞清虚拟机的输入,或者opcode位置
·理清虚拟机结构,包括Dispatcher和各个Handler
·逆向各个Handler,分析opcode的意义
在实际调试中,个人觉得最为关键的是要搞清虚拟机的执行流。调试过程中,在汇编层面调试当然是最基本最直接的方法,但是由于虚拟机Handler可能比较多,调试十分繁琐。若虚拟机内部没有很复杂的代码混淆,可以考虑使用IDA进行源码级调试,这对于快速整理emulator意义很有帮助。再进一步,可以结合IDA反编译伪代码,加上一些宏定义,加入输出,重新编译,可以十分快速的逆向整个emulator执行过程。
目前比赛中,虚拟机题目特点是核心算法不是很复杂,虚拟机本身没有反调试和代码加密混淆的加入。当然,随着整体CTF水平的不断进步,未来虚拟机逆向难度只会越来越高。
一种方法是通过IDA反编译结果(https://expend20.github.io/2018/05/24/RCTF-simple-vm.html),稍微改改之后,重新编译,然后就可以进行源码级调试。这里的修改基本不用动IDA的反编译结果,就是需要加些IDA的宏定义,在每个case后面都使用printf输出当前指针位置,就可以自动的分析出程序的执行流,十分方便。