Java Bytecode Analyzer über JSON -Regeln anpassbar. Es handelt sich um ein Befehlszeilen-Tool, das einen Pfad empfängt, der ein oder mehrere Glas- oder Kriegsdateien enthält, sie anhand der bereitgestellten Regeln analysiert und HTML-Berichte mit den Ergebnissen generiert.
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.
Die Regelnungsdatei kann mit -f,--custom-file Argument angegeben werden. Die Datei befindet sich im JSON -Format und hat die folgende Struktur:
final ist Sie können auch net.nandgr.cba.custom.model.Rules.java überprüfen, um die Struktur im Java -Code zu sehen.
Es gibt bereits mehrere Regeln in den Beispielen für Verzeichnisse. Im Folgenden finden Sie unten Beispiele für jede Regel.
Wenn wir Klassen mit benutzerdefinierter Deserialisierung finden müssen, können wir dies ganz einfach tun. Eine Klasse definiert eine benutzerdefinierte Deserialisierung durch Implementierung private void readObject(ObjectInputStream in) . Wir müssen also nur alle Klassen finden, in denen diese Methode definiert ist. Es wäre ausreichend, nur eine Regel als zu definieren:
{
"rules" : [{
"name" : " Custom deserialization " ,
"methods" : [{
"name" : " readObject " ,
"visibility" : " private " ,
"parameters" : [{
"type" : " java.io.ObjectInputStream "
}]
}]
}]
} Es werden Methoden mit private Sichtbarkeit, readObject als Name und einem Parameter des Typs java.io.ObjectOutputStream gemeldet. Parameter sind ein Array, wenn mehr als einer angegeben ist, müssen alle übereinstimmen, um gemeldet zu werden. Da wir nur eine Regel haben, wird ein Bericht mit dem Namen Custom-Deserialization-0.html erstellt.
In diesem Fall muss eine Regel mit zwei Methoden definiert werden. Das gleiche als im vorherigen Beispiel für die Deserialisierung und eine neue, die private void writeObject(ObjectOutputStream out) entspricht. Wie in der obigen JSON -Struktur gezeigt, ist die Eigenschaftsregeln.
{
"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 "
}]
}]
}]
} Der report wurde auf False festgelegt, um zu vermeiden, dass die Meldung zweimal für dieselbe Regel gemeldet wurde. Wir verwenden die zweite Methode genau als Bedingung, aber die readObject nur für den Zweck dieser Regel sollte ausreichen.
Wenn eine Eigenschaft nicht definiert ist, stimmt sie immer als wahr überein. Beispielsweise würde diese Regel alle Methodendefinitionen zurückgeben:
{
"rules" : [{
"name" : " Method definitions " ,
"methods" : [{
}]
}]
}Methodenaufrufe können auch gefunden werden. Der JSON in diesem Fall wäre:
{
"rules" : [{
"name" : " String equals " ,
"invocations" : [{
"owner" : " java.lang.String " ,
"method" : {
"name" : " equals "
}
}]
}]
} Der owner des Eigentums gibt die Klasse an, die die Methode enthält.
Ein anderer Beispiel für Methodenaufruf ein bisschen nützlicher als der vorherige:
{
"rules" : [{
"name" : " Method invocation by reflection " ,
"invocations" : [{
"owner" : " java.lang.reflect.Method " ,
"method" : {
"name" : " invoke "
}
}]
}]
} Es ist dasselbe als jeder Methodenaufruf, aber der Name der Methode in diesem Fall sollte <init> sein.
{
"rules" : [{
"name" : " String instantiation " ,
"invocations" : [{
"owner" : " java.lang.String " ,
"method" : {
"name" : " <init> "
}
}]
}]
}Diese Regel findet Ereignisse von:
[...]
String s = new String ( "foo" );
[...] In diesem Beispiel möchten wir Deserialisierungsnutzungen finden (keine Klassen, die das Serialisierungsverhalten wie in den vorherigen Beispielen definieren). Die Deserialisierung erfolgt, wenn ObjectInputStream.readObject() aufgerufen wird. Zum Beispiel in diesem Code -Snippet:
ObjectInputStream in = new ObjectInputStream ( fileInputStream );
Object o = in . readObject (); Daher müssen wir Methodenanfälle aus ObjectInputStream mit dem Namen readObject finden. In einem recherchierenden Kontext wird jedoch viele falsch positive Ergebnisse finden, denn wenn eine Klasse eine benutzerdefinierte Deserialisierung definiert, machen sie in einer private void readObject(ObjectInputStream in) einen Aufruf zu dieser Methode, und das würde den Bericht zu stark verschmutzen. Wenn wir diese Fälle ausschließen und nur eine echte Deserialisierung beibehalten möchten, kann notFrom Eigenschaften verwendet werden:
{
"rules" : [{
"name" : " Deserialization usage " ,
"invocations" : [{
"owner" : " java.io.ObjectInputStream " ,
"method" : {
"name" : " readObject "
},
"notFrom" : {
"name" : " readObject " ,
"visibility" : " private "
}
}]
}]
} In dieser Datei wird java.io.ObjectInputStream.readObject() -Avokationen gefunden, wenn der Aufruf nicht in private void readObject(ObjectInputStream in) durchgeführt wird.
Eine mit diesem Code zusammengestellte Klasse wird nicht gemeldet:
private void readObject ( ObjectInputStream in ) throws IOException , ClassNotFoundException {
Object o = in . readObject ();
}Aber dieser wird gemeldet:
public Object deserializeObject ( ObjectInputStream in ) throws IOException , ClassNotFoundException {
Object o = in . readObject ();
return o ;
} Die Eigenschaft from kann in Aufrufe genau auf die gleiche Weise als notFrom werden, aber das Ergebnis ist das Gegenteil: Sie stimmt nur überein, wenn der Aufruf aus der definierten Methode erfolgt.
Die Immobilien superClass kann in diesem Fall verwendet werden. Wenn wir alle Klassen finden möchten, die javax.servlet.http.HttpServlet erweitern, kann eine Regel lautet:
{
"rules" : [{
"name" : " Java servlets " ,
"superClass" : " javax.servlet.http.HttpServlet "
}]
}
Eine Regel kann geschrieben werden, um Klassen zu finden, die eine Reihe von Schnittstellen implementieren. Wenn in der Regel mehr als eine Schnittstelle definiert ist, muss die Klasse alle implementieren, um gemeldet zu werden. Wenn wir Klassen finden möchten, javax.net.ssl.X509TrustManager implementieren, wäre die Regel:
{
"rules" : [{
"name" : " X509TrustManager implementations " ,
"interfaces" : [ " javax.net.ssl.X509TrustManager " ]
}]
} Bitte beachten Sie, dass interfaces ein Array sind. Stellen Sie daher sicher, dass Sie die Zeichenfolgen zwischen quadratischen Klammern hinzufügen, z. B. ["interface1", "interface2", ...] .
Anmerkungen werden ebenfalls unterstützt. Mehrere Annotationeneigenschaften können in einer Regel (Anmerkungen finden) in Methoden o Variablen (Parameter oder lokale Variablen) definiert werden. Wenn alle in der analysierten Klasse gefunden werden, wird gemeldet. Wenn wir beispielsweise Frühlingsendpunkte finden möchten, würden wir nach Klassen oder Methoden suchen, die mit org.springframework.web.bind.annotation.RequestMapping kommuniziert werden. Die Regel kann also sein:
{
"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 "
}]
}]
}]
} Die rule.fields können verwendet werden, um Klassenfelder zu finden. Wenn wir private String -Felder mit Kennwortnamen finden möchten, kann eine solche Regel verwendet werden:
{
"rules" : [{
"name" : " Password fields " ,
"fields" : [
{
"visibility" : " private " ,
"type" : " java.lang.String " ,
"nameRegex" : " (password|pass|psswd|passwd) "
}
]
}]
} Um Variablen zu finden, können rule.variables verwendet werden. Diese Eigenschaft meldet lokale Variablen und Methodenargumente Variablen. Wenn wir alle Variablen vom Typ javax.servlet.http.Part finden möchten, könnte eine Regel lautet:
{
"rules" : [{
"name" : " Servlet upload file " ,
"methods" : [{
"variables" : [{
"type" : " javax.servlet.http.Part "
}]
}]
}]
}Mehrere Regeln können in derselben JSON -Datei definiert werden. Sie werden separat verarbeitet und gemeldet und wirken sich nicht gegenseitig aus. Wir können einige der vorherigen Beispielregeln kombinieren:
{
"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 "
}
}]
}]
}Hier haben wir zwei Regeln ("benutzerdefinierte Deserialisierung" und "Methodenaufruf nach Reflexion"). Sie werden verarbeitet, als ob Sie es in zwei getrennten Ausführungen tun. Und es wird ein Bericht pro Regel erstellt. Wenn die Regeln den gleichen Namen haben, werden sie in derselben Datei gemeldet.
Das Projekt kann heruntergeladen und erstellt werden, um komplexere benutzerdefinierte Regeln in Java -Code hinzuzufügen, die nicht vom JSON -Format behandelt werden. Es gibt bereits drei Beispiele unter dem Paket net.nandgr.cba.visitor.checks . Dies sind CustomDeserializationCheck, DeserializationCheck and InvokeMethodCheck . Sie können Ihre eigenen Regeln erstellen, indem Sie net.nandgr.cba.custom.visitor.base.CustomAbstractClassVisitor erweitern.
Wie oben erwähnt, werden die Berichte im report standardmäßig erstellt. Jede Regel hat eine separate Datei, es sei denn, sie haben denselben Namen. Wenn der Bericht zu groß ist, können Sie ihn mit dem Parameter -i,--items-report <maxItems> teilen. Jedes von ihnen wird das angegebene oder weniger Argument enthalten (falls er das letzte ist). Jedes gemeldete Element gibt das Glas an, in dem es gefunden wird, den Klassennamen und den Methodennamen (falls es relevant ist). Es zeigt auch die dekompilierte Version der Klasse, um eine schnelle visuelle Prüfung zu erleichtern. Beispiel dafür, wie die Elemente für eine Regel angezeigt werden, um java.io.File -Instanziationen zu finden:

