探索Clang Static Analyzer:使用方法与源码解读

一、前  言

二、工作原理

三、安装指南

四、CSA Driver 使用

五、CSA 源码浅析

六、CSA 控制流分析

七、CSA CallGraph

八、CSA 数据流分析

九、总  结



前  言

静态分析技术因其在学术研究和工业应用中的广泛用途而备受关注。尽管静态分析技术已经取得了长足的进步,但现有的静态分析框架仍然存在易用性方面的挑战。本文主要介绍了 Clang Static Analyzer(CSA)的使用方法,对部分检查器的源代码进行了浅析,并探讨了其设计思想。希望通过本文,能够帮助希望深入了解静态分析技术的同学更好地掌握相关知识。


工作原理

CSA 分析器的设计灵感来自几篇基础性的研究论文 ([1], [2])。

分析器基本上是一个源代码模拟器,它能跟踪可能的执行路径。程序的状态由状态(ProgramStateRef)进行封装。在 CSA 术语中对 ExplodedNode


分析器通过对分支进行推理,找出多条路径,然后对状态进行分叉。在真分支上,假设该分支的条件为真,而在假分支上,假设该分支的条件为假。这种“假设”会对程序的值产生约束,这些约束被记录在程序状态(ProgramState)中并由约束管理器管理。如果假设分支的条件会导致约束条件无法满足,那么该分支将被视为不可行,该路径将被删除,这就是路径敏感性。CSA 通过缓存节点来减少指数级爆炸,如果新节点的状态和程序点与现有节点相同,路径就会被“缓存”,我们只需重新使用现有节点即可。

更详细的工作原理可以参考 CSA README.md [3]。


总而言之,Clang Static Analyzer(CSA) 是基于 Clang AST(编译器前端) 实现的源代码静态符号执行工具。支持分析 C,C++,和 Objective-C 。基于静态符号执行技术实现了路径敏感技术,也支持过程间分析。工具被集成进 llvm-project,有长期的维护和发展。如果你对符号执行比较熟悉,会注意到其与 KLEE,Angr 的诸多相似之处。


安装指南

clang is all you need。

  • 通过包管理工具安装因平台而异,这里以 ubuntu 为例。

探索Clang Static Analyzer:使用方法与源码解读
  • 根据源码编译 LLVM 项目,编译细节可以参考 Getting Started with the LLVM System ([4], [5])。

探索Clang Static Analyzer:使用方法与源码解读

CSA Driver 使用

由于 CSA 工具被集成进 clang 中, 并不是一个非常“独立”的工具,使用起来会有一些麻烦。当然 CSA 相关项目也提供了一些相对独立的工具如 clang-checkCodeCheck,但是从结果来看与直接使用 clang 是差不多的。一般来说,使用 CSA 需要 driver 来加载参数来决定是否开启某些 checker,是否开启 z3 进行约束求解,是否导出报告等。由于参数实在之多,不可能一一列出,这里只介绍部分。


探索Clang Static Analyzer:使用方法与源码解读


  • 查看与前端 analyze 相关的参数:

探索Clang Static Analyzer:使用方法与源码解读

期望输出:

探索Clang Static Analyzer:使用方法与源码解读
  • 查看当前 clang 有哪些 checker:

探索Clang Static Analyzer:使用方法与源码解读

或者

探索Clang Static Analyzer:使用方法与源码解读

期望输出:

探索Clang Static Analyzer:使用方法与源码解读

为例便于使用 CSA ,我们需要用 driver 来配置 CSA 的参数,对单个或多个源代码进行分析,自己写或者利用现成的工具。


以分析如下代码为例,说明 CSA 是如何进行过程间分析,并熟悉相关参数:

  • 源代码:

探索Clang Static Analyzer:使用方法与源码解读
探索Clang Static Analyzer:使用方法与源码解读
  • 导出 ast:

探索Clang Static Analyzer:使用方法与源码解读
  • 导出 externalDefMap.txt

探索Clang Static Analyzer:使用方法与源码解读
  • 替换 externalDefMap.txt 一些路径问题:

探索Clang Static Analyzer:使用方法与源码解读
  • 使用 CSA 进行过程间分析:

探索Clang Static Analyzer:使用方法与源码解读
  • 报告可以通过参数来指定,下图是一个报告的例子:

探索Clang Static Analyzer:使用方法与源码解读

CSA 源码浅析

下面以 TaintTesterChecker.cpp 为例说明 CSA checker 的一般结构与实现。


  • TaintTesterChecker.cpp 可以从 path-to-llvm-project/clang/lib/StaticAnalyzer/Checkers 目录下找到,这里直接引用它的内容。

探索Clang Static Analyzer:使用方法与源码解读
  • 这个 checker 继承了 public Checker<check::PostStmt<Expr>>,表示在 CSA 进行分析的过程中,它会在每个 Stmt 执行后调用该 checker。如果在该 StateRef 被污染了,就会产生路径敏感的报告。

探索Clang Static Analyzer:使用方法与源码解读
  • 最后还需要在 CheckerMananger 中中注册相应的 checker。

探索Clang Static Analyzer:使用方法与源码解读

下面对 MallocChecker 进行简单的分析,希望读者也能有一些收获,关于 malloc free pair 和 new delete pair 等相关的实现问题。


MallocChecker 也在上文提及的目录中,CSA checkers 一般都在此实现。


