官网给了一个简单的教程,这篇文章就是按github上的文档来实践的。
网址:https://github.com/ShiftLeftSecurity/joern/blob/master/docs2/docs/quickstart.mdx
这个工具的作者最近更新频率好高。太强了。
这里用joern作者提供的一个样本程序叫x42
$ git clone [email protected]:ShiftLeftSecurity/x42.git
如果遇到下面这个问题:permission denied (publickey)
可以使用这个链接的方法来解决:https://baijiahao.baidu.com/s?id=1628034638876029219&wfr=spider&for=pc
在joern的文件夹下,运行./joern
即可打开shell。这个joern shell是基于scala的REPL,REPL类似于python的IDLE。作者提到,如果没用过scala也没关系,仍然可以用joern的命令做到狠多事情。如果很熟悉scala的话,你会感叹它的灵活性。
这里就不像上次那篇博客用joern-parse
来导入代码,shell里也可以直接使用importCode
命令导入代码。
importCode(inputPath="./x42/c", projectName="x42-c")
open("x42-c")
打开马上就要用joern和代码属性图来分析程序了。joern的程序分析的实现是基于查询语言的,一种专门为代码属性图设计的语言。它包含了代码属性图各种各样节点的有用表示,以及有用的函数来查询它们的属性及它们之间的关系。代码属性图中最顶层的入口点加载在内存中。然后查询语言中的根对象叫cpg
,如果我们在shell里输入cpg,只会返回一个id,并没有什么卵用。
我们首先学习一个有用的技巧,输入cpg.
然后按TAB
键,会列出cpg可用的方法。
res12: String = """
Upon importing code, a project is created that holds an intermediate
representation called `Code Property Graph`. This graph is a composition of
low-level program representations such as abstract syntax trees and control flow
graphs, but it can be arbitrarily extended to hold any information relevant in
your audit, information about HTTP entry points, IO routines, information flows,
or locations of vulnerable code. Think of Ocular and Joern as a CPG editors.
In practice, `cpg` is the root object of the query language, that is, all query
language constructs can be invoked starting from `cpg`. For example,
`cpg.method.l` lists all methods, while `cpg.finding.l` lists all findings of
potentially vulnerable code."""
输入cpg.help
可以查看cpg
下面各个方法的描述
res13: String = """Available starter steps:
____________________________________________________________________________
step | description |
===========================================================================|
.all | All nodes of the graph |
.argument | All arguments (actual parameters) |
.arithmetic | All arithmetic operations |
.assignment | All assignments |
.call | All call sites |
.comment | All comments in source-based CPGs |
.controlStructure| All control structures (source-based frontends) |
.file | All source files |
.identifier | All identifier usages |
.jumpTarget | All jump targets, i.e., labels |
.literal | All literals, e.g., numbers or strings |
.local | All local variables |
.member | All members of complex types (e.g., classes/structures)|
.metaData | Meta data blocks for graph |
.method | All methods |
.methodRef | All method references |
.methodReturn | All formal return parameters |
.namespace | All namespaces |
.parameter | All parameters |
.returns | All actual return parameters |
.tag | All tags |
.typeDecl | All declarations of types |
.types | All used types |
"""
首先来看一下x42
程序:
// X42.c
#include
#include
#include
int main(int argc, char *argv[]) {
if (argc > 1 && strcmp(argv[1], "42") == 0) {
fprintf(stderr, "It depends!\n");
exit(42);
}
printf("What is the meaning of life?\n");
exit(0);
}
这个程序主要分为两个部分:
Joern使我们很容易回答这两个问题。回答第一个问题,我们只需要搜索图里属性为CALL
的节点,然后使用filter
直接选择那些和类型为ARGUMENT
且值为stderr
的节点有关系的CALL
节点。
cpg.call.filter(_.argument.code("stderr")).l
结果如下:
这个查询告诉我们确实有个地方向STDERR写了点啥玩意。接下来,我们看第二个问题。由于我们分析的程序是用C语言写的,所以我们在代码属性图里搜索main
函数里的argc
和argv
参数,这些参数可能会触发写STDERR的调用。基于上一步的查询,现在我们可以使用astParent
来移到代码属性图的抽象语法树层面来,而找到更多的关于fprintf
调用的信息。移到AST层,只给了我们一个block,并不是很有用:
cpg.call.filter(_.argument.code("stderr")).astParent.l
另外一层给了我们一个if语句,更有用:
cpg.call.filter(_.argument.code("stderr")).astParent.astParent.l
这个ControlStructure
里的code
属性告诉我们,写STDERR的调用是基于argc
和argv
的。
现在我们完成了分析,让我们关闭工程。不需要担心会丢失数据,因为x42-c
工程在importCode
的时候就创建完毕了。
close
输入命令workspace
可以看到你之前建立的所有工程,如下图:
这样我们可以查询到各个工程的状态,有时候忘记自己建立的工程的名字,也可以在这里查看到。
接下来你可以使用open("simple")
打开这个simple的工程。