Bei der Suche nach Sicherheitsfehlern ist es sehr nützlich, einen Anrufdiagramm zu haben. Derzeit wird im report eine einfache dot -kompatible Datei erstellt. Der Diagramm enthält alle möglichen Flüsse, aus denen die gefundenen Probleme aufgerufen werden können. Wenn beispielsweise eine Regel zum Finden der Deserialisierung verwendet wird, wird ein Diagramm, das alle möglichen Pfade enthält, die zur Methode führen, die die Deserialisierung aufruft, generiert.
Die Datei ist call-graph.dot und sie würde so aussehen (dies ist ein extrem einfaches Beispiel):
graph callGraph {
"demo.callgraph.Class1:method1" -- "demo.callgraph.Class2:method2"
"demo.callgraph.Class3:method3" -- "demo.callgraph.Class2:method2"
}
Um es visuell anzuzeigen, kann DOT (oder eine beliebige kompatible Software) verwendet werden. Zum Beispiel um die Datei in svg umzuwandeln:
dot -Tsvg call-graph.dot -o call-graph.svg
Dies geschieht standardmäßig automatisch, wenn DOT im Systempfad gefunden wird. Wenn nicht, kann DOT in Debian-basierten Systemen mit sudo apt-get install graphviz installiert werden.
Es erstellt eine SVG-Datei mit dem Namen call-graph.svg , die mit Programmen wie inkscape oder nur firefox in PNG umgewandelt oder visualisiert werden kann.
Ein sehr einfaches Beispiel für den oben genannten Dateiaufruf-Graph.dot wäre:

