z3入门学习

z3约束求解器是解决多元问题的好工具

其应用广泛,比如,著名的二进制分析框架angr也内置了修改版的z3

安装下载

虽然z3具有适用于各种编程语言的绑定(由C++开发,提供了 .NET、C、C++、Java、Python 等语言调用接口),但更多以及方便的是python接口。以python接口进行详讲

GitHub直通车:GitHub - Z3Prover/z3: The Z3 Theorem Prover

准备好Python环境和pip,就可以使用如下命令安装:

pip install z3-solver

# 一般直接下速度很慢,推荐使用清华源等国内镜像源
# 清华源 -i <https://pypi.tuna.tsinghua.edu.cn/simple>

z3_微软开发的SMT求解器

什么是SMT?

  • SAT-布尔可满足性问题
  • SMT-可满足性模理论

若简化理解的话:SMT = SAT + Theory Solvers(理论求解器)

特点SATSMT
逻辑范围仅限于布尔逻辑支持多种理论,包括整数、实数、数组、位向量等
公式表示通常是 CNF 或其他布尔逻辑表示可以是混合的逻辑公式,包含不同理论中的变量和约束
求解器框架基于 DPLL 或 CDCL 算法基于 DPLL(T) 框架,结合了 SAT 求解器和理论求解器
表达能力只能处理简单的布尔变量关系能处理复杂的约束和多样化的数据类型

相比较而言,SMT 能够处理更复杂的约束和数据类型,可以说是SAT的扩展

使用z3

在确保你的python环境以及安装好z3-solver后

python脚本中使用如下导入相应的包

from z3 import *

常用的API

API功能
Solver()创建一个通用求解器
add()添加约束条件
check()用来判断在添加完约束条件后,来检测解的情况
model()在存在解的时候,该函数会将每个限制条件所对应的解集取交集,进而得出正解

变量声明

z3中有3种类型的变量,分别是整型(Int)、实型(Real)和向量(BitVec)

只有向量类型可以进行位运算(异或,左右移位等)

from z3 import *

# 声明整形
a = Int('a')             # 声明单个整型变量
a,b = Ints('a b')        # 声明多个整型变量

# 声明实性
a = Real('a')            # 声明单个实型变量
a,b = Reals('a b')       # 声明多个实型变量

# 声明向量
a = BitVec('a', 8)        # 声明单个8位的变量
a, b = BitVec('a b', 8)   # 声明多个8位的变量

# tips:更多使用的是循环声明变量
# 更方便创建同类型多元
a1 = [Int(f'a1[{i}]') for i in range(32)]
a2 = [Real(f'a2[{i}]') for i in range(32)]
a3 = [BitVec(f'a3[{i}]', 8) for i in range(32)] # BitVec类型创建时必须给出大小

如何使用z3

使用一些CTF题例来进行讲解(以注释形式)

[HGAME 2023 week3]kunmusic

from z3 import *
taa = [132, 47, 180, 7, 216, 45, 68, 6, 39, 246, 124, 2, 243, 137, 58, 172, 53,
200, 99, 91, 83, 13, 171, 80, 108, 235, 179, 58, 176, 28, 216, 36, 11, 80, 39,
162, 97, 58, 236, 130, 123, 176, 24, 212, 56, 89, 72]

# 涉及位运算,创建BitVec变量(其中32是指的指的是变量的宽度)
num = [BitVec(f'num[{i}]', 32) for i in range(13)]

s = Solver() # 创建一个约束求解器s

