Introduction
基本操作
- 安装过程按照Docs中的:
./joern-install.sh --interactive
之后进入安装目录,./joern
- 项目管理
// 导入项目
importCode(inputPath="<ProjectPaht>", projectName="<ProjectName>")
importCode("/Users/fe1w0/Project/SoftWareAnalysis/java-sec-code",projectName="java-sec-code")
// 查看工程
workspace
// 关闭当前项目
close
// 打开工程
open(ProjectNmae)
查询
-
选择起点
cpg.call
cpg.call.argument.code("")
cpg.call.argument.code("").astParent.l
cpg.method
cpg.assignment
- …
- 筛选节点
where
filter
- 筛选输出信息
map
- help
help.cpg
// 查询 cpg 的基本元数据
// l 表示 toList , 也可以用 toJson
// Tab 键可以补全查询命令
cpg.metaData.l
// 列出 所有的 method
cpg.method.l
// 过滤 method
cpg.method.map( m => List(m.fullName, m.name)).take(3).l
// 查看 caller 函数
cpg.method.name("readObject").caller.l
// 正则匹配
cpg.method.name("getRequestBody").callee.name("convert.*").l
// 在 SpringBoot 项目中,大部分 web 接口都有 `@RequestMapping` 之类的注解
cpg.method.where(_.annotation.name(".*Mapping")).map(n=>(n.name, n.annotation.code.l)).l
查找哪些函数调用了getRequestBody
方法:
- 反向查找
- 第一种就是从给定的方法
getRequestBody
开始反向查找所有的调用方,看看调用方是否具有相关的注解 - 反向查找所有的caller,直到有相关的注解为止
- 第一种就是从给定的方法
cpg.method.name("getRequestBody").repeat(_.caller)(_.until(_.annotation.name(".*Mapping"))).size
- 正向查找
- 从所有带有相关注解的方法,正向向下展开查找。
- 正向查找,非常费时,搜索成本极高
cpg.method.where(_.annotation.name(".*Mapping")).repeat(_.callee)(_.until(_.name("getRequestBody"))).size
重复操作:
repeat..times..
:重复指定次数。- 例如:
x.repeat(_.caller)(_.times(5))
重复调用五次caller
查询。
- 例如:
repeat..until..
:重复操作直到满足 until 中指定的条件。- 例如:
x.repeat(_.caller)(_.until(_.name("foo")))
,重复调用caller
查询,直到找到一个方法名为 foo 的方法,找不到就返回空。
- 例如:
repeat..emit..times..
:同repeat..times..
,emit 的作用是将搜索过的节点加入结果集,无论是否到达指定的 times 次数,可以简单理解为or
关系。如果emit
没有指定参数,则将所有搜索过的节点加入结果集,如果指定了参数,只将符合条件的节点加入结果集中。- 例如:
x.repeat(_.caller)(_.emit(_.isMethod).times(5))
,这个查询会将查询 5 次caller
的结果加入结果集,同时将搜索路径上所有满足isMethod
(即所有Method
节点)的节点也加入结果集中。
- 例如:
repeat..emit..until..
:同repeat..until..
,其中emit
的作用同上,不再赘述。- 例如:
x.repeat(_.caller)(_.emit.until(_.name("foo")))
,该查询会重复调用caller
查询,直到找到方法名为foo
的方法,同时会记录所有的搜索过的节点。可以简单的理解为将符合emit
条件的节点或符合until
条件的节点都加入结果集,但循环的终结依然是until
指定的条件。
- 例如:
控制流分析
获取当前的调用链路
cpg.method.name("getRequestBody").enablePathTracking.repeat(_.caller)(_.until(_.annotation.name(".*Mapping"))).path.l
// or
cpg.method.where(_.annotation.name(".*Mapping")).enablePathTracking.repeat(_.callee)(_.until(_.name("getRequestBody"))).path.l (不推荐)
结果是正确的,就是有重复的项,不太理解
数据流分析
Joern 中也可以执行 数据流分析,需要定义好 source 和 sink ,之后调用 reachableBy 函数执行数据流分析。
查询格式: sink.reahableBy(source)
// 定义 source
def source = cpg.method.where(_.annotation.name(".Mapping")).parameter
// 定义 sink
def sink = cpg.call.name("openConnection")
// 调用分析
// dedup 去重
// p print
sink.reachableByFlows(source).dedup.p
画图
- 可视化
- plotDotAst
- plotDotCfg
- plotDotCpg14
- plotDotPdg
- plotDotCdg
- plotDotDdg
- …
- 导出dot
- dotAst
- …
- dotDdg
- joern-export
Say Goodbye
close
关闭项目exit
退出项目
References:
- 代码分析工具joern的基本用法-安全客 – 安全资讯平台 (anquanke.com)
- Code Analysis With Joern – lightless blog
- navex
- aalhuz/navex (github.com)
- 复活Navex:使用图查询进行代码分析 – FreeBuf网络安全行业门户 – 知乎上也有,但还是选择整合版文章
要深入的话,还得去阅读官方手册