长亭百川云 - 文章详情

白盒分析神器-代码属性图CPG-3000字参数讲解

CodeAnalyzer Ultra

83

2024-07-13

一、项目导入

`implementation(group = "de.fraunhofer.aisec", name = "cpg-core", version = cpgVersion)   implementation(group = "de.fraunhofer.aisec", name = "cpg-language-java", version = cpgVersion)   `

分析java项目需要导入针对java的cpg-language-java

二、cpg调用

1.使用方式

大致逻辑:构建翻译配置TranslationConfiguration,翻译管理TranslationManager获取配置,通过builder构建分析器,然后开始分析

TranslationConfiguration构建

参考:de.fraunhofer.aisec.cpg.analysis.AnalysisTest#testNullPointer

`fun testNullPointer() {       val config =           TranslationConfiguration.builder()               .sourceLocations(File("src/test/resources/Array.java"))               .defaultPasses()               //                .registerLanguage<JavaLanguage>()               .build()       val analyzer = TranslationManager.builder().config(config).build()       val result = analyzer.analyze().get()       NullPointerCheck().run(result)   }   `

通过cpg官方的测试用例中的测试方法,可以发现,在构建TranslationConfiguration时传递了三个参数:

  • sourceLocations

进入该方法

可以发现,参数是待分析的源码/源码所在目录,

  • defaultPasses

进入该方法

该方法指定默认的Passes策略,包含了 类型处理、DFG处理、EOG处理等等(为cpg的完整性提供了保障)

  • registerLanguage(被注释)

进入该方法

可以看到,其可以指定待分析项目的语言,其支持的语言有这么多~

另外,其还支持自定义语言,大概步骤如下:

要支持一种新的编程语言,你需要执行以下步骤:

  1. 提供一种新的语言定义。在这个阶段,你需要考虑该编程语言的特点以及它必须实现哪些LanguageTraits(语言特征)。通过这种方式,你可以对该语言进行精细化调整,确保各个分析流程(Passes)能够正确运作。

  2. 实现一个新的LanguageFrontend。当遇到匹配新语言的文件时,这个前端将会被执行。前端的要求已经在上面详细描述过。

  3. 为了让CPG能够使用新添加的前端,你需要相应地配置翻译过程。这是通过TranslationConfiguration来完成的,在此配置中,你需要通过调用其中一个registerLanguage()方法来注册你的新语言,以便于CPG能够识别并使用针对新语言的前端进行源代码到代码属性图的转换。示例如下:

`val config: TranslationConfiguration = TranslationConfiguration       .builder()       // More configuration       .registerLanguage(MyNewLanguage()) // Option 1       .registerLanguage<MyOtherNewLanguage>() // Option 2       .registerLanguage("MyThirdNewLanguage") // Option 3       .build()   `

很显然,要调用cpg进行代码分析,肯定要明确其内部有一些什么功能,可以通过什么方法去控制他(参数/配置等),那么TranslationConfiguration就很重要了~

2.TranslationConfiguration解析

高能预警!!!高能预警!!!高能预警!!!

location:kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt

TranslationConfiguration的类结构如下,这么多字段,看看每个字段都是干嘛的

1.symbols

附加符号定义,主要用于C++。这是一个Map<String,String>映射表,可以用来预先定义一些全局符号或者宏定义,帮助解析器正确解析源代码。

2.softwareComponents

源代码文件列表。用Map<String,List<File>>存储,cpg默认将key作为"application",value就是源文件List

当使用Builder构建时,可以调用sourceLocations方法,指定源文件列表,该方法将文件列表存储到softwareComponents表中

3.topLevel

通常是待分析项目的根目录。其可以为null

4.debugParser

是否启用解析器调试输出。当设为true时,将在解析过程中生成额外的调试信息。 一般调试时开启。

5.failOnError

解析过程发生Error时是否结束。如果为true,则在遇到任何错误时立即停止解析和翻译;若为false,即使存在错误,也会尽力继续执行。

6.loadIncludes

是否递归加载头文件到CPG中(针对C++)。如果为true,解析器不仅会解析头文件来解析符号和模板,还会将其内容加载到CPG中;否则仅解析但不加载。

7.includePaths

头文件目录列表(针对C++)。这是解析器查找头文件的目录集合,确保符号能够正确解析。

8.includeWhitelist

头文件白名单(针对C++)。非空情况下,只有指定列表中的头文件会被解析和加入到CPG中,除非它同时出现在黑名单中。支持相对路径。

9.includeBlocklist

头文件黑名单(针对C++)。非空情况下,指定的头文件不会被解析,也不会加入CPG节点中,黑名单优先于白名单

10.passes

解析过程中一系列的处理步骤。这些Pass定义了代码翻译或解析过程中要执行的各种操作或步骤。每个Pass可能包含多个处理类,这些类负责执行特定的任务,如解析、转换或优化代码。

