可通过 JSON 规则自定义的 Java 字节码分析器。它是一个命令行工具,接收包含一个或多个 Jar 或 War 文件的路径,使用提供的规则进行分析,并生成 HTML 格式的结果报告。
usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE
-a,--analyze <pathToAnalyze> 待分析目录的路径。
-c,--checks <checks...> 以空格分隔的自定义检查列表,这些检查将在分析中运行。
-f,--custom-file <customFile> 指定 JSON 格式的文件以运行自定义规则。更多信息请参阅
https://github.com/fergarrui/custom-bytecode-analyzer。
-h,--help 打印此消息。
-i,--items-report <maxItems> 每个报告的最大条目数。如果发现的问题数超过此值,
报告将分割成多个文件。如果预期报告中有太多问题,此选项很有用。默认值:50。
-o,--output <outputDir> 保存报告的目录。警告 – 如果此目录中已有报告,
它们将被覆盖。默认为 "report"。
-v,--verbose-debug 将详细程度提高到调试模式。
-vv,--verbose-trace 将详细程度提高到追踪模式 – 会使速度变慢,仅在需要时使用。
可以使用 -f,--custom-file 参数指定规则文件。该文件为 JSON 格式,结构如下:
final 时支持你也可以查看 net.nandgr.cba.custom.model.Rules.java 来了解 Java 代码中的结构。
在 examples 目录下已经有一些规则。下面针对每种规则列出了示例。
如果需要查找具有自定义反序列化的类,可以很容易地做到。一个类通过实现 private void readObject(ObjectInputStream in) 来定义自定义反序列化。因此我们只需要找到所有定义了该方法的类。定义如下的规则就足够了:
{
"rules": [{
"name": "自定义反序列化",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
它将报告 private 可见性、名为 readObject、具有 java.io.ObjectOutputStream 类型参数的方法。parameters 是一个数组,如果指定了多个参数,则必须全部匹配才会被报告。由于我们只有一个规则,将创建一个名为 custom-deserialization-0.html 的报告。
在这种情况下,需要定义一个包含两个方法的规则。一个与上一个示例中的反序列化相同,另一个用于匹配 private void writeObject(ObjectOutputStream out)。如上面的 JSON 结构所示,属性 rules.rule.methods 是方法的数组,因此可以编写如下的规则:
{
"rules": [{
"name": "自定义序列化和反序列化",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
},{
"name": "writeObject",
"report": "false",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectOutputStream"
}]
}]
}]
}
属性 report 被设置为 false,以避免对同一规则报告两次。我们只将第二个方法作为条件,仅报告 readObject 方法应该足以满足此规则的目的。
如果某个属性未定义,它将始终匹配为 true。例如,以下规则将返回所有方法定义:
{
"rules": [{
"name": "方法定义",
"methods": [{
}]
}]
}
也可以查找方法调用。此情况下的 JSON 为:
{
"rules": [{
"name": "String equals",
"invocations": [{
"owner": "java.lang.String",
"method": {
"name": "equals"
}
}]
}]
}
属性 owner 指定包含该方法的类。
另一个比上一个更有用的方法调用示例:
{
"rules": [{
"name": "通过反射调用方法",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
这与任何方法调用相同,但此情况下方法的名称应为 <init>。
{
"rules": [{
"name" : "String 实例化",
"invocations" : [{
"owner" : "java.lang.String",
"method" : {
"name" : "<init>"
}
}]
}]
}
此规则将查找以下代码的出现:
[...]
String s = new String("foo");
[...]
在此示例中,我们想要查找反序列化的使用(而不是像前面示例中定义序列化行为的类)。反序列化发生在调用 ObjectInputStream.readObject() 时。例如在以下代码片段中:
ObjectInputStream in = new ObjectInputStream(fileInputStream);
Object o = in.readObject();
因此我们需要查找来自 ObjectInputStream 的名为 readObject 的方法调用。但在研究上下文中,它会产生大量误报,因为当一个类定义了自定义反序列化时,它们会在 private void readObject(ObjectInputStream in) 方法内部调用此方法,这会使报告过于杂乱。如果我们想排除这些情况,只保留真正的反序列化,可以使用 notFrom 属性:
{
"rules": [{
"name": "反序列化使用",
"invocations": [{
"owner": "java.io.ObjectInputStream",
"method": {
"name": "readObject"
},
"notFrom": {
"name": "readObject",
"visibility": "private"
}
}]
}]
}
此文件将查找 java.io.ObjectInputStream.readObject() 调用,但排除在 private void readObject(ObjectInputStream in) 方法内部进行的调用。
使用以下代码编译的类不会被报告:
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
Object o = in.readObject();
}
但以下代码会被报告:
public Object deserializeObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
Object o = in.readObject();
return o;
}
属性 from 可以以完全相同的方式用于调用,但结果相反:仅当调用来自定义的方法时才匹配。
此情况下可以使用属性 superClass。如果我们想查找所有继承自 javax.servlet.http.HttpServlet 的类,可以定义规则:
{
"rules": [{
"name": "Java servlets",
"superClass" : "javax.servlet.http.HttpServlet"
}]
}
可以编写规则来查找实现一组接口的类。如果规则中定义了多个接口,则类必须实现所有这些接口才会被报告。如果我们想查找实现 javax.net.ssl.X509TrustManager 的类,规则为:
{
"rules": [{
"name": "X509TrustManager 实现",
"interfaces" : ["javax.net.ssl.X509TrustManager"]
}]
}
请注意 interfaces 是一个数组,因此请确保将字符串放在方括号内,例如 ["interface1", "interface2", ...]。
注解也受支持。可以在规则(查找类注解)、方法或变量(参数或局部变量)中定义多个注解属性。如果在分析的类中找到所有这些注解,则会报告。
例如,如果我们想查找 Spring 端点,我们将搜索带有 org.springframework.web.bind.annotation.RequestMapping 注解的类或方法。因此,规则可以是:
{
"rules": [{
"name": "Spring 端点 - 类注解",
"annotations" : [{
"type" : "org.springframework.web.bind.annotation.RequestMapping"
}]
},
{
"name": "Spring 端点 - 方法注解",
"methods" : [{
"annotations" : [{
"type" : "org.springframework.web.bind.annotation.RequestMapping"
}]
}]
}]
}
可以使用属性 rule.fields 来查找类字段。如果我们想查找包含密码名称的私有 String 字段,可以使用如下规则:
{
"rules": [{
"name" : "密码字段",
"fields" : [
{
"visibility" : "private",
"type" : "java.lang.String",
"nameRegex" : "(password|pass|psswd|passwd)"
}
]
}]
}
要查找变量,可以使用 rule.variables。此属性将报告局部变量和方法参数变量。
如果我们想查找所有类型为 javax.servlet.http.Part 的变量,规则可以是:
{
"rules": [{
"name" : "Servlet 上传文件",
"methods" : [{
"variables" : [{
"type" : "javax.servlet.http.Part"
}]
}]
}]
}
可以在同一个 JSON 文件中定义多个规则。它们将分别被处理和报告,并且互不影响。我们可以组合前面的一些示例规则:
{
"rules": [{
"name": "自定义反序列化",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters": [{
"type" : "java.io.ObjectInputStream"
}]
}]
},{
"name": "通过反射调用方法",
"invocations": [{
"owner": "java.lang.reflect.Method",
"method": {
"name": "invoke"
}
}]
}]
}
这里我们有两个规则("自定义反序列化" 和 "通过反射调用方法")。它们会像在两个单独的执行中一样被处理。每个规则将生成一个报告。如果规则具有相同的名称,它们将在同一个文件中报告。
可以下载并构建项目,以添加更复杂的 Java 代码自定义规则,这些规则无法通过 JSON 格式覆盖。在包 net.nandgr.cba.visitor.checks 中已有三个示例:CustomDeserializationCheck、DeserializationCheck 和 InvokeMethodCheck。你可以通过继承 net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor 来创建自己的规则。
如上所述,报告默认创建在 report 文件夹下。每个规则都有一个单独的文件,除非它们具有相同的名称。
如果报告太大,可以使用 -i,--items-report <maxItems> 参数进行拆分,每个报告将包含指定数量或更少的条目(如果是最后一个)。
每个报告的条目都指定了它所在的 jar、类名和方法名(如果相关)。它还显示类的反编译版本,以便快速进行视觉检查。
以下是针对查找 java.io.File 实例化的规则,条目显示方式的示例:

