三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

CTF逆向新手必看:用Python的z3-solver库5分钟搞定复杂方程组(附完整脚本)

CTF逆向新手必看:用Python的z3-solver库5分钟搞定复杂方程组(附完整脚本)

CTF逆向新手必看:用Python的z3-solver库5分钟搞定复杂方程组(附完整脚本)

在CTF逆向工程中,经常会遇到需要解复杂方程组的情况。面对十几个甚至几十个变量的约束方程,手动计算不仅耗时耗力,还容易出错。这时候,Python的z3-solver库就能大显身手了。

z3是由微软研究院开发的高性能定理证明器,特别适合解决约束满足问题。对于CTF逆向题目中常见的flag验证逻辑,z3可以在几秒钟内求出所有变量的值,大大提升解题效率。本文将带你从零开始,快速掌握这个强大工具的使用方法。

1. 为什么需要z3-solver

逆向工程中,程序通常会通过一系列复杂的数学运算来验证输入的flag是否正确。这些验证逻辑往往表现为多个变量的线性或非线性方程组。例如:

# 典型的CTF逆向题目验证逻辑 if (x1 * 7 == 546 and x2 * 2 == 166 and x3 * 6 + x4 + x6 * 7 == 1055 and ... # 更多约束条件 ): print("Correct flag!") else: print("Wrong!")

手动解这类方程组的痛点显而易见:

  • 变量数量多:通常10-20个变量起步
  • 约束条件复杂:包含加减乘除、位运算等多种操作
  • 容错率低:一个变量算错,整个flag就错了
  • 耗时严重:可能需要数小时手工计算

z3-solver的优势在于:

  1. 自动化求解:只需定义变量和约束,自动计算所有可能解
  2. 支持多种类型:整数、实数、位向量、布尔值等
  3. 高效准确:通常能在秒级完成求解
  4. 易于集成:Python接口简单直观

2. 快速搭建z3环境

2.1 安装z3-solver

新手第一个坑往往是安装错误的包。正确的安装命令是:

pip install z3-solver

常见错误:

  • pip install z3→ 安装的是错误的包
  • 导入时使用import z3→ 应该用from z3 import *

2.2 验证安装

创建一个简单的测试脚本:

from z3 import * x = Int('x') s = Solver() s.add(x > 10, x < 20) print(s.check()) # 应该输出sat print(s.model()) # 输出x=11等解

如果能看到类似输出,说明环境配置正确。

3. z3求解CTF方程组的完整流程

3.1 定义变量

对于有16个变量的典型CTF题目,推荐使用列表推导式创建变量:

v = [Int(f'v{i}') for i in range(16)]

这比逐个定义v0 = Int('v0')更简洁,特别适合变量多的情况。

3.2 创建求解器并添加约束

solver = Solver() # 添加约束条件 solver.add(v[0] * 7 == 546) solver.add(v[1] * 2 == 166) solver.add(v[2] * 6 + v[3] + v[5] * 7 == 1055) # ... 添加所有约束条件

注意:约束条件必须完全按照题目要求添加,任何遗漏或错误都会导致求解失败。

3.3 求解并输出结果

if solver.check() == sat: ans = solver.model() flag = ''.join([chr(ans[vi].as_long()) for vi in v]) print(f"Flag: {flag}") else: print("No solution found")

关键点:

  • check()返回sat表示有解
  • model()返回具体的解
  • as_long()将z3数值转为Python整数
  • chr()将ASCII码转为字符

4. 实战:完整CTF解题脚本

下面是一个可直接复用的通用脚本模板:

from z3 import * def solve_ctf_equations(): # 1. 定义变量 v = [Int(f'v{i}') for i in range(16)] # 2. 创建求解器 solver = Solver() # 3. 添加所有约束条件 solver.add(v[0] * 7 == 546) solver.add(v[1] * 2 == 166) solver.add(v[2] * 6 + v[3] + v[5] * 7 == 1055) solver.add(v[1] * 4 + v[3] + v[4] * 4 + v[5] * 7 + v[7] * 6 + v[8] + v[13] * 2 + v[15] * 8 == 3107) solver.add(v[4] * 4 == 336) solver.add(v[1] * 2 + v[5] * 7 == 656) solver.add(v[6] * 3 + v[7] * 6 + v[8] + v[9] * 5 + v[10] * 16 + v[11] * 3 + v[12] * 6 + v[13] * 2 + v[15] * 8 == 5749) solver.add(v[7] * 6 == 606) solver.add(v[8] + v[14] * 5 == 652) solver.add(v[9] * 5 + v[10] * 16 + v[12] * 6 == 3213) solver.add(v[6] * 3 + v[7] * 6 + v[8] + v[9] * 5 + v[10] * 24 + v[11] * 3 + v[12] * 6 + v[13] * 2 + v[15] * 8 == 6717) solver.add(v[11] * 3 == 285) solver.add(v[6] * 3 + v[7] * 6 + v[8] * 2 + v[10] * 8 + v[12] * 6 + v[13] * 2 + v[14] * 5 + v[15] * 8 == 4573) solver.add(v[14] * 5 == 600) solver.add(v[2] * 6 + v[3] + v[4] * 4 + v[5] * 7 + v[13] * 2 == 1615) solver.add(v[1] * 2 + v[5] * 7 + v[7] * 6 + v[8] + v[15] * 8 == 2314) # 4. 求解并输出 if solver.check() == sat: ans = solver.model() flag = ''.join([chr(ans[vi].as_long()) for vi in v]) print(f"[+] Success! Flag: {flag}") else: print("[-] No solution found") if __name__ == "__main__": solve_ctf_equations()

5. 高级技巧与常见问题

5.1 处理复杂约束条件

当题目给出的约束条件是一长串逻辑表达式时,可以先用字符串处理提取单个方程:

import re # 示例:处理复杂的条件表达式 condition = "v2*6+v3+v5*7==1055 && v1*4+v3+v4*4+v5*7+v7*6+v8+v13*2+v15*8==3107" equations = re.split(r'&&|\|\|', condition.replace(' ', '')) # 清理后的方程列表 clean_equations = [eq.strip() for eq in equations if eq.strip()]

5.2 变量类型选择

z3支持多种变量类型,根据题目需求选择:

类型创建方法适用场景
整数Int('x')普通整数运算
实数Real('x')浮点数运算
位向量BitVec('x', 32)涉及位运算或溢出
布尔值Bool('x')逻辑条件

5.3 常见错误排查

  1. 无解情况

    • 检查约束条件是否全部正确添加
    • 确认变量类型是否匹配(如该用BitVec时用了Int)
  2. 错误解

    • 检查方程是否复制正确
    • 确认变量顺序与题目一致
  3. 性能问题

    • 对于复杂问题,可以尝试简化约束
    • 使用BitVec代替Int可能更快

5.4 扩展应用

z3不仅能解线性方程组,还能处理:

  • 非线性方程
  • 位运算约束
  • 数组和字符串操作
  • 组合逻辑问题

例如,解一个包含位运算的题目:

x = BitVec('x', 32) solver.add((x >> 4) & 0xF == 10) solver.add((x & 0xF) + (x >> 8) == 25)

掌握z3-solver后,CTF逆向中的方程组类题目将从一个耗时难题变成几分钟就能解决的"送分题"。建议收藏本文的脚本模板,下次遇到类似题目时直接修改约束条件即可快速求解。

← 返回列表