技术文章

了解最新技术文章

当前位置:首页>技术文章>技术文章
全部 5 常见问题 0 技术文章 5

逆向 Android 应用程序保护器,第 3 部分 - 代码虚拟化

时间:2022-10-21   访问量:1082

什么是代码虚拟化

相对新颖,代码虚拟化可能是目前最有效的保护技术之一1随之而来的是相对严重的缺点,例如执行速度受阻2以及难以对生产代码进行故障排除。与其他更传统的软件保护技术相比,其优势在于提高了逆向工程的障碍。

代码保护上下文中的虚拟化意味着:

虽然M1的一般特性可能是固定的(例如,所有代的M1都是具有这样那样特性的堆栈机器),但M1的指令集架构 (ISA)可能不一定是固定的。例如,操作码、微码及其实现可能因代而异。至于C1,一代的特性只受转换器能力的限制。不用说,标准的混淆技术可以应用于C1虚拟化过程可能是递归的(C1可以是实现机器M2规范的 VM ,执行代码对象C2,模拟C0的原始行为等)。

总而言之,在实践中,这使得M1C1独一无二且难以逆向工程。

将代码对象 C0 虚拟化为 C1 之前和之后

受保护方法的示例

注意:所有标识符名称都已被混淆。为了清晰和理解,它们被重命名。

下面,该类VClass被发现是“虚拟化的”。虚拟化类意味着所有非构造函数(除了<init>(*)Vand之外的所有<clinit>()V方法)都被虚拟化了。

有趣的是,构造函数没有虚拟化

方法d(byte[])byte[]是虚拟化的:

虚拟化方法。注意 pcode 数组。操作码处理程序位于两个开关中。此图为第二个开关,用于处理特定操作和 API 调用。

p-code VM 类的片段。此处的完整代码,还包含虚拟化类。

通用解释器通过调用vm.exec(opcode)如果操作未被处理,则执行回退到虚拟化方法中的第二个开关条目。

有关“通用”VM 操作的完整列表,请参阅上面链接的要点。三个示例,其中一个表明操作并不像术语所暗示的那样通用:

(特定于此 VM)操作码 6,用于查看最近推送的对象
(特定于此 VM)操作码 8,一个 push-int 操作
(特定于这个 VM)操作码 23 相对专用,它实现了一个 add-xor 堆栈操作(pop、pop、push)。有趣的是,保护系统并不仅仅生成一对一的 dalvik-to-VM 操作码。相反,目标例程被彻底分析,很可能被解除,高级(复合)算术运算被隔离,并生成伪通用(在 PCodeVM 中)或专用(在虚拟化方法中)操作码。

如前所述,负操作码表示特定于虚拟化方法的自定义操作,包括控制流更改。一个例子:

opcode -25:一个if(a >=b) goto LABEL操作(首先,调用 opcode 55 对前两个整数进行 GE 操作;然后,使用结果进行条件分支)

P 代码 VM 的特征

通过对该代码的分析以及在其他二进制文件中发现的虚拟化方法,可以推断出应用保护程序生成的 p-code VM 的特征:

P代码混淆:垃圾插入,意大利面条代码

虽然PCodeVM操作码都是“有用的”,但虚拟化方法(负 id)的许多特定操作码只能执行在语义上等同于NOPor的代码GOTO

操作码 -2、-1:本质上是分支指令。可以找到其中的大量内容,包括一些分支到除了该源之外没有其他输入的块(即,不必要的 GOTO - = 意大利面条代码 - 或者如果下一个块是后续块,则为 NOP 操作。)

重建虚拟化方法

下面,我们将解释用于重建虚拟化方法的过程。呈现的 CFG 是dexdec 4管道使用的 IR-CFG(中间表示) 。请注意,与gendec的 IR 5不同,dexdec的 IR 不公开,但其文本表示大多是不言自明的。

总的来说,一个虚拟化的例程,一旦像任何其他例程一样被 dexdec 处理,看起来如下所示: 一个循环 p-code 条目(存储在x8下面),a()首先在 0xE 处处理,或者由大型例程切换处理。

虚拟化方法,优化,虚拟化

该例程a()是 PCodeVM.exec(),其优化的 IR 归结为一个大的单个开关。6

PCodeVM.exec()

