mcpField
R
reversing-flutter-apps

reversing-flutter-apps

github.com/trilwuセキュリティ17756 回インストール12 週間前に更新
リポジトリ

reversing-flutter-apps について

Flutter モバイルアプリの調査手順をまとめたスキルです。Dart AOT の構造復元、通信の傍受、実行時検査を扱い、リリースビルドのロジックや API の特定に役立ちます。

主な機能

  1. 01Flutter のビルド形式の識別
  2. 02blutter による Dart シンボル復元
  3. 03オブジェクトプールと逆アセンブル結果の確認
  4. 04Flutter 通信の転送と傍受
  5. 05Frida による Dart 関数のフック

活用シーン

  1. 01Flutter リリースビルドでのエンドポイント特定
  2. 02Burp や mitmproxy に通信が出ない場合の調査
  3. 03リクエスト構築関数やトークン処理の検査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/trilwu/secskills --skill reversing-flutter-apps

インストール後、Claude Code で/reversing-flutter-appsを入力して読み込まれたか確認します。

詳細

Flutter アプリのリバースエンジニアリング

Flutter は、標準的なモバイルワークフローの両方を一度に崩します。Dart は AOT コンパイルされてネイティブスナップショットになるため、jadx で逆コンパイルする dex がありません。また、Flutter エンジンは独自の BoringSSL と信頼ストアを同梱しているため、システムプロキシとユーザー CA ストアの両方を無視し、通信を傍受するプロキシには何も表示されません。どちらの問題も症状からはすぐに分からないため、何が起きているのか気づくまでに何時間も費やしてしまうことがあります。

使用する場合

  • APK を jadx で確認すると、ほとんど空の MainActivity しかなく、ほかにもほとんど何もない
  • APK に lib/arm64-v8a/libflutter.so と libapp.so、または assets/flutter_assets/ が含まれている
  • IPA に Frameworks/App.framework と Frameworks/Flutter.framework が含まれている
  • アプリが明らかにネットワーク通信を行っているのに、Burp、mitmproxy、または Charles に通信がまったく表示されない
  • Dart のクラス名やメソッド名を復元する、またはリリースビルドから API レイヤーを見つける必要がある

使用しない場合

  • 一般的な Java/Kotlin Android アプリ — testing-mobile-applications を使用してください
  • React Native(index.android.bundle、Hermes)— reversing-react-native-apps を使用してください
  • Unity(global-metadata.dat、libil2cpp.so)— reversing-unity-il2cpp を使用してください
  • アプリが通信するバックエンド — 通信を確認できるようになったら testing-apis を使用してください
  • 一般的なネイティブ .so のリバースエンジニアリング — analyzing-binaries を使用してください

まずビルドを特定する

ビルドモードによって、この後の手順がすべて決まります。ツールをインストールする前に確認してください。

unzip -l target.apk | rg 'libflutter|libapp|flutter_assets|kernel_blob'
確認できるものビルド意味
libapp.so + libflutter.soAOT リリース一般的なケースです。Dart がネイティブスナップショットにコンパイルされています。このスキルの主な対象です
assets/flutter_assets/kernel_blob.binデバッグ / JITはるかに簡単です。Dart カーネルバイトコードがあり、VM サービスに接続できる可能性があります
App.framework/App(iOS)AOT リリースlibapp.so と同様です。復号済み IPA から抽出してください
arm64-v8a のみ存在—想定どおりです。blutter は arm64 を対象としています。armeabi-v7a のサポートは弱めです
# Confirm the engine version — it gates which tools work
strings lib/arm64-v8a/libflutter.so | rg -m5 'Flutter|Dart VM version|dart-sdk'

kernel_blob.bin がある場合は、そこで作業を止め、代わりにそれを展開してください。デバッグビルドにははるかに多くの情報が残っており、Dart VM サービスが有効ならライブ検査もできます。リリースビルドは難しいケースであり、以降はリリースビルドを前提としています。

blutter で Dart の構造を復元する

blutter は Dart AOT スナップショットを解析し、クラス名、メソッド名、フィールド名、およびオブジェクトプールを復元します。これにより、解読不能な arm64 の逆アセンブル結果が、アプリ内をたどれる形になります。

# blutter needs BOTH libraries: libapp.so is the snapshot, libflutter.so
# identifies the exact Dart version used to compile it
unzip -j target.apk 'lib/arm64-v8a/*' -d ./libs
python3 blutter.py ./libs ./out

実際に使用する出力:

成果物用途
pp.txtオブジェクトプールのダンプ。文字列定数、URL、キー、エンドポイントが含まれます。まずこれを読んでください
objs.txt復元された Dart オブジェクトとクラス
asm/Dart シンボルを適用したライブラリごとの逆アセンブル結果
ida_script/IDA にシンボルを読み込みます。Ghidra 用の同等のものもあります
blutter_frida.js復元された Dart 関数を対象とする、生成済みの Frida フック