What is a Pass?

Passes get a prebuilt CPG that at least contains the CPG-AST and output a modified graph. Their purpose is to extend the syntactic representation of code with additional nodes and edges to represent the semantics of the program. Passes can be executed in sequence, where the output of the previous pass serves as input of the next pass.

解释:

Passes接收一个预构建的CPG,其中至少包含了CPG-AST(代码属性图抽象语法树),并输出一个经过修改的图。它们的目的是通过添加额外的节点和边来扩展代码的句法表示,从而体现程序的语义。这些Pass可以按照序列执行,前一个Pass的输出结果作为下一个Pass的输入

由于Pass执行时是有顺序的,所以cpg还提供了一些注解来控制Pass之间的执行顺序

  • DependsOn(other: KClass<out Pass>, softDependency: Boolean = false) -- The annotated pass is executed after the other pass(es). If softDependency is set to false, it automatically registers these passes if they haven't been registered by the user.

  • ExecuteBefore(other: KClass<out Pass>, ...) -- The annotated pass is executed before the other pass(es) specified.

  • ExecuteFirst -- The annotated pass is executed as the first pass if possible.

  • ExecuteLast -- The annotated pass is executed as the last pass if possible.

  • RequiredFrontend(frontend: KClass<out LanguageFrontend>) -- The annotated pass is only executed if the frontend has been used.

那么具体有哪些Pass呢,看Pass类结构:

11.replacedPasses

替换特定Pass的映射表。允许用户为特定语言替换默认的Pass实现,通过Builder.replacePass方法或ReplacePass注解实现。

12.languages

支持的语言列表,包括相应的前端解析器。

13.codeInNodes

CPG节点中是否包含源代码片段。默认true

14.processAnnotations

是否处理源代码中的注解。默认 false

15.disableCleanup

是否禁用解析后的TypeManager内存清理步骤。默认false,测试时才可以打开

16.useUnityBuild

是否启用统一构建模式。默认为false,仅针对C++。

在这种模式下,所有的翻译单元会被合并成单一的一个,从而避免重复处理头文件,降低向图中添加重复节点的情况。

17.useParallelFrontends

是否使用多线程并发处理不同的语言前端。 默认fasle

如果设为true,表明源文件的抽象语法树(AST)将并行地进行解析,尽管后续的遍历和增强操作仍然在一个线程中串行执行。这加快了初始解析的速度,同时保证后续图增强算法的正确性。

18.useParallelPasses

是否开启Pass并行执行,加速分析速度。默认false

19.inferenceConfiguration

推断配置对象,控制对未知节点的推断策略。

默认如下:

`private var inferenceConfiguration = InferenceConfiguration.Builder().build()   `

InferenceConfiguration.Builder 有如下参数:(不显式build时默认都开启)

19.1.enabled

在整个系统中开启推断模块

19.2.guessCastExpressions

启用对强制转换表达式与调用表达式的智能猜测。仅针对C/C++。

19.3.inferRecords

是否推断record声明(类声明)。

19.4.inferFunctions

是否推断方法声明。

19.5.inferVariables

是否推断变量,例如全局变量。

19.6.inferDfgForUnresolvedSymbols

是否将方法调用表达式的DFG边添加到未解析的函数中。(例如:在给定的源代码中没有方法的实现)

20.compilationDatabase

编译数据库,可选参数,提供编译信息以辅助解析。  默认为 null

它存储了一个从源文件到其编译时所需包含文件路径的映射关系。目前主要由C++前端[CXXLanguageFrontend]使用。可以通过[CompilationDatabase.Companion.fromFile]方法从文件创建一个新的编译数据库。

21.matchCommentsToNodes

是否采用启发式方法将源文件中找到的注释与其最近的AST节点匹配,并将注释保存在节点的注释属性中。

默认false

22.addIncludesToGraph

是否将头文件包含关系添加到CPG中(针对C++)。 默认true

当设为true时,C++前端会在图中建立节点与所需的头文件之间的连接。

23.passConfigurations

针对每个Pass类型的个性化配置映射表,允许对单个Pass调整其运行参数或行为。 默认为空

是一个映射表,键是Pass的类型,值是Pass的配置对象。这个映射允许针对不同类型Pass定制特定的运行配置参数。

推荐阅读

代码属性图CPG系列文章(白盒/静态代码分析方向):超万字的详细讲解,文章理论与实践相结合,示例代码可拿来即用通俗易懂,这样的文章你爱了吗! 

能够看到这篇文章,就是我们的缘分,坚持输出优质内容是笔者一直在做的事情。若文章对你有帮助,感谢点个免费的 点赞在看,大家的鼓励是我最大的动力

点个关注,不后悔

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2