CN104965788A - 一种代码静态检测方法 - Google Patents

一种代码静态检测方法 Download PDF

Info

Publication number
CN104965788A
CN104965788A CN201510390576.8A CN201510390576A CN104965788A CN 104965788 A CN104965788 A CN 104965788A CN 201510390576 A CN201510390576 A CN 201510390576A CN 104965788 A CN104965788 A CN 104965788A
Authority
CN
China
Prior art keywords
instruction
value
type
result
blkstate
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Granted
Application number
CN201510390576.8A
Other languages
English (en)
Other versions
CN104965788B (zh
Inventor
屈鸿
王晓斌
李奇军
符明晟
邵领
王留帅
袁健
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
University of Electronic Science and Technology of China
Original Assignee
University of Electronic Science and Technology of China
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by University of Electronic Science and Technology of China filed Critical University of Electronic Science and Technology of China
Priority to CN201510390576.8A priority Critical patent/CN104965788B/zh
Publication of CN104965788A publication Critical patent/CN104965788A/zh
Application granted granted Critical
Publication of CN104965788B publication Critical patent/CN104965788B/zh
Active legal-status Critical Current
Anticipated expiration legal-status Critical

Links

Abstract

本发明公开了一种代码静态检测方法,具体涉及一种基于LLVM IR的符号执行算法——利用给予程序变量符号初值技术领域,解决现在技术中的静态检测方法支持语言单一、不能较好的处理指针和别名问题,探测潜在的程序漏洞等问题。本发明的步骤:(1)获取源代码,并将源代码进行预处理转换为LLVM汇编程序;(2)将转换得到的LLVM汇编程序,运用符号执行算法模拟解释执行LLVM汇编程序,并记录各个变量在不同路径上的符号值和各条路径的约束条件;(3)根据记录各个变量在不同路径上的符号值和各条路径的约束条件,调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束,判断程序是否存在潜在漏洞。本发明应用于源代码中的数组越界,除零错误和空指针引用。

Description

一种代码静态检测方法
技术领域
一种代码静态检测方法,应用于源代码中的数组越界,除零错误和空指针引用,涉及编译器理论,程序内存模型,SMT约束求解,形式化方法,一阶谓词逻辑等领域,具体涉及一种基于LLVM IR的符号执行算法——利用给予程序变量符号初值技术领域。
背景技术
随着计算机技术的发展,软件系统已经被运用到我们生活的方方面面,依赖互联网我们足不出户就可以网上购物(淘宝/京东)、接受远程教育(coursera/网易公开课)、与朋友聊天(QQ/微信)、甚至网上看病等。
2014年9月19日,阿里巴巴在美国纽交所正式进行IPO,其市值超过facebook成为仅次于google的全球第二大互联网公司。这也标志着我国IT业逐渐走向国际市场。近年来我国IT业发展迅速,2013年我国软件业实现业务收入总量规模达到3.1万亿元,同比增长24.6%,比同期电子信息制造业增速高13.6%。与此同时我国软件应用水平和产业创新能力也得到逐步提升。2013年软件类产品登记数总数达39821件,相比同期增长11.5%,软件著作权登记数量达164349件,相比同期增长18.04%,整个行业总体投入研发经费同比增长6.3%。
软件技术的发展极大的方便了我们的生活,提高了我们的工作效率。软件业蓬勃发展,未来会有更多人员投入到软件开发中,软件开发对从业人员的门槛也会越来越低。新华(大连)软件和信息技术服务业发展指数报告指出2014年中国信息技术服务业和软件开发从业人员高达470万,是2001年数据的15倍。
但是另一方面由于软件的广泛使用使得一旦软件出现崩溃或者被恶意攻击便会带来极为严重的后果,所以对于软件质量的要求将越来越高。这种从业门槛的降低和对软件质量要求的增高无疑形成了一对激烈的矛盾。这种矛盾的直接后果就是对软件测试、源代码分析等技术提出了更高的要求。
近些年来,随着民用航空事业的蓬勃发展和部队飞行训练任务的逐步加重,国内飞机的数目与日俱增,空域内航线日益密集,飞行器流量日益加大,又由于飞机只能在平流层平稳飞行,使得飞机可飞行空间是一个相对有限的空间,这就造成了空域变得越来越拥挤,拥挤意味着冲突。无论是航线设定过密,飞机本身故障或是风力等环境因素,都可能造成飞机发生碰撞冲突。由于飞机运输的特殊性,一旦飞机在空中发生冲突,就很难保证乘客人身及财产安全。同时,如不能有效地疏通这种拥挤,也会降低空域资源的利用率,极大地阻碍国家航空事业的发展。因此,能够提前预知冲突的发生,并及早地采取有效的防范措施就显得尤为重要。
符号执行能够有效的给复杂的程序生成高覆盖率的测试用例和发现深层次的漏洞,最近几年受到了极大的关注。符号执行的核心思想在1976年被引入,但最近几年才被有效的运用于实际工程中。主要原因在于约束求解器等技术的发展符号执行的一个关键目标是在给定的时间内探索尽量多不同的程序路径,对于每一条路径(1)生成一组具体的输入使得程序在实际运行时能够执行这条路径。(2)在该路径上检测各种常见的错误如断言失败,未捕获异常,内存泄露等。能生成具体的测试用例是符号执行的一个强大能力。从软件测试的角度,它能产生高覆盖率的的输入,从探测bug的角度,它能给开发者提供一个能触发bug的输入,以便于帮助开发者分析漏洞是如何发生的。在漏洞的检测中,从某种程度上来说符号执行相比于传统的测试执行技术如Valgrind和Purify等更加强大。因为这些传统工具依赖于实际执行程序所输入的参数,如果这些参数不能够触发漏洞,则这些工具是无法发现这些漏洞的。另外符号执行不但能探测到传统测试工具能检查到的漏洞,如内存泄露等,更能检查到一些程序高层次的性质,比如复杂的程序断言逻辑。
发明内容
本发明针对现有技术的不足之处提供了一种代码静态检测方法,解决现在技术中的静态检测方法支持语言单一、不能较好的处理指针和别名问题,探测潜在的程序漏洞等问题。
为了实现上述目的,本发明采用的技术方案为:
一种代码静态检测方法,其特征在于,如下步骤:
(1)获取源代码,并将源代码进行预处理转换为LLVM汇编程序;
(2)将转换得到的LLVM汇编程序,运用符号执行算法模拟解释执行LLVM汇编程序,并记录各个变量在不同路径上的符号值和各条路径的约束条件;
(3)根据记录各个变量在不同路径上的符号值和各条路径的约束条件,调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束,判断程序是否存在潜在漏洞。
进一步,所述步骤(1)中,将源代码进行预处理转换为LLVM汇编程序的具体步骤如下:
(11)调用编译器clang检查源代码是否存在语法错误,如有错误,修改语法错误直到编译器不报错,以得到语法正确的源代码;
(12)将得到的语法正确的源代码利用编译器Clang转化为LLVM汇编程序。
进一步,所述步骤(2)中,模拟解释执行LLVM汇编程序的具体步骤如下:
(21)调用LLVM基础库,将LLVM汇编程序输入LLVM基础库并转化为文本形式程序的LLVM汇编程序CFG,LLVM基础库返回的CFG中包含的具体对象有全局变量,全局变量对应全局变量对象g,每一个函数LLVM汇编程序对应函数对象F,每个函数对象F包含函数参数p和若干代码块b,而每个代码块又包含多个指令instruction;
(22)对于被分析函数对象,为其引用到全局变量对象、函数参数和内存对象,内存对象是一个四元组,其结构为:
SValue=(tp×val×ptr×data)∈(llvm::Type*×String×SValue*×(int×SValue*)),
其中SValue是内存对象,用于描述各变量的符号状态,tp、val、ptr和data分别是,tp代表数据的类型,val代表符号值,表示一个字符串的类型为String,ptr代表符号指针值,data是一个数字到符号值指针的映射,用于记录数组类型和结构体,llvm::Type*,String,SValue*,(int×SValue*)分别是对应分量tp、val、ptr、data的数据类型,Llvm::Type是LLVM基础库中的一个描述LLVM汇编程序类型的对象,LLVM::Type*代表该类型对象的指针,String是C++标准库中的字符串对象类型,SValue是内存对象,也称为符号对象,int代表C++基础类型中的整数类型;
系统的初始状态为PT、PTS,首先初始化一个长度为1的根路径PT,将该路径存放到可执行路径PTS中,解释执行的过程类似于一个有限状态机的状态迁移,解释器的状态以PT和PTS进行描述,每解释执行一条指令状态就发生迁移,将初始化好的第一条路径放入PTS中;这些初始化的过程可以表示为如下指令语义模式操作公式:
P T . c u r r B = F . f i r s t B l o c k , P T . var s &lsqb; g = S V a l u e ( g . t y p e ( ) ) , p = S V a l u e ( p . t y p e ( ) ) &rsqb; , P T . i t = P T . c u r r B . f i r s t I n s t r u c t i o n , P T S . a d d ( P T ) < P T , P T S > { in i t } &RightArrow; < P T , P T S > ;
其中,PT代表当前正在探索的可行路径,PTS代表目前发现的所有未探测的可行路径集合,B代表LLVM汇编程序CFG中的一个代码块,PT.currB代表当前路径正在探索的代码块,F表示当前正在被分析的函数,F.firstBlock代表函数的第一个代码块,PT.vars代表路径上所有的变量,它是一个映射,PT.vars[key=value]代表的是将键值对(key,value)存入映射vars当中,其中key代表映射的索引,value代表映射的key索引所对应的值,key和value可以被替换为各种值,公式中PT.vars[g=SValue(g.type()),p=SValue(p.type())]是PT.vars[g=SValue(g.type())]和PT.vars[p=SValue(p.type())]的缩写形式,代表将键值对(g,SValue(g.type()))和(p,SValue(p.type))存入PT.vars中,此处的g和p作为key,SValue(g.type())和SValue(p.type)则作为值value部分,SValue(type)是符号对象的构造函数,它有一个参数type表明符号对象,即表示数据的类型,g代表全局变量,g.type()表示全局变量的类型,p代表函数F的参数,p.type()表示参数的类型,SValue(p.type())代表根据参数p的类型构造内存对象,SValue(g.type())代表根据全局变量g的类型构造内存对象,PT.it代表当前路径所执行到的指令,PT.currB.firstInstruction代表当前路径当前块的第一条指令,PTS.add(PT)将一条路径PT添加到可行路径集合PTS中,init代表这是一个初始化动作,在任何具体指令分析之前应该进行该操作,它不对应任何指令;
(23)判断PTS是否为空,如果PTS不为空,则弹出其中一条路径存放到PT中,并执行步骤(24);若PTS为空,则模拟执行结束;
(24)若PT路径中的当前执行位置PT.it所指指令为非跳转指令,则执行步骤(25),否则执行(26);
(25)根据不同的非跳转指令类型,执行不同的指令语义模式操作公式;
(26)根据不同的跳转指令类型,执行不同的指令语义模式操作公式;
(27)判断步骤(25)或步骤(26)直到发现致命性错误或者PTS为空,否则PT.it++继续执行步骤(23)至步骤(26)。
进一步,所述步骤(22)中,指令语义模式的结构如下公式所示:
c o m p u t a t i o n < c u r r e n t s t a t e > { s t m t } &RightArrow; < e n d s t a t e > ,
其中<current state>代表系统的当前状态,<end state>代表语句执行后程序的状态,computation是状态迁移计算过程的一些细节展示,stmt代表被执行的语句包含的所有指令,具体指令如下所示:
(I1)ret[void|<type><value>];
(I2)br i1<cond>,label<iftrue>,label<iffalse>;
(I3)br label<dest>;
(I4)<result>=[add|sub|mul|udiv|sdiv|urem|srem]<ty><op1>,<op2>;
(I5)<result>=icmp<cond><ty><op1>,<op2>;
(I6)<result>=alloca<type>[,<ty><NumElements>];
(I7)<result>=load<ty>*<pointer>;
(I8)store<ty><value>,<ty>*<pointer>;
(I9)<result>=getelementptr inbounds<pty>*<ptrval>{,<ty><idx>}*;
(I10)<result>=invoke<ptr to function ty><function ptr val>(<function args>;
I1代表函数返回指令,它分为有参数值和无参数值返回,无参数值ret void,有参数值ret<type><value>,其中<value>代表具体的返回值,<type>代表返回值的类型,ret表示这是一条函数返回;
I2代表有条件跳转指令,其中<cond>代表跳转的条件,i1代表布尔类型,<iftrue>代表条件为真时跳转到的目标代码块,<iffalse>代表条件为假时跳转到的目标代码块,label代表是一个标签类型,br表示这是一条跳转指令;
I3代表无条件跳转指令,<dest>代表无条件跳转到的目标代码块,label代表是一个标签类型,br表示这是一条跳转指令;
I4代表算术运算指令,[add|sub|mul|udiv|sdiv|urem|srem]代表的是算术运算的具体操作类型,<ty>代表两个操作数的数据类型,<op1>和<op2>分别是两个操作数,<result>代表算术运算结果;
I5代表比较指令,其中<cond>代表的是比较的具体类型,如大于、小于还是等于,<ty>代表操作数的数据类型,<op1>和<op2>分别代表比较的两个操作数,icmp表示这是一条比较指令,<result>代表比较结果;
I6代表的是局部变量内存分配指令,<type>代表变量的类型,该指令的语义是为局部变量在栈上分配一定的内存,模拟执行时,会为指定局部变量分配相应的内存对象SValue,<ty><NumElements>为可选部分,<NumElements>代表分配的元素的个数,<ty>代表<NumElements>的数据类型,alloca表示这是一个栈内存分配分配指令,<result>代表分配的栈内存对象;
I7是内存加载指令,<pointer>代表具体的指针,<ty>代表指针指向对象的数据类型,load表示这是一条内存加载指令,<result>代表被加载的数据;
I8是内存存贮指令,<pointer>代表存贮的内存位置,<value>代表被存贮的对象;<ty>代表了被存贮对象的数据类型,store代表这是一条内存存贮指令;
I9是数组或结构体子元素获取指令,<ptrval>代表数组或结构体对象,<pty>*代表<ptrval>的类型,<idx>代表子元素所在的索引为可选部分,<ty>代表<idx>的数据类型,getelementptr inbounds表示这是一条子元素获取指令,<result>是指令执行结果;
I10是函数调用指令,<function ptr val>代表被调用函数对象,<ptr to function ty>表示函数的类型,<function args>表示函数参数,invoke表示这是一条函数调用指令,<result>表示函数调用返回值;
上述各指令的各个分量都可以从对象化的LLVM汇编程序中获取,并且以对象的形式存在。
进一步,所述步骤(25)中,执行不同的指令语义模式操作公式的公式如下:
若当前指令是函数返回指令(I1),按如下公式迁移系统状态:
P T = P T S . p o p ( ) < P T , P T S > { r e t &lsqb; v o i d | < t y p e > < v a l u e > &rsqb; } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PTS.pop()表示从当前的可行路径集合中弹出一条路径,ret[void|<type><value>]表示一条函数返回指令;
若当前指令是算术运算指令(I4),按如下公式迁移系统状态:
s v _ o p 1 = P T . b l k S t a t e &lsqb; o p 1 &rsqb; , s v _ o p 2 = P T . b l k S t a t e &lsqb; o p 2 &rsqb; , o p _ h a n d = &lsqb; a d d | s u b | m u l | u d i v | s d i v | u r e m | s r e m &rsqb; , s v _ r e s u l t = S V a l u e : : a r i t h ( s v _ o p 1 , s v _ o p 2 , o p _ h a n d ) , P T . b 1 k S t a t c &lsqb; < r e s u l t > = s v _ r c s u l t &rsqb; < P T , P T S > { < r e s u l t > = &lsqb; a d d | s u b | m u l | u d i v | s d i v | u r e m | s r e n &rsqb; < t y > < o p 1 > , < o p 2 > } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的路径,PTS是当前已经发现的所有可行路径集合,PT.blkState是路径PT的当前块所对应的状态,其中记录了解释执行过程中各个变量的值的变化情况,<result>=[add|sub|mul|udiv|sdiv|urem|srem]<ty><op1>,<op2>是算术运算指令,op1和op2是算术运算的两个操作数,result表示算术运算的结果,PT.blkState[op1]表示的是以op1为键值在映射PT.blkState中查询对应的值,sv_op1是一个临时变量用于保存PT.blkState[op1]的结果,sv_op2也是一个临时变量,用于保存PT.blkState[op2],op_hand表示具体的操作类型,它可以是加减乘除等算术运算,分别对应add、sub、mul、udiv、urem、srem,SValue::arith是符号对象SValue的一个静态方法,它对两个符号值进行算术运算并返回一个表示结果的符号值,sv_result是一个临时对象用于记录算术运算的结果,PT.blkState[key=value]是将键值对(key,value)存入映射PT.blkState中,key表示任意键值,value表示任意值,此处PT.blkState[<result>=sv_result]表示将键值对(<result>,sv_result)存入映射PT.blkState中;
若当前指令是比较指令(I5),按如下公式迁移系统状态:
d o n o t h i n g < P T , P T S > { < r e s u l t > = i c m p < c o n d > < t y > < o p 1 > , < o p 2 > } < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,donothing表示不执行任何操作,<result>=icmp<cond><ty><op1>,<op2>表示一条比较指令;
若当前指令是局部变量内存分配指令(I6),按如下公式迁移系统状态:
P T . var s &lsqb; < r e s u l t > = S V a l u e ( t y p e ) &rsqb; < P T , P T S > { < r e s u l t > = a l l o c a < t y p e > &lsqb; , < t y > < N u m E l e m e n t s > &rsqb; } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.vars表示当前路径的变量集合,PT.vars[key=value]表示将键值对(key,value)存入PT.vars中,key表示任意键,value表示任意值,此处PT.vars[<result>=SValue(type)]表示将键值对(<result>,SValue(type))存入PT.vars中,SValue(t)是符号对象的构造函数,其中t表示构造函数的参数它指定一个数据类型,构造函数根据t的类型构造一个符号对象,在实际函数调用时t可以被替换为任意表示数据类型的变量,如此处的type,此处type表示了所分配对象的类型,它来自于分母上的指令的type字段,<result>=alloca<type>[,<ty><NumElements>]表示这是一条局部变量内存分配指令;
若当前指令是内存加载指令(I7),按如下公式迁移系统状态:
P T . b l k S t a t e &lsqb; < r e s u l t > = P T . b l k S t a t e &lsqb; p o int e r &rsqb; . p t r &rsqb; < P T , P T S > { < r e s u l t > = l o a b < t y > * < p o int e r > } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,pointer表示被加载的指针,result表示指令加载的结果,PT.blkState[pointer]以pointer为键值在pt.blkState中查询相应的值,该值是一个符号对象,PT.blkState[Pointer].ptr表示该符号对象的ptr值,<result>=load<ty>*<pointer>表示这是一条内存加载指令;
若当前指令是内存存贮指令(I8),按如下公式迁移系统状态:
P T . b l k S t a t e &lsqb; p o int e r &rsqb; . p t r = P T . b l k S t a t e &lsqb; v a l u e &rsqb; < P T , P T S > { s t o r e < t y > < v a l u e > , < t y > * < p o int e r > } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,pointer表示存贮位置的指针,value表示存入的值,PT.blkState[pointer]以pointer为键值在pt.blkState中查询相应的值,该值是一个符号对象,PT.blkState[Pointer].ptr表示该符号对象的ptr值,PT.blkState[value]表示以value为键在PT.blkState中查询其相应的值,store<ty><value>,<ty>*<pointer>表示这是一条内存存贮指令;
若当前指令是数组或结构体子元素获取指令(I9)且索引idx是常数,按如下公式迁移系统状态:
s v _ p t r = P T . b l k S t a t e &lsqb; p t r v a l &rsqb; , s v _ e l e m e n t = s v _ p t r . d a t a &lsqb; i d x &rsqb; , P T . b l k S t a t e &lsqb; < r e s u l t > = s v _ e l e m e n t &rsqb; < P T , P T S > { < r e s u l t > = g e t e l e m e n t p t r i n b o u n d s < p t y > * < p t r v a l > { , < t y > < i d x > } } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,ptrval表示目标结构,是一个数组或者一个结构体,idx表示要获取的对象在ptrval中的子元素索引,PT.blkState[ptrval]表示以ptrval为键值在PT.blkState映射中所对应的值对象,sv_ptr是一个临时变量,用于记录查询到的值对象,sv_ptr.data[idx]表示取sv_ptr的data成员的第idx个子元素,结果存贮在临时变量sv_element中,PT.blkState[<result>=sv_element]代表将键值对(<result>,sv_element)放入映射PT.blkState中,其中<result>代表指令执行结果的标示符,<result>=getelement ptr inbounds<pty>*<ptrval>{,<ty><idx>}表示这是一条数组或结构体子元素获取指令;
若当前指令是数组或结构体子元素获取指令(I9)且索引idx不为常数,按如下公式迁移系统状态:
s v _ p t r = P T . b l k S t a t e &lsqb; p t r v a l &rsqb; , s v _ i d x = P T . b l k S t a t e &lsqb; i d x &rsqb; , P T . b l k S t a t e . p c s . a d d ( &prime; a s s e r t ( s v _ i d x . v a l = = n ) &prime; ) , n &Element; ( 0 , a r r a y _ max _ i n d e x ) , s v _ e l e m e n t = s v _ p t r . d a t a &lsqb; n &rsqb; , P T . b l k S t a t e &lsqb; < r e s u l t > = s v _ e l e m e n t &rsqb; < P T , P T S > { < r e s u l t > = g e t e l e m e t p t r i n b o u n d s < p t y > * < p t r v a l > { , < t y > < i d x > } } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,ptrval表示目标结构,是一个数组或者一个结构体,idx表示要获取的对象在ptrval中的子元素索引,PT.blkState[ptrval]表示以ptrval为键值在PT.blkState映射中所对应的值对象,sv_ptr是一个临时变量,用于记录以ptrval为键值查询到的值对象,PT.blkState[idx]表示以idx为键值在PT.blkState映射中所对应的值对象,sv_idx是一个临时变量,用于记录以idx为键值查询到的值对象,PT.blkState.pcs表示当前块状态中记录的约束集合,PT.blkState.pcs.add表示往当前块约束集中添加一条约束,’assser(sv_idx.val==n)’表示一个字符串,其中n取遍数组的每个索引,sv_idx.val表示索引对象sv_idx的val字段的值,该值也是索引对象的符号值的字符串形式,array_max_index表示数组的最大索引,n表示区间[0,array_max_index]中的任意值,sv_ptr.data[n]表示取sv_ptr的data成员的第n个子元素,结果存贮在临时变量sv_element中,PT.blkState[<result>=sv_element]代表将键值对(<result>,sv_element)放入映射PT.blkState中,其中<result>代表指令执行结果的标示符,<result>=getelement ptrinbounds<pty>*<ptrval>{,<ty><idx>}表示这是一条数组或结构体子元素获取指令;
若当前指令是函数调用指令(I10),按如下公式迁移系统状态:
P T . b l k S t a t e &lsqb; < r e s u l t > = S V a l u e ( r e s u l t . g e t T y p e ( ) ) &rsqb; < P T , P T S > { < r e s u l t > = i n v o k e < p t r t o f u n c t i o n t y > < f u n c tion prt val > ( < f unction arg s > } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合;PT.blkState表示当前路径的当前块状态,SValue(t)是符号对象的构造函数,其中t表示构造函数的参数,它指定一个数据类型,具体函数调用时t可以被替换为其他任意表示数据类型的值,如此处的result.getType(),result表示指令执行的返回值,此处SValue(result.getType())表示根据result的类型构造一个内存对象,其中result.getType()表示返回result的数据类型,整个PT.blkState[<result>=SValue(result.getType())]表示将键值对(<result>,SValue(result.getType()))放入PT.blkState中,<result>=invoke<ptr tofunction ty><function ptr val>(<function args>)表示这是一条函数调用指令;
进一步,所述步骤(26)中,执行不同的指令语义模式操作公式的公式下:
若跳转是无条件跳转指令(I3),按如下公式迁移系统状态:
P T . c u r r B = L 2 B ( l a b e l < d e s t > ) , P T . i t = P T . c u r r B . f i r s t I n s t r u c t i o n < P T , P T S > { br label < d e s t > } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.currB表示当前路径的当前块,L2B表示将一个标签转换为它具体表示的块,label<dest>表示跳转的目标块,PT.it表示当前路径执行到的指令位置,PT.currB.firstInstruction表示路径当前块的第一条指令,br label<desl>表示这是一条无条件跳转指令;
若跳转是有条件跳转指令(I2),按如下公式迁移系统状态:
P T _ T . c u r r B = L 2 B ( l a b e l < i f t r u e > ) , P T _ T . i t = P T _ T . c u r r B . f i r s t I n s t r u c t i o n , P T _ T . b l k S t a t e = B l o c k S t a t e ( o r e = P T . b l k S t a t e , p c s . a d d ( g e t P C S ( c o n d , t r u e ) ) ) P T _ T . var s = P T . var s , P T _ F . c u r r B = L 2 B ( l a b e l < i f f a l s e > ) , P T _ F . i t = P T _ F . c u r r B . f i r s t I n s t r u c t i o n , P T _ F . b l k S t a t e = B l o c k S t a t e ( p r e = P T . b l k S t a t e , p c s . a d d ( g e t P C S ( c o n d , t r u e ) ) ) , P T _ F . var s = P T . var s , i f ( P T _ T fe a s i b l e ) P T S . a d d ( P T _ T ) , i f ( P T _ F fe a s i b l e ) P T S . a d d ( P T _ F ) , P T = P T S . p o p ( ) < P T , P T S > { br il < c o n d > , l a b e l < i f t r u e > , l e b e l < i f f a l s e > } &RightArrow; < P T , P T S > ;
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT_T和PT_F表示两条即将创建的新路径,PT_T.currB表示PT_T路径的当前块,L2B表示将一个标签转换为它具体表示的块,label<iftrue>表示true分支的目标块,label<iffalse>表示false分支的目标块,PT_T.it表示PT_T路径执行到的指令位置,PT_T.currB.firstInstruction表示PT_T路径当前块的第一条指令,BlockState()是块状态的构造函数,它主要用于设置其pre字段和pcs字段,BlockState(pre=PT.blkState,pcs.add(getPCS(cond,true)))返回一个pre字段指向PT.blkState,pcs字段里面包含约束getPCS(cond,true)的块状态对象,getPCS()是一个函数,它将一条比较指令转换一个字符串形式的约束条件,PT_T.vars为PT_T路径的变量集合,PT.vars为当前路径的变量集合,PT_F的对应变量与PT_T类似,if(PT_T feasible)和if(PT_F feasible)代表判断PT_T和PT_F路径是否可行,判断路径是否可行请参考步骤(3),它通过对PT中的约束条件进行一些计算从而得到结果,若约束条件可满足则代表该分支可行,PTS.add()和PTS.pop()分别表示往PTS中添加一条路径和弹出一条路径,br i1<cond>,label<iftrue>,label<iffalse>表示这是一条有条件跳转指令。
进一步,所述步骤(3)中,调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束的具体步骤如下:
(31)从PT.blkState开始顺着PT.blkState.pre链向上遍历到头结点,对于每一个blkState对象,将其pcs中的所有约束顺次连接,形成一个总的路径约束PC1;
(32)扫描PC1,探测其中的自由变量,自由变量符合正则表达式val_[0-9]+,为每一个自由变量生成声明,对于自由变量val_i生成声明字符串’(declare-const val_i(_BitVec 32)))’,将声明连接并记为DF;
(33)将DF和PC1连接,形成符合SMTLIB2.0规范的SMT约束PC,将PC送交Z3求解;
(34)将Z3返回的结果z3::sat和z3::unsat分别表示为true和false的形式,然后再返回,z3::sat对应true,z3::unsat对应false,其中z3::sat和z3::unsat是z3的c++库中定义的两个常数分别表示可满足和不可满足。
与现有技术相比,本发明的优点在于:
一、通过将高级语言源代码转换为LLVM IR,可以实现跨语言的分析;
二、通过采用一种类似于树状结构的内存模型,可以解决源代码解释执行过程中的各种难题如变量指针与别名、不确定大小的数据对象以及变量类型转换等;
三、符号值结构的良好设计,可以记录模拟执行过程中变量更多的信息;
四、在约束的表示上采用了标准的SMTLIB2.0形式,检测方法可以使用任何标准的SMT求解器,实现了SMT求解器无关性;
五、将索引值为符号值的对象具体化,可以更加精确地跟踪路径上的变量信息;
六、通过模拟解释程序的方法收集各变量在各条执行路径上的状态并检查这些状态是否符合约束,从而探测潜在的程序漏洞。
附图说明
图1为本发明中算法的框架示意图;
图2为本发明的变量抽象结构示意图;
图3为本发明中一个函数的CFG示意图;
图4为本发明中一个函数的探测状态树示意图;
图5为本发明中算法的详细流程图;
图6为本发明中约束条件的生成算法示意图;
图7为本发明中一个编译单元的所有对象示意图。
具体实施方式
下面结合附图和实施例对本发明作进一步的说明。
一种代码静态检测方法,步骤如下:
(1)获取源代码,并将源代码进行预处理转换为LLVM汇编程序;即调用clang编译器获取c/c++/object-c源代码,并将源代码进行预处理转换为LLVM汇编程序;将源代码进行预处理转换为LLVM汇编程序的具体步骤如下:
(11)调用编译器clang检查c/c++/object-c源代码是否存在语法错误,如有错误,修改语法错误直到编译器不报错,以得到语法正确的源代码;
(12)将得到的语法正确的源代码利用编译器Clang转化为LLVM汇编程序。
(2)将转换得到的LLVM汇编程序,运用符号执行算法模拟解释执行LLVM汇编程序,并记录各个变量在不同路径上的符号值和各条路径的约束条件;模拟解释执行LLVM汇编程序的具体步骤如下:
(21)调用LLVM基础库(FunctionPass::runOnFunction(Function&F)),将LLVM汇编程序输入LLVM基础库并转化为文本形式程序的LLVM汇编程序CFG,LLVM基础库返回的CFG中包含的具体对象有全局变量,全局变量对应全局变量对象g,每一个函数LLVM汇编程序对应函数对象F,每个函数对象F包含函数参数p和若干代码块b,而每个代码块又包含多个指令instruction,转化的过程就是进行词法分析和语法分析,这部分工作由LLVM基础库自动实现;
(22)对于被分析函数对象,为其引用到全局变量对象、函数参数和内存对象,内存对象是一个四元组,其结构为:
SValue=(tp×val×ptr×data)∈(llvm::Type*×String×SValue*×(int×SValue*)),
其中SValue是内存对象,用于描述各变量的符号状态,tp、val、ptr和data分别是,tp代表数据的类型,val代表符号值,表示一个字符串的类型为String,ptr代表符号指针值,data是一个数字到符号值指针的映射,用于记录数组类型和结构体,llvm::Type*,String,SValue*,(int×SValue*)分别是对应分量tp、val、ptr、data的数据类型,Llvm::Type是LLVM基础库中的一个描述LLVM汇编程序类型的对象,LLVM::Type*代表该类型对象的指针,String是C++标准库中的字符串对象类型,SValue是内存对象,也称为符号对象,int代表C++基础类型中的整数类型;
系统的初始状态为PT、PTS,首先初始化一个长度为1的根路径PT,将该路径存放到可执行路径PTS中,解释执行的过程类似于一个有限状态机的状态迁移,解释器的状态以PT和PTS进行描述,每解释执行一条指令状态就发生迁移,将初始化好的第一条路径放入PTS中;这些初始化的过程可以表示为如下指令语义模式操作公式:
P T . c u r r B = F . f i r s t B l o c k , P T . var s &lsqb; g = S V a l u e ( g . t y p e ( ) ) p = S V a l u e ( p , t y p e ( ) ) &rsqb; , P T . i t = P T . c u r r B . f i r s t I n s t r u c t i o n , P T S . a d d ( P T ) < P T , P T S > { in i t } &RightArrow; < P T , P T S > ;
该公式用于描述系统的初始化过程,分子上的前几个赋值语句,用于构造出第一条路径的各参数,然后将该路径放入PTS中,PT.currB=F.firsBlock表示第一条路径的当前代码块为函数的第一个代码块,F.firstBlock表示函数对象的第一个代码块对象,PT.vars记录了路径上的所有变量,它是一个映射,PT.vars[g=Svalue(g.type())和p=Svalue(p.type())]的作用是在路径的变量中为全局变量和参数变量赋符号初值,Svalue(g.type())调用SValue对象的构造函数,根据具体的类型返回符号值对象,Svalue的构造函数SValue(llvm::type*t)根据参数的t类型不同对不同的分量进行初始化,如果t为整数类型则将val分量的初始值置为’val_i’,如果t是指针类型则将ptr的初始值设为Svalue(t‐>getPointType()),如果t是数组或者结构体,对t的所有分量i,有data[i]=Svalue(t.getIElement(i)),其中t.getElement(i)表示返回复合类型t的第i的元素的类型,最后PTS.add(PT)将初始化好的第一条路径放入PTS中。
指令语义模式的结构如下公式所示:
c o m p u t a t i o n < c u r r e n t s t a t e > { s t m t } &RightArrow; < e n d s t a t e > ,
其中<current state>代表系统的当前状态,<end state>代表语句执行后程序的状态,computation是状态迁移计算过程的一些细节展示,stmt代表被执行的语句包含的所有指令,具体指令如下所示:
(I1)ret[void|<type><value>];
(I2)br i1<cond>,label<iftrue>,label<iffalse>;
(I3)br label<dest>;
(I4)<result>=[add|sub|mul|udiv|sdiv|urem|srem]<ty><op1>,<op2>;
(I5)<result>=icmp<cond><ty><op1>,<op2>;
(I6)<result>=alloca<type>[,<ty><NumElements>];
(I7)<result>=load<ty>*<pointer>;
(I8)store<ty><value>,<ty>*<pointer>;
(I9)<result>=getelementptr inbounds<pty>*<ptrval>{,<ty><idx>}*;
(I10)<result>=invoke<ptr to function ty><function ptr val>(<function args>;
I1代表函数返回指令,它分为有参数值和无参数值返回,无参数值ret void,有参数值ret<type><value>,其中<value>代表具体的返回值,<type>代表返回值的类型,ret表示这是一条函数返回;
I2代表有条件跳转指令,其中<cond>代表跳转的条件,i1代表布尔类型,<iftrue>代表条件为真时跳转到的目标代码块,<iffalse>代表条件为假时跳转到的目标代码块,label代表是一个标签类型,br表示这是一条跳转指令;
I3代表无条件跳转指令,<dest>代表无条件跳转到的目标代码块,label代表是一个标签类型,br表示这是一条跳转指令;
I4代表算术运算指令,[add|sub|mul|udiv|sdiv|urem|srem]代表的是算术运算的具体操作类型,<ty>代表两个操作数的数据类型,<op1>和<op2>分别是两个操作数,<result>代表算术运算结果;
I5代表比较指令,其中<cond>代表的是比较的具体类型,如大于、小于还是等于,<ty>代表操作数的数据类型,<op1>和<op2>分别代表比较的两个操作数,icmp表示这是一条比较指令,<result>代表比较结果;
I6代表的是局部变量内存分配指令,<type>代表变量的类型,该指令的语义是为局部变量在栈上分配一定的内存,模拟执行时,会为指定局部变量分配相应的内存对象SValue,<ty><NumElements>为可选部分,<NumElements>代表分配的元素的个数,<ty>代表<NumElements>的数据类型,alloca表示这是一个栈内存分配分配指令,<result>代表分配的栈内存对象;
I7是内存加载指令,<pointer>代表具体的指针,<ty>代表指针指向对象的数据类型,load表示这是一条内存加载指令,<result>代表被加载的数据;
I8是内存存贮指令,<pointer>代表存贮的内存位置,<value>代表被存贮的对象;<ty>代表了被存贮对象的数据类型,store代表这是一条内存存贮指令;
I9是数组或结构体子元素获取指令,<ptrval>代表数组或结构体对象,<pty>*代表<ptrval>的类型,<idx>代表子元素所在的索引为可选部分,<ty>代表<idx>的数据类型,getelementptr inbounds表示这是一条子元素获取指令,<result>是指令执行结果;
I10是函数调用指令,<function ptr val>代表被调用函数对象,<ptr to function ty>表示函数的类型,<function args>表示函数参数,invoke表示这是一条函数调用指令,<result>表示函数调用返回值;
上述各指令的各个分量都可以从对象化的LLVM汇编程序中获取,并且以对象的形式存在。
(23)判断PTS是否为空,如果PTS不为空,则弹出其中一条路径存放到PT中,并执行步骤(24);若PTS为空,则模拟执行结束;
(24)若PT路径中的当前执行位置PT.it所指指令为非跳转指令,则执行步骤(25),否则执行(26);
(25)根据不同的非跳转指令类型,执行不同的指令语义模式操作公式,通过PT.it->getOpcode()获得指令的操作码,即指令类型,不同的指令操作码为不同的常数,getOpCode是指令对象的一个方法,它返回相应指令的操作码,操作码是一个整数,不同的指令操作码不同且为固定整数;执行不同的指令语义模式操作公式的公式如下:
若当前指令是函数返回指令(I1),按如下公式迁移系统状态:
P T = P T S . p o p ( ) < P T , P T S > { r e t &lsqb; v o i d | < t y p e > < v a l u e > &rsqb; } &RightArrow; < P T , P T S > ;
根据无参数值ret void或者有参数值ret<tyep><value>,执行操作PT=PTS.pop(),将PTS中的一条路径弹出,并将该路径的值信息赋值给PT;PTS是一个集合,具体可以实现为一个队列结构,pop()函数表示弹出最先进入它的那条路径,PT是当前探索路径;
若当前指令是算术运算指令(I4),按如下公式迁移系统状态:
s v _ o p 1 = P T . b l k S t a t e &lsqb; o p 1 &rsqb; , s v _ o p 2 = P T . b l k S t a t e &lsqb; o p 2 &rsqb; , o p _ h a n d = &lsqb; a d d | s u b | m u l | u d i v | s d i v | u r e m | s r e m &rsqb; , s v _ r e s u l t = S V a l u e : : a r i t h ( s v _ o p 1 , s v _ o p 2 , o p _ h a n d ) , P T . b 1 k S t a te &lsqb; < r e s u l t > = s v _ res u l t &rsqb; < P T , P T S > { < r e s u l t > = &lsqb; a d d | s u b | m u l | u d i v | s d i v | u r e m | s r e n &rsqb; < t y > < o p 1 > , < o p 2 > } &RightArrow; < P T , P T S > ;
对于算术运算指令<result>=[add|sub|mul|udiv|urem|srem]<ty><op1><op2>,sv_op1=PT.blkState[op1]根据操作数op1,到PT的当前块状态(blkState,它是一个标示符到符号值的映射)中查询它的符号值并将结果保存在sv_op1中,sv_op1只是计算过程中的一个中间变量,sv_op2的操作与sv_op1类似,op_hand表示指令中算术运算符具体表示的操作,SValue::arith是一个符号进行算术运算的方法,它会返回符号运算的结果,符号值是一个字符串,符号运算的结果也是一个字符串,它表示为逆波兰式,如‘v1’+‘v2’=’(+v1v2)’.一个SValue的符号值存在于其四元组结构中的val分量,sv_result=SValue::arith(sv_op1,sv_op2,op_hand),将计算出的结果保存在中间变量sv_result中,最后PT.blkState[<result>=sv_result],将变量标识符和其符号值组成的键值存入PT的当前状态块blkState中,当算术指令是做除法时,需要进行漏洞检查,假设此时指令为<result>=div op1op2,分母符号值sv_op2=PT.blkState[op2].val,然后向代码状态块中加入约束条件PT.blkState.pcs.add(‘(assert(==sv_op20))’),PT.blkState.pcs表示当前块状态的约束集,pcs.add(str)方法将字符串约束添加到pcs中,然后调用SMT求解器Z3判断当前约束是否满足,若满足则发现一个潜在风险(除零错误),若不满足,则将这个约束条件从pcs中移除;
若当前指令是比较指令(I5),按如下公式迁移系统状态:
d o n o t h i n g < P T , P T S > { < r e s u l t > = i c m p < c o n d > < t y > < o p 1 > , < o p 2 > } &RightArrow; < P T , P T S > ;
对于比较指令<result>=icmp<cond><ty><op1>,<op2>,不做任何操作,解释该指令前后,系统状态不发生变化;
若当前指令是局部变量内存分配指令(I6),按如下公式迁移系统状态:
P T . var s &lsqb; < r e s u l t > = S V a l u e ( t y p e ) &rsqb; < P T , P T S > { < r e s u l t > = a l l o c a < t y p e > &lsqb; , < t y > < N u m E l e m e n t s > &rsqb; } &RightArrow; < P T , P T S > ;
对于局部变量内存分配指令<result>=alloca<type>[,<ty><NumElements>],为<result>所标示的变量分配符号值对象,并保存在路径的变量映射vars中,vars是BS类型,BS[id=val]表示将键值对(id,val)存入BS结构的reg2sv映射中;
若当前指令是内存加载指令(I7),按如下公式迁移系统状态:
P T . b l k S t a t e &lsqb; < r e s u l t > = P T . b l k S t a t e &lsqb; p o int e r &rsqb; . p t r &rsqb; < P T , P T S > { < r e s u l t > = l o a d < t y > * < p o int e r > } &RightArrow; < P T , P T S > ;
对于内存加载指令<result>=load<ty>*<pointer>,PT.blkState[pointer]返回以pointer为键值在PT.blkState中对应的符号值对象,并以该符号值对象的ptr分量为值,将(result,ptr)键值对存入PT的块状态blkState中,内存加载操作前,需要判断pointer是否可能为空,是否可能发生空指针引用,记p=PT.blkState[pointer],q=p.ptr若p或者q等于0则存在空指针引用;
若当前指令是内存存贮指令(I8),按如下公式迁移系统状态:
P T . b l k S t a t e &lsqb; p o int e r &rsqb; . p t r = P T . b l k S t a t e &lsqb; v a l u e &rsqb; < P T , P T S > { s t o r e < t y > < v a l u e > , < t y > * < p o int e r > } &RightArrow; < P T , P T S > ;
对于内存存贮指令store<ty><value>,<ty>*<pointer>,首先取标示符value对应的符号值PT.blkState[value],然后将该值存到pointer对应的符号值PT.blkState[pointer]的ptr分量中,内存存贮操作前,需要判断是否可能发生空指针引用,记p=PT.blkState[pointer],q=p.ptr若p或者q等于0则存在空指针引用;
若当前指令是数组或结构体子元素获取指令(I9)且索引idx是常数,按如下公式迁移系统状态:
s v _ p t r = P T . b l k S t a t e &lsqb; p t r v a l &rsqb; , s v _ e l e m e n t = s v _ p t r . d a t a &lsqb; i d x &rsqb; , P T . b l k S t a t e &lsqb; < r e s u l t > = s v _ e l e m e n t &rsqb; < P T , P T S > { < r e s u l t > = g e t e l e m e n t p t r i n b o u n d s < p t y > * < p t r v a l > { , < t y > < i d x > } } &RightArrow; < P T , P T S > ;
对于数组或结构体子元素获取指令<result>=getelement ptr inbounds<pty>*<ptrval>{,<ty><idx>},sv_ptr=PT.blkState[ptrval]首先根据标识符ptrval取出其符号对象,然后sv_ptr.data[idx]根据访问的子元素索引返回子元素的符号对象,最终将(<result>,sv_element)键值对存入PT.blkstate中,idx满足正则表达式(0|[1‐9][0‐9]*)时为常数,否则为非常数,取数组的子元素时需要检测是否发生数组越界,记idx的符号值sv_idx=PT.blkState[idx].val,首先添加下界约束PT.blkState.pcs.add(‘assert(<sv_idx 0)’),调用SMT求解器Z3进行约束检查,若满足则存在数组越下界风险,若约束不能满足则弹出刚添加的约束,数组的上界检查与此类似,设数组的上界为n(该值可以从类型pty中获得),相应的添加约束’assert(>=sv_idx n)’查看是否可满足,若可满足则存在越上界的风险,检查完毕后同样将该约束移除;
若当前指令是数组或结构体子元素获取指令(I9)且索引idx不为常数,按如下公式迁移系统状态:
s v _ p t r = P T . b l k S t a t e &lsqb; p t r v a l &rsqb; , s v _ i d x = P T . b l k S t a t e &lsqb; i d x &rsqb; , P T . b l k S t a t e . p c s . a d d ( &prime; a s s e r t ( s v _ i d x . v a l = = n ) &prime; ) , n &Element; ( 0 , a r r a y _ max _ i n d e x ) , s v _ e l e m e n t = s v _ p t r . d a t a &lsqb; n &rsqb; , P T . b l k S t a t e &lsqb; < r e s u l t > = s v _ e l e m e n t &rsqb; < P T , P T S > { < r e s u l t > = g e t e l e metptr inbou n d s < p t y > * < p t r v a l > { , < t y > < i d x > } } &RightArrow; < P T , P T S > ;
对于数组或结构体子元素获取指令<result>=getelement ptr inbounds<pty>*<ptrval>{,<ty><idx>},其中idx不为常数,sv_ptr=PT.blkState[ptrval]首先获得ptrval所表示的数组对象的符号值对象保存在中间变量sv_ptr中,然后获得索引所表示的符号对象sv_idx=PT.blkState[idx],对于数组长度内的每一个索引n,将sv_idx的符号值限定为具体值n,通过向约束pcs中添加约束实现,具体操作为PT.blkState.pcs.add(‘assert(sv_idx.val==n)’),在将符号值具体化后将具体符号值n所对应的子元素sv_element取出,操作为sv_element=sv_ptr.data[n],最后将结果标示符和其符号对象存入PT的当前块状态中PT.blkState[<result>=sv_element],数组是否越界的检查与索引为常数时一致;
若当前指令是函数调用指令(I10),按如下公式迁移系统状态:
P T . b l k S t a t e &lsqb; < r e s u l t > = S V a l u e ( r e s u l t . g e t T y p e ( ) ) &rsqb; < P T , P T S > { < r e s u l t > = i n v o k e < ptr to fun c t ion ty > < f u nction ptr val > ( < f u n c t ion arg s > } &RightArrow; < P T , P T S > ;
对于函数调用指令<result>=invoke<ptr to function ty><function ptrval>(<function args>),函数调用时假设函数可以返回任意可能的值,所以调用SValue的构造函数构造一个初值为无约束符号值(即SValue的val部分置为‘val_i’表示一个变量,其中i是当前未用到的索引,i每被使用后都会加1,其初值为0)的符号对象,最后将该符号对象和<result>组成的键值对存到当前块状态的映射中。
(26)根据不同的跳转指令类型,执行不同的指令语义模式操作公式,通过PT.it->getOpcode()获得指令的操作码,即指令类型,不同的指令操作码为不同的常数,getOpCode是指令对象的一个方法,它返回相应指令的操作码,操作码是一个整数,不同的指令操作码不同且为固定整数;执行不同的指令语义模式操作公式的公式下:
若跳转是无条件跳转指令(I3),按如下公式迁移系统状态:
P T . c u r r B = L 2 B ( l a b e l < d e s t > ) , P T . i t = P T . c u r r B . f i r s t I n s t r u c t i o n < P T , P T S > { b r l a b e l < d e s t > } &RightArrow; < P T , P T S > ;
对于无条件跳转指令br label<desl>,首先通过指令中的标签获得该标签所对应的代码块,L2B(label<dest>)获得目标代码块的块对象,并将该目标代码块保存到PT.currB中,该操作对应PT.currB=L2B(label<dest>),然后将当前指令指向目标代码块的第一条指令,操作为PT.it=PT.currB.firstInstruction,其中PT.currB.fisrtInstruction是获得当前代码块中的第一条指令的对象;
若跳转是有条件跳转指令(I2),按如下公式迁移系统状态:
P T _ T . c u r r B = L 2 B ( l a b e l < i f t r u e > ) , P T _ T . i t = P T _ T . c u r r B . f i r s t I n s t r u c t i o n , P T _ T . b l k S t a t e = B l o c k S t a t e ( p r e = P T . b l k S t a t e , p c s . a d d ( g e t P C S ( c o n d , t r u e ) ) ) P T _ T . var s = P T . var s , P T _ F . c u r r B = L 2 B ( l a b e l < i f f a l s e > ) , P T _ F . i t = P T _ F . c u r r B . f i r s t I n s t r u c t i o n , P T _ F . b l k S t a t e = B l o c k S t a t e ( p r e = P T . b l k S t a t e , p c s . a d d ( g e t P C S ( c o n d , t r u e ) ) ) , P T _ F . var s = P T . var s , i f ( P T _ T fea s i b l e ) P T S . a d d ( P T _ T ) , i f ( P T _ F fe a s i b l e ) P T S . a d d ( P T _ F ) , P T = P T S . p o p ( ) < P T , P T S > { b r i l < c o n d > , l a b e l < i f t r u e > , l e b e l < i f f a l s e > } &RightArrow; < P T , P T S > ;
对于有条件跳转指令br i1<cond>,label<iftrue>,label<iffalse>,首先根据true分支和false分支构造两条新的路径PT_T和PT_F,PT_T.currB=L2B(label<iftrue>),PT_T的当前代码块为true分支的目标代码块,PT_T.it=PT_T.currB.firstInstruction,设置路径PT_T当前执行指令it为当前代码块的第一条指令,PT_T.blkState=BlockState(pre=PT.blkState,pcs.add(getPCS(cond,true))),PT_T的当前代码块状态blksState的pre指向PT的blkState(这个字段会把所有的解释执行过的代码块链接为一棵执行树),并将cond产生的约束条件添加到pcs中,getPCS(cond,true)函数返回cond产生约束的字符串表示,cond是一个比较指令,通常其形式为cond=icmp op_handop1,op2.其生成约束的过程为op1_val=PT.blkState[op1].val,op2_val=PT.blkState[op2].val,对于getPCS(cond,true)其生成的字符串形式的约束结果为’(assert(op_hand op1_val op2_val))’,对于getPCS(cond,false)其结果为’(assert(not(op_hand op1_val op2_val_)))’,PT_F的赋值与PT_T类似,最后判断两条分支的约束条件是否满足,如果满足则将相应路径放入PTS中,等待之后解释执行,判断当前约束条件是否满足的操作调用步骤(3)。
(27)判断步骤(25)或步骤(26)直到发现致命性错误或者PTS为空,否则PT.it++继续执行步骤(23)至步骤(26)。
(3)根据记录各个变量在不同路径上的符号值和各条路径的约束条件,调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束,判断程序是否存在潜在漏洞。调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束的具体步骤如下:
(31)从PT.blkState开始顺着PT.blkState.pre链向上遍历到头结点,对于每一个blkState对象,将其pcs中的所有约束顺次连接,形成一个总的路径约束PC1;
(32)扫描PC1,探测其中的自由变量,自由变量符合正则表达式val_[0-9]+,为每一个自由变量生成声明,对于自由变量val_i生成声明字符串’(declare-const val_i(_BitVec 32)))’,将声明连接并记为DF;
(33)将DF和PC1连接,形成符合SMTLIB2.0规范的SMT约束PC,将PC送交Z3求解;
(34)将Z3返回的结果z3::sat和z3::unsat分别表示为true和false的形式,然后再返回,z3::sat对应true,z3::unsat对应false,其中z3::sat和z3::unsat是z3的c++库中定义的两个常数分别表示可满足和不可满足。可满足和不可满足所针对的只是一组约束条件,它是否有解的问题:如果是在遇到分支判断时调用步骤(3)进行判断,可满足则意味着这条路径的某一个分支是可行的,不可满足则代表该路径不可行;如果是在判断漏洞时,调用步骤(3)可满足确实代表存在漏洞。
本发明已经通过上述实施例进行了说明,但应当理解的是,上述实施例只是用于举例和说明的目的,而非意在将本发明限制于所描述的实施例范围内。此外本领域技术人员可以理解的是,本发明并不局限于上述实施例,根据本发明的教导还可以做出更多种的变型和修改,这些变型和修改均落在本发明所要求保护的范围以内。本发明的保护范围由附属的权利要求书及其等效范围所界定。

Claims (7)

1.一种代码静态检测方法,其特征在于,如下步骤:
(1)获取源代码,并将源代码进行预处理转换为LLVM汇编程序;
(2)将转换得到的LLVM汇编程序,运用符号执行算法模拟解释执行LLVM汇编程序,并记录各个变量在不同路径上的符号值和各条路径的约束条件;
(3)根据记录各个变量在不同路径上的符号值和各条路径的约束条件,调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束,判断程序是否存在潜在漏洞。
2.根据权利要求1所述的一种代码静态检测方法,其特征在于:所述步骤(1)中,将源代码进行预处理转换为LLVM汇编程序的具体步骤如下:
(11)调用编译器clang检查源代码是否存在语法错误,如有错误,修改语法错误直到编译器不报错,以得到语法正确的源代码;
(12)将得到的语法正确的源代码利用编译器Clang转化为LLVM汇编程序。
3.根据权利要求2所述的一种代码静态检测方法,其特征在于:所述步骤(2)中,模拟解释执行LLVM汇编程序的具体步骤如下:
(21)调用LLVM基础库,将LLVM汇编程序输入LLVM基础库并转化为文本形式程序的LLVM汇编程序CFG,LLVM基础库返回的CFG中包含的具体对象有全局变量,全局变量对应全局变量对象g,每一个函数LLVM汇编程序对应函数对象F,每个函数对象F包含函数参数p和若干代码块b,而每个代码块又包含多个指令instruction;
(22)对于被分析函数对象,为其引用到全局变量对象、函数参数和内存对象,内存对象是一个四元组,其结构为:
SValue=(tp×val×ptr×data)∈(llvm::Type*×String×SValue*×(int×SValue*)),
其中SValue是内存对象,用于描述各变量的符号状态,tp、val、ptr和data分别是,tp代表数据的类型,val代表符号值,表示一个字符串的类型为String,ptr代表符号指针值,data是一个数字到符号值指针的映射,用于记录数组类型和结构体,llvm::Type*,String,SValue*,(int×SValue*)分别是对应分量tp、val、ptr、data的数据类型,Llvm::Type是LLVM基础库中的一个描述LLVM汇编程序类型的对象,LLVM::Type*代表该类型对象的指针,String是C++标准库中的字符串对象类型,SValue是内存对象,也称为符号对象,int代表C++基础类型中的整数类型;
系统的初始状态为PT、PTS,首先初始化一个长度为1的根路径PT,将该路径存放到可执行路径PTS中,解释执行的过程类似于一个有限状态机的状态迁移,解释器的状态以PT和PTS进行描述,每解释执行一条指令状态就发生迁移,将初始化好的第一条路径放入PTS中;这些初始化的过程可以表示为如下指令语义模式操作公式:
其中,PT代表当前正在探索的可行路径,PTS代表目前发现的所有未探测的可行路径集合,B代表LLVM汇编程序CFG中的一个代码块,PT.currB代表当前路径正在探索的代码块,F表示当前正在被分析的函数,F.firstBlock代表函数的第一个代码块,PT.vars代表路径上所有的变量,它是一个映射,PT.vars[key=value]代表的是将键值对(key,value)存入映射vars当中,其中key代表映射的索引,value代表映射的key索引所对应的值,key和value可以被替换为各种值,公式中PT.vars[g=SValue(g.type()),p=SValue(p.type())]是PT.vars[g=SValue(g.type())]和PT.vars[p=SValue(p.type())]的缩写形式,代表将键值对(g,SValue(g.type()))和(p,SValue(p.type))存入PT.vars中,此处的g和p作为key,SValue(g.type())和SValue(p.type)则作为值value部分,SValue(type)是符号对象的构造函数,它有一个参数type表明符号对象,即表示数据的类型,g代表全局变量,g.type()表示全局变量的类型,p代表函数F的参数,p.type()表示参数的类型,SValue(p.type())代表根据参数p的类型构造内存对象,SValue(g.type())代表根据全局变量g的类型构造内存对象,PT.it代表当前路径所执行到的指令,PT.currB.firstInstruction代表当前路径当前块的第一条指令,PTS.add(PT)将一条路径PT添加到可行路径集合PTS中,init代表这是一个初始化动作,在任何具体指令分析之前应该进行该操作,它不对应任何指令;
(23)判断PTS是否为空,如果PTS不为空,则弹出其中一条路径存放到PT中,并执行步骤(24);若PTS为空,则模拟执行结束;
(24)若PT路径中的当前执行位置PT.it所指指令为非跳转指令,则执行步骤(25),否则执行(26);
(25)根据不同的非跳转指令类型,执行不同的指令语义模式操作公式;
(26)根据不同的跳转指令类型,执行不同的指令语义模式操作公式;
(27)判断步骤(25)或步骤(26)直到发现致命性错误或者PTS为空,否则PT.it++继续执行步骤(23)至步骤(26)。
4.根据权利要求3所述的一种代码静态检测方法,其特征在于:所述步骤(22)中,指令语义模式的结构如下公式所示:
其中<current state>代表系统的当前状态,<end state>代表语句执行后程序的状态,computation是状态迁移计算过程的一些细节展示,stmt代表被执行的语句包含的所有指令, 具体指令如下所示:
(I1)ret[void|<type><value>];
(I2)br i1<cond>,label<iftrue>,label<iffalse>;
(I3)br label<dest>;
(I4)<result>=[add|sub|mul|udiv|sdiv|urem|srem]<ty><op1>,<op2>;
(I5)<result>=icmp<cond><ty><op1>,<op2>;
(I6)<result>=alloca<type>[,<ty><NumElements>];
(I7)<result>=load<ty>*<pointer>;
(I8)store<ty><value>,<ty>*<pointer>;
(I9)<result>=getelementptr inbounds<pty>*<ptrval>{,<ty><idx>}*;
(I10)<result>=invoke<ptr to function ty><function ptr val>(<function args>;
I1代表函数返回指令,它分为有参数值和无参数值返回,无参数值ret void,有参数值ret<type><value>,其中<value>代表具体的返回值,<type>代表返回值的类型,ret表示这是一条函数返回;
I2代表有条件跳转指令,其中<cond>代表跳转的条件,i1代表布尔类型,<iftrue>代表条件为真时跳转到的目标代码块,<iffalse>代表条件为假时跳转到的目标代码块,label代表是一个标签类型,br表示这是一条跳转指令;
I3代表无条件跳转指令,<dest>代表无条件跳转到的目标代码块,label代表是一个标签类型,br表示这是一条跳转指令;
I4代表算术运算指令,[add|sub|mul|udiv|sdiv|urem|srem]代表的是算术运算的具体操作类型,<ty>代表两个操作数的数据类型,<op1>和<op2>分别是两个操作数,<result>代表算术运算结果;
I5代表比较指令,其中<cond>代表的是比较的具体类型,如大于、小于还是等于,<ty>代表操作数的数据类型,<op1>和<op2>分别代表比较的两个操作数,icmp表示这是一条比较指令,<result>代表比较结果;
I6代表的是局部变量内存分配指令,<type>代表变量的类型,该指令的语义是为局部变量在栈上分配一定的内存,模拟执行时,会为指定局部变量分配相应的内存对象SValue,<ty><NumElements>为可选部分,<NumElements>代表分配的元素的个数,<ty>代表<NumElements>的数据类型,alloca表示这是一个栈内存分配分配指令,<result>代表分配的栈内存对象;
I7是内存加载指令,<pointer>代表具体的指针,<ty>代表指针指向对象的数据类型,load表示这是一条内存加载指令,<result>代表被加载的数据;
I8是内存存贮指令,<pointer>代表存贮的内存位置,<value>代表被存贮的对象;<ty>代表了被存贮对象的数据类型,store代表这是一条内存存贮指令;
I9是数组或结构体子元素获取指令,<ptrval>代表数组或结构体对象,<pty>*代表<ptrval>的类型,<idx>代表子元素所在的索引为可选部分,<ty>代表<idx>的数据类型,getelementptr inbounds表示这是一条子元素获取指令,<result>是指令执行结果;
I10是函数调用指令,<function ptr val>代表被调用函数对象,<ptr to function ty>表示函数的类型,<function args>表示函数参数,invoke表示这是一条函数调用指令,<result>表示函数调用返回值;
上述各指令的各个分量都可以从对象化的LLVM汇编程序中获取,并且以对象的形式存在。
5.根据权利要求4所述的一种代码静态检测方法,其特征在于:所述步骤(25)中,执行不同的指令语义模式操作公式的公式如下:
若当前指令是函数返回指令(I1),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PTS.pop()表示从当前的可行路径集合中弹出一条路径,ret[void|<type><value>]表示一条函数返回指令;
若当前指令是算术运算指令(I4),按如下公式迁移系统状态:
sv_op1=PT.blkState[op1],sv_op2=PT.blkState
[op2],op_hand=[add|sub|mul|udiv|sdiv|urem|srem],
其中,PT表示当前正在探索的路径,PTS是当前已经发现的所有可行路径集合,PT.blkState是路径PT的当前块所对应的状态,其中记录了解释执行过程中各个变量的值的变化情况,<result>=[add|sub|mul|udiv|sdiv|urem|srem]<ty><op1>,<op2>是算术运算指令,op1和op2是算术运算的两个操作数,result表示算术运算的结果,PT.blkState[op1]表示的是以op1为键值在映射PT.blkState中查询对应的值,sv_op1是一个临时变量用于保存PT.blkState[op1]的结果,sv_op2也是一个临时变量,用于保存PT.blkState[op2],op_hand 表示具体的操作类型,它可以是加减乘除等算术运算,分别对应add、sub、mul、udiv、urem、srem,SValue::arith是符号对象SValue的一个静态方法,它对两个符号值进行算术运算并返回一个表示结果的符号值,sv_result是一个临时对象用于记录算术运算的结果,PT.blkState[key=value]是将键值对(key,value)存入映射PT.blkState中,key表示任意键值,value表示任意值,此处PT.blkState[<result>=sv_result]表示将键值对(<result>,sv_result)存入映射PT.blkState中;
若当前指令是比较指令(I5),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,do nothing表示不执行任何操作,<result>=icmp<cond><ty><op1>,<op2>表示一条比较指令;
若当前指令是局部变量内存分配指令(I6),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.vars表示当前路径的变量集合,PT.vars[key=value]表示将键值对(key,value)存入PT.vars中,key表示任意键,value表示任意值,此处PT.vars[<result>=SValue(type)]表示将键值对(<result>,SValue(type))存入PT.vars中,SValue(t)是符号对象的构造函数,其中t表示构造函数的参数它指定一个数据类型,构造函数根据t的类型构造一个符号对象,在实际函数调用时t可以被替换为任意表示数据类型的变量,如此处的type,此处type表示了所分配对象的类型,它来自于分母上的指令的type字段,<result>=alloca<type>[,<ty><NumElements>]表示这是一条局部变量内存分配指令;
若当前指令是内存加载指令(I7),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,pointer表示被加载的指针,result表示指令加载的结果,PT.blkState[pointer]以pointer为键值在pt.blkState中查询相应的值,该值 是一个符号对象,PT.blkState[Pointer].ptr表示该符号对象的ptr值,<result>=load<ty>*<pointer>表示这是一条内存加载指令;
若当前指令是内存存贮指令(I8),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,pointer表示存贮位置的指针,value表示存入的值,PT.blkState[pointer]以pointer为键值在pt.blkState中查询相应的值,该值是一个符号对象,PT.blkState[Pointer].ptr表示该符号对象的ptr值,PT.blkState[value]表示以value为键在PT.blkState中查询其相应的值,store<ty><value>,<ty>*<pointer>表示这是一条内存存贮指令;
若当前指令是数组或结构体子元素获取指令(I9)且索引idx是常数,按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,ptrval表示目标结构,是一个数组或者一个结构体,idx表示要获取的对象在ptrval中的子元素索引,PT.blkState[ptrval]表示以ptrval为键值在PT.blkState映射中所对应的值对象,sv_ptr是一个临时变量,用于记录查询到的值对象,sv_ptr.data[idx]表示取sv_ptr的data成员的第idx个子元素,结果存贮在临时变量sv_element中,PT.blkState[<result>=sv_element]代表将键值对(<result>,sv_element)放入映射PT.blkState中,其中<result>代表指令执行结果的标示符,<result>=getelement ptr inbounds<pty>*<ptrval>{,<ty><idx>}表示这是一条数组或结构体子元素获取指令;
若当前指令是数组或结构体子元素获取指令(I9)且索引idx不为常数,按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.blkState表示当前路径的当前块状态,ptrval表示目标结构,是一个数组或者一个结构体,idx表示要获取的对象在ptrval中的子元素索引,PT.blkState[ptrval]表示以ptrval为键值在PT.blkState映射中所对应的值对象,sv_ptr是一个临时变量,用于记录以ptrval为键值查询到的值对象,PT.blkState[idx]表示以idx为键值在PT.blkState映射中所对应的值对象,sv_idx是一个临时变量,用于记录以idx为键值查询到的值对象,PT.blkState.pcs表示当前块状态中记录的约束集合,PT.blkState.pcs.add表示往当前块约束集中添加一条约束,’assser(sv_idx.val==n)’表示一个字符串,其中n取遍数组的每个索引,sv_idx.val表示索引对象sv_idx的val字段的值,该值也是索引对象的符号值的字符串形式,array_max_index表示数组的最大索引,n表示区间[0,array_max_index]中的任意值,sv_ptr.data[n]表示取sv_ptr的data成员的第n个子元素,结果存贮在临时变量sv_element中,PT.blkState[<result>=sv_element]代表将键值对(<result>,sv_element)放入映射PT.blkState中,其中<result>代表指令执行结果的标示符,<result>=getelement ptr inbounds<pty>*<ptrval>{,<ty><idx>}表示这是一条数组或结构体子元素获取指令;
若当前指令是函数调用指令(I10),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合;PT.blkState表示当前路径的当前块状态,SValue(t)是符号对象的构造函数,其中t表示构造函数的参数,它指定一个数据类型,具体函数调用时t可以被替换为其他任意表示数据类型的值,如此处的result.getType(),result表示指令执行的返回值,此处SValue(result.getType())表示根据result的类型构造一个内存对象,其中result.getType()表示返回result的数据类型,整个PT.blkState[<result>=SValue(result.getType())]表示将键值对 (<result>,SValue(result.getType()))放入PT.blkState中,<result>=invoke<ptr to function ty><function ptr val>(<function args>)表示这是一条函数调用指令。
6.根据权利要求4所述的一种代码静态检测方法,其特征在于:所述步骤(26)中,执行不同的指令语义模式操作公式的公式下:
若跳转是无条件跳转指令(I3),按如下公式迁移系统状态:
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT.currB表示当前路径的当前块,L2B表示将一个标签转换为它具体表示的块,label<dest>表示跳转的目标块,PT.it表示当前路径执行到的指令位置,PT.currB.firstInstruction表示路径当前块的第一条指令,br label<desl>表示这是一条无条件跳转指令;
若跳转是有条件跳转指令(I2),按如下公式迁移系统状态:
PT_T.currB=L2B(label<iftrue>),PT_T.it=PT_T.currB.first
Instruction,PT_T.blkState=BlockState(pre=PT.blkState,
pcs.add(getPCS(cond,true)))PT_T.vars=PT.vars,PT_F
.currB=L2B(label<iffalse>),PT_F.it=PT_F.currB.firstInstru
ction,PT_F.blkState=BlockState(pre=PT.blkState,pcs.add
(getPCS(cond,true))),PT_F.vars=PT.vars,
其中,PT表示当前正在探索的可行路径,PTS是当前已经发现的所有可行路径集合,PT_T和PT_F表示两条即将创建的新路径,PT_T.currB表示PT_T路径的当前块,L2B表示将一个标签转换为它具体表示的块,label<iftrue>表示true分支的目标块,label<iffalse>表示false分支的目标块,PT_T.it表示PT_T路径执行到的指令位置,PT_T.currB.firstInstruction表示PT_T路径当前块的第一条指令,BlockState()是块状态的构造函数,它主要用于设置其pre字段和pcs字段,BlockState(pre=PT.blkState,pcs.add(getPCS(cond,true)))返回一个pre字段指向PT.blkState,pcs字段里面包含约束getPCS(cond,true)的块状态对象,getPCS()是一个函数,它将一条比较指令转换一个字符串形式的约束条件,PT_T.vars为PT_T路径的变量集合,PT.vars为当前路径的变量集合,PT_F的对应变量与PT_T类似,if(PT_T feasible)和if(PT_F feasible)代表判断PT_T和PT_F路径是否可行,判断路径是否可行请参考步骤(3),它通过对PT中的约束条件进行一 些计算从而得到结果,若约束条件可满足则代表该分支可行,PTS.add()和PTS.pop()分别表示往PTS中添加一条路径和弹出一条路径,br i 1<cond>,label<iftrue>,label<iffalse>表示这是一条有条件跳转指令。
7.根据权利要求6所述的一种代码静态检测方法,其特征在于,所述步骤(3)中,调用SMT求解器Z3检查变量的符号值是否满足路径约束和漏洞约束的具体步骤如下:
(31)从PT.blkState开始顺着PT.blkState.pre链向上遍历到头结点,对于每一个blkState对象,将其pcs中的所有约束顺次连接,形成一个总的路径约束PC1;
(32)扫描PC1,探测其中的自由变量,自由变量符合正则表达式val_[0-9]+,为每一个自由变量生成声明,对于自由变量val_i生成声明字符串’(declare-const val_i(_BitVec 32)))’,将声明连接并记为DF;
(33)将DF和PC1连接,形成符合SMTLIB2.0规范的SMT约束PC,将PC送交Z3求解;
(34)将Z3返回的结果z3::sat和z3::unsat分别表示为true和false的形式,然后再返回,z3::sat对应true,z3::unsat对应false,其中z3::sat和z3::unsat是z3的c++库中定义的两个常数分别表示可满足和不可满足。
CN201510390576.8A 2015-07-03 2015-07-03 一种代码静态检测方法 Active CN104965788B (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN201510390576.8A CN104965788B (zh) 2015-07-03 2015-07-03 一种代码静态检测方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN201510390576.8A CN104965788B (zh) 2015-07-03 2015-07-03 一种代码静态检测方法

Publications (2)

Publication Number Publication Date
CN104965788A true CN104965788A (zh) 2015-10-07
CN104965788B CN104965788B (zh) 2017-07-28

Family

ID=54219823

Family Applications (1)

Application Number Title Priority Date Filing Date
CN201510390576.8A Active CN104965788B (zh) 2015-07-03 2015-07-03 一种代码静态检测方法

Country Status (1)

Country Link
CN (1) CN104965788B (zh)

Cited By (19)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN105912459A (zh) * 2016-04-01 2016-08-31 北京理工大学 一种基于符号执行的数组越界检测方法
CN106055484A (zh) * 2016-06-30 2016-10-26 南京南瑞集团公司 一种抽水蓄能电站控制软件在线故障诊断方法及系统
CN106326123A (zh) * 2016-08-24 2017-01-11 北京奇虎测腾科技有限公司 一种用于检测数组越界缺陷的方法及系统
CN106803028A (zh) * 2017-01-18 2017-06-06 西安电子科技大学 一种防止安卓手机短信验证码被窃取的方法
CN107291622A (zh) * 2017-07-18 2017-10-24 北京计算机技术及应用研究所 C25汇编代码静态分析方法
CN107748716A (zh) * 2017-09-15 2018-03-02 深圳英飞拓科技股份有限公司 一种程序漏洞的查找方法及终端设备
CN107908405A (zh) * 2017-11-17 2018-04-13 苏州蜗牛数字科技股份有限公司 代码静态审核装置及方法
CN108153659A (zh) * 2016-12-02 2018-06-12 腾讯科技(深圳)有限公司 程序漏洞检测方法及相关装置
CN108304320A (zh) * 2018-01-05 2018-07-20 西北工业大学 基于动态符号执行的Java程序错误检测方法
CN109857384A (zh) * 2018-11-21 2019-06-07 江苏方天电力技术有限公司 一种热工指标在线计算的编码方法
CN110018828A (zh) * 2019-03-11 2019-07-16 深圳市元征科技股份有限公司 源代码检查方法、装置及终端设备
CN110187988A (zh) * 2019-06-06 2019-08-30 中国科学技术大学 适用于虚函数和函数指针的静态函数调用图构建方法
CN110188029A (zh) * 2019-03-15 2019-08-30 中山大学 一种基于定值到达分析方法的Java空指针分析系统
CN111443916A (zh) * 2020-03-10 2020-07-24 南京航空航天大学 一种面向程序内存安全性验证工具的静态优化方法
CN112100059A (zh) * 2020-08-20 2020-12-18 浙江大学 一种c语言的指针类型分析方法
CN112733153A (zh) * 2021-01-27 2021-04-30 腾讯科技(深圳)有限公司 源代码扫描方法、装置、电子设备和存储介质
CN113297069A (zh) * 2021-04-30 2021-08-24 中国科学院信息工程研究所 一种基于目标驱动的软件测试方法和装置
CN115904937A (zh) * 2022-10-24 2023-04-04 青岛丰拓力行科技服务有限公司 一种基于人工智能与物联网的可视化编程工具系统及方法
CN117312722A (zh) * 2023-09-21 2023-12-29 华东师范大学 一种基于smt求解器的多系统任务规划方法

Citations (4)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US20100223599A1 (en) * 2009-02-27 2010-09-02 Fujitsu Limited Efficient symbolic execution of software using static analysis
CN102262580A (zh) * 2010-05-24 2011-11-30 南京航空航天大学 一种改进的基于符号执行的软件静态测试方法及工具
CN102289362A (zh) * 2011-08-26 2011-12-21 北京邮电大学 分段符号执行装置及其工作方法
CN103645987A (zh) * 2013-12-20 2014-03-19 南京大学 基于代码生成和符号执行的访问控制策略测试自动生成方法

Patent Citations (4)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
US20100223599A1 (en) * 2009-02-27 2010-09-02 Fujitsu Limited Efficient symbolic execution of software using static analysis
CN102262580A (zh) * 2010-05-24 2011-11-30 南京航空航天大学 一种改进的基于符号执行的软件静态测试方法及工具
CN102289362A (zh) * 2011-08-26 2011-12-21 北京邮电大学 分段符号执行装置及其工作方法
CN103645987A (zh) * 2013-12-20 2014-03-19 南京大学 基于代码生成和符号执行的访问控制策略测试自动生成方法

Non-Patent Citations (2)

* Cited by examiner, † Cited by third party
Title
梁娟娟 等: "基于符号执行的软件静态测试研究", 《计算机技术与发展》 *
闫晓伟: "基于符号执行的软件脆弱性测试技术", 《中国优秀硕士学位论文全文数据库 信息科技辑》 *

Cited By (29)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN105912459A (zh) * 2016-04-01 2016-08-31 北京理工大学 一种基于符号执行的数组越界检测方法
CN106055484A (zh) * 2016-06-30 2016-10-26 南京南瑞集团公司 一种抽水蓄能电站控制软件在线故障诊断方法及系统
CN106055484B (zh) * 2016-06-30 2019-03-19 南京南瑞集团公司 一种抽水蓄能电站控制软件在线故障诊断方法及系统
CN106326123A (zh) * 2016-08-24 2017-01-11 北京奇虎测腾科技有限公司 一种用于检测数组越界缺陷的方法及系统
CN106326123B (zh) * 2016-08-24 2018-12-04 北京奇虎测腾安全技术有限公司 一种用于检测数组越界缺陷的方法及系统
CN108153659A (zh) * 2016-12-02 2018-06-12 腾讯科技(深圳)有限公司 程序漏洞检测方法及相关装置
CN106803028B (zh) * 2017-01-18 2019-08-30 西安电子科技大学 一种防止安卓手机短信验证码被窃取的方法
CN106803028A (zh) * 2017-01-18 2017-06-06 西安电子科技大学 一种防止安卓手机短信验证码被窃取的方法
CN107291622A (zh) * 2017-07-18 2017-10-24 北京计算机技术及应用研究所 C25汇编代码静态分析方法
CN107291622B (zh) * 2017-07-18 2020-03-31 北京计算机技术及应用研究所 C25汇编代码静态分析方法
CN107748716A (zh) * 2017-09-15 2018-03-02 深圳英飞拓科技股份有限公司 一种程序漏洞的查找方法及终端设备
CN107908405A (zh) * 2017-11-17 2018-04-13 苏州蜗牛数字科技股份有限公司 代码静态审核装置及方法
CN108304320A (zh) * 2018-01-05 2018-07-20 西北工业大学 基于动态符号执行的Java程序错误检测方法
CN109857384A (zh) * 2018-11-21 2019-06-07 江苏方天电力技术有限公司 一种热工指标在线计算的编码方法
CN110018828A (zh) * 2019-03-11 2019-07-16 深圳市元征科技股份有限公司 源代码检查方法、装置及终端设备
CN110018828B (zh) * 2019-03-11 2023-02-28 深圳市元征科技股份有限公司 源代码检查方法、装置及终端设备
CN110188029A (zh) * 2019-03-15 2019-08-30 中山大学 一种基于定值到达分析方法的Java空指针分析系统
CN110187988A (zh) * 2019-06-06 2019-08-30 中国科学技术大学 适用于虚函数和函数指针的静态函数调用图构建方法
CN110187988B (zh) * 2019-06-06 2021-08-13 中国科学技术大学 适用于虚函数和函数指针的静态函数调用图构建方法
CN111443916B (zh) * 2020-03-10 2021-06-22 南京航空航天大学 一种面向程序内存安全性验证工具的静态优化方法
CN111443916A (zh) * 2020-03-10 2020-07-24 南京航空航天大学 一种面向程序内存安全性验证工具的静态优化方法
CN112100059A (zh) * 2020-08-20 2020-12-18 浙江大学 一种c语言的指针类型分析方法
CN112100059B (zh) * 2020-08-20 2021-09-14 浙江大学 一种c语言的指针类型分析方法
WO2022036783A1 (zh) * 2020-08-20 2022-02-24 浙江大学 一种c语言的指针类型分析方法
CN112733153A (zh) * 2021-01-27 2021-04-30 腾讯科技(深圳)有限公司 源代码扫描方法、装置、电子设备和存储介质
CN113297069A (zh) * 2021-04-30 2021-08-24 中国科学院信息工程研究所 一种基于目标驱动的软件测试方法和装置
CN115904937A (zh) * 2022-10-24 2023-04-04 青岛丰拓力行科技服务有限公司 一种基于人工智能与物联网的可视化编程工具系统及方法
CN117312722A (zh) * 2023-09-21 2023-12-29 华东师范大学 一种基于smt求解器的多系统任务规划方法
CN117312722B (zh) * 2023-09-21 2024-03-19 华东师范大学 一种基于smt求解器的多系统任务规划方法

Also Published As

Publication number Publication date
CN104965788B (zh) 2017-07-28

Similar Documents

Publication Publication Date Title
CN104965788A (zh) 一种代码静态检测方法
Gosain et al. Static analysis: A survey of techniques and tools
Ahrendt et al. The KeY platform for verification and analysis of Java programs
Ali et al. Generating test data from OCL constraints with search techniques
Souza et al. Coral: Solving complex constraints for symbolic pathfinder
EP2128769B1 (en) Method, apparatus, and system for automatic test generation from statecharts
Hinge et al. Process seer: A tool for semantic effect annotation of business process models
Fu et al. Xsat: A fast floating-point satisfiability solver
van Amstel et al. An exercise in iterative domain-specific language design
Daca et al. Abstraction-driven concolic testing
Börger Construction and analysis of ground models and their refinements as a foundation for validating computer-based systems
Dalsgaard et al. Distributed computation of fixed points on dependency graphs
Abadi et al. Verifying parallel code after refactoring using equivalence checking
Saadawi et al. Verification of real-time DEVS models
da Silva et al. On-the-fly verification of discrete event simulations by means of simulation purposes.
Gluch et al. Model-Based Verification: A Technology for Dependable System Upgrade
Cassez et al. Wuppaal: Computation of worst-case execution-time for binary programs with uppaal
Farahbod et al. Executable formal specifications of complex distributed systems with CoreASM
ter Beek et al. States and events in KandISTI: a retrospective
Gonzalez-de-Aledo et al. An approach to static-dynamic software analysis
Ge et al. Formal development process of safety-critical embedded human machine interface systems
Arusoaie A generic framework for symbolic execution: theory and applications
Wehrmeister et al. Support for early verification of embedded real-time systems through UML models simulation
Nguyen et al. Runtime verification for hybrid analysis tools
Thompson et al. Verification of C++ flight software with the MCP model checker

Legal Events

Date Code Title Description
C06 Publication
PB01 Publication
C10 Entry into substantive examination
SE01 Entry into force of request for substantive examination
GR01 Patent grant
GR01 Patent grant