executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
re-z3Z3 约束求解:建模、密钥/flag 推导。触发词:z3、约束求解、solver、SMT
Independent GitHub snapshot · Automated classification · Original documentation governs
参考 [[re-analyze/platform-tips]] 最高原则——求解本身不执行目标,但用求解结果运行目标验证时默认沙箱。
pip install z3-solver(Linux / macOS / Windows 均提供预编译 wheel;纯 Python 绑定 + 原生库,安装简单)pip install --upgrade z3-solver,或独立 venv 内安装python3 -c "import z3; print(z3.get_version_string())"pip download z3-solver 后拷入)或系统包 apt install z3(注意系统 z3 的 python 绑定与 pip 版 API 差异,推荐 pip 版)apt install python3(多数自带);macOS: brew install python;Windows: 官方安装包 / choco install pythonpython3 --version按顺序执行,每步记录结果(约束清单 / 建模脚本 / 求解输出 / 验证结果,证据路径见 [[re-triage]])。
从反编译还原约束(比较链 / 数学关系):
if (a == b) / 校验位相等 / 逐字节 strcmp)if (x * 3 + 5 != 0x100) fail → x*3 + 5 == 0x100for (i=0;i<8;i++) out[i]=in[i]^key[i]; if(strcmp(out, s)) fail → 8 条 in[i]^key[i] == s[i]BitVec / Int 建模:
BitVec('x', 32),逐字节用 8 位)from z3 import *
s = Solver()
inp = [BitVec(f'inp_{i}', 8) for i in range(8)] # 8 字节输入
BitVec(..., 8) 与 BitVec(..., 32) 直接相加会报 TypeError / 无解——宽度统一,见坑 5solver.check / model:
s.add(...) 后:r = s.check() —— sat / unsat / unknownsat → m = s.model() 取解;unsat → 约束集有矛盾(见坑 4 / 坑 5);unknown → 非线性 / 复杂表达式(见坑 3)''.join(chr(m[inp[i]].as_long()) for i in range(8))(BitVec 取值用 as_long())unsat 时用 s.assertions() 逐条注释排查(见坑 4)边界(长度 / 字符集)约束:
for c in inp:
s.add(c >= 0x20, c <= 0x7e) # 可打印 ASCII(flag 场景)
s.add(inp[0] == ord('f')) # 已知格式头 flag{ 逐位固化
inp[7] == ord('}'));校验位 / 分隔符格式按反编译补flag{...})得知的部分直接固化为等式,大幅提速输出 flag / 密钥:
flag.txt(或密钥二进制),同时打印 repr 检查(可打印性)while s.check() == sat: 取解 → s.add(Or(逐位 != 当前解)) 枚举多解,与题目预期比对(见坑 4)s.check() 几十分钟不返回,或内存暴涨;原因——符号变量没加取值范围约束,求解器遍历巨大空间(尤其乘除 / 移位组合);对策——先加边界再求解(步骤 4:长度 / 字符集 / 位宽上限),已知格式位(flag{ 头)直接固化;仍慢就收紧边界逐段验证unsat 但人工看约束明明可满足,或 TypeError: unsupported operand;原因——不同位宽 BitVec 混算(8 位与 32 位相加、移位宽度不一致)、C 的隐式整数提升没建模(char 运算提升到 int 再截断);对策——逐条对照伪代码确认运算宽度(32 位乘法结果只取低 32 位 = 加 Extract(31,0));先还原最小的完整语义再放宽(见坑 5)check() 返回 unknown,或含乘法 / 异或组合时求解极慢;原因——非线性多项式(乘除、部分按位运算组合)对 SMT 求解器是难点;对策——能人工化简的先化简(XOR 对称性、常数折叠、用模逆 pow(a,-1,m) 消除法);仍 unknown → 换 [[re-angr]] 符号执行整条路径,或逐位爆破(约束拆成单字节求解);哈希 / 验签段直接放弃建模(不可逆),转 [[re-patching]] / 诚实报告s.add;多解时枚举所有解逐一跑目标验证(步骤 5);unsat 排查时逐条注释约束定位矛盾int 乘法回绕)、移位方向(>> 算术 / 逻辑)、字节序(大端目标)没对齐;对策——确定目标架构与位数([[re-triage]]),有符号运算用 BitVec(..., 32) + 符号扩展模拟,或改用 Int 加范围约束模拟回绕(s.add(a == (b * c) % 2**32));字节序按目标(多数 CTF 题小端)check() 返回 sat 且模型数值合理,但代回原程序(如 xorshift128+ 生成器)输出对不上,或干脆无解;原因——某些位向量问题(PRNG 内部状态还原)对 SMT 求解器是已知难点(Z3 4.12.x 从两次输出还原 xorshift128+ 双 64 位状态有已知失败案例);对策——结果必须交叉验证:把模型代回目标程序重放([[re-sandbox]])、枚举多解、或与 [[re-angr]]/暴力破解(密钥空间小时)对照;sat 不保证正确unsat 但人工核对约束明明可满足;原因——把实验观测的中间值直接断言成等式(如 RNG(seed).next(26) == 57508594,而计算实际得 14325532),等式永假;对策——先不加中间观测值的断言,只断言输入-输出关系让 Z3 反推未知;确需固定中间值时先用 m.eval() 验证观测值本身与模型是否一致