在查找安全漏洞时,调用图非常有用。目前,在 report 目录下会生成一个简单的 DOT 兼容文件。
该图包含所有可能的流程,这些流程可以调用发现的问题。例如,如果使用查找反序列化的规则,将生成一个包含所有可能路径的图,这些路径通向调用反序列化的方法。
该文件名为 call-graph.dot,其内容类似于(这是一个极其简单的示例):
graph callGraph {
"demo.callgraph.Class1:method1" -- "demo.callgraph.Class2:method2"
"demo.callgraph.Class3:method3" -- "demo.callgraph.Class2:method2"
}
要以可视方式显示,可以使用 DOT(或任何兼容的软件)。例如,将文件转换为 svg:
dot -Tsvg call-graph.dot -o call-graph.svg
如果系统 PATH 中找到了 DOT,则会自动执行此操作。如果没有,可以在基于 Debian 的系统上使用 sudo apt-get install graphviz 安装 DOT。
它将创建一个名为 call-graph.svg 的 SVG 文件,可以使用 inkscape 或 firefox 等程序进行转换或可视化。
上述文件 call-graph.dot 的一个非常简单示例如下:

存在一些限制,例如,如果搜索的项目在 java.lang.Runnable.run() 或类似方法中,则无法找到线程从哪里执行。
此外,该图会清除循环以避免 StackOverflowError,它是以一种相对保守的方式完成的,以避免在分析大型目录时耗尽系统内存。
未来版本将添加更多选项。
java -jar cba-cli-<version>.jar -a /path/with/jars -f /path/with/json/file/rules.json
要使用自定义 Java 规则,必须将类名指定为 -c 的参数。
java -jar cba-cli-<version>.jar -a /path/with/jars -c DeserializationCheck
接受以空格分隔的列表,因此可以定义多个自定义规则(每个规则将创建一个单独的报告):
java -jar cba-cli-<version>.jar -a /path/with/jars -c DeserializationCheck InvokeMethodCheck CustomDeserializationCheck YourCustomRule
java -jar cba-cli-<version>.jar -a /path/with/jars -f /path/with/json/file/rules.json -c YourCustomRule1 YourCustomRule2
要查找错误,可以增加详细程度。 调试级别:
java -jar cba-cli-<version>.jar -a /path/with/jars -c YourCustomRule1 -v
追踪级别:
java -jar cba-cli-<version>.jar -a /path/with/jars -c YourCustomRule1 -vv
目前,APK 必须首先转换为 JAR 才能进行分析。
d2j-dex2jar.sh -f -o app_to_analyze.jar app_to_analyze.apk-a 参数。在 bin 目录下已经有一个可执行的 jar 文件,位于:https://github.com/fergarrui/custom-bytecode-analyzer/blob/master/bin/cba-cli-0.1-SNAPSHOT.jar。如果你想进行修改或添加自定义规则,可以通过以下方式构建项目:
git clone https://github.com/fergarrui/custom-bytecode-analyzer.git
cd custom-bytecode-analyzer
mvn clean package
将在 target 文件夹下生成两个 jar 文件。cba-cli-<version>.jar 包含所有依赖项并且是可执行的。可以使用 java -jar cba-cli-<version>.jar 运行。