# 添加约束条件
s.add(num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 41236) +
num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] +
45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 21087) ==
12702282)
s.add(num[0] - 25228 + (num[1] ^ 20699) + (num[2] ^ 8158) + num[3] - 65307 +
num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 49055) + num[8] -
7992 + (num[9] ^ 57465) + (num[10] ^ 57426) + num[11] + 13299 + num[12] - 50966
== 9946829)
s.add(num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4]
+ 29882 + (num[5] ^ 13574) + (num[6] ^ 21310) + num[7] + 47366 + num[8] + 41784 +
(num[9] ^ 53690) + num[10] * 58436 + num[11] * 15590 +num[12] + 58225 == 2372055)
s.add(num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] +
21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 +
num[9] + 18568 + num[10] + 12995 + (num[11] ^ 39260) + num[12] + 25329 ==
6732474)
s.add(num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4]
^ 16284) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 18491) +
(num[9] ^ 4795) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 ==
14020739)
s.add(num[0] - 10968 + num[1] - 31780 + (num[2] ^ 31857) + num[3] - 61983 +
num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^
7064) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 12441) ==
14434062)
s.add(num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4]
* 26571 + num[5] * 15086 + (num[6] ^ 22986) + (num[7] ^ 23349) + (num[8] ^ 16381)
+ (num[9] ^ 23173) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 ==
7433598)
s.add(num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4]
^ 1287) + (num[5] ^ 35272) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 +
(num[9] ^ 44874) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 ==
7989404)
s.add((num[0] ^ 28978) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 +
(num[4] ^ 39287) + num[5] - 48576 + (num[6] ^ 28542) + num[7] - 43265 + num[8] +
22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 ==
3504803)
s.add(num[0] * 22466 + (num[1] ^ 55999) + num[2] - 53658 + (num[3] ^ 47160) +
(num[4] ^ 12511) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^
25279) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 16757)
== 11003580)
s.add(num[0] * 57492 + (num[1] ^ 13421) + num[2] - 13941 + (num[3] ^ 48092) +
num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274
+ num[9] + 36175 + (num[10] ^ 18568) + num[11] * 49694 + (num[12] ^ 9473) ==
25546210)
s.add(num[0] - 23355 + num[1] * 50164 + (num[2] ^ 34618) + num[3] + 52703 +
num[4] + 36245 + num[5] * 46648 + (num[6] ^ 4858) + (num[7] ^ 41846) + num[8] *
27122 + (num[9] ^ 42058) + num[10] * 15676 + num[11] - 31863 +num[12] + 62510 ==
11333836)
s.add(num[0] * 30523 + (num[1] ^ 7990) + num[2] + 39058 + num[3] * 57549 +
(num[4] ^ 53440) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 55436) + (num[8] ^
2624) + (num[9] ^ 13652) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 ==
13863722)

s.check() # 尝试求解 

m = s.model() # 将每个限制条件所对应的解集取交集,存入m中
a = []

# 将解导入a中
for i in range(len(m)):
a.append(int(str(m[num[i]])))

# 题目额外问题,下面与z3无关
flag = ""
for i in range(47):
flag += chr((taa[i] ^ a[i % 13]) % 0x80)
print(flag)

