前言
近期在研究符号执行相关的技术。近年利用符号执行进行分析的论文在安全的顶会中出现较为频繁,因此,笔者在本文中将做一份总结和近期学习的知识分享。会由浅入深的从符号执行的基础讲起,再延伸到目前科技的最前沿,带领大家看看在符号技术利用方面,到底发展到什么程度的。
符号执行入门
那么什么是符号执行(symbolic execution)技术呢?Wiki中的定义是:在计算机科学中,符号执行技术指的是通过程序分析的方法,确定哪些输入向量会对应导致程序的执行结果向量的方法。通俗的说,如果把一个程序比作DOTA英雄,英雄的最终属性值为程序的输出(包括攻击力、防御力、血槽、蓝槽),英雄的武器出装为程序的输入(出A杖还是BKB)。那么符号执行技术的任务就是,给定了一个英雄的最终属性值,分析出该英雄可以通过哪些出装方式达到这种最终属性值效果。(咚咚咚!LGD明年还会回来的!)
可以发现,符号执行技术是一种白盒的静态分析技术。即,分析程序可能的输入需要能够获取到目标源代码的支持。同时,它是静态的,因为并没有实际的执行程序本身,而是分析程序的执行路径。如果把上述英雄的最终属性值替换成程序形成的bug状态,比如,存在数组越界复制的状态,那么,我们就能够利用此技术挖掘漏洞的输入向量了。
这里再举一个简单的例子,让大家有深刻的理解。
以下面的源代码为例子:
int m=M, n=N, q=Q;
int x1=0,x2=0,x3=0;
if(m!=0)
{
x1=-2;
}
if(n<12)
{
if(!m && q)
{
x2=1;
}
x3=2;
}
assert(x1+x2+x3!=3)
上述代码是一个简单的c语言分支结构代码,它的输入是M,N,Q三个变量;输出是x1,x2,x3的三个变量的和。我们这里设置的条件是想看看什么样的输入向量的情况下,得到的三个输出变量的和等于3. 那么我们通过下面的树形结构来看看所有的情况:
上面的分析图把所有可能的情况都列举出来了,其中,叶子节点显示的数值表示当前输入情况下,可以得到的数值。(比如,如果英雄出装是M^(N<12),那么最终的属性值R=0)。其中M^(N<12)表达的是,M是非零值且N要小于12,Q为任意值的情况下,得到R=0。可以发现,当条件为~M^(N<5)^Q时,得到了最终结果等于3.即,我们通过这种方式逆向发现了输入向量。如果把结果条件更改为漏洞条件,理论上也是能够进行漏洞挖掘了。
对于如何根据最终得到的结果求解输入向量,已经有很多现成的数学工具可以使用。上述问题其实可以规约成约束规划的求解问题(更详细的介绍看这里:Constraint_programming )。比较著名的工具比如SMT(Satisfiability Modulo Theory,可满足性模理论)和SAT。
但是在实际的漏洞分析过程中,目标程序可能更加复杂,没有我们上面的例子这么简单。实际的程序中,可能包含了与外设交互的系统函数,而这些系统函数的输入输出并不会直接赋值到符号中,从而阻断了此类问题的求解。比如下面的这个包含了文件读写的例子ÿ