非虚拟化器需要识别关键项目才能开始,例如 p-code 条目、用作 p-code 数组索引的标识符等。一旦它们被收集,虚拟化例程的 concolic 执行成为可能,并且允许重建原始执行流程的原始版本。需要注意多个注意事项,例如 p 代码内联、分支或流终止。在当前状态下,非虚拟化器忽略异常控制流。

下面是未展平 CFG 的原始版本。请注意,所有操作都是基于堆栈的;此时代码本身尚未修改,它仍然由基于 VM 堆栈的操作组成。

展开后的虚拟化方法,原始

dexdec 的标准 IR 优化通道(死代码删除、常量和变量传播、折叠、算术简化、流程简化等)基本上清理了代码:

展开和 IR 优化后的虚拟化方法 (opt1)

在这个阶段,所有操作都是基于堆栈的。从上面生成的高级代码将非常笨拙且难以分析,尽管比原来的双开关要好得多。

下一阶段是分析基于堆栈的操作,以恢复堆栈槽的使用并将其转换回标识符(可以看作是虚拟寄存器;本质上,我们实现了基于堆栈的操作到基于寄存器的操作的转换)。堆栈分析可以通过多种方式完成,例如,使用定点分析。同样,需要注意一些警告,正确识别堆栈及其索引的需要对于此操作至关重要。

展开后的虚拟化方法、IR 优化、VM 堆栈分析 (opt2)

经过另一轮优化:

展开后的虚拟化方法、IR 优化、VM 堆栈分析、IR 优化 (opt2_1)

堆栈分析完成后,我们可以将堆栈槽访问替换为标识符访问。

展开后的虚拟化方法、IR 优化、VM 堆栈分析、IR 优化、虚拟寄存器插入 (opt3)

经过一轮优化:

展开后的虚拟化方法、IR 优化、VM 堆栈分析、IR 优化、虚拟寄存器插入、IR 优化 (opt3)

此时,“原始”CFG 基本上被重建,并且可以应用其他高级反混淆通道(例如,基于仿真的反混淆器)。

高级代码生成产生一个干净的、非虚拟化的例程:

高级代码,非虚拟化,未标记

反转后,似乎是修改后的 RC4 算法。请注意添加到键的 +3/+4。

高级代码,非虚拟化,标记

检测虚拟化方法

所有版本的 JEB 都检测虚拟化方法和类:在 APK/DEX 上运行Global Analysis(GUI 菜单:Android )并查找那些特殊事件:

dexdec 事件:
“找到虚拟化例程处理程序 (P-Code VM)”

JEB Pro版本 3.22 7附带 unvirtualizer 模块。

提示:

结论

我们希望您喜欢关于代码(非)虚拟化的第三部分。

本系列的本机代码保护可能会有第四章也是最后一章。直到下一次!

  1. 就个人而言,我第一次涉足基于 VM 的保护可以追溯到 2009 年,当时我分析了 Trojan.Clampi,这是一种受 VMProtect 保护的 Windows 恶意软件

  2. 尽管有人可能会争辩说,对于当前的硬件(快速 x64/ARM64 处理器)和软件(JIT'er 和 AOT 编译器),这个缺点可能不像以前那么重要了。

  3. 这里的机器可以理解为物理机或虚拟机dexdec 是 JEB 的 dex 反编译引擎

  4. gendec 是 JEB 的通用反编译管道

  5. 注意与通过 chenxification 和类似技术展平的 CFG 的相似之处。这里的一个关键区别是下一个块可以使用 p-code 数组而不是关键变量来确定,在每次操作后更新。即,是 FSM——控制下一个状态(= 下一个基本块)是什么——嵌入在扁平化代码本身中,或者实现为 p 代码数组。

  6. JEB Android和 JEB 演示版本不提供 unvirtualizer 模块。我最初编写这个模块是为了不打算发布的概念验证,但最终决定将它提供给我们拥有合法(非恶意)用例的专业用户,例如代码审计和黑盒评估。


上一篇:编写 dexdec IR 优化器插件

下一篇:逆向 Android 应用程序保护器,第 2 部分 - 资产和代码加密

发表评论:

评论记录:

未查询到任何数据!

在线咨询

点击这里给我发消息 售前咨询专员

点击这里给我发消息 售后服务专员

在线咨询

免费通话

24小时免费咨询

请输入您的联系电话,座机请加区号

免费通话

微信扫一扫

微信联系
返回顶部