技术揭秘 | 代码追踪工具分享及应用指南来啦
自从绒绒与大家分享病毒分析报告以来,评论区的相关讨论不仅让绒绒成就感满满,也下定决心(暗暗攥拳)要和大家分享更多有意义的文章。总览评论后,绒绒发现大家不仅对病毒本身充满探究精神,也对工程师是通过什么方式对病毒进行分析以及在分析过程中使用什么工具感到好奇。那么今天就不讲病毒!让我们讲一讲如何“研究”病毒——特别是追踪技术在病毒分析中的应用。
随着网络环境的不断变化,恶意软件与病毒变种开始层出不穷,给安全研究和病毒分析工作带来了不小的挑战。比如在病毒逆向分析过程中,有高效代码保护能力的强壳加密技术,这种技术通常通过加密、压缩和虚拟化等手段隐藏或混淆程序代码,导致分析人员难以获得有效的解密方法,进而使得对病毒样本的调试、分析和修改工作变得复杂。常见的强壳如Themida、VMProtect,以及越来越广泛的自定义混淆壳(代码虚拟化),通常采用多种复杂的保护策略,包括但不限于代码加密、API钩子、反调试、反虚拟机技术等,这些都一定程度上增加了病毒逆向分析的难度。
病毒在升级,但我们的技术手段也在不断升级。比如在病毒逆向分析的过程中,分析者可通过追踪解析被保护程序或加壳程序。通过追踪程序的执行过程,分析者可以观察到程序运行时的行为,逐步揭示其加壳或加密的方式。追踪可通过多种方法进行,例如调试器、动态分析工具、代码注入等,这些方法有助于分析者跟踪程序的调用栈、数据流以及了解加壳后的解密过程,进而破除壳保护,恢复程序的原始功能和代码。
以下分享几种我们常用的代码追踪工具。
一、Pin
Pin 是 Intel 推出的一款适用于 IA-32 和 x86-64 架构的动态二进制插桩框架,支持包括指令级、基本块级、镜像级和函数级插桩在内的多种插桩方式。同时 Pin 拥有丰富的 API,这使得 Pin 能够抽象底层指令集的特性,并允许将上下文信息(如寄存器内容)作为参数传递给注入的代码。Pin 会自动保存和恢复被注入代码覆盖的寄存器内容,从而确保应用程序能够继续正常运行。此外,Pin 还可提供有限的符号及具有调试信息访问功能。作为一种前端工具,Pin 能够有效支持代码逆向分析,尤其在数据提取方面表现出色。
同时,Pin 也可视为一种即时翻译器(JIT),与其他翻译器不同的是,其输入的内容并非是字节码,而是常规的可执行文件,当文件在执行第一条指令时,Pin 会进行拦截控制,并为该指令及其后续代码序列生成新的“翻译”代码。之后,控制权被转交到新生成的中间代码序列,该序列与原始代码几乎一致。每当程序执行到分支退出时,Pin 会重新获得控制权,并为分支目标生成新的代码,继续执行后续操作。通过将所有生成的代码保留在内存中,Pin 提高了执行效率,使得代码可以被重复使用,并允许程序从一个序列直接跳转到另一个序列。
在 JIT 模式下,实际运行的是新生成的中间代码。原始代码仅作为参考,在生成代码时,Pin 给用户提供了注入自己代码(插桩)的机会。
Pin 原理
回调函数
-
INS_AddInstrumentFunction (INSCALLBACK fun, VOID *val) 注册以指令粒度插桩的函数
-
TRACE_AddInstrumentFunction (TRACECALLBACK fun, VOID *val) 注册以 trace 粒度插桩的函数 (基本块插桩)
-
RTN_AddInstrumentFunction (RTNCALLBACK fun, VOID *val) 注册以 routine 粒度插桩的函数,函数级别的插桩需要符号信息
-
IMG_AddInstrumentFunction (IMGCALLBACK fun, VOID *val) 注册以 image 粒度插桩的函数
-
PIN_AddFiniFunction (FINICALLBACK fun, VOID *val) 注册在应用程序退出前执行的回调函数
-
PIN_AddDetachFunction (DETACHCALLBACK fun, VOID *val) 注册在 Pin 通过PIN_Detach()函数放弃对应用程序的控制权限之前执行的函数,一个进程只调用一次,可以被任何线程调用
BLL(基本块)
BBL(Basic Block)即基本块,是程序执行中最小的执行单元之一。它通常由一系列连续的指令组成,这些指令之间不存在跳转,因此在基本块内程序控制流程是顺序执行的。在动态分析中,基本块被视为程序的一个“原子”执行单元。
Pin 保证每个 trace (追踪)只有一个顶部入口点,但可以有多个出口点。如果一个分支指令指向 trace 的中间位置,Pin 会生成一个新的 trace,并以该分支为起点。Pin 将 trace 切分成基本块,每个基本块称为“BBL”,每个 BBL 是一个具有单一入口和单一出口的指令序列。如果有分支指向 BBL 的中间位置,则会定义一个新的 BBL。通常,分析调用会以 BBL 为单位插入,这样可以减少分析调用对性能的影响。
-
函数:VOID BBL_InsertCall(BBL bbl, IPOINT ipoint, AFUNPTR fun, ...) -
功能:在基本块插入回调函数
-
说明:允许在指定的基本块中插入回调函数,并在程序执行时触发该回调函数。IOPOINT 可指定回调函数插入位置的枚举类型,可以选择如 IOPOINT_BEFORE 、 IOPOINT_AFTER、IPointAny 等位置进行插桩 -
IPointBefore:在基本块开始执行之前调用回调函数
-
IPointAfter:在基本块执行结束之后调用回调函数
-
IPointAny:在基本块的任何位置都可以调用回调函数
-
Trace Instrumentation 通过 TRACE_AddInstrumentFunction API 进行注册 Trace 回调。
Pintools
基于 Pin 开发的 Pintools 是一种动态二进制插桩工具,也相当于动态程序分析工具,可用于对Linux、Windows 上的用户空间应用程序进行程序分析。因其能够实现无需重新编译源代码,即可在程序运行时进行插桩,所以 Pintools 也支持对动态生成代码进行插桩。Pintools 包括Intel® VTune™ Amplifier、Intel® Inspector、Intel® Advisor以及Intel®软件开发模拟器(Intel® SDE)。
通过编写简单的示例代码,可以对代码覆盖率进行简单分析,但这种方法在处理大量执行流的明文字符串时,会涉及到频繁的 IO 操作,特别是在分析那些被 VMProtect、Themida 等强壳保护的程序时,如果虚拟化的指令数量达到千万条时,追踪效率会大幅降低。为了优化这一过程,我们可以利用 ProtoBuf 库对数据进行序列化,并通过对 BBL 进行白名单标记,取消对重复执行代码块的插桩,从而提高追踪效率。