reversing-xamarin-maui について
Xamarin または .NET MAUI で構築された Android・iOS アプリをリバースエンジニアリングするスキルです。パッケージ内の .NET アセンブリを識別・抽出し、復元した C# を調べ、アプリの動作分析に Mono 対応のランタイムフックを使う方法を説明します。
主な機能
- 01Xamarin のパッケージ世代の識別
- 02AssemblyStore と XALZ アセンブリの抽出
- 03DLL の可読な C# への逆コンパイル
- 04Frida による Mono メソッドのフック
- 05アプリ内の秘密情報とロジックの確認
- 06アセンブリのパッチと再パッケージ
活用シーン
- 01Xamarin APK または IPA の分析
- 02アセンブリ内に埋め込まれた認証情報の発見
- 03実行時のマネージドメソッドの観察
- 04アプリパッケージの変更と再構築
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill reversing-xamarin-mauiインストール後、Claude Code で/reversing-xamarin-mauiを入力して読み込まれたか確認します。
詳細
Xamarin および .NET MAUI アプリのリバースエンジニアリング
Xamarin アプリは、Android パッケージ内に完全な .NET アプリケーションを隠しています。アセンブリを抽出して展開すると、ソースコードに近い品質の C# が得られます。そのため、コンテナ形式を突破できれば、非常に成果の大きいリバースエンジニアリング作業です。ただし、コンテナ形式はこれまでに 2 度変更されており、単純な解凍ではうまくいきません。
使用する場面
- APK に
lib/*/libmonodroid.so、libmonosgen-2.0.so、またはlibxamarin-app.soが含まれている - APK に
assemblies/assemblies.blobまたはassemblies/*.dllが含まれている jadxにmono.MonoPackageManagerと Xamarin のブートストラップクラスしか表示されない- IPA に
.dllファイルまたはFrameworks/Mono*の構成が含まれている - 対象が Xamarin、Xamarin.Forms、または .NET MAUI として説明されている
使用しない場面
- Unity(
global-metadata.dat、libil2cpp.so) —reversing-unity-il2cppを使用してください。 Unity も .NET ですが、パッケージ形式はまったく異なります - Flutter または React Native —
reversing-flutter-appsまたはreversing-react-native-appsを使用してください - TLS インターセプトの問題 —
bypassing-mobile-pinningを使用してください - 評価全般 —
testing-mobile-applicationsを使用してください
パッケージ形式の世代を特定する
製品のライフサイクルを通じて 3 種類の形式が提供されており、抽出方法も異なります。
unzip -l target.apk | rg 'assemblies|libmonodroid|libxamarin|libmonosgen'
| 確認できるもの | 世代 | 抽出方法 |
|---|---|---|
assemblies/*.dll、読み取り可能な PE ヘッダー(MZ) | クラシック、非圧縮 | 解凍して直接逆コンパイル |
XALZ で始まる assemblies/*.dll | クラシック、LZ4 圧縮 | まず XALZ ラッパーを展開 |
assemblies/assemblies.blob + .manifest | AssemblyStore | pyxamstore で展開 |
assemblies/ がなく、lib/*/libxamarin-app.so がある | .NET 6 以降 / MAUI、アセンブリは ELF 内 | 共有ライブラリから抽出 |
| 管理対象アセンブリがどこにもない | NativeAOT | .NET のリバースエンジニアリングではありません — analyzing-binaries を使用 |
# Which one is it? Check the first four bytes of an assembly
unzip -p target.apk 'assemblies/Mono.Android.dll' 2>/dev/null | xxd | head -1
# 4d5a... → MZ, a plain .NET PE, decompile now
# 58414c5a → "XALZ", LZ4-compressed, decompress first
抽出
# AssemblyStore (assemblies.blob)
pyxamstore unpack -d ./out target.apk
# produces the individual .dll files under ./out
# XALZ-compressed individual DLLs
# The header is: magic "XALZ" | descriptor index | uncompressed size | LZ4 block
python3 -c "
import lz4.block, struct, sys
d = open(sys.argv[1],'rb').read()
assert d[:4] == b'XALZ'
size = struct.unpack('<I', d[8:12])[0]
open(sys.argv[2],'wb').write(lz4.block.decompress(d[12:], uncompressed_size=size))
" Mono.Android.dll Mono.Android.decompressed.dll
# MAUI / .NET 6+ assemblies embedded in libxamarin-app.so
# They are stored as sections; carve by MZ header or use pyxamstore's newer modes
unzip -j target.apk 'lib/arm64-v8a/*' -d libs && binwalk -Me libs/libxamarin-app.so
プレーンな .dll ファイルを取得したら、通常の .NET の作業です。
ilspycmd ./out/YourApp.dll -o ./decompiled # or dnSpy / dnSpyEx / dotPeek
rg -n 'https?://|ApiKey|ConnectionString|Bearer|password' ./decompiled | head -40
フレームワークのアセンブリではなく、まずアプリ独自のアセンブリから始めてください。 通常は製品名にちなんだ名前が付いています。Mono.Android.dll、System.*、Xamarin.* は標準のものなので、調べても時間の無駄です。
ランタイムフック
Frida の Java ブリッジから Mono メソッドは見えません。アプリのロジックは JVM 内にないためです。Mono に対応した計測が必要です。
# Enumerate the Mono runtime and its loaded assemblies
frida -U -f com.target.app -l frida-mono-api.js
# Fridax targets Xamarin specifically; also useful:
# mono_get_root_domain, mono_assembly_foreach, mono_class_get_methods,
# mono_compile_method → resolve a managed method to a native address, then
# Interceptor.attach at that address
実際には、まず逆コンパイルして目的のクラスとメソッドを特定し、それから Mono API を通じて解決してアタッチするのが実用的です。すでに復元した C# を読むより、Mono を手当たり次第にフックするほうがはるかに時間がかかります。
Xamarin では証明書検証が管理コード内で行われます。 そのため、一般的な Android のピンニング回避ではまったく対応できません。
// The two hook targets, both in the managed layer
ServicePointManager.ServerCertificateValidationCallback
HttpClientHandler.ServerCertificateCustomValidationCallback
Mono フックを使ってこれらが true を返すようにするか、アセンブリにパッチを適用して再パッケージ化してください。関連する診断については bypassing-mobile-pinning を参照してください。
調査で見つかるもの
Xamarin アプリには、逆コンパイルすると見つけやすい形でリスクが集中しています。
- C# にハードコードされたシークレット。 開発者はコンパイル済みアセンブリを解読不能だと考えがちですが、API キー、接続文字列、ストレージの認証情報がプレーンな IL のまま含まれています。
- クライアント内にあるバックエンドの完全なロジック。 モバイルアプリとサーバー側プロジェクトでコードを共有するのは Xamarin の慣例であるため、クライアントには信頼できるビジネスルールが含まれていることがよくあります。場合によってはサーバー側のモデルや検証処理も含まれており、悪意あるリクエストをどのように構成すればよいかがそのまま分かります。
trueを返すカスタムのServerCertificateValidationCallback。 出荷されてしまった開発ビルドでよく見られます。- 安全でないローカルストレージ。
Xamarin.Essentials.SecureStorageは通常問題ありませんが、Preferencesや生のファイル書き込みは安全ではありません。 - 認証情報や完全なオフラインデータセットを含む埋め込み SQLite。
パッチ適用と再パッケージ化
# Edit IL directly in dnSpy, save the assembly, then rebuild the container
pyxamstore pack -d ./out # rebuild assemblies.blob
apktool b target -o patched.apk && apksigner sign --ks debug.keystore patched.apk
再パッケージ化では、元のコンテナ形式を正確に再現する必要があります。ローダーが XALZ を想定している場所に非圧縮 DLL を置いたり、サイズの合わない blob を使ったりすると、起動時に分かりにくいネイティブクラッシュが発生します。観察だけが目的なら、ランタイムフックを優先してください。
否定すべき誤った判断
- 「jadx で何も見つからないので、難読化されています。」 DEX 内には何もありません。
アプリは
assemblies/にあります。 - 「解凍すると DLL が出てきましたが、dnSpy で開けません。」 それらは XALZ で圧縮されています。 先頭の 4 バイトを確認してください。
- 「Frida の Java フックが機能しません。」 メソッドは JVM ではなく Mono のものです。Mono API を使ってください。
- 「DLL にコンパイルされているので、キーは安全です。」 IL は読みやすい C# に逆コンパイルできます。 アセンブリ内のすべての定数は公開情報として扱ってください。
- 「ピンニング回避を実行しましたが、通信は依然として失敗します。」 Xamarin はマネージドコードで検証するため、Java レイヤーの回避処理は検証に一切触れていません。
- 「アセンブリが何百個もあります。」 そのほとんどは標準のフレームワークです。製品名が付いたものを調べてください。
参考資料
testing-mobile-applications— より広範な評価とプラットフォームのストレージbypassing-mobile-pinning— この問題に関連する傍受失敗の診断reversing-unity-il2cpp— もう一つの .NET ベースのモバイルフレームワークanalyzing-binaries— マネージドアセンブリのない NativeAOT ビルド- pyxamstore、dnSpy/dnSpyEx、ILSpy、Fridax、frida-mono-api