bddisasm

bitdefenderApache-2.0活跃维护

快速轻量的 x86/x64 指令解码器,附带基础指令模拟器,专门用来识别 shellcode 之类的可疑代码行为。

bddisasm is a fast, lightweight, x86/x64 instruction decoder. The project also features a fast, basic, x86/x64 instruction emulator, designed specifically to detect shellcode-like behavior.

v3.1.2发布于 2026-11-03 15:58 UTC1,088star122fork同步于 2026-11-10 11:50 UTC

怎么上手

由 README 提炼,执行前请确认
vcpkg install bddisasm
或:mkdir build && cmake -B build . && cmake --build build
硬件要求按 README 说明

README 未说明具体硬件要求,仅提及性能在 Intel Core Ultra 7 165H 上测得;库本身可在用户态、内核、hypervisor 及 Windows/Linux 环境运行。

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

轻量无依赖的 x86/x64 指令解码库,附带针对 shellcode 的简易模拟器

快速轻量的 x86/x64 指令解码器,附带基础指令模拟器,专门用来识别 shellcode 之类的可疑代码行为。

核心能力

  • 纯 C 实现,无外部依赖、不分配内存、线程安全
  • 提供 legacy 与 mini 两套解码 API,mini 结构仅 64 字节
  • 解码结果含操作数、访问模式、CPUID 特性与标志位等元数据
  • bdshemu 可模拟基础 x86/x64 指令以识别 shellcode 行为
  • 附带 isagenerator 脚本,用 Python 3 生成指令表以扩展指令支持

容易踩的坑

