可通过 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"
}]
}]
}]
}