大家好,我是武汉科锐逆向CR49期的学员,这篇文章是为我最近开发的基于LLVM的混淆框架中的基本块VMP保护所写的验证Demo的解析,希望能够帮助到需要开发虚拟机保护的朋友们。我们这次写的是个简单指令集的堆栈式虚拟机,堆栈式虚拟机相对于寄存器虚拟机虽然效率不及寄存器式虚拟机但是指令集可以足够简单对逆向还原能够造成较大的困扰,同时能够较好的产生混淆指令,所以我们选择堆栈式虚拟机,接下来我们谈谈虚拟机的结构。
首先对于虚拟机结构设计在看雪中已经有很多文章阐述过常见的虚拟机结构,通常都是Interpreter + opcode + handleTable + VMContext的形式如下图所示 整个流程就是模拟CPU的行为进行。
虚拟机执行循环:指令集我们可以根据需要虚拟化的目标对象来进行虚拟化指令集设计,比如如果你仅仅想保护一段核心的算法其实只需要模拟出常见的计算指令即可,如果你想要保护整个函数则需要模拟出整个指令集,我们作为演示程序仅做出一些简单指令的模拟,且对于指令集由于具有Handle表的存在所以我们可以将指令的Opcode设计成线性递增的这样可以直接通过下标从Handle表中获取对应的Handle.我们暂且模拟上面没有退格的指令,在上述的虚拟机结构体系中每一个指令都代表着一个Handle该Handle存在一个全局数组中被称为Handle表。如下所示此handle表的顺序是随机的,这样我们就可以通过随机重排Handle表来保证HandleOpcode的随机这样就导致攻击者无法通过一次逆向解决整个保护系统,我们同时还可以对Handle进行膨胀混淆,通过虚假控制流 随机控制流 膨胀混淆 以阻扰攻击者对Handle进行特征工程,这样能够避免低级攻击者的脚本行为。在这个基础上由于此Demo是用于我的框架之中的所以我还为他加了一个单字节密钥,此密钥将会作用于每一个基本块,在虚拟机解释执行的时候将会自动利用该密钥解密基本块中的VMOpcode当然这里可以选用其他更好的加密方案这篇文章的重点不在这里所以就选择了最简单的方案。如下VMContext是虚拟机的环境控制块,该结构主要负责保证寄存器环境的正确性,其中需要保存常见的寄存器环境如下,在VMContext中我们有一个status成员该成员用于标识虚拟机是否可以返回。在这里我们可以将指令集中的寄存器编号设置为 00 04 08 0c 10这样的序列因为这样我们在访问寄存器的时候可以通过转换Ctx Int*通过下标来获取到寄存器的值。如下所示然后我们还需要用堆申请一个空间用作虚拟机栈最重要的就是Handle的设计,因为Handle是正确模拟指令操作,以及直接操作VMContext对象的函数,我们需要根据堆栈式虚拟机的指令特性正确写出Handle。如下要想要Handle能被正确的执行我们还需要一个解释器,该解释器模仿CPU的行为,即取指令->译码->执行 在我们的保护程序中还需要对Opcode 进行解密。实话说解释器其实并没有太多的技术难点,对于虚拟机保护而言更重要的是如何保证原函数的API调用以及跳转语句的翻译和返回才是真正的难点。因为这个还是个Demo程序所以我们进入虚拟机,还不是那么方便因为正常的虚拟机需要在编译期或者插入的时候计算出解释器到被保护的函数的距离然后在函数开头进行跳转,在函数的结尾跳回来,但是我们全程在VS中演示且因为我懒,所以导致没有正确的调用栈,不过这也不是什么难事,我们只需要在写VMCode时候,手动加上上一层函数的栈底即可。最后我们可以看下运行的效果
换一个参数
对于完整的虚拟保护兼容性是最大的问题,但是如果我们能够想办法将虚拟机保护的粒度减少到基本块也能避开一部分的兼容性问题,当然基于LLVM IR来设计开发虚拟化保护也能让你少做部分工作。
贴出全部源码大家可以自己复现并且在这基础上增强虚拟机,并且尝试进行PIC(大概就是Malloc可能需要替换成VirtualAlloc然后用PEB通过APIHash拿到指针后调用即可)然后植入到汇编中去, 谢谢大家的阅读,如果有什么不对的地方也烦请各位在评论区指出。00(参数指示位) 000000 Handle idx
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-287259.htm
[原创]LLVM 基本块VMP保护之简易虚拟机示例实现
186 浏览
12 回复
抱歉各位 因为有些原因LLVM内嵌解释器以及相关IR处理的核心部分被我删除了,估计会过段时间再开源了。
感谢分享
很强
牛逼
niub plus,有pc的vmp分析吗
学习
Mark,坐下慢慢学习
蛋疼 ,
谢谢分享
学习
支持一下学弟~
学习