Java Bytecode Analyzer настраивается через правила JSON. Это инструмент командной строки, который получает путь, содержащий один или несколько файлов JAR или WAR, анализирует их, используя предоставленные правила и генерирует отчеты 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 должны быть достаточно для целей этого правила.
Если свойство не определена, оно всегда будет соответствовать правде. Например, это правило будет возвращать все определения методов:
{
"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 (); Таким образом, нам нужно найти вызова метода из ObjectInputStream с именем readObject . Но он найдет много ложных срабатываний в исследовательском контексте, потому что, когда класс определяет пользовательскую десериализацию, они делают призыв к этому методу в методе private void readObject(ObjectInputStream in) , и это будет слишком много загрязнять отчет. Если мы хотим исключить эти случаи и сохранить только подлинную десериализацию, можно использовать свойство notFrom :
{
"rules" : [{
"name" : " Deserialization usage " ,
"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 может быть установлено в вызовах точно так же, как и notFrom , но результат будет противоположным: оно будет соответствовать только в случае получения вызова из определенного метода.
В этом случае может использоваться 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 "
}
}]
}]
}Здесь у нас есть два правила («Пользовательская десериализация» и «вызов метода по размышлению»). Они будут обработаны, как если бы вы делали это в двух отделенных казни. И отчет на правило будет создан. Если правила имеют одинаковое имя, они будут сообщены в том же файле.
Проект может быть загружен и создан, чтобы добавить более сложные пользовательские правила в коде Java, которые не покрываются форматом JSON. В пакете уже есть три примера 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 instantiations:

При поиске ошибок безопасности очень полезно иметь график вызовов. На данный момент простой DOT -совместимый файл создается в каталоге report . График содержит все возможные потоки, из которых можно вызвать возникшие проблемы. Например, если используется правило для поиска десериализации, график, содержащий все возможные пути, приводящие к методу, который вызывает десериализацию, будет создан.
Файл- 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
Это делается автоматически по умолчанию, если DOT находится в пути системы. Если нет, DOT может быть установлен в системах на основе Debian, используя sudo apt-get install graphviz .
Он создаст файл SVG с именем call-graph.svg , который может быть преобразован в PNG или визуализирован с использованием таких программ, как inkscape или просто firefox .
Очень простой пример приведенного выше файла call-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 Каталог, содержащий конвертированный файл JAR. В каталоге 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 папке. cba-cli-<version>.jar содержит все зависимости и исполняется. Можно запустить с помощью java -jar cba-cli-<version>.jar