Es gibt einige Einschränkungen, wie zum Beispiel, wenn sich das durchsuchte Element in einer java.lang.Runnable.run() oder einer ähnlichen Methode befindet, wird nicht festgestellt, woher der Thread ausgeführt wird. Außerdem ist das Diagramm Reinigungszyklen, um StackOverflowError -S zu vermeiden. Es wird auf ein wenig konservativer Weise hergestellt, sodass der Gedächtnis des Systems während einer Analyse eines großen Verzeichnisses nicht entwässert wird.
In zukünftigen Versionen werden weitere Optionen hinzugefügt.
java -jar cba-cli-<version>.jar -a /path/with/jars -f /path/with/json/file/rules.json
Um benutzerdefinierte Java -Regeln zu verwenden, müssen Klassennamen als Argumente von -c angegeben werden.
java -jar cba-cli-<version>.jar -a /path/with/jars -c DeserializationCheck
Akzeptiert eine Liste mit Leerzeichen, sodass mehrere benutzerdefinierte Regeln definiert werden können (jede der Regeln erstellen einen separaten Bericht):
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
Um Fehler zu finden, kann Ausführlichkeit erhöht werden. Debug -Level:
java -jar cba-cli-<version>.jar -a /path/with/jars -c YourCustomRule1 -v
Spurenstufe:
java -jar cba-cli-<version>.jar -a /path/with/jars -c YourCustomRule1 -vv
Derzeit muss der APK zuerst in Jar konvertiert werden, um zu analysieren.
d2j-dex2jar.sh -f -o app_to_analyze.jar app_to_analyze.apk-a -Parameter das Verzeichnis mit der konvertierten JAR -Datei aus. Es gibt bereits eine ausführbare JAR-Datei unter bin Verzeichnis unter: https://github.com/fergarrui/custom-bytecode-analyzer/blob/master/bin/cba-cli-0.1-snapshot.jar. Wenn Sie Änderungen vornehmen oder benutzerdefinierte Regeln hinzufügen möchten, kann das Projekt erstellt werden:
git clone https://github.com/fergarrui/custom-bytecode-analyzer.git
cd custom-bytecode-analyzer
mvn clean package
Zwei Gläser werden unter target generiert. cba-cli-<version>.jar enthält alle Abhängigkeiten und ist ausführbar. Kann mit java -jar cba-cli-<version>.jar ausgeführt werden