transform code to graph with clang


程序图各节点之间的关系需要尽可能包含能体现漏洞特征的信息。

节点cursor

生成抽象语法树,然后再重抽象语法树中选择需要连接进图的节点。

现在有两种解决方案。

  1. 直接通过clang将源代码转换成抽象语法树。
  2. Python 的clang 接口。

直接使用clang 命令

# 这两种命令是直接将生成的AST输出在shell中。如果file中只是一个函数片段,其中的某些变量或者函数并没有在函数内部定义,那么就会报错。这时就无法正常输出结果,而是会将error和warning报出来。
clang -Xclang -fsyntax-only -ast-dump file 
clang -emit-ast file
# 如果不加 -fsyntax-only,会输出些结果。在这些结果里,会有些statement被忽略掉,
clang -Xclang -ast-dump file 

如果直接在终端用clang生成抽象语法树,由于数据过多,批量化处理不方便。所以采用clang的Python接口。

clang 的Python接口配置。都需要两个部分,一个是libclang的Python 第三方库,另一个是libclang库。

  1. 在windows上。Windows上的libclang是libclang.dll。需要注意几个问题,libclang.dll的位数需要与本机的Python位数一致,32位还是64位要分清楚。然后就是libclang的版本需要和Python库clang的版本相一致。目前clang最新已经有10.0的版本,但是在Python中clang库只目前只更新到6.0的版本,所以如果要最新的最好选择clang 6.0的版本。

    在这里推荐直接装 clang 6.0 从 https://pypi.org/project/clang/6.0.0.2/直接下载 然后本地安装。libclang.dll这里我们可以直接从llvm安装exe文件 https://releases.llvm.org/download.html,从中选择6.0的win64.exe版本下载完后直接安装就可以得到libclang.dll。

  2. 在linux上, libclang是libclang.so.1的共享库的形式。与windows相同,其版本需要与Python库中的clang一致。

    clang直接选择源码编译安装可以得到libclang.so.1

    然后将路径设置为libclang所在的位置即可。

    from clang.cindex import Config
    libclangpath = r''
    Config.set_library_file(libclangPath)
    

需要的节点类型

用Python接口生成的AST也是用clang生成的,因此其节点的类别是一样的。在经过clang直接输出的信息中,每一行代表一个节点的信息,其中包括了节点的类型,在代码的第几行第几个,以及节点本身的类型信息和自身的字符表示。然后不同的节点通过一定的解析结构连在一起。

对于一个C/C++文件来说,通过clang生成的AST是解析过程是这样的。如果有头文件,且头文件与该文件在同一目录下,则会首先会将头文件进行解析。(这是在调用cursor.get_children()来对AST进行遍历的情况)然后生成的cursor对象,有几种属性。

cursor.kind是指此cursor在AST中的位置(函数、类、参数、表达式等)即 Operator(操作符)、Decl(声明)、Stmt(陈述)、Expr(表达式)、Literal(字符)、Reference(引用 )等。例如FunctionDecl、ParaDecl、VarDecl;IF_STMT、WHILE_STMT。在cindex.py中对cursor.kind进行了说明总共有大约700种。

在对文件进行解析的时候会出现这种问题。如果函数中有外部声明的变量或者函数类型(可能在头文件里,但是头文件没有),那么clang不会识别出来,因此对这些语句就会忽略。clang -l 参数可以指定链接库,如果将文件的头文件的路径全部放在同一目录下,这样就可以加快批量处理的速度。

Index

AST的生成首先是通过调用cindex.py中的 Index类来对translation units进行读取和解析。Index主要有以下几种方法。

class Index(ClangObject):
	@staticmethod
    def create(excludeDecls=False):
    	return Index(conf.lib.clang_createIndex(excludeDecls, 0))
    def __del__(self):
        conf.lib.clang_disposeIndex(self)
    def read(self, path):
        return TranslationUnit.from_ast_file(path, self)
    def parse(self, path, args=None, unsaved_files=None, options = 0):
        return TranslationUnit.from_source(path, args, unsaved_files, options,self)

