Skip to content
KitploitKITPLOIT
工具博客
Log in
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
flare-emu — 基于脚本的二进制仿真框架,集成 IDA Pro/Radare2 与 Unicorn 引擎,用于自动化恶意软件分析、字符串解密以及跨 x86、ARM 和 ARM64 架构的代码路径探索。 | Kitploit
工具/GitHubGitHub/mandiant/flare-emu
动态分析 (沙盒)逆向工程恶意软件分析二进制分析
GitHubmandiant/flare-emu

flare-emu

基于脚本的二进制仿真框架,集成 IDA Pro/Radare2 与 Unicorn 引擎,用于自动化恶意软件分析、字符串解密以及跨 x86、ARM 和 ARM64 架构的代码路径探索。

查看仓库
958143501年前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

flare-emu

flare-emu 将受支持的二进制分析框架(如 IDA Pro 或 Radare2)与 Unicorn 的仿真框架相结合,为用户提供易于使用且灵活的接口来编写仿真任务脚本。它旨在处理设置灵活且健壮的仿真器所需的所有内务管理,使其支持目标架构,从而让您专注于解决代码分析问题。目前,flare-emu 支持 x86、x86_64、ARM 和 ARM64 架构。

它目前提供了五种不同的接口来满足您的仿真需求,以及一系列相关的辅助和实用函数。

  1. emulateRange – 此 API 用于在用户指定的上下文内仿真一系列指令或一个函数。它提供了用户定义的钩子选项,用于单个指令以及遇到“call”指令时。用户可以选择仿真器是跳过函数调用还是调用函数。此接口提供了一种简单的方式,让用户为给定的寄存器和栈参数指定值。如果指定了字节串,它会被写入仿真器的内存,并且指针会被写入寄存器或栈变量。仿真结束后,用户可以使用 flare-emu 的实用函数从仿真内存或寄存器中读取数据,或者使用返回的 Unicorn 仿真对象进行直接探测。emulateRange 的一个小型包装函数名为 emulateSelection,可用于仿真当前在 IDA Pro 中高亮显示的指令范围。

  2. iterate – 此 API 用于强制仿真沿着函数内的特定分支向下执行,以到达给定的目标。用户可以指定一个目标地址列表,或者一个函数的地址,该函数的交叉引用列表将作为目标,同时提供一个到达目标时的回调函数。无论仿真过程中可能导致不同分支的条件如何,目标都会被到达。与 emulateRange API 类似,它提供了用户定义的钩子选项,用于单个指令和遇到“call”指令时。iterate API 的一个示例用法是实现类似于我们的 argtracker 工具的功能。

  3. iterateAllPaths – 此 API 与 iterate 非常相似,不同之处在于您不必提供目标地址,而是提供一个目标函数,它将尝试找到该函数的所有路径并进行仿真。当您进行代码分析希望到达函数的每个基本块时,这非常有用。

  4. emulateBytes – 此 API 提供了一种简单的方法来仿真一段额外的 shellcode。提供的字节不会被添加到 IDB 中,而是直接按原样仿真。这对于准备仿真环境可能很有用。例如,flare-emu 本身使用此 API 来操纵 Unicorn 未暴露的 ARM64 CPU 的特定模型寄存器(MSR),以启用向量浮点(VFP)指令和寄存器访问。Unicorn 仿真对象会返回给用户进行进一步探测。

  5. emulateFrom – 此 API 在函数边界不明确的情况下非常有用,这在混淆二进制文件或 shellcode 中经常出现。您提供一个起始地址,它将一直仿真直到没有剩余内容可仿真,或者您在某一个钩子中停止仿真。使用 IDA Pro 时,可以通过将 strict 参数设置为 False 来调用此函数,以启用动态代码发现;flare-emu 会让 IDA Pro 在仿真过程中遇到指令时创建指令。

安装

要安装适用于 IDA Pro 的 flare-emu,只需将 flare_emu.py、flare_emu_ida.py 和 flare_emu_hooks.py 放入 IDA Pro 的 python 目录,并在您的 IDAPython 脚本中将其作为模块导入。

要安装适用于 Rizin 的 flare-emu,只需确保 flare_emu.py、flare_emu_rizin.py 和 flare_emu_hooks.py 位于 Python 的模块搜索路径中。当使用 Rizin 作为 flare-emu 的二进制分析组件时,需要 rzpipe。