vcpkg 上的版本可能不是最新;bdshemu 不支持模拟 API 调用,内存访问仅限 shellcode 自身和模拟栈;disasmtool 仅供演示测试,不保证接口稳定,勿用于生产

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v3.1.2
  1. v3.1.2最新正式版2026-11-03 · 2026-11-03 15:58 UTC
    • disasmtool: several minor improvements & tweaks around error and output file handling.
    • disasmtool: the emulator decoded output file requires the explicit -out file argument in the command line. The -skipdecoded option is now deprecated.
    • disasmtool: limited the input file size to 512M, to ensure enough memory can be allocated for the shellcode & internal buffer even on 32-bit builds.
  2. v3.1.12026-11-03 · 2026-11-03 09:40 UTC
    查看更新说明

    disasmtool: truncate hex buffer input size to 4096 bytes. This avoids a potential crash when the command line is too long.

  3. v3.1.02026-10-14 · 2026-10-14 07:10 UTC
    查看更新说明

    Removed the Python & Rust bindings, as they are no longer supported or maintained.

  4. v3.0.22026-10-06 · 2026-10-06 09:36 UTC
    查看更新说明

    Fixed displacement decoding for instructions using 16-bit and displacement only addressing.

  5. v3.0.12026-02-19 · 2026-02-19 13:34 UTC
    查看更新说明

    Added missing header files to BDDISASM_PUBLIC_HEADERS.

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    The Bitdefender disassembler

    The Bitdefender disassembler (bddisasm) is a lightweight, x86/x64 only instruction decoder. It is easy to integrate, easy to work with, it has no external dependencies, it is thread-safe, it allocates no memory at all, it works in virtually any environment (we use it inside user, kernel, hypervisor, on both Windows and Linux environments), and it provides lots of info regarding the decoded instructions, such as: operands (both explicit and implicit), access mode for each operand, CPUID feature flag, flags access, etc. More examples and info about the project can be found on the official documentation: Bitdefender disassembler

    Projects

    1. bddisasm - this is the main disassembler project. In order to use the Bitdefender disassembler, all you have to do is build this project, and link with the output library. The only headers you need are located inside the inc folder.
    2. bdshemu - this project makes use of the main bddisasm lib in order to build a simple, lightweight, fast, instructions emulator, designated to target shellcodes. This project is also integrated inside the disasmtool, so you can emulate raw binary files, and see their output. Note that this simple emulator supports basic x86/x64 instructions, and does not support emulating any kind of API call. In addition, the only supported memory accesses are inside the shellcode itself, and on the emulated stack.
    3. isagenerator_x86 - this project contains the instruction definitions and the scripts required to generate the disassembly tables. If you wish to add support for a new instruction, this is the place. This project will automatically generate several header files (instructions.h, mnemonics.h, constants.h, table_*.h), so please make sure you don't manually edit any of these files. You will need Python 3 to run the generation scripts.
    4. disasmtool - this project provides a command-line disassembler intended solely to demonstrate & test the bddisasm and bdshemu libraries integration. It is not intended for production or security-sensitive use. Its behavior and interfaces may change without notice, including through breaking changes.

    Core Features

    1. Lightweight - it's written in C, with no external dependencies, no memory allocated, and thread safe by design.
    2. Fast - measured on an Intel® Core™ Ultra 7 165H using BDDISASM v3.0:
      • legacy decoder: around 240 CPU clocks per decoded instruction (~12M decoded instructions per second);
      • mini decoder: around 150 CPU clocks per decoded instruction (~17M decoded instructions per second)
    3. Resilient - tested against internal fuzzers and the famous mishegos tool.
    4. Easy to work with - just include the main header file, bddisasm.h, link with the bddisasm library, and call the decode API!
    5. Complete – comprehensive support for Intel and AMD x86 instructions implemented in modern CPUs, including rich per-instruction metadata.

    Build and install

    Using vcpkg

    The easiest way of installing bddisasm is with vcpkg:

    vcpkg install bddisasm
    

    This will install bddisasm and bdshemu static libraries.

    Note that the version available on vcpkg may not always be the latest.

    Using CMake

    This is the recommended way of using the library.

    Prerequesites:

    • CMake 3.16 or newer (older version may work, but they have not been tested)
    • Ninja (optional, but recommended, especially on Windows)

    The build process was tested with GCC and Clang on Linux and MSVC on Windows. Note that the Windows kernel build target is available only when using MSBuild.

    To build the project run:

    mkdir build
    cmake -B build .
    cmake --build build
    

    This will build bddisasm, bdshemu, and disasmtool. For skipping the disasmtool build configure CMake with BDD_INCLUDE_TOOL=OFF:

    mkdir build
    cmake -B build . -DBDD_INCLUDE_TOOL=OFF
    

    To install the project use:

    cmake --build build --target install
    

    This will install the bddisasm and bdshemu static libraries and their public headers. If disasmtool was built it will also be installed. Depending on the install location you may need to run the command as root.

    Optionally, if a python 3 interpreter is found the instruction tables can be regenerated with:

    cmake --build build --target isagenerator
    

    To disable the isagenerator target configure CMake with BDD_INCLUDE_ISAGENERATOR=OFF.

    Once installed, CMake projects can use find_package to find the library:

    find_package(bddisasm REQUIRED)
    

    Two targets are provided:

    • bddisasm::bddisasm - this should be used for targets that need only the decoder, without the shell code emulator
    • bddisasm::bdshemu - this should be used for targets that need the shell code emulator (note that it will pull in bddisasm::bddisasm automatically)

    There is no need to manually set include or link directories, simply use target_link_libraries with the needed target, for example:

    find_package(bddisasm REQUIRED)
    
    # decoder-tool needs only the decoder library
    target_link_libraries(decoder-tool PRIVATE bddisasm::bddisasm)
    
    # emulator-tool needs bdshemu
    target_link_libraries(emulator-tool PRIVATE bddisasm::bdshemu)
    

    nd_vsnprintf_s and nd_memset

    By default, if vsnprintf and memset functions are available, the nd_vsnprintf_s and nd_memset functions are implemented directly by bddisasm. To signal this, BDDISASM_HAS_VSNPRINTF and BDDISASM_HAS_MEMSET will be added to the public compile definitions of bddisasm. This can be disabled by configuring CMake with BDD_USE_EXTERNAL_VSNPRINTF=ON and BDD_USE_EXTERNAL_MEMSET=ON.

    Using as a sub-project

    The project can be consumed as a sub-project, either by adding it as a git submodule, or by using CMake's FetchContent:

    With FetchContent:

    FetchContent_Declare(
        bddisasm
        GIT_REPOSITORY https://github.com/bitdefender/bddisasm
        GIT_TAG origin/master
    )
    
    FetchContent_MakeAvailable(bddisasm)
    

    As a git submodule:

    # Assuming the submodule is checked out at external/bddisasm
    add_subdirectory(external/bddisasm)
    

    In both cases the bddisasm::bddisasm and bddisasm::bdshemu targets will be provided.

    When used as a sub-project the disasmtool, isagenerator, and install targets are not available.

    Using Make on Linux

    To build the project run make in the root of the repository. This will build only the bddisasm and bdshemu static libraries, without disasmtool.

    To install the project run make install. Depending on the install location you may need to run the command as root.

    nd_vsnprintf_s and nd_memset will not be defined by bddisasm, integrators must provide these functions.

    Using MSBuild on Windows

    In order to build the projects on Windows you need:

    When you first open bddisasm.sln in Visual Studio should prompt you to install any missing components. This should be enough to build bddisasm, bdshemu, and disasmtool.

    For the DebugKernel and ReleaseKernel configurations, WDK 1903 is needed, alongside the Windows Driver Kit Visual Studio extension (the WDK installer should take care of this).

    For isagenerator, Python 3 is needed.

    Building any of the projects is done directly from Visual Studio.

    The results will be in the bin directory in the root of the repository.

    nd_vsnprintf_s and nd_memset will not be defined by bddisasm, integrators must provide these functions.

    Using pre-compiled binaries

    Each release publishes static libraries, as well as the disasmtool CLI tool.

    You can verify these artifacts using GitHub Artifact Attestation:

    # Verify a component
    $ gh attestation verify disasmtool -o bitdefender
    # Or the entire bundle
    $ gh attestation verify x86-windows-release.zip -o bitdefender
    

    This is not available for bddisasm 2.1.4 or older.

    Note that this is currently a beta feature (see Introducing Artifact Attestations–now in public beta for details).

    Decoding x86 instructions

    Decoding API

    There are 4 decoding functions, but internally, they all do the same, albeit some of them with implicit arguments:

    • NDSTATUS NdDecode(INSTRUX *Instrux, const uint8_t *Code, uint8_t DefCode, uint8_t DefData) - this API should be used only if you don't care about the length of the input buffer (which must be at least 15 bytes long);
    • NDSTATUS NdDecodeEx(INSTRUX *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode, uint8_t DefData); - decode instruction from a buffer with maximum length Size;
    • NDSTATUS NdDecodeEx2(INSTRUX *Instrux, const uint8_t *Code, size_t Size, uint8_t DefCode, uint8_t DefData, uint8_t DefStack, uint8_t PreferedVendor); - decode instructions with a preferred vendor;
    • NDSTATUS NdDecodeWithContext(INSTRUX *Instrux, const uint8_t *Code, size_t Size, ND_CONTEXT *Context); - base decode API; the input parameters - DefCode, DefData, DefStack, VendMode and FeatMode must all be filled in the Context structure before calling this function. The Context structure should also be initialized using NdInitContext before the first decode call.

    Note that by default, the default vendor ND_VEND_ANY is used for decoding (which means that bddisasm will try to decode as much as possible). Also, the default features mask is ND_FEAT_ALL, meaning that bddisasm will optimistically try to decode instructions which are mapped onto the wide NOP space as well (for example, MPX or CET). If these parameters must be changed, it is advised to use the NdDecodeWithContext API.

    Converting decoded instructions to textual disassembly must be done using the NdToText API. bddisasm only supports Intel, masm-style syntax.

    Minimal Decoding API

    The default (legacy) decoding API provides a large INSTRUX structure (around 480-bytes long) which contains all the possible information about the instruction, including all of the operands. When faster decoder performance and/or smaller INSTRUX is needed, the minimal decode API can be used:

    • 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);

    The INSTRUX_MINI is only 64-bytes long, and provides all the core instruction information, except for the operands and metadata. If needed, operands can be accessed via the following new API:

    • NDSTATUS NdGetOperandMini(const INSTRUX_MINI *Instrux, ND_UINT8 Index, ND_OPERAND *Operand); - decodes instruction operand at index Index

    Each type of metadata can also be retrieved from an INSTRUX_MINI using API. For example, in order to retrieve the stack access type, NdGetStackAccessMini API can be used; to retrieve the valid modes, NdGetValidModesMini can be used, etc. Consult bdx86_api_mini.h for a list of all available APIs.

    Example

    Working with bddisasm is very easy. Decoding and printing the disassembly of an instruction is quick & simple:

    #include <stdio.h>
    #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;
    }
    

    Working with the extended API is also trivial:

        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);
        ...
    

    Working with the minimal decoder is equally simple:

        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);
        ...
    

    Credits

    The entire Bitdefender HVI team.