python Z3库 以及之后ctf实战中遇到的进阶操作
python z3库
一、介绍

二、使用方法
1、设置变量
用Int型设置变量代表所有的解只能是整数,也可以用Ints同时设置多个变量
如a,s,d = Ints('a,s,d')

也可以设置具体值:
比如 BitVecVal(10,32) 可以创建一个32位的,值为10 的位向量
2、设置解方程的类Solver
设置完这个类,我们就能一个一个的添加约束项了
Solver()创造一个求解器,用于之后添加限制条件

3、添加约束项
add()进行添加限制条件
4、判断是否有解
check()用于判断是否有解,有解返回sat,无解返回unsat
5、返回解
model()在有解时返回解
三、实例
1、求解位向量问题
Z3库可以求解位向量,对RE来说极其友好
from z3 import * a,b,c = BitVecs('a b c',10) #这个10是在说明位数 x = Solver() x.add(a ^ b & c == 12) x.add(a & b >> 3 == 3) x.add(b ^ c == 4) print(x.check()) print(x.model())

2、用Z3库解实际ctf题目
CISCN 2021 baby_bc
import hashlib from z3 import * zy=[0x00, 0x00, 0x00, 0x01,0x01, 0x00, 0x00, 0x00,0x02, 0x00, 0x00, 0x01,0x00, 0x00, 0x00, 0x00,0x01, 0x00, 0x01, 0x00] sx=[0x00, 0x00, 0x02, 0x00,0x02,0x00, 0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x01, 0x00,0x00, 0x01, 0x00, 0x00, 0x01] ts = Solver() map = [BitVec('s%d' % i, 4) for i in range(25)] ts.add(map[5*2+2] == 4) ts.add(map[5*3+3] == 3) for i in map: ts.add(i > 0) ts.add(i <= 5) for a in range(5): ts.add( And(map[5 * a] != map[5 * a + 1], map[5 * a] != map[5 * a + 2], map[5 * a] != map[5 * a + 3], map[5 * a] != map[5 * a + 4], map[5 * a + 1] != map[5 * a + 2], map[5 * a + 1] != map[5 * a + 3], map[5 * a + 1] != map[5 * a + 4], map[5 * a + 2] != map[5 * a + 3], map[5 * a + 2] != map[5 * a + 4], map[5 * a + 3] != map[5 * a + 4])) for b in range(5): ts.add( And(map[5 * 0 + b] != map[5 * 1 + b], map[5 * 0 + b] != map[5 * 2 + b], map[5 * 0 + b] != map[5 * 3 + b], map[5 * 0 + b] != map[5 * 4 + b], map[5 * 1 + b] != map[5 * 2 + b], map[5 * 1 + b] != map[5 * 3 + b], map[5 * 1 + b] != map[5 * 4 + b], map[5 * 2 + b] != map[5 * 3 + b], map[5 * 2 + b] != map[5 * 4 + b], map[5 * 3 + b] != map[5 * 4 + b] )) for b in range(4): for y in range(5): ts.add(map[5 * b + y] != map[5 * (b + 1) + y]) for a in range(5): for x in range(4): if zy[4 * a + x]==1: ts.add(map[5 * a + x] > map[5 * a + x + 1]) elif zy[4 * a + x] == 2: ts.add(map[5 * a + x] < map[5 * a + x + 1]) for b in range(4): for y in range(5): if sx[5 * b + y]==1: ts.add(map[5 * b + y] < map[5 * (b + 1) + y]) elif sx[5 * b + y] == 2 : ts.add(map[5 * b + y] > map[5 * (b + 1) + y]) print() while ts.check() == sat: answer = ts.model() condition = [] p = [] for i in map: p += [answer[i]] condition.append(i != answer[i]) p[5 * 2 + 2] = 0 p[5 * 3 + 3] = 0 ts.add(Or(condition)) print(p) #p=[1, 4, 2, 5, 3, 5, 3, 1, 4, 2, 3, 5, 0, 2, 1, 2, 1, 5, 0, 4, 4, 2, 3, 1, 5] l='' for i in p: l+=str(i) md = hashlib.md5() md.update(l.encode()) print('CISCN{'+md.hexdigest()+'}')
其中And语句与Or语句的作用参考:
一般来说,我们只要会 add()函数,会 创建符号向量就能解决大部分问题
但我在实际CTF中遇到了一些难以解决的问题,需要用到其内置的一些API函数
三、z3进阶之 使用Concat 与 Extract 函数实现 concatenate 和 separate
1、问题来源
在实际CTF中,我们经常会遇到字符数组的连接和拆分,比如把四个字符连接起来转4字节int类型数据,或者拆分回去
在处理字符数组的时候,我们经常使用一下方式来达到我们的预期效果:
连接:
arr[i] = ch[i+0] + (ch[i+1] << 8) + (ch[i+2] << 16) + (ch[i+3] << 24)
分隔:
ch[i] = arr[i] & 0xff
ch[i+1] = (arr[i] & 0xff00) >> 8
ch[i+2] = (arr[i] & 0xffff00) >> 16
ch[i+3] = (arr[i] & 0xffffff00) >> 24
但这在z3的符号向量里是行不通的
下面我们介绍Concat 与 Extract 函数
2、Concat 函数
Concat可以进行符号向量的连接
比如 定义四个符号向量,然后相连他们:
from z3 import * xx = BitVec("xx",8) yy = BitVec("yy",8) zz = BitVec("zz",8) pp = BitVec("pp",8) v = Concat(xx,yy,zz,pp) print(v.sort())
最后会输出
BitVec(32)
3、Extract 函数
Extract 函数第一个参数代表了分隔的末位置,第二个参数代表分隔的起始位置,第三个参数代表要分割的符号向量
v = BitVec("v",32) x = Extract(7,0,v) y = Extract(15,8,v) z = Extract(23,16,v) p = Extract(31,24,v) print(v.sort())
注意点
Concat 函数是不用考虑小端序的,他是直接连接,而 Extract函数需要注意到这点
# 0x12,0x34,0x56,0x78 => Concat => 0x12345678 # 0x12345678 => Extract(8*(i+1)-1,8*i) => 0x78,0x56,0x34,0x12
4、成功实现字符数组的相连与拆分
#相连: for i in range(4): long_f1[i] = Concat(flag[4*i+3] , flag[4*i+2] , flag[4*i+1], flag[4*i+0])
#拆分:
ver_f = [None] * 32
for i in range(4):
ver_f[4*i + 0] = Extract(7,0,long_1[i]) # 好怪的函数
ver_f[4*i + 1] = Extract(15,8,long_1[i])
ver_f[4*i + 2] = Extract(23,16,long_1[i])
ver_f[4*i + 3] = Extract(31,24,long_1[i])
问题解决

浙公网安备 33010602011771号