bypassing-root-jailbreak-detection について
AndroidやiOSアプリのroot・脱獄検出を調べる、許可を得たモバイルアプリテスト向けの技能です。アプリ内チェック、Frida検出、ハードウェア認証を区別し、それぞれの限界を説明します。
主な機能
- 01検出レイヤーの特定
- 02Androidのrootチェック分析
- 03iOSの脱獄チェック分析
- 04Frida検出とroot検出の区別
- 05ハードウェア認証の限界の説明
- 06検出状況とテスト範囲の記録
活用シーン
- 01起動時に終了するアプリの調査
- 02テスト端末での互換性問題の診断
- 03動的テスト環境の準備
- 04認証によるテスト範囲の制約の記録
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill bypassing-root-jailbreak-detectionインストール後、Claude Code で/bypassing-root-jailbreak-detectionを入力して読み込まれたか確認します。
詳細
root 検出とジェイルブレイク検出の回避
検出処理が単一のチェックだけで構成されていることはほとんどありません。アプリ全体に散在する、低コストなチェックが十数個あり、さらに近年では、フックでは操作できないハードウェアベースのアテステーションが1つ加わります。チェックを1つずつフックするのは、いたちごっこです。重要なのは、アプリが実際に依存しているレイヤーを見つけることです。
テストを許可されているアプリに対してのみ使用してください。
使用する場面
- 起動時にアプリが終了する、「root化されたデバイスが検出されました」と表示される、または気付かれないまま機能が低下する
- 市販状態のデバイスではアプリが動作するが、テスト用デバイスやエミュレーターでは動作しない
- Frida をアタッチするとプロセスがすぐに終了する、または
frida-psに何も表示されない objectionで調査できない、または数秒後にフックが作動しなくなる- ほかのテストを始める前に、安定した計装環境が必要
使用しない場面
- TLS インターセプトの失敗 —
bypassing-mobile-pinningを使用してください。プロキシエラーは別の問題ですが、検出処理が原因でそう見えることもあります - より広範な評価 —
testing-mobile-applicationsを使用してください - 特定のフレームワークに特化したリバースエンジニアリング — 該当する
reversing-*スキルを使用してください - アプリを不正に入手するために DRM やライセンスを回避すること — 対象外です
まずレイヤーを特定する
検出処理は4つのレイヤーにあり、それぞれ異なる対応が必要です。どのレイヤーが作動しているかを特定すると、時間を大幅に節約できます。
| レイヤー | シグナル | 対応 |
|---|---|---|
| Java/マネージドチェック | RootBeer、File.exists("/system/xbin/su")、Magisk に対するパッケージ照会 | Java レイヤーでフックする(objection、Frida) |
.so 内のネイティブチェック | su に対する stat/access/fopen、/proc/self/maps のスキャン | libc をフックするか、.so をパッチする |
| 計装の検出 | Frida をアタッチした場合にのみ終了する、ポート 27042 のプローブ、スレッド名のスキャン | root ではなく、エージェントを隠す |
| ハードウェアアテステーション | Play Integrity、SafetyNet、DeviceCheck、App Attest | フックできません — 以下を参照 |
# What does the app reference? Decompile and look before hooking.
apktool d target.apk -o out
rg -n 'RootBeer|isRooted|su\b|magisk|superuser|test-keys|/system/xbin|busybox|xposed|frida' -i out/smali* out/res 2>/dev/null | head -30
rg -n 'SafetyNet|PlayIntegrity|IntegrityManager|attest|DeviceCheck' -i out/ | head
# Native side
unzip -j target.apk 'lib/arm64-v8a/*' -d libs
rg -a -o 'su|/system/bin/su|magisk|frida|gum-js-loop|gmain' libs/*.so | sort -u | head -20
ネイティブライブラリで見つかる文字列から、アプリが何を探しているかがわかります。これは、何もわからないままフックを仕掛け、終了するのを待つよりも、はるかに効率的です。
Android
フックではなく、まず環境を隠してください。 root をうまく隠せば、フックを1つも使わずに大半の検出を回避でき、アプリが更新されても動作し続けます。
# Magisk: DenyList the target so the root is invisible to it
# Settings → Zygisk ON, Enforce DenyList ON, select the target package
# Shamiko (Zygisk module) hides more thoroughly than DenyList alone
# Play Integrity Fix (PIF) module for the attestation layer — see limits below
# Verify what the app can see
adb shell "pm list packages | grep -i magisk" # should return nothing to the app
次に、残ったチェックをフックします。
objection -g com.target.app explore
# then: android root disable
frida -U -f com.target.app -l anti-root-bypass.js --no-pause
一般的な Java のフックポイント(通常の出現順):
// File existence checks — the single most common family
Java.use('java.io.File').exists.implementation = function () {
const p = this.getAbsolutePath();
if (/su$|magisk|superuser|busybox|xposed/i.test(p)) return false;
return this.exists();
};
// Runtime.exec("su") and ProcessBuilder
// Build.TAGS containing "test-keys"
// PackageManager.getPackageInfo for known root packages
// System properties: ro.debuggable, ro.secure, ro.build.selinux
ネイティブチェックにはネイティブフックが必要です。 Java フックを設定してもアプリが終了する場合、チェックは .so 内にあります。
# See which syscall is finding the evidence
frida-trace -U -f com.target.app -i 'stat*' -i 'access' -i 'fopen' -i 'open'
# then edit the generated handlers to lie about the paths it probes
Frida の検出は別の問題です
root 化した状態でもアプリが問題なく動作し、アタッチした瞬間に終了する場合は、計装の検出処理に対処しているのであり、root を隠しても解決しません。
| チェック | 対処 |
|---|---|
| TCP 27042 が開いている、または D-Bus ハンドシェイクの応答 | frida-server をランダムなポートで実行するか、Server の代わりに Gadget を使用する |
/proc/self/maps に frida-agent、gum が含まれる | エージェントの名前を変更する。パッチ済みビルドを使用する |
スレッド名が gmain、gum-js-loop、pool-frida | パッチ済み Frida ビルドでは、これらの名前が変更されます |
/proc/self/task/*/stat のスキャン | 同上 |
名前付きパイプと re.frida.server 文字列 | パッチ済みビルド |
| 起動後の定期的な再チェック | チェックされる時間帯が過ぎてからアタッチするか、タイマーをフックする |
現実的な対処法は、この目的で作られたコミュニティ製ビルドが存在するパッチ済み Frida と、リッスンするサーバーではなく Gadget のインジェクションを組み合わせることです。それでも検出を回避できない場合は、静的解析と対象を絞った APK のパッチに切り替えてください。対話的な操作はできなくなりますが、作業は続けられます。
iOS
objection -g com.target.app explore
# then: ios jailbreak disable
# Tweak-based, system-wide: Liberty Lite, A-Bypass, Shadow (rootless jailbreaks)
iOS アプリで確認される項目と、隠すべきもの:
/Applications/Cydia.app、/bin/bash、/usr/sbin/sshd、/etc/apt、/private/var/lib/aptの存在- サンドボックス外への書き込み可能性(
fopen("/private/jailbreak.txt", "w")) fork()の成功。サンドボックス内のアプリでは成功してはならないものですdyldのイメージリストに含まれる MobileSubstrate / substitute- 開くことができる URL スキーム
cydia:// - 生の
statよりもフックしやすいNSFileManagerによる確認
ネイティブ層では stat、access、fopen、getenv("DYLD_INSERT_LIBRARIES")、および
_dyld_get_image_name をフックし、Objective-C 層では
NSFileManager のメソッドをフックします。ジェイルブレイクされていないデバイスでは、
Frida ガジェットを使って再パッケージします(objection patchipa)。ただし、
これによってアテステーションに引っかかることを想定してください。
フックできないもの
ハードウェアに裏打ちされたアテステーションが越えられない境界です。 Play Integrity の
MEETS_STRONG_INTEGRITY、TEE/StrongBox によるキーアテステーション、Apple の
App Attest はハードウェアキーで署名され、ベンダーのサーバーで検証されます。
クライアント側のフックで変更できるのはアプリが読み取る内容であり、サーバーが検証する
署名を偽造することはできません。
実際には、次のことを意味します。
- Magisk と Play Integrity Fix モジュールを組み合わせると、多くの場合、
BASICとDEVICEの完全性を満たせます。STRONGには改変されていない ブートローダーが必要です。 - 強い完全性が満たされない場合にアプリがフェイルクローズするなら、現実的な選択肢は、 再パッケージしたアプリを使う非ルート化デバイス(ただし、アプリ自体の署名チェックに 失敗します)、ネットワーク層のテストだけを行うストック ROM 搭載デバイス、または テスト期間中のアテステーション免除についてクライアントと合意することです。
- 免除を依頼してください。 アテステーションを無効にしたテストビルドは通常の エンゲージメント依頼であり、TEE に阻まれて何日も費やすより、はるかに低コストです。
スコープを黙って縮小するのではなく、レポートに明記してください。「ハードウェア アテステーションのため、本番ビルドでは動的インストルメンテーションを実施できませんでした。 テスト対象は X で、Y は対象外です」は、テスト範囲に関する所見です。これを隠すことは、 制約を明示するよりも問題です。
検出品質そのものが所見です
バイパスを試みる際は、検出の品質も記録してください。クライアントから質問される項目であり、 レポートにも記載すべき内容です。
- フェイルクローズ(終了)するのか、フェイルオープン(ログに記録して続行)するのか?
- チェック結果はサーバーに送信されるのか、それともローカルで信頼されるのか? ローカルのみの チェックは、よくても参考情報にすぎません。
- 中央の関数に集約されている(簡単にフックできる)のか、それとも分散され、再チェックされるのか?
- ハードウェアアテステーションを使っているのか、それともファイルシステムのヒューリスティックだけなのか?
ルート対策が File.exists("/system/xbin/su") だけのアプリには低重大度の注記が妥当です。
サーバー検証済みのアテステーションを使うアプリは評価に値します。
退けるべき言い訳
- 「objection のバイパスが機能しなかった。検出が強すぎる。」 一般的な Java のチェックには 対応しています。次にネイティブ層を調べてください。
- 「ルートを隠したのだから、動くはずだ。」 Frida の実行時にだけ停止するなら、 ルート検出ではなくインストルメンテーション検出と戦っています。
- 「Play Integrity は Frida でバイパスできる。」 ハードウェアに裏打ちされた判定は バイパスできません。フックで変えられるのはアプリが読み取る内容であり、TEE が署名する内容では ありません。
- 「見つけたチェックをすべてパッチすればいい。」 分散されたチェックは再実行されます。 中央の判定ポイントを見つけるか、環境自体を隠してください。
- 「ルート検出は脆弱性だ。」 これはセキュリティ対策であり、強固ではありません。 それがないことは低重大度の注記であり、主要な所見ではありません。
- 「インストルメントできなかったので、報告することは何もない。」 テスト範囲の制約を 明確に報告してください。
参考資料
testing-mobile-applications— この作業によって可能になる評価bypassing-mobile-pinning— モバイルアプリが「ただ動かない」もう一つの理由analyzing-binaries— ネイティブの検出ルーチンへのパッチ適用reporting-security-findings— テスト範囲の制約を正直に記載する方法- Magisk + Zygisk、Shamiko、objection、Frida(およびパッチ済みビルド)、Liberty Lite、Shadow