返回介绍

21.4 基于虚拟机的模糊

发布于 2024-10-11 21:05:49 字数 3400 浏览 0 评论 0 收藏 0

如本章前面所述(见 21.1.2 节中的“操作码模糊”),一些最复杂的模糊器使用自定义字节码及相关的虚拟机重新实现了原本具有输入接收功能的程序。面对以这种方式模糊的二进制文件,你看到的唯一本机代码为虚拟机。假设你认识到所看到的是软件虚拟机,那么一般而言,完全了解所有这些代码并不能揭示该模糊程序的真实意图。这是因为程序的行为仍然隐藏在必须由虚拟机解释的嵌入式字节码中。要完全了解这个程序,首先你必须定位所有的嵌入式字节码,然后逆向工程虚拟机的指令集,以便能够正确解释该字节码的含义。

作为比较,想象一下,如果你对 Java 一无所知,有人给你一个 Java 虚拟机和一个包含已编译字节码的.class 文件,并问你它们有什么作用。由于缺乏任何文档资料,你可能对字节码文件知之甚少,并且你需要完全逆向工程虚拟机才能了解.class 文件的结构以及如何解释它的内容。对字节码机器语言有一定了解后,接下来你就可以了解.class 文件的作用。

VMProtect 是一款利用非常复杂的基于虚拟机的模糊技术的商业产品。更多是作为一种学术活动,TheHyper 的 HyperUnpackMe21 挑战二进制文件是在模糊中使用虚拟机的一个相当简单的示例,主要的挑战在于定位虚拟机的嵌入式字节码程序并确定每个字节码的含义。在 OpenRCE 上描述 HyperUnpackMe2 的文章2 中,Rolf Rolles 采用的方法是:充分了解虚拟机以构建一个能够反汇编其字节码的处理器模块。然后,他使用该处理器模块来反汇编嵌入到所挑战的二进制文件中的字节码。使用这个方法存在一个小限制,通过它你可以查看 HyperUnpackme2 中的 x86 代码(使用 IDA 的 x86 模块)或虚拟机代码(使用 Rolle 的处理器模块),但不能同时查看这两种代码。为此,你需要创建两个不同的数据库(每个数据库使用不同的处理器模块)。另一种方法是在使用插件的过程中利用定制现有处理器模块的功能(参见 19.5 节),来有效扩展指令集,在其中包括嵌入式虚拟机的所有指令。将这种方法应用于 HyperUnpackMe2,我们就可以在一个数据库中同时查看 x86 代码和虚拟机代码了,如下面的清单所示:

1. HyperUnpackMe2 是一个 crackme。——译者注
2. 参见 http://www.openrce.org/articles/full_view/28 网站的“Defeating HyperUnpackMe2 With an IDA Processor Module ”。

TheHyper:01013B2F             ➊ h_pop.l       R9  
TheHyper:01013B32                h_pop.l       R7  
TheHyper:01013B35                h_pop.l       R5  
TheHyper:01013B38                h_mov.l       SP, R2  
TheHyper:01013B3C                h_sub.l       SP, 0Ch  
TheHyper:01013B44                h_pop.l       R2  
TheHyper:01013B47                h_pop.l       R1  
TheHyper:01013B4A                h_retn        0Ch  
TheHyper:01013B4A sub_1013919  endp  
TheHyper:01013B4A  
TheHyper:01013B4A ; ----------------------------------------------------------  
TheHyper:01013B4D                dd 24242424h  
TheHyper:01013B51                dd 0A9A4285Dh           ; TAG VALUE  
TheHyper:01013B55  
TheHyper:01013B55 ; ============ S U B R O U T I N E =========================  
TheHyper:01013B55  
TheHyper:01013B55 ; Attributes:  bp-based frame  
TheHyper:01013B55  
TheHyper:01013B55 sub_1013B55    proc near      ; DATA XREF: TheHyper:0103AF7A?o  
TheHyper:01013B55  
TheHyper:01013B55 var_8          = dword ptr -8  
TheHyper:01013B55 var_4          = dword ptr -4  
TheHyper:01013B55 arg_0          = dword ptr  8  
TheHyper:01013B55 arg_4          = dword ptr  0Ch  
TheHyper:01013B55  
TheHyper:01013B55             ➋ push    ebp  
TheHyper:01013B56                mov     ebp, esp  
TheHyper:01013B58                sub     esp, 8  
TheHyper:01013B5B                mov     eax, [ebp+arg_0]  
TheHyper:01013B5E                mov     [esp+8+var_8], eax  
TheHyper:01013B61                mov     [esp+8+var_4], 0  
TheHyper:01013B69                push    4  
TheHyper:01013B6B                push    1000h

其中,从➊处开始的代码被反汇编成 HyperUnpackMe2 字节码,而➋处以后的代码则以 x86 代码显示。

Hex-Rays 预料到了同步显示本机代码和字节码的功能,并在 IDA 5.7 中引入了自定义数据类型和格式。如果 IDA 的内置格式化选项无法满足你的需求,这时就可以使用自定义数据格式。为格式指定(使用脚本或插件)一个菜单名,并指定一个执行格式化的函数,就可以注册新的格式化功能。为某个数据项选择自定义格式后,每次需要显示该数据项,IDA 都会调用格式化函数。如果 IDA 的内置数据类型并不足以表示你在特定二进制文件中遇到的数据,这时就需要用到自定义数据类型。与自定义格式一样,自定义数据类型也使用脚本或插件进行注册。Hex-Rays 示例注册了一个自定义数据类型来指派虚拟机字节码,并通过使用一种自定义数据格式将每个字节码显示为一条指令。这种方法的缺点在于,它需要你定位每条虚拟机指令,并明确更改其数据类型。使用自定义处理器扩展,将单个值自动指派为虚拟机指令可发现每条可到达的指令,因为 IDA 会推动反汇编进程,且处理器扩展会通过它的 custom_emu 实现来发现可到达的新指令。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文