5.8.1 Z3

Z3arrow-up-right 是一个由微软开发的可满足性摸理论(Satisfiability Modulo Theories,SMT)的约束求解器。所谓约束求解器就是用户使用某种特定的语言描述对象(变量)的约束条件,求解器将试图求解出能够满足所有约束条件的每个变量的值。Z3 可以用来检查满足一个或多个理论的公式的可满足性,也就是说,它可以自动化地通过内置理论对一阶逻辑多种排列进行可满足性校验。目前其支持的理论有:

  • equality over free 函数和谓词符号

  • 实数和整形运算(有限支持非线性运算)

  • 位向量

  • 阵列

  • 元组/记录/枚举类型和代数(递归)数据类型

  • ...

因其强大的功能,Z3 已经被用于许多领域中,在安全领域,主要见于符号执行、Fuzzing、二进制逆向、密码学等。另外 Z3 提供了多种语言的接口,这里我们使用 Python。

安装

在 Linux 环境下,执行下面的命令:

$ git clone https://github.com/Z3Prover/z3.git
$ cd z3
$ python scripts/mk_make.py --python
$ cd build
$ make
$ sudo make install

另外还可以使用 pip 来安装 Python 接口(py2和py3均可),这是二进制分析框架 angr 里内置的修改版:

Z3 理论基础

Op
Mnmonics
Description

0

true

恒真

1

flase

恒假

2

=

相等

3

distinct

不同

4

ite

if-then-else

5

and

n元 合取

6

or

n元 析取

7

iff

implication

8

xor

异或

9

not

否定

10

implies

Bi-implications

使用 Z3

先来看一个简单的例子:

首先定义了两个常量 x 和 y,类型是 Z3 内置的整数类型 Intsolve() 函数会创造一个 solver,然后对括号中的约束条件进行求解,注意在 Z3 默认情况下只会找到满足条件的一组解。

simplify() 函数用于对表达式进行化简,同时可以设置一些选项来满足不同的要求。更多选项使用 help_simplify() 获得。

同时,Z3 提供了一些函数可以解析表达式:

set_param() 函数用于对 Z3 的全局变量进行配置,如运算精度,输出格式等等:

逻辑运算有 AndOrNotImpliesIf,另外 == 表示 Bi-implications。

Z3 提供了多种 Solver,即 Solver 类,其中实现了很多 SMT 2.0 的命令,如 push, pop, check 等等。

为了将 Z3 中的数和 Python 区分开,应该使用 IntVal()RealVal()RatVal() 分别返回 Z3 整数、实数和有理数值。

在混合使用实数和整数变量时,Z3Py 会自动添加强制类型转换将整数表达式转换成实数表达式。

现代的CPU使用固定大小的位向量进行算术运算,在 Z3 中,使用函数 BitVec() 创建位向量常量,BitVecVal() 返回给定位数的位向量值。

Z3 在 CTF 中的运用

re PicoCTF2013 Harder_Serial

题目如下,是一段 Python 代码,要求输入一段 20 个数字构成的序列号,然后程序会对序列号的每一位进行验证,以满足各种要求。题目难度不大,但完全手工验证是一件麻烦的事,而使用 Z3 的话,只要定义好这些条件,就可以得出满足条件的值。

首先创建一个求解器实例,然后将序列的每个数字定义为常量:

接着定义约束条件,注意,除了题目代码里的条件外,还有一些隐藏的条件,比如这一句:

因为被除数不能为 0,所以 serial[3] 不能为 0。另外,每个序列号数字都是大于等于 0,小于 9 的。最后求解得到结果。

完整的 exp 如下,其他文件在 githubarrow-up-right 相应文件夹中。

Bingo!!!

这一题简直是为 Z3 量身定做的,方法也很简单,但 Z3 远比这个强大,后面我们还会讲到它更高级的应用。

参考资料

Last updated