关于 dynamic-instrumentation
dynamic-instrumentation 是一项介绍基于 LLVM 的动态二进制插桩、跟踪和运行时监控的技能,面向分析或修改程序运行行为的开发者,也适用于无法访问源代码的情况。
主要功能
- 01动态二进制插桩
- 02指令与内存访问跟踪
- 03LLVM IR 插桩
- 04使用 XRay 进行运行时跟踪
- 05覆盖率与性能分析
- 06系统调用监控
适用场景
- 01在没有源代码时观察二进制程序
- 02跟踪指定指令或内存访问
- 03为模糊测试收集覆盖率
- 04分析运行时行为
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ gmh5225/ awesome-llvm-security --skill dynamic-instrumentation安装后在 Claude Code 中输入/dynamic-instrumentation确认已加载。
详细介绍
动态插桩技能
本技能介绍如何使用 LLVM 基础设施进行动态二进制插桩(DBI)、运行时跟踪和程序监控。
动态二进制插桩概述
什么是 DBI?
动态二进制插桩允许在运行时修改程序行为,无需访问源代码:
- 在任意位置插入分析代码
- 监控程序执行
- 修改控制流和数据
基于 LLVM 的 DBI 工具
- QBDI:QuarkslaB 动态二进制插桩
- Instrew:通过 LLVM 提升实现快速插桩
- binopt:二进制代码的运行时优化
QBDI(QuarkslaB DBI)
基本用法
#include <QBDI.h>
// Callback function for instrumentation
QBDI::VMAction onInstruction(QBDI::VMInstanceRef vm,
QBDI::GPRState *gprState,
QBDI::FPRState *fprState,
void *data) {
// Get current instruction info
const QBDI::InstAnalysis *inst = vm.getInstAnalysis();
printf("Executing: 0x%lx - %s %s\n",
inst->address,
inst->mnemonic,
inst->operandsStr);
return QBDI::VMAction::CONTINUE;
}
int main() {
QBDI::VM vm;
// Get current stack
uint8_t *fakestack;
QBDI::allocateVirtualStack(vm.getGPRState(), 0x100000, &fakestack);
// Add instrumentation callback
vm.addCodeCB(QBDI::PREINST, onInstruction, nullptr);
// Run target function
QBDI::rword retval;
vm.call(&retval, (QBDI::rword)targetFunction, {arg1, arg2});
return 0;
}
内存访问跟踪
QBDI::VMAction onMemoryAccess(QBDI::VMInstanceRef vm,
QBDI::GPRState *gprState,
QBDI::FPRState *fprState,
void *data) {
// Get memory accesses for current instruction
std::vector<QBDI::MemoryAccess> memAccesses = vm.getMemoryAccess();
for (const auto &access : memAccesses) {
const char* type = (access.type == QBDI::MEMORY_READ) ? "READ" : "WRITE";
printf("%s: addr=0x%lx, size=%d, value=0x%lx\n",
type, access.accessAddress, access.size, access.value);
}
return QBDI::VMAction::CONTINUE;
}
// Register callback for memory access events
vm.addMemAccessCB(QBDI::MEMORY_READ_WRITE, onMemoryAccess, nullptr);
指令过滤
// Only instrument specific instruction ranges
vm.addCodeRangeCB(startAddr, endAddr, QBDI::PREINST, callback, nullptr);
// Instrument specific modules
vm.addCodeAddrCB(targetAddr, QBDI::PREINST, callback, nullptr);
// Remove instrumentation dynamically
vm.deleteInstrumentation(callbackId);
Instrew - LLVM 提升 DBI
概念
Instrew 在运行时将二进制代码提升为 LLVM IR,从而支持:
- 对二进制代码进行高级优化
- 通过 LLVM Pass 高效插桩
- 通过修改实现 JIT 重新编译
架构
Binary → Rellume Lifter → LLVM IR → Custom Passes → JIT → Execute
↓
[Instrumentation Passes]
编译时插桩
LLVM IR 插桩 Pass
struct InstrumentationPass : public llvm::PassInfoMixin<InstrumentationPass> {
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
auto &Ctx = M.getContext();
// Declare instrumentation functions
auto *VoidTy = llvm::Type::getVoidTy(Ctx);
auto *Int64Ty = llvm::Type::getInt64Ty(Ctx);
auto *LogFuncTy = llvm::FunctionType::get(VoidTy, {Int64Ty}, false);
auto LogFunc = M.getOrInsertFunction("__log_bb", LogFuncTy);
for (auto &F : M) {
for (auto &BB : F) {
// Insert at beginning of each basic block
llvm::IRBuilder<> Builder(&*BB.getFirstInsertionPt());
auto *BBAddr = llvm::ConstantInt::get(
Int64Ty, reinterpret_cast<uint64_t>(&BB));
Builder.CreateCall(LogFunc, {BBAddr});
}
}
return llvm::PreservedAnalyses::none();
}
};
SanitizerCoverage
LLVM 内置覆盖率插桩:
# Enable coverage instrumentation
clang -fsanitize-coverage=trace-pc-guard source.c
# Edge coverage
clang -fsanitize-coverage=edge source.c
# Trace comparisons
clang -fsanitize-coverage=trace-cmp source.c
// Implement coverage callbacks
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return;
void *PC = __builtin_return_address(0);
printf("Edge: guard=%u, PC=%p\n", *guard, PC);
}
extern "C" void __sanitizer_cov_trace_pc_guard_init(
uint32_t *start, uint32_t *stop) {
static uint32_t N = 0;
for (uint32_t *x = start; x < stop; x++) {
*x = ++N;
}
printf("Total edges: %u\n", N);
}
运行时跟踪
函数跟踪
// Compile with: clang -finstrument-functions source.c
extern "C" {
void __cyg_profile_func_enter(void *func, void *caller) {
Dl_info info;
if (dladdr(func, &info)) {
printf("ENTER: %s\n", info.dli_sname);
}
}
void __cyg_profile_func_exit(void *func, void *caller) {
Dl_info info;
if (dladdr(func, &info)) {
printf("EXIT: %s\n", info.dli_sname);
}
}
}
XRay 插桩
LLVM 内置的插桩框架:
# Enable XRay
clang -fxray-instrument -fxray-instruction-threshold=1 source.c
// Custom XRay handler
[[clang::xray_always_instrument]]
void my_function() {
// Function will always be instrumented
}
// Runtime control
__xray_patch(); // Enable instrumentation
__xray_unpatch(); // Disable instrumentation
性能分析
基本块频率
struct BlockProfiler : public llvm::PassInfoMixin<BlockProfiler> {
llvm::PreservedAnalyses run(llvm::Function &F,
llvm::FunctionAnalysisManager &FAM) {
auto &BFI = FAM.getResult<llvm::BlockFrequencyAnalysis>(F);
for (auto &BB : F) {
auto Freq = BFI.getBlockFreq(&BB);
llvm::errs() << BB.getName() << ": " << Freq.getFrequency() << "\n";
}
return llvm::PreservedAnalyses::all();
}
};
采样分析器集成
// Use with perf or similar
// Map addresses back to source using debug info
void interpretProfile(const std::string &profilePath) {
// Parse profile data
// Map samples to LLVM IR/source locations
// Generate optimization hints
}
系统调用监控
SysCallStubber
拦截并监控系统调用:
// Hook system calls at LLVM IR level
struct SyscallMonitor : public llvm::PassInfoMixin<SyscallMonitor> {
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &MAM) {
for (auto &F : M) {
for (auto &BB : F) {
for (auto &I : BB) {
if (auto *Call = llvm::dyn_cast<llvm::CallInst>(&I)) {
if (isSyscallWrapper(Call)) {
instrumentSyscall(Call);
}
}
}
}
}
return llvm::PreservedAnalyses::none();
}
};
eBPF 集成
bpfcov - 使用 eBPF 进行代码覆盖
// eBPF program for coverage collection
SEC("uprobe/target_function")
int trace_function(struct pt_regs *ctx) {
u64 addr = PT_REGS_IP(ctx);
// Record coverage
u32 *count = bpf_map_lookup_elem(&coverage_map, &addr);
if (count) {
__sync_fetch_and_add(count, 1);
}
return 0;
}
污点跟踪
动态污点分析
// Shadow memory for taint tracking
class TaintTracker {
std::unordered_map<void*, TaintInfo> shadowMemory;
public:
void markTainted(void *addr, size_t size, TaintSource source) {
for (size_t i = 0; i < size; i++) {
shadowMemory[(char*)addr + i] = {source, true};
}
}
bool isTainted(void *addr) {
return shadowMemory.count(addr) && shadowMemory[addr].tainted;
}
void propagateTaint(void *dst, void *src, size_t size) {
for (size_t i = 0; i < size; i++) {
if (isTainted((char*)src + i)) {
markTainted((char*)dst + i, 1, shadowMemory[(char*)src + i].source);
}
}
}
};
最佳实践
- 最大限度减少开销:仅插桩必要的代码路径
- 缓冲事件:批量记录事件以减少 I/O
- 使用采样:完整跟踪开销较大,生产环境中应使用采样
- 线程安全:确保插桩具备线程安全性
- 符号解析:使用调试信息以生成有意义的输出
集成模式
模糊测试器集成
// Coverage-guided fuzzing with instrumentation
void fuzzerCallback(uint8_t *data, size_t size) {
// Reset coverage
__sanitizer_cov_reset_coverage();
// Run target
targetFunction(data, size);
// Collect coverage
uint8_t *coverage = __sanitizer_cov_get_coverage();
feedbackToFuzzer(coverage);
}
调试集成
// Breakpoint-like instrumentation
void onBreakpoint(void *addr, void *context) {
// Dump registers
// Inspect memory
// Allow continue/step
}
资源
有关相关工具和项目,请参阅 README.md 中的动态二进制插桩、监控和 eBPF 部分。
获取详细信息
需要详细且最新的资源链接、工具列表或项目参考时,请从以下位置获取最新数据:
https://raw.githubusercontent.com/gmh5225/awesome-llvm-security/refs/heads/main/README.md
此 README 包含经过整理的综合列表:
- 动态二进制插桩工具(DBI 部分)
- 运行时监控和跟踪工具(Monitor 部分)
- eBPF 相关项目和资源
评论
支持 Markdown