静态分析工具 CSA 的核心就是建模,阅读 MallocChecker 后很明显能了解到 CSA 的开发者是以何种方式思考,希望能够描述出什么样的 malloc 与 free 是合法与非法的,以此类推,建立程序语言与形式化方法之间的桥梁。


MallocChecker 通过 AllocationFamilyKind 标识内存分配/释放的家族,总共有以下几类,对每一个家族有不同的处理,这一点比较好理解。

探索Clang Static Analyzer:使用方法与源码解读

分配/释放的内存处于某种状态,可以理解为在有限自动机上的状态转换。每一种都有注释,可以详细阅读一番。其中 Relinquished 还是比较有趣的,在统计引用的过程中表示引用已经被“偷走”,不必再对其 free 或者减引用,否则有可能导致 UAF。

探索Clang Static Analyzer:使用方法与源码解读

关于程序建模,以 Malloc 的建模为例,程序中的注释表示得还算清楚。分配内存需要考虑分配内存的函数 AllocationFamily Family 是什么,需要分配的大小 const Expr *SizeEx,是否需要初始化 Sval Init,以及内存分配函数的上下文 CallEventCheckerContext

探索Clang Static Analyzer:使用方法与源码解读

还需要考虑 Malloc 的返回值 RetVal,是否分配成功,应该分配怎么样的内存,什么样的情况下需要被污染,什么样的情况下报错,这就需要结合 FreeMemAux 考虑。

探索Clang Static Analyzer:使用方法与源码解读

Malloc 只要分配一块可被追踪的内存,而 Free 需要考虑的就很多了。

探索Clang Static Analyzer:使用方法与源码解读

实际上,FreeMemAux 写的并不是那么完美,首先本身指针分析就不是非常的准确,比如调用有关 Free 的函数指针,就不一定能被检测出来。另外 CSA 本身也有一些问题,迭代的次数也是有限的,全局变量初始化也不一定能被分析到,而且 StateRef 也意味着单线程的分析,得到的报告很可能因为路径不可行而成为 FP,因为 CSA 在分析过程中有太多假设了,这些误报理论上应该通过程序的其他信息/规则来得到或者说优化。


在种种对入参进行检查之后,才到检查漏洞的相关代码。关于 Free alloca 分配内存,甚至后面还有合法性的检查(如 Double free 检查),与内存的建模关系非常大。

探索Clang Static Analyzer:使用方法与源码解读

现在来考虑一下如何在 CSA 中实现最基础的静态分析算法(流不敏感)。可以参考下文摘录的部分代码(比较经典的 worklist)。


MallocChecker 一些 FIXME,TODO 中可以看出 CSA 还存在一些“奇怪”的 bug,如 Ownership 可能会因为存储发生变化。

探索Clang Static Analyzer:使用方法与源码解读
探索Clang Static Analyzer:使用方法与源码解读

CSA 控制流分析

对应于源码中的定义: llvm-project/clang/Analysis/CFG.h 基于 AST 构建的 CFG。

探索Clang Static Analyzer:使用方法与源码解读

CSA CallGraph

对应于源码中的定义: llvm-project/clang/Analysis/CallGraph.h 基于 AST 构建的 CallGraph。

探索Clang Static Analyzer:使用方法与源码解读

CSA 数据流分析

对应于源码中的定义: llvm-project/clang/Analysis/FlowSensitive/DataflowAnalysis.h

基本思想是通过控制流图(CFG)的边缘传播有关程序的事实,直到到达固定点。[6] 受限于静态分析对路径的执行次数上限,(不能太多次展开循环),同时需要对数据进行一个合理的估计。

探索Clang Static Analyzer:使用方法与源码解读

总  结

可以看出,CFG,DFG,CG 等关键的图都能基于 AST 构建出来,与时下流行的基于 DSL 的静态分析并驾齐驱,相比于 CodeQL 等 DSL 工具。虽然 CSA 在规则编写上会有一些繁琐,但是能客制化更复杂,功能更强的规则以检测代码缺陷。另外,当前静态分析仍然受制于最基础的指向分析(Point-To Analysis),过程间分析的能力,随着静态分析等理论/工具的发展,能检测到的代码缺陷也会变多。但是代码不会因此变得没有漏洞,只是漏洞可能会更深。


参考链接

1. Precise interprocedural dataflow analysis via graph reachability, T Reps, S Horwitz, and M Sagiv, POPL ’95

2. A memory model for static analysis of C programs, Z Xu, T Kremenek, and J Zhang

3. CSA README.txt

4. Getting Started with the LLVM System

5. Clang 的編譯——面向 Clang Static Analyzer 開發的編譯配置指北, Xutong Ma

6. Data flow analysis: an informal introduction





【版权说明】

本作品著作权归R0g3rTh4t所有

未经作者同意,不得转载

探索Clang Static Analyzer:使用方法与源码解读
R0g3rTh4t

@天工实验室🧑‍💻@Vidar-Team,主攻静态分析,模糊测试

往期回顾

01
Ivanti Avalanche WLAvanacheServer组件漏洞分析
02
V8堆沙箱绕过方法分析总结
03
正则表达式安全研究
04
VMware设备虚拟化漏洞挖掘(下篇)


探索Clang Static Analyzer:使用方法与源码解读
每周三更新一篇技术文章  点击关注我们吧!

原文始发于微信公众号(奇安信天工实验室):探索Clang Static Analyzer:使用方法与源码解读

版权声明:admin 发表于 2024年10月23日 上午11:31。
转载请注明:探索Clang Static Analyzer:使用方法与源码解读 | CTF导航

相关文章