ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

QSYM 的 EFLAGS 符号化:如何精确模拟 CPU 状态标志

QSYM 的 EFLAGS 符号化:如何精确模拟 CPU 状态标志 QSYM 的 EFLAGS 符号化如何精确模拟 CPU 状态标志【免费下载链接】qsymQSYM: A Practical Concolic Execution Engine Tailored for Hybrid Fuzzing项目地址: https://gitcode.com/gh_mirrors/qs/qsymQSYM 是一个面向混合模糊测试Hybrid Fuzzing的实用混合执行引擎Concolic Execution Engine而EFLAGS 符号化正是它区别于普通符号执行工具的核心技术之一。简单来说QSYM 会把 CPU 的状态标志寄存器EFLAGS转换成符号表达式从而精确模拟分支跳转背后的真实原因。本文将从零开始用通俗的语言讲清 QSYM 如何一步步建模这些标志位并附上源码位置方便你边读边查。什么是 EFLAGSCPU 状态标志速览 在 x86 架构中CPU 执行算术与逻辑运算后会把结果的特征写入一个叫EFLAGS的状态标志寄存器。常见标志位如下标志位名称含义CF进位标志无符号运算是否发生进位/借位PF奇偶标志结果低 8 位中 1 的个数是否为偶数AF辅助进位标志低 4 位是否发生进位ZF零标志结果是否为 0SF符号标志结果是否为负数OF溢出标志有符号运算是否发生溢出 这些标志位组合起来决定了jb、jl、jo、jz等条件跳转指令是否成立。模糊测试工具只有读懂了这些标志才能理解程序的分支逻辑。在 QSYM 的源码 flags.h 中这些标志被定义为EFLAGS_CF、EFLAGS_PF、EFLAGS_ZF等枚举常量每一位都对应真实寄存器中的一个 bit 位置。为什么说 EFLAGS 符号化是符号执行的拦路虎很多初学符号执行的人会问条件跳转不是直接比较两个操作数就行了吗为什么要大费周章去建模标志位问题在于真实指令会同时改写多个标志位比如add一条指令就可能影响 CF、PF、AF、ZF、SF、OF 六个标志不同指令改写的标志集合不同inc不改 CFbt只改 CFshl/shr的结果还依赖移位次数条件跳转依赖的是标志的当前值而不是某个操作数本身。如果简化处理把分支条件当成目标值直接比较就会丢失路径约束的精度导致生成的测试用例无法真正触发目标分支。QSYM 的 EFLAGS 符号化方案正是为了解决这一精度问题而设计的。QSYM 的建模思路从 QEMU 借鉴的 OpKind 分类法 ️QSYM 没有为每条 x86 指令单独写一套标志逻辑而是把它们归纳为几类典型运算也就是源码中的OpKindCC_OP_ADD / CC_OP_SUB / CC_OP_LOGIC / CC_OP_INC / CC_OP_DEC CC_OP_SHL / CC_OP_SHR / CC_OP_ROR / CC_OP_ROL CC_OP_SMUL / CC_OP_UMUL / CC_OP_BT这套分类方法直接借鉴自 QEMU见 flags.h 中的注释 from QEMU。每一类运算都预先声明了两组信息受影响标志Affected该运算会重新计算的标志未定义标志Undefined该运算后结果不可预测、应视为失效的标志。例如CC_OP_BT只影响 CF其余标志全部未定义而CC_OP_INC影响 PF/ZF/SF/OF但不碰 CF。这种精细分类让 QSYM 可以在运行时快速判断当前哪些标志是可信的。五个核心标志的符号计算逻辑 在 flags.cpp 中每种运算类型都对应一个FlagOperation子类如AddFlagOperation、SubFlagOperation、ShlFlagOperation等各自实现computeCF、computeOF、computeSF、computeZF、computePF。核心思路如下CF加法结果 源操作数无符号比较即result srcOF加法~(src ^ dst)的最高位 与(res ^ dst)的最高位同时为 1SF结果符号位为 1即结果小于 0ZF结果等于 0PF结果低 8 位逐位异或后等于 0即 1 的个数为偶数。 每条计算都生成一个符号表达式ExprRef交给底层的 Z3 求解器处理。这样分支条件就能与输入字节建立精确的数学联系。快慢两条路径加速条件跳转的求解 ⚡QSYM 在处理条件跳转Jcc时采用了先快后慢的两级策略对应源码中的computeFastJcc和computeSlowJcc快速路径如果最近一条操作是CC_OP_SUB例如cmp那么jb、jl、jz等跳转可以直接改写为dst src、dst src带符号、dst src这类简单比较一步生成约束性能极高慢速路径如果无法快速转化则按标志位逐个回溯调用computeFlag找到最近一次产生该标志的运算再调用对应的computeCF/computeOF等组合出完整条件。入口函数computeJcc见 flags.cpp会先检查标志有效性再依次尝试快慢路径。最终在插桩层 instrument.cpp 的instrumentJcc中把生成的符号约束交给求解器g_solver-addJcc(e, taken, pc)完成对分支的符号化。 快慢路径的设计让 QSYM 在保持精度的同时把求解开销压到最低这正是它适合大规模混合模糊测试的原因。标志失效机制防止状态污染 ️还有一个容易被忽略却至关重要的细节标志位的新鲜度管理。CPU 在执行mov、lea等不修改标志的指令后之前的标志值依然有效但一旦执行了div等会破坏标志的指令旧标志就不可信了。QSYM 通过Eflags类维护一个valid_set_位图validate生效记录当前运算产生/影响的标志invalidate失效按 OpKind 声明清除受影响的标志位isValid校验在求解 Jcc 前检查所需标志是否全部有效。这套机制配合时间戳排序op_kinds_环形队列保证每次都能回溯到最近一次产生该标志的运算避免错误复用旧值从而杜绝符号状态污染。实战验证eflags 目录下的回归测试 理论再漂亮也要靠测试说话。QSYM 在 tests/assembly/eflags/ 目录下准备了一整套针对标志位的回归用例add_jb / add_jl / add_jo加法后的进位、符号、溢出跳转sub_jo减法溢出跳转inc_jo / dec_jb / dec_jo自增自减的溢出与借位rol_jo / ror_jo / shl_jo / shr_jo循环移位与逻辑移位的溢出。每个用例都是一个独立的汇编小程序如 add_jb/main.c读取输入后执行相关指令并跳转到不同路径。测试脚本 test_assembly.py 通过 pytest 参数化自动跑完所有用例只要某个标志位建模有误测试立刻报红。这为 EFLAGS 符号化的正确性提供了持续保障。动手体验如何运行这些标志测试 ️如果你想亲自验证 QSYM 的 EFLAGS 符号化效果可以这样操作克隆仓库git clone https://gitcode.com/gh_mirrors/qs/qsym按 README.md 安装依赖z3、Pin 等进入tests目录执行python build.py编译用例运行python -m pytest tests/test_assembly.py查看结果。跑通之后你就能直观感受到QSYM 通过精确的CPU 状态标志模拟让模糊测试在深层条件分支上的探索能力大幅提升。总结 EFLAGS 符号化是 QSYM 的核心竞争力之一。通过借鉴 QEMU 的运算分类法、逐标志符号计算、快慢双路径求解和失效管理机制QSYM 能够在高并发混合模糊测试场景下依然保持对 CPU 状态标志的精确模拟。无论你是符号执行的新手还是想深入了解混合模糊测试原理的开发者阅读 flags.h 与 flags.cpp 这两份源码都是绝佳的入门起点。上图是 QSYM 底层依赖的 Intel Pin 插桩环境配置界面。QSYM 正是借助 Pin 在指令级捕捉 EFLAGS 的变化再配合 Z3 求解器完成符号化分析最终与 AFL 协同驱动混合模糊测试这也是它被称作面向混合模糊测试的实用符号执行引擎的原因。【免费下载链接】qsymQSYM: A Practical Concolic Execution Engine Tailored for Hybrid Fuzzing项目地址: https://gitcode.com/gh_mirrors/qs/qsym创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表