要安装适用于 Radare2 的 flare-emu,只需确保 flare_emu.py、flare_emu_radare.py 和 flare_emu_hooks.py 位于 Python 的模块搜索路径中。当使用 Radare2 作为 flare-emu 的二进制分析组件时,需要 r2pipe。

无论如何,flare-emu 都依赖于 Unicorn 及其 Python 绑定。

重要说明
flare-emu 是使用新的 IDA Pro 7x API 编写的,它不向后兼容旧版本的 IDA Pro。

用法

虽然 flare-emu 可用于解决许多不同的代码分析问题,但它最常见的用途之一是帮助解密恶意软件二进制文件中的字符串。FLOSS 是一个很棒的工具,它通常可以通过尝试识别字符串解密函数,并使用仿真来解密每个交叉引用处传入的字符串,从而自动完成此操作。然而,FLOSS 并不总能识别这些函数并使用其通用方法正确地对它们进行仿真。有时您需要做一些额外的工作,而一旦您熟悉了 flare-emu,它可以为您节省大量时间。让我们来演练一下恶意软件分析师在处理加密字符串时遇到的常见场景。

使用 IDA Pro 的简单字符串解密场景

您已识别出用于解密 x86_64 二进制文件中所有字符串的函数。此函数在多个地方被调用,并解密了许多不同的字符串。在 IDA Pro 中,您将此函数命名为 decryptString。以下是在 flare-emu 脚本中解密所有这些字符串,并在每个函数调用处放置包含解密后字符串的注释,同时记录每个解密的字符串及其解密地址的方法。``` from future import print_function import flare_emu

def decrypt(argv): myEH = flare_emu.EmuHelper() myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), registers = {"arg1":argv[0], "arg2":argv[1], "arg3":argv[2], "arg4":argv[3]}) return myEH.getEmuString(argv[0])

def iterateCallback(eh, address, argv, userData): s = decrypt(argv) print("%s: %s" % (eh.hexString(address), s)) eh.analysisHelper.setComment(address, s, False)

if name == 'main':
eh = flare_emu.EmuHelper() eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)

在 `__main__` 中,我们首先创建一个 `EmuHelper` 类的实例,该类来自 `flare-emu`。这是我们用 `flare-emu` 完成所有任务所使用的类。接着,我们使用 `iterate` API,传入 `decryptString` 函数的地址以及回调函数的名称,`EmuHelper` 将为每个模拟到的交叉引用调用该回调函数。

`iterateCallback` 函数接收 `EmuHelper` 实例(此处命名为 `eh`),以及交叉引用的地址、此次调用传递的参数,以及一个特殊字典(此处命名为 `userData`)。在这个简单示例中并未使用 `userData`,但可以将其视为模拟器的持久上下文,你可以在其中存储自定义数据。但要注意,因为 `flare-emu` 自身也使用这个字典来存储执行任务所需的关键信息。其中一个数据就是 `EmuHelper` 实例本身,存储在 `“EmuHelper”` 键中。如果你感兴趣,可以搜索源代码了解这个字典的更多信息。该回调函数简单地调用 `decrypt` 函数,打印解密后的字符串,并在调用 `decryptString` 的地址处为其添加注释。

`decrypt` 创建第二个 `EmuHelper` 实例,用于模拟 `decryptString` 函数本身,从而为我们解密字符串。该 `decryptString` 函数的原型如下:`char * decryptString(char *text, int textLength, char *key, int keyLength)`。它直接在原位置解密字符串。我们的 `decrypt` 函数将 `iterateCallback` 函数接收到的参数传递给 `EmuHelper` 的 `emulateRange` API。由于这是一个 `x86_64` 二进制文件,调用约定使用寄存器传递参数,而不是栈。`flare-emu` 根据 IDA Pro 分析的架构和文件格式自动确定哪些寄存器代表哪些参数,从而允许你编写至少在一定程度上与架构无关的代码。如果是 32 位 `x86`,则需要使用 `stack` 参数来传递参数,如下所示:`myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), stack = [0, argv[0], argv[1], argv[2], argv[3]])`。第一个栈值是 `x86` 中的返回地址,所以我们这里用 `0` 作为占位值。模拟完成后,我们调用 `getEmuString` API 来检索存储在该函数第一个参数指向的内存位置中的以 null 结尾的字符串。

### flare-emu 和 idalib
* 安装 IDA Pro
* 根据 Hex-Rays 用户指南安装 idalib
  * (激活虚拟环境)
  * pip install /path/to/IDA/installation/idalib/python
  * python /path/to/IDA/installation/idalib/python/py-activate-idalib.py [-d /path/to/active/IDA/installation]
* 导入 idapro 并编写脚本
  * 参见 tests/test_flare_emu_idalib.py 示例

### 使用 Rizin 的简单字符串解密场景

使用上面的相同示例,使用 Rizin 而不是 IDA Pro 时变化不大。一个区别是,`flare-emu` 目前设计为在使用 Rizin 时作为命令行脚本或在 Python shell 中运行。Python shell 非常适合临时解决问题,而命令行脚本则非常适合批处理。上述脚本的 Rizin 版本如下所示(你也可以省略样本路径以在 rizin 中运行):```
from __future__ import print_function
import sys
import flare_emu