首先创建一个新的index对象 Index.create(),然后用这个实例化后的index调用parse方法对文件进行解析Index.create().parse() 我们从parse()可以看到实际上是调用 TranslationUnit.from_source()

@classmethod
def from_source(cls, filename, args=None, unsaved_files=None, options=0,
                    index=None):
    ...
    ptr = conf.lib.clang_parseTranslationUnit(index, filename, args_array,
                                    len(args), unsaved_array,
                                    len(unsaved_files), options)
    ...
    return cls(ptr, index=index)

这是个类方法,最后返回的是一个实例化的 TranslationUnit。其各参数解释如下:

filename即需要被解析的文件(既可以是文件系统中的文件,也可以是内存中的内容)

args是对clang 传入的参数以列表的形式

options是clang 解析的方式,一共有9种方式。

我们发现在实际解析过程中,如果其中的变量类型或者变量在函数内部或者头文件中没有声明,那么此语句就会解析有误。其次,即使变量的类型在其它地方有声明,那么在进行生成AST的时候,打印出来的变量的类型不是标准类型的形式,这对变量类型的理解就会有偏差。(这里应该对变量的类型进行归一化转换,使得typedef后的变量类型能够映射回原类型

Token

用clang的词法分析可以将文件中所有的text分成token序列。对于某个节点cursor,只要调用其get_tokens()方法就能获取此cursor下的所有token。在clang解析过程中将token分成这几种类型,keyword、identifier、literal、punctuation,通过token.kind可以获取其kind属性。token.cursor可以获取其属于哪个节点cursor。

graph

将代码转换成图的结构,需要考虑以下几个问题。

  1. **我们的目标代码是什么?**是一整个C/C++文件还是其中的某个函数,或者只是一个代码片段。如果不是完整的代码结构(其中所有的变量或者函数都被声明或者在头文件中),那么没有声明的变量就会在语法解析的时候报错,这样生成的AST节点是有问题的,不完整的。其实很多情况下,C/C++头文件并不完整,所以准备从两条路对代码进行解析,仅进行词法分析然后通过token之间的关系构件图;在头文件完整的情况下进行语法分析AST以cursor的方式构建图。

  2. 以token的方式建立代码图。图中的每个节点就是每个token。对于图神经网络来说,每个节点是有特征向量的,那么以token表示的节点,什么样的特征能够体现体现漏洞特征。对于一个token无非就是其本身的spelling,它的kind。我们在进行静态分析

    https://www.sohu.com/a/388834376_115128


现在用joern来构建代码属性图。joern可以生成AST,CFG,PDG,然后将这三者全部融合在一起。对于GNN来说,输入需要各个节点的属性以及节点之间的关系。因此,下面有两种方式来用joern构建,一种是直接用其生成的CPG来进行处理;第二种方式是分别将生成的三种结构进行组合。

用joern生成的这三种图的存储方式都可以通过json对象或者json string的形式进行存储。(那么这三种结构中的节点,是否能够统一联系在一起?)

AST

用joern可以将函数解析出来生成AST,这个AST的数据是以json的格式进行存储的。为了方便批量处理数据,我们用将json用Python转换成字典的形式。

如果直接将一个函数实体通过此命令进行解析 cpg.runScript(graph/ast-for-funcs-dumps.sc) ,那会将此函数以及其内部的函数都生成一个AST,然后这些AST都存在 dict["functions"]中,这是一个列表,其中每个元素是以字典形式存储的每个函数的AST。但我们只要主函数的AST,因此我们需要知道主函数的函数名,然后

),那会将此函数以及其内部的函数都生成一个AST,然后这些AST都存在dict[“functions”]`中,这是一个列表,其中每个元素是以字典形式存储的每个函数的AST。但我们只要主函数的AST,因此我们需要知道主函数的函数名,然后

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值