Joern:Code Property Graph 静态分析引擎深度解析

April 21, 2026

Joern:Code Property Graph 静态分析引擎深度解析

类型:静态代码分析框架
核心概念:Code Property Graph (CPG) = AST + CFG + PDG
语言:Scala
查询语言:CPGQL
官网https://joern.io
GitHubhttps://github.com/joernio/joern

一、Code Property Graph (CPG) 核心概念

CPG = AST + CFG + PDG

┌─────────────────────────────────────────────────────────────┐
│              Code Property Graph (CPG)                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   ┌─────────────┐                                           │
│   │  AST        │  抽象语法树                                │
│   │ (语法结构)   │  if/while/赋值/函数调用...                 │
│   └─────────────┘                                           │
│         +                                                    │
│   ┌─────────────┐                                           │
│   │  CFG        │  控制流图                                  │
│   │ (执行路径)   │  程序执行的分支路径                        │
│   └─────────────┘                                           │
│         +                                                    │
│   ┌─────────────┐                                           │
│   │  PDG        │  程序依赖图                                │
│   │ (数据依赖)   │  变量定义→使用 的数据流关系                │
│   └─────────────┘                                           │
│                                                              │
│   = 一个统一图结构,支持跨函数数据流追踪                       │
└─────────────────────────────────────────────────────────────┘

三张图的融合

图类型表示内容漏洞分析用途
AST语法结构(语法树节点)定位函数、变量、表达式
CFG控制流(分支跳转路径)分析 if/switch/循环 执行路径
PDG数据依赖(变量流向)污点追踪:source → sink

二、Joern 在 codebadger 中的架构

codebadger MCP Server
         │
         │ Docker API
         ↓
┌─────────────────────────────────────────────────────────────┐
│            Joern Docker Container                            │
│         (codebadger-joern-server)                            │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   ┌─────────────────┐                                       │
│   │  Joern CLI      │  CPG 生成                             │
│   │  joern-parse    │  源码 → CPG.bin                       │
│   └─────────────────┘                                       │
│         │                                                    │
│         ↓                                                    │
│   ┌─────────────────┐                                       │
│   │  CPG 文件       │  playground/cpgs/{hash}/cpg.bin       │
│   │  (二进制图)      │  包含完整的 AST+CFG+PDG               │
│   └─────────────────┘                                       │
│         │                                                    │
│         ↓                                                    │
│   ┌─────────────────┐                                       │
│   │  Joern Server   │  查询执行                             │
│   │  (Scala REPL)   │  加载 CPG → 执行 CPGQL 查询            │
│   └─────────────────┘                                       │
│                                                              │
└─────────────────────────────────────────────────────────────┘

三、Joern 工作流程

3.1 CPG 生成

// Joern CLI 命令
joern-parse --language c /path/to/codebase

// 输出
生成 cpg.bin(二进制 CPG 文件)

生成过程

  1. 解析源码 → AST
  2. 构建 CFG(控制流分析)
  3. 构建 PDG(数据依赖分析)
  4. 合并为 CPG → 序列化为 cpg.bin

3.2 CPGQL 查询执行

// 加载 CPG
importCode("path/to/cpg.bin")

// 查询所有函数
cpg.method.name.l

// 查询所有调用点
cpg.call.code.l

// 污点追踪:从 getenv 到 system
val sources = cpg.call.name("getenv")
val sinks = cpg.call.name("system")
sink.reachableByFlows(source).l

四、CPGQL 查询示例

4.1 查找所有外部函数调用

cpg.call
  .where(_.methodName.not(".*internal.*"))
  .code.l

4.2 查找缓冲区操作

cpg.call
  .name("strcpy", "memcpy", "sprintf")
  .code.l

4.3 污点追踪

// 定义污点源
val sources = cpg.call.name("getenv", "fgets", "read")

// 定义污点汇
val sinks = cpg.call.name("system", "strcpy", "sprintf")

// 追踪数据流
sinks.reachableByFlows(sources).map(_.elements).l

4.4 程序切片

// 从某个调用点向后切片
cpg.call.name("malloc")
  .newSink
  .reachableBy(cpg.method.astNodes)
  .l

4.5 查找危险函数调用

// 查找所有 system() 调用
cpg.call.name("system").code.l

// 查找参数来源
cpg.call.name("system")
  .argument.code.l

4.6 查找整数溢出模式

// 查找乘法运算后用于内存分配
cpg.call.name("malloc")
  .where(_.argument.isCall.name("mul", "*"))
  .code.l

五、codebadger 对 Joern 的封装

