考试通知

LIEF Extended DWARF 实战指南:读取调试信息、附加外部调试文件与生成 C/C++ 声明

LIEF Extended DWARF 实战指南:读取调试信息、附加外部调试文件与生成 C/C++ 声明 逆向工程开发工具【免费下载链接】LIEFLIEF - Library to Instrument Executable Formats (C, Python, Rust)项目地址https://gitcode.com/gh_mirrors/li/LIEF点击查看免费下载本文是 LIEF Extended 中 DWARF 模块的使用指南围绕「加载与检查 DWARF」「附加外部调试文件」「生成 C/C 声明」「用 Editor 从零创建 DWARF 文件」四条主线展开。读完本文你将掌握如何用 Python / C / Rust 三种语言读取内嵌或独立的 DWARF 调试信息、定位函数/变量/类型的源码级信息、将外部调试文件与已解析二进制绑定并联动反汇编以及通过高层 API 程序化地生成新的 DWARF 调试文件。DWARF 调试信息与 LIEF ExtendedDWARF 是 ELF、Mach-O、PE 等可执行格式通用的源码级调试信息格式。LIEF Extended 可以在不依赖外部工具链的情况下读取 DWARF 中的函数function、变量variable、类型type与源码位置source location并支持从二进制内嵌的调试段或独立调试文件中解析 DWARF把外部调试文件绑定attach到已解析的二进制对象上将函数、变量、类型与编译单元渲染为 C/C 声明通过 DWARF Editor 创建全新的调试文件。需要说明的是DWARF 能力属于 LIEF Extended 独有功能普通版 LIEF 不包含且底层基于 LLVM用户无需自行编译集成 LLVM。LIEF Extended 的安装方式见 What is LIEF Extended?Python wheel、C/Rust SDK 的下载与LIEF_RUST_PRECOMPILED环境变量配置都在该页说明。DWARF 调试信息可以嵌入二进制文件内部也可以存放在单独的文件中。如果二进制已被strip剥掉了调试段则需改用「加载独立调试文件」或「附加外部调试文件」的方式。关于加载与关联调试文件的总体策略包括 DWARF 与 PDB 两种格式的选择表可参考 Debug Information。加载 DWARF内嵌调试信息与独立调试文件通过已解析的二进制访问内嵌 DWARF当调试信息内嵌在二进制中时可以直接通过二进制的debug_info属性拿到一个lief.dwarf.DebugInfo对象。注意该属性可能为None二进制没有调试信息时先判断再使用import lief elf lief.ELF.parse(/bin/with_debug) if debug_info : elf.debug_info: assert isinstance(debug_info, lief.dwarf.DebugInfo) print(fDWARF Debug handler: {debug_info})对应 C 写法LIEF::Binary::debug_info()返回LIEF::DebugInfo*可用LIEF::dwarf::DebugInfo::classof校验类型后向下转型#include LIEF/DWARF.hpp #include LIEF/ELF.hpp #include cassert auto elf LIEF::ELF::Parser::parse(/bin/with_debug); if (const LIEF::DebugInfo* info elf-debug_info()) { assert(LIEF::dwarf::DebugInfo::classof(info) Wrong debug type); const auto dwarf_dbg static_castconst LIEF::dwarf::DebugInfo(*info); }Rust 写法debug_info()返回可枚举的lief::DebugInfo匹配到Dwarf变体即为 DWARFlet elf lief::elf::Binary::parse(/bin/ls).unwrap(); if let Some(lief::DebugInfo::Dwarf(dwarf)) elf.debug_info() { // DWARF debug info }从源码实现看DebugInfo派生自LIEF::DebugInfo抽象基类format()返回FORMAT::DWARFclassof正是依据该格式标识做运行时类型判断见 DebugInfo.hpp。直接加载 DWARF 文件lief.dwarf.load无论调试信息是内嵌在二进制里还是以独立文件形式存在例如单独的.debug文件、.dwo文件都可以用lief.dwarf.load直接加载import lief dbg: lief.dwarf.DebugInfo | None lief.dwarf.load(/bin/with_debug) dbg: lief.dwarf.DebugInfo | None lief.dwarf.load(external_dwarf) dbg: lief.dwarf.DebugInfo | None lief.dwarf.load(debug.dwo)C 侧对应LIEF::dwarf::load即DebugInfo::from_file的内联封装见 DebugInfo.hppauto dbg LIEF::dwarf::load(/bin/with_debug); dbg LIEF::dwarf::load(external_dwarf); dbg LIEF::dwarf::load(debug.dwo);Rust 侧let dbg lief::dwarf::load(/bin/with_debug); let dbg lief::dwarf::load(external_dwarf); let dbg lief::dwarf::load(debug.dwo);使用上有两点注意事项macOS.dSYM包应传入Contents/Resources/DWARF/目录下的 DWARF 对象文件路径而不是.dSYM包本身。仓库测试 test_macho_dsym.py 专门覆盖了 Mach-O 配套.dSYM的加载场景。先检查返回值load返回可能为None在访问compilation_units、调用find_function/find_type之前务必判空避免空指针或空对象访问。遍历编译单元并查询函数、变量、类型拿到DebugInfo后核心交互入口是compilation_units编译单元迭代器。每个编译单元包含生产者信息producer、函数、变量与类型DebugInfo与CompilationUnit还提供按名称或地址查找的快捷接口dbg: lief.dwarf.DebugInfo for compilation_unit in dbg.compilation_units: print(compilation_unit.producer) for func in compilation_unit.functions: print(func.name, func.linkage_name, func.address) for var in compilation_unit.variables: print(var.name, var.address) for ty in compilation_unit.types: print(ty.name, ty.size) dbg.find_function(_ZNSi4peekEv) dbg.find_function(std::basic_istreamchar, std::char_traitschar ::peek()) dbg.find_function(0x137A70) dbg.find_variable(_ZNSt12out_of_rangeC1EPKc) dbg.find_variable(std::out_of_range::out_of_range(char const*)) dbg.find_variable(0x2773A0) dbg.find_type(my_type_t)C 侧逐项对应的写法名称、链接名、地址用log输出地址与大小均为optional/result语义取不到时回退为 0std::unique_ptrLIEF::dwarf::DebugInfo dbg; for (const LIEF::dwarf::CompilationUnit CU : dbg-compilation_units()) { log(Level::Info, Producer: {}, CU.producer()); for (const LIEF::dwarf::Function func : CU.functions()) { log(Level::Info, name{}, linkage{}, address{}, func.name(), func.linkage_name(), std::to_string(func.address().value_or(0))); } for (const LIEF::dwarf::Variable var : CU.variables()) { log(Level::Info, name{}, address{}, var.name(), std::to_string(var.address().value_or(0))); } for (const LIEF::dwarf::Type ty : CU.types()) { log(Level::Info, name{}, size{}, ty.name().value_or(), std::to_string(ty.size().value_or(0))); } } dbg-find_function(_ZNSi4peekEv); dbg-find_function(std::basic_istreamchar, std::char_traitschar ::peek()); dbg-find_function(0x137a70); dbg-find_variable(_ZNSt12out_of_rangeC1EPKc); dbg-find_variable(std::out_of_range::out_of_range(char const*)); dbg-find_variable(0x2773a0);Rust 侧let dbg lief::dwarf::load(path).unwrap_or_else(|| { process::exit(1); }); for cu in dbg.compilation_units() { println!(Producer: {}, cu.producer()); for func in cu.functions() { println!(name{}, linkage{}, address{}, func.name(), func.linkage_name(), func.address().unwrap_or(0)); } for var in cu.variables() { println!(name{}, address{}, var.name(), var.address().unwrap_or(0)); } for ty in cu.types() { println!(name{}, size{}, ty.name().unwrap_or(.to_string()), ty.size().unwrap_or(0)); } } dbg.function_by_name(_ZNSi4peekEv); dbg.function_by_name(std::basic_istreamchar, std::char_traitschar ::peek()); dbg.function_by_addr(0x137a70); dbg.variable_by_name(_ZNSt12out_of_rangeC1EPKc); dbg.variable_by_name(std::out_of_range::out_of_range(char const*)); dbg.variable_by_addr(0x137a70);结合头文件可以明确各查找接口的语义DebugInfo::find_function接受已 mangled 或已 demangle 的名字也接受虚拟地址见 DebugInfo.hpp。名字查找同时覆盖DW_AT_name与DW_AT_linkage_name两套属性所以_ZNSi4peekEvmangled和std::basic_istream...::peek()demangled都能命中地址查找则要求传入虚拟地址virtual address。CompilationUnit::functions()只迭代在该编译单元内有实体实现的函数例如被内联inline到main中的函数不会单独出现被导入但未在本单元实现的函数如printf由imported_functions()提供见 CompilationUnit.hpp。CompilationUnit还提供name()对应DW_AT_name、producer()对应DW_AT_producer、compilation_dir()对应DW_AT_comp_dir、language()对应DW_AT_language含语言与版本如 C17 的version17、low_address()/high_address()/ranges()等属性见 CompilationUnit.hpp。函数对象除名字与地址外还可获取size()、ranges()、debug_location()源码文件与行号结构体定义见 debug_loc.hpp、返回类型type()、参数列表parameters()、lexical_blocks()等见 Function.hpp。变量对象则区分静态变量返回虚拟地址与栈变量返回相对帧基寄存器的偏移并提供location()、locations()等位置信息见 Variable.hpp。仓库还提供了可直接运行/改写的完整示例 dwarf_inspect.cpp其遍历逻辑与上文 C 片段一致可作为最小可运行模板。测试 test_misc.py、test_vars.py、test_types.py 也覆盖了这些查询接口。附加外部调试文件并与反汇编联动当调试信息不在二进制内部、而是独立文件时可以把调试文件绑定到已解析的二进制对象上接口是Binary::load_debug_infobinary: lief.Binary dbg binary.load_debug_info(/home/romain/dev/LIEF/some.dwo)C 侧std::unique_ptrLIEF::Binary binary; binary-load_debug_info(/home/romain/dev/LIEF/some.dwo);Rust 侧let bin: mut dyn lief::generic::Binary some_bin; let path PathBuf::from(/home/romain/dev/LIEF/some.dwo); bin.load_debug_info(path);注意三点关键语义原文档与 Binary.hpp 中均有说明必须使用与二进制同一构建产出的调试文件。load_debug_info本身不校验build ID / GUID / PDB 标识是否匹配错误配套可能导致解析结果不一致或无效。附加调试文件只是更新 LIEF 的分析对象不会把 DWARF 段插入可执行文件。绑定后binary.disassemble(函数名)即可按名字反汇编——函数的位置地址/大小来自调试文件机器码仍从二进制本体读取binary.load_debug_info(/home/romain/dev/LIEF/some.dwo) # The location (address/size) of my_function is defined in some.dwo for inst in binary.disassemble(my_function): print(inst)Cbinary-load_debug_info(/home/romain/dev/LIEF/some.dwo); // The location (address/size) of my_function is defined in some.dwo for (const LIEF::assembly::Instruction inst : binary-disassemble(my_function)) { std::cout inst \n; }Rust注意方法名不同为disassemble_symbolbin.load_debug_info(path); // The location (address/size) of my_function is defined in some.dwo for inst in bin.disassemble_symbol(my_function) { println!({inst}); }Binary::disassemble的重载族支持按地址0xacde、按地址大小、按符号名、按内存缓冲区反汇编见 Binary.hpp与调试信息联动的是按符号名/函数名这一形式。调试文件还可以来源于逆向框架的分析结果仓库提供 BinaryNinja DWARF 导出插件菜单Plugins LIEF Export as DWARF另附命令行工具lief-tool-dwarf-export-linux-x86_64 input.bndb --output out.dwarf与 Ghidra DWARF 导出插件Project Manager 的Export Format DWARF、CodeBrowser 的 LIEF 菜单或 Java APIManager.export(output)可基于这两款框架的分析结果生成调试信息再配合本节的方法绑定到二进制上使用。生成 C/C 声明to_declDWARF 中的函数、变量、类型与编译单元都可以直接渲染为 C/C 声明文本对应四个接口Function::to_decl、Variable::to_decl、Type::to_decl、CompilationUnit::to_decl。输出风格通过lief.DeclOpt配置例如偏好 C 语法、调整缩进dbg lief.dwarf.load(/bin/with_debug) func dbg.find_function(main) print(func.to_decl()) opt lief.DeclOpt() opt.is_cpp True opt.indentation 4 for cu in dbg.compilation_units: # Emit the definition of the functions of the compilation unit print(cu.to_decl(opt))Cauto dbg LIEF::dwarf::load(/bin/with_debug); std::unique_ptrLIEF::dwarf::Function func dbg-find_function(main); std::cout func-to_decl() \n; LIEF::DeclOpt opt; opt.is_cpp(true).indentation(4); for (const LIEF::dwarf::CompilationUnit CU : dbg-compilation_units()) { std::cout CU.to_decl(opt) \n; }RustDeclOpt以结构体方式构造to_decl_with_opt显式传入配置let dbg lief::dwarf::load(/bin/with_debug).unwrap(); if let Some(func) dbg.function_by_name(main) { println!({}, func.to_decl()); } let opt lief::DeclOpt { is_cpp: true, indentation: 4, ..Default::default() }; for cu in dbg.compilation_units() { println!({}, cu.to_decl_with_opt(opt)); }DeclOpt 配置项详解DeclOpt是调试信息DWARF/PDB翻译为声明时统一的配置结构见 DebugDeclOpt.hpp常用配置项如下配置项类型默认作用indentationuint32_t见实现代码缩进使用的空格数is_cppboolfalse优先使用 C 语法如bool关键字而非 C 语法show_extended_annotationsboolfalse生成注释形式的底层细节内存地址、偏移、类型大小、原始源码位置include_typesboolfalse输出结构体、枚举、联合等类型的完整定义include_localsboolfalse在函数体中列出局部/栈变量desugarboolfalse是否把 typedef/类型别名展开为其底层规范类型如uint32_t变为unsigned intshow_field_offsetsboolfalse结构体每个成员前标注字节偏移如/* 0x00 */ int A;type_aliases/add_type_aliasmapstring,string空类型名到友好别名的映射如把std::basic_string...别名为std::stringtarget_triplestring空LLVM target如aarch64-unknown-linux-gnu决定内建类型大小如long与注释中的寄存器名为空时从关联二进制推断声明生成的实际效果可参考仓库测试 test_to_decl.py 中的断言例如对反调试样本生成/* * Address: 0x1ed4 */ jint JNI_OnLoad(JavaVM *vm, void *reserved) { /* Start: 0x001eec */ { /* Start: 0x001ef8 */ { Call the JNI_OnLoad from the payload loaded by the first DT_INIT_ARRAY constructor } /* End: 0x001efc */ } /* End: 0x001f10 */ }以及开启is_cpp后变量生成的带注释声明Addr、size注解即由show_extended_annotations类能力产生/* * pointer to the r_debug structure defined in the linker(64) * Addr: 0xabc8 * size: 0x0008 */ static r_debug_t *linker64_r_debug;DWARF Editor从零创建 DWARF 文件重要限制LIEF 目前不支持修改已有的 DWARF 文件。Editor 只用于从零创建新的 DWARF 文件。Editor 通过lief.dwarf.Editor.from_binary实例化——它从二进制对象推断目标格式ELF/PE/Mach-O与架构从而生成与之匹配的 DWARF 文件import lief pe lief.PE.parse(demo.exe) assert isinstance(pe, lief.PE.Binary) editor lief.dwarf.Editor.from_binary(pe)Cstd::unique_ptrLIEF::PE::Binary pe LIEF::PE::Parser::parse(demo.exe); std::unique_ptrLIEF::dwarf::Editor editor LIEF::dwarf::Editor::from_binary(*pe);Rustlet mut bin lief::pe::Binary::parse(path).unwrap(); let editor lief::dwarf::Editor::from_binary(mut bin);Editor还提供create(FORMAT, ARCH)手工指定格式与架构的构造方式其支持的格式与架构在头文件中声明为FORMAT::{ELF, MACHO, PE}与ARCH::{UNKNOWN, X64, X86, AARCH64, ARM}见 Editor.hpp。核心方法为create_compilation_unit()与write(output)。拿到Editor后创建一个或多个编译单元editor.CompilationUnit再由编译单元创建其所属的函数、变量、类型对象editor: lief.dwarf.Editor unit editor.create_compilation_unit() unit.set_producer(LIEF) func unit.create_function(hello) func.set_address(0x123) struct_ptr unit.create_structure(my_struct_t).pointer_to() assert isinstance(struct_ptr, lief.dwarf.editor.PointerType) func.set_return_type(struct_ptr) var func.create_stack_variable(local_var) var.set_stack_offset(8) editor.write(/tmp/out.debug)Cstd::unique_ptrLIEF::dwarf::Editor editor; std::unique_ptrLIEF::dwarf::editor::CompilationUnit unit editor-create_compilation_unit(); unit-set_producer(LIEF); std::unique_ptrLIEF::dwarf::editor::Function func unit-create_function(hello); func-set_address(0x123); func-set_return_type(*unit-create_structure(my_struct_t)-pointer_to()); std::unique_ptrLIEF::dwarf::editor::Variable var func-create_stack_variable(local_var); var-set_stack_offset(8); editor-write(/tmp/out.debug);Rustlet editor: mut lief::dwarf::Editor some_editor; let mut unit editor.create_compile_unit().unwrap(); unit.set_producer(LIEF); let mut func unit.create_function(hello).unwrap(); func.set_address(0x123); func.set_return_type(unit.create_structure(my_struct_t).pointer_to()); let mut var func.create_stack_variable(local_var); var.set_stack_offset(8); editor.write(/tmp/out.debug);Editor 的对象模型与可用方法Editor 的对象模型分四层Editor→editor.CompilationUnit→editor.Function/editor.Variable/editor.Type类型还有PointerType、StructType、BaseType、EnumType、ArrayType、FunctionType、TypeDef等子类。各层可用的核心方法如下editor.CompilationUnit见 editor/CompilationUnit.hppset_producer(name)设置DW_AT_producercreate_function(name)/create_variable(name)创建函数与全局变量类型工厂create_generic_typeDW_TAG_unspecified_type、create_enum枚举、create_typedef(name, type)、create_structure(name, kind)kind 可为 STRUCT / CLASS / UNION、create_base_type(name, size, encoding)、create_function_type(name)、create_pointer_type(type)、create_const_type(type)、create_volatile_type(type)、create_void_type()、create_array(name, type, count)。editor.Function见 editor/Function.hppset_address(addr)设置DW_AT_entry_pcset_low_high(low, high)设置DW_AT_low_pc/DW_AT_high_pc假设函数连续set_ranges(ranges)设置DW_AT_ranges用于非连续函数set_external()标记为外部导入函数set_return_type(type)、add_parameter(name, type)返回Parameter可assign_register指定寄存器create_stack_variable(name)、add_lexical_block(start, end)DW_TAG_lexical_block、add_label(addr, label)DW_TAG_label、add_description(...)。editor.Variable见 editor/Variable.hppset_addr(address)静态全局变量的绝对地址set_stack_offset(offset)栈变量专用设置相对帧基的偏移内部写入DW_AT_locationset_location(location)、set_type(type)、set_external()、add_description(...)。测试 test_editor.py 展示了更完整的创建流程包括用set_low_high/set_ranges描述函数地址范围、创建带成员的结构体add_member(next, struct.pointer_to())与带偏移的重载add_member(name, type, 8)、union/class 类结构create_structure(name, StructType.TYPE.UNION)、函数类型create_function_typeadd_parameterset_return_type、枚举与位域add_value/add_bitfield等。可运行示例见 dwarf_editor.cpp。编辑器对象产出的文件可用第一节的lief.dwarf.load重新加载验证。与 BinaryNinja / Ghidra 插件联动DWARF Editor 的能力同时以插件形式提供给两款主流逆向框架实现「框架分析 → 导出 DWARF → 供 LIEF/其他工具消费」的闭环BinaryNinja - DWARF PluginPlugins LIEF Export as DWARF导出或使用独立工具lief-tool-dwarf-export-linux-x86_64 input.bndb --output out.dwarf除常规函数/类型/参数外还额外导出栈变量与基本块。Ghidra - DWARF PluginProject Manager 右键Export Format DWARFCodeBrowser 的 LIEF 菜单或 headless 脚本中Manager.export(output)调用 Java API。API 参考各语言 DWARF 模块的完整 API 文档见 extended/dwarf 下的分语言页面Python APIlief.dwarf.load、DebugInfo、CompilationUnit、Function、Parameter、Variable、Location及其子类RegisterLoc、AddressLoc、FrameBaseLoc、RegisterOffsetLoc、ExpressionLoc、UnavailableLoc、CompositeLocation、Type及全部类型子类Array、Base、ClassLike、Pointer、Typedef、Enum、Const、Volatile、Reference、Subroutine 等、Editor与editor命名空间下各可编辑类型C APILIEF::dwarf::load与对应类族Rust APIlief::dwarf模块。共享的DeclOpt、DebugInfo、debug_location_t通用接口文档在 Debug Information两格式DWARF 与 PDB共用同一套声明生成与调试信息抽象。赞分享逆向工程开发工具【免费下载链接】LIEFLIEF - Library to Instrument Executable Formats (C, Python, Rust)项目地址https://gitcode.com/gh_mirrors/li/LIEF点击查看免费下载相关推荐LIEF C DWARF API 全指南加载解析、生成声明与程序化创建调试信息LIEF C DWARF API 全指南加载解析、生成声明与程序化创建调试信息 LIEFLibrary to Instrument Executable逆向工程开发工具LIEF 调试信息统一接口DWARF/PDB 的加载、绑定与跨语言 API 实战指南LIEF 调试信息统一接口DWARF/PDB 的加载、绑定与跨语言 API 实战指南 调试信息Debug Information把机器码与源代码级的函数名逆向工程开发工具猫抓资源嗅探插件网页视频下载完整指南猫抓资源嗅探插件网页视频下载完整指南 如果右键另存为为什么总是抓不到那个视频视频源很多是一串 M3U8 流里的临时链接右键直接另存不了。猫抓cat ca音视频上一篇FFXIVChnTextPatch终极汉化教程3分钟让国际服变中文界面下一篇nginx-admins-handbookNGINX安全加固终极清单31项措施全面防御DoS、XSS与缓冲区溢出创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
← 返回资讯列表 预约报考咨询 →
NEXT STEP

看完公告,下一步怎么走?

把报考交给靠谱的人:材料预审、批次抢报、考前辅导、复审提醒,全程有人跟。

进入报考专题