虚拟机代码混淆实现细节

逆向工程 混淆 去混淆 虚拟机
2021-06-24 16:51:26

我想实现一个基于 VM 的简单概念验证混淆器。它应该将一个 exe 文件作为输入并生成一个新的 pe 文件,并附加了 vm 部分。为简单起见,我们假设 exe 文件被编译为 32 位 pe。

问题是我在网上找到的大多数材料都只解释了如何破解而不是如何实现这样的解决方案,或者只是解释了如何用非常有限的指令实现一个简单的虚拟机。

建筑学

我想构建这样一个架构,如这篇(通过动态字节码调度增强基于虚拟机的代码混淆安全)论文:

在此处输入图片说明

让我引用一下:

Fig. 1. A classical process for VM-based code obfuscation.
To obfuscate the code, we first dissemble the code region to be
protected into native assembly code (1).
The assembly code will be mapped into our virtual instructions (2)
which will then be encoded into a bytecode format (3). Finally,
the generated byecode will be inserted into a specific region of
the binary which is linked with a VM library (4).

问题

假设我已经实现了一个非常基本的虚拟机,其中包含 20 条指令,如本[presentation] 所示

iadd, 
isub, 
imul, 
ilt, 
ieq, 
br addr, 
brt addr, 
brf addr, 
iconst value, 
load addr, 
gload addr, 
store addr, 
gstore addr, 
print, 
pop, 
call addr, numArgs
ret
hlt

在第 2 步(虚拟化)中,我必须以某种方式将提取的英特尔指令集映射到我的虚拟化指令。Intel 指令集很大(超过 200 条指令)。我完全不知道该怎么做。首先,我的虚拟机使用的寄存器比英特尔少。

2个回答

您将需要阅读 200 多条指令中的每一条,并弄清楚如何在指令集中执行“相同”的操作。因此,您需要查看堆栈如何变化、堆栈指针、程序计数器,以及是否有任何内存或寄存器发生变化。对于没有足够寄存器等差异,您可以留出一部分内存用于存储否则将进入 Intel 寄存器的值。还要注意数据的字节序,以及它是 32 位、64 位等。

如果您可以假设每个指令都独立于其他指令,那就是这样。您开始使用的指令集可能有破坏这一假设的怪癖,您需要注意这些。例如,它可能有分支延迟槽,就像在 MIPS(了解用于反转 MIPS 的分支延迟槽)中一样,其中评估分支的条件,即使发生跳转/分支,也会评估下一条指令。

由于映射可能是一项具有挑战性的任务,因此有些人采用另一种方法,从高级语言(例如 C)的源代码开始,直接将其编译为虚拟机的字节码。例如,看一下Tigress 混淆器virtualize转换TIGRESSOBFUSCATION是源代码(C到C),因此选择后,生成的C代码实际实现所需的虚拟机,并在该虚拟机上运行字节码(通过TIGRESS编译为VM)。virtualize

但是在您的情况下,如果您必须从 exe 开始,那么您需要处理映射。

如果您想要基于概念验证 VM 的混淆器,您实际上可以跳过虚拟化步骤和字节码生成步骤。您可以在 x86 汇编器中编写 x86 指令的解释器,这样实现起来很容易。例如,您可以稍后通过汇编器宏一一添加自定义指令。这比编写一个完整的虚拟机要简单得多。

可以在这里找到运行一些 x86 指令的最简单的“VM”:

https://github.com/Barebit/trivial-vm

vm1.c 只是将 x86 指令复制到缓冲区并运行它。它甚至不是一个翻译。vm2 做同样的事情,但解释了一些不能直接运行的分支指令。