直面する問題は、Dart のバージョン不一致です。 blutter は特定の Dart SDK に対してコンパイルされており、スナップショットの形式はバージョンごとに変わります。解析時にエラーが発生した場合は、別のスナップショットを試すのではなく、アプリのエンジンに一致するバージョンの Dart SDK に対して blutter をビルドしてください。まず libflutter.so からバージョンを確認してください。また、blutter が対応するまで、新しい Flutter リリースでの最初の実行は失敗することを想定してください。

難読化されたビルド(flutter build --obfuscate --split-debug-info=...)では、Dart の名前が削除されます。blutter は呼び出しグラフ、オブジェクトプール、文字列定数などの構造を引き続き復元できますが、名前は残りません。pp.txt を手掛かりにしてください。URL、エンドポイントパス、エラー文字列は難読化後も残り、通常は API レイヤーを見つけるのに十分です。そもそも必要だったのは、API レイヤーの特定です。

トラフィックを確認する

別々の問題が2つあり、どちらも解決する必要があります。片方だけを解決しても、プロキシは空のままで、原因も分かりません。

問題1 — Flutterはシステムプロキシを無視します。 DartのHTTPクライアントはAndroid/iOSのプロキシ設定を読み取らないため、Wi-Fiプロキシを設定しても何も起こりません。代わりにネットワーク層でトラフィックを強制的に経由させます。

# Redirect transparently on the device/emulator
adb shell su -c 'iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to <proxy-ip>:8080'
# or use a VPN-based tunnel (tun2socks / a VPN-mode proxy app)

問題2 — Flutterには独自のCAストアがあります。 CAをユーザー証明書として、あるいはシステム証明書としてインストールしても、Flutterがそれを信頼するわけではありません。機能する方法は2つあります。

# A. reFlutter — patches the Flutter engine to trust your proxy and to
#    reroute traffic. Repackage and re-sign afterwards.
reflutter target.apk           # prompts for the proxy IP
# outputs a patched APK; then:
apksigner sign --ks debug.keystore release.RE.apk

# B. Frida — hook the certificate check inside libflutter.so at runtime.
#    ssl_verify_peer_cert is not exported, so scripts locate it by pattern
#    scanning; the pattern is engine-version specific.
frida -U -f com.target.app -l flutter-ssl-bypass.js

再パッケージできる場合はreFlutterを優先してください(スナップショットのダンプとソケットレベルのログ取得も可能になります)。アプリに再パッケージを検知する整合性チェックがある場合や、再署名できない場合はFridaを優先してください。どちらも失敗した場合は、blutterが復元したDart HTTPクライアントの関数をフックするのが代替策です。プロトコルレベルの忠実性は失われますが、リクエストとレスポンスは確認できます。

アプリを調査する

シンボルとトラフィックを取得したら、次の手順に進みます。

  1. pp.txtから始めます。 ベースURL、エンドポイントのパス、ヘッダー名、埋め込みキー素材はオブジェクトプールにあります。通常は数分でAPIの構成を把握できます。
  2. 復元した関数をフックします。 blutter_frida.jsを出発点に、リクエストビルダー、認証トークンのハンドラー、ローカル暗号処理の周辺で引数と戻り値をダンプします。
  3. プラットフォームチャネルを確認します。 Flutterアプリは生体認証、キーストア、決済をネイティブコードに委ねます。これらは通常のJava/KotlinまたはSwiftなので、testing-mobile-applicationsに引き継ぎます。
  4. 続いて、testing-apisを使ってAPIを適切にテストします。 ここまでの手順はすべてリクエストを確認するためのものです。脆弱性は通常、その先にあります。

受け入れてはいけない言い訳

  • 「プロキシに何も表示されない。だからアプリはネットワークを使っていないはずだ。」 ネットワークは使っています。あなたのプロキシを使っていないだけです。
  • 「CAをシステム証明書としてインストールした。これでTLSインターセプトが機能するはずだ。」 Flutterはシステムストアを参照しません。
  • 「jadxに何も表示されない。だからアプリは高度に難読化されているはずだ。」 dex内に確認できるものはありません。アプリ本体はlibapp.soにあります。
  • 「blutterが失敗した。だからこのビルドは解析できない。」 ほとんどの場合、Dartのバージョンが一致していないことが原因です。エンジンのバージョンを確認し、それに合わせてblutterを再ビルドしてください。
  • 「難読化されているから、文字列は消えているはずだ。」 名前は消えていますが、オブジェクトプールは残っています。pp.txtを確認してください。
  • 「arm64を手作業でリバースすればいい。」 シンボルのないDart AOT出力を読むには、非常に時間がかかります。まずシンボルを復元してください。それが作業のすべてです。

参考資料

  • testing-mobile-applications — より広範なモバイルアセスメントとネイティブチャネル
  • testing-apis — トラフィックを確認できるようになった後のバックエンドのテスト
  • analyzing-binaries — エンジン自体のより詳細なネイティブ解析
  • blutter、reFlutter、Frida/objection、jadx、apktool、apksigner

コメント