codebadger 工具Joern 底层调用
generate_cpgjoern-parse --language {lang} {path}
list_methodscpg.method.name.l
get_call_graphcpg.method.callIn.l + cpg.method.callOut.l
find_taint_sourcescpg.call.name(getenv, fgets, ...)
find_taint_flowssink.reachableByFlows(source)
get_program_slicesink.reachableBy(cpg.method.astNodes)
find_use_after_freefree() → 后续引用追踪
find_double_free多个 free() 调用点检测
find_null_pointer_deref空指针解引用模式匹配
find_integer_overflowmalloc 参数溢出检测
find_format_string_vulnsprintf 非字面量格式参数
find_heap_overflow堆缓冲区写入越界
find_stack_overflow栈缓冲区写入越界
find_toctouaccess/stat + open 分离操作
find_uninitialized_reads变量未初始化使用

六、Joern 技术优势

优势说明
跨函数分析PDG 支持跨函数数据流追踪(突破 LLM token 限制)
多语言支持C/C++/Java/Python/JS/Go/C#/PHP/Ruby/Swift/Kotlin (12 种)
可扩展查询CPGQL 是 Scala DSL,可写任意复杂查询
高效缓存CPG.bin 可持久化,代码不变无需重新生成
精确语义三图融合提供完整语义信息(AST+CFG+PDG)
开源免费Apache 2.0 许可证,社区活跃

七、与其他静态分析工具对比

工具类型跨函数分析污点追踪LLM 集成
JoernCPG✓ 精确✓ 原生✗ 需封装
CodeQLDatalog✓ 精确✓ 原生✗ 需封装
SemgrepPattern✗ 单函数✗ 无✓ 直接
Clang Static AnalyzerPath-sensitive✓ 精确✓ 原生✗ 需封装
RAG/grep文本✗ 无✗ 无✓ 直接

codebadger 的独特价值:Joern 的精确语义分析 + MCP 协议 LLM 集成。


八、Joern 核心数据结构

8.1 CPG 节点类型

节点类型说明示例
Method函数/方法定义int foo(int x) {...}
Call函数调用foo(42)
Local局部变量int x = 10;
Identifier标识符引用x, y, foo
Literal字面量42, "hello", true
Block代码块{ ... }
If/Else/While/For控制结构if (x > 0) {...}
Return返回语句return x;
Member类成员class.field
TypeDecl类型声明struct Foo {...}

8.2 CPG 边类型

边类型说明用途
AST 边语法父子关系结构遍历
CFG 边控制流跳转执行路径分析
DDG 边数据依赖(定义→使用)污点追踪
CDG 边控制依赖条件影响分析
CALL 边函数调用关系调用图构建
EVAL_TYPE 边类型信息类型推断

九、部署 Joern

9.1 codebadger Docker(推荐)

# 启动预配置的 Joern 容器
docker compose up -d

# 验证服务
docker ps | grep joern

9.2 独立安装

# 安装 Joern
curl -L "https://github.com/joernio/joern/releases/latest/download/joern-install.sh" | sh

# 生成 CPG
joern-parse /path/to/code

# 启动交互式查询
joern

# 在 REPL 中查询
importCode("cpg.bin")
cpg.method.name.l

9.3 配置选项

# Java 堆内存(大代码库需要更多内存)
JOERN_JAVA_OPTS="-Xmx16G -Xms8G"

# CPG 生成超时
CPG_GENERATION_TIMEOUT=600  # 10 分钟

# 最大仓库大小
MAX_REPO_SIZE_MB=500

十、CPG 论文背景

原始论文

The Code Property Graph — A New Program Representation for Security Analysis
Fabian Yamaguchi, Nico Golde, Daniel Arp, Konrad Rieck
NDSS 2014 (Network and Distributed System Security Symposium)
https://www.usenix.org/conference/ndss14

论文贡献

  • 提出 Code Property Graph 统一表示(AST + CFG + PDG)
  • 设计 CPGQL 查询语言
  • 验证在漏洞检测中的有效性(分析 6 个真实 CVE)
  • 奠定 Joern 的理论基础

十一、Joern 应用场景

场景用途
漏洞扫描检测 UAF/Double-Free/Overflow/TOCTOU 等
代码审计理解大型代码库结构和数据流
安全研究发现未公开漏洞(如 codebadger 的 5 个 CVE)
补丁验证确认修复是否完整覆盖漏洞路径
逆向分析Ghidra 二进制 CPG 支持
LLM 增强为 AI Agent 提供精确语义导航

十二、总结

三句话总结

  1. 理论基础:CPG = AST + CFG + PDG,三图融合提供完整代码语义
  2. 技术优势:跨函数数据流追踪、12 种语言支持、可扩展 CPGQL 查询
  3. 应用价值:codebadger 封装后成为 LLM Agent 的漏洞检测引擎,发现 5 个真实 CVE

核心价值表

价值说明
精确语义分析三图融合提供完整语义信息,超越纯文本分析
跨函数追踪PDG 支持跨边界数据流追踪,突破 LLM token 限制
开源生态Apache 2.0 许可证,活跃社区,持续迭代
LLM 集成桥梁codebadger MCP Server 让 Joern 能被 AI Agent 直接调用

参考文献