def decrypt(argv, eh):
  myEH = flare_emu.EmuHelper(samplePath=sys.argv[1], emuHelper=eh, isRizin=True)
  myEH.emulateRange(
      myEH.analysisHelper.getNameAddr("decryptString"),
      registers={
          "arg1": argv[0],
          "arg2": argv[1],
          "arg3": argv[2],
          "arg4": argv[3],
      },
  )
  return myEH.getEmuString(argv[0])


def iterateCallback(eh, address, argv, userData):
  s = decrypt(argv, eh)
  print("%s: %s" % (eh.hexString(address), s))
  eh.analysisHelper.setComment(address, s, False)


if __name__ == "__main__":
  eh = flare_emu.EmuHelper(samplePath=sys.argv[1], isRizin=True)
  rz = eh.analysisHelper.r
  eh.analysisHelper.setName(0x100000D60, "decryptString")
  eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)

使用 Radare2 的简易字符串解密场景

沿用上述示例,当使用 Radare2 而非 IDA Pro 时,变化不大。一个区别是,flare-emu 目前设计为在使用 Radare2 时通过命令行脚本或 Python 终端运行。Python 终端非常适合临时解决问题,而命令行脚本则适合批量处理。上述脚本的 Radare2 版本如下所示:``` from future import print_function import flare_emu

def decrypt(argv, eh): myEH = flare_emu.EmuHelper(samplePath=sys.argv[1], emuHelper=eh) myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), registers = {"arg1":argv[0], "arg2":argv[1], "arg3":argv[2], "arg4":argv[3]}) return myEH.getEmuString(argv[0])

def iterateCallback(eh, address, argv, userData): s = decrypt(argv, eh) print("%s: %s" % (eh.hexString(address), s)) eh.analysisHelper.setComment(address, s, False)

if name == 'main':
eh = flare_emu.EmuHelper(samplePath=sys.argv[1]) eh.analysisHelper.setName(, "decryptString") eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)

该脚本有两个不同之处。首先,`EmuHelper` 构造函数在此处接受一个参数:`samplePath=sys.argv[1]`。当提供 `samplePath` 参数时,`flare-emu` 将使用 Radare2 配合 `r2pipe` 作为其二进制分析引擎。你还可以看到,第二个参数传递给了在 `decrypt` 函数中创建的第二个 `EmuHelper` 实例。`emuHelper` 参数接受一个已有的 `EmuHelper` 对象,并在创建新对象时克隆其内存。此外,如果你使用 Radare2,新实例会重用现有的 Radare2 会话,而不是创建一个新会话,从而减少额外开销。其次,`flare-emu` 使用 `r2pipe.open` 创建 Radare2 的新实例,因此它很可能不会将我们感兴趣的函数命名为 `decryptString`。你可以通过 `EmuHelper` 的 `analysisHelper` 对象自行设置名称,例如:`eh.analysisHelper.setName(<some地址>, "decryptString")`,或者你可以直接为 `iterate` 和 `emulateRange` 的调用输入地址。

## [仿真函数](#emulationfuncs)
`emulateRange(startAddr, endAddr=None, registers=None, stack=None, instructionHook=None, callHook=None, memAccessHook=None, hookData=None, skipCalls=True, hookApis=True, strict=True, count=0)` - 模拟从 `startAddress` 开始到 `endAddress`(不包括 `endAddress` 处的指令)的指令范围。如果 `endAddress` 为 `None`,则当在同一函数(仿真起始的函数)内遇到“返回”类型指令时停止仿真。
下载工具