Java字节码分析仪可通过JSON规则自定义。它是一个命令行工具,它接收包含一个或多个JAR或战争文件的路径,使用提供的规则对其进行分析,并生成带有结果的HTML报告。
usage: java -jar cba-cli.jar [OPTIONS] -a DIRECTORY_TO_ANALYZE
-a,--analyze <pathToAnalyze> Path of the directory to run the
analysis.
-c,--checks <checks...> Space separated list of custom checks
that are going to be run in the analysis.
-f,--custom-file <customFile> Specify a file in JSON format to run
custom rules. Read more in
https://github.com/fergarrui/custom-bytecode-analyzer.
-h,--help Print this message.
-i,--items-report <maxItems> Max number of items per report. If the
number of issues found exceeds this
value, the report will be split into
different files. Useful if expecting too
many issues in the report. Default: 50.
-o,--output <outputDir> Directory to save the report. Warning -
if there are already saved reports in
this directory they will be overwritten.
Default is "report".
-v,--verbose-debug Increase verbosity to debug mode.
-vv,--verbose-trace Increase verbosity to trace mode - makes it slower, use it only when you need.
可以使用-f,--custom-file参数指定规则文件。该文件为JSON格式,具有以下结构:
final时才支持您还可以检查net.nandgr.cba.custom.model.Rules.java以查看Java代码中的结构。
目录示例中已经有几个规则。无论如何,以下是每个规则的列出的示例。
如果我们需要找到具有自定义挑选化的课程,我们可以很容易地做到这一点。一类通过实现private void readObject(ObjectInputStream in)来定义自定义的挑选化。因此,我们只需要找到定义该方法的所有类。仅将规则定义为:
{
"rules" : [{
"name" : " Custom deserialization " ,
"methods" : [{
"name" : " readObject " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectInputStream "
}]
}]
}]
}它将报告具有private可见性的方法, readObject作为名称和类型java.io.ObjectOutputStream的参数。参数是一个数组,如果指定了多个,则所有这些数组都必须匹配以进行报告。由于我们只有一个规则,因此将创建一个名为:Custom-DeSerialization-0.html的报告。
在这种情况下,必须定义一种具有两种方法的规则。与上一个典型化的示例相同,以及一个新的,可以匹配private void writeObject(ObjectOutputStream out) 。如上上面的JSON结构所示,属性规则rule.Methods是一系列方法,因此可以写入这样的规则:
{
"rules" : [{
"name" : " Custom serialization and deserialization " ,
"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" : " Method definitions " ,
"methods" : [{
}]
}]
}也可以找到方法调用。在这种情况下,JSON将是:
{
"rules" : [{
"name" : " String equals " ,
"invocations" : [{
"owner" : " java.lang.String " ,
"method" : {
"name" : " equals "
}
}]
}]
}属性owner指定包含该方法的类。
另一个方法调用示例比上一个更有用:
{
"rules" : [{
"name" : " Method invocation by reflection " ,
"invocations" : [{
"owner" : " java.lang.reflect.Method " ,
"method" : {
"name" : " invoke "
}
}]
}]
}它与任何方法调用相同,但是在这种情况下,该方法的名称应为<init> 。
{
"rules" : [{
"name" : " String instantiation " ,
"invocations" : [{
"owner" : " java.lang.String " ,
"method" : {
"name" : " <init> "
}
}]
}]
}该规则将发现:
[...]
String s = new String ( "foo" );
[...]在此示例中,我们希望找到必不可少的用法(不是像以前的示例中那样定义序列化行为的类)。当ObjectInputStream.readObject()调用时,应进行挑战。例如,在此代码段中:
ObjectInputStream in = new ObjectInputStream ( fileInputStream );
Object o = in . readObject ();因此,我们需要从名为readObject的ObjectInputStream中找到方法调用。但是,它会在研究环境中找到很多误报,因为当一类定义自定义避免化时,它们在private void readObject(ObjectInputStream in)方法中对此方法进行调用,这会造成过多污染报告。如果我们要排除这些案例并仅保留真正的避难所,则可以使用notFrom属性:
{
"rules" : [{
"name" : " Deserialization usage " ,
"invocations" : [{
"owner" : " java.io.ObjectInputStream " ,
"method" : {
"name" : " readObject "
},
"notFrom" : {
"name" : " readObject " ,
"visibility" : " private "
}
}]
}]
}如果在private void readObject(ObjectInputStream in)方法中,此文件将找到java.io.ObjectInputStream.readObject()调用。
与此代码编译的类不会报告:
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 ;
}可以以与notFrom完全相同的方式设置from属性,但结果将相反:仅当从定义的方法中进行调用时,它才会匹配。
在这种情况下,可以使用属性superClass 。如果我们想找到扩展javax.servlet.http.HttpServlet所有类,则可以是:
{
"rules" : [{
"name" : " Java servlets " ,
"superClass" : " javax.servlet.http.HttpServlet "
}]
}
可以编写规则以查找实现一系列接口的类。如果规则中定义了多个接口,则类必须实现所有接口以进行报告。如果我们想找到实现javax.net.ssl.X509TrustManager类,则该规则将是:
{
"rules" : [{
"name" : " X509TrustManager implementations " ,
"interfaces" : [ " javax.net.ssl.X509TrustManager " ]
}]
}请注意, interfaces是一个数组,因此请确保在方括号之间添加字符串,例如: ["interface1", "interface2", ...] 。
还支持注释。可以在规则(查找类注释),方法o变量(参数或本地变量)中定义多个注释属性。如果所有这些都在分析的类中找到,则将报告。例如,如果我们想找到弹簧端点,我们将搜索使用org.springframework.web.bind.annotation.RequestMapping注释的类或方法。因此,规则可以是:
{
"rules" : [{
"name" : " Spring endpoint - class annotation " ,
"annotations" : [{
"type" : " org.springframework.web.bind.annotation.RequestMapping "
}]
},
{
"name" : " Spring endpoint - method annotation " ,
"methods" : [{
"annotations" : [{
"type" : " org.springframework.web.bind.annotation.RequestMapping "
}]
}]
}]
}属性rule.fields可用于查找类字段。如果我们想找到带有密码名称的私有字符串字段,则可以使用这样的规则:
{
"rules" : [{
"name" : " Password fields " ,
"fields" : [
{
"visibility" : " private " ,
"type" : " java.lang.String " ,
"nameRegex" : " (password|pass|psswd|passwd) "
}
]
}]
}要查找变量,可以使用rule.variables 。该属性将报告局部变量和方法参数变量。如果我们想找到javax.servlet.http.Part类型的所有变量,则可以是:
{
"rules" : [{
"name" : " Servlet upload file " ,
"methods" : [{
"variables" : [{
"type" : " javax.servlet.http.Part "
}]
}]
}]
}可以在同一JSON文件中定义多个规则。它们将被分开处理和报告,不会互相影响。我们可以结合一些以前的示例规则:
{
"rules" : [{
"name" : " Custom deserialization " ,
"methods" : [{
"name" : " readObject " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectInputStream "
}]
}]
},{
"name" : " Method invocation by reflection " ,
"invocations" : [{
"owner" : " java.lang.reflect.Method " ,
"method" : {
"name" : " invoke "
}
}]
}]
}在这里,我们有两个规则(“自定义避免”和“通过反射调用”)。它们将被处理好,就好像您在两个分开的执行中进行处理一样。并将生成规则的报告。如果规则具有相同的名称,则将在同一文件中报告。
可以下载和构建该项目,以在JSON格式未涵盖的Java代码中添加更复杂的自定义规则。软件包net.nandgr.cba.visitor.checks下的示例已经有三个示例。这些是CustomDeserializationCheck, DeserializationCheck and 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
如果在系统路径中找到点,则默认情况下会自动完成。如果不是,则可以使用sudo apt-get install graphviz将DOT安装在基于Debian的系统中。
它将创建一个名为call-graph.svg的SVG文件,可以使用inkscape或firefox之类的程序转换为PNG或可视化。
上述文件呼叫graph.dot的一个非常简单的示例是:

有一些限制,例如,如果搜索项目在java.lang.Runnable.run()或类似方法中,则将找不到从何处从何处执行该线程。此外,该图正在清洁周期以避免使用StackOverflowError s,它是以保守的方式制成的,因此在大型目录的分析过程中不会排干系统的记忆。
将来的版本将添加更多选项。
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参数运行cba -cli.jar,其中包含转换后的JAR文件的目录。 bin Directory下已经有一个可执行的JAR文件:如果要进行修改或添加自定义规则,则可以构建项目:
git clone https://github.com/fergarrui/custom-bytecode-analyzer.git
cd custom-bytecode-analyzer
mvn clean package
target文件夹将生成两个罐子。 cba-cli-<version>.jar包含所有依赖项,并且是可执行的。可以使用java -jar cba-cli-<version>.jar运行