JSONルールでカスタマイズ可能なJavaバイトコードアナライザ。これはコマンドラインツールで、1つ以上の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": "Custom deserialization",
"methods": [{
"name": "readObject",
"visibility": "private",
"parameters" : [{
"type" : "java.io.ObjectInputStream"
}]
}]
}]
}
これは、private 可視性、readObject という名前、および java.io.ObjectOutputStream 型のパラメータを持つメソッドを報告します。パラメータは配列であり、複数指定された場合、すべてが一致する必要があります。ルールは1つだけなので、custom-deserialization-0.html という名前のレポートが作成されます。
この場合、2つのメソッドを持つ1つのルールを定義する必要があります。デシリアライゼーションについては前の例と同じものを、そして private void writeObject(ObjectOutputStream out) に一致する新しいものを定義します。上記のJSON構造に示されているように、プロパティ rules.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 に設定することで、同じルールで2回報告されるのを防ぎます。2番目のメソッドは条件としてのみ使用し、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();
そのため、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"
}
}]
}]
}
このファイルは、呼び出しが 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;
}
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", ...]。
アノテーションもサポートされています。ルール(クラスアノテーションを見つけるため)、メソッド、変数(パラメータまたはローカル変数)に複数のアノテーションプロパティを定義できます。解析されたクラスでそれらすべてが見つかった場合、報告されます。
例えば、Springエンドポイントを見つけたい場合、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 プロパティを使用してクラスフィールドを見つけることができます。パスワード名を持つプライベートなStringフィールドを見つけたい場合、次のようなルールを使用できます。
{
"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"
}
}]
}]
}
ここでは、"Custom deserialization" と "Method invocation by reflection" の2つのルールがあります。それらは別々の実行として処理され、ルールごとに1つのレポートが生成されます。ルールの名前が同じ場合、それらは同じファイルに報告されます。
プロジェクトをダウンロードしてビルドすることで、JSON形式ではカバーされない、より複雑なカスタムルールをJavaコードで追加できます。パッケージ net.nandgr.cba.visitor.checks の下にすでに3つの例があります。それらは 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 などのプログラムを使用してPNGに変換したり表示したりできます。
上記の 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 パラメータとして指定して、通常どおりcba-cli.jarを実行します。実行可能なjarファイルは、bin ディレクトリの下にすでにあります: 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 フォルダの下に2つのjarが生成されます。cba-cli-<version>.jar はすべての依存関係を含み、実行可能です。java -jar cba-cli-<version>.jar で実行できます。