bitdefender/bddisasm

GitHub: bitdefender/bddisasm

一个轻量级、高性能的 x86/x64 指令解码器,附带 shellcode 行为检测模拟器,适用于安全分析和底层系统开发。

Stars: 1068 | Forks: 122

# Bitdefender 反汇编器 Bitdefender 反汇编器 (bddisasm) 是一个轻量级的、仅支持 x86/x64 的指令解码器。它易于集成、易于使用,没有外部依赖,是线程安全的,完全不分配内存,几乎可以在任何环境中工作(我们在用户态、内核态、Hypervisor 以及 Windows 和 Linux 环境中都使用它),并且提供了大量关于解码指令的信息,例如:操作数(包括显式和隐式的)、每个操作数的访问模式、CPUID 特性标志、标志访问等。更多关于该项目的示例和信息可以在官方文档中找到:[Bitdefender 反汇编器](http://bddisasm.readthedocs.io) ## 项目 1. [bddisasm](https://github.com/bitdefender/bddisasm/tree/master/bddisasm) - 这是主要的反汇编器项目。为了使用 Bitdefender 反汇编器,你所要做的就是构建这个项目,并链接到输出的库。你需要的唯一头文件位于 `inc` 文件夹中。 2. [bdshemu](https://github.com/bitdefender/bddisasm/tree/master/bdshemu) - 该项目利用主要的 bddisasm 库,构建了一个简单、轻量、快速的指令模拟器,专门针对 shellcode。这个项目也被集成到了 disasmtool 中,因此你可以模拟原始二进制文件并查看其输出。请注意,这个简单的模拟器支持基本的 x86/x64 指令,但不支持模拟任何类型的 API 调用。此外,唯一支持的内存访问是在 shellcode 本身内部以及模拟的栈上。 3. [isagenerator_x86](https://github.com/bitdefender/bddisasm/tree/master/isagenerator) - 该项目包含指令定义和生成反汇编表所需的脚本。如果你想添加对新指令的支持,就是在这里添加。该项目会自动生成几个头文件(instructions.h、mnemonics.h、constants.h、table_\*.h),所以请确保不要手动编辑任何这些文件。你需要 Python 3 来运行生成脚本。 4. [disasmtool](https://github.com/bitdefender/bddisasm/tree/master/disasmtool) - 该项目是一个命令行反汇编工具,主要用作如何集成 bddisasm 和 bdshemu 库的示例。 5. [bindings](https://github.com/bitdefender/bddisasm/tree/master/bindings) - [python](https://github.com/bitdefender/bddisasm/tree/master/bindings/pybddisasm) 和 [Rust](https://github.com/bitdefender/bddisasm/tree/master/bindings/rsbddisasm) 的绑定。 ## 核心特性 1. 轻量级 - 使用 C 语言编写,没有外部依赖,不分配内存,且在设计上是线程安全的。 2. 快速 - 在 Intel® Core™ Ultra 7 165H 上使用 BDDISASM v3.0 测量: - 传统解码器:每条解码指令大约需要 240 个 CPU 时钟周期(每秒可解码约 1200 万条指令); - 精简解码器:每条解码指令大约需要 150 个 CPU 时钟周期(每秒可解码约 1700 万条指令) 3. 高鲁棒性 - 已通过内部 fuzzers 和著名的 [mishegos](https://github.com/trailofbits/mishegos) 工具测试。 4. 易于使用 - 只需包含主头文件 bddisasm.h,链接到 bddisasm 库,然后调用 NdDecode API 即可! 5. 完整 – 全面支持在现代 CPU 中实现的 Intel 和 AMD x86 指令,包括丰富的每指令元数据。 ## 构建和安装 ### 使用 [vcpkg](https://github.com/microsoft/vcpkg) 安装 bddisasm 最简单的方法是使用 [vcpkg](https://github.com/microsoft/vcpkg): ``` vcpkg install bddisasm ``` 这将安装 `bddisasm` 和 `bdshemu` 静态库。 请注意,vcpkg 上可用的版本可能并不总是最新版本。 ### 使用 CMake 这是使用该库的推荐方式。 前置条件: - CMake 3.16 或更新版本(旧版本可能有效,但未经测试) - Ninja(可选,但推荐使用,尤其是在 Windows 上) 构建过程已在 Linux 上的 GCC 和 Clang 以及 Windows 上的 MSVC 中进行了测试。请注意,Windows 内核构建目标仅在使用 [MSBuild](#Using-MSBuild-on-Windows) 时可用。 要构建项目,请运行: ``` mkdir build cmake -B build . cmake --build build ``` 这将构建 `bddisasm`、`bdshemu` 和 `disasmtool`。要跳过 `disasmtool` 的构建,请使用 `BDD_INCLUDE_TOOL=OFF` 配置 CMake: ``` mkdir build cmake -B build . -DBDD_INCLUDE_TOOL=OFF ``` 要安装项目,请使用: ``` cmake --build build --target install ``` 这将安装 `bddisasm` 和 `bdshemu` 静态库及其公共头文件。如果构建了 `disasmtool`,它也将被安装。根据安装位置,你可能需要以 root 用户身份运行该命令。 可选地,如果找到了 python 3 解释器,可以使用以下命令重新生成指令表: ``` cmake --build build --target isagenerator ``` 要禁用 `isagenerator` 目标,请使用 `BDD_INCLUDE_ISAGENERATOR=OFF` 配置 CMake。 安装完成后,CMake 项目可以使用 `find_package` 来查找该库: ``` find_package(bddisasm REQUIRED) ``` 提供了两个 target: - `bddisasm::bddisasm` - 这应该用于只需要解码器,而不需要 shellcode 模拟器的 target - `bddisasm::bdshemu` - 这应该用于需要 shellcode 模拟器的 target(注意,它会自动引入 `bddisasm::bddisasm`) 无需手动设置 include 或链接目录,只需将 `target_link_libraries` 与所需的 target 一起使用,例如: ``` find_package(bddisasm REQUIRED) # decoder-tool 只需要 decoder 库 target_link_libraries(decoder-tool PRIVATE bddisasm::bddisasm) # emulator-tool 需要 bdshemu target_link_libraries(emulator-tool PRIVATE bddisasm::bdshemu) ``` ### nd_vsnprintf_s 和 nd_memset 默认情况下,如果 `vsnprintf` 和 `memset` 函数可用,`nd_vsnprintf_s` 和 `nd_memset` 函数将直接由 `bddisasm` 实现。为了表明这一点,`BDDISASM_HAS_VSNPRINTF` 和 `BDDISASM_HAS_MEMSET` 将被添加到 `bddisasm` 的公共编译定义中。可以通过使用 `BDD_USE_EXTERNAL_VSNPRINTF=ON` 和 `BDD_USE_EXTERNAL_MEMSET=ON` 配置 CMake 来禁用此功能。 #### 作为子项目使用 该项目可以作为子项目使用,可以通过将其添加为 git submodule,或者使用 [CMake 的 FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html): 使用 `FetchContent`: ``` FetchContent_Declare( bddisasm GIT_REPOSITORY https://github.com/bitdefender/bddisasm GIT_TAG origin/master ) FetchContent_MakeAvailable(bddisasm) ``` 作为 git submodule: ``` # 假设 submodule 检出到 external/bddisasm add_subdirectory(external/bddisasm) ``` 在这两种情况下,都会提供 `bddisasm::bddisasm` 和 `bddisasm::bdshemu` target。 当作为子项目使用时,`disasmtool`、`isagenerator` 和 `install` 目标不可用。 ### 在 Linux 上使用 Make 在仓库根目录下运行 `make` 来构建项目。这只会构建 `bddisasm` 和 `bdshemu` 静态库,不包含 `disasmtool`。 运行 `make install` 来安装项目。根据安装位置,你可能需要以 root 用户身份运行该命令。 [nd_vsnprintf_s 和 nd_memset](#nd_vsnprintf_s-and-nd_memset) 将不会由 `bddisasm` 定义,集成者必须提供这些函数。 ### 在 Windows 上使用 MSBuild 为了在 Windows 上构建项目,你需要: - [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) 并带有使用 C++ 的桌面开发工作负载。 - [Windows SDK 10.0.18362.0](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk/)。 - [Python 3.7 或更新版本](https://www.python.org/downloads/release/python-373/)(可选) 当你第一次在 Visual Studio 中打开 `bddisasm.sln` 时,应该会提示你安装任何缺失的组件。 这应该足以构建 `bddisasm`、`bdshemu` 和 `disasmtool`。 对于 DebugKernel 和 ReleaseKernel 配置,需要 [WDK 1903](https://go.microsoft.com/fwlink/?linkid=2085767),以及 Windows Driver Kit Visual Studio 扩展(WDK 安装程序应该会自动处理)。 对于 `isagenerator`,需要 Python 3。 任何项目的构建都可以直接在 Visual Studio 中完成。 结果将位于仓库根目录的 bin 目录中。 [nd_vsnprintf_s 和 nd_memset](#nd_vsnprintf_s-and-nd_memset) 将不会由 `bddisasm` 定义,集成者必须提供这些函数。 ## 使用预编译的二进制文件 每个版本都会发布静态库,以及 `disasmtool` CLI 工具。 你可以使用 [GitHub Artifact Attestation](https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/) 验证这些产物: ``` # 验证一个组件 $ gh attestation verify disasmtool -o bitdefender # 或者整个 bundle $ gh attestation verify x86-windows-release.zip -o bitdefender ``` 这对于 bddisasm 2.1.4 或更老版本不可用。 请注意,这目前是一个 beta 功能(有关详细信息,请参阅 [Introducing Artifact Attestations–now in public beta](https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/))。 ## 解码 x86 指令 ### 解码 API 有 4 个解码函数,但在内部它们的作用都是一样的,尽管有些带有隐式参数: - `NDSTATUS NdDecode(INSTRUX *Instrux, const uint8_t *Code, uint8_t DefCode, uint8_t DefData)` - 仅当你不关心输入缓冲区的长度时才应使用此 API; - `NDSTATUS NdDecodeEx(INSTRUX *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode, uint8_t DefData);` - 从最大长度为 `Size` 的缓冲区解码指令; - `NDSTATUS NdDecodeEx2(INSTRUX *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode, uint8_t DefData, uint8_t DefStack, uint8_t PreferedVendor);` - 使用首选供应商解码指令; - `NDSTATUS NdDecodeWithContext(INSTRUX *Instrux, const uint8_t *Code, size_t Size, ND_CONTEXT *Context);` - 基础解码 API;输入参数 - `DefCode`、`DefData`、`DefStack`、`VendMode` 和 `FeatMode` 必须在调用此函数之前全部填充到 `Context` 结构中。在第一次解码调用之前,还应使用 `NdInitContext` 初始化 Context 结构。 请注意,默认情况下,解码使用默认供应商 `ND_VEND_ANY`(这意味着 bddisasm 将尽可能多地进行解码)。此外,默认的特性掩码是 `ND_FEAT_ALL`,这意味着 bddisasm 将乐观地尝试解码映射到宽 NOP 空间的指令(例如 MPX 或 CET)。如果必须更改这些参数,建议使用 `NdDecodeWithContext` API。 将解码后的指令转换为文本反汇编必须使用 `NdToText` API。bddisasm 仅支持 Intel、masm 风格的语法。 ### 精简解码 API 默认(传统)解码 API 提供了一个庞大的 `INSTRUX` 结构(大约 480 字节长),其中包含关于该指令的所有可能信息,包括所有的操作数。当需要更快的解码器性能和/或更小的 `INSTRUX` 时,可以使用精简解码 API: - `NDSTATUS NdDecodeMini(INSTRUX_MINI *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode)` - `NDSTATUS NdDecodeWithContextMini(INSTRUX_MINI *Instrux, const uint8_t *Code, size_t Size, ND_CONTEXT *Context);` `INSTRUX_MINI` 只有 64 字节长,并提供除操作数和元数据之外的所有核心指令信息。如果需要,可以通过以下新 API 访问操作数: - `NDSTATUS NdGetOperandMini(const INSTRUX_MINI *Instrux, ND_UINT8 Index, ND_OPERAND *Operand);` - 解码索引为 `Index` 的指令操作数 每种类型的元数据也可以使用 API 从 `INSTRUX_MINI` 中检索。例如,为了检索栈访问类型,可以使用 `NdGetStackAccessMini` API;为了检索有效模式,可以使用 `NdGetValidModesMini`,等等。请查阅 `bdx86_api_mini.h` 获取所有可用 API 的列表。 ### 示例 使用 bddisasm 非常简单。解码并打印指令的反汇编既快速又简单: ``` #include #include "bddisasm/disasmtypes.h" #include "bddisasm/bddisasm.h" int nd_vsnprintf_s( char *buffer, size_t sizeOfBuffer, size_t count, const char *format, va_list argptr ) { return vsnprintf(buffer, sizeOfBuffer, format, argptr); } void* nd_memset(void *s, int c, size_t n) { return memset(s, c, n); } int main() { INSTRUX ix; uint8_t code[] = { 0x48, 0x8B, 0x48, 0x28 }; NDSTATUS status = NdDecodeEx(&ix, code, sizeof(code), ND_CODE_64, ND_DATA_64); // Check if decoding failed. if (!ND_SUCCESS(status)) { printf("Decode failed with error %x!\n", status); return -1; } // Checking if the instruction is a MOV. if (ix.Instruction == ND_INS_MOV) { printf("The instruction is mov!\n"); } // Checking if memory is read by the instruction. if (!!(ix.MemoryAccess & ND_ACCESS_ANY_READ)) { printf("The instruction reads memory!\n"); } // Checking if the destination is the RCX register. if (ND_IS_OP_REG(&ix.Operands[0], ND_REG_GPR, 8, NDR_RCX)) { printf("The first op is RCX!\n"); } // Checking if the source is memory. if (ix.Operands[1].Type == ND_OP_MEM) { printf("Source is memory!\n"); if (ix.Operands[1].Info.Memory.HasSeg) { printf(" Segment reg: %d\n", ix.Operands[1].Info.Memory.Seg); } if (ix.Operands[1].Info.Memory.HasBase) { printf(" Base reg: %d\n", ix.Operands[1].Info.Memory.Base); } if (ix.Operands[1].Info.Memory.HasIndex) { printf(" Index reg: %d, scale: %d\n", ix.Operands[1].Info.Memory.Index, ix.Operands[1].Info.Memory.Scale); } if (ix.Operands[1].Info.Memory.HasDisp) { printf(" Displacement: 0x%llx\n", ix.Operands[1].Info.Memory.Disp); } } // Create the text disassembly for this instruction. char text[ND_MIN_BUF_SIZE]; NdToText(&ix, 0, sizeof(text), text); printf("Instruction: %s\n", text); return 0; } ``` 使用扩展 API 同样非常简单: ``` INSTRUX ix; ND_CONTEXT ctx; uint8_t code[] = { 0x48, 0x8B, 0x48, 0x28 }; // This has to be done only once. NdInitContext(&ctx); ctx.DefCode = ND_CODE_64; ctx.DefData = ND_DATA_64; ctx.DefStack = ND_STACK_64; ctx.VendMode = ND_VEND_ANY; ctx.FeatMode = ND_FEAT_ALL; // Use ND_FEAT_NONE, if you wish to see NOPs instead of MPX/CET/CLDEMOTE instructions. // From here one, the ctx can be reused for any number of NdDecodeWithContext calls. NDSTATUS status = NdDecodeWithContext(&ix, code, sizeof(code), &ctx); ... ``` 使用精简解码器也一样简单: ``` INSTRUX_MINI ix; ND_CONTEXT ctx; ND_OPERAND op; char text[ND_MIN_BUF_SIZE]; uint8_t code[] = { 0x48, 0x8B, 0x48, 0x28 }; // This has to be done only once. The same context can be used by both the legacy and mini API! NdInitContext(&ctx); ctx.DefCode = ND_CODE_64; ctx.DefData = ND_DATA_64; ctx.DefStack = ND_STACK_64; ctx.VendMode = ND_VEND_ANY; ctx.FeatMode = ND_FEAT_ALL; // Use ND_FEAT_NONE, if you wish to see NOPs instead of MPX/CET/CLDEMOTE instructions. // From here one, the ctx can be reused for any number of NdDecodeWithContextMini calls. NDSTATUS status = NdDecodeWithContextMini(&ix, code, sizeof(code), &ctx); ... // Getting the first operand. status = NdGetOperandMini(&ix, 0, &op); ... // Formatting the instruction. status = NdToTextMini(&ix, 0, sizeof(text), text); ... ``` ## 鸣谢 整个 Bitdefender HVI 团队。
标签:Bash脚本, DNS 反向解析, macOS, x86/x64, 反汇编器, 可视化界面, 客户端加密, 底层开发, 指令模拟器, 逆向工具