[NSSRound#X Basic]ez_z3

from z3 import *

# 全是数运算,创建Int变量
a1 = [Int(f'a1[{i}]') for i in range(20)]

x = Solver() # 创建约束求解器x

# 添加约束条件
x.add(20 * a1[19] * 19 * a1[18]
      + 14 * a1[13]
      + 13 * a1[12]
      + 11 * a1[10] * 10 * a1[9]
      + 30 * a1[5]
      + 5 * a1[4]
      + a1[0]
      + 2 * a1[1]
      - 3 * a1[2]
      - 4 * a1[3]
      - 7 * a1[6]
      + 8 * a1[7]
      - 9 * a1[8]
      - 12 * a1[11]
      - 16 * a1[15] * 15 * a1[14]
      - 17 * a1[16]
      - 18 * a1[17] == 2582239)
x.add(20 * a1[19] * 19 * a1[18]
      + 14 * a1[13]
      + 13 * a1[12]
      + 11 * a1[10] * 10 * a1[9]
      + 30 * a1[5]
      - 7 * a1[6]
      + 8 * a1[7]
      - 9 * a1[8]
      + 5 * a1[4]
      + 3 * a1[2]
      + 2 * a1[1] * a1[0]
      - 4 * a1[3]
      - 12 * a1[11]
      - 16 * a1[15] * 15 * a1[14]
      - (18 * a1[17]
         + 17 * a1[16]) == 2602741)
x.add(19 * a1[18]
      + 18 * a1[17]
      + 14 * a1[13] * 13 * a1[12]
      + 12 * a1[11] * 11 * a1[10]
      + 9 * a1[8]
      + 7 * a1[6] * 30 * a1[5]
      + a1[0]
      - 2 * a1[1]
      - 4 * a1[3] * 3 * a1[2]
      - 5 * a1[4]
      + 8 * a1[7]
      - 10 * a1[9]
      - 15 * a1[14]
      - 17 * a1[16] * 16 * a1[15]
      - 20 * a1[19] == 2668123)
x.add(20 * a1[19] * 19 * a1[18]
      + 14 * a1[13]
      + (13 * a1[12] + 11 * a1[10] - 12 * a1[11]) * 10 * a1[9]
      + 30 * a1[5]
      + 5 * a1[4]
      + a1[0]
      + 2 * a1[1]
      - 3 * a1[2]
      - 4 * a1[3]
      - 7 * a1[6]
      + 8 * a1[7]
      - 9 * a1[8]
      - 16 * a1[15] * 15 * a1[14]
      - 17 * a1[16]
      - 18 * a1[17] == 2520193)
x.add(18 * a1[17]
      + 17 * a1[16]
      + 15 * a1[14]
      + 13 * a1[12] * 12 * a1[11]
      + 10 * a1[9]
      + 9 * a1[8] * 8 * a1[7]
      + 3 * a1[2] * 2 * a1[1] * a1[0]
      - 4 * a1[3]
      - 5 * a1[4]
      - 30 * a1[5]
      - 7 * a1[6]
      - 11 * a1[10]
      - 14 * a1[13]
      - 16 * a1[15]
      - 19 * a1[18]
      - 20 * a1[19] == 8904587)
x.add(18 * a1[17]
      + 7 * a1[6] * 30 * a1[5] * 5 * a1[4]
      + 4 * a1[3]
      + 8 * a1[7]
      + a1[0]
      - 2 * a1[1]
      - 3 * a1[2]
      - 9 * a1[8]
      - 11 * a1[10] * 10 * a1[9]
      - 16 * a1[15] * (13 * a1[12] + 12 * a1[11] - 14 * a1[13] - 15 * a1[14])
      - 17 * a1[16]
      - 19 * a1[18]
      - 20 * a1[19] == 1227620874)
x.add(20 * a1[19] * 19 * a1[18]
      + 17 * a1[16]
      + 14 * a1[13]
      + 13 * a1[12]
      + 12 * a1[11] * 11 * a1[10] * 10 * a1[9]
      + 7 * a1[6] * 30 * a1[5]
      + 5 * a1[4]
      + 3 * a1[2]
      + a1[0]
      + 2 * a1[1]
      + 4 * a1[3]
      + 8 * a1[7]
      - 9 * a1[8]
      - 16 * a1[15] * 15 * a1[14]
      - 18 * a1[17] == 1836606059)
x.add(20 * a1[19] * 19 * a1[18]
      + 16 * a1[15] * 15 * a1[14]
      + 14 * a1[13]
      + 13 * a1[12]
      + 12 * a1[11]
      + 7 * a1[6] * 30 * a1[5]
      + 5 * a1[4]
      + 2 * a1[1] * a1[0]
      - 3 * a1[2]
      + 4 * a1[3]
      + 8 * a1[7]
      - 9 * a1[8]
      - 10 * a1[9]
      - 11 * a1[10]
      - 17 * a1[16]
      - 18 * a1[17] == 8720560)
x.add(20 * a1[19] * 19 * a1[18]
      + 14 * a1[13]
      + 13 * a1[12]
      + 11 * a1[10] * (10 * a1[9] + 30 * a1[5] + 5 * a1[4] + 4 * a1[3] - 7 * a1[6] + 8 * a1[7] - 9 * a1[8])
      + a1[0]
      + 2 * a1[1]
      - 3 * a1[2]
      - 12 * a1[11]
      - (16 * a1[15] - 17 * a1[16] - 18 * a1[17]) * 15 * a1[14] == 11387045)
x.add(20 * a1[19] * 19 * a1[18]
      + 16 * a1[15] * 15 * a1[14]
      + 14 * a1[13]
      + 11 * a1[10] * 10 * a1[9]
      + 9 * a1[8]
      + 3 * a1[2]
      + a1[0]
      - 2 * a1[1]
      + 4 * a1[3]
      - 5 * a1[4]
      - 30 * a1[5]
      - 7 * a1[6]
      + 8 * a1[7]
      - 12 * a1[11]
      - 13 * a1[12]
      - 17 * a1[16]
      - 18 * a1[17] == 7660269)
x.add(20 * a1[19] * 19 * a1[18]
      + 14 * a1[13]
      + 13 * a1[12]
      + 11 * a1[10] * 10 * a1[9]
      - 12 * a1[11]
      + a1[0]
      + 2 * a1[1]
      - (4 * a1[3] * 3 * a1[2]
         - 5 * a1[4]
         - 30 * a1[5])
      - 7 * a1[6]
      + 8 * a1[7]
      - 9 * a1[8]
      - 16 * a1[15] * 15 * a1[14]
      - 17 * a1[16]
      - 18 * a1[17] == 2461883)
x.add(14 * a1[13]
      + 11 * a1[10] * 10 * a1[9]
      + 9 * a1[8] * 8 * a1[7]
      + 7 * a1[6]
      + 2 * a1[1] * a1[0]
      - 4 * a1[3] * 3 * a1[2]
      - 5 * a1[4]
      - 30 * a1[5]
      - 12 * a1[11]
      - 13 * a1[12]
      - 15 * a1[14]
      - 17 * a1[16] * 16 * a1[15]
      - 18 * a1[17]
      - 19 * a1[18]
      - 20 * a1[19] == -966296)
x.add(14 * a1[13]
      + 13 * a1[12]
      + (11 * a1[10] * 10 * a1[9] + 30 * a1[5] + 5 * a1[4] + 3 * a1[2] + 4 * a1[3] - 7 * a1[6] + 8 * a1[7] - 9 * a1[8])
      * 2
      * a1[1]
      + a1[0]
      - 12 * a1[11]
      - 15 * a1[14]
      - 16 * a1[15]
      - 17 * a1[16]
      - 18 * a1[17]
      - 20 * a1[19] * 19 * a1[18] == 254500223)
x.add(16 * a1[15] * 15 * a1[14]
      + 14 * a1[13]
      + 11 * a1[10] * 10 * a1[9]
      + 7 * a1[6] * 30 * a1[5]
      + a1[0]
      - 2 * a1[1]
      - 3 * a1[2]
      - 5 * a1[4] * 4 * a1[3]
      + 8 * a1[7]
      - 9 * a1[8]
      - 12 * a1[11]
      - 13 * a1[12]
      - 17 * a1[16]
      - 18 * a1[17]
      - 19 * a1[18]
      - 20 * a1[19] == 6022286)
x.add(18 * a1[17]
      + 16 * a1[15]
      - 17 * a1[16]
      + 14 * a1[13]
      + 12 * a1[11]
      + 11 * a1[10] * 10 * a1[9]
      + 30 * a1[5]
      + 5 * a1[4]
      + 4 * a1[3] * 3 * a1[2]
      + 2 * a1[1] * a1[0]
      - 9 * a1[8] * 8 * a1[7] * 7 * a1[6]
      - 13 * a1[12]
      - 15 * a1[14]
      - 19 * a1[18]
      - 20 * a1[19] == -636956022)
x.add(20 * a1[19] * 19 * a1[18]
      + 13 * a1[12]
      + 12 * a1[11]
      + 11 * a1[10] * 10 * a1[9]
      + 7 * a1[6]
      + 30 * a1[5]
      + 5 * a1[4]
      + 3 * a1[2] * 2 * a1[1] * a1[0]
      - 4 * a1[3]
      - 9 * a1[8] * 8 * a1[7]
      - 14 * a1[13]
      - 15 * a1[14]
      - 16 * a1[15]
      - 17 * a1[16]
      - 18 * a1[17] == 10631829)
x.add(20 * a1[19] * 19 * a1[18]
      + 16 * a1[15]
      - 17 * a1[16]
      - 18 * a1[17]
      + 15 * a1[14] * 14 * a1[13]
      + 13 * a1[12]
      + 11 * a1[10] * 10 * a1[9]
      - 12 * a1[11]
      + 7 * a1[6]
      + (4 * a1[3] - 5 * a1[4] - 30 * a1[5]) * 3 * a1[2]
      + a1[0]
      + 2 * a1[1]
      + 8 * a1[7]
      - 9 * a1[8] == 6191333)
x.add(14 * a1[13]
      + 10 * a1[9] * 9 * a1[8] * 8 * a1[7]
      + 5 * a1[4]
      + 4 * a1[3] * 3 * a1[2]
      + 2 * a1[1] * a1[0]
      - 7 * a1[6] * 30 * a1[5]
      - 11 * a1[10]
      - 13 * a1[12] * 12 * a1[11]
      - 16 * a1[15] * 15 * a1[14]
      - 18 * a1[17] * 17 * a1[16]
      - 20 * a1[19] * 19 * a1[18] == 890415359)
x.add(20 * a1[19]
      + 19 * a1[18]
      + 18 * a1[17]
      + 16 * a1[15]
      - 17 * a1[16]
      + 12 * a1[11]
      + 11 * a1[10]
      + 10 * a1[9]
      + 9 * a1[8]
      + 30 * a1[5]
      + a1[0]
      + 4 * a1[3] * 3 * a1[2] * 2 * a1[1]
      - 5 * a1[4]
      - 7 * a1[6]
      + 8 * a1[7]
      - 13 * a1[12]
      - 14 * a1[13]
      - 15 * a1[14] == 23493664)
x.add(20 * a1[19] * 19 * a1[18]
      + 13 * a1[12]
      + 12 * a1[11]
      + 10 * a1[9]
      + 3 * a1[2] * 2 * a1[1]
      + a1[0]
      - 4 * a1[3]
      - 5 * a1[4]
      + 8 * a1[7] * 7 * a1[6] * 30 * a1[5]
      - 9 * a1[8]
      - 11 * a1[10]
      - 14 * a1[13]
      - 16 * a1[15] * 15 * a1[14]
      - 17 * a1[16]
      - 18 * a1[17] == 1967260144)

# 添加约束条件,将解限制在0~256,简化运算范围,加快脚本速度      
for i in a1:
    x.add(i >= 0, i <= 0xff)
    
if x.check() == sat: # 表示判断约束是否可满足
    m = x.model() # 将解导入m
    print(m)

[CISCN 2021初赛]babybc

用z3搓个求解器解出满足的数独

from z3 import *

row = [[0, 0, 0, 1],
       [1, 0, 0, 0],
       [2, 0, 0, 1],
       [0, 0, 0, 0],
       [1, 0, 1, 0]]
col = [[0, 0, 2, 0, 2],
       [0, 0, 0, 0, 0],
       [0, 0, 0, 1, 0],
       [0, 1, 0, 0, 1]]

s = Solver() # 创建约束求解器s
maps = [[0] * 5,
        [0] * 5,
        [0] * 5,
        [0] * 5,
        [0] * 5]

for i in range(5):
    for j in range(5):
        maps[i][j] = Int("x%d%d" % (i + 1, j + 1))  # 先初始化设置未知数

s.add(maps[2][2] == 4, maps[3][3] == 3)  # 把map数组已知的两个值填入

for i in range(5):
    for j in range(5):
        s.add(maps[i][j] >= 1, maps[i][j] <= 5)  # 约束填入数据在1-5中

for i in range(5):
    for j in range(5):
        for k in range(j):
            s.add(maps[i][j] != maps[i][k])  # 一行中不能有相同数据

for i in range(5):
    for j in range(5):
        for k in range(i):
            s.add(maps[i][j] != maps[k][j])  # 一列中不能有相同数据

for i in range(5):
    for j in range(4):
        if row[i][j] == 1:
            s.add(maps[i][j] > maps[i][j + 1])
        elif row[i][j] == 2:
            s.add(maps[i][j] < maps[i][j + 1])  # row如果有1,map中对应位置要大于右边;row如果有2,map中对应对应位置要小于右边

for i in range(4):
    for j in range(5):
        if col[i][j] == 2:
            s.add(maps[i][j] > maps[i + 1][j])
        elif col[i][j] == 1:
            s.add(maps[i][j] < maps[i + 1][j])  # col如果有1,map中对应位置数据小于下边;col如果有2,map中对应位置数据要大于下边

answer = s.check()
if answer == sat:
    print(s.model())
    num = s.model()
    flag = []
    for i in maps:
        for j in i:
            flag.append(int(str(num[j])))
    for i in range(len(flag)):
        flag[i] += 48
    flag[12] = 48
    flag[18] = 48
    flag = bytes(flag)
    print(flag)

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值