Z3约束器是什么

Z3是一个微软出品的开源约束求解器,能够解决很多种情况下的给定部分约束条件寻求一组满足条件的解的问题

Z3的简单认识

from z3 import *
x=Int('x')
y=Int('y')
solve(x>2,y<10,x+2*y==7)

这里需要注意一下下载Z3的时候用的命令是pip install z3-solver

上面代码的含义是先声明两个Int类型的变量(和C/C++中的Int不一样)这里就是两个整数的意思,Z3 中的 BitVector 才对应到 C/C++ 中的 int。之后在solve中限定x,y的取值范围,然后后面的式方程,求得是满足条件方程的解

结果如下

约束求解 java 约束求解器sat_ci


值得注意的是它只会算一组解

在CTF中的应用

DEFCAMP 2017 Misc 题 forgot my key

题目如下:

I forgot my flag & key. Help me recover them.

5616f5962674d26741d2810600a6c5647620c4e3d2870177f09716b2379012c342d3b584c5672195d653722443f1c39254360007010381b721c741a532b03504d2849382d375c0d6806251a2946335a67365020100f160f17640c6a05583f49645d3b557856221b2

function my_encrypt($flag, $key) {
  $key = md5($key);
  $message = $flag . "|" . $key;

  $encrypted = chr(rand(0, 126));
  for($i=0;$i<strlen($message);$i++) {
    $encrypted .= chr((ord($message[$i]) + ord($key[$i % strlen($key)]) + ord($encrypted[$i])) % 126);
  }
  $hexstr = unpack('h*', $encrypted);
  return array_shift($hexstr);
}

分析一下上述代码一个my_encrypt函数要我们求$key和$message,首先计算了$key的MD5值赋给$key,之后随机了一个字符作为第一个字符,然后循环,加密出来的字符和$message,$key和上一个加密出来的字符有关
常规思路呢就是我们已经知道$message的后32位是md5值,已知倒数第33位是|,现在第倒数33位明文已知且密文已知,那么就可以算出md5值中的第一个字符,然后继续算就可以算出$message的所有值
然后我们使用z3进行求解,首先应该保证答案的唯一性,题目可定保证了答案的唯一,所以不用担心,因此Z3求解成果就是flag
我们根据题目的变换方式,给 Z3 所有的正推关系式把逆推的逻辑让 Z3 通过约束求解来完成

'''
import binascii
s='5616f5'
for i in range(0,len(s),2):
	print(s[i+1]+s[i])#发现是小端显示所以要先还原
	print(binascii.unhexlify(s[i+1]+s[i])[0])#16进制转成ascii码,不加[0]直接显示字符串,16进制转字符串
'''
from z3 import *
import binascii
s='5616f5962674d26741d2810600a6c5647620c4e3d2870177f09716b2379012c342d3b584c5672195d653722443f1c39254360007010381b721c741a532b03504d2849382d375c0d6806251a2946335a67365020100f160f17640c6a05583f49645d3b557856221b2'
encrypted=[]
for i in range(0,len(s),2):
	encrypted.append(binascii.unhexlify(s[i+1]+s[i])[0])#将16进制转换成ascii码
print('mseeage len:',len(encrypted)-1)
print(encrypted)
#声明变量,encrypted是已知,因此IntVal即可
encrypted=[IntVal(i) for i in encrypted]#生成一个变量列表
message=[Int('flag%d'%i) for i in range(len(encrypted)-1)]
#创建一个求解器
solver=Solver()
m1=len(encrypted)-1
#添加明文字符的约束条件
for i in range(m1):
	if i==m1-33:
		solver.add(message[i]==ord('|'))
	else:
		#可见字符
		solver.add(message[i]<127)
		solver.add(message[i]>=32)
#添加明文和密文对照关系的约束条件
for i in range(m1):
	solver.add(encrypted[i+1]==(message[i]+message[m1-32+i%32]+encrypted[i])%126)#这个就是方程
if solver.check()==sat:#有解时会回显sat
	m=solver.model()#在有解的情况下,该函数会将每个限制条件的解求交集,从而得出正解
	s=[]
	for i in range(m1):
		s.append(m[message[i]].as_long())
	print(bytes(s))
else:
	print('unsat')

这是求解的脚本,上面的函数和具体的过程都写在了注释里面,就不具体解释了

结果如下

约束求解 java 约束求解器sat_约束条件_02

从上面可以看出使用Z3的基本步骤为
1.创建约束求解器
solver=Solver() 2.添加约束条件(这一步是关键,能不能求解出来就看约束条件合不合理)
solver.add() 3. 判断是否有解
if solver.check()==sat: 4.求解
solver.model()